카테고리 없음

30만 건 API 응답 처리 (502 에러 → Streaming 방식 해결)

Code Canvas 2025. 9. 29. 10:40

1.상황

우리 서버에서 제공하는 API를 외부 시스템 A에서 호출하고 있습니다. 시스템 A는 처리 완료 후 결과 데이터를 배열 형식([1, 2, 3, 4, 5, ...])으로 한 번에 전달하는데, 약 30만 건의 대량 데이터를 전송하다 보니 서버에서 502 Bad Gateway 에러가 발생하는 문제가 나타났습니다.

 

2.문제 원인

서버가 JSON 전체를 한꺼번에 메모리에 로딩하면서 메모리 부담이 증가하고, 네트워크 지연과 함께 502 Bad Gateway 에러가 발생하고 있습니다. 대량의 데이터가 메모리에 몰리면서 서버 부하가 급격히 증가하는 것이 근본적인 원인입니다.

 

3.해결방안

Jackson JsonParser를 활용한 스트리밍 방식으로 문제를 해결할 수 있습니다. 클라이언트(시스템 A)는 기존과 동일하게 배열 형태로 데이터를 한 번에 전송하되, 우리 서버에서는 InputStream을 스트리밍 방식으로 처리합니다. 이 방법은 JSON을 토큰 단위(JsonToken)로 하나씩 읽어가며 바로 처리하기 때문에, 전체 데이터를 메모리에 한 번에 올리지 않아도 되어 메모리 효율성을 크게 개선할 수 있습니다.

 

이러한 스트리밍 처리 방식을 통해 대용량 데이터 처리 시에도 안정적인 서버 운영이 가능하며, 메모리 사용량을 최적화하여 502 에러 발생을 방지할 수 있습니다.


4. 예시 흐름

[           → START_ARRAY
1           → VALUE_NUMBER_INT
2           → VALUE_NUMBER_INT
3           → VALUE_NUMBER_INT
]           → END_ARRAY

 

이렇게 들어오는 값을 즉시 꺼내서 처리하므로, 전체 데이터를 모두 담아두지 않고도 대용량 처리가 가능



5. 예시 코드

@PostMapping("/upload")
public ResponseEntity<Void> uploadLargeJson(HttpServletRequest request) throws IOException {
    JsonFactory factory = new JsonFactory();
    List<Integer> batch = new ArrayList<>(1000);

    try (JsonParser parser = factory.createParser(request.getInputStream())) {
        while (!parser.isClosed()) {
            JsonToken token = parser.nextToken();

            if (JsonToken.VALUE_NUMBER_INT.equals(token)) {
                int value = parser.getIntValue();
                batch.add(value);

                // 1000개 모이면 DB 업데이트
                if (batch.size() == 1000) {
                    myMapper.updateBatch(batch); // MyBatis Mapper 호출
                    batch.clear();
                }
            }
        }

        // 마지막에 남은 데이터 처리
        if (!batch.isEmpty()) {
            myMapper.updateBatch(batch);
        }
    }

    return ResponseEntity.ok().build();
}

 

6. Maven 의존성

- com.fasterxml.jackson.core
- jackson-core