Character-Encoding

Character Encoding 외계어로 변한 한글 파일명, 이것으로 밤샘 작업 끝냈습니다

Character Encoding 외계어로 변한 한글 파일명, 이것으로 밤샘 작업 끝냈습니다

Character-Encoding
Character-Encoding

밤샘을 부르는 리눅스 마이그레이션의 복병, 한글 깨짐

리눅스 서버 인프라를 마이그레이션하거나 업그레이드할 때, 엔지니어를 가장 지치게 만드는 복병이 있습니다. 바로 한글 파일명 깨짐(Character Encoding) 현상입니다.

얼마 전 저도 서비스 이관 작업을 진행했습니다. 기존에 사용하던 구형 레거시 환경인 AS-IS: RHEL 5.8 (ko_KR.euckr) 시스템의 대량 첨부파일 데이터를 최신 환경인 TO-BE: RHEL 9.6 및 Rocky Linux 9.8 (en_US.UTF-8) 환경으로 옮기는 작업이었습니다.

그런데 동일한 파일을 옮겼고 로케일 설정도 분명히 맞췄는데, 유독 특정 OS 환경에서만 한글 파일명이 외계 한자 문자로 처참하게 깨지는 기이한 현상이 발생했습니다. 인터넷에 나오는 뻔한 방법으로는 도저히 해결이 안 돼서 결국 밤을 새우며 원인을 파악해 냈습니다. 저와 같은 시행착오를 겪으며 삽질하는 엔지니어 분들이 없기를 바라는 마음으로, 실제 트러블슈팅 경험을 녹여낸 완벽한 해결 프로세스를 공유합니다.

RHEL 5.8에서 RHEL 9 서버로의 여정: 로케일의 근본적인 차이

서버를 이전하기 전 가장 먼저 확인해야 하는 벽은 바로 시스템의 언어 규칙인 로케일(Locale) 설정입니다. 두 서버의 스펙을 비교해 보면 환경 자체가 완전히 다릅니다.

항목AS-IS 환경 (구형 레거시)TO-BE 환경 (현대 표준)
OS 버전Red Hat Enterprise Linux 5.8RHEL 9.6 / Rocky Linux 9.8
기본 로케일ko_KR.euckren_US.UTF-8
문자 인코딩EUC-KR (완성형 한글 고유 표준)UTF-8 (유니코드 가변 길이 표준)
한글 표현 범위현대 한글 2,350자만 지원전 세계 모든 문자 및 확장 한글 지원

RHEL 9나 Rocky Linux 9 같은 최신 시스템에서는 과거처럼 /etc/sysconfig/i18n 파일을 직접 열어서 수정하지 않습니다. 요즘은 localectl이라는 시스템 제어 명령어를 사용하는 것이 정석입니다. 실제로 제가 작업할 때 적용했던 명령어 순서입니다.

Bash

# 1. 지원 가능한 로케일 확인 및 언어팩 설치
sudo dnf install langpacks-ko
locale -a | grep ko_KR

# 2. 시스템 전체 로케일을 en_US.UTF-8로 설정
sudo localectl set-locale LANG=en_US.UTF-8

# 3. 설정 상태 확인
localectl status

현업 엔지니어의 한 줄 노트

localectl로 바꾼 시스템 로케일은 /etc/locale.conf에 안전하게 저장됩니다. 다만, 명령어를 입력하자마자 바로 적용되는 것은 아닙니다. SSH 세션을 끊고 새로 로그인(재접속)해야 전체 계정에 깔끔하게 반영됩니다.

왜 en_US.UTF-8 환경에서 한글 파일명이 보일까?

처음 인프라를 접하는 초급 엔지니어분들이 흔히 하는 오해가 있습니다.

“로케일 이름 앞부분이 en_US(미국 영어)인데 한글 파일명이 깨지거나 인식이 안 되는 것 아닌가요?”

결론부터 말씀드리면 아무 상관 없습니다. 앞의 국가 코드(en_US)는 시스템 에러 메시지나 날짜 표기 규칙을 영어로 출력하겠다는 의미일 뿐입니다. 진짜 중요한 핵심은 뒤에 붙은 인코딩 엔진인 UTF-8입니다.

