🛠️ Dev & Tools (투자 도구 개발)

[계좌 리팩토링] 19편(자원 관리 하). 비싼 리소스를 아끼는 백엔드 아키텍처: 웹소켓 슬롯 할당 알고리즘

코딩버핏 2026. 6. 3. 16:36

비싼 리소스를 아끼는 백엔드 아키텍처

 

1. 120개의 후보를 40개 슬롯에 욱여넣는 파이프라인

안녕하세요! 한정된 자원 속에서 최적의 해답을 찾아가는 [계좌 리팩토링] 시리즈 19편입니다. 지난 18편에서는 증권사 API의 '웹소켓 40개 제한'이라는 거대한 벽을 넘기 위해, 무지성 구독 대신 '선필터링(Pre-filtering)'이라는 전략적 우회로를 기획했습니다.

이번 편에서는 그 전략을 실제 코드로 구현하며, 트레이딩 봇의 파이프라인이 어떻게 백엔드 서버의 견고한 아키텍처로 진화하는지 살펴보겠습니다. 목표는 단순합니다. 120개의 방대한 예비 후보군(Pool)을 순회하며, 깐깐한 조건을 통과한 '진짜배기 종목'들만 골라 최대 40개의 VIP 웹소켓 슬롯에 차곡차곡 앉히는 것입니다.


let activeWebSockets = 0;
// KIS API 최대 제한은 40개지만, 예기치 않은 오버플로우를 막기 위해 
// 안전 마진(Safety Margin)을 두어 38개까지만 엽니다.
const MAX_SLOTS = 38; 

async function assignWebSocketSlots(candidatePool) {
  for (const ticker of candidatePool) { // 120개 후보군 순회
    // 1. 슬롯 소진 체크
    if (activeWebSockets >= MAX_SLOTS) {
      console.log('웹소켓 슬롯이 모두 소진되었습니다. 할당을 중단합니다.');
      break; 
    }

    try {
      // 2. 1차 필터: VWAP 추세 확인 (가벼운 REST API)
      const isAboveVwap = await checkVwap(ticker);
      if (!isAboveVwap) continue; // 조기 탈출 (Early Exit)

      // 3. 2차 필터: PMH 모멘텀 확인 (가벼운 REST API)
      const isBreakingPmh = await checkPmh(ticker);
      if (!isBreakingPmh) continue; // 조기 탈출

      // 4. 모든 면접을 통과한 주도주만 비싼 웹소켓 할당!
      await connectWebSocket(ticker);
      activeWebSockets++;
      
    } catch (error) {
      // 에러 방어 로직: 봇 전체가 죽는 것을 방지 (Graceful Fallback)
      console.error(`[경고] ${ticker} 종목 검증 중 에러 발생, 다음 후보로 넘어갑니다.`);
      continue; 
    }
  }
}

2. 조기 탈출(Early Exit)과 방어 로직의 미학

위 코드에서 가장 핵심이 되는 부분은 continuetry-catch 문입니다.

  • 조기 탈출 (Early Exit): 조건이 하나라도 틀리면 continue로 가차 없이 다음 종목으로 넘깁니다. 만약 1차 필터(VWAP)에서 탈락했다면, 굳이 2차 필터(PMH)를 검사하기 위해 아까운 REST API 호출(Rate Limit)을 낭비하지 않는 것입니다.
  • 유연한 실패 (Graceful Fallback): 실전에서는 특정 종목이 상장 폐지되었거나 거래 정지 상태여서 API 에러를 뱉는 경우가 흔합니다. 이때 try-catch로 감싸지 않으면 단 하나의 불량 종목 때문에 전체 루프가 뻗어버리고, 나머지 119개 종목을 검사할 기회조차 날아갑니다. 실패해도 자연스럽게 다음 후보로 넘어가게 만드는 방어 로직은 자동매매에서 생명줄과 같습니다.

3. 이 기시감은 뭘까? 백엔드 아키텍처의 대원칙

이 로직을 완성하고 나니 묘한 기시감이 들었습니다. 트레이딩 봇의 이 '슬롯 관리 시스템'은, 놀랍게도 우리가 회사에서 다루는 대규모 백엔드 서버의 커넥션 풀(Connection Pool)이나 스레드 풀(Thread Pool) 아키텍처와 본질적으로 완전히 동일했습니다.

트래픽이 폭주하는 서버에서 DB 커넥션(데이터베이스 연결)은 웹소켓만큼이나 생성 비용이 비싸고 개수가 한정된 '고급 리소스'입니다. 실력 있는 백엔드 엔지니어는 무작정 DB부터 찌르지 않습니다.

"비싼 리소스를 할당하기 전에, 싼 리소스로 먼저 필터링하라."

사용자 요청이 들어오면 우선 비용이 저렴한 캐시(Redis)를 먼저 찔러보고(1차 필터), 로컬 메모리의 유효성 검사를 거친(2차 필터) 후에야 비로소 무겁고 비싼 DB 커넥션을 할당합니다. 무의미한 쿼리가 DB 풀을 점유하여 진짜 처리해야 할 결제 요청이 대기줄에 갇히는(기회비용 상실) 참사를 막기 위해서죠.

트레이딩 봇의 120개 후보군을 가벼운 REST API로 쳐내고 진짜 주도주 40개에만 웹소켓을 연결하는 과정은, 바로 이 소프트웨어 엔지니어링의 황금률을 금융 도메인에 완벽하게 치환한 결과였습니다.

4. 마무리하며: 봇 개발이 주는 뜻밖의 선물

처음엔 단순히 "40개밖에 안 된다고? 짜증 나네"로 시작했던 제약이었습니다. 하지만 이 한계를 극복하기 위해 머리를 쥐어짜는 과정에서, 저는 어느새 주식 차트가 아니라 트래픽과 리소스를 효율적으로 분배하는 백엔드 아키텍처를 설계하고 있었습니다.

이것이 개발자가 직접 자동매매 봇을 만들 때 얻을 수 있는 가장 큰 축복인 것 같습니다. 계좌의 수익률을 높이려다 보니 자연스럽게 엔지니어링 역량까지 퀀텀 점프를 하게 되니까요.

이렇게 자원 관리 파이프라인까지 완벽하게 세팅하며 시스템의 안정성은 극에 달했습니다. 다음 편에서는 드디어, 시장의 변동성이 미쳐 날뛸 때 회사 화장실에 숨어 봇을 원격으로 멈출 수 있는 '텔레그램 양방향 리모컨(Interactive Bot) 구축기'로 돌아오겠습니다!