먼저, 글자가 깨진 것과 열이 밀린 것을 분리합니다

한글 CSV를 열었는데 이름이 낯선 기호로 보인다면, 곧바로 셀 안의 글자를 고치기보다 파일을 읽는 단계를 나눠 보세요. 디스크에는 바이트가 있고, 프로그램은 인코딩 규칙으로 바이트를 문자로 바꾼 뒤 쉼표와 따옴표를 해석합니다. 글자 해석이 틀린 상태에서 열 구분부터 조정하면 주소는 읽히지만 이름만 달라지는 불완전한 결과가 남을 수 있습니다. 반대로 글자는 정상인데 한 행이 여러 열로 갈라졌다면 인코딩보다 구분자나 따옴표의 문제를 먼저 봐야 합니다.

이 글은 동일한 가상 한글 자료를 세 가지 방식으로 저장하고 실제 바이트와 첫 머리글을 대조한 기록입니다. 특정 엑셀 버전의 열기 동작을 시험한 글은 아닙니다. CSV 생성 도구가 어떤 인코딩을 사용했는지 알고 있는 조건에서, 읽는 쪽의 선택이 값에 어떤 영향을 주는지 Python으로 확인했습니다. 원래 한글이 이미 물음표로 저장된 파일을 이 절차만으로 복구할 수 있다는 뜻도 아닙니다. 인코딩을 잘못 선택한 것과 저장 단계에서 문자가 사라진 것은 해결 방법이 다릅니다.

원본 파일을 보존하고, 복사본 하나에서 첫 몇 줄을 확인하세요. 파일명 끝의 csv는 인코딩 표시가 아닙니다. 내려받은 사이트의 언어, 작업자의 운영체제, 문서가 만들어진 나라만으로 규칙을 확정하지 않습니다. 먼저 생성 도구의 내보내기 설정이나 제공 문서에서 UTF-8·CP949 같은 명시적 정보를 찾고, 그 정보와 바이트 검사가 맞는지 확인하는 순서가 좋습니다.

같은 두 행을 세 가지 바이트열로 만들었습니다

입력은 코드와 이름이라는 두 열, K-21과 K-22라는 두 식별자, 바다와 한빛이라는 가상 이름입니다. 같은 문자열을 UTF-8, BOM을 붙인 UTF-8, CP949로 각각 인코딩했습니다. 별도로 영문과 숫자만 들어 있는 ASCII 사례도 만들었습니다. 실제 고객 정보는 쓰지 않았으며, 결과 파일에는 각 사례의 바이트 수와 앞부분의 16진수, 전체 SHA-256, 선택한 디코더와 복원된 행이 함께 들어 있습니다. 이번 실험에서 중요한 것은 파일 크기 자체보다 같은 의미가 다른 바이트로 표현된다는 점입니다.

코드,이름
K-21,바다
K-22,한빛

복원 뒤에는 화면에 비슷하게 보이는지만 확인하지 않았습니다. 머리글 이름과 각 행의 사전을 비교해 세 가지 입력이 동일한 구조와 값으로 돌아오는지 검사했습니다. 읽는 인코딩을 알고 있는 사례에서는 세 결과가 일치했습니다. 여기서 인코딩을 선택한 근거는 정답이 포함된 가상 입력 계약입니다. 여러 디코더를 실행해 가장 자연스러워 보이는 문장을 자동으로 고르는 감지기를 만든 것이 아닙니다. 이 구분이 없으면 재현 코드가 실제보다 더 넓은 자동 판별 능력을 가진 것처럼 보입니다.

실행 코드는 파일을 덮어쓰지 않고 메모리에서 바이트를 생성합니다. 먼저 아래 결과의 실험 이름과 실행 시각을 확인하고, 원하는 사례의 앞부분 바이트를 원본 복사본과 비교해 보세요. 해시는 원본과 복사본이 같은지 확인하는 데 쓰는 값입니다. 해시가 있다고 문장의 뜻이나 생성 도구의 설명이 참으로 검증되는 것은 아닙니다.

