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
69 changes: 69 additions & 0 deletions .claude/skills/check-screens/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
---
name: check-screens
description: 바뀐 화면을 PC 크롬·PC 사파리(WebKit)·안드로이드(크롬 기기 에뮬레이션)·아이폰 시뮬레이터 Safari에서 스크린샷으로 찍어 깨짐을 확인한다. "화면 확인", "스크린샷 찍어줘", "기기별로 봐줘" 같은 요청이나 /check-screens, /open-pr 검증 단계에서 사용한다.
argument-hint: [경로...] [before|after]
---

# 화면 확인

인자: $ARGUMENTS (경로와 라벨이 들어올 수 있다)

어떤 변경에 어떤 환경을 볼지는 [CONTRIBUTING.md](../../../CONTRIBUTING.md)의 `화면 확인 기준` 표가 정본이다. 스크린샷은 `scripts/check-screens.mjs`(`npm run screens`)가 찍는다.

## 0. 처음 한 번 준비

스크립트가 로그인 화면으로 이동됐다고 하거나 브라우저가 없다고 하면 아래를 안내하고 멈춘다.

- PC 크롬·사파리·안드로이드 로그인: `npm run screens -- login`을 실행하면 크롬 창이 열린다. 구글 로그인을 끝내면 `~/.cache/uttae/check-screens/`에 저장되고 세 환경이 함께 쓴다.
- 아이폰 로그인: 시뮬레이터 Safari에서 `http://localhost:3000/login`을 열어 한 번 로그인한다. 시뮬레이터는 쿠키를 따로 쓴다.
- 브라우저: `npx playwright install chromium webkit`
- 시뮬레이터: Xcode가 있어야 한다. 기본 기기는 `iPhone 17`이고 `--iphone "<기기 이름>"`으로 바꾼다.

## 1. 환경 고르기

CONTRIBUTING.md 표를 기준으로 `--targets`를 정한다. 값은 `pc-chrome`, `pc-safari`, `android`, `iphone`이다.

- PC 화면을 바꾸면서 아래 중 하나를 건드렸으면 `pc-safari`를 넣는다: `fixed`·`sticky`, `h-screen`·`dvh`·`svh`, `safe-area`, `backdrop-blur`·`backdrop-filter`, 날짜·시간 input, 스크롤 동작(`overflow`, `scroll-snap`), 16px 미만 글자의 input
- 판단이 어려우면 4개를 모두 찍는다.

## 2. 경로 고르기

- 인자로 경로를 받았으면 그대로 쓴다.
- 없으면 `git diff --name-only origin/dev...HEAD`와 작업 트리 변경에서 화면을 찾는다.
- `src/app/**/page.tsx`는 경로로 바꾼다. `(main)` 같은 괄호 그룹은 빼고, `[roomId]` 같은 동적 값은 실제 값이 필요하다.
- 컴포넌트는 사용처를 검색해 그 컴포넌트가 보이는 페이지를 찾는다.
- 동적 값(방 ID 등)이나 화면에 들어가는 방법을 모르면 사용자에게 예시 URL을 묻는다. 대화 안에서 받은 값은 다시 묻지 않는다.
- 한 번에 1~4개 경로만 찍는다.

## 3. dev 서버

스크립트는 3000번 dev 서버가 지금 폴더의 코드인지 검사한다. 백엔드 CORS와 구글 로그인이 3000번만 허용하므로 다른 포트는 쓰지 않는다.

- 서버가 없으면 이 폴더에서 `npm run dev`를 백그라운드로 띄우고, 응답할 때까지 기다린다.
- 다른 폴더(다른 worktree)의 서버가 3000번을 쓰고 있으면 멈추고 사용자에게 알린다. 그 서버를 임의로 끄지 않는다.

## 4. 찍기

```bash
npm run screens -- --path <경로> [--path <경로>] --targets <대상,...> --label <라벨>
```

