backend-claude-code 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.json +42 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +35 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +33 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +32 -0
- package/.mcp.json +19 -0
- package/CLAUDE.md +126 -0
- package/README.md +142 -0
- package/agents/code-reviewer.md +84 -0
- package/agents/database-reviewer.md +91 -0
- package/agents/java-build-resolver.md +127 -0
- package/agents/java-performance-reviewer.md +262 -0
- package/agents/planner.md +99 -0
- package/agents/security-reviewer.md +119 -0
- package/agents/tdd-guide.md +189 -0
- package/bin/cli.js +144 -0
- package/commands/db-migrate.md +134 -0
- package/commands/dev-build.md +72 -0
- package/commands/dev-coverage.md +73 -0
- package/commands/dev-fix.md +75 -0
- package/commands/dev-plan.md +501 -0
- package/commands/dev-review.md +144 -0
- package/commands/dev-run.md +385 -0
- package/commands/dev-test.md +89 -0
- package/commands/dev-verify.md +95 -0
- package/commands/dev.md +45 -0
- package/commands/git-commit.md +112 -0
- package/commands/git-issue.md +74 -0
- package/commands/git-pr.md +184 -0
- package/commands/git-push.md +28 -0
- package/package.json +24 -0
- package/rules/architecture.md +33 -0
- package/rules/coding-style.md +113 -0
- package/rules/controller-patterns.md +63 -0
- package/rules/dto-patterns.md +76 -0
- package/rules/entity-patterns.md +70 -0
- package/rules/error-handling.md +56 -0
- package/rules/hooks.md +73 -0
- package/rules/repository-patterns.md +75 -0
- package/rules/security.md +101 -0
- package/rules/service-patterns.md +70 -0
- package/rules/testing.md +174 -0
- package/skills/api-design/SKILL.md +523 -0
- package/skills/architecture-decision-records/SKILL.md +179 -0
- package/skills/database-migrations/SKILL.md +429 -0
- package/skills/hexagonal-architecture/SKILL.md +276 -0
- package/skills/java-coding-standards/SKILL.md +236 -0
- package/skills/jpa-patterns/SKILL.md +218 -0
- package/skills/postgres-patterns/SKILL.md +147 -0
- package/skills/springboot-patterns/SKILL.md +255 -0
- package/skills/springboot-security/SKILL.md +241 -0
- package/skills/springboot-tdd/SKILL.md +236 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Quick commit with natural language file targeting — describe what to commit in plain English"
|
|
3
|
+
argument-hint: "[target description] (blank = all changes)"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Smart Commit
|
|
7
|
+
|
|
8
|
+
> Adapted from PRPs-agentic-eng by Wirasm. Part of the PRP workflow series.
|
|
9
|
+
|
|
10
|
+
**Input**: $ARGUMENTS
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Phase 1 — ASSESS
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
git status --short
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
If output is empty → stop: "Nothing to commit."
|
|
21
|
+
|
|
22
|
+
Show the user a summary of what's changed (added, modified, deleted, untracked).
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Phase 2 — INTERPRET & STAGE
|
|
27
|
+
|
|
28
|
+
Interpret `$ARGUMENTS` to determine what to stage:
|
|
29
|
+
|
|
30
|
+
| Input | Interpretation | Git Command |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| *(blank / empty)* | Stage everything | `git add -A` |
|
|
33
|
+
| `staged` | Use whatever is already staged | *(no git add)* |
|
|
34
|
+
| `*.ts` or `*.py` etc. | Stage matching glob | `git add '*.ts'` |
|
|
35
|
+
| `except tests` | Stage all, then unstage tests | `git add -A && git reset -- '**/*.test.*' '**/*.spec.*' '**/test_*' 2>/dev/null \|\| true` |
|
|
36
|
+
| `only new files` | Stage untracked files only | `git ls-files --others --exclude-standard \| grep . && git ls-files --others --exclude-standard \| xargs git add` |
|
|
37
|
+
| `the auth changes` | Interpret from status/diff — find auth-related files | `git add <matched files>` |
|
|
38
|
+
| Specific filenames | Stage those files | `git add <files>` |
|
|
39
|
+
|
|
40
|
+
For natural language inputs (like "the auth changes"), cross-reference the `git status` output and `git diff` to identify relevant files. Show the user which files you're staging and why.
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
git add <determined files>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
After staging, verify:
|
|
47
|
+
```bash
|
|
48
|
+
git diff --cached --stat
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
If nothing staged, stop: "No files matched your description."
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Phase 3 — COMMIT
|
|
56
|
+
|
|
57
|
+
Craft a single-line commit message in imperative mood:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
{type}: {description}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Types:
|
|
64
|
+
- `feat` — New feature or capability
|
|
65
|
+
- `fix` — Bug fix
|
|
66
|
+
- `refactor` — Code restructuring without behavior change
|
|
67
|
+
- `docs` — Documentation changes
|
|
68
|
+
- `test` — Adding or updating tests
|
|
69
|
+
- `chore` — Build, config, dependencies
|
|
70
|
+
- `perf` — Performance improvement
|
|
71
|
+
- `ci` — CI/CD changes
|
|
72
|
+
|
|
73
|
+
Rules:
|
|
74
|
+
- Imperative mood ("add feature" not "added feature")
|
|
75
|
+
- Lowercase after the type prefix
|
|
76
|
+
- No period at the end
|
|
77
|
+
- Under 72 characters
|
|
78
|
+
- Describe WHAT changed, not HOW
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
git commit -m "{type}: {description}"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Phase 4 — OUTPUT
|
|
87
|
+
|
|
88
|
+
Report to user:
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
Committed: {hash_short}
|
|
92
|
+
Message: {type}: {description}
|
|
93
|
+
Files: {count} file(s) changed
|
|
94
|
+
|
|
95
|
+
Next steps:
|
|
96
|
+
- git push → push to remote
|
|
97
|
+
- /git pr → create a pull request
|
|
98
|
+
- /code-review → review before pushing
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Examples
|
|
104
|
+
|
|
105
|
+
| You say | What happens |
|
|
106
|
+
|---|---|
|
|
107
|
+
| `/git commit` | Stages all, auto-generates message |
|
|
108
|
+
| `/git commit staged` | Commits only what's already staged |
|
|
109
|
+
| `/git commit *.ts` | Stages all TypeScript files, commits |
|
|
110
|
+
| `/git commit except tests` | Stages everything except test files |
|
|
111
|
+
| `/git commit the database migration` | Finds DB migration files from status, stages them |
|
|
112
|
+
| `/git commit only new files` | Stages untracked files only |
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: GitHub 이슈 생성 — bug_report 또는 feature_request 템플릿으로 이슈를 만든다
|
|
3
|
+
argument-hint: [bug | feat] <제목>
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Create Issue
|
|
7
|
+
|
|
8
|
+
GitHub 이슈를 생성합니다.
|
|
9
|
+
|
|
10
|
+
**입력**: $ARGUMENTS
|
|
11
|
+
- `bug <제목>` — 버그 리포트 이슈
|
|
12
|
+
- `feat <제목>` — 기능 요청 이슈
|
|
13
|
+
- 타입 생략 시 대화로 결정
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Step 1 — 타입과 제목 파악
|
|
18
|
+
|
|
19
|
+
`$ARGUMENTS`에서 첫 단어로 타입(`bug` / `feat`)을 판별하고, 나머지를 제목으로 사용한다.
|
|
20
|
+
둘 다 없으면 사용자에게 타입과 제목을 묻는다.
|
|
21
|
+
|
|
22
|
+
## Step 2 — 내용 작성
|
|
23
|
+
|
|
24
|
+
현재 컨텍스트(변경된 파일, 오류 메시지, 대화 내용)를 바탕으로 이슈 본문을 작성한다.
|
|
25
|
+
|
|
26
|
+
**bug** 인 경우:
|
|
27
|
+
```
|
|
28
|
+
## 버그 설명
|
|
29
|
+
<현상 요약>
|
|
30
|
+
|
|
31
|
+
## 재현 절차
|
|
32
|
+
1.
|
|
33
|
+
2.
|
|
34
|
+
|
|
35
|
+
## 기대 동작
|
|
36
|
+
<기대값>
|
|
37
|
+
|
|
38
|
+
## 실제 동작
|
|
39
|
+
<실제값>
|
|
40
|
+
|
|
41
|
+
## 로그 / 스택 트레이스
|
|
42
|
+
<관련 로그>
|
|
43
|
+
|
|
44
|
+
## 환경
|
|
45
|
+
- Spring Boot 버전:
|
|
46
|
+
- Java 버전:
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**feat** 인 경우:
|
|
50
|
+
```
|
|
51
|
+
## 목적
|
|
52
|
+
<왜 필요한지>
|
|
53
|
+
|
|
54
|
+
## 도메인 / 레이어
|
|
55
|
+
- [ ] Controller / Service / Repository / Domain / 인프라
|
|
56
|
+
|
|
57
|
+
## 제안하는 해결책
|
|
58
|
+
<구현 방향>
|
|
59
|
+
|
|
60
|
+
## 인수 조건
|
|
61
|
+
- [ ]
|
|
62
|
+
- [ ]
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Step 3 — 이슈 생성
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
gh issue create \
|
|
69
|
+
--title "<제목>" \
|
|
70
|
+
--body "<본문>" \
|
|
71
|
+
--label "<bug|enhancement>"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
생성된 이슈 URL을 출력한다.
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Create a GitHub PR from current branch with unpushed commits — discovers templates, analyzes changes, pushes"
|
|
3
|
+
argument-hint: "[base-branch] (default: main)"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Create Pull Request
|
|
7
|
+
|
|
8
|
+
> Adapted from PRPs-agentic-eng by Wirasm. Part of the PRP workflow series.
|
|
9
|
+
|
|
10
|
+
**Input**: `$ARGUMENTS` — optional, may contain a base branch name and/or flags (e.g., `--draft`).
|
|
11
|
+
|
|
12
|
+
**Parse `$ARGUMENTS`**:
|
|
13
|
+
- Extract any recognized flags (`--draft`)
|
|
14
|
+
- Treat remaining non-flag text as the base branch name
|
|
15
|
+
- Default base branch to `main` if none specified
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Phase 1 — VALIDATE
|
|
20
|
+
|
|
21
|
+
Check preconditions:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
git branch --show-current
|
|
25
|
+
git status --short
|
|
26
|
+
git log origin/<base>..HEAD --oneline
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
| Check | Condition | Action if Failed |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| Not on base branch | Current branch ≠ base | Stop: "Switch to a feature branch first." |
|
|
32
|
+
| Clean working directory | No uncommitted changes | Warn: "You have uncommitted changes. Commit or stash first. Use `/git commit` to commit." |
|
|
33
|
+
| Has commits ahead | `git log origin/<base>..HEAD` not empty | Stop: "No commits ahead of `<base>`. Nothing to PR." |
|
|
34
|
+
| No existing PR | `gh pr list --head <branch> --json number` is empty | Stop: "PR already exists: #<number>. Use `gh pr view <number> --web` to open it." |
|
|
35
|
+
|
|
36
|
+
If all checks pass, proceed.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Phase 2 — DISCOVER
|
|
41
|
+
|
|
42
|
+
### PR Template
|
|
43
|
+
|
|
44
|
+
Search for PR template in order:
|
|
45
|
+
|
|
46
|
+
1. `.github/PULL_REQUEST_TEMPLATE/` directory — if exists, list files and let user choose (or use `default.md`)
|
|
47
|
+
2. `.github/PULL_REQUEST_TEMPLATE.md`
|
|
48
|
+
3. `.github/pull_request_template.md`
|
|
49
|
+
4. `docs/pull_request_template.md`
|
|
50
|
+
|
|
51
|
+
If found, read it and use its structure for the PR body.
|
|
52
|
+
|
|
53
|
+
### Commit Analysis
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
git log origin/<base>..HEAD --format="%h %s" --reverse
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Analyze commits to determine:
|
|
60
|
+
- **PR title**: Use conventional commit format with type prefix — `feat: ...`, `fix: ...`, etc.
|
|
61
|
+
- If multiple types, use the dominant one
|
|
62
|
+
- If single commit, use its message as-is
|
|
63
|
+
- **Change summary**: Group commits by type/area
|
|
64
|
+
|
|
65
|
+
### File Analysis
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
git diff origin/<base>..HEAD --stat
|
|
69
|
+
git diff origin/<base>..HEAD --name-only
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Categorize changed files: source, tests, docs, config, migrations.
|
|
73
|
+
|
|
74
|
+
### PRP Artifacts
|
|
75
|
+
|
|
76
|
+
Check for related PRP artifacts:
|
|
77
|
+
- `.claude/PRPs/reports/` — Implementation reports
|
|
78
|
+
- `.claude/PRPs/plans/` — Plans that were executed
|
|
79
|
+
- `.claude/PRPs/prds/` — Related PRDs
|
|
80
|
+
|
|
81
|
+
Reference these in the PR body if they exist.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Phase 3 — PUSH
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
git push -u origin HEAD
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
If push fails due to divergence:
|
|
92
|
+
```bash
|
|
93
|
+
git fetch origin
|
|
94
|
+
git rebase origin/<base>
|
|
95
|
+
git push -u origin HEAD
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
If rebase conflicts occur, stop and inform the user.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Phase 4 — CREATE
|
|
103
|
+
|
|
104
|
+
### With Template
|
|
105
|
+
|
|
106
|
+
If a PR template was found in Phase 2, fill in each section using the commit and file analysis. Preserve all template sections — leave sections as "N/A" if not applicable rather than removing them.
|
|
107
|
+
|
|
108
|
+
### Without Template
|
|
109
|
+
|
|
110
|
+
Use this default format:
|
|
111
|
+
|
|
112
|
+
```markdown
|
|
113
|
+
## Summary
|
|
114
|
+
|
|
115
|
+
<1-2 sentence description of what this PR does and why>
|
|
116
|
+
|
|
117
|
+
## Changes
|
|
118
|
+
|
|
119
|
+
<bulleted list of changes grouped by area>
|
|
120
|
+
|
|
121
|
+
## Files Changed
|
|
122
|
+
|
|
123
|
+
<table or list of changed files with change type: Added/Modified/Deleted>
|
|
124
|
+
|
|
125
|
+
## Testing
|
|
126
|
+
|
|
127
|
+
<description of how changes were tested, or "Needs testing">
|
|
128
|
+
|
|
129
|
+
## Related Issues
|
|
130
|
+
|
|
131
|
+
<linked issues with Closes/Fixes/Relates to #N, or "None">
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Create the PR
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
gh pr create \
|
|
138
|
+
--title "<PR title>" \
|
|
139
|
+
--base <base-branch> \
|
|
140
|
+
--body "<PR body>"
|
|
141
|
+
# Add --draft if the --draft flag was parsed from $ARGUMENTS
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Phase 5 — VERIFY
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
gh pr view --json number,url,title,state,baseRefName,headRefName,additions,deletions,changedFiles
|
|
150
|
+
gh pr checks --json name,status,conclusion 2>/dev/null || true
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Phase 6 — OUTPUT
|
|
156
|
+
|
|
157
|
+
Report to user:
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
PR #<number>: <title>
|
|
161
|
+
URL: <url>
|
|
162
|
+
Branch: <head> → <base>
|
|
163
|
+
Changes: +<additions> -<deletions> across <changedFiles> files
|
|
164
|
+
|
|
165
|
+
CI Checks: <status summary or "pending" or "none configured">
|
|
166
|
+
|
|
167
|
+
Artifacts referenced:
|
|
168
|
+
- <any PRP reports/plans linked in PR body>
|
|
169
|
+
|
|
170
|
+
Next steps:
|
|
171
|
+
- gh pr view <number> --web → open in browser
|
|
172
|
+
- /code-review <number> → review the PR
|
|
173
|
+
- gh pr merge <number> → merge when ready
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Edge Cases
|
|
179
|
+
|
|
180
|
+
- **No `gh` CLI**: Stop with: "GitHub CLI (`gh`) is required. Install: <https://cli.github.com/>"
|
|
181
|
+
- **Not authenticated**: Stop with: "Run `gh auth login` first."
|
|
182
|
+
- **Force push needed**: If remote has diverged and rebase was done, use `git push --force-with-lease` (never `--force`).
|
|
183
|
+
- **Multiple PR templates**: If `.github/PULL_REQUEST_TEMPLATE/` has multiple files, list them and ask user to choose.
|
|
184
|
+
- **Large PR (>20 files)**: Warn about PR size. Suggest splitting if changes are logically separable.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Git Commit & Push
|
|
2
|
+
|
|
3
|
+
변경사항을 커밋하고 원격 저장소에 푸시합니다.
|
|
4
|
+
|
|
5
|
+
## 실행 순서
|
|
6
|
+
|
|
7
|
+
1. `git status`로 변경 파일 확인
|
|
8
|
+
2. `git diff --staged` + `git diff`로 변경 내용 파악
|
|
9
|
+
3. `git log --oneline -5`로 최근 커밋 스타일 확인
|
|
10
|
+
4. 변경사항에 맞는 커밋 메시지 작성 (Semantic Commit)
|
|
11
|
+
5. 관련 파일 스테이징 후 커밋
|
|
12
|
+
6. 현재 브랜치로 push
|
|
13
|
+
|
|
14
|
+
## 규칙
|
|
15
|
+
|
|
16
|
+
- 커밋 메시지는 Semantic Commit 형식 (`feat:`, `fix:`, `refactor:`, `chore:` 등)
|
|
17
|
+
- 서명 스킵 (`--no-verify`) 금지 — 훅 실패 시 원인 수정
|
|
18
|
+
- `.env`, 시크릿 파일 커밋 금지
|
|
19
|
+
- `git add .` 대신 파일명 지정으로 스테이징
|
|
20
|
+
- force push 금지
|
|
21
|
+
- Co-Authored-By 태그 포함:
|
|
22
|
+
```
|
|
23
|
+
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## 완료 기준
|
|
27
|
+
|
|
28
|
+
`git log --oneline -1`로 커밋 확인 + push 성공 메시지 확인.
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "backend-claude-code",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Java/Spring Boot 프로젝트를 위한 Claude Code 설정 패키지",
|
|
5
|
+
"bin": {
|
|
6
|
+
"backend-claude-code": "./bin/cli.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin/",
|
|
10
|
+
"agents/",
|
|
11
|
+
"rules/",
|
|
12
|
+
"commands/",
|
|
13
|
+
"skills/",
|
|
14
|
+
".claude/",
|
|
15
|
+
".mcp.json",
|
|
16
|
+
".github/",
|
|
17
|
+
"CLAUDE.md"
|
|
18
|
+
],
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=18"
|
|
21
|
+
},
|
|
22
|
+
"keywords": ["claude-code", "spring-boot", "java", "backend"],
|
|
23
|
+
"license": "MIT"
|
|
24
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/*.java"
|
|
4
|
+
---
|
|
5
|
+
# Architecture
|
|
6
|
+
|
|
7
|
+
Spring Boot 3.x (Jakarta namespace) 표준 레이어드 아키텍처.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
src/main/java/com/{company}/{app}/
|
|
11
|
+
├── web/ # REST controllers (@RestController)
|
|
12
|
+
├── service/ # 비즈니스 로직 (@Service, @Transactional)
|
|
13
|
+
├── repository/ # JPA + QueryDSL 데이터 접근
|
|
14
|
+
├── domain/
|
|
15
|
+
│ ├── entity/ # JPA 엔티티
|
|
16
|
+
│ ├── enums/ # 공유 열거형
|
|
17
|
+
│ └── value/ # Value objects
|
|
18
|
+
├── dto/ # 요청/응답 DTO (*Dtos.java 내 static inner record)
|
|
19
|
+
└── global/
|
|
20
|
+
├── config/ # Spring 설정
|
|
21
|
+
├── security/ # 인증/인가
|
|
22
|
+
├── error/ # 예외 및 핸들러
|
|
23
|
+
└── util/ # 공통 유틸리티
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## 레이어 책임
|
|
27
|
+
|
|
28
|
+
| 레이어 | 책임 | 금지 사항 |
|
|
29
|
+
|--------|------|----------|
|
|
30
|
+
| Controller | HTTP 매핑, 입력 검증, 응답 포맷 | 비즈니스 로직 |
|
|
31
|
+
| Service | 비즈니스 로직, 트랜잭션 | 직접 HTTP 처리 |
|
|
32
|
+
| Repository | 데이터 접근, 쿼리 | 비즈니스 로직 |
|
|
33
|
+
| Entity | 도메인 상태 | 외부 DTO 의존 |
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/*.java"
|
|
4
|
+
---
|
|
5
|
+
# Java Coding Style
|
|
6
|
+
|
|
7
|
+
> This file extends [common/coding-style.md](../common/coding-style.md) with Java-specific content.
|
|
8
|
+
|
|
9
|
+
## 포매팅
|
|
10
|
+
|
|
11
|
+
- **google-java-format** 또는 **Checkstyle** (Google 또는 Sun 스타일) 강제 적용
|
|
12
|
+
- 파일당 하나의 공개 최상위 타입
|
|
13
|
+
- 일관된 들여쓰기: 2 또는 4 스페이스 (프로젝트 표준에 맞춤)
|
|
14
|
+
- 멤버 순서: 상수, 필드, 생성자, 공개 메서드, protected, private
|
|
15
|
+
|
|
16
|
+
## 불변성
|
|
17
|
+
|
|
18
|
+
- 값 타입에는 `record` 선호 (Java 16+)
|
|
19
|
+
- 기본적으로 필드를 `final`로 표시 — 필요한 경우에만 가변 상태 사용
|
|
20
|
+
- 공개 API에서 방어적 복사 반환: `List.copyOf()`, `Map.copyOf()`, `Set.copyOf()`
|
|
21
|
+
|
|
22
|
+
```java
|
|
23
|
+
// GOOD — 불변 값 타입
|
|
24
|
+
public record OrderSummary(Long id, String customerName, BigDecimal total) {}
|
|
25
|
+
|
|
26
|
+
// GOOD — final 필드, setter 없음
|
|
27
|
+
public class Order {
|
|
28
|
+
private final Long id;
|
|
29
|
+
private final List<LineItem> items;
|
|
30
|
+
|
|
31
|
+
public List<LineItem> getItems() {
|
|
32
|
+
return List.copyOf(items);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## 명명 규칙
|
|
38
|
+
|
|
39
|
+
표준 Java 관례 준수:
|
|
40
|
+
- `PascalCase` — 클래스, 인터페이스, 레코드, 열거형
|
|
41
|
+
- `camelCase` — 메서드, 필드, 매개변수, 지역 변수
|
|
42
|
+
- `SCREAMING_SNAKE_CASE` — `static final` 상수
|
|
43
|
+
- 패키지: 모두 소문자, 역방향 도메인 (`com.example.app.service`)
|
|
44
|
+
|
|
45
|
+
## 모던 Java 기능
|
|
46
|
+
|
|
47
|
+
명확성을 향상시키는 경우 모던 언어 기능 사용:
|
|
48
|
+
- **Records** — DTO 및 값 타입 (Java 16+)
|
|
49
|
+
- **Sealed classes** — 닫힌 타입 계층 (Java 17+)
|
|
50
|
+
- **Pattern matching** `instanceof` — 명시적 캐스트 불필요 (Java 16+)
|
|
51
|
+
- **Text blocks** — 여러 줄 문자열 (SQL, JSON 템플릿) (Java 15+)
|
|
52
|
+
- **Switch expressions** — 화살표 구문 (Java 14+)
|
|
53
|
+
- **Pattern matching in switch** — sealed 타입 완전 처리 (Java 21+)
|
|
54
|
+
|
|
55
|
+
```java
|
|
56
|
+
// 패턴 매칭 instanceof
|
|
57
|
+
if (shape instanceof Circle c) {
|
|
58
|
+
return Math.PI * c.radius() * c.radius();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Sealed 타입 계층
|
|
62
|
+
public sealed interface PaymentMethod permits CreditCard, BankTransfer, Wallet {}
|
|
63
|
+
|
|
64
|
+
// Switch 표현식
|
|
65
|
+
String label = switch (status) {
|
|
66
|
+
case ACTIVE -> "Active";
|
|
67
|
+
case SUSPENDED -> "Suspended";
|
|
68
|
+
case CLOSED -> "Closed";
|
|
69
|
+
};
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Optional 사용법
|
|
73
|
+
|
|
74
|
+
- 결과가 없을 수 있는 finder 메서드에서 `Optional<T>` 반환
|
|
75
|
+
- `map()`, `flatMap()`, `orElseThrow()` 사용 — `isPresent()` 없이 `get()` 절대 금지
|
|
76
|
+
- `Optional`을 필드 타입이나 메서드 매개변수로 사용 금지
|
|
77
|
+
|
|
78
|
+
```java
|
|
79
|
+
// GOOD
|
|
80
|
+
return repository.findById(id)
|
|
81
|
+
.map(ResponseDto::from)
|
|
82
|
+
.orElseThrow(() -> new OrderNotFoundException(id));
|
|
83
|
+
|
|
84
|
+
// BAD — 매개변수로 Optional
|
|
85
|
+
public void process(Optional<String> name) {}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## 오류 처리
|
|
89
|
+
|
|
90
|
+
- 도메인 오류에는 unchecked 예외 선호
|
|
91
|
+
- `RuntimeException`을 확장하는 도메인별 예외 생성
|
|
92
|
+
- 최상위 핸들러 외에는 `catch (Exception e)` 지양
|
|
93
|
+
- 예외 메시지에 컨텍스트 포함
|
|
94
|
+
|
|
95
|
+
```java
|
|
96
|
+
public class OrderNotFoundException extends RuntimeException {
|
|
97
|
+
public OrderNotFoundException(Long id) {
|
|
98
|
+
super("Order not found: id=" + id);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Streams
|
|
104
|
+
|
|
105
|
+
- 변환에 스트림 사용; 파이프라인을 짧게 유지 (최대 3-4 연산)
|
|
106
|
+
- 가독성이 있을 때 메서드 참조 선호: `.map(Order::getTotal)`
|
|
107
|
+
- 스트림 연산에서 부작용 방지
|
|
108
|
+
- 복잡한 로직은 복잡한 스트림 파이프라인보다 루프 선호
|
|
109
|
+
|
|
110
|
+
## 참고
|
|
111
|
+
|
|
112
|
+
skill: `java-coding-standards` — 예시가 포함된 전체 코딩 표준
|
|
113
|
+
skill: `jpa-patterns` — JPA/Hibernate 엔티티 설계 패턴
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/controller/**/*.java"
|
|
4
|
+
- "**/web/**/*.java"
|
|
5
|
+
---
|
|
6
|
+
# Controller 패턴
|
|
7
|
+
|
|
8
|
+
## 클래스 구조
|
|
9
|
+
|
|
10
|
+
```java
|
|
11
|
+
@RestController
|
|
12
|
+
@RequestMapping("/api/v1/{domain}")
|
|
13
|
+
@RequiredArgsConstructor
|
|
14
|
+
@Validated
|
|
15
|
+
public class FooController {
|
|
16
|
+
private final FooService fooService;
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## 응답 포맷
|
|
21
|
+
|
|
22
|
+
공통 래퍼 없이 DTO 직접 반환.
|
|
23
|
+
|
|
24
|
+
```java
|
|
25
|
+
// 조회
|
|
26
|
+
public FooDtos.FooResponse getFoo(...) { ... }
|
|
27
|
+
|
|
28
|
+
// 생성
|
|
29
|
+
@ResponseStatus(HttpStatus.CREATED)
|
|
30
|
+
public FooDtos.FooResponse createFoo(...) { ... }
|
|
31
|
+
|
|
32
|
+
// 삭제 / 본문 없는 수정
|
|
33
|
+
@ResponseStatus(HttpStatus.NO_CONTENT)
|
|
34
|
+
public void deleteFoo(...) { ... }
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Validation
|
|
38
|
+
|
|
39
|
+
- `@Validated` 클래스 레벨 선언
|
|
40
|
+
- Request record 필드: `@NotBlank`, `@NotNull`, `@Positive`, `@PositiveOrZero`
|
|
41
|
+
- nullable 필드 조건부 검증: `@AssertTrue` 메서드로 처리
|
|
42
|
+
|
|
43
|
+
```java
|
|
44
|
+
public record FooUpdateRequest(String title) {
|
|
45
|
+
@AssertTrue(message = "title must not be blank")
|
|
46
|
+
public boolean hasNonBlankTitleWhenPresent() {
|
|
47
|
+
return title == null || !title.isBlank();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## 페이지네이션 파라미터
|
|
53
|
+
|
|
54
|
+
```java
|
|
55
|
+
@RequestParam(defaultValue = "1") @Positive int page,
|
|
56
|
+
@RequestParam(defaultValue = "20") @Positive int size
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## 금지 사항
|
|
60
|
+
|
|
61
|
+
- 컨트롤러에 비즈니스 로직 작성 금지
|
|
62
|
+
- `@Autowired` 필드 주입 금지
|
|
63
|
+
- 엔티티 직접 반환 금지
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/dto/**/*.java"
|
|
4
|
+
---
|
|
5
|
+
# DTO 패턴
|
|
6
|
+
|
|
7
|
+
## 파일 구조
|
|
8
|
+
|
|
9
|
+
한 도메인의 모든 DTO는 `dto/FooDtos.java` 한 파일 안에 static inner record로 정의한다.
|
|
10
|
+
|
|
11
|
+
```java
|
|
12
|
+
public class FooDtos {
|
|
13
|
+
|
|
14
|
+
public record CreateRequest(
|
|
15
|
+
@NotBlank String title,
|
|
16
|
+
Integer duration
|
|
17
|
+
) {
|
|
18
|
+
public Foo toEntity(String id, String ownerId) {
|
|
19
|
+
return new Foo(id, ownerId, title, duration);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public record UpdateRequest(String title) {
|
|
24
|
+
@AssertTrue(message = "title must not be blank")
|
|
25
|
+
public boolean hasNonBlankTitleWhenPresent() {
|
|
26
|
+
return title == null || !title.isBlank();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public record FooResponse(
|
|
31
|
+
String id,
|
|
32
|
+
String title,
|
|
33
|
+
FooStatus status
|
|
34
|
+
) {
|
|
35
|
+
public static FooResponse from(Foo foo) {
|
|
36
|
+
return new FooResponse(foo.getId(), foo.getTitle(), foo.getStatus());
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public record FooListResponse(
|
|
41
|
+
List<FooResponse> items,
|
|
42
|
+
long total,
|
|
43
|
+
int page,
|
|
44
|
+
int size,
|
|
45
|
+
boolean hasMore
|
|
46
|
+
) {
|
|
47
|
+
public static FooListResponse of(List<FooResponse> items, long total, int page, int size) {
|
|
48
|
+
return new FooListResponse(items, total, page, size, (long) page * size < total);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## 변환 메서드 규칙
|
|
55
|
+
|
|
56
|
+
| 메서드 | 용도 |
|
|
57
|
+
|--------|------|
|
|
58
|
+
| `from(Entity)` | 단일 엔티티 → DTO |
|
|
59
|
+
| `fromDetail(Entity, ...)` | 연관 데이터 포함한 상세 조회 |
|
|
60
|
+
| `toEntity(...)` | Request DTO → 엔티티 |
|
|
61
|
+
|
|
62
|
+
## 네이밍 컨벤션
|
|
63
|
+
|
|
64
|
+
| 용도 | 이름 |
|
|
65
|
+
|------|------|
|
|
66
|
+
| 생성 요청 | `CreateRequest` |
|
|
67
|
+
| 수정 요청 | `UpdateRequest` |
|
|
68
|
+
| 단건 응답 | `FooResponse` |
|
|
69
|
+
| 목록 응답 | `FooListResponse` |
|
|
70
|
+
| 상세 응답 | `FooDetailResponse` |
|
|
71
|
+
|
|
72
|
+
## 금지 사항
|
|
73
|
+
|
|
74
|
+
- DTO에서 엔티티 import 금지 (toEntity 제외)
|
|
75
|
+
- record 대신 class 사용 금지
|
|
76
|
+
- 응답 DTO에 비즈니스 로직 작성 금지
|