Skip to content

Commit d2c6e32

Browse files
authored
Merge pull request #39 from HappyProcess/fix/cors-vercel-origin
CORS 허용 출처 추가 및 프로젝트 작업 가이드/코드리뷰 스킬 도입
2 parents fa67db2 + ec2d2d6 commit d2c6e32

4 files changed

Lines changed: 204 additions & 0 deletions

File tree

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
---
2+
name: code-reviewer
3+
description: >-
4+
현재 브랜치의 변경분(main 대비 git diff)을 리뷰해서 버그/로직 오류, 성능 문제(N+1 등),
5+
코드 스타일/가독성 관점의 핵심 지적을 한국어로 간결하게 정리한다. 사용자가 "리뷰",
6+
"코드 리뷰", "이 변경 봐줘", "PR 올리기 전에 점검" 등을 요청할 때 사용한다. 또한 일련의
7+
구현 작업이 끝났을 때 — 기능 추가나 리팩토링을 마치고 커밋/PR 직전 단계에 도달했을 때 —
8+
사용자가 명시적으로 요청하지 않았더라도 이 스킬을 적극적으로 사용해 변경분을 점검할 것.
9+
---
10+
11+
# Code Reviewer
12+
13+
현재 브랜치에서 `main` 대비 바뀐 코드를 리뷰한다. 목표는 **장황한 리포트가 아니라
14+
실제로 고칠 가치가 있는 핵심 지적 몇 가지**를 빠르게 짚어주는 것이다.
15+
16+
## 언제 실행하나
17+
18+
- 사용자가 리뷰를 요청할 때 ("리뷰해줘", "PR 올리기 전에 봐줘" 등)
19+
- 기능 구현이나 리팩토링이 일단락되어 커밋/PR 직전 단계에 도달했을 때. 이때는 사용자가
20+
명시적으로 부탁하지 않았더라도, 한 줄로 "변경분을 리뷰하고 갈까요?" 정도로 제안하거나
21+
바로 가볍게 점검해주는 것이 자연스럽다.
22+
23+
## 리뷰 대상 파악
24+
25+
먼저 무엇이 바뀌었는지 본다. 보통 현재 브랜치와 `main`의 차이가 리뷰 범위다.
26+
27+
```bash
28+
git diff main...HEAD # 브랜치에서 추가된 커밋들의 변경분
29+
git diff main...HEAD --stat # 어떤 파일이 얼마나 바뀌었는지 개요
30+
```
31+
32+
아직 커밋 안 된 작업 중인 변경이 있다면 그것도 포함한다(`git status`, `git diff`).
33+
`main`이 없거나 비교 기준이 애매하면 사용자에게 어떤 범위를 리뷰할지 한 번 확인한다.
34+
35+
변경된 파일이 많을 때는 `--stat`으로 핵심 파일을 먼저 추린 뒤, 의미 있는 변경이
36+
담긴 파일을 실제로 읽어본다. diff만으로는 맥락이 부족하면 주변 코드도 함께 읽는다.
37+
38+
## 무엇을 보나
39+
40+
우선순위 순으로 본다. 아래 세 가지에 집중하고, 그 외(문서 오타 등)는 굳이 지적하지 않는다.
41+
42+
1. **버그 / 로직 오류** — 가장 중요하다. 실제 동작이 의도와 달라질 수 있는 부분.
43+
경계 조건, null/예외 처리 누락, 잘못된 조건문, 사이드 이펙트, 동시성 문제 등.
44+
2. **성능** — 특히 반복문 안의 DB 호출(N+1), 불필요한 외부 API 호출, 중복 연산,
45+
비효율적인 자료구조. 이 프로젝트는 과거 N+1과 불필요한 외부 호출을 제거한 이력이
46+
있으니 같은 패턴이 다시 들어왔는지 눈여겨본다.
47+
3. **코드 스타일 / 가독성** — 네이밍, 구조, 주변 코드와의 컨벤션 일관성. 단, 사소한
48+
취향 차이는 줄이고 실제로 읽기 어렵거나 오해를 부르는 부분만 짚는다.
49+
50+
각 지적은 **왜 문제인지** 이유를 함께 설명한다. 단순히 "이렇게 바꿔라"가 아니라
51+
"이 경우 X가 null이면 NPE가 난다"처럼 근거를 댄다.
52+
53+
## 출력 형식
54+
55+
간결하게. 아래 틀을 따른다.
56+
57+
```
58+
## 리뷰 요약
59+
(1~2문장으로 전체 변경에 대한 총평. 큰 문제가 없으면 그렇다고 분명히 말한다.)
60+
61+
## 짚을 점
62+
- `파일경로:라인` — [심각도] 무엇이 문제이고 왜 문제인지. 가능하면 어떻게 고칠지 한 줄.
63+
- ...
64+
```
65+
66+
- 심각도는 **버그 / 성능 / 스타일** 정도로 가볍게 태그한다.
67+
- 지적이 없으면 없는 대로 "특별히 고칠 점 없음"이라고 솔직하게 말한다. 억지로
68+
지적을 만들어내지 않는다.
69+
- 정말 중요한 것부터 위에 둔다. 보통 3~7개 이내가 적당하고, 더 많으면 사용자가
70+
소화하기 어렵다.
71+
72+
##
73+
74+
리뷰 결과는 한국어로 작성한다. 동료에게 코드리뷰 코멘트를 남기듯, 단정적이되
75+
근거를 갖춘 톤으로 쓴다.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#!/usr/bin/env bash
2+
# Stop 훅: 현재 브랜치에 main 대비(또는 커밋 안 된) 변경분이 있는데
3+
# 아직 리뷰되지 않았으면 Claude가 code-reviewer 스킬로 리뷰하도록 알린다.
4+
#
5+
# 무한 루프 방지:
6+
# 1) stop_hook_active=true 면 즉시 종료 (직전 블록으로 인한 재진입)
7+
# 2) 변경분 해시를 .git/.code-review-done 에 기록 → 같은 변경분은 한 번만 알림
8+
9+
input=$(cat)
10+
11+
# 1) 직전 Stop 훅 블록으로 다시 들어온 경우 → 그냥 종료
12+
[ "$(printf '%s' "$input" | jq -r '.stop_hook_active // false')" = "true" ] && exit 0
13+
14+
# git 저장소가 아니면 종료
15+
root=$(git rev-parse --show-toplevel 2>/dev/null) || exit 0
16+
17+
# 비교 기준 브랜치(main 없으면 master, 둘 다 없으면 커밋 안 된 변경만)
18+
base=""
19+
for b in main master; do
20+
if git rev-parse --verify --quiet "$b" >/dev/null; then base="$b"; break; fi
21+
done
22+
23+
if [ -n "$base" ]; then
24+
diff=$(git diff "$base"...HEAD; git diff; git diff --staged)
25+
else
26+
diff=$(git diff; git diff --staged)
27+
fi
28+
29+
# 변경분 없으면 종료
30+
[ -z "$diff" ] && exit 0
31+
32+
# 이미 같은 변경분을 리뷰 알림했으면 종료
33+
hash=$(printf '%s' "$diff" | shasum | cut -d' ' -f1)
34+
marker="$root/.git/.code-review-done"
35+
[ "$(cat "$marker" 2>/dev/null)" = "$hash" ] && exit 0
36+
printf '%s' "$hash" > "$marker"
37+
38+
# 변경분 있음 → 리뷰 유도 (Stop을 차단하고 reason을 Claude에 전달)
39+
printf '%s' '{"decision":"block","reason":"현재 브랜치에 아직 리뷰하지 않은 코드 변경분이 있습니다. code-reviewer 스킬을 사용해 변경분(버그/로직, 성능, 스타일 관점)을 리뷰하고 핵심 지적을 한국어로 정리하세요. 방금 구현을 마친 직후라면 특히 유용합니다. 이미 이번 작업의 변경분을 리뷰했거나 사용자가 리뷰를 원치 않는다고 했다면, 추가 작업 없이 그대로 종료하세요."}'

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,6 @@ src/main/resources/application.yml
4343
## docker
4444
docker-compose.yml
4545