- 라벨: 작업 시작 전에는 `before`, PR 전에는 `after`. 노션 전후 비교에 그대로 쓴다. 생략하면 시간이 붙는다.
- 긴 화면은 `--full`, 안드로이드 기기를 바꾸려면 `--android-device "Galaxy Z Fold 7"`처럼 Playwright 기기 이름을 넘긴다.
- 지도처럼 늦게 그려지는 화면은 `--wait 4000`, 아이폰은 `--iphone-wait 6000`으로 늘린다.

## 5. 확인

찍은 이미지를 직접 열어 본다. 출력 표와 `.screens/<라벨>/results.json`도 확인한다.

- 로그인 화면이 찍혔으면(아이폰은 이미지로 판단) 0단계 로그인을 안내하고 멈춘다.
- 콘솔 에러가 있으면 `results.json`의 메시지를 요약한다.
- 넘침, 잘림, 겹침, 가로 스크롤, 빈 화면, 끝나지 않는 스켈레톤을 찾는다.
- Figma 링크가 있는 디자인 작업이면 Figma 노드와 px 단위로 비교한다. PC 스크린샷은 1배율이라 이미지 px이 CSS px과 같다. 안드로이드·아이폰은 3배율이다.
- 터치, 드래그, 키보드, 포커스는 스크린샷으로 확인할 수 없다. 해당 변경이면 사람이 직접 눌러 볼 항목을 구체적으로 적는다.

## 6. 보고

- 환경별 결과를 한 줄씩 적는다(이상 없음 / 문제 요약).
- 이미지 폴더 경로(`.screens/<라벨>/`)를 알린다.
- 사람이 직접 확인할 조작 항목이 있으면 목록으로 남긴다.
89 changes: 89 additions & 0 deletions .claude/skills/open-pr/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
---
name: open-pr
description: 작업 브랜치에 최신 대상 브랜치를 반영하고 lint·test·build를 통과시킨 뒤, PR 템플릿을 채워 확인을 받고 gh로 PR을 연다. "PR 올려줘", "PR 만들어줘", "작업 마무리하고 PR" 같은 요청이나 /open-pr에서 사용한다. 병합은 하지 않는다.
argument-hint: [노션 링크]
---

# PR 올리기

인자: $ARGUMENTS (노션 링크가 들어올 수 있다)

규칙의 정본은 [CONTRIBUTING.md](../../../CONTRIBUTING.md)와 [PR 템플릿](../../../.github/pull_request_template.md)이다.

병합은 리뷰어가 확인하고 직접 한다. 이 스킬은 PR을 여는 데까지만 하고 `gh pr merge`를 실행하지 않는다.

## 0. 준비

- `gh auth status`가 실패하면 멈추고 `! gh auth login` 실행을 안내한다.
- 현재 브랜치가 `main`이나 `dev`면 멈춘다.
- 브랜치 접두사로 PR 대상과 제목 타입을 정한다. 표에 없는 접두사면 대상 브랜치를 묻는다.

| 접두사 | PR 대상 | 제목 타입 |
| :-- | :-- | :-- |
| `feature/` | `dev` | `feat` |
| `fix/` | `dev` | `fix` |
| `refactor/` | `dev` | `refactor` |
| `chore/` | `dev` | `chore` |
| `docs/` | `dev` | `docs` |
| `hotfix/` | `main` | `fix` |

- `gh pr view --json url,state`로 이 브랜치의 열린 PR이 있는지 확인한다. 이미 있으면 새로 만들지 않는다. 1~3단계를 거친 뒤 4단계는 건너뛰고, 5단계에서 push할 커밋을 확인받고 6단계에서 push만 한다.

## 1. 커밋하지 않은 변경

`git status --short`에 변경이 있으면 요약해 보여 주고 이번 PR에 넣을지 묻는다. 넣기로 한 변경만 AGENTS.md의 커밋 메시지 규칙으로 커밋한다.