UTF-8은 전 세계 모든 문자를 수용하는 ‘유니코드’ 체계입니다. 그래서 시스템 언어가 영어로 되어 있어도 터미널에 touch 한글파일.txt를 입력하면 아무런 문제 없이 한글 파일이 잘 만들어지고 눈에 보입니다.

반면 과거에 쓰던 ko_KR.euckr은 시스템 메시지는 한국어로 나와서 편할지 몰라도, 인코딩이 완성형(EUC-KR) 제한에 묶여 있습니다. 그래서 ‘똠’, ‘뷁’, ‘쌤’ 같이 표준 규격에 없는 신조어나 확장 한글이 파일명에 포함되면 글자가 유실되거나 심각한 입출력 에러를 일으킵니다. 그래서 요즘 현대적인 기업 인프라 아키텍처에서는 글로벌 오픈소스 호환성을 위해 en_US.UTF-8을 표준으로 채택하는 추세입니다.

단순 tar 압축 이관 시 발생하는 대참사

실제 마이그레이션 당일, 저는 AS-IS(RHEL 5.8) 서버에서 수만 개의 한글 첨부파일을 tar -cvf backup.tar * 명령어로 압축했습니다. 그리고 이 백업 파일을 대수롭지 않게 신규 서버(RHEL 9.6)로 전송한 뒤 tar -xvf backup.tar로 압축을 풀었습니다.

결과는 대실패였습니다. 터미널에서 ls -l을 치는 순간 눈앞이 캄캄해졌습니다. 모든 한글 파일명이 ??? 혹은 \355\225... 같은 정체불명의 바이너리 문자로 출력되었기 때문입니다. 당연히 웹 애플리케이션 화면에서도 파일을 찾지 못해 대량의 404 Not Found 에러가 터져 나왔습니다.

원인은 파일명 바이트의 ‘태생적 한계’

리눅스 파일 시스템에서 파일 이름은 단순한 텍스트가 아니라 특정 인코딩 규칙으로 정렬된 ‘바이트 배열(Byte Array)’입니다.

  1. RHEL 5.8 환경에서 생성된 파일명은 디스크에 한글 글자당 2바이트 크기의 EUC-KR 규격으로 박혀 있습니다.
  2. 리눅스의 기본 tar 유틸리티는 압축할 때 파일명 내부의 문자 인코딩까지 최신식으로 바꿔주는 지능이 없습니다. 오직 원래 있던 EUC-KR 바이너리 상태 그대로 파일명을 복사해 압축합니다.
  3. 이 파일을 유니코드 체계인 RHEL 9.6(en_US.UTF-8)에서 풀면, 새 OS는 디스크에 기록된 2바이트 배열을 보며 “이건 당연히 3바이트짜리 UTF-8 구조겠지”라고 잘못 읽어 들입니다. 규칙성이 완전히 어긋나기 때문에 우리 눈에는 찌그러진 문자로 표현되는 것입니다.

반디집(Bandizip)을 활용한 파일명 일괄 교정 팁

이 문제를 해결하기 위해 데이터를 중간 지대인 윈도우 PC로 다운로드하여 파일명 코드페이지를 강제로 치환하는 우회 프로세스를 정립했습니다. 윈도우 환경에서 파일명 코드페이지 변환을 가장 안정적으로 지원하는 툴은 바로 반디집(Bandizip)입니다.

반디집 파일명 코드페이지 일괄 변경 순서

  1. 파일질라(FileZilla) 같은 FTP 툴을 이용해 기존 구형 서버의 EUC-KR 파일을 로컬 윈도우 PC로 다운로드합니다.
  2. 반디집 프로그램을 실행합니다.
  3. 상단 메뉴 바에서 [도구] ➡️ [파일명 코드페이지 변경…] 메뉴를 클릭합니다. (압축 파일이 아닌 일반 폴더 구조를 직접 바꿀 때는 이 도구 메뉴가 필수입니다.)
  4. 변환 세팅 창이 뜨면 다음과 같이 설정합니다.
    • 원본 코드페이지: 한국어 (949 / EUC-KR)
    • 대상 코드페이지: 유니코드 (65001 / UTF-8)
  5. 하단 미리보기 화면에서 깨져 있던 폴더와 파일 이름이 정상적인 한글로 잘 돌아오는지 체크합니다.
  6. [시작] 버튼을 누르면 내부 데이터 손상 없이, 파일 시스템 테이블상의 파일명 바이트 구조만 깔끔하게 UTF-8(글자당 3바이트 표준 유니코드)로 리네임 처리가 완료됩니다.

