diff --git a/.codex/skills/mission-close/SKILL.md b/.codex/skills/mission-close/SKILL.md deleted file mode 100644 index fa5173a9..00000000 --- a/.codex/skills/mission-close/SKILL.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -name: mission-close -description: Use when the user wants to close out a mission, summarize the active state from MISSIONS.md, and write the final decision document in docs/decisions. Trigger on phrases like "문서화하자", "회고 정리하자", or "decisions 문서로 남기자". ---- - -# Mission Close - -이 스킬은 미션을 마무리하고 `MISSIONS.md`의 진행 상태를 바탕으로 `docs/decisions` 문서를 정리하는 용도다. - -## 반드시 할 일 - -1. [AGENTS.md](../../../AGENTS.md)를 먼저 읽고 문서화 원칙을 따른다. -2. 루트 `MISSIONS.md`를 먼저 읽고 현재 미션의 목표, 검증, 작업 로그, 남은 이슈를 복원한다. 파일이 없다면 [docs/templates/mission-state-template.md](../../../docs/templates/mission-state-template.md)를 참고해 필요한 항목을 먼저 정리한다. -3. [docs/decisions/README.md](../../../docs/decisions/README.md)와 [docs/templates/decision-record-template.md](../../../docs/templates/decision-record-template.md)를 참고한다. -4. 현재 미션이 새 문서인지, 기존 문서 갱신인지 먼저 판단한다. -5. 문서는 아래 흐름이 보이게 정리한다. - - 문제 - - 선택 - - 이유 - - 검증 - - 결과와 남은 이슈 -6. 필요하면 `MISSIONS.md`의 현재 미션 상태를 완료 기준에 맞게 정리하거나 다음 미션 준비 상태로 갱신한다. - -## 출력 원칙 - -- 결과 자랑보다 고민과 판단의 흐름을 먼저 드러낸다. -- 미션당 문서 하나 원칙을 우선한다. -- decision 문서는 최종 회고와 판단 기록으로만 쓴다. -- 다음 미션으로 이어질 남은 이슈가 있으면 짧게 남긴다. diff --git a/.codex/skills/mission-evaluate/SKILL.md b/.codex/skills/mission-evaluate/SKILL.md deleted file mode 100644 index 0375be4c..00000000 --- a/.codex/skills/mission-evaluate/SKILL.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -name: mission-evaluate -description: Use when the user says a mission is submitted or asks for explicit evaluation of the completed mission. Trigger on phrases like "제출할게, 평가해줘", "이 미션 평가해줘", "이제 평가해줘", or "이 미션 끝났는지 봐줘". ---- - -# Mission Evaluate - -이 스킬은 미션 제출 이후 평가를 수행하는 용도다. - -## 반드시 할 일 - -1. [AGENTS.md](../../../AGENTS.md)를 먼저 읽고 평가 기준을 따른다. -2. 루트 `MISSIONS.md`에서 현재 미션의 요구사항, 범위, 검증 계획을 먼저 확인하고, 없으면 [docs/templates/mission-state-template.md](../../../docs/templates/mission-state-template.md)를 기준으로 필요한 평가 기준을 복원한다. -3. 현재 PR의 변경점, 사용자의 접근 방식, 실제 검증 결과를 함께 본다. -4. 관련 `docs/decisions` 문서가 있으면 완료된 유사 미션의 판단이나 참고 맥락만 보조적으로 확인한다. -5. 아래 순서로 평가한다. - - 정합성과 버그 위험 - - 회귀 위험 - - 안정성 - - 성능 - - 설계 -6. 잘한 점보다 부족한 점과 남은 리스크를 먼저 정리한다. -7. 미션 완료 기준을 충족했는지 분명히 말한다. - -## 출력 원칙 - -- 코드 리뷰처럼 구체적으로 말한다. -- 막연한 칭찬보다 “왜 통과/미통과인지”를 설명한다. -- 필요하면 다음 수정 포인트를 1~3개로 제한해 제시한다. diff --git a/.codex/skills/mission-guide/SKILL.md b/.codex/skills/mission-guide/SKILL.md deleted file mode 100644 index dab11943..00000000 --- a/.codex/skills/mission-guide/SKILL.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -name: mission-guide -description: Use when the user is actively solving the current mission and asks for hints, guidance, code reading order, or design help without wanting the full solution. Trigger on phrases like "힌트 줘", "가이드해줘", "어디부터 봐야 해", or "이 접근이 맞아?". ---- - -# Mission Guide - -이 스킬은 현재 미션 해결 중 코칭을 제공하는 용도다. - -## 반드시 할 일 - -1. [AGENTS.md](../../../AGENTS.md)를 먼저 읽고 현재 미션 단계가 `guide` 관점인지 확인한다. -2. 루트 `MISSIONS.md`가 있으면 현재 목표, 진행 상태, 다음 시작점을 먼저 확인하고, 없으면 [docs/templates/mission-state-template.md](../../../docs/templates/mission-state-template.md)를 참고해 필요한 섹션을 먼저 세운다. -3. 완료된 미션의 과거 판단이 필요할 때만 관련 `docs/decisions` 문서를 보조 자료로 참고한다. -4. 사용자의 현재 가설, 설계, 구현 상태를 먼저 요약한다. -5. 바로 정답을 주지 말고 아래 순서를 우선한다. - - 질문 - - 코드 위치 - - 테스트/엣지 케이스 - - 설계 방향 - - 최소 예시 -6. 필요하면 함께 봐야 할 개념을 최대 3개 이하로만 연결한다. - -## 출력 원칙 - -- 힌트는 사용자가 스스로 다음 액션을 정할 수 있을 정도로만 준다. -- 구현보다 영향 범위, 테스트, 리스크를 먼저 짚는다. -- “이대로 고쳐라”보다 “무엇을 확인해야 하는가”를 먼저 말한다. diff --git a/.codex/skills/mission-interview/SKILL.md b/.codex/skills/mission-interview/SKILL.md deleted file mode 100644 index df436b5d..00000000 --- a/.codex/skills/mission-interview/SKILL.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -name: mission-interview -description: Use when the user wants a mock interview after finishing a mission. Trigger on phrases like "모의 인터뷰하자", "인터뷰 질문 해줘", or "내가 설명하는 연습을 하고 싶어". ---- - -# Mission Interview - -이 스킬은 방금 수행한 미션을 기반으로 모의 인터뷰를 진행하는 용도다. - -## 반드시 할 일 - -1. [AGENTS.md](../../../AGENTS.md)를 먼저 읽고 `interviewer` 관점으로 전환한다. -2. 루트 `MISSIONS.md`가 있으면 직전 미션의 목표, 범위, 검증 계획, 남은 이슈를 먼저 복기하고, 없으면 [docs/templates/mission-state-template.md](../../../docs/templates/mission-state-template.md)를 기준으로 핵심 항목을 복원한다. -3. 관련 `docs/decisions` 문서가 있으면 완료된 판단과 결과를 보조적으로 확인한다. -4. 직전 미션의 핵심 맥락을 짧게 정리한다. -5. 질문은 아래 주제를 우선한다. - - 왜 이 문제를 그렇게 정의했는가 - - 왜 그 설계를 택했는가 - - 대안은 무엇이었는가 - - 실패/장애/고트래픽 상황에서는 어떻게 되는가 - - 무엇을 다시 개선할 것인가 -6. 질문은 한 번에 너무 많이 주지 말고, 답변 후 꼬리 질문이 가능하게 구성한다. - -## 출력 원칙 - -- 실제 면접처럼 짧고 날카로운 질문을 우선한다. -- 기술 선택의 이유와 트레이드오프를 설명하게 만든다. -- 필요하면 마지막에 답변 품질에 대한 짧은 피드백을 준다. diff --git a/.codex/skills/mission-start/SKILL.md b/.codex/skills/mission-start/SKILL.md deleted file mode 100644 index ea0f9b5e..00000000 --- a/.codex/skills/mission-start/SKILL.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -name: mission-start -description: Use when the user explicitly starts a new backend training mission, asks for mission candidates, or wants to select the next practice topic in this repository. Trigger especially on phrases like "새로운 학습 시작하자", "다음 미션 추천해줘", or "이번엔 어떤 과제를 할까". ---- - -# Mission Start - -이 스킬은 새 미션의 시작점을 명시적으로 여는 용도다. - -## 반드시 할 일 - -1. [AGENTS.md](../../../AGENTS.md)를 먼저 읽고 현재 운영 규칙을 따른다. -2. 현재 브랜치와 작업 상태를 확인한다. -3. 현재 작업 브랜치가 `develop`에서 갈라진 작업 브랜치라면 `develop...HEAD`와 working tree 변경점을 먼저 리뷰한다. -4. 루트 `MISSIONS.md`가 있으면 직전 미션의 상태와 다음 시작점을 먼저 확인하고, 없으면 [docs/templates/mission-state-template.md](../../../docs/templates/mission-state-template.md)를 참고해 상태 파일을 바로 시작한다. 완료된 미션의 맥락이 필요할 때만 관련 `docs/decisions` 문서를 참고한다. -5. 현재 저장소 기준으로 미션 후보 2~4개를 제안한다. -6. 각 후보에 대해 아래만 짧게 제시한다. - - 미션명 - - 왜 지금 적절한지 - - 학습 포인트 - - 난이도 -7. 가능하면 추천 미션 1개를 함께 제시한다. -8. 미션이 선택되면 `MISSIONS.md`에 현재 활성 미션 상태를 바로 남길 수 있게 목표와 다음 시작점을 분명하게 정리한다. -9. 현재 PR의 변경점과 직접 연결되는 미션이 있으면 우선순위를 높인다. - -## 출력 원칙 - -- 정답이나 구현 계획을 길게 풀지 않는다. -- 사용자가 선택할 수 있을 정도로만 정보를 준다. -- 미션은 한 번에 하나의 핵심 문제만 다루게 설계한다. -- 탐색 시간이 길어져도 괜찮으니, 미션 후보의 근거를 분명하게 만든다. diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..ad2ebbcb --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.java] +indent_style = space +indent_size = 4 + +[*.gradle] +indent_style = tab + +[*.md] +trim_trailing_whitespace = false diff --git a/.github/workflows/dev-deploy.yml b/.github/workflows/dev-deploy.yml index 229ea0d5..4082196c 100644 --- a/.github/workflows/dev-deploy.yml +++ b/.github/workflows/dev-deploy.yml @@ -21,7 +21,7 @@ jobs: distribution: 'adopt' - name: Build with Gradle - run: ./gradlew build -x test + run: ./gradlew bootJar - name: Login to Docker Hub uses: docker/login-action@v2 diff --git a/.github/workflows/pr-run-test.yml b/.github/workflows/pr-run-test.yml index 0fcc00ec..16242186 100644 --- a/.github/workflows/pr-run-test.yml +++ b/.github/workflows/pr-run-test.yml @@ -12,6 +12,8 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v3 + with: + fetch-depth: 0 - name: Set up JDK 17 uses: actions/setup-java@v3 @@ -22,5 +24,5 @@ jobs: - name: Setup Docker for TestContainers uses: docker/setup-buildx-action@v2 - - name: Run tests - run: ./gradlew test \ No newline at end of file + - name: Run checks + run: ./gradlew spotlessCheck test diff --git a/AGENTS.md b/AGENTS.md index cd80c2e8..0b8529b4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,120 +1,41 @@ # AGENTS.md -## 목적 - -이 저장소는 `미션 기반 백엔드 훈련장`이다. -에이전트의 목표는 정답을 빨리 주는 것이 아니라, 사용자가 실무형 서버 개발 과제를 풀면서 충분히 고민하고 설명하는 경험을 하게 만드는 것이다. - -## 메인 역할 - -메인 에이전트는 `프로젝트 관리자 + 기술 코치 + 리뷰어`로 동작한다. -사용자는 메인 에이전트와만 상호작용하고, 메인 에이전트가 단계에 따라 역할 관점을 전환해 사용한다. - -## 4개 역할 - -### `problem-setter` - -- 현재 코드베이스와 학습 맥락을 보고 미션 후보를 제안한다. -- 미션명, 학습 목표, 난이도, 추천 이유를 함께 제공한다. - -### `guide` - -- 사용자가 문제를 해결하도록 질문과 힌트로 유도한다. -- 힌트는 `질문 -> 코드 위치 -> 테스트/엣지 케이스 -> 설계 방향 -> 최소 예시` 순으로 준다. - -### `evaluator` - -- 제출 결과가 요구사항을 충족했는지 평가한다. -- 우선순위는 `버그/회귀 -> 안정성 -> 성능 -> 설계 -> 스타일` 순이다. - -### `interviewer` - -- 방금 해결한 미션을 기반으로 모의 인터뷰를 진행한다. -- 설계 이유, 대안, 리스크, 트레이드오프를 설명하게 만든다. - -## 역할과 단계 - -- 역할: `problem-setter`, `guide`, `evaluator`, `interviewer` -- 단계: `미션 선정 -> 해결 진행 -> 제출 -> 평가 -> 인터뷰 -> 문서화` - -역할과 단계는 구분한다. -메인 에이전트는 현재 단계에 맞는 역할 관점으로 응답한다. - -## 세션 시작 규칙 - -사용자가 `"새로운 학습 시작하자"`라고 말하면 새로운 미션 사이클을 시작한다. - -기본 흐름: - -1. 현재 브랜치와 작업 상태를 확인한다. -2. 현재 작업 브랜치가 `develop`에서 갈라진 작업 브랜치라면 `develop...HEAD`와 working tree 변경점을 먼저 리뷰한다. -3. 루트 `MISSIONS.md`가 있으면 현재 활성 미션의 목표, 상태, 다음 시작점을 먼저 복원하고, 없으면 `docs/templates/mission-state-template.md`를 기준으로 새 상태 파일을 바로 만들 수 있게 안내한다. -4. 미션 후보 2~4개를 제안한다. -5. 사용자가 하나를 선택하거나 추천을 요청한다. -6. 선택된 미션의 목표, 요구사항, 제한 조건, 먼저 볼 코드를 정리한다. -7. 해결 중에는 `guide` 관점으로 돕는다. -8. 제출 후에는 `evaluator`, 필요 시 `interviewer` 관점으로 이어간다. -9. 활성 미션이 시작되면 진행 중 상태를 `MISSIONS.md`에 바로 남기고, 마지막에는 `docs/decisions`에 최종 미션 문서를 남긴다. - -## 명시적 호출 문구 - -컨텍스트가 길어질 때는 아래 문구를 명시적 시작점으로 사용한다. - -- `"새로운 학습 시작하자"`: `mission-start` -- `"힌트 줘"` 또는 `"가이드해줘"`: `mission-guide` -- `"제출할게, 평가해줘"` 또는 `"이 미션 평가해줘"`: `mission-evaluate` -- `"모의 인터뷰하자"`: `mission-interview` -- `"문서화하자"` 또는 `"회고 정리하자"`: `mission-close` - -메인 에이전트는 이 문구를 보면 해당 스킬 관점으로 컨텍스트를 다시 세운다. - -## 코칭 원칙 - -- 학습의 기본 단위는 `작업`보다 `미션`이다. -- 정답을 바로 주기보다 질문, 힌트, 리뷰를 우선한다. -- 한 번에 하나의 핵심 문제만 다룬다. -- 개념 학습은 항상 `문제 -> 구조 -> 프레임워크 -> 언어/DB -> CS` 순서로 연결한다. -- 한 미션에서 깊게 다루는 개념은 가능하면 3개를 넘기지 않는다. -- 미션 선정은 빠른 응답보다 정확한 문제 정의를 우선한다. -- 성능 개선은 측정과 근거로 설명해야 한다. -- 외부 네트워크 의존 동작은 별도 리스크로 취급한다. - -## 미션 완료 기준 - -하나의 미션은 아래 조건을 만족해야 완료로 본다. - -1. 문제와 영향 범위를 자신의 말로 설명했다. -2. 구현 전에 테스트 또는 검증 전략을 제시했다. -3. 변경이 문제 해결 방향과 일치했다. -4. 테스트, 로그, 메트릭, 측정값 중 최소 하나로 결과를 검증했다. -5. 무엇이 개선됐고 무엇이 아직 부족한지 정리했다. - -## 문서화 원칙 - -- `docs/architecture`는 현재 구조를 이해하기 위한 지도다. -- 루트 `MISSIONS.md`는 현재 활성 미션의 상태를 관리하는 작업 파일이다. -- `docs/templates/mission-state-template.md`는 `MISSIONS.md`를 시작할 때 복사하는 템플릿이다. -- `docs/decisions`는 미션 종료 후 내린 판단과 고민을 남기는 회고형 문서다. -- 기본 원칙은 `미션당 문서 하나`다. -- 미션 진행 중에는 `MISSIONS.md`를 단일 상태 소스로 사용한다. -- `docs/decisions`는 완료된 미션의 최종 판단과 결과만 남긴다. -- 문서는 `문제 -> 선택 -> 이유 -> 검증 -> 결과와 남은 이슈`가 보이게 쓴다. - -## 기본 학습 초점 - -기본적으로 아래 주제를 우선한다. - -1. 테스트 안정화 -2. 클린코드를 위한 리팩터링 -3. 측정 기반 성능 개선 -4. 고트래픽 안정성 -5. 운영성 개선 - -## 기본 시작 주제 - -현재 PR이나 working tree에서 더 직접적인 맥락이 보이지 않을 때 우선 보는 도메인이다. - -1. `streak` -2. `common/ratelimit` -3. `word` -4. `content/book` +이 문서는 AI 도구가 이 저장소에서 코드 변경을 돕기 전에 먼저 읽을 프로젝트 맥락을 정리한다. +외부 방문자를 위한 소개는 루트 [README.md](README.md)를 기준으로 하고, 상세 구조와 판단 기록은 아래 문서를 따라간다. + +## 먼저 볼 문서 + +1. [시스템 컨텍스트 다이어그램](docs/architecture/overview.md) +2. [Word 도메인 미니맵](docs/architecture/word.md) +3. [Book 도메인 미니맵](docs/architecture/content-book.md) +4. [Streak 도메인 미니맵](docs/architecture/streak.md) +5. [기술 의사결정 기록](docs/decisions/) + +## 코드 작업 전 확인할 운영 포인트 + +- 주요 데이터 저장소는 MongoDB이며, Redis는 rate limit, 짧은 상태, 분산 조정에 사용한다. +- 단어 생성 경로는 Spring AI와 AWS Bedrock 호출 비용, 실패 재시도, 동시 요청 중복을 함께 고려해야 한다. +- `word` 동적 생성 경로는 Redisson `RLock` 기반 single-flight 조정 흐름을 가진다. +- 사용자 요청 경로에는 Bucket4j 기반 rate limiting이 적용될 수 있다. +- 이미지와 파일 저장은 S3/R2 계열 외부 저장소에 의존한다. +- 알림 기능은 Firebase Cloud Messaging에 의존한다. +- 성능 변경은 가능하면 k6 프로필이나 메트릭으로 전후 차이를 확인한다. + +## 설계 및 운영 판단 기록 + +- [MongoDB 중심 데이터 모델링](docs/decisions/007-choose-mongodb-for-early-flexibility.md) +- [Redis 기반 Rate Limiting](docs/decisions/002-rate-limiting-with-bucket4j.md) +- [AI 단어 분석 파이프라인 비용과 안정성 개선](docs/decisions/005-ai-word-analysis-cost-and-reliability.md) +- [Word single-flight 분산 안정화](docs/decisions/011-word-single-flight-distributed-stability-with-redlock.md) +- [이미지 전달 최적화](docs/decisions/008-image-delivery-optimization.md) +- [DSL 기반 크롤링 규칙 관리](docs/decisions/009-dsl-driven-crawling.md) + +## 변경 작업 원칙 + +- README는 외부 방문자용으로 유지하고, 긴 설계 설명은 `docs` 아래에 둔다. +- 문서화되지 않은 모듈은 `src/main/java/com/linglevel/api` 아래 실제 패키지와 테스트를 기준으로 맥락을 확인한다. +- 커밋 메시지, 브랜치명, PR 제목과 본문은 [Repository Conventions](docs/templates/repository-conventions.md)를 따른다. +- Java 포맷은 Spotless와 google-java-format AOSP 스타일을 기준으로 하며, 코드 변경 후 필요하면 `./gradlew spotlessCheck`를 실행한다. +- 구조 변경은 관련 architecture 문서와 decision 문서의 갱신 필요성을 함께 확인한다. +- 운영 리스크가 있는 변경은 테스트, 로그, 메트릭, 부하 테스트 중 최소 하나로 검증 근거를 남긴다. +- 외부 네트워크, AI 모델, 저장소, 푸시 알림에 의존하는 코드는 실패와 비용을 별도 리스크로 다룬다. diff --git a/README.md b/README.md index 32ab91aa..b49e1b0c 100644 --- a/README.md +++ b/README.md @@ -1,66 +1,71 @@ -# Ling Level Spring API +# Ling Level API -## 프로젝트 소개 +Ling Level API는 모바일 영어 학습 앱을 위한 Spring Boot 기반 백엔드 서버입니다. +학습 콘텐츠, 단어 분석, 스트릭, 추천, 알림, 크롤링, 관리자 기능을 하나의 API에서 제공합니다. -Ling Level API는 학습 콘텐츠, 단어 학습, 스트릭, 추천, 알림 기능을 포함하는 Spring Boot 기반 백엔드 서버입니다. +이 저장소는 기능 구현뿐 아니라 구조 개선, 성능 최적화, 안정성 강화, 운영 관찰성을 함께 관리하는 것을 목표로 합니다. -이 프로젝트는 단순 기능 구현용 저장소가 아니라, 구조 개선, 성능 최적화, 안정성 강화, 의사결정 기록을 함께 관리하는 운영형 API 프로젝트를 목표로 합니다. +## 주요 기능 -## 문서 +- 책, 아티클, 커스텀 콘텐츠 기반 학습 흐름 제공 +- 단어 조회, 변형어 저장, Spring AI와 AWS Bedrock 기반 단어 분석 +- 학습 진행도와 일 단위 스트릭 계산 +- 추천, 배너, 북마크, FCM 푸시 알림 +- 관리자용 콘텐츠, 크롤링, 마이그레이션 API +- Prometheus, Grafana, Sentry 기반 모니터링 -- [프로젝트 문서 허브](docs/README.md) -- 활성 미션 상태 파일: 로컬 루트 `MISSIONS.md` (`docs/templates/mission-state-template.md` 기준) -- [아키텍처 문서 모음](docs/architecture/) -- [의사결정 기록 모음](docs/decisions/) +## 기술 스택 -## 교육용 스킬 +- Java 17, Spring Boot 3.5, Spring Security +- MongoDB, Redis, Redisson +- Spring AI, AWS Bedrock +- AWS S3 / Cloudflare R2, Firebase Cloud Messaging +- Bucket4j, Micrometer, Prometheus, Grafana, Sentry +- Docker Compose, k6, Testcontainers -이 저장소는 미션 기반 학습을 위해 repo-local Codex skill을 함께 관리합니다. +## 로컬 실행 -- [mission-start](.codex/skills/mission-start/SKILL.md) -- [mission-guide](.codex/skills/mission-guide/SKILL.md) -- [mission-evaluate](.codex/skills/mission-evaluate/SKILL.md) -- [mission-interview](.codex/skills/mission-interview/SKILL.md) -- [mission-close](.codex/skills/mission-close/SKILL.md) - -## 사전 요구사항 +### 사전 요구사항 - JDK 17 -- docker compose +- Docker Compose +- `.env.local` -## 로컬 실행 방법 +`docker-compose.yml`은 `.env.local`을 읽어 애플리케이션 환경 변수를 주입합니다. +민감한 값이 포함될 수 있어 예시 파일은 저장소에 포함하지 않습니다. -> 주의 : `.env.local` 파일이 존재해야 합니다. +### 실행 ```bash -# Docker Compose를 사용한 실행 - ./gradlew clean build && docker-compose up +./gradlew clean build +docker compose up --build ``` -## 접속 정보 +애플리케이션은 기본적으로 `local` 프로필과 `8080` 포트를 사용합니다. -- **Swagger UI**: `http://localhost:8080/swagger-ui/index.html` +### 접속 정보 -> Swagger UI는 local, dev 프로필에서만 활성화됩니다. +- Swagger UI: `http://localhost:8080/swagger-ui/index.html` +- API Docs: `http://localhost:8080/api-docs` +- Actuator health: `http://localhost:8080/actuator/health` -## 모니터링 (선택사항) +Swagger UI와 API Docs는 `local`, `dev` 프로필에서만 활성화됩니다. -### 로컬 개발용 모니터링: -```bash -# 로컬 앱만 모니터링 -docker-compose -f monitoring/docker-compose.monitoring-local.yml up -d +## 테스트 -# 접속 정보 -# Prometheus: http://localhost:9090 -# Grafana: http://localhost:3000 (admin/admin123 또는 환경변수) +```bash +./gradlew test ``` -### 운영환경 모니터링: -```bash -# dev + prod 환경 통합 모니터링 -docker-compose -f monitoring/docker-compose.monitoring-prod.yml up -d +일부 통합 테스트는 Testcontainers를 사용하므로 Docker가 실행 중이어야 합니다. -# 접속 정보 -# Prometheus: http://localhost:9090 -# Grafana: http://localhost:3000 (admin/admin123 또는 환경변수) +## 모니터링 + +로컬 앱 메트릭을 Prometheus와 Grafana로 확인할 수 있습니다. + +```bash +docker compose -f monitoring/docker-compose.monitoring-local.yml up -d ``` + +- Prometheus: `http://localhost:9090` +- Grafana: `http://localhost:3000` diff --git a/build.gradle b/build.gradle index 167f0ecb..7122a32c 100644 --- a/build.gradle +++ b/build.gradle @@ -3,6 +3,7 @@ plugins { id 'org.springframework.boot' version '3.5.3' id 'io.spring.dependency-management' version '1.1.7' id "io.sentry.jvm.gradle" version "5.10.0" + id 'com.diffplug.spotless' version '8.7.0' } springBoot { @@ -28,6 +29,15 @@ repositories { mavenCentral() } +spotless { + ratchetFrom 'origin/develop' + + java { + googleJavaFormat('1.28.0').aosp() + formatAnnotations() + } +} + dependencies { implementation platform('org.springframework.ai:spring-ai-bom:1.0.0-M6') implementation 'org.springframework.boot:spring-boot-starter-web' @@ -72,3 +82,7 @@ dependencies { tasks.named('test') { useJUnitPlatform() } + +tasks.named('check') { + dependsOn 'spotlessCheck' +} diff --git a/docs/README.md b/docs/README.md index 924e2c69..e409df65 100644 --- a/docs/README.md +++ b/docs/README.md @@ -4,16 +4,18 @@ ## 문서 구성 +- [AI 개발 컨텍스트](../AGENTS.md) - [아키텍처 문서 모음](architecture/) - [의사결정 기록 모음](decisions/) - [템플릿 모음](templates/) ## 언제 무엇을 쓰는가 +- AI 도구가 코드 작업 전 프로젝트 맥락을 빠르게 잡아야 할 때: [AGENTS.md](../AGENTS.md) - 현재 구조, 도메인 관계, 대표 흐름을 정리할 때: [architecture](architecture/) -- 미션을 수행하며 내린 큰 판단과 고민을 미션당 문서 하나로 남길 때: [decisions](decisions/) +- 구조, 성능, 안정성, 운영성에 영향을 주는 큰 판단을 남길 때: [decisions](decisions/) - 새 문서를 시작할 때: [templates](templates/) -- 활성 미션 상태 파일이 필요할 때: [mission-state-template.md](templates/mission-state-template.md) 를 복사해 루트 `MISSIONS.md`로 사용 +- 브랜치, 커밋, PR 형식을 확인할 때: [repository-conventions.md](templates/repository-conventions.md) ## 기본 원칙 diff --git a/docs/architecture/README.md b/docs/architecture/README.md index 157619ca..b5a7843d 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -9,7 +9,7 @@ - 도메인별로 `핵심 용어 사전(용어/정의)`을 유지해 의미 흔들림을 줄인다. - 도메인 의미에 영향을 주는 선택은 `간결 의사결정 기록`에 누적한다. - 핵심 기능은 중요도와 이해 난이도를 기준으로 2~3개만 고른다. -- 도메인 내부 구현 상세나 세부 설계 판단은 `MISSIONS.md` 또는 `docs/decisions`에서 다룬다. +- 도메인 내부 구현 상세나 세부 설계 판단은 `docs/decisions`에서 다룬다. ## 템플릿 diff --git a/docs/decisions/010-mission-oriented-agent-guidelines.md b/docs/decisions/010-mission-oriented-agent-guidelines.md deleted file mode 100644 index 532cfbdb..00000000 --- a/docs/decisions/010-mission-oriented-agent-guidelines.md +++ /dev/null @@ -1,40 +0,0 @@ -# 미션 기반 Codex 에이전트 운영 규칙 정리 - -## 문제 - -이 저장소는 미션 기반 백엔드 훈련장으로 쓰고 싶었지만, 기존 에이전트 규칙은 실습 흐름보다 일반적인 저장소 운영 안내에 더 가까웠다. -그 결과 미션 선정, 힌트 제공, 평가, 인터뷰, 문서화가 하나의 학습 사이클로 연결되지 않았고, 긴 세션에서는 현재 미션 상태를 잃기 쉬웠다. -또한 미션 시작 시 현재 PR 변경점보다 추상적인 도메인 이름을 먼저 보게 되면, 실제 변경과 동떨어진 문제를 제안할 위험이 있었다. - -## 선택 - -에이전트 규칙을 `미션 선정 -> 해결 진행 -> 제출 -> 평가 -> 인터뷰 -> 문서화` 사이클 기준으로 다시 정리하고, 각 단계에 대응하는 repo-local skill을 명시적으로 분리했다. -미션 시작은 `develop...HEAD`와 working tree 변경점 리뷰를 먼저 보도록 바꾸고, 활성 미션 상태는 루트 `MISSIONS.md`에서 관리하기로 했다. -`docs/decisions`는 미션 종료 후 핵심 판단과 검증 결과를 남기는 최종 기록으로만 사용한다. - -## 이유 - -학습 품질은 좋은 정답보다 좋은 문제 정의와 일관된 피드백 루프에서 나온다. -그래서 에이전트가 “무엇을 구현할지”보다 “지금 어떤 미션 단계에 있는지”를 먼저 인식하게 만드는 편이 맞았다. -또한 현재 브랜치의 변경점을 먼저 보면 실제 PR과 연결된 미션을 제안할 수 있어, 미션의 정확도와 리뷰 가능성이 같이 좋아진다. -활성 상태와 최종 회고를 같은 문서에 섞으면 진행 중 메모와 완료된 판단의 책임이 흐려진다. -그래서 진행 중 상태는 `MISSIONS.md`에 두고, 미션 종료 후에만 `docs/decisions`로 요약하는 편이 더 단순하고 덜 흔들린다. - -## 검증 - -- [AGENTS.md](../../AGENTS.md) 에서 역할, 단계, 세션 시작 규칙, 문서화 원칙이 미션 중심으로 정리되었는지 확인한다. -- [mission-state-template.md](../templates/mission-state-template.md) 에서 활성 미션 상태 파일의 기본 템플릿을 확인한다. -- [mission-start/SKILL.md](../../.codex/skills/mission-start/SKILL.md) 와 [mission-evaluate/SKILL.md](../../.codex/skills/mission-evaluate/SKILL.md) 에서 `develop...HEAD` 리뷰 우선 규칙과 `MISSIONS.md` 참조 흐름을 확인한다. -- [docs/decisions/README.md](README.md) 와 [decision-record-template.md](../templates/decision-record-template.md) 에서 종료 후 회고 문서 규칙을 확인한다. -- 실제 대화 dry-run으로 `mission-start`, `mission-evaluate`, `mission-close` 출력이 이전보다 덜 흔들리는지 검토한다. - -## 결과와 남은 이슈 - -- 미션 기반 에이전트 설계의 방향과 출력 규칙은 한 문서 집합으로 정리되었고, 현재 PR 변경점 기반 미션 선정 흐름도 명시됐다. -- `MISSIONS.md`가 활성 미션 상태의 단일 소스로 추가됐고, decision 문서는 미션 종료 후 판단과 회고를 남기는 기록으로 정리됐다. -- `MISSIONS.md`와 mission 스킬 사이의 자동 동기화 규칙은 더 구체화할 수 있다. - -## 연관 이슈 및 PR - -- 관련 이슈: 없음 -- 관련 PR: 없음 diff --git a/docs/decisions/README.md b/docs/decisions/README.md index f792d8db..30a1d17e 100644 --- a/docs/decisions/README.md +++ b/docs/decisions/README.md @@ -1,6 +1,6 @@ # Decision Records -이 디렉터리는 미션을 수행하며 내린 큰 기술적 선택과 개선 판단을 기록하는 문서 모음이다. +이 디렉터리는 프로젝트를 개발하며 내린 큰 기술적 선택과 개선 판단을 기록하는 문서 모음이다. 톤은 딱딱한 ADR보다 기술 블로그형 회고에 가깝게 유지한다. ## 기록 대상 @@ -14,7 +14,7 @@ - 자잘한 구현 선택은 기록하지 않는다. - 문제, 선택, 이유, 검증, 결과와 남은 이슈 중심으로 짧게 정리한다. -- 기본 원칙은 `미션당 문서 하나`다. +- 기본 원칙은 `큰 판단 하나당 문서 하나`다. - 문서 하단에는 `연관 이슈 및 PR` 섹션을 두고, 추적 가능한 이슈와 PR을 남긴다. - 구현 자체보다 왜 그런 선택을 했는지와 그 결과를 이해할 수 있게 적는다. - 구현보다 고민과 판단의 맥락이 먼저 보이게 적는다. @@ -34,5 +34,4 @@ - [007. 서비스 초기 데이터 저장소를 MongoDB 중심으로 고정](007-choose-mongodb-for-early-flexibility.md) - [008. 글로벌 이미지 전달 성능 최적화](008-image-delivery-optimization.md) - [009. DSL 기반 크롤링 규칙 관리 구조 도입](009-dsl-driven-crawling.md) -- [010. 미션 기반 Codex 에이전트 운영 규칙 정리](010-mission-oriented-agent-guidelines.md) - [011. Word Single-Flight 분산 안정화와 RLock 표준화](011-word-single-flight-distributed-stability-with-redlock.md) diff --git a/docs/templates/decision-record-template.md b/docs/templates/decision-record-template.md index 421c126a..5ef25c84 100644 --- a/docs/templates/decision-record-template.md +++ b/docs/templates/decision-record-template.md @@ -3,12 +3,12 @@ ## 제목 짧고 명확한 결정 제목 또는 회고 제목 -가능하면 하나의 미션을 대표하는 제목으로 적는다. +가능하면 하나의 큰 기술적 판단을 대표하는 제목으로 적는다. ## 문제 현재 어떤 구조적, 성능적, 안정성 문제를 해결하려는지 적는다. -가능하면 이 문제가 왜 학습 가치가 있는지도 함께 적는다. +가능하면 이 문제가 왜 프로젝트나 운영 관점에서 중요한지도 함께 적는다. ## 선택 diff --git a/docs/templates/mission-state-template.md b/docs/templates/mission-state-template.md deleted file mode 100644 index b56bbc58..00000000 --- a/docs/templates/mission-state-template.md +++ /dev/null @@ -1,54 +0,0 @@ -# Mission State Template - -이 템플릿은 루트 `MISSIONS.md`를 새로 만들 때 복사해서 쓰는 용도다. -`MISSIONS.md`는 활성 미션의 상태를 관리하는 로컬 작업 파일이고, 완료된 미션의 회고는 `docs/decisions`에 남긴다. - -## 운영 원칙 - -- 활성 미션 상태의 단일 소스는 루트 `MISSIONS.md`다. -- 한 번에 하나의 활성 미션만 관리한다. -- 세션이 바뀌면 이 파일을 먼저 읽고 현재 상태를 복원한다. -- 완료된 미션의 회고는 `docs/decisions`에 남긴다. - -## 현재 활성 미션 - -- 미션명: -- 단계: -- 관련 이슈: -- 관련 PR: - -## 목표 - -- - -## 범위 - -- - -## 비범위 - -- - -## 요구사항 - -- - -## 검증 계획 - -- - -## 현재 이해 - -- - -## 작업 로그 - -- YYYY-MM-DD: - -## 다음 시작점 - -- - -## decision 문서로 옮길 핵심 판단 - -- diff --git a/docs/templates/repository-conventions.md b/docs/templates/repository-conventions.md new file mode 100644 index 00000000..0b84653b --- /dev/null +++ b/docs/templates/repository-conventions.md @@ -0,0 +1,114 @@ +# Repository Conventions + +이 문서는 브랜치, 커밋 메시지, PR 작성 형식을 정의한다. +새 작업을 시작하거나 PR을 만들 때는 최근 git log와 이 문서를 함께 확인한다. + +## 브랜치명 + +기본 형식: + +```text +/ +``` + +사용하는 type: + +- `feat`: 새 기능 +- `fix`: 버그 수정 +- `refactor`: 구조 개선 또는 리팩터링 +- `docs`: 문서 변경 +- `hotfix`: 긴급 수정 +- `codex`: AI 에이전트가 분리 작업을 수행하는 임시 작업 브랜치 + +예시: + +```text +docs/readme-update +refactor/optimize-books-domain +fix/stabilize-failing-tests +codex/readme-update +``` + +## 커밋 메시지 + +기본 형식: + +```text +(): +``` + +scope가 명확하지 않으면 생략한다. + +```text +: +``` + +사용하는 type: + +- `feat`: 사용자 기능 추가 +- `fix`: 버그 또는 회귀 수정 +- `refactor`: 동작 변경 없는 구조 개선 +- `docs`: 문서 변경 +- `test`: 테스트 추가 또는 수정 +- `chore`: 빌드, 설정, 운영 보조 변경 + +작성 원칙: + +- summary는 영어 소문자 명령형에 가깝게 짧게 쓴다. +- 한 커밋은 하나의 의도를 담는다. +- 문서만 바꾼 경우 `docs:`를 사용한다. +- 도메인이 분명하면 `fix(word): ...`, `refactor(book): ...`처럼 scope를 붙인다. + +예시: + +```text +docs: refine project readme and agent context +docs(decisions): add single-flight stability closure report +fix(word): patch single-flight lock expiry +refactor(word): optimize indexes and transaction boundaries +``` + +## PR 제목 + +기본 형식은 커밋 메시지와 동일하게 둔다. +여러 커밋이 포함된 PR이면 전체 변경을 대표하는 제목을 쓴다. + +예시: + +```text +docs: refine project documentation entrypoints +refactor(word): optimize single-flight persistence path +``` + +## PR 본문 + +PR 본문은 [pull_request_template.md](../../.github/pull_request_template.md)를 따른다. +각 섹션은 비워두지 말고, 해당 내용이 없으면 `없음` 또는 `문서 변경이라 예시 없음`처럼 명시한다. + +필수 섹션: + +- `Summary`: 변경사항 요약 +- `Problem`: 해결하려는 문제 +- `Solution`: 해결 방법 +- `Changes`: 주요 변경사항 +- `Example`: 예시나 사용법 +- `Related Issues`: 연관 이슈 + +## 코드 스타일 + +Java 포맷은 Spotless와 google-java-format AOSP 스타일을 기준으로 한다. +AOSP 스타일을 사용해 기존 Java/Spring 코드의 4-space indentation을 유지한다. + +명령: + +```bash +./gradlew spotlessCheck +./gradlew spotlessApply +``` + +작성 원칙: + +- 포맷 검사는 `spotlessCheck`로 수행한다. +- 자동 포맷 적용은 `spotlessApply`로 수행한다. +- 포맷 변경은 기능 변경과 분리한다. +- 기존 전체 Java 파일 재포맷은 별도 PR로 분리한다.