## 2. 최신 대상 브랜치 반영

```bash
git fetch origin --prune
git rev-list --count HEAD..origin/<대상>
```

0보다 크면 `git merge --no-edit origin/<대상>`으로 반영한다. rebase나 force-push는 하지 않는다. PR은 squash 병합되므로 작업 브랜치 안의 merge commit은 대상 브랜치에 남지 않는다.

충돌이 나면 충돌 파일과 양쪽 변경 요지를 보여 주고 멈춘다. 어느 쪽을 살릴지 사용자와 정한 뒤 해결한다. 그만두려면 `git merge --abort`로 되돌릴 수 있다고 알린다.

## 3. 검증

`git diff --name-only origin/<대상>...HEAD`가 문서(`*.md`)·`.claude/`·`.github/`뿐이면 검증을 생략하고 테스트 칸에 생략했다고 쓴다.

그 외에는 순서대로 실행하고, 실패하면 다음 명령으로 넘어가지 않는다.

1. `npm run lint`
2. `npm test`
3. `npm run build`
4. 화면 확인: 화면에 영향을 주는 파일(`src/app/`, `src/components/`, `src/styles/`, `src/assets/`, `public/`, `*.css`)이 바뀌었으면 [check-screens](../check-screens/SKILL.md) 순서대로 `after` 라벨로 찍고 확인한다. CONTRIBUTING.md `화면 확인 기준` 표에서 고른 환경만 찍는다.

실패하거나 화면이 깨졌으면 PR을 열지 않는다. 실패한 명령, 파일·테스트 이름, 핵심 에러 몇 줄만 보여 준다. 이번 변경 때문인지 판단해 알리고, 고칠지 묻는다. 이번 변경과 무관해 보이면 근거(변경 파일과 실패 위치의 관계)를 들어 그대로 PR을 열지 묻는다. 열기로 하면 테스트 칸에 실패 항목과 사유를 적는다.

## 4. 본문 작성

- 제목: `<타입>(<scope>): <한국어 요약>`. scope는 커밋들의 대표 scope 하나이며, 없으면 생략한다.
- 본문은 PR 템플릿의 섹션 순서와 제목을 그대로 쓰고, HTML 주석은 지운다.
- `노션`: 인자나 대화에 링크가 있으면 넣는다. 없으면 5단계에서 묻는다. 링크가 없으면 섹션을 통째로 지운다.
- `변경 내용`: `git log origin/<대상>..HEAD`와 diff를 보고 리뷰어가 확인할 핵심만 3~5줄로 쓴다. 파일 목록을 나열하지 않는다.
- `변경 이유`: 1~2줄. 배경은 노션에 있으니 길게 쓰지 않는다.
- `테스트`: 3단계에서 통과한 명령만 `[x]`로 체크한다. 화면 확인은 찍은 환경만 적고(예: `pc-chrome, android, iphone`), 찍지 않았으면 항목을 지운다. 사람이 직접 한 수동 검증은 사용자에게 들은 것만 적는다.

## 5. 확인

push와 PR 생성은 원격에 바로 보이고, PR을 열면 디스코드 알림도 나간다. 기존 PR 갱신을 포함해 확인받기 전에는 push하거나 PR을 열지 않는다.

- 새 PR: 대상 브랜치, 제목, 본문을 보여 주고 한 번에 묻는다.
- 노션 링크(링크가 아직 없을 때만): "없음" 또는 직접 입력
- 이대로 올릴지: 올리기 / 수정
- 기존 PR: 기존 PR 주소와 push할 커밋(`git log --oneline @{u}..HEAD`, upstream이 없으면 `origin/<대상>..HEAD`)을 보여 주고 push할지 묻는다.

## 6. PR 열기

본문은 저장소 밖 임시 파일(스크래치 디렉터리가 있으면 그곳, 없으면 `mktemp`)에 쓴다.

