MySQL에 한글이나 이모지를 저장하다가 갑자기 '1366 Incorrect string value for column' 에러가 터진 경험이 있을까. 대부분의 개발자들은 이 에러를 만나면 인코딩 설정이 뭔가 이상하다는 건 알지만, 정확히 어디서 뭘 잘못 설정했는지는 헷갈린다. 데이터베이스 레벨인지, 테이블 레벨인지, 컬럼 레벨인지, 아니면 연결 설정인지 말이다. 이번에는 이 오류가 정확히 왜 발생하는지, 어느 계층에서 문제가 생기는지, 그리고 완벽하게 해결하는 방법을 실무 근거로 정리해주겠다.

 

1단계. UTF-8 인코딩 오류의 정확한 원인 이해하기

이 에러는 MySQL 클라이언트가 보낸 문자열 데이터가 컬럼에 설정된 문자 집합(charset)과 맞지 않을 때 발생한다. 예를 들어 클라이언트가 UTF-8로 인코딩된 '한글'을 보냈는데, 컬럼이 latin1로 설정되어 있으면 MySQL은 그 데이터를 이해할 수 없어서 에러를 던진다. 혹은 반대로 컬럼은 utf8mb4이지만 연결(connection) 문자 집합이 utf8(3바이트)로 설정되어 있으면, 4바이트가 필요한 이모지 같은 글자는 제대로 저장되지 않는다. 문제는 MySQL의 utf8이 사실 완전한 UTF-8이 아니라는 점이다. MySQL의 기본 utf8은 3바이트까지만 지원하므로, 4바이트 문자(이모지, 일부 한중일 문자 등)를 저장하려면 utf8mb4를 써야 한다.

 

어느 계층에서 문제가 발생할까

MySQL 문자 집합은 4가지 계층으로 나뉜다. 1) 서버 기본값(my.cnf 설정), 2) 데이터베이스 기본값(CREATE DATABASE 시 지정), 3) 테이블 문자 집합(CREATE TABLE 시 지정), 4) 컬럼 문자 집합(각 컬럼별 지정). 연결 문자 집합은 따로 또 있다. 클라이언트가 쿼리를 보낼 때 사용할 문자 집합(character_set_client), 결과를 받을 때 사용할 문자 집합(character_set_results), 그리고 연결 자체의 문자 집합(character_set_connection)이다. 이 중 하나라도 잘못 설정되면 에러가 난다.

 

2단계. 현재 MySQL 문자 집합 상태 진단하기

먼저 서버의 현재 설정을 확인해야 한다.

SHOW VARIABLES LIKE 'character_set_%';

이 쿼리는 아래 같은 결과를 보여준다.

character_set_client            utf8mb4
character_set_connection        utf8mb4
character_set_database          utf8mb4
character_set_filesystem        binary
character_set_results           utf8mb4
character_set_server            utf8mb4
character_set_system            utf8

이상적인 상태는 client, connection, database, results, server가 모두 utf8mb4로 일치하는 것이다. 만약 utf8로 설정되어 있다면 이모지 저장이 실패한다. 각 데이터베이스와 테이블의 문자 집합도 확인해야 한다.

SELECT SCHEMA_NAME, DEFAULT_CHARACTER_SET_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = 'your_database';
SELECT TABLE_NAME, TABLE_COLLATION FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'your_database';

 

3단계. 3가지 실전 해결 방법
방법 1: PHP 연결 시 지정 (단기 해결, 가장 빠름)

✓ 올바른 방법. PDO 연결 시 charset을 utf8mb4로 명시한다.

