프로젝트에서 필요한 데이터를 모아 특정 규칙에 맞게 본문을 재구성한 뒤, 다른 서버로 전송하는 기능을 맡은 적이 있다.
이 작업을 위해,
- 데이터 전송에 필요한 라이브러리
- 본문의 Layout 작성 규칙이 담긴 엑셀 파일
- 전송 방법의 예시
를 전달받았다.
전문을 구성하는 방식을 살펴보니, 각 필드의 길이를 최대 크기로 고정하는 방식이었다.
대학 전공 수업에서 TCP 패킷을 공부할 때, 데이터의 크기를 고정시켜 송수신하는 방식을 접한 적이 있었는데, 그때는 그냥 "이런 방법이 있구나" 하고 넘어갔었다. 그런데 실제 프로젝트에서 이 방식을 적용해볼 수 있다는 점이 흥미로웠다.
이번 글에서는 이 방식을 적용하면서 겪은 문제와 해결 과정을 기록해보려 한다.
인터넷에 검색해보니까 이런 유형의 데이터 송수신 방식을 Fixed length format 라고 부른다고 합니다.
Fixed length format 이란?
직역하면 "고정된 길이 구성방식"으로 말 그대로 길이가 고정되어있다고 생각하면 된다.
예시로 방식으로 날짜(8), 제목(10), 내용(20) 을 문자 길이로 구성한다고 했을 때 (예시:공백은'+'로 표현)
| 날짜 | 제목 | 내용 |
| 20250311 | 고정된 길이 | 내용을 구성해 보겠습니다. |
// 예시를 재구성한 전문
20250311고정된+길이++++내용을+구성해+보겠습니다.++++++
지금은 예를 들기위해 문자의 길이로 전문을 구성했지만 정해진 요청 규칙 따라 문자열의 길이가 아닌
Byte로 변환, 정렬 위치(좌측, 우측) 등 요청 규칙에 맞게 전문을 구성해야 합니다.
금융권에서 주로 사용되는 방식
- 금융권의 대부분의 프레임워크 언어가 C로 되어있고 해당 시스템에서 가장 널리 쓰이는 방식으로 아마 이 영향을 받은게 아닌가 싶다.
전문(메시지)은 크게 두 부분으로 나뉜다.
- 공통된 데이터가 포함된 Header
- 실제 통신에 필요한 데이터를 담은 Body
제공받은 라이브러리에는 Header 구성 및 API 요청을 처리하는 기능이 이미 구현되어 있었다.
내가 해야 할 일은 Body 데이터를 구성하고, API를 호출하는 작업이었다.
전달받은 라이브러리의 Header 구성 로직을 살펴보니,
필드 길이를 계산하여 공백을 추가하는 addLeftPad(), addRightPad() 메소드가 존재했다.
그리고 담당자로부터 이미 정상적으로 동작하는 함수라는 설명을 들었기에 그대로 사용했다.
전문을 구성하는 방식은 단순했다.
- 요청 규칙에 맞게 각 필드를 최대 길이로 고정하여 구성
- 구성된 데이터들을 규칙에 맞는 순서로 조합하여 전송
이제 구성한 전문을 이용해 Server to Server API를 요청했다.
그런데, 응답(Response)을 확인해 보니 "요청 실패"라는 메시지만 돌아왔다.
응답 메시지에는 실패 사유에 대한 정보가 포함되지 않아, 담당 팀에 문의했다.
담당 팀의 답변은 다음과 같았다.
"전문 길이가 맞지 않습니다."
우선 내가 작성한 코드를 점검했다.
- 라이브러리의 공백 추가 함수(addLeftPad(), addRightPad())를 호출할 때,
→ 전달하는 byteSize 값이 요청 규칙과 일치하는지 확인 - 공백 추가 함수에서 반환된 값이
→ 의도한 대로 변환되고 있는지 확인
하지만 두 가지 모두 문제없이 동작하고 있었다.
직접 Byte 변환 후 크기를 확인했을 때도 이상이 없었다.
이후 담당 팀에 추가 로그를 요청했다.
그리고 돌아온 답변은 다음과 같았다.
"제목 부분부터 전문이 뒤로 밀리는 것 같아요."
전달받은 라이브러리에 Header구성해주는 로직을 살펴보니 Field길이를 계산해서 공백을 붙여주는 addLeftPad(), addRightPad() 메소드가 있었고 이미 잘 동작하고 있다고 전달 받았기에 그대로 사용했다.
이 단서를 바탕으로, 라이브러리에서 사용하는 공백 추가 함수(addLeftPad(), addRightPad())를 다시 살펴봤다.
그리고 결국, 이 함수들에 문제가 있다는 것을 발견했다.

