bibimbap/.atp/work-session/20260630-160000/research/mapping-spec.md

16 KiB

phase agent agent_version generated_at concerns concerns_checked source_confidence workers_spawned
research research-advisor 2 2026-06-30T16:00:00+09:00
low source confidence — errer.jsp의 error-page 매핑(web.xml/ErrorConfig) 및 pageContext.errorData 노출 가능 여부 미확인. error/error.jsp의 ${pageContext.errorData.statusCode} 바인딩이 현 Tomcat/Spring 구성에서 동작하는지 검증 전 권위 데이터(시드·계약)로 승격 금지.
디자인 산출물 다수가 include 경로 '/WEB-INF/views/common/header.jsp'를 참조하나 현 프로젝트는 'common/' 서브디렉터리가 없음(header.jsp는 /WEB-INF/views/ 직하). 이식 시 경로 전부 교정 필요 — 디자인 마크업을 '있는 그대로' 복사 금지.
true mixed 5

조사 결과 — 비주얼 레이어 이식 mapping-spec

주제

디자인 산출물(외부 클로드 디자인 툴 결과물)을 기존 JSP에 "CSS 클래스/마크업 구조만" 이식하기 위한 페이지별 (a)모델 속성명 (b)CSRF 패턴 (c)head/include 구조 (d)제출 방식 (e)안전 이식 가능 블록 추출.

근거: 각 항목 file:line 인용은 parallel-explorer 5개(G1~G5)가 전문 읽기로 확인. 라우트/필드 바인딩은 advisor가 controller 직접 grep으로 교차검증.


공유 인프라 (G1) — 확인됨

theme-init.jsp (32줄)

  • 독립 <head> 조각. <link>/<style> 없음. 포함: <meta name="csrf-token">(line 7) + <script>(테마 FOUC 방지 + window.BibimbapCsrf 전역 등록, line 19-30).
  • window.BibimbapCsrf는 여기서 정의됨 — 모든 AJAX CSRF 헤더의 출처. (확인됨)
  • views 디렉터리 27개 JSP 중 공유조각 4개를 제외한 23개 전원이 theme-init.jsp를 include, 미포함 0건. (확인됨, grep)

header.jsp (250줄)

  • 자체 <style>(14-194). header 전용 CSS 변수는 .site-header 스코프에서만 선언. BEM 클래스(.site-header__*) 일관. (확인됨)
  • contextPath = ${pageContext.request.contextPath} (line 197). ${ctx} 별칭 정의 없음. (확인됨)
  • nav = <nav class="site-header__nav"> + 플랫 <a class="site-header__nav-link">, ul 없음. (line 201-203)
  • window.BibimbapCsrf 정의 없음(theme-init 소유). modal.jsp include(line 231).

footer.jsp (96줄)

  • 자체 <style>(2-77), script 없음. .site-footer__* 클래스. 링크색 #e8a54b 하드코딩(line 43) — bibimbap.css --color-accent: #D4A853와 불일치(토큰 정합성 관찰). (확인됨)

bibimbap.css (352줄) — 디자인 산출물

  • 상단 주석(1-5): "외부 라이브러리 없음·순수 CSS". @import / @font-face / CDN url() 없음. 폰트는 로컬 'Pretendard Variable' 의존. (확인됨)
  • :root 변수 24개(8-41): --color-*, --radius-*, --font-sans/mono. 다크 모드 오버라이드 블록 없음(라이트 고정 토큰). (확인됨)
  • 레이아웃 클래스: .page .btn .btn-primary .field .input .textarea .card .empty-state .game-grid .game-card .profile-head .avatar .terms .terms-toc .terms-body .form-grid .form-actions 등.
  • .site-header*/.site-footer*를 정의하지 않음 → header/footer와 직접 클래스 충돌 없음. (확인됨)
  • 주석 line 4 예시는 ${ctx}/css/bibimbap.css 사용 — 현 코드베이스 패턴(${pageContext.request.contextPath})과 다름. link 작성 시 교정 필요.

G2 홈 + 빈상태

index.jsp (기존)

  • 게임 모델: GameData — accessor getName()/getCreator()/getThumbnailUrl() (확인됨, GameData.java:40/48/80). → EL은 ${game.name} ${game.creator} (디자인의 ${game.title}/g.author는 불일치).
  • 자체 head 보유 + theme-init/header/footer include.
  • 게임 등록 라우트 = /game/new 단수 (확인됨, WebMvcController.java:62 / GameController.java:67). 디자인의 /games/new 복수는 불일치.