<?php
$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8mb4';
$pdo = new PDO($dsn, 'root', 'password', [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);
?>

MySQLi라면 이렇게 한다.

<?php
$conn = new mysqli('localhost', 'root', 'password', 'testdb');
$conn->set_charset('utf8mb4');
?>

혹은 쿼리로도 가능하다.

<?php
$pdo->exec("SET NAMES utf8mb4");
$pdo->exec("SET CHARACTER SET utf8mb4");
?>

✗ 잘못된 방법. charset을 명시하지 않고 기본 설정에만 의존한다.

<?php
$dsn = 'mysql:host=localhost;dbname=testdb';
$pdo = new PDO($dsn, 'root', 'password');
?>

이렇게 하면 서버의 character_set_client 기본값을 따르게 되는데, 이게 utf8이면 이모지는 저장되지 않는다.

방법 2: 테이블 문자 집합 변경 (근본 해결, 기존 데이터 영향)

✓ 올바른 방법. 테이블 전체를 utf8mb4로 변환한다.

ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

기존 데이터가 있어도 이 쿼리는 자동으로 변환한다. 다만 용량이 큰 테이블이면 시간이 걸릴 수 있으니 피크 시간대를 피해야 한다. 테이블별이 아니라 특정 컬럼만 변경하려면 다음처럼 한다.

ALTER TABLE users MODIFY COLUMN name VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

데이터베이스 전체를 변경하려면 이렇게 한다.

ALTER DATABASE testdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

✗ 잘못된 방법. 컬럼을 utf8로만 변경하고 utf8mb4로 하지 않는다.

ALTER TABLE users MODIFY COLUMN name VARCHAR(255) CHARACTER SET utf8;

이렇게 하면 여전히 이모지는 저장되지 않는다.

방법 3: 새 테이블 생성 시부터 utf8mb4 지정 (예방, 신규 프로젝트 권장)
CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
    email VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
    bio TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

또는 좀 더 간단하게 테이블 레벨로만 지정하면 컬럼들이 상속한다.

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    email VARCHAR(255) NOT NULL,
    bio TEXT
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

 

4단계. 주의사항과 흔한 실수
상황 잘못된 방법 올바른 방법
charset을 명시할 때 charset=utf8 charset=utf8mb4 (이모지 필요시)
Collation 선택 utf8mb4_general_ci (성능만 중시) utf8mb4_unicode_ci (유니코드 표준 준수)
기존 데이터 변환 DROP 후 재생성 ALTER TABLE ... CONVERT
파라미터화 쿼리 문자열 직접 연결 바인딩 변수 사용 (SQL 인젝션도 방지)

utf8 vs utf8mb4 헷갈리는 이유는 MySQL 초창기부터 실수였다는 것이다. MySQL의 utf8은 사실 UTF-8이 아니라 UCS2의 일부일 뿐이다. 진정한 UTF-8을 원한다면 무조건 utf8mb4를 써야 한다. 또 하나의 흔한 실수는 character_set_connection만 바꾸고 character_set_client를 안 바꾸는 것이다. PHP에서 set_charset()을 호출하면 둘 다 바뀌지만, 만약 직접 쿼리를 날린다면 SET NAMES utf8mb4를 써야 양쪽 다 설정된다.

 

5단계. 실무 예제: 실제로 동작하는 전체 코드
<?php
// 1단계: 안전한 UTF-8 연결 설정
$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8mb4';
$pdo = new PDO($dsn, 'root', 'password', [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]);

// 2단계: 한글과 이모지 데이터 삽입
$stmt = $pdo->prepare(
    'INSERT INTO users (name, comment) VALUES (:name, :comment)'
);

try {
    $stmt->execute([
        ':name' => '홍길동',
        ':comment' => '반갑습니다 😊 한글 테스트'
    ]);
    echo "성공: 한글과 이모지가 저장되었습니다.\n";
} catch (PDOException $e) {
    echo "실패: " . $e->getMessage() . "\n";
    // ERROR 1366이 나면 위의 방법 2나 3을 적용하세요
}

// 3단계: 데이터 조회
$result = $pdo->query('SELECT name, comment FROM users LIMIT 1');
$row = $result->fetch();

echo "저장된 데이터: " . $row['name'] . " - " . $row['comment'] . "\n";
?>

만약 여전히 1366 에러가 난다면, 테이블 문자 집합을 확인하자.

SHOW CREATE TABLE users\G

결과에서 CHARACTER SET utf8mb4가 보이는지 확인한다. 만약 utf8이면 방법 2를 실행해야 한다.

 

마무리: 이모지와 한글은 기본, utf8mb4는 필수

MySQL ERROR 1366은 단순한 인코딩 오류가 아니라, 문자 집합 계층이 일치하지 않을 때 발생하는 구조적 문제다. PHP 연결 시 charset을 명시하는 것만으로 대부분 해결되지만, 기존 테이블이 utf8으로 만들어졌다면 ALTER로 변환해야 한다. 신규 프로젝트라면 처음부터 utf8mb4와 utf8mb4_unicode_ci를 기본값으로 설정해서 이 문제 자체를 피하는 게 가장 현명하다. 한글 사용자가 많은 한국 서비스라면 더욱 utf8mb4는 선택이 아니라 필수다. 이 글의 방법 1을 지금 당신의 코드에 적용해서 charset=utf8mb4를 추가하면, 더 이상 1366 에러를 볼 일이 없을 것이다.