코딩관계론

주식 Insight 갱신 아키텍처 고도화: 가상 쓰레드부터 Kafka까지 본문

개발/Hot-Stock

주식 Insight 갱신 아키텍처 고도화: 가상 쓰레드부터 Kafka까지

개발자_티모 2025. 1. 21. 17:22
반응형

1. 문제 상황

매일 밤, 주식 Insight를 갱신하기 위해서는 여러 단계를 거쳐야 합니다:

  1. 뉴스 데이터 크롤링
  2. 외부 API 호출 (주식 정보 등)
  3. DB 적재 및 분석/가공

매일 밤, 주식 Insight를 갱신하기 위해 여러 단계를 거치는 과정에서 우리는 다음과 같은 기술적 도전에 직면했습니다:

  1. 뉴스 데이터 크롤링
  2. 외부 API 호출 (주식 정보 등)
  3. DB 적재 및 분석/가공

초기에는 동기적 HTTP 요청으로 직관적으로 처리했지만, 더 큰 규모와 빠른 처리가 요구되면서 새로운 아키텍처를 모색해야 했습니다.

초기 프로세스

초창기에는 순차적(동기) HTTP 요청으로 처리했습니다. 이 경우,

  • “동시에 큰 트래픽이 발생하지 않는다”는 장점
  • “각 단계별로 순차성을 보장한다”는 직관적 이해도
  • 그러나 처리 시간이 오래 걸린다는 치명적인 단점이 있었습니다.

기존 프로세스의 한계

  • 동시성 부족: 종목 수가 많고, 작업량이 커질수록 대기 시간이 선형적으로 증가
  • 새벽 이외 시간으로 작업이 넘어갈 위험: 재처리나 예외 상황 시, 운영팀이 개입해야 함

이 문제를 극복하고자 “좀 더 빠르게 대규모 병렬 처리를 할 수 없을까?”라는 고민으로부터 이야기는 시작되었습니다.

2. 가상 쓰레드를 활용한 대규모 병렬화

2.1 가상 쓰레드 도입 배경

  • 기존 커널 스레드(OSThread) 방식으로는 몇천 개 이상의 스레드를 만들면 Context Switch 비용이 기하급수적으로 늘어납니다.
  • 가상 쓰레드(Virtual Thread) 는 JVM 레벨에서 스케줄링되므로, 스레드 생성·소멸이 훨씬 경량화됩니다.
  • 따라서, “각 종목마다 스레드를 생성해도 된다”는 수준의 병렬화 구현이 가능해집니다.

 

위 방법을 적용하니 성능은 빨라졌지만, 모든 종목의 인사이트가 업데이트 되지 않았습니다. 그 이유는 기존에는 1대1로 수행하면서 뉴스 서버에 부하가 없었지만, 가상쓰레드가 생성되다보니 너무 많은 요청이 한꺼번에 몰리면서, 뉴스 서버가 다운되는 현상이 발생한 것입니다. 

3. Kafka를 활용한 비동기 메시징 전환

3.1 Kafka 도입 목적

앞선 문제를 해결하기 위해, 메시지 큐(Message Queue) 개념을 고려했습니다.

    • 단일 서비스를 통한 직접 호출이 아니라, 중간 버퍼 역할을 하는 큐(Topic)에 메시지를 넣고, 필요한 만큼만 소비자가 가져가도록 유도
    • 카프카(Kafka)는 대용량 처리에 특화되어 있고, 다수의 Consumer Group을 통해 확장성을 확보하기 쉽습니다.

4. 새로운 도전: 순서 보장

4.1 왜 순서 보장이 문제인가?

비동기 시스템으로 전환하면, 단계 간 순서가 예전처럼 자동으로 보장되지 않습니다.

예를 들어,

  1. 뉴스 링크를 먼저 수집
  2. 해당 링크를 DB에 적재
  3. “어떤 테마와 관련 있는 뉴스인지” 분석
  4. 분석 결과를 다시 DB에 반영

이 순서를 무조건 지켜야만 최종 데이터 무결성이 확보됩니다.
비동기 환경에서는 메시지가 뒤죽박죽 소비되거나, 일부 단계가 빨리 끝나버리는 식으로 순서가 어긋날 위험이 있게되고, 데이터가 완벽하게 정리되기 전에 인사이트를 갱신 할 위험이 있습니다.

4.2 일반적인 해결책과 그 한계

