16 KiB
| phase | agent | agent_version | generated_at | concerns | source_confidence | workers_spawned |
|---|---|---|---|---|---|---|
| research | research-advisor | 2 | 2026-06-30T02:41:09Z | high | 5 |
프론트엔드 중복/이슈 분석
주제
JSP 기반 프론트엔드 코드에서 컴포넌트 중복 구현, 유지보수·확장성 이슈 조사
확정 중복 패턴 (이미 파악된 사실)
JS 함수 4중 중복 — admin 4개 파일
| 함수 | 파일 | 영향도 |
|---|---|---|
notify(message) |
admin-console, admin-jam-list, admin-post-categories, admin-unity-feeds 각각 선언 | Medium |
post(url, params) |
동일 4개 파일. 구현이 약간씩 다름 (params 처리, _csrf 추가 여부) | High |
handleResult(res), handleError() |
동일 4개 파일 동일 패턴 | Medium |
신뢰도: 확인됨 (입력 사실 보존)
BibimbapModal.alert 체크 인라인 반복
window.BibimbapModal && typeof window.BibimbapModal.alert === 'function'패턴이 15개 이상 파일에 인라인 반복- 신뢰도: 확인됨 (입력 사실 보존, 탐색 E에서 구체 위치 확인)
CSRF 토큰 추출 방식 불일치
- admin-console/jam-scoring/jam-detail:
request.getAttribute("csrfToken")rawCsrf 패턴 - admin-post-categories/admin-unity-feeds:
(String) request.getAttribute("csrfToken")캐스팅 - signup/login:
CsrfTokens.getOrCreate(request.getSession())직접 호출 - JS에서 일부는
<meta name="_csrf">읽기, 일부는<%= csrfTokenJs %>직접 출력 - 신뢰도: 확인됨 (입력 사실 보존)
CSS 중복 (탐색 A)
규모 요약
- 전체 27개 JSP 파일 중 26개에 인라인
<style>블록 존재 (theme-init.jsp 제외) - 누적 인라인 CSS: 5,969줄
- 범위: footer.jsp 76줄 ~ game-detail.jsp 1,173줄
- 신뢰도: 확인됨
중복 패턴 1위 — html {} 라이트 테마 CSS 변수 선언 (23개 파일)
핵심 8개 색상 토큰(--surface: #faf8f5, --card-bg: #fff, --text: #1a1a1a, --text-muted, --accent: #e8a54b, --border, --shadow, --accent-soft)이 23개 파일에 동일 값으로 복사됨.
- 대표 위치:
admin-console.jsp:38,jam-list.jsp:21,login.jsp:12 - 신뢰도: 확인됨
중복 패턴 2위 — html[data-theme="dark"] {} 다크 테마 CSS 변수 선언 (23개 파일)
다크 팔레트(#121212, #1e1e1e, #ece8e1, #a39e96, rgba(255,255,255,0.1))가 23개 파일에 동일 반복.
- 대표 위치:
admin-console.jsp:51,posts-list.jsp:41,recruit-list.jsp:34 - 신뢰도: 확인됨
중복 패턴 3위 — body {} 기본 리셋 + 폰트 + 배경 (23개 파일)
margin:0; min-height:100vh; font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Noto Sans KR", sans-serif; background:var(--surface); color:var(--text); 블록이 23개 파일에 동일 반복.
- 대표 위치:
admin-console.jsp:62,jam-list.jsp:41,terms.jsp:29 - 신뢰도: 확인됨
중복 패턴 4위 — 페이지 컨테이너 safe-area padding 패턴 (25개 파일)
padding: 1.5rem max(1rem, env(safe-area-inset-left)) 3rem max(1rem, env(safe-area-inset-right)); 가 거의 모든 페이지에 반복. max-width 값만 48/56/64/72/78rem으로 파일별 상이.
- 대표 위치:
admin-console.jsp:70-72,posts-list.jsp:58-60,recruit-list.jsp:52-54 - 신뢰도: 확인됨
중복 패턴 5위 — .admin-btn / .admin-btn--primary / .admin-btn--danger (4개 admin 파일)
버튼 컴포넌트 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 - 신뢰도: 확인됨
중복 패턴 6위 — .detail-button / .detail-actions / .detail-section (2개 파일)
jam-detail.jsp:128,recruit-detail.jsp:116— 거의 동일 (color 값 1곳만 다름:#1a1a1avsvar(--button-text))- 신뢰도: 확인됨
CSS Grid 리스트 래퍼 패턴 (posts·recruit 동일)
posts-list.jsp:119-123—.posts-grid { display:grid; grid-template-columns:repeat(3, minmax(0,1fr)); gap:1rem; }recruit-list.jsp:143-147—.recruit-grid { ... }동일 구조- 신뢰도: 확인됨
리스트 페이지 패턴 (탐색 B)
카드 HTML 구조 비교
| 항목 | posts-list.jsp | recruit-list.jsp | jam-list.jsp |
|---|---|---|---|
| 래퍼 클래스 | .posts-grid (section) |
.recruit-grid (section) |
.jam-grid (div) |
| Grid 열 | repeat(3, minmax(0,1fr)) |
repeat(3, minmax(0,1fr)) |
repeat(auto-fill, minmax(16rem,1fr)) |
| 카드 태그 | <a class="post-card"> |
<a class="recruit-card"> |
<a class="jam-card"> |
| 이미지 | 있음 (16/9 aspect-ratio) | 없음 | 없음 |
| 데이터 속성 | 없음 | data-role, data-type, data-search |
없음 |
신뢰도: 확인됨
검색/필터 방식 비교
- posts-list.jsp — 검색 UI 없음. 카테고리 탭(
<nav class="posts-tabs">)은<a>링크 방식으로 서버 재요청(?categoryId=). JS 없음. - recruit-list.jsp —
<input type="search" id="recruit-search">(라인 261) + 역할·참여형태<button class="filter-chip">(aria-pressed 토글).applyFilters()함수가card.hidden으로 클라이언트 필터 (라인 328-340). 검색 텍스트는 서버가data-search속성에 pre-encode. - jam-list.jsp — 검색/필터 모두 없음.
- 신뢰도: 확인됨
페이지네이션 방식 비교
- posts-list.jsp — 커서 기반 "더보기" 링크. 서버 Java가
cursorCreatedAt,cursorId파라미터를 URL로 조립 (라인 263-270). JS 없음. - recruit-list.jsp — 페이지네이션 없음. 전체 목록 한 번에 렌더.
- jam-list.jsp — 커서 기반 "더보기" 링크. 서버 Java가
cursor파라미터 URL 조립 (라인 177-180). JS 없음. - 신뢰도: 확인됨
공통 중복 블록
- CSS 변수 + body 스타일 블록: 세 파일 모두 동일 (상위 CSS 중복 패턴과 동일)
- 히어로 섹션 flex 레이아웃: posts·recruit 동일 (
align-items:flex-end; justify-content:space-between; gap:1rem) - 쓰기 버튼 스타일: posts·recruit 동일 (
min-height:2.75rem; border-radius:10px; background:var(--accent)) - 카드 hover 효과: posts·recruit 동일, jam은 transform 없이 border-color만
- empty-state 박스: 세 파일 유사 (border-style만 dashed vs solid 차이)
- 반응형 미디어 쿼리(3열→2열→1열): posts·recruit 동일 (@media 900px/640px)
request.getContextPath()+ 목록 attribute 캐스팅 패턴: 세 파일 공통<jsp:include page="/WEB-INF/views/header.jsp"/>/footer.jsp: 세 파일 동일- 신뢰도: 확인됨
폼 페이지 패턴 (탐색 C)
공통 골격 (거의 복사-붙여넣기 수준)
두 파일은 fetch 기반 비동기 폼 제출, checkValidity() 검증, BibimbapModal.alert 성공/실패 처리, BibimbapCsrf CSRF 헤더 주입이라는 동일한 골격을 공유.
| 항목 | posts-form.jsp | recruit-form.jsp |
|---|---|---|
| body 구성 | new URLSearchParams(new FormData(form)) (라인 232) |
동일 (라인 383) |
| Content-Type | application/x-www-form-urlencoded;charset=UTF-8 (라인 225) |
동일 (라인 387) |
| 클라이언트 검증 | form.checkValidity() + form.reportValidity() (라인 219-222) |
동일 (라인 378-381) |
| res.ok 판정 | if (!res.ok) { var error = new Error(...); error.status = res.status; throw error; } (라인 241-246) |
동일 (라인 400-404) |
| 성공 리다이렉트 | window.location.href = ctx + (data.location || '/posts') (라인 250) |
window.location.href = '<%= ctx %>' + (data.location || '/recruit') (라인 409) |
신뢰도: 확인됨
차별화 부분 (다른 점)
-
CSRF 처리 방식 불일치 (High)
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미존재 시 fallback 헤더에 CSRF 토큰이 전혀 포함되지 않는 보안 갭- 신뢰도: 확인됨
-
성공 시 모달 없을 때 분기 차이
posts-form.jsp:260-261—} else { go(); }(alert 없음)recruit-form.jsp:419-421—} else { alert('팀원 모집글이 등록되었습니다.'); go(); }- 신뢰도: 확인됨
-
401 리다이렉트 처리 패턴 불일치
posts-form.jsp:264-268—redirectLogin함수 분리,onConfirm에서 호출recruit-form.jsp:430-431, 437-439— 모달onConfirm과 else 분기 양쪽에 각각 인라인 중복 작성- 신뢰도: 확인됨
-
수정 모드:
posts-form.jsp:9-10, 215—mode,postId로 등록/수정 URL 분기.recruit-form.jsp는 수정 모드 분기 없음. -
실시간 미리보기:
recruit-form.jsp:340-373—pairs배열 +render()+ input/change 이벤트로 우측 preview-card 갱신. posts-form에는 없음. -
제출 버튼 disabled 처리: 두 파일 모두 없음. fetch 중 비활성화 처리 미구현.
날짜 함수 (탐색 D)
발견 요약
- 날짜/시간 포맷 JavaScript 함수가 선언된 파일:
game-detail.jsp단 하나 - 나머지 6개 파일(posts-detail, recruit-detail, jam-detail, posts-list, jam-list, recruit-list)에는 날짜 포맷 JS 함수가 전혀 없음
- 신뢰도: 확인됨
game-detail.jsp 날짜 함수 (라인 1516-1551)
| 함수 | 위치 | 구현 방식 |
|---|---|---|
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> 요소 + "(수정됨)" 뱃지 반환 |
Intl.RelativeTimeFormat/Intl.DateTimeFormat직접 사용: 조사 범위 7개 파일 전체에서 0 hit- 신뢰도: 확인됨
사용 위치
game-detail.jsp:1878—buildTimeEl사용 (댓글 항목)game-detail.jsp:2183-2186—fmtAbsolute/fmtRelative직접 사용 (리뷰 항목)- 신뢰도: 확인됨
서버사이드 날짜 포매팅
- 조사 범위 내 Java Date 포매팅으로 화면 출력하는 경우: 0건
posts-list.jsp:6-7,19—OffsetDateTimeimport하지만 커서 URL 생성에만 사용 (화면 출력 아님)jam-detail.jsp:442—OffsetDateTime.now().isAfter(...)서버사이드 분기 조건 (화면 포맷 출력 아님)- 신뢰도: 확인됨
구조적 위험
fmtAbsolute/fmtRelative/buildTimeEl이 game-detail.jsp 인라인 스크립트에만 존재 → 다른 detail 페이지가 동일 기능이 필요할 때 복사 중복 발생 구조.
모달 패턴 (탐색 E)
BibimbapModal API (modal.jsp:174-238)
| 메서드 | 시그니처 | 설명 |
|---|---|---|
alert(options) |
{ title, message, confirmText, onConfirm } |
취소 버튼 없음, 확인 버튼만 |
confirm(options) |
{ title, message, confirmText, cancelText, onConfirm, onCancel } |
확인+취소 양쪽 콜백 |
prompt(options) |
{ title, message, label, value, placeholder, maxLength, confirmText, cancelText, onConfirm(inputValue), onCancel } |
텍스트 입력 필드 포함 |
- 구현 방식: 단일
<div id="site-modal">재사용, 콜백 기반 (Promise 아님), Enter=확인/Escape=취소 키보드 처리, 포커스 복귀 - 신뢰도: 확인됨
모달 사용 패턴 분류 (12개 JSP, modal.jsp 제외)
패턴 A — openModal() 헬퍼 래퍼 (4개 파일, High 중복)
login.jsp, signup.jsp, game-register.jsp, profile.jsp가 동일 시그니처의 openModal(title, message, confirmText, onConfirm) 로컬 함수를 각자 정의.
- 내부 BibimbapModal 체크 후 폴백
alert(message)+ 동기onConfirm()호출 - 위치:
login.jsp:256,signup.jsp:269,game-register.jsp:490,profile.jsp:539 - 신뢰도: 확인됨
패턴 B — notify() 헬퍼 래퍼 (4개 admin 파일, High 중복)
admin-console.jsp, admin-jam-list.jsp, admin-post-categories.jsp, admin-unity-feeds.jsp가 notify(message) 로컬 함수를 각자 정의 (alert 전용, title은 하드코딩된 페이지명).
- 위치:
admin-jam-list.jsp:389,admin-console.jsp:401,admin-post-categories.jsp:251,admin-unity-feeds.jsp:305 - 신뢰도: 확인됨
패턴 C — 인라인 직접 체크 (3개 파일)
recruit-form.jsp, posts-form.jsp, game-detail.jsp가 호출 지점마다 인라인으로 체크.
recruit-form.jsp:411, 424,posts-form.jsp:252, 269,game-detail.jsp:1425, 1439, 1474, 1482, 1553, 1558- 신뢰도: 확인됨
특이 케이스 — game-detail.jsp 혼용
game-detail.jsp:1553-1561에 notifyError(), confirmAction() 헬퍼를 별도 정의하면서, 동일 파일 1425-1449에 인라인 체크도 병존. 단일 파일 내 두 패턴 혼재.
- 신뢰도: 확인됨
window.confirm/alert 직접 사용 (BibimbapModal 없이)
| 종류 | 위치 | 비고 |
|---|---|---|
window.confirm() 직접 호출 (체크 없음) |
admin-post-categories.jsp:323 |
삭제 확인 대화상자 |
window.confirm() 직접 호출 (체크 없음) |
admin-unity-feeds.jsp:401 |
삭제 확인 대화상자 |
window.alert() 폴백 (else 브랜치) |
전 파일의 BibimbapModal 없을 때 else 브랜치 | 정상 폴백 |
window.prompt() 폴백 |
profile.jsp:686 |
BibimbapModal.prompt 실패 시 |
admin-post-categories.jsp와admin-unity-feeds.jsp는 알림에는notify()헬퍼(BibimbapModal 체크)를 쓰면서 삭제 확인에는window.confirm()을 직접 사용하여 동일 파일 내 불일치 존재- 신뢰도: 확인됨
종합 판단
상위 패턴
-
CSS 인라인 토큰 중복이 가장 심각: 라이트/다크 테마 CSS 변수 블록과
body기본 스타일이 23개 파일에 동일하게 복사됨. 디자인 토큰 변경 시 23개 파일을 모두 수정해야 하는 유지보수 고비용 구조.theme-init.jsp가 존재하나 CSS 변수는 여기에 없고 JS 테마 초기화만 담당하는 것으로 보임. -
JS 유틸리티 함수 인라인 중복 이중 구조:
notify()(admin 4개),openModal()(일반 4개),post(url, params)(admin 4개) 등이 페이지마다 별도 선언됨. 특히post()함수는 파일마다 구현이 미묘하게 달라 동작 불일치 위험 내재. -
BibimbapModal 체크 패턴 3가지 혼존: 헬퍼 래퍼 A, 헬퍼 래퍼 B, 인라인 직접 체크 C가 서로 다른 파일에 혼재. 일관성 없고 신규 페이지 작성 시 어떤 패턴을 따라야 할지 기준 없음.
-
폼 제출 골격 복사: posts-form과 recruit-form이 fetch 제출 코드를 거의 복사 수준으로 공유하면서 CSRF 처리 방식에 불일치 존재 (recruit-form의 BibimbapCsrf 미존재 시 CSRF 토큰 누락 위험).
-
날짜 포맷 함수 편재: game-detail.jsp에만 날짜 함수가 존재하며 다른 detail 페이지는 날짜 렌더링 자체가 없거나 JS 미사용. 추후 다른 페이지에서 날짜 포맷이 필요해지면 복사 중복 발생 구조.
충돌/갭
- CSS 변수 선언이 파일마다 포함하는 변수 수가 8~14개로 약간씩 달라, 특정 파일에서 일부 변수가 미선언된 상태일 수 있음.
admin-post-categories.jsp와admin-unity-feeds.jsp는 알림에는 BibimbapModal 체크를 쓰면서 삭제 확인은window.confirm()직접 사용 — 같은 파일 내 불일치.jam-detail.jsp:128과recruit-detail.jsp:116의.detail-button--primary color값 불일치 (#1a1a1avsvar(--button-text)) — 디자인 일관성 갭.
미발견 / 조사 불충분 항목
theme-init.jsp내부 구현 미탐색 — JS 테마 초기화 방식과 CSS 변수 일원화 가능성 미확인header.jsp,footer.jsp에서 이미 CSS 변수를 선언하는지 여부 미탐색 — 이중 선언 여부 불명modal.jsp가 실제로 어느 레이아웃 include 경로를 통해 로드되는지 미탐색 — BibimbapModal 미존재 상황의 실제 발생 가능성 미확인- game-detail.jsp 외 admin-* 파일들의 날짜 렌더링 패턴 미탐색
BibimbapCsrf실제 구현체(별도 JS 파일) 미탐색 —headers()메서드 폴백 동작 미확인- posts-form.jsp의
post()함수와 admin 4개 파일의post()함수가 실제로 다른 구현인지 정밀 비교 미수행