16 KiB
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 |
|
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— accessorgetName()/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-statefragment 마크업. - 이식 금지/교정:
${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
_csrfline 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/providerhidden 없음 → 기존 signup 필수 hidden 누락.- 이식 가능(비주얼 전용):
.pw-field+.pw-toggle마크업·인라인 onclick(02-login 29-33),.pw-meterdata-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 attrmyGames(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_codescriptlet(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
#e8a54bvs bibimbap.css--color-accent #D4A853.
횡단 패턴 (확인됨)
- 모든 디자인 산출물이 자체 head 미보유 +
common/include 경로 사용 → 기존 자체-head 구조에 "그대로" 붙일 수 없음. body 내부 마크업만 추출해 기존 head/include 유지한 페이지에 이식. - 모든 디자인 form이 CSRF hidden·AJAX 로직 미보유(native POST) → 어떤 form 태그도 통째 이식 금지. 제출 로직(AJAX+BibimbapCsrf)은 전 페이지 보존 필수.
- 모델 바인딩 이름 불일치 다발: 디자인 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 토큰 처리(라이트 고정) — 디자인 의도 확인 필요(조사 범위 밖, 설계 결정).