로컬 개발 환경에서는 정상적으로 동작하던 애플리케이션이 AWS RDS(Relational Database Service) 배포 후 한글 데이터가 물음표('???')로 표시되거나 외계어(Mojibake) 형태로 깨지는 현상은 초기 구축 단계에서 빈번하게 발생하는 기술 부채 중 하나입니다. 이는 단순히 한글 폰트의 문제가 아니라, 클라이언트에서 데이터베이스 엔진, 그리고 스토리지 계층에 이르기까지 데이터가 흐르는 파이프라인 상의 인코딩(Character Set)과 정렬 규칙(Collation)의 불일치에서 기인합니다. 특히 AWS RDS의 기본 파라미터 그룹 설정은 대부분 `latin1`을 기본값으로 채택하고 있어, 명시적인 오버라이드 없이는 멀티바이트 문자 처리에 실패하게 됩니다. 본 아티클에서는 이러한 인코딩 미스매치의 근본 원인을 아키텍처 관점에서 분석하고, MySQL 및 PostgreSQL 엔진별 엔지니어링 해결책을 다룹니다.
1. 인코딩 파이프라인과 핸드셰이크 메커니즘
데이터베이스의 인코딩 문제는 단일 설정이 아닌 계층적 구조에서 발생합니다. MySQL을 예로 들면, 데이터가 저장될 때 다음 6가지 단계의 변수를 검증해야 합니다. 이 중 하나라도 `utf8mb4`가 아닌 다른 값(주로 `latin1`)으로 설정되어 있다면 변환 과정에서 손실이 발생합니다.
- Server: DB 서버의 기본 인코딩
- Database: 특정 스키마의 기본 인코딩 (Server 설정을 상속)
- Table: 테이블의 기본 인코딩 (Database 설정을 상속)
- Column: 실제 데이터가 저장되는 컬럼의 인코딩 (Table 설정을 상속)
- Client & Connection: 애플리케이션과 DB 간 세션의 인코딩
가장 흔한 오류 패턴은 서버와 DB는 `utf8mb4`로 설정했으나, 클라이언트와 서버가 연결을 맺는 Connection 단계에서 `latin1`으로 핸드셰이크가 일어나는 경우입니다. 이 경우 애플리케이션은 UTF-8 데이터를 보내지만, DB는 이를 Latin1 바이트 스트림으로 해석하여 잘못된 매핑을 수행합니다.
2. MySQL/MariaDB 파라미터 그룹 최적화전략
AWS RDS for MySQL의 경우, 콘솔에서 '파라미터 그룹(Parameter Group)'을 생성하여 기본 설정을 덮어써야 합니다. 단순히 `utf8`을 사용하는 것은 권장되지 않습니다. 표준 `utf8`은 가변 3바이트로 이모지(Emoji)와 같은 4바이트 문자를 저장할 수 없기 때문에, 반드시 `utf8mb4`를 사용해야 합니다.
2.1 필수 파라미터 설정
RDS 파라미터 그룹 편집기에서 다음 항목들을 찾아 변경하십시오. `collation`은 정렬 속도와 정확도 트레이드오프를 고려하여 선택해야 합니다. 일반적으로 `utf8mb4_general_ci`가 속도는 빠르지만, 현대적인 정렬 정확도를 위해 `utf8mb4_unicode_ci` 또는 8.0 버전 이상인 경우 `utf8mb4_0900_ai_ci`를 권장합니다.
| Parameter Name | Recommended Value | Description |
|---|---|---|
| character_set_client | utf8mb4 | 클라이언트로부터 들어오는 문자의 인코딩 |
| character_set_connection | utf8mb4 | 서버가 쿼리를 처리할 때 사용할 인코딩 |
| character_set_database | utf8mb4 | 생성되는 DB의 기본 인코딩 |
| character_set_filesystem | utf8mb4 | 파일 시스템 경로명 인코딩 |
| character_set_results | utf8mb4 | 쿼리 결과 또는 에러 메시지의 인코딩 |
| character_set_server | utf8mb4 | 서버의 기본 인코딩 |
| collation_connection | utf8mb4_general_ci | 연결 시 문자열 비교 규칙 |
| collation_server | utf8mb4_general_ci | 서버 기본 정렬 규칙 |
2.2 skip-character-set-client-handshake의 중요성
많은 엔지니어가 놓치는 부분이 `skip-character-set-client-handshake` 설정입니다. 클라이언트(애플리케이션)가 접속 시 자신의 인코딩 정보를 보내면, MySQL은 기본적으로 클라이언트의 요청을 우선시하여 세션 변수를 변경합니다. 이를 방지하고 서버 측에 설정된 파라미터 그룹 값을 강제하려면 이 옵션을 활성화해야 합니다.
-- 변경 전 확인 (AWS RDS 기본 상태일 경우 latin1 등이 섞여 있음)
SHOW VARIABLES LIKE 'char%';
/*
출력 예시 (Bad Case):
+--------------------------+----------------------------+
| Variable_name | Value |
+--------------------------+----------------------------+
| character_set_client | utf8mb4 |
| character_set_connection | utf8mb4 |
| character_set_database | latin1 | <-- 문제 발생 지점
| character_set_filesystem | binary |
| character_set_results | utf8mb4 |
| character_set_server | latin1 | <-- 문제 발생 지점
| character_set_system | utf8 |
+--------------------------+----------------------------+
*/
3. PostgreSQL 인코딩 처리 메커니즘
PostgreSQL은 MySQL과 달리 인코딩 처리가 더 엄격하며, 클러스터 초기화(initdb) 시점에 결정되는 경우가 많습니다. RDS for PostgreSQL은 인스턴스 생성 시 기본적으로 `UTF8`을 선택하도록 유도하지만, `Collation`과 `Ctype` 설정에 주의해야 합니다.
- Encoding: 데이터베이스에 저장되는 문자 집합 (반드시 UTF8 권장).
- LC_COLLATE: 문자열 정렬 순서 (예: `ko_KR.UTF-8` 사용 시 한글 가나다순 정렬).
- LC_CTYPE: 문자 분류 (대소문자 구분 등).
PostgreSQL에서 인코딩 문제가 발생하는 주된 원인은 클라이언트 인코딩(client_encoding) 설정입니다. 서버는 UTF8이어도 클라이언트가 SQL_ASCII 등으로 접속하면 변환 오류가 발생할 수 있습니다.
-- 현재 클라이언트 인코딩 확인
SHOW client_encoding;
-- 세션 레벨에서 강제 설정 (임시 조치)
SET client_encoding TO 'UTF8';
애플리케이션단(JDBC, Node.js Driver 등)에서 접속 URL 파라미터에 인코딩을 명시하는 것이 가장 확실한 예방책입니다. 예를 들어 JDBC URL에는 `?charSet=UTF-8`을 추가합니다.
4. 이미 깨진 데이터의 진단 및 복구
이미 `???`로 데이터가 들어간 경우와, 이상한 외계어(`안ë…`)로 보이는 경우는 대응 방법이 다릅니다.
4.1 '???' (Question Marks)로 저장된 경우
4.2 Mojibake (외계어)로 저장된 경우
데이터가 `latin1` 필드에 `utf8` 바이트 스트림 그대로 구겨져 들어간 경우입니다. 바이트 정보는 보존되어 있으므로, 덤프 후 올바른 인코딩으로 다시 주입하거나 `CAST` 함수를 통해 복구할 수 있습니다. MySQL의 경우 `BINARY`로 캐스팅 후 다시 `utf8mb4`로 변환하는 방식을 시도해볼 수 있습니다.
-- Mojibake 복구 시도 (컬럼 단위)
-- 1. 현재 컬럼을 binary 형태로 변경 (바이트 보존)
ALTER TABLE users MODIFY name VARBINARY(255);
-- 2. binary 컬럼을 올바른 utf8mb4 캐릭터 셋으로 변경
ALTER TABLE users MODIFY name VARCHAR(255) CHARACTER SET utf8mb4;
이 작업은 데이터 손실 위험이 있으므로, 반드시 스냅샷 생성 또는 테이블 백업 후 진행해야 합니다. 또한, 테이블 전체의 인코딩을 변경할 때는 다음 명령어를 사용합니다.
-- 테이블의 기본값 변경 (기존 데이터는 변환되지 않음)
ALTER TABLE table_name DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- 테이블 및 기존 데이터까지 모두 변환 (Blocking Operation 주의)
ALTER TABLE table_name CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
5. 결론 및 베스트 프랙티스
RDS의 한글 인코딩 문제는 발생 후 해결하는 것보다, 인프라 프로비저닝 단계에서 예방하는 것이 비용 효율적입니다. Terraform이나 CloudFormation 같은 IaC(Infrastructure as Code) 도구를 사용하여 RDS 파라미터 그룹을 코드로 관리하고, `utf8mb4` 설정을 표준 템플릿화하십시오.
또한, 애플리케이션의 데이터베이스 연결 풀(HikariCP 등) 설정과 ORM(JPA, TypeORM) 설정에서도 인코딩을 명시적으로 지정하여, 전체 파이프라인의 정합성을 유지해야 합니다. 데이터베이스는 데이터의 최종 저장소입니다. 입구(Client)부터 출구(Storage)까지 단 하나의 레이어도 `latin1`과 같은 레거시 설정이 개입할 틈을 주어서는 안 됩니다.
Post a Comment