Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions .claude/commands/issue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
---
description: AI-SIP/OnO_BACKEND 리포지토리에 GitHub 이슈를 생성한다. 텍스트 설명 또는 파일 경로를 받아 템플릿에 맞게 이슈를 작성한다.
argument-hint: "<이슈 설명 또는 파일 경로>"
---

# /issue — GitHub 이슈 생성

## 입력: $ARGUMENTS

## 단계

### 1. 입력 파악
- `$ARGUMENTS`가 존재하는 파일 경로이면 해당 파일을 읽어 내용을 이슈 소스로 사용한다.
- 파일 경로가 아니면 텍스트 그대로를 이슈 소스로 사용한다.
- 인자가 없으면 사용자에게 이슈 내용을 물어본다.

### 2. 이슈 유형 판단
입력 내용을 분석해 다음 중 하나로 분류한다:

| 유형 | 사용 템플릿 | labels | 제목 prefix |
|---|---|---|---|
| 버그·오류·에러·crash·NPE | Bug Report Template | `bug` | `[bug]` |
| 기능 추가·신규 개발 | Common Issue Template | `feature` | `[feat]` |
| 리팩터·코드 정리 | Common Issue Template | `refactor` | `[refactor]` |
| 설정·환경·의존성·chore | Common Issue Template | `chore` | `[chore]` |
| 성능 개선 | Common Issue Template | `performance` | `[perf]` |
| 문서·주석 | Common Issue Template | `documentation` | `[docs]` |
| 테스트 | Common Issue Template | `test` | `[test]` |
| 기타·불명확 | Common Issue Template | (없음) | `[chore]` |

### 3. 템플릿 채우기

**Bug Report Template** (버그 유형):
```
## ⚙️ 어떤 버그인가요?
[버그 핵심 요약 — 1~2문장]

### 스크린샷(선택)
[입력에 첨부 이미지·로그가 있으면 삽입, 없으면 섹션 생략]

## 🔎 어떤 상황에서 발생한 버그인가요?
**Given** — [사전 조건]
**When** — [행동]
**Then** — [실제 결과 / 오류]

## ✅ 예상 결과
[정상적으로 동작해야 하는 결과]
```

**Common Issue Template** (그 외):
```
## 📝 Description
[작업에 대한 간략한 설명]

## ✅ TODO
- [ ] [할 일 1]
- [ ] [할 일 2]
(입력에서 구체적인 작업 항목을 추출해 체크리스트로 작성)

## 💡 ETC (선택)
[기타 참고 사항, 고려할 점, 논의 필요 사항 — 없으면 섹션 생략]
```

### 4. 이슈 생성 전 미리보기 확인
이슈를 생성하기 전에 다음 항목을 사용자에게 보여주고 확인한다:
- **제목**: 결정된 제목
- **Labels**: 결정된 레이블
- **본문 미리보기**: 작성된 본문 전체

사용자가 수정을 요청하면 반영한 뒤 재확인한다. 승인하면 5단계로 진행한다.

### 5. GitHub 이슈 생성
`mcp__github__issue_write` 도구를 사용해 이슈를 생성한다:
- `owner`: `AI-SIP`
- `repo`: `OnO_BACKEND`
- `method`: `create`
- `title`: 3단계에서 결정한 제목
- `body`: 3단계에서 작성한 본문
- `assignees`: `["KiSeungMin"]` (항상 고정)
- `labels`: 2단계에서 결정한 레이블 배열

### 6. 결과 보고
생성된 이슈 URL을 사용자에게 알린다.
143 changes: 133 additions & 10 deletions .claude/commands/pr.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,140 @@
---
description: PR 제목과 본문 초안 작성. 기준 커밋부터 현재까지의 변경사항을 PR 템플릿에 맞춰 정리.
argument-hint: "<기준 커밋 해시>"
description: 현재 브랜치의 개발 내역을 develop 브랜치로 merge 요청하는 GitHub PR 생성.
argument-hint: "<추가 설명 (선택)>"
---

# /pr — PR 초안
# /pr — GitHub PR 생성 (→ develop)

## 기준: $ARGUMENTS
## 추가 설명: $ARGUMENTS

## 주의사항

> ⚠️ **main 브랜치로는 절대 PR을 생성하지 않는다.** main merge는 프로덕션 자동 배포로 이어지므로 항상 사용자가 직접 수행한다.
> 이 명령어는 현재 브랜치 → `develop` PR만 생성한다.

---

## 단계

