bibimbap/docs/analysis/2026-06-30-frontend-compone...

16 KiB

kind title description perspective valid_starting_point_for owner stability last_reviewed source superseded_note
analysis 프론트엔드 컴포넌트 중복/유지보수·확장성 검토 (2026-06-30) JSP 기반 프론트엔드에서 발견된 CSS 토큰 23개 파일 중복, JS 유틸리티 함수 중복, BibimbapModal 호출 패턴 혼존, recruit-form CSRF 폴백 갭(보안), 날짜 포맷 함수 편재를 분석하고 권고 구현 순서를 기록한다. neutral 프론트엔드 중복 제거 작업 착수, CSRF 갭 수정(보안 P0), CSS/JS 공용 파일 설계 art snapshot 2026-06-30 work-session 20260630-113258 (research-advisor frontend-analysis.md, design-advisor frontend-design-recommendations.md) 에서 distill 후속 결정으로 전제가 바뀌면 이 줄을 갱신한다 — 현재 없음

프론트엔드 컴포넌트 중복/유지보수·확장성 검토 (2026-06-30)

조사 범위: 27개 JSP 파일 전수 직접 읽기. 모든 사실은 코드 직접 확인(확인됨). 미탐색 항목은 미발견/조사 불충분에 명시.

출처: work-session 20260630-113258research/frontend-analysis.md (research-advisor) + artifacts/frontend-design-recommendations.md (design-advisor).

개요

JSP 기반 프론트엔드에서 5개 유형의 중복/이슈가 확인됐다. 가장 심각한 것은 라이트/다크 테마 CSS 변수 블록과 body 기본 스타일이 23개 파일에 동일하게 복사된 구조이며, 보안 측면에서는 recruit-form.jsp의 CSRF 폴백 분기에서 토큰이 전혀 포함되지 않는 갭이 존재한다.

전체 27개 JSP 중 26개에 인라인 <style> 블록이 존재하며, 누적 인라인 CSS는 5,969줄이다.

발견 이슈 요약 (우선순위 매트릭스)

이슈 영향도 구현 비용 우선순위
CSS 토큰 23개 파일 중복 높음 (유지보수 23배 비용) 낮음 (파일 추가 + include 삽입) P0
폼 제출 CSRF 갭 (recruit-form) 높음 (보안) 낮음 (10줄 이내 수정) P0 — 보안
JS 공용 유틸리티 분리 중간 (동작 불일치 방지) 중간 (파일 작성 + 기존 인라인 제거) P1
모달 패턴 표준화 중간 (신규 페이지 기준 제공) 중간 (패턴 A/B 대체 + admin 2곳 수정) P1
날짜 포맷 유틸리티 모듈 낮음 (예방적 분리) 낮음 (파일 추출) P2

이슈별 상세

CSS 디자인 토큰 23개 파일 중복 (P0)

현황

전체 27개 JSP 중 26개에 인라인 <style> 블록이 존재한다 (theme-init.jsp 제외). 다음 3개 블록이 23개 파일에 동일하게 복사된다.

중복 패턴 반복 파일 수 대표 위치
html {} 라이트 테마 CSS 변수 선언 (8개 색상 토큰) 23개 admin-console.jsp:38, jam-list.jsp:21, login.jsp:12
html[data-theme="dark"] {} 다크 테마 CSS 변수 선언 23개 admin-console.jsp:51, posts-list.jsp:41, recruit-list.jsp:34
body {} 기본 리셋 + 폰트 + 배경 23개 admin-console.jsp:62, jam-list.jsp:41, terms.jsp:29

핵심 공통 토큰: --surface, --card-bg, --text, --text-muted, --accent, --accent-soft, --border, --shadow (라이트) / #121212, #1e1e1e, #ece8e1, #a39e96, rgba(255,255,255,0.1) (다크).

추가 중복: .admin-btn / .admin-btn--primary / .admin-btn--danger 컴포넌트 CSS 20줄 이상이 4개 admin 파일에 동일 복사됨 (admin-console.jsp:171-194, admin-jam-list.jsp:167-190, admin-post-categories.jsp:100, admin-unity-feeds.jsp:108).

safe-area padding 패턴(padding: 1.5rem max(1rem, env(safe-area-inset-left)) 3rem max(1rem, env(safe-area-inset-right)))은 25개 파일에 반복되나, max-width 값은 48/56/64/72/78rem으로 파일별 상이하다.