이렇게 변환된 표준 UTF-8 결과물들을 최종 인프라 환경으로 업로드하면 인코딩 장벽이 깔끔하게 해결됩니다.

미스터리: 왜 똑같은 로케일인데 RHEL 9에서만 깨질까?

반디집을 통해 유니코드(UTF-8) 파일명으로 세팅을 완벽히 마친 후, 검증을 위해 두 군데의 테스트 인프라 환경인 Rocky Linux 9.8 서버와 RHEL 9.6 서버에 각각 동일한 파일을 업로드했습니다. 두 서버 모두 locale 명령어를 쳤을 때 출력 결과값은 정확히 일치했습니다.

Bash

LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
# ... 이하 동일

그러나 결과는 완전히 판이하게 갈렸습니다.

  • Rocky Linux 9.8: 최종정산내역공문.pdf ➡️ 정상 노출
  • RHEL 9.6: E10__20170112164913_理醫...새 륙 롵 됲.pdf ➡️ 외계 한자로 처참하게 깨짐

성공한 서버와 실패한 서버의 파일 사이즈를 확인해 보니 완전하게 일치하는 동일한 파일이었습니다. 이때 리눅스용 파일명 인코딩 변환 유틸리티인 convmv 드라이 런(Dry-run) 테스트를 해보고 나서야 결정적인 힌트를 찾을 수 있었습니다.

Bash

[root@target-server]# convmv -f euckr -t utf-8 *
Starting a dry run without changes...
Skipping, already UTF-8: ./E10__20170112164913_理醫...새 륙 롵 됲.pdf
No changes to your files done.

convmv 엔진의 리턴 메시지는 Skipping, already UTF-8이었습니다. 시스템은 이 깨진 파일명이 문법상 완벽한 UTF-8 바이트 배열이라고 단정 짓고 변환을 거부한 것입니다.

범인은 바로 ‘이중 인코딩(Double Encoding)의 박제화’

이 현상의 본질은 파일 내부의 인코딩 문제가 아니었습니다. 업로드 매개체인 파일질라(FileZilla) 같은 클라이언트 프로그램이 파일 전송 레이어에서 개입해 발생한 “이중 인코딩 박제 현상”이었습니다.

윈도우 환경에서 파일질라를 통해 RHEL 9.6 서버에 접속할 때, 파일질라의 인코딩 프로토콜이 ‘자동 감지’ 과정에서 오작동을 일으킨 것입니다.

로컬 PC에 담겨 있던 파일명의 흔적을 업로드 스트림으로 보낼 때, 깨진 외계 문자 형태 구조 그 자체를 유효한 UTF-8 바이트 셋으로 변환하여 RHEL 9.6 스토리지 블록에 영구히 저장(Write)해 버린 것입니다.

그렇기 때문에 RHEL 9.6 OS 관점에서는 규격에 위반되지 않는 온전한 UTF-8 코드로 인식되어 convmv가 스킵해 버린 것이고, 정작 우리 눈에는 여전히 외계어로 렌더링되는 진퇴양난의 상황이 연출된 것입니다.

이중 인코딩으로 꼬인 한글 파일명 해결법 2가지

이렇게 시스템에 잘못 박제된 파일명 구조는 일반적인 명령어로 쉽게 풀리지 않습니다. 실제 프로젝트 현장에서 이 난관을 뚫어낸 핵심 엔지니어링 해법 2가지를 제안합니다.

해법 ①: convmv 스마트 감지 무력화 옵션 사용 (--nosmart)

convmv가 “이미 UTF-8 구조이다”라며 변환을 거부할 때는, 시스템의 자체 스마트 필터링 엔진을 끄고 강제로 역변환 매핑을 때리는 파라미터를 추가해야 합니다.

