[Troubleshooting] Jackson 2.15+ 대용량 JSON 처리 시 StreamConstraintsException 해결기
1. 문제 배경
최근 프로젝트에서 메일 발송 기능을 구현하던 중, Base64로 인코딩한 이미지 데이터가 포함된 대용량 JSON을 처리하던 중 아래와 같은 예외를 마주했습니다.
com.fasterxml.jackson.core.exc.StreamConstraintsException: String value length (20032631)
exceeds the maximum allowed (20000000, from StreamReadConstraints.getMaxStringLength())
2. 원인 분석
원인은 Jackson 2.15 버전부터 도입된 보안 강화 정책 때문이었습니다. 악의적인 사용자가 거대한 문자열을 보내 서버 메모리를 고갈시키는 Dos(서비스 거부) 공격을 방지하기 위해, 문자열 길이를 기본 2,000만 자(약 20MB)로 제한하기 시작한 것입니다.
현재 개발하고 있는 서비스에서는 이미지를 Base64로 인코딩한 JSON을 메일 본문에 첨부하고 있었기에, 고해상도 이미지 한장만으로도 이 수치를 가볍게 초과하게 된 상황이었습니다.
3. 기술 선택 배경
해결 방법은 크게 세가지가 있었으나, 아래의 방법 중 세번째 방법을 선택했습니다. 선택의 기준은 단순히 전체 설정을 바꾸는 편리함 대신, “필요한 곳에만 최소한의 예외를 허용한다”는 보안 원칙을 지키고자 했습니다.
1) 전역 설정 변경: 모든 API의 제한을 푼다.
→보안 위험: 전체 시스템이 Dos 공격에 노출되기에 선택하지 않았습니다.
2) 라이브러리 다운그레이드
→최신 보안 패치를 포기해야되기에 선택하지 않았습니다.
3) 대용량 전용 ObjectMapper 분리
→특정 API에만 예외를 허용하는 것이기에 보안과 기능의 타협점이라 판단되어 선택하였습니다.
4. 해결 과정 및 코드 적용
1) 대용량 전용 Bean 설정(50MB 제한)
메모리 효율을 고려하여, 무제한이 아닌 현재 개발하는 서비스의 최대 허용치인 50MB로 정밀하게 조정했습니다.
@Configuration public class JacksonConfig { @Bean @Primary // 일반적인 API는 기존의 20MB 제한을 유지하여 보안 강화 public ObjectMapper objectMapper() { return new ObjectMapper(); } @Bean(name = "largePayloadMapper") // 대용량 메일 전송 전용 public ObjectMapper largePayloadMapper() { ObjectMapper mapper = new ObjectMapper(); // 50,000,000자(약 50MB)로 제약 조건 조정 mapper.getFactory().setStreamReadConstraints( StreamReadConstraints.builder() .maxStringLength(50_000_000) .build() ); return mapper; } }
2) 컨트롤러에 특정 매퍼 주입
기본 매퍼가 아닌 @Qualifier를 통해 명시적으로 대용량 매퍼를 호출하여 처리하도록 구현했습니다.
@RestController @RequiredArgsConstructor public class MailController { @Qualifier("largePayloadMapper") private final ObjectMapper largeMapper; @PostMapping(value = "/send", consumes = "multipart/form-data") public ResponseEntity<?> sendMail(@RequestPart("mailRegCdo") String jsonStr) throws Exception { // 50MB까지 허용되는 전용 매퍼로 역직렬화 수행 MailRegCdo cdo = largeMapper.readValue(jsonStr, MailRegCdo.class); // 비즈니스 로직 실행... return ResponseEntity.ok().build(); } }
5. 문제 해결 경험과 성과
1) 보안성 유지: 시스템 전체의 예외를 푸는 대신, 특정 경로(Mail API)만 전략적으로 개방하여 인프라를 보호했습니다.
2) 유연한 대응: 프론트엔드와의 통신 규약(JSON 구조)을 수정하지 않고 서버 내부 설정만으로 문제를 해결하여 협업 비용을 줄였습니다.
3) 기술적 깊이: 단순 에러 해결을 넘어, 라이브러리의 릴리스 노트를 추적하고 오픈소스의 설계 의도를 파악하는 계기가 되었습니다.
6. 향후 개선 과제
설정 변경으로 급한 불은 껐지만, 장기적으로는 데이터 아키텍처 개선이 필요합니다. 대용량 이미지를 JSON에 직접 담는 방식은 힙 메모리에 부담을 주기 때문입니다. 향후에는 이미지를 S3에 먼저 업로드하고 JSON에는 이미지 URL만 담아 전달하는 방식으로 고도화하여, 서버 자원을 더욱 효율적으로 관리할 계획입니다.
Yoseb.P