recruit-list.jsp / posts-list.jsp (기존)

  • /recruit/new (RecruitController.java:41/49) · /posts/new (PostController.java:99) 둘 다 실재 확인됨. 디자인 CTA href는 이 값으로 교정.
  • 빈 상태 분기 위치에 디자인 fragments(recruit-empty.jspf, posts-empty.jspf) include 가능 — 단, fragments는 디자인 클래스(.empty-state)를 쓰므로 bibimbap.css 도입이 선행돼야 시각 정상.

01-home.jsp + fragments 3종 (디자인)

  • 이식 가능: 히어로 섹션, 정렬칩, .game-card/.game-grid 외형 클래스, .empty-state fragment 마크업.
  • 이식 금지/교정: ${game.title}${game.name}, ${game.author}${game.creator}, /games/new/game/new. 모델 바인딩 이름을 디자인 그대로 쓰면 빈값 렌더.

G3 인증 (login / signup)

login.jsp (기존) — 확인됨

  • 제출: form action="…/login" 선언되나 submit 인터셉트 후 AJAX fetch POST /login (line 272-273), 성공 시 location.href=ctx+'/'.
  • CSRF 이중 방어: hidden <input name="_csrf">(line 224) + window.BibimbapCsrf.headers()(line 286-294).
  • 필드 name: email(227), password(231), remember(235).
  • 에러 표시: 인라인 DOM 슬롯 없음 → window.BibimbapModal.alert() 호출.
  • 자체 head 보유(4-6) + theme-init(9)/header(208).

signup.jsp (기존) — 확인됨

  • 제출: AJAX fetch POST /signup, 성공 시 BibimbapModal.alert 후 /login 이동.
  • CSRF 이중 방어 동일(hidden _csrf line 222 + headers line 300-306).
  • 필드 name: displayName(227), email(231), password(236), passwordConfirm(240), termsAccepted(244), provider(hidden 223), providerUserId(hidden 224, submit 직전 email 복사 line 293).
  • 클라이언트 비밀번호 일치 검증 JS 없음(checkValidity만, line 285-293).

02-login.jsp / 03-signup.jsp (디자인)

  • 자체 head 없음(fragment). include가 common/header.jsp(불일치 경로).
  • CSRF hidden input 전무 + submit 인터셉트 JS 전무 → native POST. form 태그 통째 이식 금지 (CSRF 누락·AJAX 로직 상실).
  • 약관 체크박스 name=agree(03-signup line 55) — 서버 기대값 termsAccepted와 불일치 → 그대로 이식 시 약관 동의 유실.
  • providerUserId/provider hidden 없음 → 기존 signup 필수 hidden 누락.
  • 이식 가능(비주얼 전용): .pw-field+.pw-toggle 마크업·인라인 onclick(02-login 29-33), .pw-meter data-level 강도막대+oninput(03-signup 34-36), #pw-ok 일치표시+oninput(43-45, id를 기존 필드 id에 맞춰 교정), .form-error/.form-ok/.help 슬롯.
  • 이식 금지: form 태그/action/method, common/ include, name=agree, native POST.
  • /password/reset 링크(02-login line 27): 기존 login.jsp에 없음. 라우트 미존재(grep 무수확) → 링크 비노출 권고. (확인됨: 컨트롤러에 매핑 없음)

G4 약관 + 프로필

terms.jsp (기존) — 확인됨

  • 조항 본문 전량 하드코딩(모델 바인딩 없음). <article class="policy-doc"> > <section class="policy-section"> + <h2>+<ol><li> (99-191).
  • 자체 head 보유(4-94, 인라인 style 포함) + theme-init(7)/header(97)/footer(193).

07-terms.jsp (디자인)

  • sticky 목차 <nav class="terms-toc">(17-29, 신규 블록), 조항 <div class="terms-body"> <section id="aN"> <h3><p><br>(32-94). 자체 head 없음, common/ include.
  • 이식 가능: .terms/.terms-toc/.terms-body 래퍼·TOC(신규 추가). 마크업 변환 필요: <section class="policy-section">+<ol><li><section id="aN">+<p>, <h2><h3>.
  • 이식 금지/교정: 자체 head 미보유분은 기존 head 유지, common/ 경로 교정.

profile.jsp (기존) — 확인됨

  • 모델: session attr displayName/email/avatarUrl(17-19), request attr myGames(27-28)/myBadges(31-36). scriptlet 방식 — 디자인의 EL ${user} 객체와 다름.
  • CSRF: hidden input 없음, window.BibimbapCsrf.headers() 헤더 주입만(634-645, 699-708).
  • AJAX 함수: submitNickname()(619, POST /profile/nickname), uploadAvatar()(692, POST /profile/avatar).
  • 썸네일 경로: rawThumbUrl.startsWith("/") ? ctx+rawThumbUrl : rawThumbUrl(491-493).
  • 공개/비공개 토글 UI·API가 기존엔 전무. (확인됨)