라이브러리의 공백 추가 함수에는 어떤 문제가 있었을까?
코드를 보면서 함께 분석해보자!
// 코드 예시
// 왼쪽 공백 추가 함수 예시
private static String addLeftPad(String input, int byteSize) {
int inputByteSize = input.getBytes().length;
StringBuilder builder = new StringBuilder();
for (int i = 0; i < byteSize - inputByteSize; i++) {
builder.append(" ");
}
builder.append(input);
return builder.toString();
}
// 오른쪽 공백 추가 함수 예시
private static String addRightPad(String input, int byteSize) {
int inputByteSize = input.getBytes().length;
StringBuilder builder = new StringBuilder(input);
for (int i = 0; i < byteSize - inputByteSize; i++) {
builder.append(" ");
}
return builder.toString();
}
아마 눈치가 빠르신 분들은 이미 알겠지만
이 함수의 역할은 주어진 문자열(input)을 byteSize 크기만큼 왼쪽 공백을 추가하여 고정 길이 문자열로 만드는 것이다.
이후, 이 문자열을 API 요청 전문(payload)에 포함하여 외부 서버에 전송했다.
⚡ 문제점
- 내가 개발한 서버의 기본 문자 인코딩(Charset)은 UTF-8이었다.
- API를 수신하는 외부 서버의 인코딩이 다르기 때문에 생긴 문제라고 생각했다.
- 전달받은 자료에는 인코딩(Charset)이 명시되어 있지 않았고 개인적으로 여러개로 시도해본 결과 "EUC-KR" 이라는걸 알았다.
int inputByteSize = input.getBytes().length;
- 위 코드는 input 문자열을 바이트 배열로 변환한 후 길이를 구하는 부분이다.
- getBytes()는 별도의 Charset을 명시하지 않으면 서버의 기본 Charset을 따른다.
- 내 서버의 기본 Charset은 UTF-8이었기 때문에, input.getBytes()는 UTF-8 기준의 바이트 크기를 계산했다.
② UTF-8 vs EUC-KR 인코딩 차이
UTF-8과 EUC-KR은 문자를 인코딩하는 방식이 다르다.
| 문자 | UTF-8 (가변 길이) | EUC-KR (고정 길이) |
| "A" | 1바이트 (0x41) | 1바이트 (0x41) |
| "가" | 3바이트 (0xEAB080) | 2바이트 (0xB0A1) |
- UTF-8은 한글 1글자를 3바이트로 인코딩하지만,
- EUC-KR은 한글 1글자를 2바이트로 인코딩한다.
해결
이제 문제의 원인을 파악했으니, 해결하는 방법을 고민했다.
방법 자체는 어렵지 않았고, 다음과 같은 여러 가지 선택지가 있었다.
1. 서버의 기본 인코딩을 UTF-8 → EUC-KR로 변경
- 이론적으로는 유효한 방법이지만, 이미 프로젝트가 상당히 진행된 상태였다.
- 프로젝트 중반에 기본 인코딩을 변경하는 것은 예상치 못한 사이드 이펙트를 유발할 가능성이 컸다.
- 따라서 이 방법은 선택하지 않았다.
2. 라이브러리의 공백 추가 함수(addLeftPad(), addRightPad())를 직접 수정
- 라이브러리를 수정하면 가장 확실한 해결책이 될 수 있었다.
- 하지만 제공받은 라이브러리를 직접 변경하는 것은 유지보수 및 책임 문제를 초래할 수 있었다.
- 따라서 이 방법도 제외했다.
3. 라이브러리의 공백 추가 클래스를 상속받아 재구현
4. 공백 추가 기능을 별도의 Util 클래스로 구현
- 3번과 4번 중에서 고민한 끝에, 4번 방법을 선택했다.
왜 4번 방법을 선택했을까?
- 라이브러리 내부 코드를 수정하지 않으면서도 원하는 방식으로 전문을 안전하게 생성할 수 있었다.
- 라이브러리 업데이트가 발생하더라도 영향을 받지 않는다.
- 추후 인코딩 방식(EUC-KR, UTF-8 등)이 변경되더라도, 소스 코드 내에서 직접 관리할 수 있어 유지보수가 용이하다.
이러한 이유로, 공백을 추가하는 별도의 Util 클래스를 만들어 적용하는 방법을 선택했다.
결과적으로, 필드 길이를 올바르게 맞춘 전문을 생성할 수 있었고, 문제를 해결할 수 있었다.
회고
속담에 "돌다리도 두들겨 보고 건너라" 라는 말이 있듯이 확실한 일, 잘 아는 일이라도 실수하지 않도록 꼼꼼하게 확인해야 한다.
라이브러리를 제공받았다고 해서 모든 것이 올바르게 동작한다고 단정 지으면 안 된다.
특히, 제공된 자료에 문자 인코딩(charset) 관련 정보가 없었지만, 이를 정확히 이해하고 문제를 예측할 수 있었다면 더 빠르게 대응할 수 있었을 것이다.
기능을 구현하기 위해서는 어떤 요소들이 필요한지 명확히 파악해야 하고, 이를 기반으로 예상 개발 시간을 산출할 수 있어야 한다.
결국, 기본적인 개념을 확실히 이해하는 것이 중요하다는 사실을 다시 한번 깨달았다.
이번 경험을 돌아보면서 나의 부족한 점도 반성하게 되었다.
만약 내가 인코딩에 대해 더 잘 알고 있었다면 쓸데없는 삽질을 줄이고, 다른 팀원들에게도 더 도움을 줄 수 있지 않았을까?
'개발' 카테고리의 다른 글
| 손으로 하던 사과게임, 이제는 자동으로! Python으로 만든 매크로 이야기 (2) | 2025.04.27 |
|---|---|
| URL Encode 왜 필요해? (1) | 2025.03.08 |
| AWS 아키텍처 개선 하기 (0) | 2025.02.01 |