어드민 html 업로드 깨짐 현상
개요
https://jira.tde.sktelecom.com/browse/MOON-7997
원인
- 업로드 파일이 CP949(ANSI) 인코딩으로 저장된 채 업로드되고 있었음
- CDN에 저장된 HTML 파일의 Content-Type이
text/html(charset 미지정) - HTML 내부에도
<meta charset>선언 없음 - 앱 WebView가 charset 힌트 없으면 CP949로 기본 해석 → UTF-8 파일 업로드 시 깨짐
FE → BFF FileController (POST /services/bos/file/uploadToTemp)
→ S3 BOS 버킷 임시 저장
→ Tday 저장 시 BFF FileService.copyToCDN()
→ S3 CDN 버킷 최종 저장 (Content-Type: text/html, charset 없음)
→ 앱 iframe 로드 → charset 불명 → 한글 깨짐
해결 방향
BFF 수정(전역 charset=UTF-8 적용)은 시스템 내 다른 14개 HTML 업로드 지점에서
기존 CP949 파일 재업로드 시 모두 깨지는 부작용이 있어 FE에서만 처리하기로 결정.
- 업로드 전 파일을 UTF-8로 변환
<meta charset="UTF-8">자동 주입으로 WebView가 서버 Content-Type과 무관하게 UTF-8로 해석
변경 파일
1. mps.bos-fe/src/components/form/ImageUpload.tsx
convertEncoding prop 추가 (기본값 false, 명시적으로 활성화한 곳에서만 동작)
// Props에 추가
convertEncoding?: boolean;
// uploadImage() 내 변환 처리
if (convertEncoding) {
file = await convertToUtf8(file);
}convertToUtf8 함수 추가
const convertToUtf8 = async (file: File): Promise<File> => {
const buffer = await file.arrayBuffer();
let text: string;
try {
new TextDecoder('utf-8', { fatal: true }).decode(buffer);
text = new TextDecoder('utf-8').decode(buffer);
} catch {
// UTF-8이 아님 → CP949로 디코딩
text = new TextDecoder('euc-kr').decode(buffer);
}
// <meta charset> 없으면 최상단에 주입 (<head> 없는 프래그먼트 구조도 대응)
if (!text.includes('<meta charset')) {
text = text.includes('<head>')
? text.replace('<head>', '<head>\n<meta charset="UTF-8">')
: '<meta charset="UTF-8">\n' + text;
}
const utf8Bytes = new TextEncoder().encode(text);
return new File([utf8Bytes], file.name, { type: file.type });
};동작 순서:
- 파일을 ArrayBuffer로 읽음
- UTF-8 유효성 검사 (
fatal: true) — 통과하면 UTF-8로 디코딩 - 실패하면 CP949로 디코딩
<meta charset="UTF-8">미포함 시 최상단 또는<head>직후에 주입- UTF-8로 재인코딩하여 새 File 객체 반환
2. mps.bos-fe/src/pages/tday/detailForm/PlusBenefitBannerCard.tsx
HTML 파일 업로드 fieldProps에 convertEncoding: true 추가
<FormItem
name={name(`htmlTempFileId`)}
label={'배너 상세 페이지 (APP)'}
type="FileUpload"
fieldProps={{
urlKey: name(`linkUrl`),
urlDefaultValue: field.landingType === 'HTML' ? field?.linkUrl : undefined,
fileExtensions: ['htm', 'html'],
convertEncoding: true, // ← 추가
}}
/>적용 범위
| 항목 | 내용 |
|---|---|
| 변환 대상 | PLUS 배너 HTML 업로드에만 적용 (convertEncoding: true 명시한 곳만) |
| 다른 페이지 영향 | 없음 (convertEncoding 기본값 false) |
| 기존 S3 파일 | 변경 없음. 재업로드 시 자동 변환됨 |
| 외부 라이브러리 | 없음 (브라우저 내장 TextDecoder / TextEncoder 사용) |
검토했지만 적용하지 않은 방안
| 방안 | 미적용 이유 |
|---|---|
BFF FileService.getContentType() charset=UTF-8 전역 추가 |
다른 14개 HTML 업로드 지점에서 CP949 파일 재업로드 시 깨짐 발생 |
| 운영 안내만 (UTF-8로 저장 후 업로드) | 근본 해결 아님, 담당자 실수 재발 가능 |
댓글
첫 번째 댓글을 남겨보세요.