BOM은 글자 깨짐보다 머리글 불일치로 나타날 수도 있습니다

UTF-8 BOM은 시작 부분의 EF BB BF 바이트입니다. 이 표식이 있는 파일을 일반 UTF-8로 디코딩하면 문자열 맨 앞에 U+FEFF가 남을 수 있습니다. 실험에서도 첫 머리글이 눈에 보이는 코드 두 글자만이 아니라, 앞에 보이지 않는 문자 하나가 붙은 값으로 읽혔습니다. 사람이 보기에는 같은 열 이름인데 코드에서 정확히 일치하는 키를 찾지 못하는 상황입니다. 따라서 한글이 정상으로 보인다는 이유로 디코딩이 끝났다고 판단하면 머리글 대응 단계에서 원인을 놓칠 수 있습니다.

Python의 utf-8-sig 디코더는 처음의 서명을 처리합니다. BOM이 확인된 사례에서 이를 사용하자 기대한 머리글로 돌아왔습니다. 그렇다고 본문 전체에서 U+FEFF를 일괄 삭제하지는 않습니다. 자료 중간에 있는 문자는 별도의 의미나 잘못된 결합 흔적일 수 있으므로 위치와 생성 경로를 확인해야 합니다. 첫 세 바이트를 확인해 적용하는 규칙과, 보이지 않는 문자를 모두 없애는 정리는 다른 작업입니다. 이 글의 코드는 앞부분 BOM을 조건으로 해당 디코더를 선택합니다.

확인 대상관찰다음 판단
BOM 포함 UTF-8처음 EF BB BF서명 처리 후 머리글을 대조
BOM 없는 UTF-8이 표식 없음제공된 인코딩 계약을 확인
본문 중간 U+FEFF파일 시작이 아님일괄 삭제하지 않고 위치를 조사

디코딩이 성공했다는 사실만으로 인코딩이 확정되지는 않습니다

영문과 숫자만 있는 추가 사례는 UTF-8과 CP949 양쪽에서 엄격한 디코딩을 통과했습니다. 같은 바이트를 다시 인코딩해도 원래와 일치했습니다. 이 결과는 어느 쪽이 진짜 생성 규칙인지 알려 주지 않습니다. ASCII 범위의 일부 바이트가 여러 인코딩에 공통으로 해석되기 때문입니다. 따라서 여러 후보 중 오류가 나지 않은 첫 번째를 고르는 방법은 출처가 다른 파일들을 안정적으로 구분하는 근거가 되기 어렵습니다. 값이 짧을수록 후보가 겹치는 상황도 쉽게 놓칩니다.

한글 사례에서는 후보별 성공 여부와 머리글을 함께 기록했습니다. 잘못 고른 디코더가 오류를 일으키면 그 선택을 배제할 수 있지만, 오류가 없으면 정답이라고 선언하지 않습니다. 바이트 왕복 일치 역시 내부 변환의 일관성을 확인하는 검사일 뿐, 글자가 원래 의도한 이름인지를 보장하지 않습니다. 제공자가 UTF-8이라고 명시한 파일이 조건을 통과하지 못한다면 임의로 다른 규칙으로 저장하기보다 원본을 다시 받거나 내보내기 설정을 확인하는 편이 추적하기 쉽습니다.

반복 수신하는 자료라면 인코딩을 파일별 추측값으로 남기지 말고 제공 경로의 계약으로 관리하세요. 예를 들어 같은 공급자가 매일 보내는 파일이라도 경로·버전이 바뀌면 설정을 다시 확인하도록 정할 수 있습니다. 계약이 없는 자료는 보류로 분리하고, 확인한 담당자와 근거를 기록하면 다음 작업자가 같은 추측을 반복하지 않아도 됩니다.

replace와 ignore는 복구 기능이 아니라 손실을 선택하는 정책입니다