1. **변경 범위 확인** — `<커밋>..HEAD`의 커밋 로그와 diff를 확인한다. 인자가 없으면 현재 작업 트리 변경사항을 대상으로 한다.
2. **미커밋 변경 확인** — `git status`로 아직 커밋되지 않은 변경사항을 확인하고, PR 본문 반영 여부를 명시한다.
3. **템플릿 확인** — `.github/PULL_REQUEST_TEMPLATE.md`를 읽고 같은 구조로 작성한다.
4. **영향 범위 정리** — 사용자 영향, API 호환성, DB migration, 인증/권한, 알림/외부 발송, 배포 리스크를 확인한다.
5. **검증 결과 정리** — 실행한 테스트/빌드 명령과 결과, 실행하지 못한 검증과 남은 리스크를 적는다.
6. **저장 위치 확인** — 기능 문서 폴더가 명확하면 같은 위치에 PR markdown을 저장하고, 불명확하면 사용자에게 확인한다.
### 1. 브랜치 상태 확인
- `git branch --show-current`로 현재 브랜치 이름 확인
- 현재 브랜치가 `main` 또는 `develop`이면 **즉시 중단**하고 작업 브랜치에서 실행해야 한다고 알린다
- `git log develop..HEAD --oneline`으로 develop 대비 커밋 목록 확인
- `git diff develop...HEAD`로 변경 내용 파악
- `git status`로 미커밋 변경사항 확인 (있으면 사용자에게 명시)
- 원격에 push 여부 확인 (`git log origin/$(git branch --show-current)..HEAD 2>/dev/null`로 미push 커밋 확인)

### 2. 변경 내용 분석
커밋 로그와 diff를 바탕으로 다음을 파악한다:
- **작업 유형**: feat / fix / refactor / chore / perf / docs / test (복수 가능)
- **영향 범위**: 어떤 패키지·API·DB 스키마가 바뀌었는지
- **연관 이슈 후보 추출**: 커밋 메시지와 브랜치명에서 이슈 번호 또는 키워드를 추출한다
- 브랜치명 패턴: `feat/123-login` → 번호 `123`, 키워드 `login`
- 커밋 메시지 패턴: `close #123`, `fix #123`, `resolve #123` → 번호 `123`
- 번호가 없으면 작업 내용을 요약한 키워드를 추출한다

### 2-1. GitHub에서 연관 이슈 검색
`mcp__github__.list_issues`를 사용해 `AI-SIP/OnO_BACKEND` 리포지토리의 **open 상태** 이슈를 검색한다:
- 2단계에서 추출한 이슈 번호나 키워드를 open issue의 번호, 제목, 본문과 비교한다
- 검색 결과 중 이번 PR 작업과 연관성이 높은 이슈를 골라 연관 이슈 목록을 확정한다
- PR에서 실제로 해결되는 이슈만 `close #이슈번호`로 연결한다
- 단순 참고 이슈는 자동 종료되지 않도록 `close` 없이 참고 문장으로만 작성한다
- 연관 이슈를 찾지 못하면 "없음"으로 처리한다

### 3. 레이블 결정
변경 내용을 분석해 다음 중 하나 이상으로 레이블을 결정한다:

| 작업 유형 | label |
|---|---|
| 신규 기능 | `feature` |
| 버그 수정 | `bug` |
| 리팩터링 | `refactor` |
| 설정·의존성·chore | `chore` |
| 성능 개선 | `performance` |
| 문서·주석 | `documentation` |
| 테스트 | `test` |

결정한 label은 적용 전에 `mcp__github__.get_label`로 존재 여부를 확인하고, 존재하는 label만 적용한다.

### 4. PR 제목 결정
브랜치명과 커밋 내역을 종합해 PR 제목을 결정한다.
- 형식: `[타입] 핵심 변경 요약` (예: `[Feat] 오답노트 태그 필터 기능 추가`)
- 여러 타입이 섞이면 가장 비중이 큰 타입 하나를 사용

### 5. PR 본문 작성
`.github/PULL_REQUEST_TEMPLATE.md` 구조를 그대로 따른다. 실제 변경 내용에 맞게 체크박스를 채운다:

```
## ✔️ 연관 이슈
- close #이슈번호 ← 2-1단계에서 찾았고 PR merge 시 닫아야 하는 이슈마다 한 줄씩 기재
- 없음 ← 관련 이슈가 없으면 기재

## 📝 작업 내용
> [커밋 단위로 bullet 정리]

## 👤 사용자 영향
- [사용자 관점에서 달라지는 점 / 영향 없으면 "없음"]

## 🔌 API 호환성
- [ ] 요청/응답 DTO 변경 없음
- [ ] 기존 Flutter 앱 버전과 호환 확인
- [ ] breaking change 있음

## 🗄️ DB migration
- [ ] 없음
- [ ] 있음: migration 파일과 기존 데이터 호환성 확인

## 🔐 인증/권한
- [ ] 영향 없음
- [ ] 사용자별 데이터 소유권 검증 확인
- [ ] 관리자/특수 권한 영향 확인

## ✅ 검증 결과
- [실행한 테스트·빌드 명령과 결과 / 실행하지 못한 검증과 남은 리스크]

## 🚀 배포 리스크
- [배포 시 주의할 점 / 없으면 "없음"]

## ↩️ 롤백/대응 방법
- [문제 발생 시 롤백 방법 / 해당 없으면 "없음"]

### 스크린샷 (선택)
```

### 6. 생성 전 미리보기 확인
PR을 생성하기 전에 다음 항목을 사용자에게 보여주고 확인을 받는다:
- **Base → Head**: `develop` ← 현재 브랜치명
- **제목**: 결정된 제목
- **Labels**: 결정된 레이블
- **연관 이슈**: `close #이슈번호` 또는 없음
- **본문 미리보기**: 작성된 본문 전체

사용자가 수정을 요청하면 반영한 뒤 재확인한다. 승인하면 다음 단계로 진행한다.

### 7. 원격 브랜치 push 확인
미push 커밋이 있으면 사용자에게 알리고, push를 완료한 뒤 진행할 것을 안내한다.
push가 완료된 것을 확인한 후 PR을 생성한다.

### 8. GitHub PR 생성
`mcp__github__.create_pull_request` 도구를 사용해 PR을 생성한다:
- `owner`: `AI-SIP`
- `repo`: `OnO_BACKEND`
- `title`: 4단계에서 결정한 제목
- `body`: 5단계에서 작성한 본문
- `head`: 현재 브랜치명
- `base`: `develop` (절대 고정 — main 불가)

Reviewers는 지정하지 않는다.

### 8-1. Assignee/Label 설정
PR 생성 후 생성된 PR 번호에 `mcp__github__.issue_write` 도구를 `method=update`로 호출한다:
- `owner`: `AI-SIP`
- `repo`: `OnO_BACKEND`
- `issue_number`: 생성된 PR 번호
- `assignees`: `["KiSeungMin"]` (항상 고정)
- `labels`: 3단계에서 결정하고 존재 확인된 label 배열

milestone, project, issue fields, type 등 나머지 항목은 지정하지 않는다.

### 9. 결과 보고
생성된 PR URL, base/head branch, assignee, 적용 label, 연결한 issue, `main`을 target으로 사용하지 않았다는 점을 사용자에게 알린다.
49 changes: 38 additions & 11 deletions .codex/skills/pr/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,52 @@
---
name: pr
description: OnO PR 제목과 본문 초안을 작성할 때 사용. 사용자가 "pr <커밋 해시>"를 요청하면 해당 커밋부터 현재까지의 변경과 미커밋 변경을 확인해 PR 템플릿에 맞춰 markdown을 작성한다.
description: OnO 현재 브랜치의 개발 내역을 바탕으로 develop 브랜치에 merge 요청하는 GitHub PR을 직접 생성할 때 사용. 사용자가 "pr"을 요청하면 현재 브랜치에서 develop으로 PR을 생성한다.
---

# OnO PR

## Workflow

1. 기준 커밋 해시 또는 변경 범위를 확인한다. 인자가 없으면 현재 git 변경사항을 대상으로 한다.
2. `git log`, `git diff`, `git status`로 커밋된 변경과 미커밋 변경을 분리한다.
3. `.github/PULL_REQUEST_TEMPLATE.md`를 반드시 읽고 같은 구조로 본문을 작성한다.
4. 수정된 파일, API/DB/인증/권한/알림/배포 영향 범위를 정리한다.
5. 테스트와 검증은 실행 여부, 명령, 결과를 구분해 적는다.
6. 저장 위치가 명확하지 않으면 사용자에게 확인한다. 기능 문서가 있으면 같은 `docs/<기능폴더>` 아래에 저장한다.
7. 미커밋 변경사항은 PR 본문에 포함했는지 여부를 명시한다.
1. 현재 브랜치를 확인한다. 현재 브랜치가 `main` 또는 `develop`이면 PR을 생성하지 말고 작업 브랜치에서 실행해야 한다고 보고한다.
2. base branch는 항상 `develop`으로 고정한다. `main` branch를 base로 하는 PR은 절대 생성하지 않는다. `main` merge와 production 배포는 항상 사용자가 직접 담당한다.
3. `git status`로 미커밋 변경사항을 확인한다. 미커밋 변경사항이 있으면 PR을 생성하지 말고 먼저 커밋 또는 정리가 필요하다고 보고한다.
4. 현재 브랜치가 원격에 push되어 있는지 확인한다. 원격 브랜치가 없거나 미push 커밋이 있으면 PR을 생성할 수 없으므로 사용자에게 push 필요 여부를 확인한다.
5. `develop..현재브랜치`의 `git log`와 `git diff`를 확인해 변경 범위를 파악한다.
6. GitHub에서 `AI-SIP/OnO_BACKEND`의 open issue를 먼저 조회해 현재 변경사항과 관련된 이슈가 있는지 확인한다.
- `mcp__github__.list_issues`를 사용한다.
- 브랜치명, 커밋 메시지, 변경 파일, 작업 키워드와 이슈 제목/본문을 비교한다.
- 실제로 이번 PR에서 해결되는 이슈가 있으면 PR 본문 `연관 이슈` 항목에 `close #이슈번호`를 포함한다.
- 단순 참고 이슈는 자동 종료되지 않도록 `close` 없이 참고 문장으로만 적는다.
- 관련 이슈가 없으면 `연관 이슈` 항목에 `없음`이라고 명시한다.
7. `.github/PULL_REQUEST_TEMPLATE.md`를 반드시 읽고 같은 구조로 PR 본문을 작성한다.
8. 수정된 파일, API/DB/인증/권한/알림/배포 영향 범위를 정리한다.
9. 테스트와 검증은 실행 여부, 명령, 결과를 구분해 적는다.
10. PR 제목은 브랜치명과 커밋 내역을 종합해 `[Feat]`, `[Fix]`, `[Refactor]`, `[Chore]`, `[Docs]`, `[Test]` 중 가장 적절한 타입으로 작성한다.
11. Labels는 변경 성격에 맞게 `feat`, `fix`, `refactor`, `chore`, `docs`, `test`, `bug` 중 적절한 후보를 고르되, `mcp__github__.get_label`로 존재 여부를 확인한 label만 적용한다.
12. `mcp__github__.create_pull_request`로 PR을 생성한다.
- `owner`: `AI-SIP`
- `repo`: `OnO_BACKEND`
- `base`: `develop`
- `head`: 현재 브랜치명
- `title`: 작성한 PR 제목
- `body`: 작성한 PR 본문
- Reviewers는 지정하지 않는다.
13. PR 생성 후 생성된 PR 번호에 `mcp__github__.issue_write`를 `method=update`로 호출해 Assignees와 Labels를 설정한다.
- `owner`: `AI-SIP`
- `repo`: `OnO_BACKEND`
- `issue_number`: 생성된 PR 번호
- `assignees`: `["KiSeungMin"]`
- `labels`: 존재 확인된 label 배열
14. milestone, project, issue fields, type 등 나머지 항목은 지정하지 않는다.

## Output

- PR 제목
- PR 본문 markdown
- 포함한 커밋/미커밋 변경 범위
- 생성된 PR 번호와 URL
- base/head branch
- Assignee: `KiSeungMin`
- 적용한 label
- 연결한 issue와 `close #이슈번호` 포함 여부
- `main`을 target으로 사용하지 않았다는 확인
- 사용자 영향
- 검증 결과
- 배포 리스크와 rollback/대응 방법
51 changes: 51 additions & 0 deletions .github/workflows/close-issues-on-develop-merge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Close Issues on Develop Merge

on:
pull_request:
types: [closed]
branches:
- develop

permissions:
issues: write

jobs:
close-related-issues:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest

steps:
- name: Close referenced issues
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const body = context.payload.pull_request.body || '';
const pattern = /(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s+#(\d+)/gi;
const issueNumbers = [];
let match;

while ((match = pattern.exec(body)) !== null) {
issueNumbers.push(parseInt(match[1], 10));
}

const unique = [...new Set(issueNumbers)];
if (unique.length === 0) {
console.log('연관 이슈 없음 — 종료');
return;
}

for (const number of unique) {
try {
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: number,
state: 'closed',
state_reason: 'completed',
});
console.log(`이슈 #${number} 닫힘`);
} catch (e) {
console.log(`이슈 #${number} 처리 실패: ${e.message}`);
}
}
Loading
Loading