주식앱 ADR 인덱스

새 과제 착수 전 관련 카테고리를 먼저 확인한다.
결정 요약은 각 ADR의 ## 결정 섹션 첫 줄이다.


아키텍처 / 서비스 분리

ADR결정 요약
Aggregator 서비스 분리 방식 선택신규 aggregator/ Spring Boot(:8090)를 FE 진입점으로 배치, BE(8080) 수정 없음
시그널 서비스 언어·프레임워크 선택ml/ FastAPI(:8000)로 분리, 코어 BE(:8080)와 독립 배포
아키텍처 패턴 선택Hexagonal — presentation→application→domain←infrastructure, OutputPort 패턴 미사용
개인계좌·주문 기능 도메인 패키지 분리 구조 결정account / order / holding 3개 패키지 분리, TradeHistory는 holding 패키지
수동 포트폴리오 도메인 분리 방식 선택portfolio 패키지에 ManualHolding 도메인 신설, manual_holdings 테이블 분리
AI 프레임워크 선택Spring AI 1.0.x — MCP Server 용도만, ChatClient/Anthropic 스타터 미포함

캐시 / 저장소 선택

ADR결정 요약
ML 응답 캐시 저장소 선택Redis, symbol 키, TTL 600초
시그널 응답 캐시 저장소 선택in-memory TtlCache, 종목별 TTL 600초 (SIGNAL_CACHE_TTL)
뉴스 스냅샷 캐시 저장소 선택BE news_snapshots 테이블에 영속화
현재가(실시간) 처리 저장소 선택SSE + BE 스케줄러
대화 히스토리 저장소 선택ConcurrentHashMap<sessionId, List> 인메모리, 히스토리는 시스템 프롬프트로 prepend
신호 영속화 저장소 선택BE signal_snapshots 테이블 + BeSignalSnapshotRepositoryImpl으로 교체 (Redis 제거)
Toss API 인증 토큰 영속화 저장소 선택MySQL toss_tokens 테이블, expires_at 기반 재발급
ML in-memory TtlCache 제거 여부 결정ml/ TtlCache 완전 제거, LLM 결과는 MySQL 영속화로 전환
SSE payload 변경 저장소 선택SSE event data를 JSON 배열로 변경 (refresh 문자열 제거)

외부 API / Rate Limiting

ADR결정 요약
Rate Limiter 라이브러리 선택Resilience4j (resilience4j-spring-boot3 + resilience4j-kotlin)
CircuitBreaker 적용 범위 선택단일 toss CircuitBreaker — 모든 Gateway에 공유
RateLimiter Gateway 적용 방식 선택명시적 래핑 — TossRateLimiterFacade.execute(group) { … }
RateLimiter 한도 초과 시 대기 vs 즉시 실패 선택timeout-duration: 0ms (즉시 실패)
429 응답의 CircuitBreaker 실패 기록 여부 선택429를 CircuitBreaker failure로 기록 — TossRateLimitException을 record-exceptions에 등록
CUD 요청의 Toss와 MySQL 처리 순서 결정Toss API 호출 → 성공 시 MySQL 저장, @Transactional은 MySQL 단계에만
보유종목·거래내역 읽기 동기화 방식 선택조회 요청마다 Toss API 호출 + unique 제약으로 upsert, 별도 스케줄러 없음

AI · ML 모델 선택

ADR결정 요약
LLM 오케스트레이터 선택claude -p CLI를 subprocess로 실행 (Anthropic API 직접 호출 미사용)
감성 분석 수행 방식 선택subprocess.run([“claude”, “-p”, prompt]), timeout 120s, JSON 강제, 실패 시 NEUTRAL 폴백
per-headline 감성 분석 호출 방식 선택claude -p 단일 배치 호출로 감성·번역·요약 일괄 처리
해외 뉴스 번역 처리 방식 선택번역·감성·요약을 단일 claude -p 프롬프트에서 처리
단기 가격 예측 곡선 모델 선택선형회귀 기울기 + momentum_score 가중
단기·중기·장기 예측 모델 선택기존 build_prediction에 horizon 파라미터 확장, sigma × sqrt(d)로 불확실성 표현
매도 추천 시점 산출 방식 선택horizon별 예측 포인트 중 최고값 주차를 매도 추천 주차로 선정
후보군 스코어링 실행 방식 선택ThreadPoolExecutor(max_workers=5) 병렬 실행, 실패 종목 제외 (부분 실패 허용)
백테스트 방식 및 감성 포함 여부 선택룩어헤드 배제, 기술 모멘텀만 검증, 뉴스 감성 미포함
웹 서치 MCP 서버 선택Brave Search MCP 서버 (Tavily 미사용, BRAVE_API_KEY 환경변수 필요)

도메인 로직 / 비즈니스 규칙