충돌/갭

  • CSS 변수 선언이 파일마다 포함하는 변수 수가 8~14개로 약간씩 달라, 특정 파일에서 일부 변수가 미선언된 상태일 수 있다.
  • jam-detail.jsp:128recruit-detail.jsp:116.detail-button--primary color 값 불일치 (#1a1a1a vs var(--button-text)).

권고 (design-advisor)

src/main/webapp/css/global.css 정적 파일을 신규 생성하여 공통 토큰, body 기본 스타일, .admin-btn 컴포넌트를 선언한다. 각 JSP <head><link rel="stylesheet" href="${pageContext.request.contextPath}/css/global.css"> 를 삽입하고 인라인 중복 블록을 제거한다.

포함 대상: 공통 8+4개 토큰, body 기본 스타일, .admin-btn* 컴포넌트.

포함하지 않는 것: 각 JSP 고유 레이아웃 클래스, max-width 값(파일마다 다름), --danger 변수(admin-unity-feeds.jsp 단독 사용), 페이지 전용 컴포넌트 CSS.

배포 경로: src/main/webapp/ 하위 정적 파일은 Tomcat DefaultServlet이 직접 서빙한다. src/main/resources/static/ 경로 불필요.

마이그레이션 3단계: Phase 1(파일 생성 + login.jsp 1개 검증) → Phase 2(admin 4개 파일) → Phase 3(나머지 19개 JSP 일괄).

위험: game-detail.jsp처럼 --radius, --webgl-bg 등 파일 전용 변수가 있는 경우 해당 변수는 인라인 유지. FOUC는 theme-init.jsp<head> 최상단에서 data-theme 속성을 먼저 설정하므로 낮음.


JS 유틸리티 함수 중복 (P1)

현황

admin 4개 파일(admin-console.jsp, admin-jam-list.jsp, admin-post-categories.jsp, admin-unity-feeds.jsp)에 동일 패턴의 함수가 각각 선언된다.

함수 파일 영향도
notify(message) admin 4개 파일 각각 선언 Medium
post(url, params) 동일 4개 파일. 구현이 약간씩 다름 (params 처리, _csrf 추가 여부) High
handleResult(res), handleError() 동일 4개 파일 동일 패턴 Medium

특히 post() 함수는 파일마다 구현이 미묘하게 달라 동작 불일치 위험이 내재한다.

권고 (design-advisor)

src/main/webapp/js/bibimbap-utils.js 신규 생성. window.BibimbapUtils 네임스페이스에 post(url, params, csrfToken), handleResult(res, onError), makeErrorHandler(onError) 를 노출한다.

시그니처 변경 이유:

  • post(url, params, csrfToken): 기존 클로저 캡처 방식 대신 토큰을 명시 파라미터로 받는다.
  • handleResult(res, onError): 기존의 notify() 직접 호출 대신 오류 메시지 콜백으로 위임한다 (title이 파일마다 달라 전역화 불가).
  • makeErrorHandler(onError): 매직 문자열 하드코딩 제거.

notify() / openModal() 통합 불가: title이 파일마다 달라 단순 통합이 불가능하므로 권고 3(모달 표준화)에서 별도 처리.

위험: admin 파일마다 post() 구현이 미묘하게 달라 통합 시 동작 변경 가능. 통합 전 4개 파일 3-way diff 수행 권고.


BibimbapModal 호출 패턴 혼존 (P1)

현황

BibimbapModal API는 modal.jsp:174-238에 단일 <div id="site-modal"> 재사용 방식으로 구현되어 있으며, alert, confirm, prompt 메서드를 제공한다. 그러나 호출 패턴이 3가지로 혼존한다.

패턴 A — openModal() 헬퍼 래퍼 (4개 파일)

login.jsp:256, signup.jsp:269, game-register.jsp:490, profile.jsp:539 가 동일 시그니처의 openModal(title, message, confirmText, onConfirm) 로컬 함수를 각자 정의.

패턴 B — notify() 헬퍼 래퍼 (4개 admin 파일)

admin-jam-list.jsp:389, admin-console.jsp:401, admin-post-categories.jsp:251, admin-unity-feeds.jsp:305notify(message) 로컬 함수를 각자 정의 (alert 전용, title 하드코딩).

패턴 C — 인라인 직접 체크 (3개 파일)

recruit-form.jsp:411, 424, posts-form.jsp:252, 269, game-detail.jsp:1425, 1439, 1474, 1482, 1553, 1558 가 호출 지점마다 인라인으로 체크.

특이 케이스: game-detail.jsp:1553-1561notifyError(), confirmAction() 헬퍼를 별도 정의하면서, 동일 파일 1425-1449에 인라인 체크도 병존하여 단일 파일 내 두 패턴이 혼재한다.

window.confirm 직접 사용: admin-post-categories.jsp:323admin-unity-feeds.jsp:401에서 삭제 확인 대화상자에 window.confirm()을 직접 사용. 알림에는 notify() 헬퍼(BibimbapModal 체크)를 쓰면서 삭제 확인에는 직접 사용하는 동일 파일 내 불일치.

권고 (design-advisor)

표준 패턴: window.BibimbapModal.alert / .confirm / .prompt 직접 호출. 기존 헬퍼 래퍼는 신규 작성 금지.

  • 패턴 A: login/signup/game-register/profile 4개 파일의 openModal() 제거 → 직접 호출로 대체.
  • 패턴 B: admin 4개 파일의 notify() 제거 → 권고 2의 BibimbapUtils.handleResult(res, onError)onError 콜백에서 직접 호출.
  • 패턴 C: posts-form/recruit-form/game-detail 현재 형태 유지. game-detail.jspnotifyError() / confirmAction() 파일 전용 헬퍼는 제거하지 않는다 (단일 파일 로컬 헬퍼 허용).
  • window.confirm 2곳을 BibimbapModal.confirm({...}) 으로 교체.

recruit-form CSRF 폴백 갭 (P0 — 보안)

현황

posts-form.jsprecruit-form.jsp는 fetch 기반 비동기 폼 제출, checkValidity() 검증, BibimbapModal.alert 처리, BibimbapCsrf CSRF 헤더 주입이라는 동일 골격을 공유하지만, CSRF 처리에 불일치가 존재한다.

  • posts-form.jsp:169<input type="hidden" name="_csrf" value="<%= csrfTokenHtml %>"> hidden input 존재 + JS 변수 이중 포함 (posts-form.jsp:211, 229).
  • recruit-form.jsp:386-394 — hidden CSRF input 없음. BibimbapCsrf 미존재 시 폴백 헤더에 CSRF 토큰이 전혀 포함되지 않는 보안 갭.

추가 불일치:

항목 posts-form.jsp recruit-form.jsp
성공 시 폴백 } else { go(); } (alert 없음) } else { alert('팀원 모집글이 등록되었습니다.'); go(); }
401 처리 redirectLogin 함수 분리, onConfirm에서 호출 모달 onConfirm과 else 분기 양쪽에 인라인 중복
수정 모드 mode, postId 로 등록/수정 URL 분기 있음 수정 모드 분기 없음

