PHP 한글 깨짐, 코드 한 줄 안 바꾸고 서버 설정만으로 해결하는 법

By: Joshua Williams

겨울이 채 가시기도 전에, 한 대학생이 밤새 만든 PHP 게시판 프로젝트를 들고 왔다. 실행하자마자 화면 가득 물음표와 깨진 글자가 넘쳐났다. 코드를 아무리 뒤져봐도 문제가 없어 보이는데, 한글만 유독 깨져 보였다. 이런 상황을 겪은 사람이라면 알 것이다. PHP와 MySQL을 처음 연동할 때 만나는 가장 흔한 벽이 바로 문자 인코딩 문제다. 많은 사람이 이 문제를 만나면 PHP 파일에 `charset`을 추가하고, DB 연결 코드에 인코딩 설정을 덧붙인다. 하지만 때로는 코드를 전혀 건드리지 않아도 서버 설정만으로 깔끔하게 해결되는 경우가 있다. 이 글에서는 코드 수정 없이 서버 환경 설정만으로 PHP와 데이터베이스 사이에서 발생하는 한글 인코딩 깨짐을 해결하는 방법을 차례대로 정리했다.

왜 아무리 코드를 고쳐도 한글이 깨지는가

PHP와 MySQL을 연동하는 과정에서 한글이 깨지는 원인은 단순히 한 곳에만 있지 않다. 데이터가 생성되고, 저장되고, 전송되고, 출력되는 모든 단계에서 인코딩 방식이 서로 맞아야 한다. MySQL 데이터베이스에 저장된 데이터가 UTF-8로 저장되어도, PHP가 데이터를 읽어올 때 EUC-KR로 처리하면 문제가 생긴다. 반대로 PHP가 UTF-8로 출력해도 브라우저가 다른 인코딩으로 해석하면 화면에는 깨진 문자가 나타난다.

이 과정에서 수많은 개발자가 PHP 파일 상단에 `header(“Content-Type: text/html; charset=utf-8”)`을 추가하고, DB 연결 직후에 `SET NAMES utf8`을 실행한다. 그런데도 문제가 해결되지 않는 경우가 있다. 그 이유는 서버 자체의 기본 문자셋이 잘못 설정되어 있거나, MySQL 서버의 기본 인코딩이 클라이언트와 맞지 않기 때문이다. 이런 경우에는 PHP 코드 수정만으로는 한계가 있다.

서버 설정 파일에서 인코딩을 통일하는 핵심 포인트

PHP는 `php.ini`라는 설정 파일을 읽어서 동작한다. 이 파일 안에는 기본 문자 인코딩을 지정하는 여러 항목이 있다. 그중에서도 `default_charset`은 PHP가 출력하는 모든 응답에 적용되는 기본 문자셋을 결정한다. 이 값이 `UTF-8`로 되어 있지 않으면, 아무리 코드에서 인코딩을 지정해도 서버가 내보내는 헤더가 잘못된 인코딩을 가리킬 수 있다.

또한 `mbstring.internal_encoding`이나 `mbstring.http_input`, `mbstring.http_output` 같은 항목도 확인해야 한다. PHP에서 멀티바이트 문자열 함수를 사용할 때 기준이 되는 인코딩이 여기서 결정되기 때문이다. 만약 이 값들이 서로 다른 인코딩으로 설정되어 있다면, 문자열 처리 과정에서 한글이 잘려나가거나 깨질 가능성이 높다.

MySQL을 사용하는 경우에는 `my.cnf` 또는 `my.ini` 설정 파일이 중요하다. MySQL 서버가 기본적으로 사용하는 문자셋이 클라이언트가 보내는 문자셋과 다르면, 데이터가 저장될 때부터 잘못된 인코딩으로 변환된다. 이 파일 안에 `character-set-server=utf8`과 `collation-server=utf8_general_ci`를 설정하면 서버 자체가 UTF-8로 동작한다. 또한 `skip-character-set-client-handshake` 항목을 활성화하면, 클라이언트가 보내는 문자셋 정보를 무시하고 서버 기준 문자셋을 따르게 되어 인코딩 충돌을 피할 수 있다.

웹 서버 레벨에서 인코딩을 고정하는 방법

PHP 코드가 아무리 올바르게 작성되어 있어도, 웹 서버가 응답 헤더에 잘못된 Content-Type을 붙여 보내면 브라우저는 그 정보를 기준으로 문자를 해석한다. Apache 웹 서버를 사용하는 경우, `.htaccess` 파일이나 httpd.conf 파일에 `AddDefaultCharset UTF-8` 설정을 추가하면 모든 응답에 UTF-8 문자셋이 기본으로 적용된다.

Nginx를 사용하고 있다면, 서버 블록 안에 `charset utf-8;` 지시어를 추가하면 된다. 이 설정은 PHP가 생성한 응답이 아니라 웹 서버가 직접 내려보내는 정적 파일과 동적 응답 모두에 적용된다. 특히 PHP-FPM과 연동하여 사용하는 경우, FastCGI 응답 헤더에 문자셋 정보가 포함되지 않을 수 있는데, 이때 웹 서버 레벨에서 charset을 지정해두면 누락된 정보를 보완할 수 있다.

이렇게 웹 서버 설정에서 기본 문자셋을 고정해두면, PHP 파일마다 일일이 인코딩 관련 코드를 추가할 필요가 없어진다. 또한 코드에 이미 인코딩 설정이 포함되어 있더라도 충돌이 나지 않으며, 오히려 서버 레벨의 설정이 일관성을 유지해주는 역할을 한다.

데이터베이스 연결 시 character_set_client와 character_set_results

