주식앱 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 |
| 신호 영속화 저장소 선택 | 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), 코드·설정파일 하드코딩 금지 |