ADR결정 요약
OrderStatus 전이 규칙 배치 위치 결정OrderStatus.canTransitTo() 메서드로 캡슐화, Order Entity 내부에서 검증
활성 계좌 단일 선택 강제 방식 결정accounts.selected TINYINT(1), 단일 트랜잭션에서 기존 0→신규 1, Account.select()/deselect() Entity 메서드
watchlist 상태 조회 시 도메인 경계 처리 방식 선택ListStocksUseCase에서 WatchlistDomainService.findAll() → symbol set 교차 조회
종합 시그널 점수 가중치 결정composite = 0.6×sentiment + 0.4×momentum, [-1,1] 클램핑, 5단계 라벨
시그널 결과물의 성격 정의참고용 명시, 백테스트로 한계 노출
목표가 알림 발송 횟수 정책 선택도달 시 TRIGGERED 전이 (터미널), 이력은 alert_history 별도 적재
진입가 산출 방식 선택현재가 ≤ MA20 → 즉시 분할 매수, 현재가 > MA20 → max(MA20, 최근저점) 눌림목 대기
추천 후보군 모집단 구성 방식 선택watchlist ∪ CURATED_SYMBOLS(KR 8 + US 4), BE 조회 실패 시 빈 리스트로 graceful

네트워크 / 통신 경로

ADR결정 요약
FE → Aggregator 포트 전환 전략 선택FE NEXT_PUBLIC_API_BASE_URL → 8090, BE(8080)는 유지
Reverse Proxy 구현 방식 선택Spring RestClient로 헤더·본문·상태코드 그대로 전달, Spring Cloud Gateway 미도입
MCP 트랜스포트 방식 선택spring-ai-starter-mcp-server-webmvc (SSE 트랜스포트), localhost:8080/mcp/sse
FE의 ML 서비스 호출 경로 결정시그널/추천은 ML(:8000) 직접, watchlist/alert는 BE(:8080) 직접
Aggregator의 Backend·ML 호출 방식 선택코루틴 async/await로 BE 손익 조회와 ML 예측을 병렬 호출
LLM 호출 트리거 방식 선택 (TTL 자동 vs 사용자 명시적 새로고침)TTL 자동 갱신 제거, 사용자 명시적 refresh=true로만 LLM 호출

데이터 수집 / 동기화

ADR결정 요약
뉴스 수집 소스 선택KR=Google News RSS(ko), US=GDELT(폴백 Google News en-US)
종목 마스터 동기화 전략 선택DB 우선 + Toss API fallback + 스케줄러 3가지 병행
시세 수집 방식 선택@Scheduled 30초 주기 폴링, 주기는 alert.polling.interval-ms 프로퍼티 외부화
중소형주 후보군 관리 방식 선택CURATED_SYMBOLS dict에 large/mid/small 카테고리 키 추가
테마 연관 종목 매핑 방식 선택v1: 정적 JSON 매핑, v2에서 Claude 동적 보완 예정

알림 / 이벤트 발송

ADR결정 요약
멀티 채널 알림 발송 구조 선택NotificationGateway 인터페이스 + 채널별 구현체 + CompositeNotificationGateway(@Primary), 채널 on/off 환경변수
추천 진입가 알림 등록 방식 선택FE에서 POST /api/v1/alerts (targetPrice=entry, direction=BELOW), 신규 BE 없음

검색 / 조회 / 확장

ADR결정 요약
종목 검색 구현 방식 선택하이브리드 — DB 우선 + Toss API fallback
커스텀 필드 확장 전략 선택단일 stocks 테이블 + nullable 컬럼 (별도 테이블 미분리)

배포 / 인프라

ADR결정 요약
CI·CD 플랫폼 선택GitHub Actions
Docker 이미지 저장소 선택GitHub Container Registry (ghcr.io)
E2E 테스트 도구 선택Playwright
E2E CI 실행 환경 선택GitHub Actions service 컨테이너 + bootRun
프로덕션 배포 전략 선택Docker Compose + SSH 배포
로컬 DB 환경 및 포트 선택docker-compose.yml MySQL 8.0, 호스트 포트 3308, Flyway 관리
DB 컬럼 타입 제약 정책 선택FK 컬럼 없음, 상태/방향은 VARCHAR, 날짜는 DATETIME(6)

Git Hook / 자동화

ADR결정 요약
AI 리뷰 fallback 순서 선택Claude 1차, Codex fallback
AI 전체 실패 시 push·PR 허용 여부 결정경고 출력 후 허용
Claude 토큰 한도 감지 방법 선택출력에 “context length” / “token limit” / “rate limit” / “maximum context” 포함 여부 또는 출력 완전 비어있음으로 감지
commit-msg 훅의 사용자 입력 보호 정책 결정커밋 메시지 파일이 비어있을 때만 Codex가 생성

옵저버빌리티 / 모니터링

ADR결정 요약
ADR-001 옵저버빌리티 백엔드 스택 선택SigNoz vs Grafana LGTM — PoC 비교 후 확정 (제안 상태)
ADR-002 계측 표준 선택전 서비스 계측을 벤더 중립 OpenTelemetry(OTLP)로 통일
ADR-003 텔레메트리 라우팅 구조 선택중앙 OTel Collector 게이트웨이가 SigNoz·Grafana로 fan-out
ADR-004 Spring 계측 방식 선택backend·aggregator는 OTel Java agent 자동계측(무코드)
ADR-005 인프라 메트릭 수집 방식 선택mysqld/kafka/redis exporter + Prometheus·Collector scrape
ADR-006 compose 구성 분리 전략 선택앱/Grafana/SigNoz/Collector를 별도 compose 파일로 분리
ADR-007 메시지 소비 트레이스 검증 방식 선택검증 전용 최소 샘플 Kafka produce→consume 경로로 검증

보안 / 설정

ADR결정 요약
민감 정보 주입 방식 선택~/.zshrc 환경변수만 (TOSS_API_KEY, DISCORD_WEBHOOK_URL), 코드·설정파일 하드코딩 금지