PARA/02_Areas/A006_회사 업무/BOS-FE/어드민 html 업로드 깨짐 현상.md

어드민 html 업로드 깨짐 현상

개요

https://jira.tde.sktelecom.com/browse/MOON-7997

원인

  1. 업로드 파일이 CP949(ANSI) 인코딩으로 저장된 채 업로드되고 있었음
  2. CDN에 저장된 HTML 파일의 Content-Type이 text/html (charset 미지정)
  3. HTML 내부에도 <meta charset> 선언 없음
  4. 앱 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 });
};

동작 순서:

  1. 파일을 ArrayBuffer로 읽음
  2. UTF-8 유효성 검사 (fatal: true) — 통과하면 UTF-8로 디코딩
  3. 실패하면 CP949로 디코딩
  4. <meta charset="UTF-8"> 미포함 시 최상단 또는 <head> 직후에 주입
  5. UTF-8로 재인코딩하여 새 File 객체 반환

2. mps.bos-fe/src/pages/tday/detailForm/PlusBenefitBannerCard.tsx

HTML 파일 업로드 fieldPropsconvertEncoding: 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로 저장 후 업로드) 근본 해결 아님, 담당자 실수 재발 가능

댓글

첫 번째 댓글을 남겨보세요.