|
| 1 | +# 회고 · Week 2 |
| 2 | + |
| 3 | +기간: 2026-06-01 ~ 2026-06-07 |
| 4 | + |
| 5 | +이번 주는 처음으로 코드가 도는 주차였다. 1주차에 정한 결정들을 끼워 맞춰 보니 사람이 보는 surface 가 한 번 다 깔렸고 — 동시에, 내가 정한 워크플로우 룰이 실제로 어떤 부분에서 약했는지가 드러났다. |
| 6 | + |
| 7 | +--- |
| 8 | + |
| 9 | +## 무엇을 만들었나 |
| 10 | + |
| 11 | +| Surface | PR | |
| 12 | +|---------|----| |
| 13 | +| Next.js 15 + Tailwind + shadcn + Docker/Caddy 부트스트랩 | #23 | |
| 14 | +| Prisma 6 + SQLite + 초기 migration | #24 | |
| 15 | +| Auth.js v5 Google OAuth + `@snu.ac.kr` 화이트리스트 + `/admin/members` | #25 | |
| 16 | +| 세션 생성 + 상세 + 본인 행 입력 (페이스 자동) | #26 | |
| 17 | +| 단체사진 업로드 (로컬 볼륨, 원본 보존) | #27 | |
| 18 | +| Hotfix: 빈 입력 noop / encType 제거 / `Session.id` cuid→Int | #28 | |
| 19 | +| 폼 inline validation + double-submit 차단 (`useActionState` + `useFormStatus`) | #29 | |
| 20 | +| `/sessions` 아카이브 리스트 + 워크플로우 보강 (SKILL §4.5) | #30 | |
| 21 | +| 사진/admin 폼 retrofit + CI + Deploy + 이 회고 | (이 PR) | |
| 22 | + |
| 23 | +8 + 1 PR. 모두 `dev` 에 머지. `main` 은 아직 비어있고 Week 2 마지막에 `v0.1.0` 태그와 함께 첫 머지 예정. |
| 24 | + |
| 25 | +--- |
| 26 | + |
| 27 | +## 워크플로우의 약점이 드러난 두 번의 사고 |
| 28 | + |
| 29 | +1주차에 06-Checkpoints 7개를 "AI 가 내 지시를 잘못 해석한 부분을 잡는 가이드라인" 으로 재정의했다. 결과적으로 fidelity 측면 — 시킨 일을 시킨 만큼 했는지 — 은 잘 잡혔다. 그런데 그게 사용자 경험까지 보장하진 못한다는 게 두 번의 사고에서 드러났다. |
| 30 | + |
| 31 | +**사고 1.** 빈 입력 / 잘못된 입력에 알림이 없는 채로 PR 두 번이 머지됐다 (#26, #28). 내가 spec 의 "Out of scope" 에 "react-hook-form 도입 시" 라고 박아두고 미뤘는데, 그게 합리적 분리의 도구가 아니라 미루기의 핑계로 쓰였다. 사용자가 빈 폼 [추가] 를 누르면 풀스크린 에러 페이지 → silent noop 으로 한 번 hotfix 했지만, 둘 다 사용자 경험은 깨져 있었다. |
| 32 | + |
| 33 | +**사고 2.** [세션 만들기] 빠른 더블 클릭으로 row 가 두 번 생성됐다. `useFormStatus` 한 줄이면 막히는 흔한 패턴인데, server action 만 박고 React 19 표준 패턴을 빼먹었다. 처음 PR #26 부터 박혀 있었어야 했다. |
| 34 | + |
| 35 | +둘 다 build 가 그린이고, 06-Checkpoints fidelity 7개도 다 통과했다. 통과한 게 사실 거짓말이었다는 게 아니라, "happy path 코드가 도는가" 와 "사용자가 잘못된 행동을 했을 때 어떻게 되는가" 가 같은 질문이 아니라는 게 정확하다. |
| 36 | + |
| 37 | +수정: PR #30 에 두 곳을 박았다. |
| 38 | +- `.gjc/skills/murun-feature/SKILL.md` §4.5 — 새 폼/입력 추가할 때 negative case alert / `useActionState` / `useFormStatus` / DB unique 안전망을 **체크 없이 진행 금지** 로 명시. |
| 39 | +- `docs/wiki/06-Checkpoints.md` #7 — "happy path 만으로는 부족, 빈 입력 / 잘못된 타입 / 더블 클릭 / 권한 없는 사용자를 머릿속에서라도 흘려본다." |
| 40 | + |
| 41 | +이번 PR(#?)에서 그 룰을 이미 있는 폼(사진 업로드, admin 승인/거절) 에 소급 적용했다. 처음부터 박혔어야 할 코드가 늦게 들어간 거라 새로운 가치는 없지만, **룰이 실제로 문서에 머무르지 않고 다음 PR 에 영향을 줬다는 게 1주차에 정한 것과의 차이**. |
| 42 | + |
| 43 | +--- |
| 44 | + |
| 45 | +## AI 가 스스로 못 잡은 것 |
| 46 | + |
| 47 | +직전 사고를 자기 점검해보면 일관된 패턴이 있다. |
| 48 | + |
| 49 | +- AI 는 코드 측 검증(typecheck/lint/build) 결과를 보고 "통과" 라고 보고했다. 그건 정확했다. |
| 50 | +- AI 는 PR 본문에 "사용자가 직접 E2E 검증" 이라고 박았다. 그것도 정확했다. |
| 51 | +- 그런데 그 사이에 — "사용자에게 시키기 전에 머릿속 시나리오로 한 번 흘려보는 단계" — 가 아예 빠졌다. |
| 52 | + |
| 53 | +AI 가 실제 브라우저를 못 클릭하는 건 사실이다. 그렇다고 머릿속 시뮬레이션까지 못 할 이유는 없다. "이 폼이 빈 입력이면 어떻게 되지?" 같은 질문은 코드를 다시 한 번 읽으면 답이 나온다. 그걸 안 했을 뿐이다. |
| 54 | + |
| 55 | +3주차에 적용할 변화는 두 개: |
| 56 | +1. spec 의 "Out of scope" 가 정말 분리 가치가 있는지, 아니면 그냥 미루는 건지 구분한다. 미루는 것일 가능성이 보이면 OOS 에서 빼고 같은 PR 에 합친다. |
| 57 | +2. PR 본문의 Fidelity check #7 에 negative case 머릿속 시나리오 1~2줄을 강제로 적는다 — "빈 입력 → ...", "권한 없는 사용자 → ..." 같은 한 줄. |
| 58 | + |
| 59 | +--- |
| 60 | + |
| 61 | +## 도메인 결정 측면 |
| 62 | + |
| 63 | +1주차에 정한 "호스트 = 세션 작성자, 참여자 = 본인 행을 적는" 모델은 어색함 없이 그대로 동작했다. 카톡 단톡방에 던지는 시나리오(아직 OG 이미지 없어 미리보기는 빠짐) 를 가정하고 짠 흐름이 실제로 운영진/참여자 모두에게 자연스러운지는 — Week 3 의 실제 운동 1회 dogfooding 에서 검증된다. |
| 64 | + |
| 65 | +작게 뒤집은 결정: |
| 66 | +- `Session.id` cuid → autoincrement Int (#28). enumeration 차단 가치 < 가독성. 동아리 30명 내부용에 더 맞음. |
| 67 | +- 검증 강화에서 "초는 0~59" 로 강제 (#29). 사용자가 5분 0초를 4'60" 로 적는 흔한 실수를 막음. |
| 68 | + |
| 69 | +--- |
| 70 | + |
| 71 | +## 인프라 측면 |
| 72 | + |
| 73 | +자체 N100 + Docker + Caddy + SQLite 결정이 운영 측에서 깔끔하게 안정됐다. |
| 74 | + |
| 75 | +- 이번 PR 의 CI 워크플로우 — PR 마다 typecheck/lint/build 자동. 솔로 프로젝트지만, "내가 매번 로컬에서 돌리는 단계" 를 GH 가 대신 해주는 게 안전망. |
| 76 | +- 이번 PR 의 Deploy 워크플로우 — dev push → ghcr.io build → SSH → N100 staging 자동. main 은 environment 보호 후 수동 승인. |
| 77 | +- SSH key + N100_HOST 등 secrets 등록은 호스트(나) 의 책임. README 에 절차가 있고, 등록 안 된 채 workflow 실행 시 sanity check 가 명확히 막아준다. |
| 78 | + |
| 79 | +남은 호스트 작업: |
| 80 | +- N100 에서 첫 `docker compose up -d` 직접 — 코드의 진짜 검증 |
| 81 | +- duckdns 도메인 연결 (Week 3 초) |
| 82 | +- `dev → main` 첫 머지 + `v0.1.0` 태그 |
| 83 | + |
| 84 | +--- |
| 85 | + |
| 86 | +## 다르게 한다면 (3주차에서 바꾸는 것) |
| 87 | + |
| 88 | +1. **spec 작성 시 OOS 항목을 "정말 미루는가" 한 번 자문**. PR #26/#28 의 inline validation 처럼, OOS 가 사실 사용자 경험을 직접 망치는 거면 같은 PR 에 합친다. |
| 89 | +2. **PR 본문 Fidelity #7 에 negative case 한 줄 명시 강제**. "빈 입력 → alert", "더블 클릭 → dedupe 동작" 같이 머릿속 시나리오를 글로. |
| 90 | +3. **AI 가 만든 PR 을 머지하기 전 코드 측 검증 + 머릿속 시나리오 둘 다 통과한 다음에만 사용자에게 검증 요청**. 빌드 그린만 보고 "검증 가능" 이라고 보고하는 패턴 폐기. |
| 91 | +4. **PR scope 묶기/쪼개기 결정을 한 줄 메모로**. 이번 PR 처럼 4 commit 1 PR 도 합리적, 작은 PR 4개도 합리적. 결정 기준은 "사용자 머지 부담" 과 "도메인 응집도" — 매번 자문. |
| 92 | +5. **dev → main 머지 주기를 정해둠**. 매 PR 마다 하지 않더라도, Week 2 끝/3 끝에 v0.x.0 태그와 함께 일괄. main 이 비어있는 채로 가는 기간이 너무 길지 않게. |
| 93 | + |
| 94 | +--- |
| 95 | + |
| 96 | +## 3주차 계획 |
| 97 | + |
| 98 | +핵심은 **사용자 입장에서 실제로 한 번 굴려보는 주**. 코드는 거의 다 됐고, 진짜 모임 1회의 기록을 끝까지 흘려본 다음에 발견되는 어색함 들을 잡는다. |
| 99 | + |
| 100 | +- [ ] N100 에 실제 배포 (CI/CD secret 등록 후 dev push → staging 동작 확인) |
| 101 | +- [ ] duckdns 도메인 + Caddy HTTPS |
| 102 | +- [ ] Google OAuth redirect URI 에 prod 도메인 추가 |
| 103 | +- [ ] `dev → main` 첫 머지 + `v0.1.0` |
| 104 | +- [ ] OG 이미지 (#15) — 카톡 공유 미리보기. 이게 빠지면 동아리 공유 UX 가 무미건조함. |
| 105 | +- [ ] (시간 되면) 검색·필터 (#14) — 참여 인원 수, 장소, 기간 |
| 106 | +- [ ] (시간 되면) 멤버 페이지 + 누적/페이스 추이 차트 (#13) |
| 107 | +- [ ] **실제 동아리 운동 1회 dogfooding** — 호스트 시점 + 참여자 시점 양쪽으로 흘려보기 |
| 108 | +- [ ] 3주차 회고 |
| 109 | + |
| 110 | +3주차에 발견되는 사고들은 이번 주처럼 spec/OOS 누락이 아니라, **실제 사용자가 도메인을 어떻게 다르게 쓰는가** 에서 나올 것이다. 그게 가장 의미있는 학습일 것이라 본다. |
0 commit comments