```bash
git push -u origin <브랜치>
gh pr create --base <대상> --head <브랜치> --title "<제목>" --body-file <임시 파일>
```

기존 PR이면 `git push`만 한다.

PR 주소를 알린다. `hotfix/`였다면 "병합 후 main → dev 백머지 PR이 필요합니다"를 한 줄 덧붙인다.
56 changes: 56 additions & 0 deletions .claude/skills/start-work/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
name: start-work
description: 새 작업 브랜치를 CONTRIBUTING.md 규칙대로 최신 기준 브랜치(dev, hotfix는 main)에서 만든다. "작업 시작", "브랜치 따줘", "새 작업 들어가자" 같은 요청이나 /start-work에서 사용한다.
argument-hint: <작업 설명>
---

# 작업 시작

작업 설명: $ARGUMENTS

브랜치 종류·기준 브랜치의 정본은 [CONTRIBUTING.md](../../../CONTRIBUTING.md)의 `브랜치 종류` 표다. 이 문서와 다르면 CONTRIBUTING.md를 따른다.

## 1. 브랜치 종류와 이름

- 작업 설명이 비어 있으면 무슨 작업인지 묻는다.
- 설명을 보고 `feature` / `fix` / `refactor` / `chore` / `docs` / `hotfix` 중 하나를 고른다.
- 버그 수정인데 "운영 장애", "바로 배포" 같은 단서가 없으면 `fix`와 `hotfix` 중 무엇인지 묻는다. `hotfix`는 기준 브랜치가 `main`이라 잘못 고르면 되돌리기 번거롭다.
- 이름은 `<종류>/<영어 소문자 kebab-case 2~4단어>`로 짓는다. 예: `feature/calendar-design`
- 같은 이름이 로컬(`git branch --list`)이나 origin(`git ls-remote --heads origin <브랜치>`)에 있으면 다른 이름을 고른다. squash 병합이 끝난 브랜치는 다시 쓰지 않는다.

종류가 분명하면 묻지 않고 바로 진행한다.

## 2. 현재 작업 상태

`git status --short`로 커밋하지 않은 변경을 확인한다.

- 변경이 없으면 3단계로 간다.
- 변경이 있으면 멈추고 어떻게 할지 묻는다. stash·reset·checkout으로 임의로 치우지 않는다.
- 새 작업에 가져갈 변경: 그대로 새 브랜치를 만든다. `git switch -c`는 작업 트리 변경을 유지한다.
- 이전 작업의 변경: 사용자가 먼저 커밋하거나, 새 작업을 worktree(`.worktrees/<topic>`)에서 시작한다.

## 3. 최신 기준 브랜치에서 만들기

기준 브랜치는 `hotfix`면 `main`, 나머지는 `dev`다.

```bash
git fetch origin --prune
git switch --no-track -c <브랜치> origin/<기준>
```

worktree에서 시작하기로 했다면:

```bash
git fetch origin --prune
git worktree add --no-track -b <브랜치> .worktrees/<topic> origin/<기준>
```

worktree에는 `node_modules`와 `.env.local`이 없다. worktree에서 `npm ci`로 설치하고, `.env.local`은 원래 저장소에서 복사한다. `node_modules`를 심볼릭 링크로 연결하면 Turbopack 빌드가 "points out of the filesystem root"로 실패한다.

`--no-track`은 새 브랜치가 `origin/dev`를 upstream으로 잡지 않게 한다. upstream은 처음 push할 때 `-u`로 자기 원격 브랜치에 연결한다.

## 4. 보고

- 만든 브랜치 이름과 기준 커밋(`git log --oneline -1`)을 알린다.
- 화면이 바뀌는 작업이면 "노션에 넣을 작업 전 스크린샷을 지금 찍어 두세요"를 한 줄 덧붙인다.
- 작업 중에는 전체 테스트를 돌리지 않는다. lint·test·build는 PR 직전에 `/open-pr`에서 한 번 실행한다.
5 changes: 5 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 노션
<!-- 작업 배경·전후 스크린샷이 있는 노션 페이지 링크. 없으면 이 칸을 지운다. -->
-

