Skip to content

[FEAT] 싱글, 멀티 게임 UI 개선#108

Merged
gaeunnlee merged 2 commits intodevfrom
FEAT-게임ui추가
Dec 24, 2025

Hidden character warning

The head ref may contain hidden characters: "FEAT-\uac8c\uc784ui\ucd94\uac00"
Merged

[FEAT] 싱글, 멀티 게임 UI 개선#108
gaeunnlee merged 2 commits intodevfrom
FEAT-게임ui추가

Conversation

@minseokim0113
Copy link
Collaborator

@minseokim0113 minseokim0113 commented Dec 24, 2025

📌 작업 내용

  • 게임 시작 전 텀 생성
  • ui 개선

🔗 관련 이슈


👀 리뷰 시 참고사항

  • 리뷰어가 중점적으로 보면 좋을 부분을 작성해 주세요.
  • 고민한 지점이나 피드백 받고 싶은 부분이 있다면 함께 남겨주세요.

💭 느낀 점

  • 작업하면서 느낀 점이나 공유하고 싶은 내용을 자유롭게 작성해 주세요.

💻 스크린샷 (선택)

  • 필요한 경우 첨부해 주세요.

Summary by CodeRabbit

릴리스 노트

  • New Features

    • 멀티플레이 게임 UI를 2패널 레이아웃으로 재설계했습니다.
    • 게임 시작 전 카운트다운 오버레이를 추가했습니다.
    • 팀 배지 및 플레이어 아바타 기능을 도입했습니다.
    • 턴 강조 표시 기능을 구현했습니다.
  • Style

    • 게임 UI를 위한 완전한 스타일시트를 추가했습니다.
    • 반응형 디자인 및 애니메이션 효과를 적용했습니다.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 24, 2025

Walkthrough

게임 페이지(단일/다중 플레이) UI를 재설계하고, 클라이언트 측 게임 상태 관리 로직을 추가했습니다. 새로운 스타일시트, 개선된 레이아웃, 게임 시작 카운트다운, 턴 강조 표시 등을 도입했습니다.

Changes

Cohort / File(s) 변경 사항
Game View Layouts
src/main/webapp/WEB-INF/views/game/multi.jsp, src/main/webapp/WEB-INF/views/game/single.jsp
정적 레이아웃을 모듈식 게임 UI로 재구성; 양쪽 플레이어 패널, 중앙 보드, 로그 및 타이머 영역 추가. 다중 플레이의 경우 시작 카운트다운 오버레이 및 팀 배지 추가. 새로운 클라이언트 측 이벤트 핸들러(MULTI_START, MULTI_TURN, SINGLE_TURN 등)와 헬퍼 함수(setTurn, moveCards, setCuteAvatarsAndTeamBadge, setTurnHighlight) 구현. 게임 잠금 메커니즘과 DOM 재배치 로직 추가.
Game Styling
src/main/webapp/resources/css/omok.css
완전한 CSS 스타일시트 신규 작성; 기본 리셋, 그리드 레이아웃, 반응형 설계 포함. 카드 컴포넌트, 상태 표시줄, 보드 영역, 로그 패널 스타일 정의. 플레이어 UI 요소(아바타, 이름, 배지), 턴 강조 표시(애니메이션 포함), 다중 플레이어 프로필 스타일링, 시작 오버레이 및 키프레임 애니메이션(turn wiggle, count pop) 추가.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

FEAT

Suggested reviewers

  • cl-o-lc
  • ochanhyeok

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목이 주요 변경사항을 명확하게 반영합니다. 싱글/멀티 게임 UI 개선이 핵심이며, 제공된 변경 요약에서 두 JSP 파일과 CSS의 UI 구조 개선이 주된 내용입니다.
Linked Issues check ✅ Passed 연결된 이슈 #96은 single, multi, chat의 UI 개선을 목표로 하며, 본 PR은 single.jsp와 multi.jsp의 UI를 완전히 재구성하고 omok.css 스타일시트를 추가하여 이를 충족합니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 연결된 이슈 #96의 범위 내에 있습니다. 변경된 파일들(single.jsp, multi.jsp, omok.css)은 모두 게임 페이지 UI 개선에 직접 관련되어 있으며, 추가적인 범위 외 변경은 없습니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch FEAT-게임ui추가

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Collaborator

@kkhhmm3103 kkhhmm3103 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수고햇다 아기

Copy link
Member

@gaeunnlee gaeunnlee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수고하셨습니다!

@gaeunnlee gaeunnlee merged commit 370db3d into dev Dec 24, 2025
3 of 4 checks passed
@gaeunnlee gaeunnlee deleted the FEAT-게임ui추가 branch December 24, 2025 06:02
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/webapp/WEB-INF/views/game/single.jsp (1)

