입력 길이를 제한하지 않으면 생기는 일
길이 미제한이 DB 컬럼 초과·메모리 고갈·잘림 손실을 부른다. 계층별 한계를 일치시키는 입력 길이 검증 원리를 정리한다.
들어가며
어느 주, 텍스트 입력의 유효성 검증을 다뤘다. 형식·필수 여부는 다들 챙기는데, 길이 상한은 자주 빠진다. “설마 누가 10만 자를 넣겠어”가 사고의 시작이다. 길이를 막지 않으면 잘림(truncation), 컬럼 초과 에러, 그리고 메모리 고갈까지 세 갈래로 터진다.
핵심 개념: 길이는 모든 계층에 한계가 있다
입력 한 덩어리가 흐르는 경로엔 저마다 다른 길이 한계가 박혀 있다.
- DB 컬럼 —
VARCHAR(255)는 255자(또는 바이트)까지만 받는다. 초과하면 DB가 거부하거나(strict) 조용히 자른다(non-strict). - 메모리 — 검증 없이 받은 문자열은 일단 힙에 다 올라간다. 거대한 본문 N개가 동시에 오면 OOM.
- 인덱스 — 인덱싱되는 컬럼은 길이가 인덱스 키 한계에 묶인다.
문제는 이 한계들이 서로 다르고, 검증이 없으면 가장 약한 곳에서 터진다는 점이다. 그래서 “DB 컬럼이 받는 최대치”를 기준으로 애플리케이션 검증을 그보다 같거나 작게 맞춰야 한다. 그래야 DB까지 가기 전에 깔끔한 400으로 거절한다.
잘림이 더 무서운 이유
길이 초과가 에러로 터지면 그나마 낫다. 진짜 위험은 조용한 잘림이다. MySQL이 strict 모드가 아니면 VARCHAR(255)에 300자를 넣을 때 255자까지만 저장하고 나머지를 버린다. 사용자는 저장됐다고 믿지만 데이터는 잘려 있다. 멀티바이트(한글 등) 환경에선 글자 중간에서 잘려 깨지기도 한다. 데이터 무결성이 소리 없이 무너진다.
코드 예시
선언적 길이 검증으로 진입을 막는다.
1
2
3
4
5
6
7
8
public class CreateCommentRequest {
@NotBlank
@Size(max = 2000) // DB 컬럼 VARCHAR(2000) 과 일치
private String content;
@Size(max = 100)
private String title;
}
DB 측은 strict 모드로 두어 조용한 잘림 대신 명시적 에러를 받는다.
1
SET sql_mode = 'STRICT_TRANS_TABLES'; -- 초과 시 자르지 않고 에러
문자 길이와 바이트 길이도 구분한다. VARCHAR(n)은 보통 문자 수지만, 인덱스·스토리지 한계는 바이트로 계산되니 멀티바이트 환경에서 n * 인코딩 바이트를 염두에 둔다.
운영 함정
함정 1 — 계층 한계 불일치. 애플리케이션은 5000자까지 통과시키는데 컬럼은 VARCHAR(2000)이면, 2000자 넘는 입력이 검증을 통과하고 DB에서 터진다(혹은 잘린다). 검증 상한 ≤ 컬럼 상한을 규칙으로 못 박는다.
함정 2 — 길이 미제한 = DoS 벡터. 본문 길이에 상한이 없으면 공격자가 수 MB짜리 문자열을 반복 전송해 메모리·대역폭·DB 저장공간을 고갈시킬 수 있다. 요청 본문 크기 제한과 필드별 길이 제한은 가용성 방어의 일부다.
핵심 요약
- 길이를 안 막으면 잘림(무결성), 컬럼 초과(에러), 메모리 고갈(가용성)의 세 갈래로 터진다.
- 검증 상한을 DB 컬럼 상한 이하로 맞춰 DB 도달 전에 400으로 거절한다.
- 면접 한 줄 — “조용한 잘림을 어떻게 막나?” → DB를 strict 모드로 두어 초과 시 에러를 받고, 애플리케이션에서 컬럼 한계와 일치하는
@Size로 먼저 거른다.