## 변경 내용
-

Expand All @@ -8,4 +12,5 @@
- [ ] `npm run lint`
- [ ] `npm test`
- [ ] `npm run build`
- [ ] 화면 확인(`npm run screens`):
- [ ] 그 외 수동 검증:
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ SERVICE_CONTEXT.md
# Local git worktrees used for isolated feature development
.worktrees/

# Claude Code personal settings (project skills in .claude/skills are committed)
.claude/settings.local.json

# check-screens 스크린샷 결과 (npm run screens)
.screens/

# misc
.DS_Store
.idea/
Expand Down
64 changes: 61 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,30 @@ docs: 개발자 온보딩 보완

- `main`은 배포 기준 브랜치입니다. 직접 push하지 않고 모든 변경을 PR로 병합합니다.
- `dev`는 기능을 통합하고 릴리스 전에 함께 검증하는 브랜치입니다.
- 일반 작업은 최신 `dev`에서 `feature/<topic>` 브랜치를 만들고 `dev`를 대상으로 PR을 엽니다. 브랜치는 하나의 작업 단위로 짧게 유지하며, squash 병합이 끝난 feature 브랜치는 다시 사용하지 않습니다.
- 긴급 수정은 최신 `main`에서 `hotfix/<topic>` 브랜치를 만들고 `main`을 대상으로 PR을 엽니다.
- 작업 브랜치는 아래 `브랜치 종류` 표의 기준 브랜치 최신 상태에서 만들고, 같은 표의 PR 대상으로 PR을 엽니다.
- 브랜치는 하나의 작업 단위로 짧게 유지하며, squash 병합이 끝난 브랜치는 다시 사용하지 않습니다.

## 브랜치 종류

| 브랜치 | 용도 | 기준 브랜치 | PR 대상 | 커밋·PR 타입 |
| :-- | :-- | :-- | :-- | :-- |
| `feature/<topic>` | 새 기능, 화면·디자인 변경 | `dev` | `dev` | `feat` |
| `fix/<topic>` | 다음 배포에 함께 나가도 되는 버그 수정 | `dev` | `dev` | `fix` |
| `refactor/<topic>` | 동작이 바뀌지 않는 구조 개선 | `dev` | `dev` | `refactor` |
| `chore/<topic>` | 빌드·설정·의존성·개발 도구 | `dev` | `dev` | `chore` |
| `docs/<topic>` | 문서만 변경 | `dev` | `dev` | `docs` |
| `hotfix/<topic>` | 운영 장애처럼 바로 배포해야 하는 수정 | `main` | `main` | `fix` |

- `<topic>`은 영어 소문자 kebab-case로 짧게 씁니다. 예: `feature/calendar-design`, `fix/logout-session`
- 표에 없는 접두사(`feat/`, `codex/` 등)로 새 브랜치를 만들지 않습니다.

## 공통 브랜치 흐름

브랜치 병합에는 다음 공통 흐름을 적용합니다.

| 병합 방향 | 방식 |
| :-- | :-- |
| `feature/*` → `dev` | Squash and merge |
| `feature/*`, `fix/*`, `refactor/*`, `chore/*`, `docs/*` → `dev` | Squash and merge |
| `dev` → `main` | Create a merge commit |
| `hotfix/*` → `main` | Squash and merge |
| `main` → `dev` 백머지 | Create a merge commit |
Expand All @@ -39,3 +53,47 @@ docs: 개발자 온보딩 보완
- main으로 직접 merge 전 최소 1명의 리뷰가 필요하다.
- PR 본문은 .github/pull_request_template.md 형식을 그대로 따른다.
- PR 본문에 변경 이유와 테스트 방법을 간략히 적는다.
- PR을 열기 전에 대상 브랜치의 최신 상태를 작업 브랜치에 merge로 반영한다. 이미 push한 작업 브랜치는 rebase하거나 force-push하지 않는다.
- 병합은 리뷰어가 확인한 뒤 위 `공통 브랜치 흐름`의 방식으로 한다.

