개발 slecs

DB 커넥션 charset 누락으로 인한 이모지 깨짐 사전 차단

목차

DB 커넥션 풀 설정에서 charset이 빠져 있어서 생긴 문제를 수정했다.

src/lib/db.ts 파일 하나만 건드린 핀포인트 수정이지만, 실제로 이런 류의 변경이 가져오는 파급은 생각보다 크다. 그래서 짧은 작업이었음에도 좀 기록해두고 싶었다.


왜 utf8mb4_unicode_ci 인가

MySQL에서 charset 설정은 크게 두 가지 레이어에서 작동한다. 하나는 테이블/컬럼 스키마 레이어, 다른 하나는 커넥션 레이어다. 스키마를 아무리 utf8mb4_unicode_ci로 잘 만들어 놔도, 커넥션 자체가 다른 charset으로 붙으면 데이터를 주고받는 과정에서 MySQL이 내부적으로 변환을 시도한다. 이 변환 과정에서 이모지나 특수 유니코드 문자가 깨지거나, 최악의 경우 Incorrect string value 에러가 터진다.

utf8mb4가 필요한 이유는 명확하다. MySQL의 utf8은 사실 진짜 UTF-8이 아니고 3바이트 시퀀스만 처리한다. 4바이트를 차지하는 이모지(예: 😂, 🔥)나 일부 한자는 utf8로는 저장이 안 된다. utf8mb4가 실질적인 완전한 UTF-8 지원이고, _unicode_ci는 유니코드 정렬 규칙(Collation)을 따른다는 의미다. 한국어 서비스라면 사실상 기본값이어야 한다.

charset 최대 바이트/문자 이모지 지원 비고
utf8 3바이트 MySQL 레거시 명칭
utf8mb4 4바이트 실질적 UTF-8
latin1 1바이트 영문/서유럽 전용

실제 코드 변경 패턴

mysql2 풀을 생성할 때 charset을 명시하지 않으면, 서버 기본값이나 라이브러리 기본값을 따라간다. 이게 환경마다 달라서 로컬에서는 멀쩡하다가 프로덕션 환경에서만 터지는 경우가 있다.

수정 전후 패턴은 대략 이렇다.

// before
const pool = mysql.createPool({
  host: process.env.DB_HOST,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  database: process.env.DB_NAME,
});

// after
const pool = mysql.createPool({
  host: process.env.DB_HOST,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  database: process.env.DB_NAME,
  charset: 'utf8mb4_unicode_ci',
});

단 한 줄 추가인데, 이게 없으면 커넥션이 맺어질 때 SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci 쿼리가 자동으로 날아가지 않는다.


팀 리뷰에서 항상 챙기는 포인트

DB 유틸/풀 설정 파일은 프로젝트 초반에 한 번 만들어두면 거의 안 건드리는 파일이다. 그래서 코드리뷰 때도 그냥 넘어가기 쉽다. 근데 바로 이런 파일일수록 기본 설정값을 꼼꼼히 봐야 한다고 팀에 늘 얘기한다.

내가 신규 프로젝트 DB 설정 리뷰할 때 체크하는 항목들:

  • charset / collation 명시 여부
  • timezone 설정 (+09:00 또는 local 혹은 UTC 통일)
  • connectionLimit 적정값 (기본값 10이 적합한지)
  • waitForConnections 옵션 (큐 대기 vs 즉시 에러)
  • connectTimeout 설정 여부

charset 누락은 이 중에서도 증상이 늦게 나타나는 축에 속한다. 영어만 쓰는 초반 개발 단계에서는 전혀 문제가 없다가, 사용자 데이터에 이모지나 특수문자가 들어오기 시작하는 시점에 갑자기 터진다. 발견이 늦어질수록 이미 쌓인 데이터가 있어서 마이그레이션이나 데이터 정합성 이슈까지 같이 붙어온다.

이번 수정은 그 전에 잡아냈으니 다행이었다. src/lib/db.ts 하나짜리 변경이지만, 이걸 운영 데이터가 쌓인 이후에 발견했다면 훨씬 긴 후처리가 필요했을 거다. 짧은 커밋일수록 "왜 지금 이걸 고쳤는가"의 맥락이 중요하다고 생각해서 남긴다.

끝.


🛒 이 글과 어울리는 추천 상품

*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.

댓글 0

첫 댓글 달아줘.