1) 오케스트레이션(Orchestration) 패턴

  • 하나의 중앙집중형 “오케스트레이터” 엔진(예: Camunda, Zeebe 등)이 각 단계를 지휘
  • A → B → C 순서로 작업이 진행되어야 할 때, 모든 로직이 중앙 엔진에 정의되어 있음
  • 장점: 순차 제어 로직이 명확하고, 시각화·추적이 용이
  • 단점:
    • 중앙 집중 의존성: 오케스트레이터가 다운되거나 장애가 발생하면 전체 프로세스 중단
    • 복잡성 증가: 큰 워크플로우 엔진을 도입하면, 세부 설정 및 관리 부담이 커짐

2) 코레오그래피(Choreography) 패턴

  • 각 서비스가 이벤트(Topic) 기반으로 자율 동작
  • A 단계가 끝나면 “A 완료” 이벤트를 발행하고, B 서비스가 이를 구독해 B 단계를 실행… 이런 식으로 이어짐
  • 장점: 완전 분산형, 이벤트 흐름만으로 결합도가 느슨해짐
  • 단점:
    • 토픽/이벤트가 점점 많아질 수 있음 (각 단계마다 새로운 이벤트, 토픽 생성 가능)
    • 서비스 간 이벤트 종속 관계가 복잡해지면, 장애 발생 시 원인 추적이 어려워짐

4.2 JOB ID & DB 기반 순차 제어: Kafka의 순수성을 지키며 유연성을 더하다

왜 JOB ID 방식인가?

1. Kafka 토픽의 ‘순수성’ 유지

코레오그래피 패턴은 각 단계에서 이벤트를 발행해야 하며, 경우에 따라 새로운 토픽이 추가될 수도 있습니다.
이는 대규모 마이크로서비스 환경에서 곧 토픽 과다 생성과 관리 부담으로 이어집니다.

반면, JOB ID 기반 방식은 이런 문제를 한 번에 해결합니다.
Kafka는 메시징과 병렬 처리에만 집중하고, 단계별 제어와 상태 관리는 DB가 맡습니다.
토픽 구조는 단순하고 깨끗하게 유지됩니다.
이 방식 덕분에 “다음 단계 알림” 같은 불필요한 이벤트를 Kafka에 발행하지 않아도 되죠.

2. 커스텀 제어의 유연성

JOB ID 방식은 DB 테이블을 중심으로 유연한 제어를 가능하게 합니다.
예를 들어, JOB 별로 NEWS_COUNT, STATUS 등을 관리하면서 다음과 같은 세밀한 처리가 가능합니다:

  • 특정 뉴스 소스만 재처리
    뉴스 소스 지연이나 장애가 발생해도, 해당 JOB만 재시도하도록 처리할 수 있습니다.
  • 추가 워크플로우 정의 불필요
    코레오그래피에서는 새 이벤트를 정의해야 하고, 오케스트레이션에서는 워크플로우를 추가 설계해야 하지만,
    JOB ID 기반 DB 관리로 이 모든 과정을 단순화할 수 있습니다.

3. Kafka와 DB의 역할 분리

이 방식의 핵심은 역할 분리에 있습니다.

  • Kafka: 메시지 중계, 비동기 처리, 버퍼링이라는 본질적인 역할에 충실.
  • DB: 순차적 제어 및 상태 관리를 책임.

결과적으로 Kafka는 더욱 단순하고 효율적인 메시징 시스템으로 유지되며, 관리의 복잡도도 낮아집니다.

JOB ID 방식의 설계: 작동 원리

원리는 아래의 그림과 같다 이벤트가 실행될 때 디비에 해당 이벤트와 주식 이름을 저장하고, 토픽에서 전달 받은 데이터를 처리할 때 마다 DB가 업데이트 된다. 

이후 HTTP의 하나의 동기적 작업이 완료되면, 

5. 결론

  1. 동기 처리 → 과부하는 낮지만 처리 속도가 느리며, 한 번에 하나씩만 작업 가능
  2. 가상 쓰레드 고병렬화 → 내부 성능 개선은 되나, 외부 서버 과부하로 장애 위험 증가
  3. Kafka 비동기 아키텍처 → 메시지 큐로 트래픽 제어 및 확장성 확보, 부분 동시성 관리 가능
  4. JOB ID & DB 관리 → 필요한 순서 보장 구간을 DB에 상태로 기록, 사실상 ‘동기적’ 흐름 구현

결과 화면

반응형