107-109: XSS 취약점: 세션 속성값을 JavaScript에 직접 삽입하고 있습니다.

loginUserIdloginNickname에 큰따옴표(") 또는 </script> 같은 문자가 포함되면 XSS 공격에 노출될 수 있습니다. JSTL이나 적절한 이스케이핑을 사용하세요.

🔎 수정 제안

JSTL을 사용하는 경우:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>

<script>
  window.loginUserId = "${fn:escapeXml(sessionScope.loginUserId)}";
  window.loginNickname = "${fn:escapeXml(sessionScope.loginNickname)}";
</script>

또는 JSON 인코딩 라이브러리를 사용하여 안전하게 출력하세요.

🧹 Nitpick comments (5)
src/main/webapp/resources/css/omok.css (1)

263-277: 접근성을 위해 prefers-reduced-motion 미디어 쿼리 추가를 권장합니다.

무한 반복 애니메이션(infinite)은 모션에 민감한 사용자에게 불편을 줄 수 있습니다. prefers-reduced-motion 미디어 쿼리를 사용하여 애니메이션을 비활성화하는 것이 좋습니다.

🔎 수정 제안
@media (prefers-reduced-motion: reduce) {
  #p1.turn-active, #p2.turn-active, #p3.turn-active, #p4.turn-active {
    animation: none;
  }
}
src/main/webapp/WEB-INF/views/game/single.jsp (1)

367-369: 주석 처리된 코드를 제거해 주세요.

주석 처리된 emojiArea div와 JSP include는 사용되지 않으므로 제거하는 것이 좋습니다.

🔎 수정 제안
-<!-- 	 <div id="emojiArea"></div> -->
-
-  <%//@ include file="/WEB-INF/views/chat/gameEmoji.jsp" %>
src/main/webapp/WEB-INF/views/game/multi.jsp (3)

292-296: setTurn 함수에서 idxToCard를 재사용하세요.

idxToCard 함수(Line 206-210)와 동일한 매핑이 setTurn에서 중복 정의되어 있습니다. DRY 원칙을 위해 기존 함수를 재사용하세요.

🔎 수정 제안
  function setTurn(turnIdx){
 	["p1","p2","p3","p4"].forEach(id => document.getElementById(id)?.classList.remove("turn-active"));
-	const map = {0:"p1", 1:"p3", 2:"p4", 3:"p2"};
-	document.getElementById(map[turnIdx])?.classList.add("turn-active");
+	idxToCard(turnIdx)?.classList.add("turn-active");
  }

201-204: ws.onclose에서 countdownTimer도 정리해 주세요.

연결이 종료될 때 countdownTimer가 여전히 실행 중일 수 있습니다. 타이머 정리를 추가하면 리소스 누수를 방지할 수 있습니다.

🔎 수정 제안
  ws.onclose = () => {
    log("연결이 종료되었습니다.");
    statusDiv.innerText = "연결 끊김";
+   clearInterval(countdownTimer);
+   clearInterval(timer);
  };

388-395: idxToCard 함수를 재사용하세요.

동일한 매핑({0:"p1", 1:"p3", 2:"p4", 3:"p2"})이 세 번 정의되어 있습니다. 코드 유지보수를 위해 idxToCard를 재사용하세요.

