RETROSPECTIVE · 회고
알림 메시지 길이 제한: 상품 목록을 바이트 단위로 나눠 보내기
Backend
주문·선물 안내 메시지에 상품 목록이 들어가는데, 상품이 여러 개면 내부에서 이어 붙일 때 쓰던 구분자(|||)가 고객에게 그대로 보이는 문제가 있었다. 템플릿 메시지는 본문 길이 제한도 있어서 목록을 그냥 줄바꿈으로 바꾸는 것만으로는 부족했다.
축약할까, 나눠 보낼까
기존 메시지들을 보니 두 방식이 섞여 있었다.
- 축약: "A상품 외 3건". 결제 안내처럼 행동을 유도하는 메시지에 맞다.
- 분할 발송: 목록을 여러 통으로 나눈다. 받은 상품 내역처럼 정보 자체가 목적인 메시지에 맞다.
선물 안내는 받는 사람이 무엇을 받는지 알아야 해서 분할 발송으로 정했다.
글자 수가 아니라 바이트로 센다
발송 시스템의 제한이 바이트 기준이면 한글은 글자 수보다 훨씬 빨리 찬다. UTF-8에서 한글 한 글자는 3바이트다(EUC-KR이면 2바이트). 어떤 인코딩으로 세는지 먼저 확인해야 한다.
static List<List<String>> chunkByBytes(List<String> lines, int maxBytes) {
List<List<String>> chunks = new ArrayList<>();
List<String> cur = new ArrayList<>();
int size = 0;
for (String line : lines) {
int len = line.getBytes(StandardCharsets.UTF_8).length + 1; // 줄바꿈 포함
if (!cur.isEmpty() && size + len > maxBytes) {
chunks.add(cur);
cur = new ArrayList<>();
size = 0;
}
cur.add(line);
size += len;
}
if (!cur.isEmpty()) chunks.add(cur);
return chunks;
}
한 줄이 혼자서 제한을 넘는 경우(아주 긴 상품명)는 따로 잘라서 말줄임 처리했다.
임계값은 가장 긴 템플릿 기준으로
제한은 목록만이 아니라 고정 문구와 치환 변수까지 합친 전체 본문에 걸린다. 템플릿마다 고정 문구 길이가 달라서, 가장 긴 템플릿을 기준으로 여유를 두고 목록 영역의 최대 바이트를 정했다.
발송 구조
수신자(배송지)별 루프 안에서 상품 청크별로 한 번 더 돌아, 한 수신자가 청크 수만큼 메시지를 받는다. 여러 통일 때는 "(1/3)"처럼 순서를 붙여서 받는 사람이 헷갈리지 않게 했다.