본문 바로가기

개발 기록/미국 증시 분석 자동화 시스템 운영기

[운영기 C-3] 백엔드 아키텍처 현재 — 구축기 이후 도메인 19개, Phase 11개 완료

이 시리즈는 증시 자동화 구축기 8편의 후속입니다.

이전 글: 운영기 C-2 — 운영 이슈 15건: 실제로 무엇이 터졌고 어떻게 고쳤나
다음 글: (운영기 시리즈 완결)


구축기 8편의 시스템과 지금은 얼마나 다른가

구축기를 마무리할 때 이 시스템은 이런 상태였다.

  • 도메인 8개 (user, trade, news, blog, marketindex, exchangerate, stock, balance)
  • 외부 API 7개
  • 파이프라인 1개 (일간 9단계)
  • 프론트엔드 5개 화면
  • 기술 부채 4가지

 

지금은 다르다.

  • 도메인 19개
  • 외부 API 10개
  • 파이프라인 8개 (일간·주간·월간·실적·경제지표·ETF배당·주간이슈·분기)
  • 프론트엔드 12개 화면, 13개 API 모듈
  • Phase 11개 완료, 1개 대기

 

이 편은 현재 상태를 있는 그대로 정리하는 기록이다.


도메인 현황 — 8개에서 19개로

구축기에 있던 8개 도메인은 그대로 유지됐다. 11개가 추가됐다.

도메인 추가 시점 핵심 역할
stockprice 초기~Phase 6 일별 종가 이력, 최근 N거래일 조회
dividendhistory 초기 배당금 수령 이력
cash Phase 3.8 현금 입출금 관리
deposit Phase 3.8 계좌 입금 내역
portfolio Phase 3~8 구성·평가·AI 비중 제안
theme Phase 3.5 테마 키워드 기반 뉴스 분류
etf Phase 4 ETF 배당 수집·분석·블로그
earnings Phase 7 실적 캘린더·심층 분석 블로그
economic Phase 11 경제지표 수집·Surprise·블로그
issue Phase 10 주간 이슈 발굴·심층 분석 블로그

 

도메인이 늘면서 외부 연동이 가장 많은 서비스도 바뀌었다.

초기에는 news가 Finnhub, Yahoo RSS, Gemini, ChatGPT 4개를 연동해서 가장 복잡했다.

지금은 blog가 Claude, DALL-E, Notion, Telegram 4개를, etf가 Finnhub, Yahoo, ChatGPT, Claude, DALL-E, Notion, Telegram 7개를 연동한다.

 


Phase 완료 현황

Phase 1부터 11까지 중 10개가 완료됐다. Phase 9 하나가 대기 중이다.

[이미지 1 — Phase 완료 현황 타임라인]

 


새로 추가된 공통 유틸리티

구축기에는 common/ 아래에 exceptionresponse만 있었다.
지금은 util/ 패키지에 5개 유틸리티가 추가됐다.

유틸리티 추가 시점 역할
DateUtil Phase 3 초기 getLastTradingDate(), 주말 보정, 마지막 거래일 계산
FileUtil Phase 4 이미지 파일 저장·삭제 경로 처리
ImageUtil Phase 4~7 imgscalr 리사이징, base64 디코딩 (ISSUE-009 이후)
StringUtil Phase 5 마크다운 정제, JSON 정규식 추출
RetryUtil ISSUE-012 외부 API 선형 백오프 재시도

 

구축기 8편에서 기술 부채로 언급했던 "DateUtil 분리"가 실제로 구현됐다.

 

getLastTradingDate()ChatGptServiceBlogService 양쪽에 중복 구현되어 있었는데,

DateUtil로 추출해서 공통화했다.

 

RetryUtil은 B-3편에서 자세히 다뤘다.

StringUtil의 JSON 정규식 추출 메서드도 AI 응답 파싱에서 반복되던 코드를 공통화한 결과다.

 


AI 모델 관리 방식의 변화

구축기에는 모델명이 서비스 코드에 직접 박혀 있었다.
지금은 application.properties 한 곳에서 관리한다.

