주요 결정과 시행착오

이유가 기록된 설계 결정

커밋 메시지는 대부분 「.」 이어서, 이유는 jb/ARCHITECTURE.md 와 소스 코드의 설명(docstring·주석)에 적힌 것만 모았습니다. 이유가 적혀 있지 않은 항목은 그렇게 표시했습니다.

1. 헥사고날 구조 채택

  • 결정: 의존성을 바깥에서 안쪽(adapter → app → domain → shared_kernel)으로만 흐르게 한다.
  • 이유: 문서가 「안쪽은 바깥을 절대 모른다」를 절대 규칙으로 두고, 참조 저장소의 패턴을 따랐다고 적는다. Mock 교체 시 유스케이스·도메인을 바꾸지 않는 것이 이 구조의 목적이다.
  • 근거: jb/ARCHITECTURE.md

2. 데모는 Mock 어댑터로

  • 결정: 데모 단계에서는 계좌·고객·지점·LLM 을 Mock 으로 동작시키고, 운영 전환 때 Mock 만 실구현으로 바꾼다.
  • 이유: 「유스케이스·도메인 변경 0」으로 교체할 수 있다고 문서가 명시한다.
  • 근거: jb/ARCHITECTURE.md, jb/core/di.py

3. 외부 연동은 폴백 체인으로

  • 결정: LLM 은 Gemini → Groq → Mock, 지점 검색은 Naver → Mock 순서로 시도한다.
  • 이유: 앞선 어댑터가 실패하거나 키가 없으면 다음 순위로 넘기고, 호출하는 쪽은 포트 하나만 알면 된다. 지도 어댑터 주석은 분기(if-else) 대신 순차 시도로 OCP·LSP 를 만족한다고 적는다.
  • 근거: jb/core/di.py, jb/apps/branch/adapter/outbound/fallback_map_adapter.py

4. 서버는 venv 의 python -m 으로 실행

  • 결정: .venv/bin/python -m uvicorn jb.main:app --reload 로 실행한다.
  • 이유: 전역 uvicorn 을 쓰면 --reload 하위 프로세스가 jb 패키지를 찾지 못한다.
  • 근거: jb/ARCHITECTURE.md 실행 절

5. 레이어 규칙을 lint-imports 로 강제

  • 결정: import-linter 계약으로 레이어 방향과 shared_kernel 독립성을 검사한다.
  • 이유: 문서가 「lint-imports가 … 방향과 shared_kernel 독립성을 빌드 단계에서 강제한다」고 적는다.
  • 근거: setup.cfg, jb/ARCHITECTURE.md

6. 타입 분기 대신 다형성

  • 결정: 창구 종류를 if-else 로 나누지 않고, 열거형 멤버가 번호표 접두(B)를 직접 들고 번호를 만든다.
  • 이유: 코딩 규칙이 타입·상태 분기 if-else 를 금지하고 다형성이나 dict 디스패치를 쓰게 한다. 코드 주석은 「타입 분기 회피」라고 적는다.
  • 근거: jb/ARCHITECTURE.md 코딩 규칙, jb/shared_kernel/value_objects.py

7. 대기번호와 도착 예상은 저장하지 않고 위치에서 계산

  • 결정: 대기번호와 도착 예상 시간을 엔티티 필드로 두지 않고 대기열 위치에서 파생한다.
  • 이유: 코드 주석이 「대기열 내 위치에서 파생하므로 엔티티가 들고 있지 않다」고 적는다.
  • 근거: jb/apps/queue/domain/entities/queue_entry_entity.py

시행착오: 범위를 좁힌 일

첫 커밋에는 적금 제안, 보이스피싱 점검, 일일 브리핑, 이자 리포트 컨텍스트가 있었습니다. 둘째 날 밤 커밋 9215dfb 에서 이 네 개를 삭제하고 창구·대기·법인 안내 흐름에 집중했습니다. 이유는 저장소에 적혀 있지 않아 여기서 추정하지 않습니다. 그 결과 jb/ARCHITECTURE.md 는 첫 커밋 시점 그대로 남아 있습니다.