MySQL과 PHP의 연동 과정에서 인코딩 문제가 발생하는 또 다른 지점은 세션 초기화 과정이다. PHP에서 PDO 또는 mysqli를 사용해 DB에 접속할 때, 서버는 `character_set_client` 값을 기준으로 클라이언트가 보낸 쿼리를 해석한다. 이 값이 서버의 기본 문자셋과 다르면 쿼리 문자열 안에 포함된 한글이 깨진 상태로 해석되어, 올바른 데이터를 찾지 못하거나 잘못된 값이 저장될 수 있다.

이 값을 강제로 통일하는 방법 중 하나는 MySQL 설정 파일에 `init_connect` 항목을 추가하는 것이다. 이 설정은 클라이언트가 MySQL 서버에 접속할 때마다 자동으로 실행되는 SQL 문을 지정한다. 예를 들어 `init_connect=”SET NAMES utf8″`을 설정해두면, PHP 코드에서 별도로 인코딩 관련 쿼리를 실행하지 않아도 모든 접속 세션에서 UTF-8 인코딩이 적용된다.

다만 `init_connect`는 SUPER 권한을 가진 계정에는 적용되지 않는다는 점을 알고 있어야 한다. 관리자 계정으로 접속할 때는 이 설정이 무시되므로, 관리자 계정으로 테스트할 때는 인코딩이 여전히 깨져 보일 수 있다. 하지만 일반 웹 애플리케이션이 사용하는 계정으로 접속할 때는 정상적으로 동작한다.

인코딩 관련 자주 발생하는 오류와 서버 설정으로 막는 법

PHP와 데이터베이스를 연동할 때 자주 부딪히는 오류 중 하나는 `PDOException`과 함께 나타나는 문자셋 관련 경고다. 이는 대부분 MySQL 서버가 요청받은 문자셋을 지원하지 않거나, 클라이언트와 서버의 문자셋이 불일치할 때 발생한다. 코드에서 위험을 감수하며 인코딩 변환 함수를 여러 번 적용하는 대신, 서버 설정 파일에서 기본 문자셋을 모두 통일해두면 이런 오류의 대부분을 예방할 수 있다.

또한 웹 서버의 로그 파일에서 한글이 깨져 기록되는 경우도 있다. 이는 로그 파일 자체의 인코딩 문제라기보다, 애플리케이션이 출력한 문자 데이터가 이미 깨진 상태일 때 나타난다. 서버 설정으로 인코딩을 통일하면 애플리케이션에서 발생하는 문자 데이터가 처음부터 올바른 인코딩으로 생성되기 때문에, 로그 기록 시 발생하는 깨짐 문제도 함께 줄어든다.

문자 인코딩을 다루는 데 있어 가장 중요한 원칙은 하나의 인코딩을 모든 계층에서 일관되게 사용하는 것이다. 서버 운영체제, 웹 서버, PHP, MySQL, 데이터베이스 테이블, 그리고 HTML 문서까지 모든 단계에서 UTF-8을 사용한다면 인코딩 충돌이 발생할 여지가 거의 없다. 코드 수정 없이 서버 설정만으로 이 일관성을 확보하는 것이 가능하다.

서버 설정 변경 후 반드시 확인할 사항과 주의점

서버 설정 파일을 수정한 후에는 반드시 서비스를 재시작해야 변경 사항이 적용된다. Apache를 사용한다면 `service apache2 restart` 또는 `systemctl restart apache2`를 실행해야 하고, Nginx를 사용한다면 `systemctl reload nginx`로 설정을 다시 읽어 들여야 한다. PHP-FPM을 함께 사용하는 경우에는 PHP-FPM 서비스도 재시작해주는 것이 좋다.

MySQL의 경우에도 설정 파일을 변경한 뒤에는 MySQL 서비스를 재시작해야 한다. 재시작 전에 `mysql –help` 또는 `SHOW VARIABLES LIKE ‘character_set%’` 명령어로 현재 적용된 문자셋 값을 미리 확인해보면 변경 전후를 비교하기 쉽다. 이렇게 서버 설정만으로 인코딩을 통일했을 때 얻는 이점은, 나중에 PHP 코드를 수정하거나 다른 개발자가 프로젝트를 인계받았을 때도 인코딩 관련 문제를 다시 겪지 않는다는 점이다.

다만 서버 운영체제의 기본 로케일이 UTF-8이 아닌 경우에는 PHP의 파일 읽기 쓰기 작업에서도 인코딩 문제가 발생할 수 있다. 이런 경우에는 PHP 설정 파일에서 `mbstring.language` 값을 `UTF-8`로 지정하고, 시스템 환경 변수에 `LANG=ko_KR.UTF-8` 또는 `LANG=en_US.UTF-8`을 설정하는 것을 고려해야 한다. 이는 서버 설정의 일부이므로 코드 수정 없이 해결할 수 있는 범위에 포함된다.

인코딩 문제는 한 번 해결되면 다시 발생하지 않는 경우가 많지만, 서버를 이전하거나 업데이트할 때 설정 파일이 초기화되면서 같은 문제가 다시 나타날 수 있다. 따라서 지금 설정한 값을 문서나 메모로 남겨두고, 서버를 새로 구성할 때 동일한 설정을 적용할 수 있도록 준비해두는 것이 실용적인 대비책이다. 결국 서버 설정으로 인코딩 문제를 해결한다는 것은, 코드의 수정 없이 여러 계층의 설정값을 한곳으로 모으는 정리 작업이며, 이는 장기적인 유지보수 관점에서도 분명한 이점을 제공한다.