You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
| 코드 audit: cursor 페이지네이션 / 죽은 env / server-only 가드 | (이 PR) |
22
25
23
-
8 + 1 PR. 모두 `dev` 에 머지. `main` 은 아직 비어있고 Week 2 마지막에 `v0.1.0` 태그와 함께 첫 머지 예정.
26
+
11 + 1 PR. 모두 `dev` 에 머지. `main` 은 Week 2 마지막에 `v0.1.0` 태그와 함께 첫 머지.
24
27
25
28
---
26
29
@@ -38,7 +41,38 @@
38
41
-`.gjc/skills/murun-feature/SKILL.md` §4.5 — 새 폼/입력 추가할 때 negative case alert / `useActionState` / `useFormStatus` / DB unique 안전망을 **체크 없이 진행 금지** 로 명시.
39
42
-`docs/wiki/06-Checkpoints.md`#7 — "happy path 만으로는 부족, 빈 입력 / 잘못된 타입 / 더블 클릭 / 권한 없는 사용자를 머릿속에서라도 흘려본다."
40
43
41
-
이번 PR(#?)에서 그 룰을 이미 있는 폼(사진 업로드, admin 승인/거절) 에 소급 적용했다. 처음부터 박혔어야 할 코드가 늦게 들어간 거라 새로운 가치는 없지만, **룰이 실제로 문서에 머무르지 않고 다음 PR 에 영향을 줬다는 게 1주차에 정한 것과의 차이**.
44
+
PR #31 에서 그 룰을 이미 있는 폼(사진 업로드, admin 승인/거절) 에 소급 적용했다. 처음부터 박혔어야 할 코드가 늦게 들어간 거라 새로운 가치는 없지만, **룰이 실제로 문서에 머무르지 않고 다음 PR 에 영향을 줬다는 게 1주차에 정한 것과의 차이**.
45
+
46
+
---
47
+
48
+
## 사고 시즌 2: 배포 사이클의 hotfix 3연발
49
+
50
+
CI/Deploy 파이프라인을 올리고 N100 에 처음 SSH 배포를 굴리면서 사고 3건이 추가로 터졌다 (#32, #33, 이번 PR). 코드는 로컬에서 그린이었는데, 환경이 바뀌니 한 번도 같이 돌려본 적 없는 조합에서 깨졌다.
51
+
52
+
**사고 3. `public/` 디렉터리가 없는데 Dockerfile 이 무조건 COPY (#32).**
Dockerfile 이 `node_modules/prisma`, `node_modules/@prisma` 만 복사해서 `effect` 같은 transitive deps 가 누락 → entrypoint 가 부팅 단계에서 죽음. Healthcheck 도 `/` 를 보고 있었는데 그게 로그인 redirect 라 healthy 가 영영 안 됐다. 인증과 무관한 `/api/health` 로 따로 뺐다.
60
+
61
+
**사고 6. 이번 audit 에서 잡힌 cursor 페이지네이션 누락.**
62
+
`orderBy: (date desc, id desc)` 인데 cursor 는 `id < cursorId` 만 봤다. 오래된 날짜인데 늦게 만들어진(=id 가 큰) 세션이 페이지 사이에서 누락. 데이터가 한 자리수일 땐 안 보이는 버그. 이건 사용자가 못 발견했어도 시간 문제였다.
63
+
64
+
같이 잡은 audit 항목:
65
+
- 죽은 `NEXT_PUBLIC_APP_URL` 변수 → `layout.tsx` 의 `metadataBase` 로 실제 사용.
66
+
-`lib/uploads.ts` 가 client 컴포넌트의 transitive import 로 끌려갈 위험 → `import "server-only"` 가드 + URL helper 분리.
67
+
-`[세션 수정 — 다음 PR에서 추가]` placeholder 노출 → 제거.
68
+
69
+
**이 시즌의 공통 패턴.** 모두 "로컬에서 한 가지 경로만 굴려보고 그린이면 통과 보고" 였다. 코드는 같은 환경에서 같은 입력으로만 굴렸고, 다른 환경 (Docker, GHCR, N100, edge case 데이터) 은 한 번도 같이 굴려본 적 없었다. Hotfix 가 3번 연달아 터지면서 알게 된 것:
70
+
71
+
-**로컬 build 그린 ≠ 컨테이너 build 그린**. Docker 단계는 다른 안전망이 필요.
72
+
-**컨테이너 부팅 ≠ 앱 healthy**. healthcheck 가 진짜 ready 신호를 봐야 함.
73
+
-**앱이 healthy ≠ 새 코드가 N100 에 도달**. deploy pipeline 의 git sync 가 빠지면 image 만 새거고 compose 파일은 옛것.
74
+
75
+
수정: `.gjc/skills/murun-feature/SKILL.md` 와 `06-Checkpoints.md` 에 "환경별 검증 분리" 를 추가한다 (3주차에서 보강).
42
76
43
77
---
44
78
@@ -52,9 +86,10 @@
52
86
53
87
AI 가 실제 브라우저를 못 클릭하는 건 사실이다. 그렇다고 머릿속 시뮬레이션까지 못 할 이유는 없다. "이 폼이 빈 입력이면 어떻게 되지?" 같은 질문은 코드를 다시 한 번 읽으면 답이 나온다. 그걸 안 했을 뿐이다.
54
88
55
-
3주차에 적용할 변화는 두 개:
89
+
3주차에 적용할 변화는 세 개:
56
90
1. spec 의 "Out of scope" 가 정말 분리 가치가 있는지, 아니면 그냥 미루는 건지 구분한다. 미루는 것일 가능성이 보이면 OOS 에서 빼고 같은 PR 에 합친다.
57
91
2. PR 본문의 Fidelity check #7 에 negative case 머릿속 시나리오 1~2줄을 강제로 적는다 — "빈 입력 → ...", "권한 없는 사용자 → ..." 같은 한 줄.
92
+
3. 인프라/배포 PR 은 **로컬 build 그린 + 컨테이너 build 그린 + healthcheck 가 진짜 ready 를 보는가** 세 줄을 PR 본문에 명시. 코드만 보면 사고 시즌 2가 또 반복된다.
58
93
59
94
---
60
95
@@ -76,20 +111,24 @@ AI 가 실제 브라우저를 못 클릭하는 건 사실이다. 그렇다고
76
111
- 이번 PR 의 Deploy 워크플로우 — dev push → ghcr.io build → SSH → N100 staging 자동. main 은 environment 보호 후 수동 승인.
77
112
- SSH key + N100_HOST 등 secrets 등록은 호스트(나) 의 책임. README 에 절차가 있고, 등록 전에는 이미지 build/push 까지만 성공시키고 SSH deploy 는 skip 하도록 했다.
78
113
79
-
남은 호스트 작업:
80
-
- N100 에서 첫 `docker compose up -d` 직접 — 코드의 진짜 검증
81
-
- duckdns 도메인 연결 (Week 3 초)
114
+
남은 호스트 작업 (Week 2 안에 끝남):
115
+
- N100 에서 `docker compose up -d` 로 staging 기동 — 자동 배포 파이프라인 통과 확인
82
116
-`dev → main` 첫 머지 + `v0.1.0` 태그
83
117
118
+
Week 3 로 미루는 호스트 작업:
119
+
- duckdns 도메인 + HTTPS
120
+
- prod environment 보호 + main → prod 라인 첫 발사
121
+
84
122
---
85
123
86
124
## 다르게 한다면 (3주차에서 바꾸는 것)
87
125
88
126
1.**spec 작성 시 OOS 항목을 "정말 미루는가" 한 번 자문**. PR #26/#28 의 inline validation 처럼, OOS 가 사실 사용자 경험을 직접 망치는 거면 같은 PR 에 합친다.
89
127
2.**PR 본문 Fidelity #7 에 negative case 한 줄 명시 강제**. "빈 입력 → alert", "더블 클릭 → dedupe 동작" 같이 머릿속 시나리오를 글로.
90
128
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 이 비어있는 채로 가는 기간이 너무 길지 않게.
129
+
4.**PR scope 묶기/쪼개기 결정을 한 줄 메모로**. 4 commit 1 PR 도 합리적, 작은 PR 여러 개도 합리적. 결정 기준은 "사용자 머지 부담" 과 "도메인 응집도".
130
+
5.**dev → main 머지 주기를 정해둠**. 매 PR 마다 하지 않더라도, Week 끝마다 v0.x.0 태그와 함께 일괄. main 이 비어있는 채로 가는 기간이 너무 길지 않게.
131
+
6.**배포 PR 은 다른 환경까지 한 사이클 굴려본 다음 머지**. local pnpm check 그린만 보고 배포 PR 머지하지 않는다. workflow_dispatch 로 한 번 굴려본 다음 그린이면 머지.
0 commit comments