08-profile.jsp (디자인)

  • .profile-head 헤더(17-24), .game-row+.thumb+.meta(39-58), 공개토글 <form action="${ctx}/games/${g.id}/visibility"> <label class="switch"><input type=checkbox onchange="this.form.submit()">(46-53).
  • 공개토글 엔드포인트 /games/{id}/visibility 현 컨트롤러에 미존재(grep 무수확; 유사한 /admin/jams/{jamId}/visibility만 존재 — 동명 경로이나 별개 엔티티/권한, 혼동 금지). 이 토글은 백엔드 신설 없이는 비주얼만 이식 불가 → 비주얼 레이어 범위 밖. (확인됨)
  • /profile/edit 링크(08-profile line 23): 기존 인라인 편집(닉네임/아바타)만 있고 라우트 미존재(grep 무수확). 비노출 또는 백엔드 결정 필요.
  • 이식 가능: .profile-head·.game-row 외형(단 EL ${user.*}→scriptlet 변수 교정, ${myGames} 게임 필드는 getName()/getThumbnailUrl() 사용).
  • 이식 금지(보존 필수): submitNickname()/uploadAvatar() fetch + window.BibimbapCsrf 헤더, id="profile-avatar-img"/profile-avatar-initial (JS 참조), /games/.../visibility 토글(백엔드 부재).

G5 게임상세 + 모집 + 에러

game-detail.jsp (기존, 2385줄) — 블록 매핑 (확인됨)

블록 line 보존필수
scriptlet 모델바인딩(XSS escape) 1-22 필수
head + 인라인 CSS(토큰 40여개) 23-1203 CSS 교체가능
body+header include 1204-1205 이식가능
topbar(뒤로/owner액션) 1206-1218 이식가능(클래스만)
메타카드(제목/번호/제작자/좋아요) 1220-1256 이식가능(id 유지)
WebGL iframe 1258-1276 필수(sandbox/allow/scriptlet src 불변)
제작자 한마디 패널 1279-1296 이식가능
리뷰 패널 마크업 1298-1347 필수(id 20여개 JS참조)
덧글 패널 마크업 1349-1381 필수(id JS참조)
IIFE 스크립트(CRUD/CSRF/6축레이더/별점) 1387-2382 필수 전체보존
  • CSRF: baseHeaders()/formHeaders() 공통 헬퍼가 window.BibimbapCsrf.headers() 중앙화(1562-1569).
  • 리뷰 CRUD: reviewsUrl = ctx+'/game/'+gid+'/reviews'(2021), GET/POST/PUT/DELETE(2306/2375/2236). 덧글: commentsUrl=…/comments(1800), POST/PUT/DELETE(1977/1845/1901).
  • 6축: AXIS_KEYS=[immersion,creativity,controls,completeness,sound,visual](1508), buildHexRadar()(1607-1702) SVG 동적생성, buildStarRadioGroup()(1731-1786).
  • 보존필수 id 목록: game-review-composer/form/axes/stars/list, game-reviews-summary-panel/radar/empty/toolbar/more, game-comment-form/input/list/composer/login-gate, game-comments-toolbar/more, game-like-btn/count.

06-game-detail.jsp (디자인)

  • 자체 head 없음, EL ${game.*}, WebGL iframe sandbox 속성 없음(보안 후퇴 — 절대 이 형태로 교체 금지), 6축은 <div class="help">6축 평가 포함</div> 플레이스홀더(빌더 없음), 리뷰는 <c:forEach> 서버렌더(기존 AJAX 아님), 좋아요는 native form POST.
  • 결론: game-detail은 통째 교체 절대 금지. 표층 CSS 클래스 토큰 교체와 topbar/메타카드/제작자패널 외형만 선별 이식. 디자인의 WebGL·6축·리뷰 구현은 모두 기능 후퇴이므로 버린다.

recruit-form.jsp (기존, 450줄) — 확인됨

  • 제출: action="…/recruit/new" novalidate + AJAX fetch POST(376-395), window.BibimbapCsrf.headers().
  • 필드 name(controller @RequestParam로 교차확인): projectName(260) genre(264) summary(268) role(272) status(280) type(289) period(298) team(302) contact(306) description(310). (RecruitController.java:52-61에서 동일 name 수신 확인됨)
  • 글자수 카운터 JS 없음(maxlength만). 미리보기 render() IIFE(338-446).