같은 가상 이름 뒤에 파도 이모지를 붙여 CP949로 인코딩하는 추가 실험을 했습니다. strict에서는 표현할 수 없는 문자를 만나 오류가 발생했습니다. replace에서는 코드가 진행됐지만 이모지가 물음표로 바뀌었습니다. 예외가 사라진 뒤에도 내용은 이미 달라진 상태입니다. 이 출력만 남기고 원본을 버리면 나중에 그 물음표가 실제 물음표였는지, 다른 글자가 대체된 것인지 구분할 수 없습니다. 정상 종료와 정보 보존을 같은 통과 기준으로 두지 않아야 하는 이유입니다.

같은 가상 문자열 “바다 🌊”에 ignore도 실제로 적용했습니다. strict는 오류, replace의 출력은 “바다 ?”, ignore의 출력은 “바다 ”였습니다. 마지막 출력에는 원래 공백은 남지만 이모지와 대체 표시는 모두 없습니다. 결과 파일의 ignoreOutput과 ignoredCharacterCount=1에서 이 차이를 확인할 수 있습니다. 파일 끝 공백이 눈에 잘 보이지 않아도 길이로 비교하면 원래 네 코드 포인트에서 세 개로 줄었습니다.

ignore는 예외를 없앤 것이 아니라 표현할 수 없는 정보를 지우는 정책입니다. 고객명·상품 코드·주소처럼 원문 보존이 필요한 열에 적용하면 서로 다른 원문이 같은 값으로 합쳐질 수 있습니다. 이번 세 정책 비교는 실제 수신 파일을 수정하지 않고 같은 합성 문자열로 실행했습니다. 변환이 필요해도 대상 열과 손실 허용 조건을 정하고 원본을 별도로 남겨야 합니다.

업무에서 대체 처리가 필요한 경우에도 원본 바이트의 위치, 오류 종류, 대체 전후 값을 별도 원장에 남기는 편이 좋습니다. 읽을 수 없는 한 행 때문에 전체 배치를 멈출지, 그 행만 격리할지도 업무 규칙입니다. 이번 인코딩 선택 실험은 어느 정책이 모든 업무에서 옳은지 결정하지 않습니다. 다만 조용히 통과시키면 무엇을 잃었는지 관찰할 기회도 줄어든다는 점을 확인합니다.

문자를 복원한 다음에 CSV 구조와 식별자를 검사합니다

디코딩이 끝나면 이제 CSV 파서가 필요한 단계입니다. 쉼표로 직접 나누면 필드 안의 쉼표나 따옴표, 줄바꿈을 잘못 처리할 수 있습니다. 이번 입력은 인코딩 차이를 고립시키기 위해 단순하게 만들었지만, 실제 가져오기에서는 문자 해석 검사와 CSV 문법 검사를 따로 두어야 합니다. Python 문서의 newline 설정도 이 단계의 읽기 규칙에 관한 내용입니다. 올바른 인코딩을 선택했다고 해서 행 수나 열 수, 따옴표 처리까지 자동으로 맞아지는 것은 아닙니다.

다음으로 코드 열이 문자열 상태로 보존됐는지 봅니다. 한국어가 잘 보이는데 긴 번호나 앞자리 0이 사라졌다면 새로운 문제입니다. 그 경우에는 식별자와 숫자를 나눠 가져오는 기준으로 이동하세요. 원본과 수정본을 서로 다른 열 순서로 받은 뒤 값까지 대조해야 한다면 ID별 재고 변경 대조 실험이 이어집니다. 인코딩 검사는 그 앞단이며, 자료의 의미와 업무 값까지 모두 확인하는 최종 승인 단계는 아닙니다.

검수표에는 파일 바이트 수, 인코딩 근거, 첫 머리글, 예상 열 이름, 정상 행 수, 격리 행 수를 구분해 적을 수 있습니다. 한 칸의 성공 여부로 합치지 않으면 어디에서 문제가 생겼는지 더 빨리 좁힐 수 있습니다. 특히 변환된 CSV를 다시 저장한 뒤에는 처음 바이트와 달라지는 것이 정상일 수 있으므로, 원본 동일성과 의도한 값 보존을 별도로 판단하세요.

