Conversation
Walkthrough게임 페이지(단일/다중 플레이) UI를 재설계하고, 클라이언트 측 게임 상태 관리 로직을 추가했습니다. 새로운 스타일시트, 개선된 레이아웃, 게임 시작 카운트다운, 턴 강조 표시 등을 도입했습니다. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Pre-merge checks and finishing touches✅ Passed checks (5 passed)
✨ Finishing touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
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에 직접 삽입하고 있습니다.
loginUserId나loginNickname에 큰따옴표(") 또는</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: 주석 처리된 코드를 제거해 주세요.주석 처리된
emojiAreadiv와 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
📒 Files selected for processing (3)
src/main/webapp/WEB-INF/views/game/multi.jspsrc/main/webapp/WEB-INF/views/game/single.jspsrc/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으로 방으로 이동하는 로직이 잘 구현되어 있습니다.
| .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; | ||
| } |
There was a problem hiding this comment.
.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.
| <script> | ||
| window.loginUserId = "<%= (String)session.getAttribute("loginUserId") %>"; | ||
| window.loginNickname = "<%= (String)session.getAttribute("loginNickname") %>"; | ||
| window.contextPath = "<%= request.getContextPath() %>"; | ||
| </script> |
There was a problem hiding this comment.
XSS 취약점: 세션 속성값을 JavaScript에 직접 삽입하고 있습니다.
single.jsp와 동일한 문제입니다. loginUserId나 loginNickname에 특수 문자가 포함되면 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.
| // ✅ 프로필 이동 | ||
| leftSlot.appendChild(p1); // 상대 → 왼쪽 | ||
| rightSlot.appendChild(p2); // 나 → 오른쪽 |
There was a problem hiding this comment.
프로필 슬롯 배치와 턴 하이라이트 로직 간 불일치가 있습니다.
Line 93-94에서 p1을 leftSlot(상대)에, p2를 rightSlot(나)에 배치하고 있습니다. 그러나 Line 346-347의 setTurnHighlight 함수 주석에서는 p1=나, p2=상대로 설명하고 있습니다. 이 불일치로 인해 턴 하이라이트가 잘못 표시될 수 있습니다.
배치 의도를 확인하고 일관성 있게 수정해 주세요.
| /* 턴 강조 */ | ||
| 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); | ||
| } |
There was a problem hiding this comment.
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.
📌 작업 내용
🔗 관련 이슈
👀 리뷰 시 참고사항
💭 느낀 점
💻 스크린샷 (선택)
Summary by CodeRabbit
릴리스 노트
New Features
Style
✏️ Tip: You can customize this high-level summary in your review settings.