
PHP ftp_put 고정장 전문 오프셋 깨짐 완전 해결
범인은 FTP_ASCII 모드였다
PHP FTP 전송 · 고정장(Fixed-Length) 전문 · CRLF 트러블슈팅
개발을 하다 보면 "범인이 왜 거기서 나와?" 싶은 황당한 트러블슈팅을 만날 때가 있습니다. 특히 레거시 시스템과 연동하거나, 바이트 단위의 칼 같은 정확성을 요구하는 고정장(Fixed-Length) 전문을 다룰 때면 더욱 그렇죠. 이번 글에서는 리눅스 서버에서 윈도우 서버로 고정장 전문 파일을 FTP 전송할 때 발생한 치명적인 오류와, 그 허탈하지만 확실한 해결 방법을 공유합니다.
01 발단 — 전문 오프셋이 뒤로 갈수록 밀린다
리눅스 서버에서 생성한 픽스드 랭스(Fixed-Length) 전문 텍스트 파일을 윈도우 서버로 FTP 전송하는 연동 작업 중이었습니다. 전송 후 타겟 서버에서 파일을 읽어 들이는데, 문자열이 뒤로 갈수록 밀리면서 규격이 완전히 깨져버리는 현상이 발생했습니다.
🚨 증상 요약
고정장 전문 파일의 각 필드 오프셋(시작 위치)이 파일 내 줄이 늘어날수록 점점 더 어긋나기 시작. 10번째 레코드쯤부터는 필드 값이 완전히 다른 곳에서 읽힘. 수신 측 파서(Parser)가 뻗어버림.
02 1차 의심 — 항상 의심받는 인코딩 문제
이런 레거시 연동에서 바이트 수가 안 맞으면 십중팔구 EUC-KR과 UTF-8 간의 한글 바이트 수 차이(EUC-KR: 2바이트, UTF-8: 3바이트)가 원인이라고 생각하기 쉽습니다. 저 역시 그 부분을 먼저 파고들었죠.
- 파일 생성 시 인코딩 확인 → EUC-KR로 통일
mb_strlen으로 바이트 길이 재검증- 수신 측 파서 인코딩 설정 재확인
하지만 아무리 인코딩을 맞춰도 길이는 계속 어긋났습니다. 그리고 결정적인 단서가 보였는데, 오프셋이 레코드 수에 비례해서 딱 1바이트씩 밀린다는 점이었습니다. 그 순간, "이건 인코딩 문제가 아니라 줄바꿈 문자 문제다"라는 직감이 들었습니다.
03 진짜 원인 — ASCII 모드의 과잉친절
이 문제는 두 가지 시스템적 특징이 만나면서 발생했습니다.
① OS별 줄바꿈 문자의 차이
| OS | 줄바꿈 문자 | 바이트 수 |
|---|---|---|
| Linux / macOS | \n (LF) |
1 byte |
| Windows | \r\n (CRLF) |
2 bytes |
② FTP_ASCII 모드의 자동 변환
PHP 스크립트에서 FTP 전송 모드가 FTP_ASCII로 설정되어 있었습니다. ASCII(텍스트) 모드는 이기종 OS 간에 텍스트 파일이 올바르게 보이도록 줄바꿈 문자를 자동으로 변환해 주는 기능을 합니다.
"아, 리눅스에서 윈도우로 가는 텍스트 파일이네? \n을 \r\n으로 바꿔줘야지!" — FTP 데몬의 독백
만약 파일 생성 단계에서 수신 측(윈도우) 규격에 맞추기 위해 이미 \r\n으로 끝을 맺어 두었다면, 전송 과정에서 \r\r\n이 되어버립니다. 윈도우는 이를 두 번의 줄바꿈으로 해석하고, 레코드마다 1바이트씩 누적되어 오프셋을 통째로 꼬아버리게 됩니다.
💡 레코드 100개 기준 예시
100개 레코드 × 1바이트(중복 \r) = 마지막 레코드의 오프셋이 100바이트 밀려 있음. 고정장 전문 규격상 치명적인 파싱 오류 발생.
04 해결 방법 — FTP_BINARY 모드로 전환
고정장 전문은 텍스트 형태를 띠고 있더라도, 시스템 관점에서는 단 1바이트의 오차도 허용되지 않는 바이너리 데이터처럼 취급해야 합니다. 해결책은 파일 전송 함수의 모드를 1:1 원본 전송을 보장하는 FTP_BINARY로 변경하는 것입니다.
✅ 결과
파라미터 하나 바꿨을 뿐인데, 줄바꿈 증식 현상이 깔끔하게 사라지고 모든 레코드의 오프셋이 정확하게 맞아떨어졌습니다. 수신 측 파서도 에러 없이 정상 동작.
05 노하우 — ASCII vs Binary, 언제 뭘 써야 하나?
20년간 FTP 연동을 수도 없이 해오면서 정리된 기준입니다. 이 기준 하나만 기억해두면 됩니다.
🔤 ASCII 모드 (FTP_ASCII)
- PHP, Shell 등 소스 코드 파일
- INI, YAML 등 설정 파일
- 사람이 직접 읽는 일반 텍스트
- 이기종 OS 간 텍스트 호환성이 중요할 때
📦 Binary 모드 (FTP_BINARY)
- 고정장(Fixed-Length) 전문 파일
- 이미지, 동영상, 압축 파일
- 실행 파일, DB 덤프
- 바이트 무결성이 생명인 모든 데이터
💡 CoLife의 판단 기준
전송하는 데이터가 "사람을 위한 텍스트"인가, "기계를 위한 바이트 덩어리"인가? 전자면 ASCII, 후자면 항상 Binary. 헷갈리면 Binary가 안전합니다. ASCII 모드는 도와주려다 오히려 데이터를 망칩니다.
06 마무리
늘 인코딩 문제만 의심하다가 전송 프로토콜의 특성 때문에 전문 길이가 달라질 수 있다는 사실은 꽤나 신선한 충격이었습니다. 결국 FTP_ASCII를 FTP_BINARY로 바꾸는 단 한 줄 수정으로 며칠간의 삽질이 끝났습니다.
시스템 간 연동 작업에서는 내가 다루는 데이터의 성격을 먼저 명확히 정의하는 것이 기본 중의 기본입니다. PHP로 FTP 연동을 개발할 때는 고정장 전문이나 바이너리 데이터에 반드시 FTP_BINARY 모드를 사용하세요. 이 글이 비슷한 삽질을 하고 있는 분께 조금이라도 도움이 되길 바랍니다.
CoLife
20년 차 현직 개발자 · code & life 운영자
레거시부터 최신 기술까지, 현장의 삽질을 솔직하게 공유합니다.
💬
같은 문제로 삽질하셨나요?
비슷한 경험이 있으시거나, 더 좋은 해결 방법을 알고 계신다면 댓글로 공유해 주세요!
FTP 연동, 레거시 트러블슈팅 관련 궁금한 점도 편하게 질문 주시면 아는 한에서 답해드리겠습니다. 😊
👍 도움이 되셨다면 공감 버튼 한 번 눌러주시면 블로그 운영에 큰 힘이 됩니다!
'Code > PHP' 카테고리의 다른 글
| EUC-KR 환경에서 JSON이 통째로 사라진 이유 — 코드 한 줄의 인코딩 함정 (0) | 2026.05.12 |
|---|---|
| 반복문 속 쿼리가 서버를 죽인다 — PHP N+1 문제와 SQL 실행 최소화 실전 가이드 (0) | 2026.04.21 |
| [PHP] 화면엔 멀쩡한데 전송하면 깨진다? EUC-KR 고정 길이 전문 숨은 에러 완벽 해결 (0) | 2026.04.19 |
| PHP Imagick 멀티 TIFF 자동 변환이 서버를 다운시킨 이유와 해결법 (1) | 2026.04.15 |
| PHP 개발자의 에디터 변천사: 드림위버부터 AI CLI 환경까지 (0) | 2026.03.25 |