배경 및 목표

채용담당자가 인적성 검사 결과를 조회할 때, 외부 서비스에서 실시간으로 파일을 가져와 처리하는 구조였습니다. 최초 조회 시 파일 다운로드 + 파싱에 6초가 걸렸고, 그 동안 DB 커넥션을 점유하여 다른 요청에도 영향을 주었습니다.

graph LR
    subgraph before["❌ 기존: 실시간 폴링"]
        C["채용담당자"] -->|"결과 조회"| API["API"]
        API -->|"외부 파일 다운로드"| EXT["외부 서비스"]
        EXT -->|"파일 처리 (6초)"| API
    end
    API -->|"커넥션 6초 점유"| DB[("DB")]

목표

  • 최초 조회 시 6초 지연·커넥션 점유를 없애고 즉시 응답한다.
  • 롱 트랜잭션 없이 결과지를 사전 적재한다.

해결 방법과 해결 후보군

후보군 비교

방식설명한계
실시간 폴링조회 시점에 외부 처리6초 지연 + 커넥션 점유
조회 결과 캐싱첫 조회 후 캐시첫 조회는 여전히 6초
사전 배치 적재 (채택)미리 가져와 DB 적재조회 즉시 응답, 롱 트랜잭션 제거

1. 사전 배치 처리 방식으로 전환

조회 시점에 외부 서비스를 호출하는 대신, 주기적 배치로 결과를 미리 가져와 DB에 적재하는 방식으로 변경했습니다.

fun preloadResults() {
    val pendingIds = resultRepository.findPendingIds()
    pendingIds.forEach { id ->
        val result = externalClient.fetchResult(id)
        if (result != null) {
            resultRepository.save(result.toEntity())
        }
    }
}

2. 조회 시 즉시 응답

채용담당자가 결과를 조회하면, 이미 적재된 데이터를 반환하므로 외부 호출이 불필요하고 DB 커넥션 점유 문제도 해소되었습니다.

graph LR
    subgraph before["❌ 기존"]
        Q1["조회 요청"] --> E1["외부 파일 다운로드"] --> P1["파싱 (6초)"] --> R1["응답"]
    end
    subgraph after["✅ 개선"]
        B["배치: 미리 적재"] --> DB[("DB")]
        Q2["조회 요청"] --> DB --> R2["즉시 응답"]
    end

결과

지표기존개선
결과 조회6초1초 이하 (83% 단축)
커넥션 점유길음해소
롱 트랜잭션발생제거

모니터링

  • 결과지 조회 응답시간(6초→1초 이하)과 커넥션 점유 시간을 관측한다.
  • 사전 배치 처리 성공률·지연을 관측한다.