권고 (design-advisor)

즉시 수정 (방법 A 채택): recruit-form.jsp<input type="hidden" name="_csrf" value="..."> hidden input 추가 + JS 폴백 분기에 X-CSRF-Token: csrfToken 헤더 삽입.

// 수정안 요지
var csrfToken = (document.querySelector('input[name="_csrf"]') || {}).value || '';
// ... BibimbapCsrf 미존재 시 폴백 헤더에 'X-CSRF-Token': csrfToken 추가

hidden input 추가 시 new FormData(form) 이 자동으로 _csrf 파라미터를 포함하므로 서버사이드 form 파라미터 검증도 함께 강화된다.

방법 B (미채택): BibimbapCsrf를 항상 신뢰하여 폴백 분기 자체 제거 — BibimbapCsrf 로드 실패 시 더 큰 갭이 생기므로 채택하지 않는다.

추가 수정: 401 처리를 redirectLogin 함수로 분리, 성공 폴백 alert 제거.


날짜 포맷 함수 편재 (P2)

현황

날짜/시간 포맷 JavaScript 함수가 game-detail.jsp 단 하나의 파일에만 존재한다. 조사 범위 7개 파일(posts-detail, recruit-detail, jam-detail, posts-list, jam-list, recruit-list, game-detail) 중 나머지 6개에는 날짜 포맷 JS 함수가 전혀 없다.

함수 위치 구현 방식
fmtAbsolute(iso) game-detail.jsp:1516-1519 new Date(iso).toLocaleString('ko-KR')
fmtRelative(iso) game-detail.jsp:1521-1536 경과 시간 직접 산술 계산 (60/3600/86400초 분기)
buildTimeEl(iso, edited) game-detail.jsp:1538-1551 두 함수 합성, <time> 요소 + "(수정됨)" 뱃지 반환

사용 위치: game-detail.jsp:1878 (댓글), game-detail.jsp:2183-2186 (리뷰).

Intl.RelativeTimeFormat / Intl.DateTimeFormat 직접 사용은 조사 범위 7개 파일 전체에서 0 hit.

구조적 위험: 다른 detail 페이지가 동일 기능이 필요해지면 복사 중복 발생 구조.

권고 (design-advisor)

