MariaDB에서 테이블을 utf8mb4로 바꿨는데도 이모지 넣으면 1366이 납니다
지난번 nginx 뒤 스프링부트 질문 올렸던 그 서버입니다. 사용자 댓글 기능을 붙였는데 본문에 이모지가 들어간 요청만 저장이 실패합니다. 환경은 Ubuntu 22.04, MariaDB 10.6, Spring Boot 3.2(JDBC는 mariadb-java-client) 입니다.
앱 로그에는 아래처럼 찍히고, 같은 INSERT를 mysql 클라이언트에서 직접 넣어도 동일하게 막힙니다.
java.sql.SQLException: (conn=812) Incorrect string value: '\xF0\x9F\x98\x80' for column 'content' at row 1
MariaDB [sarc]> INSERT INTO comment(content) VALUES ('hi \xF0\x9F\x98\x80');
ERROR 1366 (22007): Incorrect string value: '\xF0\x9F\x98\x80' for column 'content' at row 1이모지 때문인 건 알아서 테이블 문자셋을 utf8mb4로 바꿨습니다. 확인해보면 테이블 기본값은 바뀐 걸로 나옵니다.
ALTER TABLE comment CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
MariaDB [sarc]> SHOW TABLE STATUS LIKE 'comment'\G
...
Collation: utf8mb4_unicode_ci그런데도 같은 ERROR 1366이 그대로 납니다. 한글이나 영문만 든 댓글은 정상 저장되고, 이모지 들어간 경우만 실패합니다. my.cnf의 [mysqld]에 character-set-server=utf8mb4도 넣고 재기동해봤지만 증상은 같습니다.
테이블을 utf8mb4로 바꿨는데 왜 4바이트 문자만 여전히 거부되는 건지, 어디를 더 봐야 하는지 알려주실 수 있을까요?
테이블을 utf8mb4로 바꾼 것 자체는 맞게 하셨습니다. 그런데 ERROR 1366은 지금 저장 대상 컬럼이 아니라 연결(세션) 문자셋 단계에서 나고 있을 가능성이 큽니다. mysql 클라이언트에서 직접 넣어도 똑같이 막힌다는 점이 결정적인 단서입니다.
MariaDB 공식 문서를 보면, 클라이언트가 보낸 문자열은 character_set_client로 받아들여진 뒤 character_set_connection으로 변환되고, 그 다음에야 컬럼 문자셋으로 저장됩니다. 문서 표현으로는 서버가 "statements sent by the client from character_set_client to character_set_connection"으로 변환합니다. 즉 연결 문자셋이 utf8(=utf8mb3, 3바이트)이면 4바이트 이모지(\xF0\x9F\x98\x80)는 컬럼에 닿기도 전에 변환 단계에서 거부되어 1366이 납니다. 컬럼이 utf8mb4라도 소용이 없습니다.
지금 상태를 먼저 확인해보시면 바로 드러납니다. 같은 세션에서:
SHOW VARIABLES LIKE 'character_set_%';
-- character_set_client / character_set_connection / character_set_results
-- 이 셋 중 하나라도 utf8(utf8mb3)나 latin1이면 그게 원인입니다.그리고 컬럼이 실제로 바뀌었는지도 SHOW TABLE STATUS 말고 아래로 보셔야 합니다. TABLE STATUS는 테이블 기본값만 보여줄 뿐, 개별 컬럼에 예전 문자셋이 명시적으로 박혀 있으면 안 보입니다.
SHOW CREATE TABLE comment\G
-- 또는
SHOW FULL COLUMNS FROM comment; -- content 컬럼의 Collation 확인해결 1) mysql 클라이언트 세션. 핵심은 SET NAMES입니다. 공식 문서에 "Sets the character_set_client, character_set_connection, character_set_results and, implicitly, the collation_connection session system variables"라고 명시되어 있어, 이 한 문장이 세 변수를 한 번에 맞춰줍니다.
SET NAMES utf8mb4;
INSERT INTO comment(content) VALUES ('hi 😀'); -- 이제 들어갑니다매번 치기 싫으시면 my.cnf의 [client] 또는 [mysql] 섹션에 넣으세요. default-character-set은 서버 옵션이 아니라 클라이언트 옵션이라 [mysqld]의 character-set-server와는 별개입니다. 이게 [mysqld]에만 넣고 증상이 그대로였던 이유로 보입니다.
[client]
default-character-set=utf8mb4
[mysql]
default-character-set=utf8mb4참고로 SET NAMES의 기본값이 utf8mb4로 바뀐 것은 MariaDB 11.8부터입니다. 지금 쓰시는 10.6에서는 연결 문자셋 기본값이 utf8mb4가 아닐 수 있으니, 위 확인 쿼리로 실제 값을 직접 보시는 게 확실합니다.
해결 2) Spring Boot(mariadb-java-client) 쪽. 드라이버가 연결 시점에 세션 문자셋을 어떻게 잡는지에 따라 달라지므로, 연결 시점에 명시적으로 고정하는 게 확실합니다. JDBC URL에 세션 변수를 걸어두거나, 커넥션 풀 초기화 쿼리로 SET NAMES를 거시면 됩니다.
spring.datasource.url=jdbc:mariadb://127.0.0.1:3306/sarc?sessionVariables=character_set_client=utf8mb4,character_set_connection=utf8mb4,character_set_results=utf8mb4
# 또는 커넥션 풀 초기화 쿼리로
spring.datasource.hikari.connection-init-sql=SET NAMES utf8mb4정리하면, 거부되는 지점은 컬럼이 아니라 연결 문자셋입니다. 먼저 SHOW VARIABLES LIKE 'character_set_%'로 세 변수가 utf8mb4인지 확인하시고, 아니면 SET NAMES utf8mb4 한 줄로 즉시 재현이 사라지는지 보세요. 그걸로 확인되면 클라이언트 쪽은 my.cnf [client], 앱 쪽은 커넥션 init SQL로 고정하시면 됩니다.
참고 문서: MariaDB - Setting Character Sets and Collations, MariaDB - SET NAMES
🔗 https://mariadb.com/kb/en/setting-character-sets-and-collations/
로그인 후 답글을 남길 수 있습니다.