Mac에서 Windows로 보낸 한글 파일명이 깨지는 이유: 자소 분리(NFD) 현상과 convmv 해결책
Mac을 메인 개발 장비로 사용하면서 Windows나 Linux 환경의 협업자들과 파일을 주고받을 때, 혹은 크로스 플랫폼 빌드 파이프라인을 구축할 때 누구나 한 번쯤 겪는 골치 아픈 문제가 있다. 바로 한글 파일명이 ㅎㅏㄴㄱㅡㄹ.txt처럼 자음과 모음이 하나씩 분리되어 깨지는 현상이다.
단순히 파일명이 보기 싫게 깨지는 것에 그치지 않고, 형상 관리 도구(Git)가 파일명이 바뀌었다고 인식해 혼란을 주거나 로컬에서 잘 돌아가던 빌드 스크립트가 파이프라인에서 파일 경로를 찾지 못해 실패하는 등 실무적인 개발 워크플로우에 직접적인 오작동을 유발한다.
이번 글에서는 macOS 파일 시스템의 특징인 자소 분리(NFD) 현상의 기술적 배경을 살펴보고, 터미널 환경에서 convmv 도구를 활용해 이 문제를 우아하게 해결하는 방법과 코드 수준에서 대응하는 가이드를 정리해 본다.
1. 왜 Mac에서 만든 파일은 Windows에서 풀어헤쳐질까: NFD vs NFC
이 문제의 근본적인 원인은 운영체제(OS)가 유니코드(Unicode) 문자열을 정규화(Normalization)하는 표준 방식의 차이에 있다. 유니코드 표준은 한글처럼 결합이 가능한 문자를 저장하고 렌더링하기 위해 크게 두 가지 정규화 공식을 정의하고 있다.
NFD (Normalization Form Decomposition - 자소 분리형)
NFD는 하나의 완성된 글자를 초성, 중성, 종성이라는 각각의 자모 코드 포인트로 분해하여 저장하는 방식이다. macOS는 시스템 파일 시스템 레벨(과거 HFS+부터 현재의 APFS까지)에서 이 NFD 정규화를 기본 규격으로 채택하고 있다.
예를 들어 Mac에서 한이라는 글자로 파일명을 저장하면, 파일 시스템 내부에는 문자 한이 아니라 ㅎ(U+1106) + ㅏ(U+1161) + ㄴ(U+11AB)의 조합으로 데이터가 기록된다. macOS 화면에서는 이를 다시 실시간으로 합쳐서 보여주므로 Mac 사용자 본인은 깨짐을 인지하지 못한다.
NFC (Normalization Form Canonical Composition - 자소 결합형)
NFC는 결합 가능한 자모음들을 하나의 완성된 단일 문자 코드 포인트로 결합하여 저장하는 방식이다. Windows와 대부분의 Linux 파일 시스템은 이 NFC 방식을 표준으로 삼고 있다.
예를 들어 Windows에서 한을 저장하면 내부적으로 하나의 코드 포인트인 한(U+D55C)으로 깔끔하게 저장된다.
정규화 충돌이 만드는 파장
문제는 Mac에서 생성된 NFD 기반의 파일이 메일 첨부, 메신저 전송, 압축 파일(ZIP) 혹은 클라우드 업로드 등을 통해 Windows 환경으로 넘어갈 때 발생한다. Windows 시스템은 NFD 형태로 풀어헤쳐진 파일명을 수신했을 때 이를 자동으로 호환성이 높은 NFC 포맷으로 재구성해주지 않는다. 파일 시스템에 적힌 코드 포인트 그대로 문자 렌더링 엔진에 넘겨버리므로, Windows 사용자의 화면에는 ㅎㅏㄴㄱㅡㄹ처럼 자모고 분리된 형태가 그대로 노출되는 것이다.
2. 크로스 플랫폼 개발 환경에서 발생하는 자소 분리 이슈
단순히 문서 파일명이 깨져서 상대방에게 부끄러워지는 수준이라면 해프닝으로 넘길 수 있다. 하지만 개발 워크플로우 내에서 이 정규화 방식의 불일치는 예상치 못한 문제를 야기한다.
Git 형상 관리에서의 혼선
Git은 기본적으로 파일명을 바이너리 바이트 단위로 인식하여 추적한다. Mac 개발자가 로컬에서 검색화면.png라는 파일을 추가해 푸시했는데, Windows 개발자가 이를 풀(Pull)받아 변경 사항을 적용하는 과정에서 파일명이 NFC 형태로 덮어써지면, Git은 파일 내용이 전혀 바뀌지 않았음에도 파일명이 미세하게 변경(NFD $\rightarrow$ NFC)된 것으로 감지하여 변경 이력(Diff)을 새로 생성한다. 이로 인해 불필요한 커밋이 쌓이고 충돌(Conflict)이 발생하기 쉬운 환경이 된다.
빌드 파이프라인과 CI/CD 장애
웹 프론트엔드나 iOS 앱 빌드 과정에서 이미지 리소스나 마크다운 콘텐츠 파일 이름을 한글로 명명하는 경우가 종종 있다. Mac 로컬 장비에서는 번들이 정상적으로 묶이고 실행되던 프로젝트가, 리눅스 기반의 컨테이너 환경(GitHub Actions 등)으로 구성된 CI/CD 파이프라인에 들어가면 특정 리소스 파일을 읽지 못하는 에러를 뱉으며 빌드 에러가 나곤 한다. 이는 소스 코드 내부 문자열 상수(NFC 형식의 "검색화면.png")와 파일 시스템의 실제 이름(NFD 형식의 ㄱㅓㅁㅅㅐㄱㅎㅘㅁㅕㄴ.png)의 해시값(바이트 배열)이 일치하지 않아 발생한다.
3. 터미널에서 우아하게 파일명 정규화하기: convmv 활용법
로컬 환경이나 빌드 배포 단계에서 자소 분리된 한글 파일명을 일괄적으로 Windows 호환(NFC) 포맷으로 변환해 주는 표준적인 터미널 도구가 바로 convmv이다. 이 도구는 파일 인코딩 및 유니코드 정규화 상태를 실시간으로 모니터링하고 일괄 수정하는 데 매우 유용하다.
1단계: convmv 설치
macOS 환경에서 터미널을 열고 패키지 관리 도구인 Homebrew를 통해 간단하게 설치할 수 있다.
brew install convmv
2단계: 파일명 복구 테스트 실행 (Dry-run)
convmv는 파일 시스템의 이름을 직접 변경하는 위험성이 있는 도구이므로, 실행 전 미리 변경 예상 목록을 안전하게 보여주는 시뮬레이션 기능이 기본값으로 작동한다.
convmv -f utf8 -t utf8 --nfc -r [변경을_원하는_디렉토리_경로]
-f utf8 -t utf8: UTF-8 포맷의 인코딩 상태를 그대로 유지하겠다는 선언이다.--nfc: 파일명의 정규화 규칙을 완성형(NFC) 형태로 바꾸겠다는 핵심 옵션이다.-r: 지정된 디렉토리 하위의 모든 폴더와 파일을 탐색하여 재귀적으로 처리한다.
명령어를 실행하면 실제로 파일명이 바뀐 것은 아니지만 아래처럼 미리 변경 대상 목록을 스크린에 출력해 준다.
mv "./src/assets/ㄱㅓㅁㅅㅐㄱㅎㅘㅁㅕㄴ.png" "./src/assets/검색화면.png"
No changes to your files performed. Use --notest to run for real.
3단계: 실제 파일명 강제 변경 적용
시뮬레이션 결과를 확인하고 변경 사항이 안전하다고 판단되면 명령어 끝에 --notest 플래그를 덧붙여 실제 파일 시스템 반영을 강제 실행한다.
convmv -f utf8 -t utf8 --nfc -r --notest [변경을_원하는_디렉토리_경로]
이 단계를 거치고 나면 폴더 내 분리되어 있던 모든 한글 파일들이 완성형 문자 코드로 정상 결합되어, Windows나 빌드 컨테이너 환경으로 옮겨가도 절대 깨지지 않는 상태가 된다.
4. Finder ‘빠른 동작’에 convmv 스크립트 심기
매번 터미널을 켜서 경로를 입력하는 것은 생산성 관점에서 비효율적이다. macOS의 내장 기능인 Automator를 활용하면 파일 매니저(Finder)에서 깨진 파일이나 폴더를 마우스 우클릭하는 것만으로 손쉽게 NFC로 변환할 수 있는 시스템 단축 동작을 만들 수.
- Mac에서 Automator (자동화) 앱을 실행한 뒤 문서 유형으로 **‘빠른 동작(Quick Action)‘**을 생성한다.
- 워크플로우 상단에서 ‘현재 수신하는 작업흐름’을 **‘파일 또는 폴더’**로 선택하고, 대상 애플리케이션을 **‘Finder’**로 지정한다.
- 동작 라이브러리 목록에서 **‘셸 스크립트 실행(Run Shell Script)‘**을 검색해 오른쪽 레이아웃으로 드래그한다.
- 통과 입력(Pass input) 설정을 ‘입력값(stdin)’ 대신 **‘인수로(as arguments)‘**로 바꾼다.
- 스크립트 입력창에 아래 코드를 복사하여 기입한다. (M1 이상 Apple Silicon Mac의 Homebrew 경로 기본값인
/opt/homebrew를 기준으로 작성되었다.)for i in "$@"; do /opt/homebrew/bin/convmv -f utf-8 -t utf-8 --nfc --notest "$i" done - 워크플로우 이름을
한글 자소 합치기등의 명확한 명칭으로 저장한다.
이제 Finder에서 자소 분리가 의심되는 임의의 파일이나 폴더를 선택하고 우클릭한 뒤, **[빠른 동작] $\rightarrow$ [한글 자소 합치기]**를 선택해 주면 백그라운드에서 즉시 안전하게 NFC 변환이 처리된다.
5. 코드 수준에서의 예방: iOS 및 웹 개발자를 위한 유니코드 정규화
빌드나 파일 시스템 차원을 넘어, 직접 작성하는 코드 내부에서 유니코드 정규화 불일치를 예방하는 방법도 명확하게 인지해 두어야 한다. 사용자가 Mac 환경에서 웹 브라우저나 앱 인풋 창을 통해 업로드한 파일 이름 혹은 텍스트 정보를 그대로 백엔드 DB나 스토리지에 적재할 경우, 데이터 정합성에 틈이 생길 수 있기 때문이다.
Swift (iOS / macOS App Development)
Foundation 프레임워크 내 NSString 클래스는 문자열 정규화를 쉽게 처리해 주는 메서드를 내장하고 있다. iOS 개발자 입장에서는 사용자가 입력한 문자열이나 로컬 파일명 패스를 가공할 때 다음과 같이 명시적인 정규화를 수행할 수 있다.
let inputFilename = "ㄱㅓㅁㅅㅐㄱㅎㅘㅁㅕㄴ.png" // 사용자가 입력한 NFD 문자열 가정
// NFC 완성형 구조로 정규화 변환
let normalizedFilename = inputFilename.precomposedStringWithCanonicalMapping
// 반대로 NFD로 풀어헤치려면
let decomposedFilename = inputFilename.decomposedStringWithCanonicalMapping
JavaScript / Node.js
웹 환경에서도 문자열 비교나 파일 전송 로직 수행 전에 유니코드 표준 정규화 메서드인 normalize() 함수를 호출하여 통일성을 유지할 수 있다.
const userFilename = "ㄱㅓㅁㅅㅐㄱㅎㅘㅁㅕㄴ.png";
const cleanFilename = userFilename.normalize("NFC"); // 완성형 변환
Python (Build Script & Automation)
자동화 툴이나 헬퍼 스크립트를 빌드 파이프라인에 이식할 때 파이썬 표준 라이브러리 unicodedata를 활용해 일관성을 부여할 수 있다.
import os
import sys
import unicodedata
def sanitize_nfc(path):
return unicodedata.normalize('NFC', path)
그래서 무엇부터 보면 좋을까
- 현재 개발 중인 크로스 플랫폼 프로젝트의 자소 분리 여부 확인: Mac 로컬 환경에 한글 파일명이 포함되어 있다면 Windows 빌드 시 에러 가능성이 없는지 먼저 검토한다.
- Homebrew를 통한 convmv 설치: CLI 환경에서 유연하게 대처할 수 있도록 로컬 컴퓨터에 설치한다.
- Git Config 인코딩 관련 속성 점검: Git 파일명 대소문자 구분 설정(
core.ignorecase)과 더불어 한글 파일명 변경 감지가 예민하게 작동하고 있는지 확인한다. - Automator 단축 액션 등록: Finder 우클릭 동작을 미리 만들어 두면 불필요한 터미널 사용을 최소화할 수 있다.
- 앱 및 서버의 유니코드 정상화 검토: 외부 업로드 파일명이 유입되는 인터페이스 구간에
NFC정규화 코드(예:.precomposedStringWithCanonicalMapping등)가 잘 적용되었는지 밸리데이션 검사 방식을 점검한다.
마무리
Mac 사용자들끼리만 일할 때는 수면 위로 잘 드러나지 않는 문제이지만, 크로스 플랫폼 협업이나 클라우드/컨테이너 빌드 자동화가 보편화된 요즘에는 언제든 장애 요소로 돌변할 수 있는 것이 바로 이 한글 자소 분리 문제이다.
단순히 파일명을 영어로 짓는 소극적인 회피 수단보다, 유니코드 정규화(NFD와 NFC)의 기술적 특성을 명확히 이해하고 convmv나 코드 레벨의 정규화 파이프라인을 이식하는 성숙한 대응 체계를 구축하는 것이 궁극적인 해결책이다. 지금 사용 중인 협업 리포지토리의 파일명들을 점검하는 것부터 시작해 보는 것을 추천한다.
출처
- Unicode Consortium - Unicode Normalization Forms
- 유니코드 표준에서 정의하는 NFD, NFC 정규화 양식의 명세 및 원리 참고
- Apple Developer - precomposedStringWithCanonicalMapping
- Swift 및 Objective-C 환경에서 Foundation 프레임워크를 이용한 NFC 변환 API 명세 참고
- Homebrew Formulae - convmv
- Homebrew 환경 하의 convmv 라이브러리 설치 가이드 정보 참고
- convmv 공식 홈페이지
- convmv 도구의 매뉴얼 및 옵션 구조 정보 참고
- Bandisoft - 반디네이머 공식 페이지
- 자소 분리 현상 개선용 일반 사용자용 유틸리티 참고
이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.