AI Briefing

Kotlin Coroutine으로 구현한 비동기 제휴 연동 시스템 구축기

·2025.07.08 13:33

Spring Batch 동기 호출을 코루틴 비동기로 바꿔 15개 API 병목을 줄였다.

제휴 수집 시스템 구축 이후, 연동 영역은 여전히 Spring Batch 기반 동기 처리로 동작하고 있었다. 처리 대상마다 15개 이상 외부 API를 순차 호출하는 구조라, 배치 서버 기동 비용과 긴 처리 시간이 병목으로 남았다.

초기에는 Kafka 이벤트 기반 + Kotlin Coroutine으로 완전한 실시간 처리 구조를 목표로 삼았지만, 중복 데이터, 속도 제어, 제휴사 호출 제한 같은 이슈 때문에 배치 + Kafka + Coroutine의 절충안으로 전환했다. 배치는 처리 대상 메시지만 Kafka로 발행하고, 핵심 병목인 외부 API 호출과 DTO 조립은 composite 서비스에서 코루틴으로 비동기화했다.

핵심 비교는 다음과 같다.

  • 기존 구조: Reader → Processor에서 다건 API 순차 호출 → Writer
  • 개선 구조: Kafka로 이벤트 전달 → composite 서비스 단일 endpoint 호출 → 내부 API 동시 호출
  • 효과: 배치의 역할은 축소하고, 15개 API 병목은 코루틴으로 분산

예제로는 6개 API, 각 1초 응답, 스레드 3개 고정 조건을 두고 Java Thread 방식과 Kotlin Coroutine 방식을 비교했다. Java Thread에서는 3개 스레드가 API-1~3을 처리한 뒤 다시 API-4~6을 처리해 총 실행 시간이 약 2초였고, 스레드가 I/O 동안 블로킹되면서 자원이 낭비됐다.

반면 Kotlin Coroutine 예제에서는 runBlocking, withContext(Dispatchers.IO.limitedParallelism(3)), coroutineScope, async/await를 사용해 스레드 3개로 코루틴 6개를 처리했다. 스레드는 유지하면서도 코루틴이 delay 동안 일시중단(suspend)되고 재개(resume)되기 때문에, 같은 자원 조건에서 더 효율적으로 동시성을 확보할 수 있었다.

코루틴의 핵심은 suspend 함수와 Continuation이다. suspend 함수는 컴파일 시 상태 머신으로 변환되고, 중단 시점의 정보가 Heap의 Continuation 객체에 저장된다. 이후 디스패처가 이를 다시 호출해 중단 지점부터 재개하므로, 스레드를 붙잡고 블로킹하는 대신 실행 상태만 보관한 채 다른 작업을 처리할 수 있다.

실무에서 활용한 코루틴 구성 요소도 함께 정리했다.

  • suspend: 중단과 재개가 가능한 함수 정의
  • withContext: 블록의 실행 스레드 전환
  • Dispatchers.Default: CPU-bound 작업용
  • Dispatchers.IO: Blocking I/O 작업용
  • withTimeout: 시간 제한 제어
  • coroutineScope: 자식 코루틴을 묶어 관리

정리하면, 이번 전환은 완전한 실시간화보다 현실적인 운영 제약을 반영한 선택이었다. 배치는 유지하되 이벤트 전달로 역할을 줄이고, 실제 병목인 외부 API 호출은 Kotlin Coroutine으로 비동기화해 성능과 확장성을 동시에 개선한 구조다.

이 요약은 원문 이해를 돕기 위한 큐레이션입니다. 저작권은 원저작자에게 있으며, 정확한 내용과 맥락은 원문을 확인하세요.

요약 오류, 출처 표기 문제, 삭제 요청은 문의 · 건의로 알려주세요.