배경 및 목표

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 응답 지연·실패율을 관측한다.