테크블로그에 글을 쓰니 이모지가 모두 ??? 물음표로 나온다. 덕분에 오랜만에 쓴 글이 의도치 않게 의문투성이인것 마냥 쓰여졌다.
아래가 내가 놓친 기본적이지만 실수 두가지다:
1. DB의 charset, collation 설정
2. 서버 -> 클라이언트 response 헤더 설정
버전1 MongoDB에서 버전2 MySQL로 마이그레이션 하며 기본적인 설정을 놓쳤다.
charset은 문자집합을 어떻게 설정할지, collation은 그 문자 집합을 어떻게 정렬할지에 대한 설정이다.
utf8mb4전 세계의 언어들을 텍스트로 처리하려고보니, 3바이트가 넘는 문자가 없었다. 이에 utf8라는 가변 3바이트 저장방식을 사용하기 시작했다.
하지만 🦊 이러한 이모지(emoji), 즉 특별한 문자를 사용하려다보니 4바이트의 영역이 탄생되어버린것이다.
이를 커버하기 위해 (이미 오래전에) 가변 4바이트 자료형인 utf8mb4가 추가되었다. utf8mb4는 utf8과 비슷하지만 이모지와 같은 좀 더 넓은 범위의 문자를 지원한다.
이에 해당 DB의 charset을 utf8mb4으로 설정하였다.
그렇다면 collation은 무엇일까?
정수(int)형에서 일반적인 오름차순 정렬을 1, 2, 3, 4, ... 로 정의하는 것과 같이, 지정된 문자집합의 정렬 방식을 지정하는 것이다.
참고로 밑의 정렬방식에 공통적으로 나오는 _ci는 case insensitive로 영어 대소문자를 구분하지 않는 방식을 의미한다.
utf8mb4_general_ci영어 텍스트의 경우, 우리는 a, b, c, d, ... 의 순서를 보편적인 정렬로 인식한다. 이는
utf8mb4_general_ci의 방식이다.
일반적으로 널리 사용되며, 라틴계 문자를 사람들의 보편적인 인식에 맞춰 정렬한다.
utf8mb4_unicode_ci영어, 한국어 등, 대부분의 언어에서는 위의 general_ci와 비슷한 결과를 낸다.
하지만 복잡한 다국어 문자열에서는 utf8mb4_unicode_ci가 더 정확한 정렬을 제공한다고 한다. (ex. 독일어의 'ß'와 'ss'를 구분)
ALTER, SET과 더불어show variables like'char%'; 등의 쿼리를 통해 확인과 수정을 진행했다.
unicode_ci는 모든 언어에서 문자 정렬을 정확하게 처리하는만큼 general_ci보다는 성능 저하가 있을 수 있다곤하지만, utf8mb4, utf8mb4_unicode_ci의 설정으로 가면 적어도 DB 단에서 이번과 같은 이슈를 마주할 원인이 일어나지는 않을것으로 판단된다.
response.setHeader()위의 방식을 통해 DB 자체에는 이모지가 잘 들어가는 것을 확인했다. 그럼에도 왜 여전히 내 글은 이모지 부분이 물음표로 가득할까?
'프론트 charset 설정 문제인가?'라는 건방진 의심을 하며 프론트 메이트 몰래 리액트 코드를 까보고 있었다. 그러다 혹시나하여 캐시를 지우고 포스트맨으로 API를 튕겨보니, 역시나 서버에서 클라이언트로 내려주는 값에서 부터 문제가 있었다. 역시 내가 무언가 놓치고 있었다:
응답 헤더 역시 charset 설정이 필요했다.
response.setHeader("Content-Type", "application/json; charset=utf-8")
'위에서 설명할 땐 utf-8은 이모지 안보인다면서 왜 utf-8로 설정하는거야? utf8mb4로 설정해야하는거 아냐?'
utf8mb4는 데이터베이스(MySQL)에서 사용하는 문자 집합이다. 따라서 디비 연결 설정 시, 그리고 이미 생성된 DB의 경우 해당 charset으로 진행한다.utf-8은 웹 페이지에서 사용되는 인코딩 방식으로, 이모지와 같은 4바이트 유니코드 문자도 지원한다.Lael's World - [MySQL/MariaDB] utf8mb4 언어셋 소개 및 표현범위. (charset, collation)