## PR 전 검증

- 작업 중에는 전체 테스트를 돌리지 않는다. 필요한 확인만 한다.
- PR을 열기 전에 `npm run lint` → `npm test` → `npm run build`를 한 번 실행하고, 결과를 PR 본문 테스트 칸에 체크한다.
- 문서(`*.md`)·`.claude/`·`.github/`만 바뀐 PR은 생략할 수 있다. 생략하면 테스트 칸에 생략했다고 적는다.
- 화면이 바뀌면 아래 기준으로 환경을 골라 스크린샷을 확인한다.

### 화면 확인 기준

| 변경 종류 | PC 크롬 | PC 사파리 | 안드로이드 (크롬 에뮬레이션) | 아이폰 (시뮬레이터) |
| :-- | :-: | :-: | :-: | :-: |
| 로직·데이터만 (화면 변화 없음) | ✓ | | | |
| PC 레이아웃·스타일 | ✓ | 사파리 주의 CSS일 때 | | |
| 모바일 레이아웃 | | | ✓ | ✓ |
| 터치·드래그·키보드·포커스·바텀시트 | | | ✓ + 직접 조작 | ✓ + 직접 조작 |
| `dev` → `main` 배포 | ✓ | ✓ | ✓ | ✓ |

- 사파리 주의 CSS: `fixed`·`sticky`, `h-screen`·`dvh`·`svh`, `safe-area`, `backdrop-filter`, 날짜·시간 input, 스크롤 동작, 16px 미만 글자의 input
- 앱은 User-Agent로 모바일 화면을 판단한다. 크롬 창 크기만 줄이면 모바일 화면이 나오지 않으므로 기기 에뮬레이션을 쓴다.
- `npm run screens -- --path /home --targets pc-chrome,android --label after`로 찍으면 `.screens/after/`에 저장된다. 처음에는 `npm run screens -- login`으로 로그인 상태를 저장하고, 아이폰 시뮬레이터 Safari에서도 한 번 로그인한다.
- 백엔드 CORS와 구글 로그인이 `localhost:3000`만 허용하므로, 여러 worktree를 쓸 때도 확인할 폴더의 dev 서버 하나만 3000번으로 띄운다.

## 작업 공유

| 위치 | 내용 |
| :-- | :-- |
| 노션 작업 페이지 | 작업 배경(왜 시작했는지), 논의 내용, 전후 스크린샷, 개발 메모 |
| PR | 변경 내용, 짧은 변경 이유, 테스트 결과, 노션 페이지 링크 |
| 디스코드 | PR을 열면 GitHub Actions가 자동으로 알린다. 작업 내역을 따로 보내지 않는다. |

- 노션 페이지가 없는 작은 작업은 PR 본문의 `노션` 칸을 지운다.

## Claude Code 스킬

저장소의 `.claude/skills/`에 위 규칙을 따르는 스킬이 있다. 사용하려면 [GitHub CLI](https://cli.github.com/)를 설치하고 `gh auth login`으로 로그인한다.

| 명령 | 하는 일 |
| :-- | :-- |
| `/start-work <작업 설명>` | 브랜치 종류·이름을 정하고 최신 기준 브랜치에서 브랜치를 만든다. |
| `/check-screens [경로...] [before\|after]` | 위 `화면 확인 기준`대로 환경을 골라 스크린샷을 찍고 깨짐을 확인한다. |
| `/open-pr [노션 링크]` | 대상 브랜치 반영 → lint·test·build·화면 확인 → 템플릿 작성 → 확인 후 PR을 연다. 병합은 하지 않는다. |
Loading