ai.openai.model=gpt-4o-mini
ai.dalle.model=gpt-image-1
ai.gemini.model=gemini-flash-latest
ai.gemini.weekly.model=gemini-2.5-flash
ai.gemini.theme.model=gemini-2.0-flash
ai.claude.model=claude-sonnet-4-5

 

Gemini 단독으로 용도별 모델이 3개다.

 

주간 블로그는 긴 컨텍스트가 필요해서gemini-2.5-flash,

일간 정제는 속도 우선이라 gemini-flash-latest,

테마 키워드 감지는 빠른 응답이 필요해서 gemini-2.0-flash다.

 

모델을 교체할 때 서비스 코드 수정과 재배포가 필요 없어졌다.

application-prod.properties 파일만 수정하면 된다.

 


기술 부채 현황 업데이트

구축기 8편에서 언급한 기술 부채 4가지의 현황이다.

 

해결된 것 (3가지)

부채 구축기 상태 현재 상태
DateUtil 분리 두 서비스에 중복 구현 DateUtil로 공통화
외부 API 재시도 없음 즉시 파이프라인 중단 RetryUtil 적용 (5개 서비스)
모델명 하드코딩 9개 서비스 파일에 분산 application.properties 외부화

 

여전히 남은 것 (3가지)

accountNo=3 하드코딩은 해결되지 않았다.

BlogService, BalanceService 등 여러 곳에 계좌 번호가 직접 박혀 있다.

나 혼자 쓰는 시스템이라 당장 문제는 없지만, 계좌 구조가 변경되거나 다른 계좌가 추가되면 코드 전반을 수정해야 한다.

 

Thread.sleep() 기반 Rate Limit 처리도 그대로다.

API 호출 루프에서 500ms~3000ms를 블로킹한다. 종

목이 늘수록 파이프라인 전체 실행 시간이 선형으로 증가한다.

CompletableFuture 병렬 처리로 전환하면 개선 가능하지만, 아직 적용하지 않았다.

 

Refresh Token 자동 갱신도 미구현 상태다.

프론트엔드에서 401 응답 시 로그인 페이지로 이동하는 것으로 처리한다.

하루에 한두 번 재로그인해야 하는 불편이 있다.

 

새로 추가된 부채 (2가지)

ForkJoinPool.commonPool() 포화 가능성이 있다.

PipelineOrchestrator 그룹 1의 병렬 수집이 기본 commonPool을 쓴다.

 

종목이 크게 늘거나 병렬 태스크가 많아지면 포화로 태스크가 큐에 쌓일 수 있다.

전용 ThreadPoolTaskExecutor 주입이 필요하다.

 

Quartz JDBC 테이블 정리도 빠져 있다. QRTZ_* 테이블에 실행 이력이 계속 쌓인다.

오래된 이력을 정기적으로 삭제하는 스케줄러가 없다.

 

[이미지 2 — 기술 부채 현황 비교 (구축기 vs 현재)]

 

현재 아키텍처 전체 요약

구축기와 비교해서 달라진 것, 달라지지 않은 것을 정리한다.

달라지지 않은 것

핵심 설계 원칙은 그대로다.

 

  • 도메인 중심 패키지 구조 (controller / service / mapper / dto 독립 소유)
  • MyBatis 명시적 SQL
  • JWT Stateless 인증
  • Docker Compose 4컨테이너 단일 호스트
  • Java 내장 HttpClient 직접 사용
  • ApiResponse<T> 공통 응답 구조
  • GlobalExceptionHandler 예외 집중 처리

 

이 결정들은 처음에 의도한 대로 지금도 잘 작동하고 있다.

도메인이 19개로 늘었어도 패키지 구조 덕분에 특정 도메인 코드를 찾는 시간이 줄어들지 않았다.

 

달라진 것

