점검 경계 · 2026년 5월 10일 · 8분
한국 사이트 첨부 파일명 깨짐 — RFC 6266 가이드
macOS·Linux·모바일 사용자가 한국 사이트에서 첨부를 받으면 파일명이 깨지는 이유와, 서버 헤더 한 줄 변경으로 해결하는 법.
Preflight 블로그의 첫 글은 자동 점검이 잡아낸 항목이 아니라, 자동 점검이 일부러 결과에 넣지 않는 경계에서 시작합니다. 한국 사이트의 첨부 파일명 깨짐은 사용자에게는 분명한 품질 문제지만, 정적 마크업 분석으로는 서버 응답 헤더를 볼 수 없습니다.
그래서 이 글은 “무엇을 고치면 되는가”와 함께 “왜 리포트 본문이 아니라 별도 가이드로 분리했는가”를 같이 공개합니다.
어떤 증상인가
한국 사이트에서 첨부 파일을 다운로드했을 때, 파일명이 다음과 같이 나오면 이 가이드 대상입니다.
%C0%CC%B7%C2%BC%AD.pdf처럼 percent string 그대로이뜨같이 글자가 깨진 mojibake- 저장은 되는데 한국어 부분이
____.pdf로 비어버림
Windows 사용자에겐 멀쩡하게 보입니다. 그래서 한국에서 만든 사이트는 이 증상을 발견하기 어렵습니다 — 만든 사람과 운영자가 대부분 Windows + Edge/Chrome 환경이거든요.
어떤 환경에서 깨지는가
깨짐은 macOS만의 문제가 아닙니다. Windows 외 운영체제·브라우저 조합에서 자주 발생합니다 — 정확한 양상은 서버가 보낸 헤더의 형태, 브라우저 버전, 파일명 문자에 따라 갈립니다.
- Windows (Edge / Chrome / IE) — 정상 표시되는 경우가 많음. Windows의 한국어 locale에서 CP949(MS949)로 해석되어 복원되는 동작.
- macOS (Safari / Chrome / Firefox) — 깨질 수 있음.
- Linux (Chrome / Firefox) — 깨질 수 있음.
- iOS (Safari / Chrome) — 깨질 수 있음.
- Android (Chrome / Samsung Internet) — 깨질 수 있음.
운영자 본인 기기에서 멀쩡하게 보여도 다른 운영체제·모바일 방문자에겐 깨진 파일명이 가는 셈입니다. 운영자 환경이 한정적이라 발견이 늦어지기 쉬운 패턴이에요.
왜 깨지는가
서버가 보내는 Content-Disposition 헤더가 원인입니다. 한국 서버는 IE·Edge 호환을 위해 파일명을 EUC-KR 또는 MS949(CP949)로 인코딩해 내보내는 관행이 있어요. 예를 들면:
Content-Disposition: attachment; filename="\xC0\xCC\xB7\xC2\xBC\xAD.pdf"여기서 \xC0\xCC\xB7\xC2\xBC\xAD는 이력서를 CP949(MS949)로 인코딩한 6 byte입니다. 본질적으로 HTTP 헤더의 일반 filename= 자리에 비-ASCII 바이트를 직접 넣는 건 표준이 정의하지 않은 방식이라, 클라이언트마다 해석이 갈립니다. Windows의 IE/Edge·Chrome은 시스템 codepage로 해석해 이력서.pdf로 복원하지만, 그 외 운영체제·브라우저 조합에서는 UTF-8 디코드 시도 후 깨진 글자나 raw byte string으로 노출되는 식으로 결과가 달라져요.
RFC 2616(1999) 시절에는 HTTP 응답의 Content-Disposition이 널리 쓰였지만 국제화 파일명 처리가 충분히 정리되어 있지 않았고, 이후 RFC 5987 / RFC 6266이 filename*=UTF-8''... 양식으로 표준적인 해결책을 제시했습니다. 한국에서 만든 서버 코드는 IE 호환 중심으로 자리잡은 시기가 길어 표준 양식으로 업데이트되지 않은 채 남아있는 경우가 많습니다.
해결 — RFC 6266 양식
헤더에 filename(legacy ASCII fallback)과 filename*=UTF-8''...(percent-encoded UTF-8) 두 form을 동시에 제공하면 모든 환경에서 정상 표시됩니다.
Content-Disposition: attachment;
filename="resume.pdf";
filename*=UTF-8''%EC%9D%B4%EB%A0%A5%EC%84%9C.pdffilename=— 표준을 모르는 구식 클라이언트용 ASCII fallback. 의미가 통하는 영문명을 권장 (예:resume.pdf).filename*=— RFC 5987 양식. 인코딩(UTF-8) + 언어 태그(생략 가능,'') + percent-encoded value.- 모던 브라우저는
filename*=를 우선 사용하고, 그 외에는filename으로 폴백합니다.
프레임워크별 적용
모던 프레임워크는 거의 자동으로 RFC 6266 양식을 만들어줍니다. 손으로 헤더를 조립하던 코드라면 라이브러리 호출 한 줄로 대체할 수 있어요.
- Express 4.16+ —
res.download(path, filename)또는res.attachment(filename)가 자동 처리. 직접 헤더를 쓰던 코드라면content-disposition패키지 사용. - Next.js (Route Handler) —
new Response(file, { headers: { "Content-Disposition": ... } })로 직접 만든다면content-disposition또는encodeURIComponent로 양식 조립. - Spring Boot —
ContentDisposition.attachment().filename(name, StandardCharsets.UTF_8).build()가 RFC 6266 양식을 출력. - Django 4.0+ —
FileResponse(file, as_attachment=True, filename=name)가 자동 처리. - Rails 6+ —
send_file(path, filename: name)또는send_data(data, filename: name)가 자동 처리. 구버전 Rails는 응답 헤더에filename*가 포함되는지 한 번 확인.
서버 앞에 nginx·Cloudflare·CDN 같은 reverse proxy가 있을 때 보통은 원본 Content-Disposition이 그대로 전달됩니다. 다만 CDN·오브젝트 스토리지의 다운로드 옵션이나 presigned URL 파라미터로 이 헤더를 덮어쓰는 경우가 있으므로, 최종 응답 헤더 기준으로 한 번 확인하는 편이 안전합니다.
확인하는 법
배포 후에는 macOS Safari·Chrome 또는 모바일 브라우저로 다운로드를 한 번만 시도해보세요. 같은 파일을 Windows + Edge/Chrome 에서만 확인하면 깨진 환경을 영영 못 만납니다.
- macOS Safari로 받아 파일명에 한국어가 정상 표시되면 통과.
- curl로 헤더 직접 확인:
curl -I <url>후Content-Disposition에filename*=양식이 있는지 확인.
Preflight에서의 위치
이 항목은 접근성 점검 모듈의 “이 점검이 보지 못한 것” 섹션에 boundary로 표시되어 있습니다. 자동 점검은 정적 마크업을 보지만, 다운로드 파일명은 서버 응답 헤더에서 결정되어 자동 점검의 시야를 벗어납니다.
또한 다운로드 파일명 깨짐은 엄밀히 말하면 접근성 문제가 아니라 한국어 환경 국제화(i18n) 문제입니다. 점검 결과에 포함하지 않고 별도 블로그 글로 분리한 이유입니다 — 접근성 점검은 자동 검출 가능한 영역만 다루고, 이렇게 자동 검출이 어려운 한국 사이트 패턴은 제품 블로그에서 기준과 해결법을 따로 공개합니다.