10-recruit-form.jsp (디자인)

  • native form POST action="${ctx}/recruit"(line 18, CSRF 없음·엔드포인트 불일치 — 기존은 /recruit/new), 필수표시 <span class="req">*</span>, 글자수카운터 summary만 인라인 oninput(33-35), 필드 name="title"(22)는 서버 기대 projectName불일치(확인됨).
  • 이식 가능: .req 필수표시, .count/#sc 글자수 마크업+인라인 oninput(독립적), .form-page/.field 외형.
  • 이식 금지: form action/method(엔드포인트 /recruit ≠ /recruit/new, CSRF 누락), name="title"(→projectName 유지), AJAX submit·미리보기 render() 보존.

errer.jsp (기존, 232줄) — 확인됨 / error/error.jsp (디자인) — 미확인 부분 존재

  • errer.jsp: isErrorPage=true, jakarta.servlet.error.status_code scriptlet(190)으로 코드 읽음, ${statusCode}/${status} EL 미사용. statusCode!=null 단일 체크(203-208), 코드별 분기 없음. requestUri/message 상세 블록 보유.
  • error/error.jsp(디자인): ${pageContext.errorData.statusCode}<c:set var=code>(19) + <c:choose> 403/otherwise(404) 분기(27/44). 상세정보 블록 없음.
  • 미확인: web.xml/ErrorConfig의 error-page 매핑 위치(grep 무수확), pageContext.errorData가 현 구성에서 노출되는지. → 디자인 EL 바인딩 채택 전 검증 필요. concern 등록함.
  • 이식 가능: SVG 아이콘/버튼/<c:choose> 분기 외형. 주의: 코드 바인딩 방식 변경(scriptlet→errorData EL)은 동작 검증 후. 상세정보(requestUri/message) 노출은 정책 결정 사항.

종합 판단

bibimbap.css 도입 지점 결론 (단일 지점 추천)

theme-init.jsp에 <link rel="stylesheet" href="${pageContext.request.contextPath}/css/bibimbap.css"> 1줄 추가가 유일한 단일 지점이다. 23개 전 페이지가 예외 없이 theme-init을 include하므로 전역 적용된다(확인됨). header/footer는 include 체인 중간이라 단일 지점 불가. CSS 자산은 src/main/webapp/css/에 두면 Tomcat DefaultServlet이 서빙(CLAUDE.md 정책 부합). 단 디자인 주석의 ${ctx} 별칭은 현 코드에 없으므로 ${pageContext.request.contextPath}로 작성.

CSS 클래스 네임스페이스 충돌 요약

  • bibimbap.css는 .site-header*/.site-footer*를 정의하지 않음 → header/footer 인라인 스타일과 클래스 직접 충돌 없음.
  • 잠재 충돌: 다크 모드 이중 체계. header/footer는 html[data-theme="dark"] .site-* 자체 변수, bibimbap.css :root는 다크 오버라이드 없는 라이트 고정 토큰. 도입 후 body 컴포넌트(라이트 고정) vs header/footer(다크 대응)가 공존 → 다크 모드에서 body 영역이 라이트로 남는 시각 불일치 가능. (확인됨, 디자인 의도 확인 필요)
  • 색상 토큰 불일치: footer #e8a54b vs bibimbap.css --color-accent #D4A853.

횡단 패턴 (확인됨)

  1. 모든 디자인 산출물이 자체 head 미보유 + common/ include 경로 사용 → 기존 자체-head 구조에 "그대로" 붙일 수 없음. body 내부 마크업만 추출해 기존 head/include 유지한 페이지에 이식.
  2. 모든 디자인 form이 CSRF hidden·AJAX 로직 미보유(native POST) → 어떤 form 태그도 통째 이식 금지. 제출 로직(AJAX+BibimbapCsrf)은 전 페이지 보존 필수.
  3. 모델 바인딩 이름 불일치 다발: 디자인 EL(game.title/author/user//games/new//recruit/name=title/name=agree) vs 기존(name/creator/scriptlet session//game/new//recruit/new/projectName/termsAccepted). 이름 그대로 이식 시 빈 렌더·필드 유실.

미해결

  • errer.jsp error-page 매핑 위치 및 pageContext.errorData 노출 가능 여부 (web.xml/Java config 미확인 — concern 등록). 디자인 error EL 채택 전 검증 필요.
  • /games/{id}/visibility(프로필 공개토글), /profile/edit, /password/reset 라우트 부재(grep 확인) → 디자인이 제안하나 백엔드 신설 없이는 비주얼 이식 무의미. 비주얼 레이어 범위 밖 — 노출 정책은 design/orchestrator 결정 사항.
  • bibimbap.css 도입 후 다크 모드 body 토큰 처리(라이트 고정) — 디자인 의도 확인 필요(조사 범위 밖, 설계 결정).