변환 결과와 함께 선택 근거를 넘깁니다

다른 사람에게 파일만 전달하면 수신자는 다시 더블클릭하고 같은 문제를 만날 수 있습니다. UTF-8이라는 설명, BOM 포함 여부, 구분자, 줄바꿈 방식과 코드 열을 문자열로 다뤄야 한다는 조건을 함께 넘기는 편이 좋습니다. 문서에는 결과가 정상으로 보였다는 문장보다 확인한 열 이름과 행 수를 적으세요. 생성 도구의 내보내기 설정 화면을 보았다면 그 설정의 범위도 남깁니다. 파일 하나를 확인한 것인지, 이후 생성되는 모든 파일에 같은 설정이 적용되는지는 별개입니다.

문제 원인을 확정하지 못한 상태에서 변환본을 유일한 원본처럼 이름 붙이지 않는 것도 중요합니다. 원본·확인용 복사본·의도적으로 재인코딩한 결과를 구분하고, 변환 전에 사용한 규칙을 기록해 두세요. 파일을 다시 열 때마다 깨진 문자열을 복구하려고 여러 인코딩으로 연속 저장하면 처음의 바이트와 작업 순서를 잃기 쉽습니다. 이번 실험의 SHA-256도 이 경계를 보존하기 위한 수단이지 복원 버튼을 대신하는 값이 아닙니다.

자료 제공자에게 확인할 질문은 구체적일수록 좋습니다. 어떤 프로그램의 어떤 내보내기 옵션인지, 인코딩을 지정했는지, 오류가 나는 행이 원본에서도 같은지, 제공한 해시가 일치하는지를 물을 수 있습니다. 막연히 한글을 고쳐 달라고 요청하는 것보다 원인이 저장 단계인지 읽기 단계인지 나누는 데 도움이 됩니다.

자동 판별을 약속하지 않고 멈추는 조건을 정합니다

이 실험은 알고 있는 세 인코딩의 작은 가상 문자열을 비교한 것입니다. 모든 CP949 파일, 인코딩이 섞인 문서, 네트워크 전송 중 일부 바이트가 빠진 파일이나 여러 번 잘못 저장된 자료를 복구했다고 말할 수 없습니다. 글꼴에 필요한 문자가 없어 네모로 보이는 현상도 디코딩 오류와 구분해야 합니다. 다른 프로그램에서 원래 문자가 정상으로 보이는지, 문자열의 코드 포인트가 무엇인지 확인해야 그 경우를 좁힐 수 있습니다.

실제 작업에서는 계약과 바이트가 모순되거나, 머리글이 기대와 다르거나, 변환 중 대체가 생겼을 때 자동 처리를 멈추도록 정하세요. 겉보기 정상이라는 이유로 다음 단계에 넘기는 것보다 보류 이유를 짧게 기록하는 편이 후속 수정에 유리합니다. 검수 대상은 이름이 예쁘게 보이는지뿐 아니라 어떤 규칙으로 어떤 값을 얻었는지입니다. 아래 코드와 결과를 대조한 뒤에는 같은 입력을 다른 규칙으로 읽었을 때의 실패도 한 번 확인해 보세요.

입력과 결과를 직접 대조하는 재현 자료

실험 실행일은 2026년 10월 15일이며 이 글의 발행 일정과 구분합니다. Python 3 표준 라이브러리로 가상 자료만 처리했습니다. 아래 파일은 기존 사용자 파일을 인자로 받지 않습니다.

python3 utf8-bom-cp949-import-lab-20261015.py

실행 시각과 런타임은 환경에 따라 달라집니다. 판정 근거는 다운로드 결과의 고정 입력과 각 사례의 실제 출력으로 확인하세요.

규칙을 확인한 공식 원문

아래 원문은 2026년 10월 15일 확인했습니다. 형식과 동작의 설명을 확인한 출처이며, 이 글의 가상 실험이나 실제 업무 성과를 대신 증명하지 않습니다.