항목 구축기 현재
도메인 수 8개 19개
파이프라인 1개 (일간) 8개 (일간+독립 7개)
파이프라인 방식 시간 기반 @Scheduled 이벤트 기반 + 독립 @Scheduled
AI 모델 관리 코드 하드코딩 application.properties 외부화
외부 API 재시도 없음 RetryUtil 선형 백오프
공통 유틸 exception·response +DateUtil·FileUtil·ImageUtil·StringUtil·RetryUtil
프론트엔드 화면 5개 12개
Phase 완료 10개 완료, 1개 대기

다음 목표 — Phase 9

대기 중인 Phase 9는 목표 비중 시뮬레이션이다.

Phase 8(AI 비중 제안)에서 Claude가 종목별 delta(±%)를 제안한다.

 

사용자가 이 제안을 수락하면 실제 매매 계획이 나와야 한다.

"NVDA를 3% 늘리려면 현재 포트폴리오 기준으로 얼마어치를 사야 하는가"를 계산하고 시뮬레이션하는 화면이다.

 

Phase 6(주단위 자산 그래프)과 Phase 8(AI 비중 제안) 완료를 전제로 하는 작업이라 마지막으로 배치됐다.

두 Phase 모두 완료된 지금, 다음 차례다.


운영기 시리즈를 마치며

구축기 8편은 "처음 만들 때의 설계 의도"를 기록했다.
운영기 9편은 "만들고 나서 실제로 겪은 것"을 기록했다.

두 시리즈를 합치면 이 시스템의 전체 맥락이 담긴다.

 

운영하면서 가장 많이 확인한 것은 세 가지다.

 

첫째, 설계는 운영 앞에서 항상 수정된다.

시간 기반 파이프라인이 이벤트 기반으로 바뀌고,

모델명이 설정 파일로 나가고, 재시도 유틸이 추가됐다.

처음부터 완벽한 설계는 없다.

 

둘째, Telegram 알림 하나가 운영 방식을 바꾼다.

파이프라인이 조용히 실패하던 시절과 알림이 오는 지금은 운영 부담이 완전히 다르다.

 

셋째, 기술 부채는 목록화가 먼저다.

어디에 문제가 있는지 알아야 우선순위를 정하고 해결할 수 있다.

이 시스템은 지금도 매일 새벽 06:20에 파이프라인을 시작한다.


1차·2차 시리즈 전체 목록

증시 자동화 구축기 (1차)

제목
1편 왜 만들었고, 어떻게 설계했나
2편 Spring Boot + MyBatis + JWT 백엔드 설계
3편 매일 새벽 자동으로 돌아가는 파이프라인 설계
4편 7개 외부 API를 어떻게 연결했나
5편 Gemini + GPT + Claude 분업으로 투자 분석하기
6편 Claude로 블로그 초안 자동 생성 + Notion 동기화
7편 Docker Compose로 4개 컨테이너 운영하기
8편 3개월 운영 후기 — 잘 된 것, 실패한 것, 다음 목표

증시 자동화 운영기 (2차)

제목
A-1 Spring Boot 파이프라인을 이벤트 기반으로 바꾼 이유
A-2 이벤트 파이프라인 옆에서 돌아가는 독립 파이프라인들
A-3 주간·월간·분기 파이프라인: Claude가 긴 글을 쓰는 방법
B-1 실적·경제지표 캘린더 구현: 데이터가 시간차로 들어올 때
B-2 ETF 배당 자동화: 종목 유형별로 다른 분석 전략
B-3 외부 API 안정화: RetryUtil 도입과 모델 폐기 대응
C-1 Vue.js 대시보드가 12개 화면으로 늘어난 과정
C-2 운영 이슈 15건: 실제로 무엇이 터졌고 어떻게 고쳤나
C-3 백엔드 아키텍처 현재: 구축기 이후 무엇이 달라졌나 (현재 글)


태그: SpringBoot아키텍처현재, 투자자동화Phase완료, 도메인19개확장, RetryUtil공통유틸, 기술부채현황, AI모델외부화, 투자자동화운영기, 증시자동화운영기, 자동화구축기, Phase9목표비중시뮬레이션