46+
47+
.DS_Store
48+
.claude/settings.local.json

CLAUDE.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# HaapyProcess 프로젝트 작업 가이드
2+
3+
## 작업 워크플로우
4+
5+
리팩토링/기능 작업 시 아래 순서를 따른다.
6+
7+
1. **브랜치 생성**: `git checkout -b <type>/<description>`
8+
2. **테스트 먼저 작성 (TDD)**: 구현 전에 실패하는 단위 테스트부터 작성한다 (아래 TDD 원칙 참고)
9+
3. **코드 작업**: 테스트를 통과시키는 최소 구현 → 리팩토링
10+
4. **빌드 + 테스트 검증**: `./gradlew test` (또는 `./gradlew compileJava`로 컴파일만; DB 없는 환경에서는 통합 테스트 실패는 무시하되 단위 테스트는 통과해야 한다)
11+
5. **커밋**: 변경사항 커밋 (테스트 코드 포함)
12+
6. **푸시 및 PR**: `git push -u origin <branch>``gh pr create --base main`
13+
14+
## TDD / 단위 테스트 원칙
15+
16+
이 프로젝트는 **테스트 주도 개발(TDD)** 을 기본 개발 방식으로 삼는다. 기능을 먼저 짜고
17+
테스트를 나중에 붙이는 게 아니라, **테스트가 설계를 이끈다**.
18+
19+
### Red → Green → Refactor
20+
21+
1. **Red** — 구현하려는 동작을 검증하는 **실패하는 테스트**를 먼저 작성한다. 테스트가
22+
실패하는 것을 눈으로 확인한다(테스트 자체가 맞는지 검증하는 단계).
23+
2. **Green** — 테스트를 통과시키는 **최소한의 코드**만 작성한다. 과한 일반화는 하지 않는다.
24+
3. **Refactor** — 테스트가 초록불인 상태에서 중복 제거·구조 개선. 테스트가 안전망이 된다.
25+
26+
### 무엇을, 어떻게 테스트하나
27+
28+
- **단위 테스트 우선** — 서비스/도메인 로직은 외부 의존성(DB, 외부 API)을 Mock으로
29+
분리해 **순수 단위 테스트**로 검증한다. JUnit5 + Mockito 기준.
30+
- **경계·예외부터** — happy path만이 아니라 null/빈 값/경계 조건/예외 흐름을 반드시 덮는다.
31+
버그는 대부분 경계에서 난다.
32+
- **테스트는 하나의 행위만** — 테스트 이름은 "무엇을 검증하는지" 한국어로 명확히 쓴다
33+
(예: `가족이_없으면_빈_리스트를_반환한다`). given-when-then 구조를 권장한다.
34+
- **외부 호출 금지** — 단위 테스트는 실제 DB·네트워크에 접근하지 않는다. 통합이 필요한
35+
검증은 별도 통합 테스트로 분리한다.
36+
- **회귀 테스트** — 버그를 고칠 때는 그 버그를 재현하는 테스트를 먼저 추가한 뒤 고친다.
37+
38+
### 강제 원칙
39+
40+
- 새 로직(서비스 메서드, 도메인 규칙)에는 **반드시 단위 테스트가 동반**되어야 한다.
41+
테스트 없는 기능 코드는 미완성으로 본다.
42+
- 커밋 전에 `./gradlew test`로 단위 테스트가 **모두 통과**하는지 확인한다.
43+
- 리팩토링 PR이라도 기존 테스트가 깨지지 않아야 하며, 동작 변경이면 테스트도 함께 수정한다.
44+
45+
## 코드 스타일 — 관심사의 분리
46+
47+
이 프로젝트는 **도메인 기반 패키지 구조(package-by-feature)** 를 따른다.
48+
`domain/<도메인>/{controller, service, repository, entity, dto}` + 공통은 `global/`.
49+
핵심은 **도메인끼리, 계층끼리 책임이 섞이지 않게 하는 것**이다. 의존성이 꼬이면
50+
변경 한 곳이 엉뚱한 곳을 깨뜨리고, 테스트도 어려워진다.
51+
52+
### 계층별 책임 (Layered Architecture)
53+
54+
각 계층은 자기 일만 한다. 책임을 넘나들지 않는다.
55+
56+
- **Controller** — HTTP 요청/응답 처리만. 파라미터 검증, DTO 매핑, 서비스 호출.
57+
**비즈니스 로직을 넣지 않는다.** 엔티티를 직접 반환하지 말고 응답 DTO로 변환한다.
58+
- **Service** — 비즈니스 로직과 트랜잭션 경계(`@Transactional`)를 담당. 도메인 규칙은
59+
여기 또는 엔티티에 둔다. 컨트롤러나 웹 관심사(HttpServletRequest 등)를 알지 못한다.
60+
- **Repository** — 데이터 접근만. JPA 쿼리. 비즈니스 판단을 넣지 않는다.
61+
- **Entity** — 도메인 상태와 그에 직접 속한 규칙. 외부 계층(DTO, 웹)을 의존하지 않는다.
62+
- **DTO** — 계층 간 데이터 전달. 엔티티를 API 경계 밖으로 노출하지 않기 위한 방어막.
63+
64+
### 도메인 간 의존성 규칙
65+
66+
- **도메인은 서로 느슨하게.** `family``member`를 알아야 한다면, 상대 도메인의
67+
**Service(공개 API)를 통해서만** 협력한다. 남의 도메인 Repository·Entity 내부에
68+
직접 손대지 않는다.
69+
- **순환 의존 금지.** A 도메인이 B를, B가 다시 A를 의존하는 구조를 만들지 않는다.
70+
순환이 생기면 도메인 경계가 잘못 그어진 신호다 — 책임을 재배치하거나 공통 부분을
71+
분리한다.
72+
- **양방향 결합 주의.** 꼭 필요한 방향으로만 의존한다. 의존 방향은 한쪽으로 흐르게 한다.
73+
- **공통 관심사는 `global/`로.** 보안·예외·설정·JWT 등 여러 도메인이 공유하는 것은
74+
특정 도메인에 두지 말고 `global/`에 둔다. 반대로 특정 도메인 전용 로직을 `global/`
75+
올리지 않는다.
76+
77+
### 실천 지침
78+
79+
- 새 기능은 **해당 도메인 패키지 안에서** 완결되게 짠다. 다른 도메인 패키지를 수정해야
80+
한다면, 경계가 맞는지 먼저 의심한다.
81+
- "이 클래스가 왜 이걸 알아야 하지?"를 자문한다. 답이 군색하면 책임이 잘못 배치된 것이다.
82+
- 엔티티를 컨트롤러/외부로 그대로 흘려보내지 않는다 (DTO 변환).
83+
- 한 메서드·클래스가 여러 이유로 바뀐다면 분리를 고려한다 (단일 책임).
84+
85+
## 주의사항
86+
87+
- 커밋 메시지, PR 본문에 AI 작성 티를 내지 않는다 (Co-Authored-By, Generated with 등 금지)

0 commit comments

Comments
 (0)