🔎 수정 제안
    if (data.type === "MULTI_USER") {
      const p = data.payload || {};
      const slot = p.slot;
      const nick = p.nickname;

-  	  // 1) data-slot으로 먼저 찾기
-     let card = document.querySelector(`.player-card[data-slot='${slot}']`);
-
-     // 2) 혹시 못 찾으면 id 매핑으로도 찾기
-     if (!card) {
-       const idMap = {0:"p1", 1:"p3", 2:"p4", 3:"p2"};
-       card = document.getElementById(idMap[slot]);
-     }
+     const card = idxToCard(slot);

      if (card) {
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between df33de3 and 0e29eb7.

📒 Files selected for processing (3)
  • src/main/webapp/WEB-INF/views/game/multi.jsp
  • src/main/webapp/WEB-INF/views/game/single.jsp
  • src/main/webapp/resources/css/omok.css
🔇 Additional comments (3)
src/main/webapp/resources/css/omok.css (1)

319-341: LGTM!

오버레이 스타일링이 잘 구성되어 있습니다. backdrop-filter: blur(8px)를 사용한 블러 효과와 반투명 배경의 조합이 적절합니다.

src/main/webapp/WEB-INF/views/game/multi.jsp (2)

452-494: LGTM!

카운트다운 로직이 잘 구현되어 있습니다. gameLocked 플래그로 게임 시작 전 클릭을 방지하고, 카운트다운 완료 후 잠금을 해제하는 흐름이 적절합니다.


423-442: LGTM!

goToRoomView 함수가 에러 발생 시에도 fallback으로 방으로 이동하는 로직이 잘 구현되어 있습니다.

Comment on lines +99 to +111
.chip{
font-size: 16px; /* 크게 */
font-weight: 900;
padding: 10px 16px;
border-radius: 999px;
}


.chip.danger{
background: rgba(239,68,68,0.12);
border-color: rgba(239,68,68,0.25);
color:#b91c1c;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

.chip 기본 클래스에 border 속성이 누락되었습니다.

.chip.danger에서 border-color를 설정하고 있지만, 기본 .chip 클래스에 border 속성이 정의되어 있지 않아 border-color가 적용되지 않습니다.

🔎 수정 제안
 .chip{
   font-size: 16px;            /* 크게 */
   font-weight: 900;
   padding: 10px 16px;
   border-radius: 999px;
+  border: 1px solid #e5e7eb;
+  background: #fff;
 }
🤖 Prompt for AI Agents
In src/main/webapp/resources/css/omok.css around lines 99 to 111, the base .chip
class lacks a border declaration so .chip.danger's border-color has no effect;
add a border on .chip (for example border: 1px solid transparent) in the .chip
rule so color overrides in modifiers like .chip.danger apply correctly.

Comment on lines +15 to +19
<script>
window.loginUserId = "<%= (String)session.getAttribute("loginUserId") %>";
window.loginNickname = "<%= (String)session.getAttribute("loginNickname") %>";
window.contextPath = "<%= request.getContextPath() %>";
</script>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

XSS 취약점: 세션 속성값을 JavaScript에 직접 삽입하고 있습니다.

single.jsp와 동일한 문제입니다. loginUserIdloginNickname에 특수 문자가 포함되면 XSS 공격에 노출될 수 있습니다. JSTL fn:escapeXml 또는 JSON 인코딩을 사용하세요.

🤖 Prompt for AI Agents
In src/main/webapp/WEB-INF/views/game/multi.jsp around lines 15–19, the page is
writing raw session attributes into JavaScript, creating an XSS risk; replace
the direct JSP expressions with properly escaped or JSON-encoded values. Use
JSTL/c tags or fn:escapeXml (or serialize the values to JSON on the server) so
special characters are escaped before insertion, and ensure values are quoted as
safe JS strings; update the three assignments (loginUserId, loginNickname,
contextPath) to use those escaped/encoded values instead of raw scriptlet
outputs.

Comment on lines +92 to +94
// ✅ 프로필 이동
leftSlot.appendChild(p1); // 상대 → 왼쪽
rightSlot.appendChild(p2); // 나 → 오른쪽
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

프로필 슬롯 배치와 턴 하이라이트 로직 간 불일치가 있습니다.

Line 93-94에서 p1leftSlot(상대)에, p2rightSlot(나)에 배치하고 있습니다. 그러나 Line 346-347의 setTurnHighlight 함수 주석에서는 p1=나, p2=상대로 설명하고 있습니다. 이 불일치로 인해 턴 하이라이트가 잘못 표시될 수 있습니다.

배치 의도를 확인하고 일관성 있게 수정해 주세요.

Comment on lines +343 to +351
/* 턴 강조 */
function setTurnHighlight(turnColor){ // 내 색: myColor
const isMyTurn = (turnColor === myColor);
const p1 = document.getElementById("p1"); // (emojiChat.js 기준) p1=나
const p2 = document.getElementById("p2"); // p2=상대
if (!p1 || !p2) return;
p1.classList.toggle("turn-active", isMyTurn);
p2.classList.toggle("turn-active", !isMyTurn);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

setTurnHighlight 함수가 정의되었지만 호출되지 않습니다.

이 함수는 턴 강조 기능을 위해 정의되었지만, SINGLE_TURN 핸들러(Line 203-221)에서 호출되지 않아 실제로 턴 하이라이트가 적용되지 않습니다.

🔎 수정 제안

SINGLE_TURN 핸들러에서 setTurnHighlight를 호출하세요:

     if (data.type === "SINGLE_TURN") {
     	if (gameOver) return; 
     	startTimer(data.time, data.color);
+    	setTurnHighlight(data.color);
     	let passnum = (myColor == 1 ? (3 - data.bpass) : (3 - data.wpass));

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/main/webapp/WEB-INF/views/game/single.jsp around lines 343-351, the
setTurnHighlight(turnColor) function is defined but never called; update the
SINGLE_TURN handler (around lines 203-221) to invoke setTurnHighlight with the
incoming turn color (e.g., setTurnHighlight(turnColor)) at the point where turn
state is processed so the DOM classes toggle when turns change; ensure the
handler has access to the same turnColor value passed by the event and that
myColor is in scope.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] 게임 페이지 ui 개선

3 participants