🚨 문제 상황

SELECT * FROM RAG_DOCUMENT_FILE
WHERE BOT_UID = 85
  AND IS_DELETED = 0
  AND ORIGINAL_FILENAME LIKE '%가이드%';
*-- 결과: 0건 (실제 데이터 존재함)*
  • 데이터가 있음에도, LIKE 문으로 조회하지 못 하는 문제가 발생함
    • 키워드를 영어로 넣었을 때는 문제가 없음 → 한글 관련 문제로 예상

환경

  • DB: MySQL 8.x
  • Collation: utf8mb4_general_ci
  • Character Set: 모두 utf8mb4

🔍 원인 분석

1. 초기 의심: 인코딩 문제?

HEX 값으로 저장 상태 확인

SELECT ORIGINAL_FILENAME, HEX(ORIGINAL_FILENAME) FROM RAG_DOCUMENT_FILE;

결과

  • 가이드.pdf → E18480E185A1E18480E185B5E18483E185B2…
    • 정상적인 UTF-8 인코딩이지만, NFD 형태임을 알게 됨

2. 핵심 발견: NFD vs NFC

정상적인 NFC (조합형)

글자HEX
EAB080
EC9DB4
EB939C

문제의 NFD (분해형)

글자HEX구성
E18480 E185A1ㄱ + ㅏ
E18480 E185B5ㅇ + ㅣ
E18483 E185B2ㄷ + ㅡ

3. 근본 원인: macOS의 파일 시스템

macOS는 파일명을 NFD (Normalization Form Decomposed) 형태로 저장함

  • 한글이 자모 단위로 분리됨 (가 → ㄱ + ㅏ)
  • Windows/Linux는 NFC (Normalization Form Composed) 사용 (가 → 가)

💡사용자가 macOS에서 파일을 업로드하면 NFD로 저장되고, 검색 시 키보드 입력은 NFC이므로 매칭되지 않는다.

[출처: 나무위키]

APFS는 파일 이름을 있는 그대로의 raw bytes를 저장하기에 유니코드 NFD, NFC 노멀라이제이션 방식에 상관없이 둘다 저장할 수 있다.

하지만 내부에서 모든 처리는 강제로 NFD로 이루어진다. 예를 들어, 파일 이름 조회 또는 변경 시 내부에서 무조건 NFD 형태로 변환하고 처리하도록 설계되어 있다. 따라서 NFC로 저장하더라도, 시스템 콜에는 NFD 노멀라이제이션을 적용하기에 한글 자모음 분리 현상이 나타날 수 있다.


✅ 해결 방법

파일 저장 시 NFC로 정규화

import java.text.Normalizer;
 
*/**
 * 파일명 정규화 (NFD -> NFC 변환, macOS 한글 자소 분리 문제 해결)
 */*
private String normalizeFilename(String filename) {
    if (StringUtils.isBlank(filename)) {
        return "unknown";
    }
    return Normalizer.normalize(filename, Normalizer.Form.NFC);
}

🛠 Java Normalizer API

normalize 메서드

/**
 * Normalize a sequence of char values.
 * The sequence will be normalized according to the specified normalization
 * form.
 * @param src        The sequence of char values to normalize.
 * @param form       The normalization form; one of
 *                   {@link java.text.Normalizer.Form#NFC},
 *                   {@link java.text.Normalizer.Form#NFD},
 *                   {@link java.text.Normalizer.Form#NFKC},
 *                   {@link java.text.Normalizer.Form#NFKD}
 * @return The normalized String
 * @throws NullPointerException If {@code src} or {@code form}
 * is null.
 */
public static String normalize(CharSequence src, Form form) {
    return NormalizerBase.normalize(src.toString(), form);
}
파라미터설명
src정규화할 문자열
form정규화 형식 (NFC, NFD, NFKC, NFKD)
반환값정규화된 문자열

정규화 형식 (Normalization Form)

Form이름설명예시 (가)
NFCCanonical Composition분해 후 조합 (표준 조합형)가 → 가 (1글자)
NFDCanonical Decomposition분해만 수행가 → ㄱ + ㅏ (2글자)
NFKCCompatibility Composition호환 분해 후 조합ℌ → H
NFKDCompatibility Decomposition호환 분해만 수행fi → f + i

🔗 레퍼런스

[Java] 파일 업로드 - MacOS 한글 자모음 분리

NAVER D2