src/main/webapp/js/bibimbap-date.js 신규 생성. window.BibimbapDate 네임스페이스에 3개 함수 노출. game-detail.jsp는 script 태그 추가 + 인라인 선언 제거 + BibimbapDate. 프리픽스 적용.

현재 다른 detail 페이지에서 날짜 포맷이 필요하지 않으므로 강제 적용하지 않는다. 필요 시 include.


권고 구현 순서

1. [P0-보안] recruit-form.jsp CSRF 갭 수정         — 단독 작업, 위험 낮음
2. [P0] global.css 작성 + login.jsp 1개 검증        — Phase 1
3. [P0] admin 4개 파일 global.css 적용              — Phase 2
4. [P0] 나머지 19개 JSP global.css 적용             — Phase 3
5. [P1] bibimbap-utils.js 작성                       — admin 파일 수정 전제
6. [P1] admin 4개 파일 bibimbap-utils.js 연동        — 5 완료 후
7. [P1] window.confirm 2곳 BibimbapModal.confirm 전환 — 6과 동시 가능
8. [P1] openModal 패턴 4개 파일 표준화               — 7 완료 후
9. [P2] bibimbap-date.js 추출 + game-detail.jsp 연동 — 독립 작업

검증 기준(AC): 구현 완료 후 design-advisor 산출물의 AC-1~AC-11 항목을 기준으로 verification-advisor가 점검한다.

롤백 경로: 추가한 <link>, <script> 태그를 되돌리고 인라인 블록 복원. 서버사이드 Java 코드, MyBatis, API 계약에 영향 없음.

캐시 버스팅: 빌드 시스템 없음 → 쿼리 스트링 버전 파라미터(?v=20260630) 수동 관리.


미발견 / 조사 불충분 항목

  • theme-init.jsp 내부 구현 미탐색 — JS 테마 초기화 방식과 CSS 변수 일원화 가능성 미확인
  • header.jsp, footer.jsp에서 이미 CSS 변수를 선언하는지 여부 미탐색 — 이중 선언 여부 불명
  • modal.jsp가 실제로 어느 레이아웃 include 경로를 통해 로드되는지 미탐색 — BibimbapModal 미존재 상황의 실제 발생 가능성 미확인
  • game-detail.jsp 외 admin-* 파일들의 날짜 렌더링 패턴 미탐색
  • BibimbapCsrf 실제 구현체(별도 JS 파일) 미탐색 — headers() 메서드 폴백 동작 미확인
  • posts-form.jsppost() 함수와 admin 4개 파일의 post() 함수가 실제로 다른 구현인지 정밀 비교 미수행

관련 파일

분석 대상 (주요)

파일 이슈
src/main/webapp/WEB-INF/views/recruit-form.jsp CSRF 폴백 갭 (P0-보안), 401 처리 불일치
src/main/webapp/WEB-INF/views/posts-form.jsp CSRF 기준 파일 (변경 없음)
src/main/webapp/WEB-INF/views/admin-console.jsp CSS 토큰 중복, JS 함수 중복, notify 패턴
src/main/webapp/WEB-INF/views/admin-jam-list.jsp CSS 토큰 중복, JS 함수 중복, notify 패턴
src/main/webapp/WEB-INF/views/admin-post-categories.jsp CSS 토큰 중복, JS 함수 중복, window.confirm 직접 사용
src/main/webapp/WEB-INF/views/admin-unity-feeds.jsp CSS 토큰 중복, JS 함수 중복, window.confirm 직접 사용
src/main/webapp/WEB-INF/views/game-detail.jsp 날짜 함수 편재, 모달 패턴 혼재
src/main/webapp/WEB-INF/views/login.jsp openModal 패턴 A
src/main/webapp/WEB-INF/views/signup.jsp openModal 패턴 A
src/main/webapp/WEB-INF/views/game-register.jsp openModal 패턴 A
src/main/webapp/WEB-INF/views/profile.jsp openModal 패턴 A

신규 생성 예정 (권고안)

파일 역할
src/main/webapp/css/global.css 디자인 토큰 + body 기본 스타일 + admin-btn 컴포넌트
src/main/webapp/js/bibimbap-utils.js post / handleResult / makeErrorHandler 공용 유틸리티
src/main/webapp/js/bibimbap-date.js fmtAbsolute / fmtRelative / buildTimeEl 날짜 포맷 유틸리티

관련 기존 분석

  • 2026-06-16-project-analysis.md — bibimbap 전면 코드 분석. D2 보안 섹션에서 CSRF 더블서밋 기초 확인. 이 문서의 recruit-form CSRF 갭은 해당 분석 이후 발견된 추가 이슈다.