티스토리 뷰
0. “이모지 입력이 안되는데요 😰”
문제 상황
모니터링 도중 특정 테이블에서 입력이 실패하는 오류가 발생했다.
[1366] Incorrect string value: '\xF0\x9F\x98\x80' for column 'name' at row 1
에러 케이스를 분석한 결과, 공통적으로 입력 값에 이모지가 포함된 걸 발견했다. 일반 텍스트와 이모지는 어떤 차이가 있는걸까?
원인
문제가 되는 컬럼의 문자집합은 utf8
이었다.
MySQL 이모지 관련 검색을 해보니 MySQL의 문자집합인 utf8
은 3바이트까지만 지원하기 때문에, 4바이트 크기의 이모지를 저장하려고 하면 오류가 발생한다고 한다.
때문에 이를 해결하기 위해 문자 집합을 utf8
에서 utf8mb4
로 변경하고, 콜레이션을 utf8mb4_unicode_ci
로 설정해야 한다.
해결
- 테이블의 문자 집합 및 콜레이션 변경
ALTER TABLE [테이블명]
CONVERT TO CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
- 컬럼의 문자 집합 및 콜레이션 변경
ALTER TABLE [테이블명]
MODIFY COLUMN [컬럼명] [문자열 컬럼 타입]
CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
간단한 해결책..처럼 보이지만 바로 운영 환경에 실행하기는 어렵다. ALTER 명령을 실행하면 테이블이 잠기고, 이로 인해 쓰기 작업에 문제가 발생할 수 있기 때문이다.
문제를 해결하기 위해 알아보니, 두 가지 방법이 있었다.
- pt-online-schema-change 툴을 이용해 실시간으로 테이블 변경 (잠금 최소화)
- 트래픽이 적은 시간대에 점검 후 작업을 진행하는 방법
1번 방법을 시도해보고 싶었지만, 팀 내에 pt-online-chema-change 사용 경험이 없어 실행이 어려웠다. 대신 운영 DB와 유사한 환경에서 ALTER 문을 실행해 예상 작업 시간을 측정한 뒤 2번 방법으로 결정했다.
점검을 한 후 작업을 진행했기 때문에 별다른 이슈 없이 테이블 변경을 마칠 수 있었다.
1. 문자집합(캐릭터 셋)과 콜레이션
문자집합 (Chracter set)
문자집합은 문자열 컬럼(CHAR, VARCHAR, TEXT)에만 설정할 수 있다. MySQL에서는 컬럼단위까지 문자집합을 관리할 수 있다.
-- MySQL 서버에서 사용 가능한 문자 집합
SHOW CHARACTER SET;
euckr
- 한국어 전용으로 사용되는 문자집합
- 모든 글자는 1~2바이트를 사용
utf8mb4
- 다국어 문자를 포함할 수 있는 컬럼에 사용하기 적합
- 한 글자를 저장하기 위해 1~4바이트까지 사용
utf8
utf8mb4
의 부분집합utf8mb4
가 도입되기 이전에 주로 사용됨
- 다국어 문자를 포함할 수 있는 컬럼에 사용하기 적합
- 한 글자를 저장하기 위해 1~3바이트까지 사용
콜레이션 (Collation)
- 문자열 컬럼의 값을 비교하거나 정렬하는 기준
-- MySQL 서버에서 사용 가능한 콜레이션 목록
SHOW COLLATION;
- 3개의 파트로 구성된 콜레이션 이름
- 첫 번째 파트 : 문자 집합의 이름
- 두 번째 파트 : 해당 문자 집합의 하위 분류
- 세 번째 파트 : 대문자나 소문자의 구분 여부
ci
: 대소문자를 구분하지 않는 콜레이션 (Case Insensitive)cs
: 대소문자를 별도의 문자로 구분하는 콜레이션 (Case sensitive)
- 2개의 파트로 구성된 콜레이션 이름
- 첫 번째 파트: 문자 집합의 이름
- 두 번째 파트 : bin
- bin : 이진 데이터 (binary)
- 이진 데이터로 관리되는 문자열 컬럼은 콜레이션을 가지지 않는다. 콜레이션이 ‘xxx_bin’이라면 비교 및 정렬은 실제 문자 데이터의 바이트 값 기준으로 수행된다.
- bin : 이진 데이터 (binary)
-- 컬럼의 문자집합, 콜레이션 조회
SELECT column_name, column_type, character_set_name, collation_name
FROM information_schema.columns
WHERE table_schema = '[테이블 스키마명]' AND table_name = '[테이블 명]';
2. 3바이트 이모지는 입력이 되나요?
MySQL의 문자집합인
utf8
은 3바이트까지만 지원하기 때문에,
4바이트 크기의 이모지를 저장하려고 하면 오류가 발생한다
‘4바이트 크기의 이모지’를 저장할 때 오류가 발생한다면 3바이트 크기의 이모지는 입력 가능한 걸까?
문자집합을 utf8
로 세팅하고 3바이트 이모지를 찾아봤다. 3바이트 크기의 이모지는 ☕️, ☀️ , ❤️ 등이 있다.
SELECT
emoji,
CHAR_LENGTH(emoji) AS char_length,
LENGTH(emoji) AS byte_length
FROM (
SELECT '[이모지1]' AS emoji UNION ALL
SELECT '[이모지2]' UNION ALL
SELECT '[이모지3]'
) AS emojis;
CHAR_LENGTH()
: 문자열의 문자 수를 반환 (유니코드 코드포인트 기준).LENGTH()
: 문자열의 바이트 길이를 반환 (인코딩된 실제 크기 기준).
입력한 이모지는 하나인데 CHAR_LENGTH
는 2, LENGTH
는 6이 나왔다. 그 이유는 ☕️ 이모지가 조합형 이모지이기 때문이다.
☕️ 이모지가 MySQL에서 CHAR_LENGTH가 2, LENGTH가 6 바이트로 나오는 이유는 이모지가 두 개의 유니코드 코드포인트로 구성되어 있기 때문입니다. 각각의 코드포인트가 UTF-8로 인코딩될 때 차지하는 바이트 수를 합산하면 총 6 바이트가 됩니다.
☕️
U+2615 (Hot Beverage, ☕):
- 하얀색 커피잔 이모지.
- 텍스트 스타일(흑백)
- UTF-8로 인코딩 시 3 바이트.
E2 98 95
(3 바이트)
U+FE0F (Variation Selector-16, VS16, ️
):
- 컬러 이모지 스타일을 지정하는 변형 선택자.
- UTF-8로 인코딩 시 3 바이트.
EF B8 8F
(3 바이트)
이렇게 두 개의 유니코드가 조합된 이모지가 ☕️ 이다.
실제로 문자집합을 utf8
로 세팅하고, 해당 3바이트 이모지를 입력하면 삽입/조회 둘 다 문제 없이 잘 되는 걸 확인했다.
하지만 4바이트 이모지가 대부분이므로, 당연히 utf8mb4
을 사용하는게 좋다.
3. 이모지 입력은 되는데 이모지가 ?로 보여요.
문자집합과 콜레이션을 변경하고 이모지를 저장하니 오류 없이 잘 저장되었다. 하지만 이상하게 해당 레코드를 조회하면 이모지가 ?
로 보이는게 아닌가.
이번엔 무슨 문제인고 알아보니 문자 집합 시스템 변수 설정이 원인이었다.
-- character_set 변수 조회
SHOW VARIABLES LIKE 'character%';
SHOW VARIABLES
로 문자집합 관련 시스템 변수를 조회해보니 다른 변수는 다 utf8mb4
인데 character_set_results
변수가 utf8mb3
로 되어 있었다.
이 말은 DB 자체에는 이모지가 정상적으로 저장되었지만, 쿼리 처리 결과를 클라이언트에 보낼 때 사용하는 문자 집합 character_set_results
변수가 utf8mb3
라서 ?로 깨져 보였던 것이다.
SET character_set_results = 'utf8mb4';
문자 집합이 일치하도록utf8mb4
로 변경해주면 이모지가 정상적으로 보인다.
문자 집합 시스템 변수
character_set_system
- MySQL 서버가 식별자를 저장할 때 사용하는 문자 집합
- 사용자가 설정하거나 변경할 필요 X
character_set_server
- MySQL 서버의 전역 기본 문자 집합
- DB나 테이블 또는 컬럼에 아무런 문자 집합이 설정되지 않을 때 이 시스템 변수에 명시된 문자 집합이 기본으로 사용된다.
character_set_database
- MySQL DB의 기본 문자 집합
- DB를 생성할 때 아무런 문자 집합이 명시되지 않았다면 이 시스템 변수에 명시된 문자 집합이 기본값으로 사용된다.
- 이 변수가 정의되지 않으면 character_set_server 시스템 변수에 명시된 문자 집합이 기본으로 사용된다.
character_set_filesystem
- LOAD DATA INFILE … 또는 SELECT … INTO OUTFILE 문장을 실행할 떄 인자로 지정되는 파일의 이름을 해석할 때 사용되는 문자 집합
- 데이터 파일의 내용을 읽을 떄 사용하는 문자 집합이 아니라 파일의 이름을 찾을 때 사용하는 문자 집합
- 각 커넥션에서 임의 문자 집합으로 변경해서 사용 가능
- LOAD DATA INFILE … 또는 SELECT … INTO OUTFILE 문장을 실행할 떄 인자로 지정되는 파일의 이름을 해석할 때 사용되는 문자 집합
character_set_client
- MySQL 클라이언트가 보낸 SQL 문장은 character_set_client에 설정된 문자 집합으로 인코딩해서 MySQL 서버로 전송한다.
- 각 커넥션에서 임의 문자 집합으로 변경해서 사용 가능
character_set_connection
- MySQL 서버가 클라이언트로부터 전달받은 SQL 문장을 처리하기 위해 character_set_connection의 문자 집합으로 변환한다. 또한 클라이언트로부터 전달받은 숫자 값을 문자열로 변환할 때도 character_set_connection에 설정된 문자 집합이 사용된다.
- 각 커넥션에서 임의 문자 집합으로 변경해서 사용 가능
character_set_results
- MySQL 서버가 각 쿼리의 처리 결과를 클라이언트로 보낼 때 사용하는 문자 집합
- 각 커넥션에서 임의의 문자 집합으로 변경해서 사용할 수 있다.
참고자료
Real MySQL 8.0 (2권) | 백은빈 - 교보문고
Real MySQL 8.0 (2권) | MySQL 서버를 활용하는 프로젝트에 꼭 필요한 경험과 지식을 담았습니다! 《Real MySQL 8.0》은 《Real MySQL》을 정제해서 꼭 필요한 내용으로 압축하고, MySQL 8.0의 GTID와 InnoDB 클러스
product.kyobobook.co.kr
'Database' 카테고리의 다른 글
문자 집합과 콜레이션 변경을 위한 온라인 DDL 활용 (0) | 2024.12.29 |
---|
- Total
- Today
- Yesterday
- 4Way Handshake
- github actions 구성요소
- 이모지입력오류
- 엔티티와값객체
- read timeout
- utf8mb3
- hibernate 쿼리 로그
- http커넥션
- AOP
- 콜레이션변경
- online ddl
- opencsv
- file
- csv 라이브러리
- github actions 기초
- csv to bean
- TCP연결
- tcp커넥션
- mysql 온라인 ddl
- 도메인구성요소
- jpa 쿼리 로그
- spring boot3 쿼리 로그
- spring retry
- 문자집합변경
- github actions components
- 콜레이션
- 쿼리 파라미터 바인딩
- mysql 이모지
- CGLIB프록시
- 코프링
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |