---
id: productivity-pr-template
title: PR Template — 좋은 description / 자동화
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [productivity, pr-template, vibe-coding]
tech_stack: { language: "Markdown", applicable_to: ["Engineering"] }
applied_in: []
aliases: [PR template, pull request template, conventional commits, semantic-release]
---

# PR Template

> 좋은 PR = 빠른 review + 미래 reference. **What / Why / Test plan** 3가지 + checkbox + screenshot. GitHub `.github/PULL_REQUEST_TEMPLATE.md`.

## 📖 핵심 개념
- What: 변경 내용.
- Why: 동기 (issue link).
- How: 구현 핵심 (필요 시).
- Test plan: 어떻게 검증.

## 💻 코드 패턴

### 기본 template
```markdown
<!-- .github/PULL_REQUEST_TEMPLATE.md -->

## What
<!-- 짧게 — 무엇을 변경 -->

## Why
<!-- 왜 — 어떤 issue / 요구 -->

Closes #

## How
<!-- 핵심 구현 결정 — 필요 시 -->

## Screenshots
<!-- UI 변경 시 before/after -->

## Test plan
- [ ] Unit tests pass
- [ ] Manual test: ...
- [ ] Edge case: ...

## Migration / rollback
<!-- DB / config 변경 시 -->

## Checklist
- [ ] Tests added/updated
- [ ] Docs updated
- [ ] Feature flag added (if behind flag)
- [ ] No PII in logs
- [ ] No breaking change OR migration guide
```

### Multi-template (PR type 별)
```
.github/PULL_REQUEST_TEMPLATE/
├── feature.md
├── bugfix.md
├── chore.md
└── docs.md
```

```bash
# URL 으로 select
github.com/org/repo/compare/main...feature?template=feature.md
```

### Bug fix template
```markdown
## Bug
<!-- 무엇이 잘못 -->

## Root cause
<!-- 왜 발생 -->

## Fix
<!-- 어떻게 해결 -->

## Repro
1. ...
2. ...

## Verification
- [ ] Before: shows bug
- [ ] After: works correctly
- [ ] Regression test added

Fixes #
```

### Feature template
```markdown
## Feature
<!-- 무엇 -->

## User story
As a <user>, I want <action>, so that <benefit>.

Spec: <link>
Issue: #

## Implementation notes
- ...

## Test plan
- [ ] Happy path
- [ ] Error cases
- [ ] Edge cases
- [ ] A11y

## Demo
[video / screenshot]

## Feature flag
- Flag: `flag-name`
- Default: off
- Rollout plan: ...

## Rollback
1. Disable flag
2. ...
```

### Migration template
```markdown
## Migration
<!-- 무엇 변경 -->

## Online safety
- [ ] No long lock
- [ ] Concurrent index (if applicable)
- [ ] Backwards-compatible deploy order

## Rollback
1. Disable new code path
2. Migrate data back: ...

## Deploy plan
1. Deploy migration
2. Verify
3. Deploy app code
4. Monitor
5. Cleanup (after N days)

## Risks
- ...
```

### Conventional commits
```
feat:     새 feature
fix:      bug fix
chore:    misc (build, deps)
docs:     docs
style:    formatting (no logic)
refactor: 코드 정리 (no behavior)
perf:     성능
test:     test
ci:       CI 변경
build:    build system

Scope: feat(api): ..., fix(ui): ...
Breaking: feat!: ... 또는 BREAKING CHANGE: footer
```

```bash
git commit -m "feat(orders): add cursor pagination

Closes #123"
```

### Commitlint
```js
// commitlint.config.js
module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [2, 'always', [
      'feat', 'fix', 'chore', 'docs', 'style', 'refactor', 'perf', 'test', 'ci', 'build',
    ]],
  },
};
```

### Husky commit-msg hook
```bash
# .husky/commit-msg
npx --no -- commitlint --edit $1
```

→ 잘못된 commit message = block.

### Semantic-release (자동 versioning)
```yaml
# .releaserc
branches: [main]
plugins:
  - '@semantic-release/commit-analyzer'
  - '@semantic-release/release-notes-generator'
  - '@semantic-release/changelog'
  - '@semantic-release/npm'
  - '@semantic-release/github'
```

```
feat: → minor bump (1.2.0)
fix:  → patch (1.1.1)
feat!: 또는 BREAKING CHANGE: → major (2.0.0)
chore / docs: → no release
```

→ Commit history → version + changelog 자동.

### Auto-merge / auto-rebase
```yaml
# .github/auto-merge.yml (mergify, kodiak)
queue_rules:
  - name: default
    conditions:
      - check-success=ci
      - "#approved-reviews-by>=1"
      - label=auto-merge
```

→ Approve + label = 자동 merge.

### PR labels
```
- type:bug, type:feature, type:chore
- priority:high, priority:low
- status:wip, status:ready, status:blocked
- size:xs, size:s, size:m, size:l, size:xl
```

→ Filter / sort / report.

### PR title 형식
```
✅ feat(orders): add cursor pagination
✅ fix(auth): handle null token
❌ "Update stuff"
❌ "Fixes"
```

### Linked issue (자동 close)
```
Closes #123
Fixes #456
Resolves #789

→ PR merge → issue 자동 close.
```

### Preview environment
```yaml
# Vercel / Netlify auto-deploy preview
# PR 마다 unique URL
# Reviewer 가 직접 test
```

### Dependabot PR
```yaml
# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: npm
    directory: /
    schedule: { interval: weekly }
    labels: [dependencies, auto-merge]
    
  # Security patches 자동
  - package-ecosystem: npm
    directory: /
    schedule: { interval: daily }
    open-pull-requests-limit: 0
    allow:
      - dependency-type: direct
    insecure-external-code-execution: deny
```

### Stale PR cleanup
```yaml
# .github/workflows/stale.yml
on:
  schedule: [{ cron: '0 0 * * *' }]
jobs:
  stale:
    steps:
      - uses: actions/stale@v9
        with:
          days-before-stale: 14
          days-before-close: 7
          stale-pr-message: "This PR is stale. It will be closed in 7 days."
```

### Dangerous patterns
```ts
// danger.js — PR check
import { danger, fail, warn, message } from 'danger';

if (danger.git.modified_files.includes('package.json') && !danger.git.modified_files.includes('CHANGELOG.md')) {
  warn('You changed package.json but not CHANGELOG.md');
}

if (danger.github.pr.body.length < 50) {
  fail('Please provide a meaningful PR description');
}

if (danger.github.pr.title.length > 70) {
  warn('PR title is long — consider shortening');
}
```

### CHANGELOG (auto)
```bash
# semantic-release 가 자동
# 또는 git-cliff / standard-version
```

```markdown
# CHANGELOG

## [1.5.0] - 2026-05-09

### Features
- orders: add cursor pagination (#123)

### Bug Fixes
- auth: handle null token (#456)

### Breaking Changes
- API: removed deprecated /v1/users endpoint
```

### PR review 자동 (AI)
```yaml
# .github/workflows/coderabbit.yml
- uses: coderabbit/ai-pr-reviewer@v2
```

→ AI 가 first review.

### Squash vs merge commit vs rebase
```
Squash: PR 의 모든 commit → 1 commit (clean history).
Merge commit: PR 의 모든 commit + merge commit (full history).
Rebase: PR commit 들을 main 에 fast-forward (linear).

→ 보통 Squash (default GitHub).
   큰 stack PR = rebase merge.
```

## 🤔 의사결정 기준
| 상황 | 추천 |
|---|---|
| 작은 팀 / 시작 | Simple template |
| 큰 / 복잡 | Multi template + danger |
| Open source | Detailed checklist |
| Internal | Lightweight |
| Strict process | Required CI + reviewers |
| Mobile / native | + screenshot / device test |

## ❌ 안티패턴
- **Description 빈약 ("fix")**: reviewer 추측.
- **Template 안 따름**: skip.
- **Test plan 없음**: 깨짐 risk.
- **Conventional commit 강제 안 함**: changelog 깨짐.
- **Auto-merge + low review**: 깨짐.
- **PR 제목 generic**: changelog 깨짐.
- **Stale PR 무한**: context loss.

## 🤖 LLM 활용 힌트
- What / Why / Test plan 3종 항상.
- Conventional commits + semantic-release.
- Multi-template (feature / bugfix / migration).
- Auto AI review + 인간 review.

## 🔗 관련 문서
- [[Productivity_Code_Review]]
- [[DevSec_Pre_Commit_Security]]
- [[DevOps_CI_CD_Pipeline_Patterns]]
