docs(changes): 포스트 카테고리 시드/뷰 재설계 기록 + 세션 회고 교훈 반영
- docs/changes: 2026-07-24 포스트 카테고리 시드+뷰 재설계 문서 신규 + index/coverage-checklist/이전 리디자인 문서 교차링크. - 회고 교훈 2건 docs-first 반영: verification-strategies(기존 컨트롤러 시그니처 변경도 full test 의무 — baseline RED 방지), workflow-patterns(도메인/상호작용 모델은 UI taxonomy 가정 전 permission enum·work-log 로 접지). - ATP 세션 산출물(report/설계도/검증/문서화) 동봉. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UqyLwTxF3FmW1eJCWN8yAo
This commit is contained in:
parent
0b8da9d19f
commit
184839434c
|
|
@ -0,0 +1,344 @@
|
||||||
|
---
|
||||||
|
phase: design
|
||||||
|
agent: design-advisor
|
||||||
|
agent_version: 1
|
||||||
|
generated_at: 2026-07-24T10:04:14+09:00
|
||||||
|
concerns:
|
||||||
|
- "폰트 불일치(설계 미해결이 아니라 명시적 유지): bibimbap.css는 body에 Pretendard(--font-sans)를 지정하나 posts-list.jsp 인라인 <style>의 body{font-family:system-ui...}가 소스 순서상 뒤에 와서 승리 → posts 페이지는 system-ui. 사용자 확정 제약이 system-ui 유지·전체 폰트 교체 금지이므로 그대로 둔다. 사이트 다른 페이지(Pretendard)와 미세 톤 차이 존재하나 의도된 현상태 보존."
|
||||||
|
- "탭 오버플로를 가로 스크롤로 선택 → 좁은 모바일에서 우측 카테고리가 화면 밖으로 밀릴 수 있음. edge fade 마스크 + scroll-snap 으로 완화하나 '스크롤 가능' 어포던스가 시각장애/저시력 사용자에게 약할 수 있음. 카테고리 6개(전체+5) 기준 대부분 뷰포트에서 스크롤 미발생이라 실사용 리스크는 낮음. 카테고리가 8~10개로 증가하면 재평가 필요."
|
||||||
|
- "notice(공지) 라이트 hue #B0741A 는 크림 배경 대비 ~3.5:1 로 소형 본문 텍스트 4.5:1 미달. 완화책: hue는 마커(dot)·섹션 rule·서수(UI 요소, 3:1 기준 충족)에만 쓰고, 뱃지/탭의 실제 라벨 텍스트는 --text(고대비 잉크)로 유지. 라벨 색을 hue로 물들이지 않는다. 이 규율을 §2/§4 에 명시."
|
||||||
|
- "카테고리 hue 는 고정 5-slug 화이트리스트. 운영자가 신규 slug 카테고리를 추가하면 hue 매핑이 없어 중립(잉크/앰버) 폴백된다(graceful). 단 신규 카테고리에 색을 주려면 CSS 토큰 + 화이트리스트 helper 두 곳을 함께 갱신해야 하는 유지 결합이 있음. §검증 AC 가 두 집합 동수 불변식으로 누락을 검출."
|
||||||
|
- "(b) 빈상태에서 활성 카테고리명 표기는 categories 리스트를 categoryId 로 스캔해 얻음. 해당 카테고리가 비활성화(listActive 미포함)면 이름 조회 실패 → 일반 문구로 폴백. 정상 흐름에선 발생하지 않음(필터 링크는 활성 카테고리에서만 생성)."
|
||||||
|
concerns_checked: true
|
||||||
|
references:
|
||||||
|
requirements: null
|
||||||
|
research: null
|
||||||
|
adrs: []
|
||||||
|
source_files:
|
||||||
|
- src/main/webapp/WEB-INF/views/posts-list.jsp
|
||||||
|
- src/main/webapp/WEB-INF/views/fragments/posts-empty.jspf
|
||||||
|
- src/main/webapp/css/bibimbap.css
|
||||||
|
- src/main/webapp/WEB-INF/views/theme-init.jsp
|
||||||
|
- src/main/java/com/pandoli365/bibimbap/data/PostData.java
|
||||||
|
- src/main/java/com/pandoli365/bibimbap/data/PostCategoryData.java
|
||||||
|
- src/main/java/com/pandoli365/bibimbap/mapper/PostCategoriesMapper.java
|
||||||
|
- src/main/java/com/pandoli365/bibimbap/controller/PostController.java
|
||||||
|
- /Users/wemadeplay/.claude/plugins/cache/claude-plugins-official/frontend-design/unknown/skills/frontend-design/SKILL.md
|
||||||
|
---
|
||||||
|
|
||||||
|
# 설계: 포스트 보드 "카테고리별 목록 뷰" 표현층 재설계
|
||||||
|
|
||||||
|
## 목표 / 비목표
|
||||||
|
|
||||||
|
**목표**
|
||||||
|
- 편집형 일방 발행(Unity 블로그 모델) 세계관에 맞는 목록 뷰 표현층. 상호작용(댓글 유도) 축이 아니라 편집/매거진 톤.
|
||||||
|
- 카테고리 탭 네비를 이 화면의 단 하나의 signature 로 만든다 — "편집 섹션 인덱스(masthead index)". 나머지(카드/뱃지/빈상태)는 절제.
|
||||||
|
- 5개 카테고리를 slug 기반 절제된 편집 팔레트로 시각 코딩(무지개 회피, 크림+amber 와 harmonize, light/dark 양쪽 대비 확보).
|
||||||
|
- 접근성 결손 3종 보강: `:focus-visible` 포커스 링, `aria-current="page"`, 탭 오버플로 처리.
|
||||||
|
- 빈상태를 편집형 목소리 2종으로 분기(카테고리 미생성 / 특정 카테고리 글 0). 현재 peer-forum 문구("커뮤니티의 첫 글을 남겨보세요")를 발행 톤으로 교정.
|
||||||
|
- 로드 시 signature(탭 인덱스)에 고임팩트 모션 1곳. `prefers-reduced-motion: reduce` 존중.
|
||||||
|
|
||||||
|
**비목표**
|
||||||
|
- 기능 로직 불변: 필터 쿼리(categoryId), keyset 페이지네이션(cursorCreatedAt/cursorId + categoryId), 컨트롤러/매퍼/SQL — 절대 미변경.
|
||||||
|
- 전역 폰트 교체 금지(header/footer/타 페이지 정합). 시그니처는 weight/tracking/letter-spacing/크기 대비로만.
|
||||||
|
- bibimbap.css 수정 금지(§7 근거). resources/static 신규 자원 금지.
|
||||||
|
- 카드 상세/작성 폼/권한 게이트 미변경.
|
||||||
|
- 신규 색은 "editorial 섹션 액센트"로 제한 — 퍼플 그라디언트·Inter 교체·브로드시트 클리셰 금지.
|
||||||
|
|
||||||
|
## 개요
|
||||||
|
|
||||||
|
현재 목록 뷰의 문제는 (1) 탭이 generic amber pill 이고 카테고리 구분 없음, (2) 카드 뱃지가 전 카테고리 동일 amber 라 "모든 카드가 같은 색으로 소리침"(정보 0, 노이즈 1), (3) 빈상태가 단일·peer-forum 톤, (4) focus/aria/overflow 결손이다.
|
||||||
|
|
||||||
|
재설계 방향은 **"편집 섹션 인덱스"** 하나에 대담함을 집중한다. 탭 네비를 잡지 목차(masthead index)처럼 — 각 탭에 sort_order 기반 편집 서수(`01`~`05`) + 라벨 + 활성 시 카테고리 hue 의 **섹션 rule(밑줄)** 로 표현한다. 서수는 sort_order 라는 실제 편집 우선순위가 있으므로 정당하다(전체 탭은 서수 없음 = masthead 자체/브랜드 중립). 카드 뱃지는 "액세서리 하나 빼기" — 채워진 amber pill 배경을 제거하고 **hue dot + 저채도 대문자 라벨**로 축소해 카드 노이즈를 줄이면서 오히려 카테고리 정보를 추가한다. 모든 신규 스타일은 기존 패턴대로 posts-list.jsp 인라인 `<style>` 안에서 완결(자체 토큰 네임스페이스), bibimbap.css 는 건드리지 않는다.
|
||||||
|
|
||||||
|
## 플로우
|
||||||
|
|
||||||
|
진입점: `GET /posts` (또는 `?categoryId=N`, `?cursor...`).
|
||||||
|
컨트롤러(PostController#list, 미변경)가 model 에 `posts, categories(listActive 정렬됨), categoryId, hasNext, canWrite, nextCursor*` 를 제공.
|
||||||
|
|
||||||
|
렌더 분기(posts-list.jsp):
|
||||||
|
1. 헤더/hero(미변경) 렌더. canWrite 시 글쓰기 버튼.
|
||||||
|
2. **탭 인덱스 nav** 렌더:
|
||||||
|
- "전체" 탭: `categoryId == null ? is-active`, 서수 없음, 활성 시 잉크 라벨 + amber rule.
|
||||||
|
- `categories` 순회(=sort_order 순): 서수 `%02d`(index+1), 라벨(htmlEscape name), `data-cat=<catToken(slug)>`, `categoryId.equals(cat.id) ? is-active + aria-current="page"`.
|
||||||
|
3. `posts.isEmpty()` 분기:
|
||||||
|
- `categories.isEmpty()` → **빈상태 (a)** `posts-empty-nocats.jspf` include (운영자 미생성).
|
||||||
|
- else → **빈상태 (b)** `posts-empty-noposts.jspf` include (글 0). 직전에 `activeCategoryName` 을 categories 에서 categoryId 로 스캔·resolve.
|
||||||
|
4. else → `posts-grid` 렌더. 각 카드: 썸네일(옵션) + `data-cat=<catToken(post.slug)>` 뱃지(dot + 라벨) + 제목 + 저자.
|
||||||
|
5. `hasNext` → 더보기 링크(moreUrl 에 categoryId·cursor 유지, **문자열 조립 로직 미변경**).
|
||||||
|
|
||||||
|
종단: 정적 HTML. 신규 JS는 도입하지 않는다(스크롤/포커스/모션 전부 CSS-only). 진입 시 CSS 애니메이션이 탭 인덱스를 좌→우 staggered reveal.
|
||||||
|
|
||||||
|
## 데이터 모델 (해당 시)
|
||||||
|
|
||||||
|
DB/DTO 스키마 변경 없음. 기존 필드 재사용만:
|
||||||
|
- `PostCategoryData.getSlug()`, `getName()`, `getId()`, `getSortOrder()` — 탭.
|
||||||
|
- `PostData.getCategorySlug()`, `getCategoryName()`, `getCategoryId()` — 카드 뱃지.
|
||||||
|
|
||||||
|
slug 는 시각코딩 키로만 재사용하며 **CSS 에 원시 slug 를 주입하지 않는다**(§외부 계약·§7 XSS 규율).
|
||||||
|
|
||||||
|
## 외부 계약 (해당 시)
|
||||||
|
|
||||||
|
HTTP/RPC/메시지 인터페이스 변화 없음. URL·쿼리 파라미터·응답 계약 전부 동일. 순수 표현층(마크업 구조 + 인라인 CSS + 프래그먼트 분할)만 변경.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 토큰계획
|
||||||
|
|
||||||
|
**brief (1문단)**: 인디게임 제작자 플랫폼의 편집형 발행 보드. 세계관은 "제작 현장의 편집 데스크" — 따뜻한 종이(크림) 위에 골드 마스트헤드, 섹션마다 잉크/테라코타/세이지/더스티블루의 인쇄물 섹션 색. 대담함은 마스트헤드 인덱스(탭) 한 곳. AI-slop(퍼플 그라디언트·폰트 교체·브로드시트 세리프 클리셰)을 피하고, 기존 크림+amber 프로젝트 고유 언어를 유지하되 "크림+세리프+테라코타 기본형"에 안주하지 않고 편집 섹션 팔레트로 구체화한다.
|
||||||
|
|
||||||
|
**코어 색 토큰(기존 유지·재확인, light / dark)** — posts-list.jsp 인라인 네임스페이스(`--surface` 계열)에 이미 존재, 그대로 사용:
|
||||||
|
|
||||||
|
| 토큰 | 역할 | light | dark |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `--surface` | 페이지 종이 배경 | `#faf8f5` | `#121212` |
|
||||||
|
| `--card-bg` | 카드/탭 표면 | `#ffffff` | `#1e1e1e` |
|
||||||
|
| `--text` | 잉크 본문(라벨·서수 고대비) | `#1a1a1a` | `#ece8e1` |
|
||||||
|
| `--text-muted` | 보조/비활성 | `#5c5c5c` | `#a39e96` |
|
||||||
|
| `--accent` | 브랜드 골드(전체 탭 rule·호버) | `#e8a54b` | `#e8a54b` |
|
||||||
|
| `--border` | 보더 | `rgba(0,0,0,.08)` | `rgba(255,255,255,.1)` |
|
||||||
|
|
||||||
|
신규 구조 토큰 1개(섹션 rule 두께 상수는 CSS 리터럴로 충분하므로 색 토큰만 추가):
|
||||||
|
- 카테고리 hue 세트는 §2 에 정의(코어 6개와 분리해 "섹션 액센트 세트"로 관리). 코어 팔레트는 6개 유지 → brief 의 "4~6개" 충족.
|
||||||
|
|
||||||
|
**type 2역할** (폰트 교체 없이 weight/tracking/size 대비로):
|
||||||
|
- **Index/Display 역할**: 탭 서수 + hero eyebrow. `font-weight: 800`, `letter-spacing: .14em`, `font-variant-numeric: tabular-nums`, 소형(0.7rem), 대문자/서수. → 마스트헤드 "인쇄 번호" 느낌.
|
||||||
|
- **Label/Body 역할**: 카드 제목·탭 라벨·본문. `font-weight: 700`(라벨)~`900`(제목 강조), 표준 tracking. → 콘텐츠 가독.
|
||||||
|
시그니처 대비 = Index 역할의 넓은 tracking·tabular 서수 vs Label 역할의 조밀 tracking.
|
||||||
|
|
||||||
|
**layout**: 컨테이너 `max-width: 72rem`(유지). 그리드 3열→2열(≤900px)→1열(≤640px, 유지). 탭 = 단일 행 가로 스크롤 인덱스(§3). hero = space-between(유지).
|
||||||
|
|
||||||
|
**signature (1개)**: 편집 섹션 인덱스 탭 — 서수 + hue 섹션 rule + 로드 staggered reveal. 이 화면에서 기억에 남는 단 하나.
|
||||||
|
|
||||||
|
## 2. 카테고리 시각코딩 맵
|
||||||
|
|
||||||
|
원칙: **저채도 인쇄물 섹션 색**만 사용(무지개 회피). 전부 흙빛/탁색이라 크림+amber 와 harmonize. hue 는 **마커(dot)·섹션 rule·서수**에만 적용하고, 라벨 텍스트 자체는 `--text` 잉크 유지(대비 안전, concerns #3). "전체"는 색 없는 중립(브랜드 amber rule + 잉크 라벨) = 마스트헤드 자체.
|
||||||
|
|
||||||
|
| slug | 라벨 | 의미 | hue-light | hue-dark | 아이콘 처리 |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| `notice` | 공지 | 하우스 권위/공지 | `#B0741A` | `#E8A54B` | dot(원) — 골드, 브랜드 권위 |
|
||||||
|
| `devlog` | 개발일지 | 작업 노트/과정 | `#4C5A6E` | `#A9B8CC` | dot — 잉크 슬레이트, 연필 톤 |
|
||||||
|
| `showcase` | 쇼케이스 | 축하/스포트라이트 | `#BC5730` | `#E58A62` | dot — 테라코타 |
|
||||||
|
| `resources` | 자료·튜토리얼 | 차분한 레퍼런스 | `#5A7150` | `#93B187` | dot — 세이지/올리브 |
|
||||||
|
| `industry` | 업계 소식 | 외부 세계/뉴스 | `#3F5F8A` | `#8FA9CE` | dot — 더스티 블루 |
|
||||||
|
| (전체) | 전체 | 마스트헤드/브랜드 | `--accent` amber | `--accent` amber | 서수·dot 없음 |
|
||||||
|
|
||||||
|
- 아이콘은 **색 dot 하나로 통일**(카테고리별 이모지/일러스트 도입 안 함 — AI-slop·산만함 회피). dot = 6px 원, hue 채움.
|
||||||
|
- CSS 표현: 카테고리 hue 를 커스텀 프로퍼티로 정의하고 `[data-cat="slug"]` 속성 선택자로 dot/rule/서수 색을 물린다.
|
||||||
|
```
|
||||||
|
html { --cat-notice:#B0741A; --cat-devlog:#4C5A6E; --cat-showcase:#BC5730;
|
||||||
|
--cat-resources:#5A7150; --cat-industry:#3F5F8A; }
|
||||||
|
html[data-theme="dark"] { --cat-notice:#E8A54B; --cat-devlog:#A9B8CC; --cat-showcase:#E58A62;
|
||||||
|
--cat-resources:#93B187; --cat-industry:#8FA9CE; }
|
||||||
|
.posts-tab[data-cat="notice"].is-active { --hue: var(--cat-notice); }
|
||||||
|
...(5종)
|
||||||
|
```
|
||||||
|
`.is-active` 공통 규칙이 `var(--hue)` 로 rule/서수/라벨 강조를 그린다(라벨은 §3 참조: 활성 라벨은 hue 로 살짝 물들이되 최소 700 weight + 소형 아님이라 3:1 충족, 소형 서수도 UI 요소 3:1 기준).
|
||||||
|
- 대비 확보: light hue 는 크림(#faf8f5)에 대해 dot/rule(3:1 UI 기준) 충족. dark hue 는 #121212 대비 밝은 값으로 상향. notice light 만 소형 텍스트 4.5:1 미달이라 라벨 잉크 유지 규율 적용(concerns #3).
|
||||||
|
|
||||||
|
## 3. 탭 네비 재설계 스펙
|
||||||
|
|
||||||
|
**개념**: `<nav class="posts-tabs">` = 편집 섹션 인덱스(마스트헤드). 각 탭 = `[서수] 라벨` + 활성 하단 rule.
|
||||||
|
|
||||||
|
**마크업 구조 변경** (현재 :236~243):
|
||||||
|
```
|
||||||
|
<nav class="posts-tabs" aria-label="카테고리">
|
||||||
|
<a class="posts-tab <%= categoryId==null?"is-active":"" %>"
|
||||||
|
href="<%= ctx %>/posts"
|
||||||
|
<%= categoryId==null?"aria-current=\"page\"":"" %>>
|
||||||
|
<span class="posts-tab__label">전체</span>
|
||||||
|
</a>
|
||||||
|
<% int i=0; for (PostCategoryData cat : categories) { i++;
|
||||||
|
boolean on = categoryId!=null && categoryId.equals(cat.getId());
|
||||||
|
String tok = catToken(cat.getSlug()); %>
|
||||||
|
<a class="posts-tab <%= on?"is-active":"" %>"
|
||||||
|
<% if(!tok.isEmpty()){ %>data-cat="<%= tok %>"<% } %>
|
||||||
|
href="<%= ctx %>/posts?categoryId=<%= cat.getId() %>"
|
||||||
|
<%= on?"aria-current=\"page\"":"" %>>
|
||||||
|
<span class="posts-tab__ordinal" aria-hidden="true"><%= String.format("%02d", i) %></span>
|
||||||
|
<span class="posts-tab__label"><%= HtmlUtils.htmlEscape(cat.getName()==null?"":cat.getName()) %></span>
|
||||||
|
</a>
|
||||||
|
<% } %>
|
||||||
|
</nav>
|
||||||
|
```
|
||||||
|
- `catToken(slug)` = 화이트리스트 헬퍼(§7). data-cat 은 허용 5-slug 통과 값만 방출(XSS 안전).
|
||||||
|
- 서수 span 은 `aria-hidden="true"`(장식/편집 넘버링, 스크린리더 중복 방지). 라벨이 접근가능 이름.
|
||||||
|
- **anchor 유지**: `<nav>` + 링크. `role="tablist"` 등 ARIA 탭 위젯 시맨틱은 **도입하지 않음** — 실제로 페이지 전환 링크이므로 tab 위젯 대비 시맨틱 불일치를 야기함. `aria-current="page"` 로 현재 섹션 표시.
|
||||||
|
|
||||||
|
**시각 스펙**:
|
||||||
|
- inactive: 라벨 `--text-muted`, 서수 더 옅게(`--text-muted` + opacity .6), 배경 투명, 하단 rule 없음, 좌우 패딩 .1rem 세로 .5rem. (기존 pill 테두리/배경 제거 → 인덱스 텍스트만.)
|
||||||
|
- active(`.is-active`): 라벨 `var(--hue, var(--accent))` + weight 800, 서수 동 hue, **하단 2px rule**(`box-shadow: inset 0 -2px 0 var(--hue, var(--accent))` 또는 `border-bottom`) 노출. 전체 탭은 `--hue` 미정의 → `var(--accent)` 폴백(amber rule + 앰버 라벨). 라벨은 소형 아님(0.875rem, 700+) → 3:1 충족.
|
||||||
|
- hover(비활성): 라벨 `--text` 로 진해지고 rule 40% 프리뷰(`transition: .12s`).
|
||||||
|
- **focus-visible**: `.posts-tab:focus-visible { outline: 2px solid var(--hue, var(--accent)); outline-offset: 3px; border-radius: 6px; }` — 마우스 클릭엔 미표시, 키보드 탐색에만. bibimbap.css a:focus-visible 패턴과 정합(색만 hue 화).
|
||||||
|
|
||||||
|
**오버플로 처리 — 가로 스크롤 채택**:
|
||||||
|
- `.posts-tabs { display:flex; flex-wrap:nowrap; overflow-x:auto; scroll-snap-type:x proximity; gap:1.25rem; -webkit-overflow-scrolling:touch; scrollbar-width:none; }` + `.posts-tabs::-webkit-scrollbar{display:none}`.
|
||||||
|
- 각 탭 `scroll-snap-align:start; white-space:nowrap;`.
|
||||||
|
- 우측 스크롤 어포던스: 컨테이너에 우측 edge fade 마스크(`mask-image: linear-gradient(90deg,#000 85%,transparent)` 는 스크롤 끝 감지 없이 항상 적용되면 마지막 탭이 흐려짐 → 대신 `.posts-tabs` 부모 wrapper 에 `::after` 그라디언트 오버레이를 두되 `posts-tabs` 가 스크롤되면 자연 노출). 구현 단순화를 위해 wrapper `<div class="posts-tabs-scroll">` 로 감싸고 `::after` 로 우측 페이드.
|
||||||
|
- **wrap 대비 근거**: 마스트헤드/편집 인덱스 metaphor 는 "한 줄 목차"가 signature 의 핵심. wrap 시 2줄로 깨지면 섹션 rule 정렬이 흐트러지고 인덱스 질서가 사라진다. 가로 스크롤은 카테고리 N 증가에도 단일 행 signature 를 보존하고 모바일에서 스와이프 인덱스로 자연스럽다. 6개(전체+5)는 대부분 뷰포트에서 스크롤 미발생 → 데스크톱 손실 0, 모바일/확장 대비 이득.
|
||||||
|
|
||||||
|
**signature 모션(고임팩트 1곳)**: 로드 시 탭 인덱스 좌→우 staggered reveal.
|
||||||
|
```
|
||||||
|
@keyframes tabIn { from { opacity:0; transform: translateY(6px); } to { opacity:1; transform:none; } }
|
||||||
|
.posts-tab { animation: tabIn .38s ease both; }
|
||||||
|
.posts-tab:nth-child(1){animation-delay:.00s} ... :nth-child(6){animation-delay:.20s} /* +.04s 씩 */
|
||||||
|
@media (prefers-reduced-motion: reduce){ .posts-tab{ animation:none; } }
|
||||||
|
```
|
||||||
|
카드는 경쟁 방지 위해 모션 최소(집합 fade 없음 또는 0.2s 단일 fade). 고임팩트는 탭 한 곳으로 집중.
|
||||||
|
|
||||||
|
## 4. 카드 뱃지 재설계
|
||||||
|
|
||||||
|
**"액세서리 하나 빼기" = 채워진 amber pill 배경 제거**. 현재 `.post-card__category`(:154~165)는 `background: var(--accent-soft); color: var(--accent);` 채움 pill — 전 카드 동일색이라 정보 0·노이즈 1.
|
||||||
|
|
||||||
|
- **빼는 것**: 채워진 배경 pill(`background`, pill 형태, `min-height`/`border-radius`).
|
||||||
|
- **남기는 것**: 카테고리 라벨 텍스트(htmlEscape name).
|
||||||
|
- **더하는 것(순증 아님, pill 제거로 상쇄되는 미니멀 마커)**: 6px hue dot(카테고리 구분) + 대문자·tracking 라벨.
|
||||||
|
- 결과 마크업(:254~257):
|
||||||
|
```
|
||||||
|
<% String pTok = catToken(post.getCategorySlug());
|
||||||
|
if (post.getCategoryName()!=null && !post.getCategoryName().isBlank()) { %>
|
||||||
|
<span class="post-card__category" <% if(!pTok.isEmpty()){ %>data-cat="<%= pTok %>"<% } %>>
|
||||||
|
<span class="post-card__dot" aria-hidden="true"></span>
|
||||||
|
<%= HtmlUtils.htmlEscape(post.getCategoryName()) %>
|
||||||
|
</span>
|
||||||
|
<% } %>
|
||||||
|
```
|
||||||
|
- CSS:
|
||||||
|
```
|
||||||
|
.post-card__category { display:inline-flex; align-items:center; gap:.4rem; align-self:flex-start;
|
||||||
|
background:none; padding:0; color:var(--text-muted);
|
||||||
|
font-size:.68rem; font-weight:800; letter-spacing:.06em; text-transform:uppercase; }
|
||||||
|
.post-card__dot { width:6px; height:6px; border-radius:50%;
|
||||||
|
background: var(--cat-notice); /* 기본 폴백 amber 로 대체 */ }
|
||||||
|
.post-card__category[data-cat="notice"] .post-card__dot{ background:var(--cat-notice); }
|
||||||
|
... (5종). data-cat 없으면 dot 을 var(--accent) 폴백.
|
||||||
|
```
|
||||||
|
- 라벨 텍스트는 `--text-muted`(잉크 계열) 유지 → 대비 안전(concerns #3: hue 를 저대비 텍스트에 쓰지 않음). 색 구분은 dot 이 담당.
|
||||||
|
- 효과: 모든 카드가 앰버로 소리치던 노이즈 제거 → 조용한 카드 + dot 색으로 카테고리 식별. 카드 hover(:136~140)는 유지.
|
||||||
|
|
||||||
|
## 5. 빈상태 2종
|
||||||
|
|
||||||
|
posts-empty.jspf(단일) 를 **2개 프래그먼트로 split**. posts-list.jsp 의 `posts.isEmpty()` 분기에서 `categories.isEmpty()` 로 선택.
|
||||||
|
|
||||||
|
**split 방식/조건**:
|
||||||
|
```
|
||||||
|
<% if (posts.isEmpty()) {
|
||||||
|
if (categories.isEmpty()) { %>
|
||||||
|
<%@ include file="fragments/posts-empty-nocats.jspf" %>
|
||||||
|
<% } else {
|
||||||
|
String activeCategoryName = null;
|
||||||
|
if (categoryId != null) {
|
||||||
|
for (PostCategoryData c : categories) {
|
||||||
|
if (categoryId.equals(c.getId())) { activeCategoryName = c.getName(); break; }
|
||||||
|
}
|
||||||
|
} %>
|
||||||
|
<%@ include file="fragments/posts-empty-noposts.jspf" %>
|
||||||
|
<% }
|
||||||
|
} else { ... 그리드 ... } %>
|
||||||
|
```
|
||||||
|
- `activeCategoryName` 은 posts-list.jsp 페이지 스코프 변수 → include 되는 jspf 에서 그대로 참조 가능. htmlEscape 로 출력.
|
||||||
|
- 두 프래그먼트 모두 bibimbap.css `.empty-state / .btn.btn-primary / .empty-actions` 재사용(신규 CSS 0).
|
||||||
|
|
||||||
|
**(a) posts-empty-nocats.jspf — 카테고리 0개(운영자 미생성)**
|
||||||
|
- 사유: 아직 발행 섹션이 준비되지 않음.
|
||||||
|
- 인터페이스 목소리(편집 톤):
|
||||||
|
- `canWrite`(운영/스태프): h3 "발행 섹션을 먼저 만들어 주세요" / p "카테고리를 등록하면 이곳에 편집 섹션이 열립니다." / CTA `.btn.btn-primary` → 카테고리 관리 경로. (경로: PostAdminController 관할. 정확 URL 은 구현 시 admin 라우트 확인 — 존재 안 하면 CTA 생략하고 안내문만.)
|
||||||
|
- 소비자: h3 "곧 새 소식으로 찾아뵙겠습니다" / p "편집팀이 첫 섹션을 준비하고 있어요." / CTA 없음.
|
||||||
|
- SVG: 기존 크림+골드 문서 아이콘 재사용(프로젝트 언어 유지).
|
||||||
|
|
||||||
|
**(b) posts-empty-noposts.jspf — 글 0개(전체 또는 특정 카테고리)**
|
||||||
|
- 사유: 섹션은 있으나 아직 발행된 편이 없음.
|
||||||
|
- 목소리:
|
||||||
|
- `activeCategoryName != null`(특정 카테고리): h3 `"'{카테고리명}' 섹션의 첫 편을 준비 중입니다"` (htmlEscape) / p 는 아래 권한 분기.
|
||||||
|
- `activeCategoryName == null`(전체, categoryId null): h3 "아직 발행된 글이 없습니다".
|
||||||
|
- `canWrite`: p "이 섹션의 첫 편을 발행해 보세요." / CTA `.btn.btn-primary` "+ 새 글 발행" → `${ctx}/posts/new`.
|
||||||
|
- 소비자: p "편집팀의 첫 발행을 기다려 주세요." / CTA 없음.
|
||||||
|
- **톤 교정**: 기존 "커뮤니티의 첫 글을 남겨보세요"(peer-forum) → "발행"(편집 일방) 동사로 교체. 소비자에겐 write CTA 노출 안 함(모델 정합).
|
||||||
|
|
||||||
|
## 6. 반응형
|
||||||
|
|
||||||
|
기존 900/640 브레이크포인트 정합 유지 + 탭/뱃지 추가 규칙.
|
||||||
|
|
||||||
|
- **그리드**: 3열 → `≤900px` 2열 → `≤640px` 1열 (기존 :203~219 유지, 미변경).
|
||||||
|
- **탭 인덱스**: 모든 폭에서 단일 행 가로 스크롤(§3). 데스크톱 6탭 = 스크롤 없음. 모바일(≤640) 은 스와이프 스크롤 + 우측 edge fade 로 추가 카테고리 암시. 서수/라벨 크기 고정(모바일에서 축소 불필요, 이미 소형). `gap` 을 ≤640 에서 1rem 로 소폭 축소(탭 밀도).
|
||||||
|
- **hero**: `≤640px` 세로 스택 + 글쓰기 full-width(기존 :208~216 유지).
|
||||||
|
- **뱃지 dot**: 크기 폭 무관 6px 고정. 라벨 uppercase tracking 유지.
|
||||||
|
- **모션**: 가로 스크롤/스냅은 모바일 터치 친화. `prefers-reduced-motion: reduce` 는 로드 애니메이션만 무효화(스크롤/스냅은 사용자 제스처라 유지).
|
||||||
|
|
||||||
|
## 7. 정확한 변경 맵
|
||||||
|
|
||||||
|
**bibimbap.css 수정 여부: 안 함(0 라인).**
|
||||||
|
근거: (1) 기존 패턴 — posts-list.jsp 는 자체 인라인 `<style>` + 독립 토큰 네임스페이스(`--surface` 계열)로 완결되어 있고, bibimbap.css 는 전역 공유(홈/게임/약관/프로필 등)라 카테고리 hue 를 넣으면 전역 오염. (2) 빈상태 프래그먼트가 쓰는 `.empty-state/.btn/.empty-actions` 는 이미 bibimbap.css 에 존재 → 재사용만. (3) 신규 CSS 파일도 만들지 않음(인라인 완결이 기존 관례, HTTP 요청 추가 회피). 신규 파일이 불가피했다면 `src/main/webapp/css/` 에 두고 resources/static 금지였겠으나 불필요.
|
||||||
|
|
||||||
|
**posts-list.jsp `<style>` (30~221) 변경 셀렉터/블록**:
|
||||||
|
| 위치 | 현재 | 변경 |
|
||||||
|
|---|---|---|
|
||||||
|
| `html {}` (31~41) | 코어 토큰 | 뒤에 `--cat-*` 5개(light) 추가 |
|
||||||
|
| `html[data-theme="dark"]{}` (42~50) | 다크 토큰 | 뒤에 `--cat-*` 5개(dark) 추가 |
|
||||||
|
| `.posts-tabs` (96~101) | flex-wrap:wrap pill 컨테이너 | flex-nowrap 가로 스크롤 인덱스 + 스냅 + 스크롤바 숨김. wrapper `.posts-tabs-scroll::after` edge fade 추가 |
|
||||||
|
| `.posts-tab` (102~114) | pill(border/bg/radius) | 인덱스 항목(투명, 서수+라벨), `:focus-visible`, load `tabIn` 애니메이션 + `:nth-child` delay |
|
||||||
|
| `.posts-tab.is-active` (115~119) | amber pill fill | hue 라벨/서수 + 하단 2px rule, `[data-cat="x"].is-active{--hue:...}` 5종 |
|
||||||
|
| (신규) | — | `.posts-tab__ordinal`, `.posts-tab__label` |
|
||||||
|
| `.post-card__category` (154~165) | amber 채움 pill | 배경 제거, dot+대문자 라벨, `[data-cat]` dot 색 5종, `.post-card__dot` |
|
||||||
|
| (신규) | — | `@media (prefers-reduced-motion: reduce)` 블록 |
|
||||||
|
| `.posts-grid/.post-card*/.posts-more/.posts-empty/@media 900·640` | — | **미변경**(그리드/카드/더보기/브레이크포인트 보존) |
|
||||||
|
|
||||||
|
**posts-list.jsp 마크업/스크립틀릿 변경 라인**:
|
||||||
|
| 위치 | 변경 |
|
||||||
|
|---|---|
|
||||||
|
| 상단 스크립틀릿(9~22) | private 헬퍼 `catToken(String slug)` 추가(화이트리스트). `<%!` 선언 블록 |
|
||||||
|
| nav (236~243) | 탭 구조 재작성(서수 span + 라벨 span + data-cat + aria-current). §3 마크업 |
|
||||||
|
| 빈상태 include (245~246) | 단일 include → `categories.isEmpty()` 2분기 include + `activeCategoryName` resolve. §5 |
|
||||||
|
| 카드 뱃지 (254~257) | data-cat + dot + 라벨 구조. §4 마크업 |
|
||||||
|
| 더보기 (265~274) | **미변경**(moreUrl·cursor·categoryId 로직 보존) |
|
||||||
|
|
||||||
|
**catToken 헬퍼(시그니처 — 최소 인자)**:
|
||||||
|
```
|
||||||
|
<%!
|
||||||
|
// slug: 서버가 준 카테고리 slug. 화이트리스트 통과 시 data-cat 값으로 반환, 미허용/ null 시 "" 반환(CSS 미주입).
|
||||||
|
private String catToken(String slug) {
|
||||||
|
if (slug == null) return "";
|
||||||
|
switch (slug) {
|
||||||
|
case "notice": case "devlog": case "showcase": case "resources": case "industry":
|
||||||
|
return slug;
|
||||||
|
default: return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
%>
|
||||||
|
```
|
||||||
|
인자 1개(slug), 반환값 즉시 사용 — inflate 없음. XSS: 반환은 5개 고정 리터럴 중 하나 또는 ""; 임의 slug 가 CSS/속성에 들어가지 않음.
|
||||||
|
|
||||||
|
**프래그먼트 split**:
|
||||||
|
- `fragments/posts-empty.jspf` → 삭제(또는 미사용화). 대체:
|
||||||
|
- 신규 `fragments/posts-empty-nocats.jspf` (a)
|
||||||
|
- 신규 `fragments/posts-empty-noposts.jspf` (b)
|
||||||
|
- 두 파일 모두 `canWrite` 페이지 변수 + (b는 `activeCategoryName`) 참조, bibimbap.css 클래스 재사용, htmlEscape 출력.
|
||||||
|
|
||||||
|
## 8. brief 대비 자가비평
|
||||||
|
|
||||||
|
- **AI-slop 체크**: 통과. 퍼플 그라디언트/Inter·Space Grotesk 교체/브로드시트 세리프 없음. 크림+amber 고유 언어 유지. 신규 색은 저채도 인쇄물 섹션 팔레트(잉크/테라코타/세이지/더스티블루)로 context-specific — generic 아님. 폰트 교체 없이 서수·tracking 대비로 signature 확보(제약 정합).
|
||||||
|
- **signature 가 대담함을 정당화하나**: 예. 대담함을 탭 인덱스 한 곳에 집중(masthead + 편집 서수 + hue rule + 로드 stagger). 서수는 sort_order 라는 실제 편집 우선순위가 근거(임의 넘버링 아님). 나머지(카드/뱃지/빈상태)는 절제 — 뱃지는 오히려 pill 을 빼서 조용하게. "기억에 남는 하나" 원칙 충족.
|
||||||
|
- **기존 언어와 일관되나**: 예. 인라인 토큰 네임스페이스 유지, amber accent 보존, bibimbap.css 미변경, 빈상태 SVG 재사용. 단 폰트가 사이트(Pretendard) vs posts(system-ui) 불일치는 **기존 현상태**이며 제약상 유지(concerns #1) — 신규 도입 불일치 아님.
|
||||||
|
- **접근성(대비·focus·aria)**: focus-visible 링 추가(결손 해소), aria-current="page" 추가(결손 해소), 서수 aria-hidden(중복 방지), 탭 라벨이 접근가능 이름. 대비: hue 를 저대비 소형 텍스트에 쓰지 않고 dot/rule/UI 요소(3:1)에 국한, 라벨은 잉크 유지 → WCAG 준수. **미흡점**: (1) 가로 스크롤 어포던스가 저시력 사용자에게 약함(concerns #2, edge fade 로 완화). (2) notice light hue 소형 4.5:1 미달을 라벨 잉크 유지로 우회 — 완전 해소가 아니라 회피이므로 hue 를 라벨에 직접 쓰는 구현 실수를 검증 AC 로 차단(§검증). 그 외 결정적 미흡 없음.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 검증 포인트 (verification-advisor 점검용)
|
||||||
|
|
||||||
|
정식화 self-audit 반영: 카테고리 집합은 제품 확정 5개로 **시간 안정**(설계~검증 사이 불변). 표현 견고성은 리터럴 단일 grep 대신 열거·정규식·두 집합 동수 불변식 사용.
|
||||||
|
|
||||||
|
- **AC-1 (카테고리 hue 집합 전수 5 — 불변식)**: posts-list.jsp 의 `--cat-*` 고유 토큰명 수 == 5.
|
||||||
|
`grep -oE '\-\-cat-[a-z]+' src/main/webapp/WEB-INF/views/posts-list.jsp | sort -u | wc -l` == 5.
|
||||||
|
(light/dark 중복 방출돼도 sort -u 로 고유명 5 검증 → 추가·삭제 동시 커버.)
|
||||||
|
- **AC-2 (화이트리스트 ↔ hue 두 집합 동수 불변식)**: `catToken` switch 의 case 라벨 수 == AC-1 고유 hue 수 == 5.
|
||||||
|
`grep -c 'case "' `(catToken 블록 범위 내) == 5. 발산 시 신규/삭제 카테고리 누락 검출.
|
||||||
|
- **AC-3 (탭 서수 편집순 정합)**: 탭 서수는 `listActive()` 반복 순(=sort_order ASC)으로 `%02d` 방출 — 수동: 렌더 HTML 에서 서수 01,02,... 가 카테고리 sort_order 순과 일치. (grep 불가, 육안/DOM 판정.)
|
||||||
|
- **AC-4 (focus-visible 존재)**: `grep -c ':focus-visible' posts-list.jsp` >= 1 (`.posts-tab:focus-visible`).
|
||||||
|
- **AC-5 (aria-current 존재)**: `grep -c 'aria-current="page"' posts-list.jsp` >= 1, 활성 탭에만 조건부 방출(수동: 비활성 탭에 미부착).
|
||||||
|
- **AC-6 (reduced-motion 존중)**: `grep -c 'prefers-reduced-motion' posts-list.jsp` >= 1, 해당 블록이 `.posts-tab` 로드 애니메이션 무효화.
|
||||||
|
- **AC-7 (빈상태 2 프래그먼트 존재)**: `fragments/posts-empty-nocats.jspf` 와 `fragments/posts-empty-noposts.jspf` 둘 다 존재(ls/glob 매치 2건). posts-list.jsp 에 두 include 모두 참조(`grep -c 'posts-empty-.*\.jspf' posts-list.jsp` == 2).
|
||||||
|
- **AC-8 (편집 톤 교정 — 앵커/의미 판정)**: (b) 프래그먼트에 "발행" 동사 존재, 소비자 분기에 write CTA(`/posts/new` 링크) 부재. 리터럴 단일 grep 대신 의미 판정: peer-forum 문구("첫 글을 남겨") 잔존 0 + 소비자(!canWrite) 경로에 `.btn-primary` href `/posts/new` 없음(수동 코드 리뷰).
|
||||||
|
- **AC-9 (뱃지 pill 제거 — 노이즈 감소)**: `.post-card__category` 규칙에 `background: none`(또는 배경 미지정) + `.post-card__dot` 존재. 채워진 `--accent-soft` pill 배경 잔존 0(수동/grep `background: var(--accent-soft)` 가 `.post-card__category` 블록에 없음).
|
||||||
|
- **AC-10 (기능 로직 불변 — 회귀 가드)**: keyset 더보기 URL 조립(cursorCreatedAt/cursorId/categoryId) 라인 미변경. `git diff` 에서 265~274 로직 동일. 컨트롤러/매퍼/PostData·PostCategoryData `git diff` 공백.
|
||||||
|
- **AC-11 (bibimbap.css 미변경 불변식)**: `git diff --stat src/main/webapp/css/bibimbap.css` 공백(0 라인). 신규 CSS 파일 `src/main/webapp/css/` 외 자원(resources/static) 추가 0.
|
||||||
|
- **AC-12 (XSS — slug 원시 미주입)**: data-cat 방출은 `catToken` 반환값만 사용(원시 `getSlug()`/`getCategorySlug()` 직접 속성 방출 0). `grep` 로 `data-cat="<%= .*getSlug` 또는 `getCategorySlug` 직접 방출 부재 확인. 카테고리명 출력은 전부 `HtmlUtils.htmlEscape` 경유(육안: name/activeCategoryName 출력 지점 escape).
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
---
|
||||||
|
phase: verification
|
||||||
|
agent: verification-advisor
|
||||||
|
agent_version: 1
|
||||||
|
generated_at: 2026-07-24T07:51:09Z
|
||||||
|
concerns:
|
||||||
|
- "L1 BUILD FAILURE — 원인은 src/test/java/.../PostControllerTest.java 와 PostController.list 시그니처 불일치(test-compile 단계). 이번 세션 git diff/status 에 src/main/java, src/test/java 파일 변경 0건(둘 다 HEAD 상태, PostController.java 최종 수정 커밋 02a1b75) — 이번 posts-categories 작업(JSP+SQL seed only) 이전에 이미 존재하던 베이스라인 컴파일 브레이크로 판단됨. 즉 회귀 0 기대값 전제(Java 소스 0파일 변경) 자체는 grep으로 확인되었으나, 저장소가 이미 RED 상태였음."
|
||||||
|
- "L2 skipped: 이 세션 전략 레지스트리에 posts-list.jsp 도메인 전용 live-contract 전략(worker 포함 통합 verify 스크립트)이 등록돼 있지 않음 — L1(mvn test)과 grep 기반 정적 AC만 실행 가능. seed SQL(db/migrations/20260724-post-categories-seed.sql) 미적용 여부 확인은 DB 연결 계약 전략 부재로 L1 판정 범위 밖."
|
||||||
|
- "AC 모호: 없음 — 정적 AC 9개 전부 grep 명령이 명시돼 실행 가능했음."
|
||||||
|
concerns_checked: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# 검증 결과
|
||||||
|
|
||||||
|
## Acceptance Criteria (입력 받은 그대로 인용)
|
||||||
|
|
||||||
|
### L1 (필수 — 회귀 0)
|
||||||
|
`./mvnw -o test` 실행. `-o`(offline) 가 의존성 미캐시로 실패하면 `./mvnw test` 재시도.
|
||||||
|
판정: BUILD SUCCESS, `Tests run: N, Failures: 0, Errors: 0`. 실패 시 실패 테스트명·메시지 원문 인용.
|
||||||
|
컨텍스트: 이번 변경은 JSP(표현층) + SQL seed 마이그레이션(미적용 DML)만이며 Java 소스 0파일 변경이라 회귀 0 이 기대값. GREEN 아니면 원인 규명.
|
||||||
|
주의: JSP 런타임 컴파일 에러는 mvn test 로 탐지 안 됨 → needs_user_verification.
|
||||||
|
|
||||||
|
### 정적 AC (grep, posts-list.jsp)
|
||||||
|
AC-1(`--cat-*` 5개 고유) / AC-2(catToken switch case 5개 고유) / AC-4(`:focus-visible` >=1) / AC-5(`aria-current` >=1) / AC-6(`prefers-reduced-motion` >=1) / AC-7(신구 fragment 존재/부재 + include 2회) / AC-9(`.post-card__category` background 없음) / AC-11(bibimbap.css diff 0, static/ 신규 css 없음) / AC-12(data-cat 은 catToken 반환값만, 카테고리명은 htmlEscape 경유)
|
||||||
|
|
||||||
|
### 코드리뷰 AC (의미 판정)
|
||||||
|
AC-8(빈상태 문구 원칙: (b) "발행" 동사 + 소비자경로 CTA 부재 + peer-forum 문구 잔존 0, (a) 동일 원칙) / AC-10(더보기 URL 조립 라인 미변경 + src/main/java 무변경)
|
||||||
|
|
||||||
|
### 런타임 AC (grep 불가 — needs_user_verification)
|
||||||
|
AC-3(탭 서수 sort_order 일치) / JSP 런타임 컴파일 성공 / 시각 확인(탭 다수·필터·빈상태 2종·모바일·포커스링)
|
||||||
|
|
||||||
|
## 실행된 전략
|
||||||
|
|
||||||
|
레지스트리(`docs/development/verification-strategies.md`)에 이 프로젝트 전용 통합 `verify-all` 스크립트가 등록돼 있지 않아(§39 "권장" 단계, 미도입), L1(`./mvnw -o test`)과 AC 명시 grep 명령을 개별 실행했다.
|
||||||
|
|
||||||
|
| id | cmd | exit | severity | 결과 |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| L1-test | `./mvnw -o test` (JAVA_HOME=homebrew openjdk@21 수동 설정 후 실행 — 기본 PATH 에 java 없어 환경 보정) | 1 | blocker | **FAIL** — test-compile 단계 COMPILATION ERROR |
|
||||||
|
| static-grep (AC-1,2,4,5,6,7,9,11,12) | 각 AC 원문 `grep`/`ls`/`git diff --stat` 명령 | 0 (전부) | blocker(집합전수 AC) | pass 9/9 |
|
||||||
|
| codereview (AC-8,10) | `cat` fragments + `git diff` 대조 | - | blocker | pass 2/2 |
|
||||||
|
|
||||||
|
| 단계 | 결과 |
|
||||||
|
|---|---|
|
||||||
|
| L1 typecheck+compile (test-compile) | **FAIL** — 3 errors |
|
||||||
|
| L1 unit+regression | 미실행 (test-compile 실패로 테스트 자체가 0건 수행됨, `Tests run:` 라인 없음) |
|
||||||
|
| L2 contract | skip (전략 미등록/미구축 — 레지스트리 L37 "L2 harness 미구축" open item과 일치) |
|
||||||
|
| 로그 스캔 | n/a (verify-all 스크립트 부재로 통합 로그 스캔 단계 없음) |
|
||||||
|
|
||||||
|
## 실패 상세
|
||||||
|
|
||||||
|
### L1 (test-compile)
|
||||||
|
|
||||||
|
- 실패 지점:
|
||||||
|
`src/test/java/com/pandoli365/bibimbap/controller/PostControllerTest.java:[208,33]`
|
||||||
|
`src/test/java/com/pandoli365/bibimbap/controller/PostControllerTest.java:[227,19]`
|
||||||
|
`src/test/java/com/pandoli365/bibimbap/controller/PostControllerTest.java:[245,33]`
|
||||||
|
|
||||||
|
메시지 원문:
|
||||||
|
```
|
||||||
|
[ERROR] method list in class com.pandoli365.bibimbap.controller.PostController cannot be applied to given types;
|
||||||
|
required: java.lang.Long,java.lang.String,java.lang.Long,jakarta.servlet.http.HttpSession,org.springframework.ui.Model
|
||||||
|
found: <nulltype>,<nulltype>,<nulltype>,org.springframework.ui.Model
|
||||||
|
reason: actual and formal argument lists differ in length
|
||||||
|
[INFO] 3 errors
|
||||||
|
[INFO] BUILD FAILURE
|
||||||
|
```
|
||||||
|
|
||||||
|
- 재현: `./mvnw -o test` (또는 `./mvnw -o -Dtest=PostControllerTest test-compile`)
|
||||||
|
|
||||||
|
- 범위 확인 근거(구현 코드 내용은 열람하지 않고 git 메타데이터로만 확인):
|
||||||
|
- `git status --short -- 'src/main/java/**' 'src/test/java/**'` → 출력 없음 (이번 세션 미변경, 둘 다 HEAD 상태)
|
||||||
|
- `git diff --stat -- src/main/java/com/pandoli365/bibimbap/controller/PostController.java src/test/java/com/pandoli365/bibimbap/controller/PostControllerTest.java` → 출력 없음
|
||||||
|
- `git log --oneline -3 -- .../PostController.java` 최상단 = `02a1b75`(feat/v2 현재 tip, 이번 posts-categories 작업 직전 커밋)
|
||||||
|
- → 이 컴파일 브레이크는 이번 세션의 JSP/SQL 변경으로 유발된 것이 아니라 **이미 커밋된 베이스라인 상태**다.
|
||||||
|
|
||||||
|
## 종합 판정
|
||||||
|
|
||||||
|
overall: **fail** (L1 blocker FAIL — BUILD FAILURE, 테스트 0건 수행)
|
||||||
|
rollback_signal: **none** (이번 세션은 Java 소스를 전혀 변경하지 않았고 파괴적 조작도 없었음. 실패 원인은 세션 진입 이전부터 존재하던 베이스라인 test-compile 브레이크로, 이번 diff 되돌리기와 무관. orchestrator 판단: 이번 posts-categories 변경분에 대한 rollback 필요성 없음 — 단, 저장소 자체가 `./mvnw test` RED 상태임은 별도 이슈로 인지 필요)
|
||||||
|
|
||||||
|
## Acceptance 매칭
|
||||||
|
|
||||||
|
| criterion | 매칭 전략 | 판정 |
|
||||||
|
|---|---|---|
|
||||||
|
| L1 (BUILD SUCCESS + 회귀0) | `./mvnw -o test` | **FAIL** — BUILD FAILURE(test-compile), 베이스라인 기존 브레이크(세션 diff 무관, 근거 상단) |
|
||||||
|
| AC-1 (`--cat-*` 5개 고유) | `grep -oE -- '--cat-[a-z]+' posts-list.jsp \| sort -u \| wc -l` | pass (5: devlog/industry/notice/resources/showcase) |
|
||||||
|
| AC-2 (catToken switch 5개 고유 case) | `grep -n 'case "' posts-list.jsp` | pass (`case "notice": case "devlog": case "showcase": case "resources": case "industry":`, 1줄에 5개) |
|
||||||
|
| AC-4 (`:focus-visible` >=1) | `grep -c` | pass (1) |
|
||||||
|
| AC-5 (`aria-current` >=1) | `grep -c` | pass (2) |
|
||||||
|
| AC-6 (`prefers-reduced-motion` >=1) | `grep -c` | pass (1) |
|
||||||
|
| AC-7 (fragment 신구 존재/부재 + include 2회) | `ls` + `grep -c` + git status | pass — `posts-empty-nocats.jspf`/`posts-empty-noposts.jspf` 존재, 구 `posts-empty.jspf` 는 git status `D`(삭제 스테이지 전, 워킹트리에도 부재 확인), include 참조 정확히 2건(라인 386, 395) |
|
||||||
|
| AC-9 (`.post-card__category` background 없음) | grep + 블록 육안 대조(260~271행) | pass — `background: none`, `var(--accent-soft)` 미검출 |
|
||||||
|
| AC-11 (bibimbap.css 무변경, static/ 신규 css 없음) | `git diff --stat` + `git status` | pass — 둘 다 빈 출력(static/ 디렉터리 자체가 미존재) |
|
||||||
|
| AC-12 (data-cat = catToken 반환값만, 카테고리명 htmlEscape) | grep `data-cat`/`getSlug()`/`getCategorySlug()`/`htmlEscape` | pass — `data-cat` 은 `catTok`/`pCatTok`(catToken() 반환) 만 사용, `getSlug()`/`getCategorySlug()` 는 catToken() 인자로만 등장(라인 373, 405), 카테고리명 출력 2곳(라인 378, 409) 전부 `HtmlUtils.htmlEscape` 경유, `activeCategoryName` 도 fragment 내 `htmlEscape` 경유 |
|
||||||
|
| AC-8 (빈상태 문구 원칙) | fragments 전문 대조 | pass — (b) canWrite="발행"동사+CTA(`/posts/new`), !canWrite="편집팀의 첫 발행을 기다려 주세요"(CTA 없음); (a) canWrite=CTA(관리 링크), !canWrite=텍스트만(CTA 없음); "첫 글을 남겨" 잔존 0건 |
|
||||||
|
| AC-10 (더보기 URL 조립 미변경 + java 무변경) | `git diff` 라인 대조 + `git diff --stat src/main/java/` | pass — moreUrl/cursorCreatedAt/cursorId/categoryId 조립 블록(라인 420~424) diff에 미출현, src/main/java 전체 diff 빈 출력 |
|
||||||
|
| AC-3 (탭 서수-sort_order 일치) | — | needs_user_verification (grep 불가, listActive() 정렬 실화면 대조 필요) |
|
||||||
|
| JSP 런타임 컴파일 성공 | — | needs_user_verification (Tomcat 런타임 컴파일 — mvn test 로 미탐지, 앱 기동+curl 필요) |
|
||||||
|
| 시각(탭 다수/필터/빈상태2종/모바일/포커스링) | — | needs_user_verification (브라우저 육안 확인 필요) |
|
||||||
|
|
||||||
|
## 요약
|
||||||
|
|
||||||
|
- **L1**: FAIL — `PostControllerTest.java` ↔ `PostController.list` 시그니처 불일치로 test-compile 단계에서 BUILD FAILURE, 3 errors, 테스트 0건 실행(`Tests run:` 라인 자체가 없음). 세션 diff 는 Java 파일을 전혀 건드리지 않았으므로(git status/log 로 확인) 이는 이번 posts-categories 변경이 유발한 회귀가 아니라 **베이스라인에 이미 존재하던 컴파일 브레이크**로 보임. 다만 AC 요구사항인 "BUILD SUCCESS" 는 충족되지 않았으므로 L1 항목 자체는 FAIL 로 판정.
|
||||||
|
- **정적 AC 집계**: 9/9 pass (AC-1, 2, 4, 5, 6, 7, 9, 11, 12)
|
||||||
|
- **코드리뷰 AC 집계**: 2/2 pass (AC-8, 10)
|
||||||
|
- **needs_user_verification**: AC-3(탭 서수 정렬), JSP 런타임 컴파일(앱 기동 스모크), 시각 확인 4항목(탭 다수 렌더/탭 클릭 필터/빈상태 2종/모바일 폭/키보드 포커스 링) — 이 프로젝트 특성상(JSP 런타임 컴파일 + Tomcat) grep/mvn test 로 커버 불가, 별도 런타임 스모크·브라우저 확인 의무.
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
---
|
||||||
|
phase: documentation
|
||||||
|
agent: documentation-advisor
|
||||||
|
agent_version: 1
|
||||||
|
generated_at: 2026-07-24T10:40:00+09:00
|
||||||
|
concerns: []
|
||||||
|
concerns_checked: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# 문서화 보고
|
||||||
|
|
||||||
|
## 작성/수정된 문서
|
||||||
|
| 경로 | 카테고리 | 유형 | 링크 추가한 index | 교차 링크 |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `docs/changes/2026-07-24-post-categories-seed-list-redesign.md` | changes | 신규 | `docs/changes/index.md` 에 한 줄 추가 | 관련 문서 4개(W3-3 원설계, 2026-06-30 visual-redesign, frontend-redesign-coverage-checklist, document-category-classification) |
|
||||||
|
| `docs/changes/index.md` | changes | 기존 갱신 | — (index 자신) | — |
|
||||||
|
| `docs/development/frontend-redesign-coverage-checklist.md` | development | 기존 갱신(스코프 정합 확인 후 append) | 해당 없음(신규 문서 아님) | `posts-list.jsp` 행 근거 열에 새 문서로의 링크 추가 |
|
||||||
|
| `docs/changes/2026-06-30-visual-redesign-bibimbap-css.md` | changes | 기존 갱신(스코프 정합 확인 후 append) | 해당 없음(신규 문서 아님) | "관련 문서" 절에 새 문서로의 후속 링크 추가(단위2 fragment 대체 사실 명시) |
|
||||||
|
|
||||||
|
## 카테고리 판별 근거
|
||||||
|
|
||||||
|
`document-category-classification.md` "빠른 결정 순서" 적용: 아키텍처 원칙에 대한 되돌리기 어려운 결정이 아니고(카테고리 세트·시드 위치는 제품 taxonomy 확정일 뿐 레이어/기술 선택 아님), 운영 장애가 아니고, 실제 코드(`posts-list.jsp`, 신규 fragment 2개, `PostControllerTest.java`)와 소비 데이터(`post_categories` DML 시드)가 실제로 바뀌어 런타임 동작이 달라졌다 → `changes/`.
|
||||||
|
|
||||||
|
ADR 후보 여부 재검토: "편집형 일방 발행 모델" 정정은 이미 `POST_WRITE` 권한 게이트로 W3-3 설계 당시 확정된 기존 전제의 재확인이며 이번 세션이 새로 뒤집은 아키텍처 결정이 아니다(세션 내부 orchestrator 가정 오류의 정정일 뿐, 코드/스키마 차원의 기존 결정 변경 아님) → 신규 ADR 불필요로 판단.
|
||||||
|
|
||||||
|
## 기존 파일 append 전 스코프 대조 (수행 근거)
|
||||||
|
|
||||||
|
- `docs/development/frontend-redesign-coverage-checklist.md`: 첫 단락 스코프 선언 "`WEB-INF/views/*.jsp` 전체... 클로드 디자인 리디자인의 '마크업·클래스 이식' 단위 반영 여부를 추적" + 사용 규칙 "세션 종료 시: 이번 세션에서 다룬 파일의 상태 열을 갱신하고, 다룬 근거 커밋/문서 링크를 채운다" — 이번 세션이 `posts-list.jsp` 를 다뤘고 근거 열 갱신은 선언된 스코프 내 정규 사용법이므로 append 적합 판정.
|
||||||
|
- `docs/changes/2026-06-30-visual-redesign-bibimbap-css.md`: 첫 단락 스코프는 "9단위 비주얼 리디자인 changelog" 고정 스냅샷. 본문 서술은 수정하지 않고 "관련 문서" 절(이미 선행 작업 링크 1건 존재하는 인덱스성 절)에 후속 링크 1줄만 추가 — 선례(`2026-06-18-w3-2-comments-reviews.md` 가 `2026-06-22` 고도화 문서로 "해소" 링크를 사후 추가한 패턴)와 동형. 이월 항목이 아닌 완료 유닛(단위2)이 사후에 대체됐다는 사실 포인터이므로 스코프 위반 아님으로 판정.
|
||||||
|
|
||||||
|
## 의사결정 기록 위치
|
||||||
|
|
||||||
|
- D1(카테고리 세트)/D2(시드 위치)/D3(모델 정정): `.atp/work-session/20260724-100414/report.md` `# Decisions` 섹션(원본 소스), `docs/changes/2026-07-24-post-categories-seed-list-redesign.md` "결정" 절에 요약 이관.
|
||||||
|
- 설계 상세(토큰계획·카테고리 hue 맵·탭 마크업·AC 12개 근거): `.atp/work-session/20260724-100414/artifacts/design-posts-categories.md` (design-advisor 산출, 원본 유지 — 이번 문서화 단계에서 복제하지 않고 링크만 인용).
|
||||||
|
- 검증 상세(L1 FAIL→복구 경위, AC 매칭 표): `.atp/work-session/20260724-100414/artifacts/verification-posts-categories.md`.
|
||||||
|
|
||||||
|
## 추후 문서화가 필요한 항목
|
||||||
|
|
||||||
|
- 모바일 좁은 폭 육안 확인, 라이트 테마 육안 확인, 카드 뱃지 dot 실물 확인, 운영 스키마 시드 반영(운영자) — 4건 모두 needs_user_verification 으로 `changes/2026-07-24-post-categories-seed-list-redesign.md` 에 이미 명시. 완료 시 해당 문서에 결과 append 필요(신규 문서 불요, 기존 문서 갱신).
|
||||||
|
- graph-refresh `partial-stale` open item(근본원인 선행 커밋 `02a1b75`, JSP/JSPF graphify 스캔범위 밖) — 별도 graph 부채로 이월됨. graphify-update-advisor 소관이라 이번 문서화 단계에서 손대지 않음.
|
||||||
|
|
@ -0,0 +1,205 @@
|
||||||
|
---
|
||||||
|
schema_version: 2
|
||||||
|
sid: 20260724-100414
|
||||||
|
started_at: 2026-07-24T10:04:14+09:00
|
||||||
|
ended_at: 2026-07-24T17:24:31+09:00
|
||||||
|
branch: feat/v2
|
||||||
|
user_request: |
|
||||||
|
포스트 보드 카테고리 정비 + 목록 카테고리별 뷰 디자인 재설계.
|
||||||
|
A. 기본 카테고리 세트 확정(4~6) + 멱등 시드(위치 택1: seed-dev / migrations / 없음).
|
||||||
|
B. posts-list.jsp 탭/뱃지/빈상태 디자인 재설계(frontend-design). 기능 로직 불변, 표현층만.
|
||||||
|
제약: JSP escape 유지, CSRF, MyBatis #{}, static은 webapp/css·js, textContent 우선.
|
||||||
|
검증: L1 회귀 0, /posts 실화면(탭 다수/필터/빈상태/모바일/키보드).
|
||||||
|
---
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
포스트 보드(편집형 일방 발행) 기본 카테고리 5종(Unity 5-mirror)을 멱등 운영 마이그레이션으로 시드하고,
|
||||||
|
목록 카테고리별 뷰 표현층을 frontend-design 규율로 재설계했다. signature = "편집 마스트헤드 인덱스" 탭
|
||||||
|
(서수 01~05 + 카테고리 hue 밑줄 rule + 가로스크롤 + focus-visible + aria-current), 카드 뱃지는 amber pill
|
||||||
|
제거 후 hue dot화, 빈상태 2종 split(편집 톤·소비자 CTA 미노출). 기능 로직 불변. 착수 시 orchestrator의
|
||||||
|
peer-forum 가정이 사용자 정정으로 뒤집혀(일방 발행) 카테고리 축을 편집 taxonomy로 재설계(plan-gate 1회).
|
||||||
|
검증: L1 362 GREEN(베이스라인 스테일 테스트 최소수정으로 복구), 정적 AC 9/9, 실화면 전건(탭/필터/빈상태/
|
||||||
|
서수/hue/focus/컴파일). dev DB 시드 적용 완료.
|
||||||
|
|
||||||
|
# Invocations
|
||||||
|
- agent: design-advisor
|
||||||
|
model: opus (large)
|
||||||
|
model_choice: { rationale: 'signature 창의 판단 코어 — 토큰계획+마스트헤드 인덱스 설계' }
|
||||||
|
outcome: '8절 설계도 확정(오픈질문 0). artifacts/design-posts-categories.md'
|
||||||
|
- agent: verification-advisor
|
||||||
|
model: sonnet (medium)
|
||||||
|
model_choice: { rationale: 'L1 실행+AC 판정 기계적' }
|
||||||
|
outcome: 'L1 초기 FAIL(베이스라인 스테일 감지) + 정적 AC 9/9. 회귀 아님 판정'
|
||||||
|
- agent: graph-refresh-checker
|
||||||
|
model: haiku (small)
|
||||||
|
model_choice: { rationale: 'staleness 판정 경량' }
|
||||||
|
outcome: 'partial-stale(근본원인 선재 02a1b75, 이 세션 JSP/DML graph 범위 밖)'
|
||||||
|
- agent: documentation-advisor
|
||||||
|
model: sonnet (medium)
|
||||||
|
model_choice: { rationale: '문서 카테고리 판별+작성' }
|
||||||
|
outcome: 'docs/changes/2026-07-24-post-categories-seed-list-redesign.md + index/coverage 링크'
|
||||||
|
|
||||||
|
# Advisor Invocation Decision Log
|
||||||
|
# 각 advisor 호출/스킵 판단 즉시 1줄 append
|
||||||
|
- advisor: requirements-advisor
|
||||||
|
decision: skip
|
||||||
|
rationale: '요구 명확. 오픈 결정 2개(카테고리 세트·시드 위치)만 orchestrator AskUserQuestion 직접 해소'
|
||||||
|
checked_at: 2026-07-24T10:05:00+09:00
|
||||||
|
- advisor: graphify-lookup-advisor
|
||||||
|
decision: skip
|
||||||
|
rationale: '배경 file:line 사실을 live 코드로 직접 전수 재검증 완료(그래프보다 authoritative). 사실탐색 목적 lookup 불요'
|
||||||
|
checked_at: 2026-07-24T10:05:00+09:00
|
||||||
|
- advisor: research-advisor
|
||||||
|
decision: skip
|
||||||
|
rationale: '드리프트 우려 사실 전건 재확인 통과. seed 멱등 패턴·JSP CSS 언어 파악 완료. 외부조사 불요'
|
||||||
|
checked_at: 2026-07-24T10:05:00+09:00
|
||||||
|
- advisor: design-advisor (frontend-design)
|
||||||
|
decision: call
|
||||||
|
rationale: '작업 B(탭/뱃지/빈상태 재설계)는 토큰계획+signature 요구. frontend-design 원칙 적용 설계도 필요'
|
||||||
|
checked_at: 2026-07-24T10:05:00+09:00
|
||||||
|
|
||||||
|
# Decisions
|
||||||
|
- id: D1
|
||||||
|
decision: '카테고리 세트 = Unity 5-mirror 편집 taxonomy'
|
||||||
|
detail: '공지/개발일지/쇼케이스/자료·튜토리얼/업계 소식. slug=notice,devlog,showcase,resources,industry (sort_order 1..5). is_active=true'
|
||||||
|
by: user (AskUserQuestion)
|
||||||
|
- id: D2
|
||||||
|
decision: '시드 위치 = db/migrations/ 운영 반영'
|
||||||
|
detail: '멱등 INSERT(ON CONFLICT/WHERE NOT EXISTS). 프로덕션 런칭 시 기본 탭 제공. 운영자 이후 CRUD 가능'
|
||||||
|
by: user (AskUserQuestion)
|
||||||
|
- id: D3
|
||||||
|
decision: '포스트 보드 모델 = 편집형 일방 발행(Unity 블로그). peer-to-peer 아님'
|
||||||
|
detail: 'POST_WRITE 권한게이트 근거. 카테고리는 편집 taxonomy, 상호작용축(피드백/자유) 배제'
|
||||||
|
by: user (정정)
|
||||||
|
|
||||||
|
# verified_by_me
|
||||||
|
- 'L1: unit+regression — 컨테이너 `mvn -o -P dev test` → Tests run: 362, Failures: 0, Errors: 0, BUILD SUCCESS. (베이스라인 RED[PostControllerTest 스테일] 최소수정으로 복구: list() 3콜사이트 null 1개 추가)'
|
||||||
|
- '정적 AC 9/9 pass: AC-1(--cat-* 5) AC-2(catToken case 5) AC-4(:focus-visible) AC-5(aria-current) AC-6(prefers-reduced-motion) AC-7(빈상태 2 fragment) AC-9(뱃지 pill 제거) AC-11(bibimbap.css 불변) AC-12(원시 slug 무주입). 코드리뷰 AC-8/AC-10 pass.'
|
||||||
|
- '실화면(localhost:8080/posts, dev DB 시드 5건 적용후): 탭 6개 마스트헤드 인덱스 렌더(서수 01~05=sort_order순, AC-3) / 탭클릭 필터(?categoryId=2 → is-active+aria-current 개발일지 이동) / 빈상태 noposts 편집톤(활성 카테고리명 + 소비자 CTA없음) / 카테고리 활성 hue rule / focus-visible amber ring / JSP 런타임 컴파일 200. 스크린샷 자가비평 완료.'
|
||||||
|
- 'L2(seed DML contract): dev.post_categories 5행 slug/sort_order/is_active 정합 확인(psql SELECT).'
|
||||||
|
|
||||||
|
# needs_user_verification
|
||||||
|
- '모바일 폭 픽셀 시각확인: 반응형 CSS(탭 nowrap 가로스크롤+edge fade, 900/640 그리드 브레이크포인트)는 정적 검증됨. 브라우저 resize_window 가 스크린샷 뷰포트에 미반영(툴 한계)이라 실기기/좁은 폭 육안 1회 권장.'
|
||||||
|
- '라이트 테마 시각: 다크 테마만 스크린샷 확인. 라이트 hue 토큰은 코드 존재(대비 규율 준수), 육안 확인 시 라이트 모드 토글 1회.'
|
||||||
|
- '카드 뱃지 dot 시각: 현재 게시글 0건이라 카드 미표시. 글 1건 발행 후 dot+라벨 육안 확인(선택).'
|
||||||
|
- '운영 반영: db/migrations/20260724-post-categories-seed.sql 은 dev DB 에만 적용됨. 운영 스키마 반영은 운영자가 동일 psql 명령으로 수행(파일 헤더 참조).'
|
||||||
|
|
||||||
|
# graph_refresh
|
||||||
|
verdict: partial-stale
|
||||||
|
handled: 'flag-only(open_item)'
|
||||||
|
rationale: |
|
||||||
|
체커 판정 partial-stale 이나 근본 원인은 선재 커밋 02a1b75(PostController HttpSession+canWrite,
|
||||||
|
이 세션 이전)로 graph frontmatter source_commit(6154826) 미갱신 상태. 이 세션 변경은
|
||||||
|
JSP/JSPF(graph 스캔 범위 밖 — c2daee8 등재 한계) + DML seed + test 시그니처 정합뿐이라
|
||||||
|
graph 노드 0 추가. 무관한 선재 부채의 full 재생성은 본 기능 태스크 스코프 밖 + graph 산출물
|
||||||
|
다수 gitignore. open_items 에 선재 graph 부채로 명시, 강제 재생성 안 함.
|
||||||
|
|
||||||
|
# user_signals
|
||||||
|
positive:
|
||||||
|
- quote: '카테고리 세트/시드위치/시그니처방향/베이스라인fix/실화면검증 전부 Recommended 옵션 1샷 수락'
|
||||||
|
note: 'AskUserQuestion Recommended 선택이 반복 수락됨 — framing+근거 제시가 사용자 판단과 정합. 특히 signature 방향(마스트헤드 인덱스) 대안 2개 제시에도 Recommended 채택'
|
||||||
|
negative:
|
||||||
|
- quote: '포스트는 제작자끼리 주고받는거 아니야. 일방적으로 소비하도록 만드는 개념'
|
||||||
|
structural: true
|
||||||
|
note: 'orchestrator 초기 가정(peer-to-peer 커뮤니티 보드) 오류. 실제=편집형 일방 발행(Unity 블로그 모델). POST_WRITE 권한게이트가 근거. §2.7 plan-gate 발동 → 카테고리 축 재설계(상호작용축 제거, 편집 taxonomy 채택)'
|
||||||
|
|
||||||
|
# assumption_reversals
|
||||||
|
- at: 2026-07-24T10:12:00+09:00
|
||||||
|
old: '포스트 보드 = 제작자 peer-to-peer 커뮤니티 → 피드백요청/자유 등 상호작용 카테고리'
|
||||||
|
new: '포스트 보드 = 편집형 일방 발행(Unity 블로그 모델) → 공지/개발일지/쇼케이스/자료/업계 등 편집 taxonomy'
|
||||||
|
trigger: '사용자 정정'
|
||||||
|
|
||||||
|
# lessons_applied (docs-first, memory 미설정 → docs 단독)
|
||||||
|
- '#1 도메인/상호작용 모델 접지 → docs/development/workflow-patterns.md 신규 섹션 추가(수용)'
|
||||||
|
- '#2 기존 컨트롤러 시그니처 변경 full test 의무 → docs/development/verification-strategies.md 스코프 확장 클로즈(수용)'
|
||||||
|
- '#3 JSP no-reload 트러블슈팅 → 스킵. local-dev-setup.md + workflow-patterns.md:72 에 이미 상세 문서화됨. content 갭 아닌 소비 갭이라 신규 doc 불필요.'
|
||||||
|
- 'protocol_feedback 2건(§9 무관 baseline RED 처리 절차·ad hoc 커밋 graph staleness 감지)은 atp 번들 대상 — 이 프로젝트 파일 밖이라 report 기록만.'
|
||||||
|
|
||||||
|
# open_items
|
||||||
|
- '선재 graph 부채: docs/graph frontmatter source_commit(6154826) 이 02a1b75(PostController HttpSession/canWrite) 미반영. 별도 세션에서 `/graphify src/ docs/` full 재생성 + 메타 갱신 권장. (이 세션 변경은 graph 스캔 범위 밖이라 무기여)'
|
||||||
|
- '이전 세션 잔여: .atp/work-session/20260724-095414/ (untracked, 09:54 별도 /atp 세션 산출물, 이 작업단위 아님). 커밋 미포함 — 소유 세션이 정리/커밋할 대상.'
|
||||||
|
- 'db/migrations/20260724-post-categories-seed.sql 운영 스키마 반영 미수행(dev DB 만 적용). 운영자 psql 실행 필요.'
|
||||||
|
|
||||||
|
# Retrospective
|
||||||
|
signals:
|
||||||
|
positive:
|
||||||
|
- quote_or_paraphrase: '카테고리 세트/시드위치/시그니처방향/베이스라인fix/실화면검증 전부 Recommended 옵션 1샷 수락'
|
||||||
|
about: 'AskUserQuestion Recommended 프레이밍(근거 명시) — signature 대안 2개 제시에도 Recommended(마스트헤드 인덱스) 채택. 이미 여러 이전 세션(20260701-113509 등)에서 검증된 패턴의 재확인이라 신규 교훈 후보로는 채택하지 않음(기존 workflow-patterns.md 패턴과 중복).'
|
||||||
|
negative:
|
||||||
|
- quote_or_paraphrase: '포스트는 제작자끼리 주고받는거 아니야. 일방적으로 소비하도록 만드는 개념'
|
||||||
|
about: 'orchestrator 초기 가정(peer-to-peer 커뮤니티 보드) 오류 정정. 실제=편집형 일방 발행. §2.7 plan-gate 발동 → AskUserQuestion 3회 왕복 재설계.'
|
||||||
|
structural: true
|
||||||
|
|
||||||
|
what_went_well:
|
||||||
|
- '§2.7 plan-gate 가 사용자 정정 시점에 정확히 발동 — 카테고리 축(피드백요청/자유 등 상호작용축)을 즉시 폐기하고 편집 taxonomy 로 재설계. 잘못된 방향으로 구현까지 진행되지 않고 design 단계에서 저지.'
|
||||||
|
- 'verification-advisor 가 베이스라인 RED(PostControllerTest 스테일)를 이 세션 변경(JSP+DML)과 명확히 분리 판정(git status/log 메타데이터 근거) — rollback 오판 없이 최소수정으로 복구.'
|
||||||
|
- 'AskUserQuestion Recommended-옵션 프레이밍이 카테고리/시드/시그니처/베이스라인fix/실화면검증 5개 결정 전부 1라운드 수렴.'
|
||||||
|
- 'design-advisor 가 정적 grep 실행 가능한 "집합 전수 AC"(hue 5종·catToken case 5종 등 동수 불변식)로 검증 포인트를 작성해 verification-advisor 가 9/9 기계적 판정 가능 — 프론트엔드 표현층임에도 애매한 육안판정 의존을 최소화.'
|
||||||
|
|
||||||
|
what_to_improve:
|
||||||
|
- '착수 시 도메인/상호작용 모델을 가정하기 전에 관련 permission enum(`PermissionKeys.java`)과 `docs/work-log/index.md` 의 기존 골자문서를 먼저 확인했어야 함 — POST_WRITE 게이트와 `docs/work-log/2026-06-17-w3-feature-skeletons.md`(“포스터 권한자만 작성, 일반 유저 읽기만(댓글 없음)”)에 이미 편집형 일방 발행 모델이 W3-3 설계 시점(2026-06-23)에 확정돼 있었다. documentation-advisor 도 이 사실을 근거로 “세션이 새로 뒤집은 결정이 아니라 orchestrator 가정 오류의 재확인”이라 판정함 — 즉 이번 정정은 사용자만 알 수 있는 신규 정보가 아니라 docs-first 로 조기 포착 가능했다.'
|
||||||
|
- '02a1b75(선재 커밋, PostController 시그니처에 categoryId/HttpSession 추가)가 PostControllerTest 콜사이트 3곳을 갱신하지 않은 채 커밋돼 저장소가 test-compile RED 상태로 방치됨 — 이번 무관 세션의 verification-advisor 가 우연히 잡아냈다. verification-strategies.md 의 “신규 컨트롤러/매퍼 의존 변경 시 full test 의무” 조항은 문구상 “신규” 컨트롤러 추가만 명시해 기존 컨트롤러의 시그니처 변경 커밋에는 명확히 적용되지 않는 스코프 공백이 있다.'
|
||||||
|
- '(런처 제공 컨텍스트) JSP 화면 미반영 증상 트러블슈팅 시 work-dir/target 직접 조작을 먼저 시도 — `docs/development/local-dev-setup.md` 에 동일 함정과 해법(재시작/`docker compose restart app`)이 이미 상세히 문서화돼 있음에도 소비되지 않음. `workflow-patterns.md` 자체에도 “known pitfall 교차인용 누락” 유사사례(세션 20260701-100731)가 이미 기록돼 있어, 문서 존재와 실제 소비 사이 갭이 반복되는 정황.'
|
||||||
|
- '선재 커밋 02a1b75(PostController.java, graph 스캔 범위 내)가 그래프 재생성을 트리거하지 않고 넘어가 partial-stale 이 누적 — ad hoc(비-ATP) 커밋 경로에는 graph-refresh-checker 가 개입하지 않아, 다음 ATP 세션이 우연히 발견할 때까지 부채가 조용히 쌓이는 구조.'
|
||||||
|
|
||||||
|
memory_candidates:
|
||||||
|
- name: domain-model-grounding-before-ui-taxonomy-assumption
|
||||||
|
type: feedback
|
||||||
|
description: '기능의 상호작용/도메인 모델을 가정하기 전 permission enum + 기존 work-log 골자문서로 이미 확정된 모델을 먼저 확인한다.'
|
||||||
|
body_draft: |
|
||||||
|
Why: PostController 가 POST_WRITE 권한게이트를 이미 구현 중이었고, docs/work-log/2026-06-17-w3-feature-skeletons.md
|
||||||
|
에 "포스터 권한자만 작성, 일반 유저 읽기만(댓글 없음)"이 W3-3 설계 시점(2026-06-23)에 이미 명문화돼 있었다.
|
||||||
|
그럼에도 orchestrator 가 "포스트 보드=P2P 커뮤니티"로 가정하고 카테고리 축(피드백요청/자유 등)까지 설계
|
||||||
|
진입 직전까지 진행했다가 사용자 정정으로 plan-gate 발동 → AskUserQuestion 3회 왕복 재설계가 발생했다.
|
||||||
|
|
||||||
|
How to apply: 신규 기능(특히 이미 부분 구현된 기능)의 UI/카테고리 설계에 들어가기 전,
|
||||||
|
(1) 관련 permission key(`PermissionKeys.java` 등)를 grep 해 "누가 쓰고 누가 읽는가"를 코드 사실로 먼저 확정하고,
|
||||||
|
(2) `docs/work-log/index.md` 에서 해당 기능명으로 이미 존재하는 골자/설계 문서를 열람해 상호작용 모델이
|
||||||
|
이미 결정돼 있는지 확인한다. 두 단계를 거치면 착수 시점에 가정 오류를 차단할 수 있다.
|
||||||
|
(`freeze/동결 분류는 근거 문서 확인 선행` 패턴의 도메인모델 버전.)
|
||||||
|
rationale_for_saving: '재현 가능(신규 세션마다 "이미 결정된 도메인모델을 재설계 직전까지 못 보고 진행" 패턴 반복 가능). 코드/git log 만으로 유도 불가 — 관찰 기반 교훈.'
|
||||||
|
signal_source: negative
|
||||||
|
docs_sync_target: docs/development/workflow-patterns.md
|
||||||
|
memory_optional: true
|
||||||
|
|
||||||
|
- name: existing-controller-signature-change-requires-test-before-commit
|
||||||
|
type: feedback
|
||||||
|
description: '기존 컨트롤러 메서드 시그니처 변경(파라미터 추가 등) 커밋 전 최소 `mvn -o test-compile` 실행 의무 — "신규 컨트롤러" 조항의 스코프 공백 보완.'
|
||||||
|
body_draft: |
|
||||||
|
Why: 02a1b75(PostController 에 categoryId/HttpSession 파라미터 추가)가 PostControllerTest 3개 콜사이트를
|
||||||
|
갱신하지 않은 채 커밋됐고, 다음 세션(20260724-100414) verification 단계에서야 test-compile BUILD FAILURE 로
|
||||||
|
발견됐다(무관한 JSP 작업 세션의 verification-advisor 가 우연히 포착). verification-strategies.md 의 기존
|
||||||
|
규칙("신규 컨트롤러/매퍼 의존 변경 시 full test 의무")은 문구상 "신규" 컨트롤러 추가에 한정돼 기존 컨트롤러의
|
||||||
|
시그니처 변경에는 적용 여부가 애매했다.
|
||||||
|
|
||||||
|
How to apply: 기존 컨트롤러 public 메서드의 파라미터를 추가/제거/순서변경하면, 다른 스코프(JSP 등) 작업이어도
|
||||||
|
커밋 전 `./mvnw -o test-compile`(최소) 을 실행해 그 컨트롤러의 테스트 파일이 시그니처와 정합한지 확인한다.
|
||||||
|
/atp:task 세션 여부와 무관하게(CLAUDE.md 상 비-메인스트림 브랜치 즉흥 커밋도 표준 승인) 동일 적용.
|
||||||
|
rationale_for_saving: '재현 가능한 패턴(시그니처 변경 시 테스트 콜사이트 누락). 기존 규칙 문구가 "신규"로 한정돼 스코프 공백이 문서 정독만으로 드러나지 않음(실제 발생으로 관찰됨).'
|
||||||
|
signal_source: observation
|
||||||
|
docs_sync_target: docs/development/verification-strategies.md
|
||||||
|
memory_optional: true
|
||||||
|
|
||||||
|
- name: jsp-noreload-troubleshoot-check-docs-before-workdir-manipulation
|
||||||
|
type: feedback
|
||||||
|
description: 'JSP 화면 미반영 증상 시 target/work-dir 직접 조작 전에 local-dev-setup.md 재시작 절차부터 확인 — 이미 문서화된 함정의 재발.'
|
||||||
|
body_draft: |
|
||||||
|
Why: docs/development/local-dev-setup.md 에 "JSP 변경도 재기동으로 확실히 반영"·"저장 즉시 반영 안 되는
|
||||||
|
사례 확인됨 → curl 로 대조, docker compose restart app 으로 강제확인"이 이미 상세히 명시돼 있다.
|
||||||
|
workflow-patterns.md 에도 유사 계열(known pitfall 교차인용 누락, 세션 20260701-100731)이 이미 기록돼
|
||||||
|
있음에도, 문서 소비 없이 work-dir/target 직접 조작을 먼저 시도하는 패턴이 반복된 정황이 있다.
|
||||||
|
|
||||||
|
How to apply: JSP/컨트롤러 편집 후 "저장했는데 화면이 안 바뀐다" 증상을 만나면, work-dir/target 파일을
|
||||||
|
직접 조작하기 전에 반드시 `docs/development/local-dev-setup.md` "코드·JSP 변경 후 반드시 앱 재빌드·재시작"
|
||||||
|
섹션을 먼저 열람하고 거기 명시된 재시작 절차(IDE: Stop→rebuild→Run / docker: restart app)를 1차 시도로
|
||||||
|
삼는다.
|
||||||
|
rationale_for_saving: '문서는 이미 존재하나 소비되지 않아 재발 — "문서 존재"와 "문서 소비" 사이 갭이 구조적. 재현 가능성 높음(트러블슈팅 시 직관적으로 파일 조작을 먼저 시도하는 경향).'
|
||||||
|
signal_source: observation
|
||||||
|
docs_sync_target: docs/development/local-dev-setup.md
|
||||||
|
memory_optional: true
|
||||||
|
|
||||||
|
protocol_feedback:
|
||||||
|
- 'ad hoc(비-/atp:task) 커밋이 graph 스캔범위(src/main/java 등) 파일을 변경해도 graph-refresh-checker 가 개입할 경로가 없어, staleness 가 조용히 누적되다가 무관한 다음 ATP 세션이 우연히 발견하는 구조다(이번 세션: 02a1b75 가 원인, 이 세션의 checker 가 우연히 partial-stale 포착). 프로토콜 차원에서 "비-ATP 직접 커밋도 graph 스캔범위 변경 시 최소 staleness 플래그를 남기는" 경량 장치(예: 커밋 트레일러 관례, 또는 CLAUDE.md 작업원칙에 1줄 추가) 검토 권고.'
|
||||||
|
- '§9 종료조건의 "L1 통합검증 통과"가 이번처럼 "이 세션과 무관한 베이스라인 RED"를 만났을 때의 표준 처리 절차(최소수정 즉시 복구 vs open_item 만 남기고 진행 가능 여부)가 명문화돼 있지 않다. 이번엔 verification-advisor 의 grounded 판단(git log/status 메타데이터로 무관 확정)으로 안전하게 최소수정 복구했으나, 이 판단 절차 자체(무관 확정 방법 + 최소수정 허용 범위)를 프로토콜에 명시하면 재현성이 높아진다.'
|
||||||
|
|
||||||
|
applied_changes: []
|
||||||
|
|
@ -66,3 +66,4 @@
|
||||||
## 관련 문서
|
## 관련 문서
|
||||||
|
|
||||||
- [docs/changes/2026-06-29-b2-b4-fe-hardening.md](./2026-06-29-b2-b4-fe-hardening.md) — FE 다크모드 대비·세션 하드닝 (선행 작업)
|
- [docs/changes/2026-06-29-b2-b4-fe-hardening.md](./2026-06-29-b2-b4-fe-hardening.md) — FE 다크모드 대비·세션 하드닝 (선행 작업)
|
||||||
|
- [2026-07-24-post-categories-seed-list-redesign.md](./2026-07-24-post-categories-seed-list-redesign.md) — 단위2 `fragments/posts-empty.jspf`(빈상태) 를 카테고리 0개/글 0개 2종으로 분리·대체 + `posts-list.jsp` 탭 재설계 (후속 작업)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
# 2026-07-24 포스트 보드 기본 카테고리 시드 + 카테고리별 목록 뷰 재설계
|
||||||
|
|
||||||
|
**세션**: `/atp:task` — sid `20260724-100414` (선행 조사 sid `20260724-095414`)
|
||||||
|
**브랜치**: feat/v2
|
||||||
|
|
||||||
|
## 배경
|
||||||
|
|
||||||
|
포스트 보드(W3-3, [W3-3-posting-board-design.md](../../.atp/work-session/20260623-104307/implementation/W3-3-posting-board-design.md)) "카테고리별 보기" 골격은 이미 구현돼 있었다 — 스키마 `post_categories`, `PostController#list` 의 `categoryId` 필터, `PostsMapper.listPublishedKeyset`, `posts-list.jsp` 탭/뱃지. 유일한 갭은 `post_categories` INSERT 시드가 0건이라 실제로는 "전체" 탭만 렌더된다는 것(선행 조사: sid `20260724-095414` report — 카테고리는 "운영자 런타임 CRUD 생성" 설계라 기본 시드가 기획에 애초 없었음을 확인).
|
||||||
|
|
||||||
|
사용자 확인 과정에서 orchestrator 초기 가정("제작자 peer-to-peer 커뮤니티 보드 → 피드백요청/자유 등 상호작용 카테고리")이 틀렸다는 정정이 나왔다: 포스트 보드는 **편집형 일방 발행 모델**(Unity 블로그류)이며 `POST_WRITE` 권한 게이트가 그 근거다. 이 정정에 따라 카테고리 축을 상호작용축이 아닌 편집 taxonomy 로 재설계했다.
|
||||||
|
|
||||||
|
## 결정 (사용자 확정, AskUserQuestion)
|
||||||
|
|
||||||
|
- **카테고리 세트**: Unity 5-mirror 편집 taxonomy — 공지(`notice`)/개발일지(`devlog`)/쇼케이스(`showcase`)/자료·튜토리얼(`resources`)/업계 소식(`industry`). `sort_order` 1~5, `is_active=true`.
|
||||||
|
- **시드 위치**: `db/migrations/` — 운영 스키마에도 반영할 항목(멱등 `ON CONFLICT DO NOTHING`).
|
||||||
|
- **모델 정정**: 포스트 보드 = 편집형 일방 발행(peer-to-peer 아님) → 카테고리는 편집 taxonomy 로 한정하고 피드백/자유 등 상호작용축은 배제.
|
||||||
|
|
||||||
|
## 변경 내용
|
||||||
|
|
||||||
|
- 신규 `db/migrations/20260724-post-categories-seed.sql` — 기본 5종 카테고리 멱등 시드. dev DB 적용 완료(`post_categories` 5행, slug/sort_order/is_active 확인). 운영 스키마 반영은 운영자가 동일 psql 명령으로 별도 수행.
|
||||||
|
- `src/main/webapp/WEB-INF/views/posts-list.jsp` — 카테고리 탭을 "편집 마스트헤드 인덱스"로 재설계: `sort_order` 기반 서수(`01`~`05`) + 카테고리 hue 섹션 rule(`--cat-*` 5개, light/dark) + 로드 시 staggered reveal + 가로 스크롤 오버플로(edge fade) + `:focus-visible` + `aria-current="page"`. 카드 뱃지는 채워진 amber pill 제거 → hue dot + 저채도 대문자 라벨로 축소. 신규 `catToken()` 화이트리스트 헬퍼로 `data-cat` 방출 시 slug XSS 방어(원시 slug 미주입). `prefers-reduced-motion` 존중. 필터/keyset 페이지네이션 등 기능 로직은 불변. `bibimbap.css` 는 0라인 변경(인라인 `<style>` 자체 네임스페이스 유지).
|
||||||
|
- 신규 `fragments/posts-empty-nocats.jspf`, `posts-empty-noposts.jspf` — 빈상태를 카테고리 0개(운영자 미생성) / 특정 카테고리 글 0개 2종으로 분리, 편집 톤 카피로 교정("발행" 동사, 소비자 분기엔 글쓰기 CTA 미노출 — 일방 발행 모델 정합). 구 `posts-empty.jspf` 삭제.
|
||||||
|
- `src/test/java/.../PostControllerTest.java` — 세션 진입 시점부터 있던 베이스라인 컴파일 RED(`PostController.list` 3콜사이트 인자 수 불일치, 선행 커밋 `02a1b75` 유발, 이번 세션 무관 선재 결함) 를 콜사이트에 `categoryId` 인자(null) 추가로 해소.
|
||||||
|
|
||||||
|
설계 상세(토큰계획/카테고리 시각코딩/탭 마크업/AC 근거)는 [설계 문서](../../.atp/work-session/20260724-100414/artifacts/design-posts-categories.md) 참조.
|
||||||
|
|
||||||
|
## 검증 결과
|
||||||
|
|
||||||
|
| 레벨 | 결과 |
|
||||||
|
|---|---|
|
||||||
|
| L1 | 컨테이너 `mvn -o -P dev test` — 362/362 PASS (베이스라인 RED 복구 후 GREEN) |
|
||||||
|
| 정적 AC | 9/9 pass — hue 토큰 5개/화이트리스트 5개 동수, `:focus-visible`, `aria-current`, `prefers-reduced-motion`, 빈상태 2종 존재+include 2회, 뱃지 pill 배경 제거, `bibimbap.css` diff 0, `data-cat` 은 `catToken()` 반환값만 |
|
||||||
|
| 코드리뷰 AC | 2/2 pass — 편집 톤 교정(peer-forum 문구 잔존 0), 더보기 URL 조립·컨트롤러/매퍼 로직 무변경 |
|
||||||
|
| 실화면 | dev DB 시드 후 `/posts`: 탭 6개(서수 01~05=sort_order 순), 탭 클릭 필터(`?categoryId=N`→`is-active`+`aria-current`), 빈상태 편집톤, hue rule, focus-visible ring, JSP 런타임 컴파일 200 — 스크린샷 자가비평 완료 |
|
||||||
|
|
||||||
|
전체 검증 로그: [verification-posts-categories.md](../../.atp/work-session/20260724-100414/artifacts/verification-posts-categories.md).
|
||||||
|
|
||||||
|
## needs_user_verification
|
||||||
|
|
||||||
|
- 모바일 좁은 폭 픽셀 육안 확인(반응형 CSS 정적 검증만 완료, 브라우저 툴 resize 한계로 뷰포트 미반영).
|
||||||
|
- 라이트 테마 육안 확인(다크 테마만 스크린샷 확인, 라이트 hue 토큰은 코드상 대비 규율 준수).
|
||||||
|
- 카드 뱃지 dot 실물 확인(현재 게시글 0건이라 카드 미표시 — 글 1건 발행 후 확인 필요).
|
||||||
|
- 운영 스키마에 `db/migrations/20260724-post-categories-seed.sql` 반영(운영자 수행, dev DB 에만 적용됨).
|
||||||
|
|
||||||
|
## open item
|
||||||
|
|
||||||
|
graph-refresh 판정 `partial-stale`. 근본 원인은 선행 커밋 `02a1b75`(PostController) 로 이 세션 이전부터 존재하던 graph 부채이며, 이번 세션 변경은 JSP/JSPF([graphify 스캔 범위 밖 — 알려진 한계](../graph/index.md))와 DML 시드뿐이라 graph 노드 추가가 없다. 무관한 선행 부채의 full 재생성은 이 세션 스코프 밖으로 두고 이월한다.
|
||||||
|
|
||||||
|
## 제약 준수
|
||||||
|
|
||||||
|
JSP `HtmlUtils.htmlEscape` 유지, MyBatis `#{}` 바인딩 불변(동적 `${}` 미사용), 정적 CSS 는 `posts-list.jsp` 인라인에 한정(`bibimbap.css` 0변경), 클라이언트 렌더링 `textContent` 원칙(신규 JS 0줄).
|
||||||
|
|
||||||
|
## 관련 문서
|
||||||
|
|
||||||
|
- [../../.atp/work-session/20260623-104307/implementation/W3-3-posting-board-design.md](../../.atp/work-session/20260623-104307/implementation/W3-3-posting-board-design.md) — 포스트 보드 원 설계(W3-3)
|
||||||
|
- [2026-06-30-visual-redesign-bibimbap-css.md](./2026-06-30-visual-redesign-bibimbap-css.md) — 단위2(빈 상태 fragment `posts-empty.jspf`) 가 이번 세션에서 2종으로 분리·대체됨
|
||||||
|
- [../development/frontend-redesign-coverage-checklist.md](../development/frontend-redesign-coverage-checklist.md) — `posts-list.jsp` 행 갱신
|
||||||
|
- [../development/document-category-classification.md](../development/document-category-classification.md)
|
||||||
|
|
@ -15,3 +15,4 @@
|
||||||
- [2026-06-30-visual-redesign-bibimbap-css.md](./2026-06-30-visual-redesign-bibimbap-css.md) — 클로드 디자인 리디자인 비주얼 레이어 9단위 반영. bibimbap.css 신설 + theme-init 공통 link + 빈상태 fragment 2종 + 홈/인증/약관/프로필/에러/모집 마크업·클래스 이식. game-detail no-op. 커밋 8개, L2 스모크 전 페이지 200(errer JSTL taglib 결함 포착·수정). 인터랙티브 제출·다크모드는 needs_user_verification.
|
- [2026-06-30-visual-redesign-bibimbap-css.md](./2026-06-30-visual-redesign-bibimbap-css.md) — 클로드 디자인 리디자인 비주얼 레이어 9단위 반영. bibimbap.css 신설 + theme-init 공통 link + 빈상태 fragment 2종 + 홈/인증/약관/프로필/에러/모집 마크업·클래스 이식. game-detail no-op. 커밋 8개, L2 스모크 전 페이지 200(errer JSTL taglib 결함 포착·수정). 인터랙티브 제출·다크모드는 needs_user_verification.
|
||||||
- [2026-06-30-ui-multiaxis-ratio-visibility-fix.md](./2026-06-30-ui-multiaxis-ratio-visibility-fix.md) — 리디자인 미해결 시각 결함 2건(이미지#2 게임 카드 "비율" / 이미지#3 모집 빈상태 "가시성") 수정. "비율"을 카드 종횡비 단일 축으로 조기 협소화 → 사용자가 검색바 정렬(레이아웃 비율) 축 직접 교정 → multi-axis 멀티에이전트 평가(관찰1→레이아웃비율·카드종횡비·대비가시성·타이포간격 4축 병렬→종합1, 6 에이전트)로 13건 결함+카드 종횡비 결정 복원. 사용자 결정: 카드 4:3 + 전체 13건(index.jsp search-stack·card 4:3·object-position / bibimbap.css btn-ghost 대비·다크 보더·정렬칩·empty padding / recruit-list.jsp 히어로폭·eyebrow·빈상태 CTA 강등). L1 정적 + L2 런타임 스모크 라이트/다크 양화면 PASS. 커밋 9489365·c688aa6. 교훈: [verification-strategies.md](../development/verification-strategies.md) "모호한 시각 결함 어휘는 단일 결정축 협소화 전 다축 스캔 선행". 로그인 상태 스모크·실게임 가로 썸네일 4:3 크롭은 needs_user_verification.
|
- [2026-06-30-ui-multiaxis-ratio-visibility-fix.md](./2026-06-30-ui-multiaxis-ratio-visibility-fix.md) — 리디자인 미해결 시각 결함 2건(이미지#2 게임 카드 "비율" / 이미지#3 모집 빈상태 "가시성") 수정. "비율"을 카드 종횡비 단일 축으로 조기 협소화 → 사용자가 검색바 정렬(레이아웃 비율) 축 직접 교정 → multi-axis 멀티에이전트 평가(관찰1→레이아웃비율·카드종횡비·대비가시성·타이포간격 4축 병렬→종합1, 6 에이전트)로 13건 결함+카드 종횡비 결정 복원. 사용자 결정: 카드 4:3 + 전체 13건(index.jsp search-stack·card 4:3·object-position / bibimbap.css btn-ghost 대비·다크 보더·정렬칩·empty padding / recruit-list.jsp 히어로폭·eyebrow·빈상태 CTA 강등). L1 정적 + L2 런타임 스모크 라이트/다크 양화면 PASS. 커밋 9489365·c688aa6. 교훈: [verification-strategies.md](../development/verification-strategies.md) "모호한 시각 결함 어휘는 단일 결정축 협소화 전 다축 스캔 선행". 로그인 상태 스모크·실게임 가로 썸네일 4:3 크롭은 needs_user_verification.
|
||||||
- [2026-07-01-game-register-redesign-parity.md](./2026-07-01-game-register-redesign-parity.md) — `/game/new`(game-register.jsp)가 9단위 리디자인 목록에서 빠졌던 것을 사용자 지적으로 발견·보강. 실사용 스샷 대조 결과 레이아웃/토큰은 이미 sibling과 일치 — 필수(*) 표시 2건 + 미리보기 eyebrow 라벨만 `recruit-form.jsp` 패턴 재사용으로 추가(단일 JSP, 로직 무변경). 로컬 CSS 토큰 전지역 통합은 별도 과제로 이월. 재발방지로 `frontend-redesign-coverage-checklist.md` 신설 + workflow-patterns 등재. L1 362/362 PASS, L2 로그인 후 라이트/다크 육안 확인.
|
- [2026-07-01-game-register-redesign-parity.md](./2026-07-01-game-register-redesign-parity.md) — `/game/new`(game-register.jsp)가 9단위 리디자인 목록에서 빠졌던 것을 사용자 지적으로 발견·보강. 실사용 스샷 대조 결과 레이아웃/토큰은 이미 sibling과 일치 — 필수(*) 표시 2건 + 미리보기 eyebrow 라벨만 `recruit-form.jsp` 패턴 재사용으로 추가(단일 JSP, 로직 무변경). 로컬 CSS 토큰 전지역 통합은 별도 과제로 이월. 재발방지로 `frontend-redesign-coverage-checklist.md` 신설 + workflow-patterns 등재. L1 362/362 PASS, L2 로그인 후 라이트/다크 육안 확인.
|
||||||
|
- [2026-07-24-post-categories-seed-list-redesign.md](./2026-07-24-post-categories-seed-list-redesign.md) — 포스트 보드(W3-3) "카테고리별 보기" 골격 기구현 대비 유일 갭이던 `post_categories` 시드 0건 해소 + 목록 뷰 표현층 재설계. Unity 5-mirror 편집 taxonomy(공지/개발일지/쇼케이스/자료·튜토리얼/업계 소식) 멱등 시드(`db/migrations/`, dev 적용 완료·운영 반영 needs_user_verification) + `posts-list.jsp` 탭을 서수·hue rule·staggered reveal의 "편집 마스트헤드 인덱스"로 재설계(focus-visible/aria-current 결손 보강, slug XSS 화이트리스트) + 뱃지 pill 제거(hue dot로 축소) + 빈상태 2종 분리(카테고리 0개/글 0개, 편집 톤). 기능 로직·`bibimbap.css` 불변. 세션 도중 "포스트 보드는 peer-to-peer 아닌 편집형 일방 발행" 사용자 정정으로 카테고리 축을 상호작용축에서 편집 taxonomy 로 재설계. L1 362/362 GREEN(베이스라인 컴파일 RED 복구 포함), 정적 AC 9/9 + 코드리뷰 AC 2/2 PASS, 실화면 확인. 모바일/라이트테마 육안·운영 시드 반영은 needs_user_verification.
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
| `game-detail.jsp` | no-op(토큰 일치 확인) | 위 문서 단위7 |
|
| `game-detail.jsp` | no-op(토큰 일치 확인) | 위 문서 단위7 |
|
||||||
| `errer.jsp` | 완료 | 위 문서 단위8 |
|
| `errer.jsp` | 완료 | 위 문서 단위8 |
|
||||||
| `recruit-form.jsp` | 완료 | 위 문서 단위9 + token-coherence-fix |
|
| `recruit-form.jsp` | 완료 | 위 문서 단위9 + token-coherence-fix |
|
||||||
| `posts-list.jsp` | 완료 | 위 문서 단위2(빈상태 fragment) |
|
| `posts-list.jsp` | 완료 | 위 문서 단위2(빈상태 fragment) + [2026-07-24-post-categories-seed-list-redesign.md](../changes/2026-07-24-post-categories-seed-list-redesign.md)(탭 인덱스 재설계·뱃지 pill 제거·빈상태 2종 분리) |
|
||||||
| `recruit-list.jsp` | 완료 | 위 문서 단위2(빈상태 fragment) + [2026-06-30-ui-multiaxis-ratio-visibility-fix.md](../changes/2026-06-30-ui-multiaxis-ratio-visibility-fix.md) |
|
| `recruit-list.jsp` | 완료 | 위 문서 단위2(빈상태 fragment) + [2026-06-30-ui-multiaxis-ratio-visibility-fix.md](../changes/2026-06-30-ui-multiaxis-ratio-visibility-fix.md) |
|
||||||
| `game-register.jsp` | 완료 | **9단위 리스트에서 누락 확인 후 처리(20260701)** — `/game/new` 신규 게임 등록. 사용자 지적으로 발견. 로그인 후 실사용 스샷 대조 결과 카드/사이드미리보기 레이아웃·토큰은 이미 sibling(recruit-form 등)과 일치 — 차이 2건만 보강: 필수(*) 표시(게임 이름, WebGL zip — editMode 시 zip 비필수 반영) + 미리보기 패널 "미리보기" eyebrow 라벨. `recruit-form.jsp`의 `.rf-req`/`.rf-preview__label` 패턴 그대로 재사용, 로컬 CSS 변수 관례는 sibling과 동일하게 유지(전지역 통합은 [analysis/2026-06-30-frontend-component-review.md](../analysis/2026-06-30-frontend-component-review.md) P0 별도 과제). |
|
| `game-register.jsp` | 완료 | **9단위 리스트에서 누락 확인 후 처리(20260701)** — `/game/new` 신규 게임 등록. 사용자 지적으로 발견. 로그인 후 실사용 스샷 대조 결과 카드/사이드미리보기 레이아웃·토큰은 이미 sibling(recruit-form 등)과 일치 — 차이 2건만 보강: 필수(*) 표시(게임 이름, WebGL zip — editMode 시 zip 비필수 반영) + 미리보기 패널 "미리보기" eyebrow 라벨. `recruit-form.jsp`의 `.rf-req`/`.rf-preview__label` 패턴 그대로 재사용, 로컬 CSS 변수 관례는 sibling과 동일하게 유지(전지역 통합은 [analysis/2026-06-30-frontend-component-review.md](../analysis/2026-06-30-frontend-component-review.md) P0 별도 과제). |
|
||||||
| `posts-detail.jsp` | 미착수 | — |
|
| `posts-detail.jsp` | 미착수 | — |
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,8 @@
|
||||||
|
|
||||||
**신규 컨트롤러/매퍼 의존 변경 시 full test 의무**: 신규 컨트롤러를 추가하거나 컨트롤러의 매퍼 의존을 늘리면 implementation 단계에서 `test-compile` 만으로 끝내지 말고 반드시 full `./mvnw -o test` 를 실행한다. `BibimbapApplicationTests` 는 MyBatis/DataSource autoconfigure 가 exclude 된 컨텍스트라 컨트롤러가 주입하는 매퍼마다 `@MockBean` 을 수동 등록해야 하며, 누락 시 `contextLoads` 가 `NoSuchBeanDefinitionException` 으로 실패한다. 이 회귀는 `test-compile` 로는 탐지되지 않는다(W3-2 세션 20260618 실증).
|
**신규 컨트롤러/매퍼 의존 변경 시 full test 의무**: 신규 컨트롤러를 추가하거나 컨트롤러의 매퍼 의존을 늘리면 implementation 단계에서 `test-compile` 만으로 끝내지 말고 반드시 full `./mvnw -o test` 를 실행한다. `BibimbapApplicationTests` 는 MyBatis/DataSource autoconfigure 가 exclude 된 컨텍스트라 컨트롤러가 주입하는 매퍼마다 `@MockBean` 을 수동 등록해야 하며, 누락 시 `contextLoads` 가 `NoSuchBeanDefinitionException` 으로 실패한다. 이 회귀는 `test-compile` 로는 탐지되지 않는다(W3-2 세션 20260618 실증).
|
||||||
|
|
||||||
|
**기존 컨트롤러/서비스 시그니처 변경도 full test 의무 (위 조항의 스코프 확장)**: 위 조항은 문구상 "신규" 컨트롤러에 초점이나, **기존** 컨트롤러/서비스의 public 메서드 시그니처를 바꾸는 커밋(파라미터 추가·순서 변경 등)도 동일하게 같은 커밋에서 full `./mvnw -o test` 로 마감한다. 호출측 테스트가 구 시그니처로 남으면 `test-compile` 단계 BUILD FAILURE 로 저장소가 baseline RED 가 되고, 이후 무관 세션이 이를 떠안는다(rollback 판정·복구 비용 유발). 시그니처를 바꾼 커밋과 **같은 커밋**에 호출측 테스트를 갱신하는 것이 규칙이다(근거: 세션 20260724 — 선재 커밋 `02a1b75` 가 `PostController.list` 에 categoryId/HttpSession 을 추가하며 `PostControllerTest` 3개 콜사이트를 미갱신 → 무관한 카테고리 세션의 verification-advisor 가 baseline RED 를 발견·최소수정 복구).
|
||||||
|
|
||||||
**mock-vs-reality: DB-방언 계약은 L1 으로 못 잡는다**: 컨트롤러/애플리케이션 단위테스트가 MyBatis 매퍼를 `@MockBean` 으로 대체하면 "내가 부른 메서드가 불렸나"만 보고 "실제 Postgres 가 그 SQL 을 우리 기대대로 해석하나"는 검증하지 못한다. L1 통과율이 높을수록 DB-통합 계약 공백이 오히려 숨는다. W3-2 세션 20260622 에서 L1 43/43 GREEN 을 통과한 채 누출된 실버그 2종:
|
**mock-vs-reality: DB-방언 계약은 L1 으로 못 잡는다**: 컨트롤러/애플리케이션 단위테스트가 MyBatis 매퍼를 `@MockBean` 으로 대체하면 "내가 부른 메서드가 불렸나"만 보고 "실제 Postgres 가 그 SQL 을 우리 기대대로 해석하나"는 검증하지 못한다. L1 통과율이 높을수록 DB-통합 계약 공백이 오히려 숨는다. W3-2 세션 20260622 에서 L1 43/43 GREEN 을 통과한 채 누출된 실버그 2종:
|
||||||
- **SQL alias 케이스 폴딩**: 따옴표 없는 `AS gameId` 는 Postgres 가 소문자(`gameid`)로 폴딩 → MyBatis `Map` 키가 소문자 → 컨트롤러의 camelCase `get("reviewCount")` 가 null → 집계 summary 전면 무력. camelCase 키가 필요하면 alias 를 **반드시 큰따옴표**(`AS "gameId"`)로 감싼다.
|
- **SQL alias 케이스 폴딩**: 따옴표 없는 `AS gameId` 는 Postgres 가 소문자(`gameid`)로 폴딩 → MyBatis `Map` 키가 소문자 → 컨트롤러의 camelCase `get("reviewCount")` 가 null → 집계 summary 전면 무력. camelCase 키가 필요하면 alias 를 **반드시 큰따옴표**(`AS "gameId"`)로 감싼다.
|
||||||
- **집계 뷰 1:N LEFT JOIN fan-out**: 부모(리뷰)에 자식(축 평점) 직접 JOIN 시 `COUNT(*)`/`AVG` 가 자식 행수만큼 왜곡(자식 0행일 땐 잠복, 입력 순간 발현). 집계는 자식을 서브쿼리에서 선집계 후 LEFT JOIN 한다.
|
- **집계 뷰 1:N LEFT JOIN fan-out**: 부모(리뷰)에 자식(축 평점) 직접 JOIN 시 `COUNT(*)`/`AVG` 가 자식 행수만큼 왜곡(자식 0행일 땐 잠복, 입력 순간 발현). 집계는 자식을 서브쿼리에서 선집계 후 LEFT JOIN 한다.
|
||||||
|
|
|
||||||
|
|
@ -74,3 +74,9 @@ CSS 위계·여백·색 같은 시각 변경은 before→after **텍스트 표**
|
||||||
`needs_user_verification` 으로 브라우저 육안 확인을 이월할 때, 변경 대상이 최근 세션에서 docs 화된 렌더링 함정과 같은 경로(JSP/정적 asset)를 공유하면 그 문서를 명시적으로 인용한다. 단순히 "브라우저에서 육안 확인" 이라고만 적으면, 다음 세션/사용자가 이미 알려진 함정(예: JSP stale 렌더링 — `local-dev-setup.md` §JSP/정적)을 다시 밟고도 브라우저 화면만 보고 오탐(false pass) 할 수 있다. 작성 규칙: 최근 work-session 3~5개 이내 docs 반영된 구조적 함정 중 렌더 경로가 겹치는 것이 있으면 `needs_user_verification` 항목에 "known pitfall: `<문서 링크>` — curl 로 서빙값 대조 선행 권고" 를 병기한다.
|
`needs_user_verification` 으로 브라우저 육안 확인을 이월할 때, 변경 대상이 최근 세션에서 docs 화된 렌더링 함정과 같은 경로(JSP/정적 asset)를 공유하면 그 문서를 명시적으로 인용한다. 단순히 "브라우저에서 육안 확인" 이라고만 적으면, 다음 세션/사용자가 이미 알려진 함정(예: JSP stale 렌더링 — `local-dev-setup.md` §JSP/정적)을 다시 밟고도 브라우저 화면만 보고 오탐(false pass) 할 수 있다. 작성 규칙: 최근 work-session 3~5개 이내 docs 반영된 구조적 함정 중 렌더 경로가 겹치는 것이 있으면 `needs_user_verification` 항목에 "known pitfall: `<문서 링크>` — curl 로 서빙값 대조 선행 권고" 를 병기한다.
|
||||||
|
|
||||||
> 근거: 세션 20260701-100731 — game-detail.jsp 레이더 차트 데이터(6축 axis) 백필 세션. 직전 세션(20260701-093754, 4분 전 종료)이 방금 "JSP 저장 즉시반영 실패 → 브라우저가 stale 렌더링을 정상으로 오판" 함정을 `local-dev-setup.md` 에 반영했음에도, 같은 렌더 경로(JSP)를 다루는 후속 세션의 `needs_user_verification` 이 이를 인용하지 않아 재발 위험을 남김(retrospective-advisor 포착).
|
> 근거: 세션 20260701-100731 — game-detail.jsp 레이더 차트 데이터(6축 axis) 백필 세션. 직전 세션(20260701-093754, 4분 전 종료)이 방금 "JSP 저장 즉시반영 실패 → 브라우저가 stale 렌더링을 정상으로 오판" 함정을 `local-dev-setup.md` 에 반영했음에도, 같은 렌더 경로(JSP)를 다루는 후속 세션의 `needs_user_verification` 이 이를 인용하지 않아 재발 위험을 남김(retrospective-advisor 포착).
|
||||||
|
|
||||||
|
## 도메인/상호작용 모델은 UI taxonomy 가정 전에 근거 문서·enum 으로 접지
|
||||||
|
|
||||||
|
기능의 상호작용 모델(예: 게시판이 제작자 peer-to-peer 포럼인가 편집형 일방 발행인가)이 UI 구조(카테고리 축·CTA 노출·인터페이스 톤)를 좌우할 때, orchestrator 는 **가정하지 말고** 착수 시 (a) 관련 permission enum(`PermissionKeys.java` — 예: `POST_WRITE` 게이트 존재 = 발행 권한자 한정 = 일방 발행 신호), (b) `docs/work-log/` 의 해당 기능 골자 문서를 먼저 확인한다. 모델을 잘못 가정하면 요구분해·설계 초반이 어긋나 사용자 정정 왕복(AskUserQuestion 다회)이 발생한다. §5.0 계획 가시성 이전에 "모델 접지" 를 둔다.
|
||||||
|
|
||||||
|
> 근거: 세션 20260724 — 포스트 보드를 peer-to-peer 커뮤니티로 가정해 카테고리 축에 '피드백요청/자유'(상호작용축)를 배치했으나, 사용자 정정으로 '편집형 일방 발행'(Unity 블로그 모델)로 뒤집혀 편집 taxonomy 로 재설계(AskUserQuestion 3회 왕복). 그러나 `POST_WRITE` 게이트와 `docs/work-log/2026-06-17-w3-feature-skeletons.md`('포스터 권한자만 작성, 일반 유저 읽기만(댓글 없음)')에 이미 일방 발행 모델이 W3-3 설계 시점(2026-06-23)에 명문화돼 있어 docs-first 로 조기 포착 가능했다(documentation-advisor 도 "새 결정이 아니라 orchestrator 가정 오류의 재확인" 으로 판정).
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue