배경 및 목표
100명의 인적성 검사를 동기 처리하면 2~3분 대기가 발생했습니다. 건당 외부 API 호출 시간이 누적되어, 채용담당자가 오랜 시간 기다려야 했고 부분 실패 시 이미 요청된 건을 취소할 수 없는 구조였습니다.
목표
- 100명 규모 동기 처리의 2~3분 대기를 없애고 즉시 응답한다.
- 부분 실패 시에도 외부 메일 오발송 없이 데이터 정합성을 보장한다.
해결 방법과 해결 후보군
후보군 비교
| 방식 | 설명 | 한계 |
|---|---|---|
| 동기 순차 처리 | 건별 순차 호출 | 100명 2~3분, 부분 실패 취소 불가 |
| 스레드풀 병렬 | 애플리케이션 내 병렬 | 외부 rate limit 부담, 유실 위험 |
| Kafka 비동기 (채택) | 건별 메시지 발행 | 즉시 응답 + 건별 정합성 + 재처리 |
1. Kafka 비동기 전환으로 즉시 응답
100명을 순차 호출하는 대신, Kafka에 건별 메시지를 발행하고 즉시 응답합니다.
fun requestExam(targetIds: List<Long>) {
targetIds.forEach { id ->
kafkaTemplate.send("queue.external.request", RequestMessage(id))
}
// 클라이언트에 즉시 응답, 실제 처리는 컨슈머에서
}
@KafkaListener(topics = ["queue.external.request"])
fun consume(message: RequestMessage) {
externalClient.request(message.targetId) // 건별 독립 처리
}2. 건별 트랜잭션으로 데이터 정합성 보장
컨슈머가 각 건을 독립적인 트랜잭션으로 처리합니다. 1건이 실패해도 나머지는 정상 처리되며, 실패 건은 별도로 기록하여 재처리할 수 있습니다.
sequenceDiagram participant C as Client participant API as API participant K as Kafka participant W as Worker participant EXT as 외부 서비스 C->>API: 검사 요청 (100명) API-->>C: 즉시 응답 API->>K: 건별 메시지 발행 loop 건별 처리 K->>W: 메시지 소비 W->>EXT: 외부 API 호출 EXT-->>W: 응답 end
결과
| 지표 | 기존 | 개선 |
|---|---|---|
| 요청 응답 | 2~3분 대기 | 즉시 |
| 처리 방식 | 동기 순차 | 비동기 병렬 |
| 부분 실패 | 전체 영향 | 건별 독립 |
모니터링
- 검사 요청 큐의 컨슈머 랙과 건별 처리 성공/실패를 관측한다.
- 외부 API 응답 지연·실패율을 관측한다.