Bash

# 1. UTF-8 제어 코드를 무시하고 강제로 EUC-KR 구조로 되돌리는 시뮬레이션 (테스트)
convmv -f utf-8 -t euckr --nosmart *

# 2. 미리보기 로그 상에서 원래 타겟 명칭인 '최종정산내역공문' 복원이 확인되면 실제 반영 (--notest)
convmv -f utf-8 -t euckr --nosmart --notest *
  • 동작 원리: --nosmart 옵션은 파일명이 이중 인코딩으로 인해 겉모습만 UTF-8인 상태일 때, 시스템의 경고를 무시하고 강제로 바이트 셋 구조를 한 단계 전 단계로 되돌려 복원해 주는 특수한 파라미터입니다.

해법 ②: 이종 서버 간 직접 SCP 다이렉트 패킷 동기화 (가장 추천)

윈도우 환경과 파일질라 인터페이스를 거치면서 프로토콜 해석 오류로 파일명이 자꾸 오염된다면, PC로 다운로드하는 과정을 과감히 건너뛰어야 합니다.

이미 동일한 유니코드 환경인 Rocky Linux 9.8 서버 환경에서는 한글 파일명이 정석적으로 안착되어 표출되고 있으므로, 두 리눅스 서버 간에 보안 셸 프로토콜을 이용해 데이터를 다이렉트로 복사 동기화하는 것이 가장 깔끔한 정답입니다.

Bash

# RHEL 9.6 타겟 서버 터미널에 접속 후 실행
# Rocky 서버의 검증 완료된 청정 UTF-8 파일들을 네트워크를 통해 직접 땡겨옵니다.
scp -r root@[Rocky_서버_IP_주소]:/home/cjadmin/pdf_files/* /tmp/

리눅스 서버 환경 대 서버 환경으로 직접 데이터를 스트리밍하게 되면, 중간 클라이언트 툴들의 인코딩 간섭(Charset Intervention) 현상이 완전히 사라집니다. 덕분에 Rocky Linux에서 검증되었던 깔끔한 UTF-8 한글 명칭 구조가 RHEL 9.6 내장 파일 시스템 노드에도 100% 순수하게 복제 보존됩니다.

결론 및 인프라 표준화를 위한 제언

리눅스 엔터프라이즈 서버 업그레이드 시 발생하는 한글 파일명 깨짐 현상의 원인과 해결책을 깊이 있게 살펴보았습니다. 단순히 로케일 환경 변수(LANG) 값을 일치시켜 주는 작업은 마이그레이션의 아주 기초적인 단계에 불과합니다. 진짜 데이터 블록이 저장되고 전송되는 스트림 전반의 인코딩 라이프사이클을 완벽히 이해해야 데이터 오염을 막을 수 있습니다.

웹 애플리케이션 서비스 환경 관점에서도 이번 트러블슈팅 조치는 막대한 의미를 지닙니다. 파일명이 유니코드(UTF-8) 규격으로 올바르게 서버 스토리지에 안착되어야만, Nginx나 Tomcat 등 백엔드 진영의 웹 서버에서 파일 다운로드 기능을 처리할 때 부가적이고 지저분한 인코딩 하드코딩 로직(new String(bytes, "EUC-KR"))을 완전히 걷어내고 순수한 표준 아키텍처를 유지할 수 있습니다.

성공적인 인프라 이관을 위해, 대량의 파일 데이터를 옮길 때는 반드시 클라이언트 전송 세션 인코딩 값을 UTF-8 전용으로 고정하거나, 엔지니어링 콘솔 단에서 scp 또는 rsync 툴을 활용해 서버 간 직접 동기화하는 방식을 최우선 순위로 삼으시길 바랍니다.

전문가의 인사이트 : 함께 읽어 보세요.

회사 컴퓨터에서 리눅스 테스트 서버 만들기: WSL2 Rocky Linux 9 설치

참조 및 출처 URL:

https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/configuring_basic_system_settings/assembly_changing-basic-environment-settings_configuring-basic-system-settings