Skip to content

[FEAT] 게임 시작 전 방장·인원 상태 검증 및 실시간 UI 반영#104

Merged
cl-o-lc merged 7 commits intodevfrom
FEAT-방장-게임시작-표시
Dec 23, 2025

Hidden character warning

The head ref may contain hidden characters: "FEAT-\ubc29\uc7a5-\uac8c\uc784\uc2dc\uc791-\ud45c\uc2dc"
Merged

[FEAT] 게임 시작 전 방장·인원 상태 검증 및 실시간 UI 반영#104
cl-o-lc merged 7 commits intodevfrom
FEAT-방장-게임시작-표시

Conversation

@gaeunnlee
Copy link
Member

@gaeunnlee gaeunnlee commented Dec 23, 2025

📌 작업 내용

게임 시작 버튼 표시 및 게임 시작 요청 시점에 방장 여부와 방 인원상태를 검증하고, 방장 변경 상황을 WebSocket으로 실시간 반영하도록 개선했습니다.

  1. 방 생성 시 방장 ID 세션 저장 및 시작 버튼 제어
  • 방 생성/입장 시 방장 ID를 세션에 저장
  • JSP에서 로그인 유저와 방장 ID를 비교해 방장에게만 게임 시작 버튼을 렌더링
  1. 방장 변경(WebSocket) 이벤트 처리
  • 방장이 퇴장하는 경우
  • HOST_CHANGE WebSocket 이벤트 수신 시:
    • 새 방장에게만 게임 시작 버튼 동적 생성
  1. 게임 시작 전 방 인원 검증 로직 추가
  • RoomController에 방 인원(active) 조회 API 추가
  • 게임 시작 버튼 클릭 시: /room/count API로 현재 active 인원 조회
  • 인원 조건 충족 시에만 /game/start 요청 전송 불필요한 게임 시작 요청을 사전에 차단

🔗 관련 이슈


👀 리뷰 시 참고사항


💭 느낀 점


💻 스크린샷 (선택)

  • 방장 변경
image
  • 인원 검증
image

Summary by CodeRabbit

  • 새로운 기능

    • 실시간 호스트 변경 이벤트가 방 화면에 반영되어 새 호스트에게 시작 버튼이 동적으로 생성됩니다.
    • 방 활성 인원 조회용 공개 API가 추가되어 참가자 수를 확인할 수 있습니다.
  • 개선 사항

    • 방 페이지에 사용자/호스트 정보가 세션과 페이지에 저장되어 클라이언트 동작이 개선되었습니다.
    • 게임 시작 흐름이 폼 제출에서 버튼 클릭 기반으로 변경되어, 클릭 시 참가자 수 검증 후 시작 요청이 수행됩니다.
  • 버그 픽스 / 오류처리

    • 방 조회 흐름에서 호스트 정보가 없을 경우 로비로 리디렉션하며 오류 쿼리로 안내합니다.

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

@gaeunnlee gaeunnlee self-assigned this Dec 23, 2025
@gaeunnlee gaeunnlee added the FEAT 새로운 기능 추가 또는 기존 기능 확장 label Dec 23, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 23, 2025

Walkthrough

방장 ID를 세션에 저장하고(룸 생성/입장 시), 방 활성 플레이어 수 조회 API를 추가하며 WebSocket 호스트 변경 브로드캐스트와 클라이언트의 시작 버튼/시작 흐름을 호스트 전용으로 변경합니다. (50단어 이내)

Changes

코호트 / 파일(s) 변경사항
세션에 방장 ID 저장
\src/main/java/lobby/controller/CreateRoomController.java`, `src/main/java/game/controller/GameStartController.java``
CreateRoomController: 방 생성 직후 session.setAttribute("hostUserId", hostUserId) 추가. GameStartController: 코드 포맷 소폭 변경(로직 없음).
방 활성 플레이어 조회 API
\src/main/java/room/controller/RoomController.java``
신규 서블릿 추가: GET /room/count 엔드포인트(액션 매핑, 세션 인증, RoomPlayerDAO.countActivePlayers 호출, JSON 응답).
방 입장 뷰에서 세션 처리
\src/main/java/room/controller/ViewRoomController.java``
RoomDAO 주입 및 findHostUserIdByRoomId 호출로 hostUserId 획득; 세션(null 체크) 사용, hostUserId·userId를 세션에 저장 후 뷰 포워드.
Room DAO 확장
\src/main/java/room/dao/RoomDAO.java`, `src/main/java/room/dao/RoomDAOImpl.java``
findHostUserIdByRoomId(String) 인터페이스·구현 추가: 방 id로 host_user_id 조회 반환(null 가능).
새 DTO 추가
\src/main/java/room/dto/RoomActiveCountDTO.java``
roomIdactiveCount 필드를 가진 DTO 추가(@Getter, 생성자).
WebSocket 호스트 변경 처리
\src/main/java/room/ws/RoomWebSocket.java`, `src/main/java/room/ws/RoomWebSocketService.java``
onExit 시그니처를 (Session,String)->(Session,String,String)로 변경; RoomDAO 의존 추가; 호스트 변경 발생 시 findHostUserIdByRoomId로 새 hostUserId 조회 후 broadcastHostChanged(roomId, hostUserId) 호출 로직 추가.
뷰/프론트엔드 변경
\src/main/webapp/WEB-INF/views/room.jsp`, `src/main/webapp/WEB-INF/views/lobby.jsp`, `src/main/webapp/static/room/room.js``
room.jsp: 루트에 data-user-id 추가, 시작 버튼 렌더링을 loginUserId == hostUserId 조건으로 변경(기존 폼 제거). lobby.jsp: 주석 제거. room.js: 사용자 ID 추출, HOST_CHANGE 처리 추가(호스트 변경 시 시작 버튼 동적 생성), 시작 흐름을 버튼 클릭 기반으로 전환하고 /room/count 호출로 활성 인원 검증 후 /game/start POST 수행.

Sequence Diagram(s)

sequenceDiagram
    rect rgba(220,235,255,0.6)
    participant Client as 브라우저 (room.jsp / room.js)
    participant View as ViewRoomController
    participant Session as HTTP Session
    participant DAO as RoomDAO
    participant DB as 데이터베이스
    end

    Client->>View: GET /room?roomId=xxx
    View->>Session: getSession(false) / getAttribute("loginUserId")
    View->>DAO: findHostUserIdByRoomId(roomId)
    DAO->>DB: SELECT host_user_id FROM room WHERE id = ?
    DB-->>DAO: hostUserId
    DAO-->>View: hostUserId
    View->>Session: setAttribute("hostUserId", hostUserId)
    View->>Session: setAttribute("userId", loginUserId)
    View-->>Client: room.jsp (렌더링, data-user-id 포함)
Loading
sequenceDiagram
    rect rgba(235,255,220,0.6)
    participant WS as RoomWebSocket
    participant Service as RoomWebSocketService
    participant DAO as RoomDAO
    participant DB as 데이터베이스
    participant Clients as 모든 클라이언트
    end

    WS->>Service: onClose(session, roomId)
    Service->>Service: exitAndHandleHost(roomId) -> result
    alt 호스트 퇴장 -> HOST_CHANGE
        Service->>DAO: findHostUserIdByRoomId(roomId)
        DAO->>DB: SELECT host_user_id FROM room WHERE id = ?
        DB-->>DAO: newHostUserId
        DAO-->>Service: newHostUserId
        Service->>Service: broadcastHostChanged(roomId, newHostUserId)
        Service->>Clients: {type: "HOST_CHANGE", roomId, hostUserId}
    else 일반 퇴장
        Service->>Clients: {type: "USER_EXIT", ...}
    end
Loading
sequenceDiagram
    rect rgba(255,240,220,0.6)
    participant Client as 브라우저 (room.js)
    participant API as RoomController (/room/count)
    participant DAO as RoomPlayerDAO
    participant DB as 데이터베이스
    end

    Client->>API: GET /room/count?roomId=xxx
    API->>API: 세션에서 loginUserId 검증
    API->>DAO: countActivePlayers(roomId)
    DAO->>DB: SELECT COUNT(*) ...
    DB-->>DAO: activeCount
    DAO-->>API: activeCount
    API-->>Client: 200 {roomId, activeCount}
    Client->>Client: playType 기준 최소 인원 검사
    alt 충분 인원
        Client->>Server: POST /game/start {roomId, playType}
        Note over Client: WebSocket GAME_START 이벤트로 게임 진입 대기
    else 부족
        Client->>User: 경고 표시
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Possibly related PRs

Suggested reviewers

  • minseokim0113
  • cl-o-lc
  • ochanhyeok

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.71% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목이 주요 변경 사항인 '게임 시작 전 방장·인원 상태 검증 및 실시간 UI 반영'을 명확하게 요약하고 있습니다.
Linked Issues check ✅ Passed 모든 #99 요구사항이 구현되었습니다: 방장 userId 조회 DAO(RoomDAO.findHostUserIdByRoomId), 세션 저장(CreateRoomController, ViewRoomController), JSP 조건부 렌더링(room.jsp).
Out of Scope Changes check ✅ Passed 추가 기능들(RoomController API, WebSocket HOST_CHANGE 처리, room.js 동적 버튼 생성)은 PR 목표의 '실시간 UI 반영'과 '인원 상태 검증'을 보완하는 범위 내 변경입니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch FEAT-방장-게임시작-표시

📜 Recent 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 17c2998 and 30e8502.

📒 Files selected for processing (1)
  • src/main/java/room/controller/ViewRoomController.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/room/controller/ViewRoomController.java

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

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/java/game/controller/GameStartController.java (1)

49-67: 게임 시작 전에 플레이어 상태 업데이트 결과를 검증하세요.

updatePlayersToInGame(roomId) (63줄)의 반환값을 검증하지 않고 게임을 시작하고 있습니다. 반환값이 0이면 실제로 플레이어의 상태가 변경되지 않았는데도 브로드캐스트가 진행됩니다. 게임 시작 전에 updated > 0인지 확인하고, 그렇지 않으면 오류를 반환해야 합니다.

또한, 이미 구현된 roomPlayerDao.countActivePlayers(roomId) 메서드를 활용하여 활성 플레이어 수를 먼저 검증하는 것을 권장합니다. ChatServer의 최소 플레이어 요구사항(GAME_START_PLAYERS = 2)과 일관성을 유지하세요.

추가로, 56줄과 65줄의 System.out.println() 디버그 문을 적절한 로깅 프레임워크로 대체하세요.

🧹 Nitpick comments (11)
src/main/java/game/controller/GameStartController.java (3)

24-26: Consider dependency injection for DAO instances.

현재 DAO 인스턴스를 필드에서 직접 생성하면 테스트 시 mocking이 어렵고 느슨한 결합이 어려워집니다. 가능하다면 생성자 또는 setter를 통한 의존성 주입을 사용하는 것이 좋습니다.


56-56: System.out.println 대신 logging framework 사용하기.

프로덕션 코드에서는 System.out.println 대신 SLF4J나 Log4j 같은 적절한 logging framework를 사용하는 것이 권장됩니다. 특히 line 56의 디버그 문은 의미가 명확하지 않으니 제거하거나 명확한 로그 메시지로 대체해 주세요.

🔎 제안된 로깅 코드 예시
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 public class GameStartController extends HttpServlet {
 	private static final long serialVersionUID = 1L;
+	private static final Logger logger = LoggerFactory.getLogger(GameStartController.class);
 	
 	private final RoomDAO roomDao = new RoomDAOImpl();
 	private final RoomPlayerDAO roomPlayerDao = new RoomPlayerDAOImpl();
 	private final RoomWebSocketService service = new RoomWebSocketService();

그리고 각 print 문을 다음과 같이 대체:

-		System.out.println(hostUserId + " " + loginUserId);
+		logger.debug("Host verification - hostUserId: {}, loginUserId: {}", hostUserId, loginUserId);
-		System.out.println("[GameStart] roomId=" + roomId + " updatedPlayers=" + updated);
+		logger.info("Game started - roomId: {}, updatedPlayers: {}", roomId, updated);

Also applies to: 65-65


74-77: 개선: 더 구체적인 예외 처리 추가하기.

현재 Exception을 광범위하게 catch하고 있어서, 어떤 작업에서 오류가 발생했는지 구별하기 어렵습니다. 데이터베이스 오류와 다른 오류를 구분하여 처리하면 디버깅과 모니터링이 더 수월할 것입니다.

🔎 예외 처리 개선 예시
 	} catch (Exception e) {
+		logger.error("Game start processing error - roomId: {}", roomId, e);
 		e.printStackTrace();
 		response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "게임 시작 처리 중 오류가 발생했습니다.");
 	}

또는 더 구체적으로:

 	try {
 		String hostUserId = roomDao.getHostUserId(roomId);
 		// ... 나머지 코드 ...
-	} catch (Exception e) {
+	} catch (IllegalArgumentException e) {
+		logger.warn("Invalid game start request - roomId: {}", roomId, e);
+		response.sendError(HttpServletResponse.SC_BAD_REQUEST, "잘못된 게임 시작 요청입니다.");
+	} catch (Exception e) {
+		logger.error("Database error during game start - roomId: {}", roomId, e);
 		e.printStackTrace();
 		response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "게임 시작 처리 중 오류가 발생했습니다.");
 	}
src/main/java/room/dao/RoomDAO.java (1)

25-30: 중복 메서드가 추가되었습니다.

findHostUserIdByRoomId(String roomId)는 기존 getHostUserId(String roomId) (Line 23)와 동일한 기능을 수행합니다. 두 메서드 모두 roomId로 host_user_id를 조회하고 없으면 null을 반환합니다.

기존 getHostUserId 메서드를 재사용하거나, 하나로 통합하는 것을 권장합니다.

src/main/java/lobby/controller/CreateRoomController.java (1)

41-43: 디버그용 System.out.println을 제거하거나 로거로 교체하세요.

프로덕션 코드에 디버그용 System.out.println이 남아있습니다. 또한, 라인 42-43의 session == null 체크는 이미 라인 29-32에서 null 검증 후 반환하므로 불필요합니다.

로깅이 필요하다면 SLF4J나 Log4j 같은 로깅 프레임워크를 사용하세요.

src/main/webapp/WEB-INF/views/room.jsp (1)

56-60: 버튼 타입을 button으로 변경하세요.

<button type="submit">이지만 감싸는 <form>이 없습니다. JavaScript 클릭 핸들러로 동작하므로 type="button"이 의미상 더 적절합니다.

🔎 수정 제안
 <c:if test="${sessionScope.loginUserId eq sessionScope.hostUserId}">
-  <button type="submit" id="btn-start" class="btn-start">
+  <button type="button" id="btn-start" class="btn-start">
       🎯 시작하기
     </button>
 </c:if>
src/main/java/room/dao/RoomDAOImpl.java (1)

172-193: 중복 구현입니다.

findHostUserIdByRoomId 메서드는 기존 getHostUserId (Lines 149-170)와 완전히 동일한 로직입니다. 동일한 SQL 쿼리와 동일한 처리 패턴을 사용합니다.

기존 getHostUserId 메서드를 재사용하거나, 인터페이스에서 하나의 메서드로 통합하는 것을 권장합니다.

🔎 통합 예시
-@Override
-public String findHostUserIdByRoomId(String roomId) throws Exception {
-
-	String sql = """
-		    SELECT host_user_id
-		    FROM room
-		    WHERE id = ?
-		""";
-
-	try (
-		Connection conn = DB.getConnection();
-		PreparedStatement ps = conn.prepareStatement(sql)) {
-		ps.setString(1, roomId);
-
-		try (ResultSet rs = ps.executeQuery()) {
-			if (rs.next()) {
-				return rs.getString("host_user_id");
-			}
-			return null;
-		}
-	}
-}
+@Override
+public String findHostUserIdByRoomId(String roomId) throws Exception {
+	return getHostUserId(roomId);
+}
src/main/webapp/static/room/room.js (2)

253-257: 불필요한 async 래퍼를 제거하세요.

async arrow function이 단순히 startButtonClick()을 호출하기만 합니다. 직접 바인딩하는 것이 더 간결합니다.

🔎 수정 제안
 const btnStart = document.querySelector("#btn-start");
-
-		btnStart?.addEventListener("click", async () => {
-			startButtonClick()
-		});
+btnStart?.addEventListener("click", startButtonClick);

259-263: 파일 상단에 정의된 상수를 재사용하세요.

startButtonClick 함수에서 page, roomId, playType을 다시 쿼리하고 있습니다. 라인 2-6에서 이미 정의한 pageEl, roomId, playType 상수를 클로저로 활용할 수 있습니다.

🔎 수정 제안
 async function startButtonClick() {
-	  const page = document.querySelector("#room-page");
-  const roomId = page.dataset.roomId;
-  const playType = page.dataset.playType;
-  const contextPath = page.dataset.contextPath || "";
+  const contextPath = pageEl?.dataset?.contextPath || "";

   try {
     const countRes = await fetch(
src/main/java/room/controller/RoomController.java (1)

40-68: 예외 처리 시 로깅 개선 권장

e.printStackTrace() 대신 로깅 프레임워크 사용을 권장합니다. 프로덕션 환경에서 표준 에러 출력은 로그 수집과 모니터링에 적합하지 않습니다.

🔎 SLF4J 로거 사용 예시
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 @WebServlet("/room/*")
 public class RoomController extends HttpServlet {
+	private static final Logger log = LoggerFactory.getLogger(RoomController.class);
 	// ...
 	} catch (Exception e) {
-		e.printStackTrace();
+		log.error("Handler execution failed for action: {}", action, e);
 		sendJson(res, 500, ApiError.of("서버 오류가 발생했습니다."));
 	}
src/main/java/room/ws/RoomWebSocketService.java (1)

86-95: 예외 발생 시 조용한 실패

HOST_CHANGE 처리 중 예외가 발생하면 스택 트레이스만 출력하고 클라이언트에 알림 없이 진행됩니다. 새 방장에게 시작 버튼이 표시되지 않을 수 있습니다. 최소한 로그 레벨을 높이거나, 재시도 로직을 고려해보세요.

📜 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 61bc386 and a392470.

📒 Files selected for processing (12)
  • src/main/java/game/controller/GameStartController.java
  • src/main/java/lobby/controller/CreateRoomController.java
  • src/main/java/room/controller/RoomController.java
  • src/main/java/room/controller/ViewRoomController.java
  • src/main/java/room/dao/RoomDAO.java
  • src/main/java/room/dao/RoomDAOImpl.java
  • src/main/java/room/dto/RoomActiveCountDTO.java
  • src/main/java/room/ws/RoomWebSocket.java
  • src/main/java/room/ws/RoomWebSocketService.java
  • src/main/webapp/WEB-INF/views/lobby.jsp
  • src/main/webapp/WEB-INF/views/room.jsp
  • src/main/webapp/static/room/room.js
💤 Files with no reviewable changes (1)
  • src/main/webapp/WEB-INF/views/lobby.jsp
🧰 Additional context used
🧬 Code graph analysis (4)
src/main/java/room/dao/RoomDAO.java (1)
src/main/webapp/static/room/room.js (1)
  • roomId (3-3)
src/main/java/room/dto/RoomActiveCountDTO.java (1)
src/main/webapp/static/room/room.js (1)
  • roomId (3-3)
src/main/java/room/dao/RoomDAOImpl.java (1)
src/main/java/util/DB.java (1)
  • DB (8-23)
src/main/java/room/ws/RoomWebSocketService.java (1)
src/main/java/room/dao/RoomDAOImpl.java (1)
  • RoomDAOImpl (15-206)
🔇 Additional comments (15)
src/main/java/game/controller/GameStartController.java (1)

62-62: 코드 가독성 개선 승인.

호스트 검증 블록과 플레이어 업데이트 로직 사이의 공백 라인 추가로 논리적 섹션이 명확히 구분되어 가독성이 향상되었습니다.

src/main/java/lobby/controller/CreateRoomController.java (1)

63-64: LGTM!

방 생성 시 세션에 hostUserId를 저장하여 방장 정보를 JSP에서 활용할 수 있도록 한 점이 적절합니다.

src/main/webapp/WEB-INF/views/room.jsp (1)

19-19: LGTM!

data-user-id 속성 추가로 JavaScript에서 현재 사용자 ID를 참조할 수 있게 되었습니다.

src/main/java/room/dto/RoomActiveCountDTO.java (1)

1-11: LGTM!

간결하고 적절한 DTO 구현입니다. Lombok 어노테이션을 활용하여 보일러플레이트 코드를 최소화했습니다.

src/main/java/room/ws/RoomWebSocket.java (2)

148-157: LGTM!

onExit 호출 시 exit 타입("ROOM_EXIT")을 전달하여 서비스 레이어에서 적절한 처리가 가능하도록 했습니다.


214-219: LGTM!

exitAndHandleHost의 결과값을 onExit에 전달하여 방장 변경 시 HOST_CHANGE 이벤트 브로드캐스트가 가능하도록 한 구현이 적절합니다.

src/main/webapp/static/room/room.js (1)

187-207: HOST_CHANGE 핸들러 구현이 적절합니다.

방장 변경 시 새 방장에게 시작 버튼을 동적으로 생성하는 로직이 잘 구현되었습니다. 기존 버튼 존재 여부 확인과 중복 생성 방지 처리도 포함되어 있습니다.

src/main/java/room/controller/RoomController.java (4)

1-31: 서블릿 구조 및 의존성 LGTM

서블릿 구조가 깔끔합니다. @WebServlet("/room/*") 어노테이션과 함수형 Handler 인터페이스 패턴이 적절히 사용되었습니다.


74-74: 사용되지 않는 userId 파라미터

handleGetCount 메서드에서 userId 파라미터가 전달되지만 사용되지 않습니다. 향후 권한 검증(예: 해당 방의 참여자인지 확인)에 활용할 예정이라면 현재 상태는 괜찮습니다. 그렇지 않다면 Handler 인터페이스 설계를 재검토하거나, 이 엔드포인트에서 인증 검증을 건너뛰는 것을 고려해보세요.


85-90: 경로 파싱 로직 LGTM

getAction 메서드가 pathInfo를 적절히 처리하고 있습니다. null 체크와 루트 경로 처리가 올바릅니다.


103-143: 내부 응답 클래스 구조 적절

ApiSuccessApiError 클래스가 일관된 API 응답 형식을 제공합니다. Gson이 리플렉션을 통해 필드를 직렬화하므로 getter 메서드는 필수는 아니지만, 향후 확장성을 위해 유지해도 무방합니다.

src/main/java/room/ws/RoomWebSocketService.java (4)

12-13: DAO 의존성 추가 LGTM

방장 정보 조회를 위한 RoomDAO 의존성 추가가 적절합니다.

Also applies to: 30-30


106-106: cleanup에서 null result 전달 - 의도 확인

cleanup 호출 시 result에 null을 전달합니다. WebSocket 연결 종료 시에도 방장 변경이 발생할 수 있는데, 이 경우 HOST_CHANGE 이벤트가 전파되지 않습니다. 방장이 브라우저를 닫거나 네트워크 연결이 끊어지는 시나리오에서 의도한 동작인지 확인이 필요합니다.


128-137: broadcastHostChanged 메서드 구현 적절

null/blank 체크 및 페이로드 구성이 기존 broadcastGameStart 패턴과 일관됩니다.


80-80: 호환성 확인 완료 - 모든 호출부가 이미 업데이트됨

onExit 메서드의 3개 파라미터 시그니처로의 변경은 이미 모든 호출부에서 처리되어 있습니다. RoomWebSocket.java의 모든 onExit 호출(151줄, 218줄)이 새 시그니처(Session, String, String)를 사용하고 있으므로 컴파일 에러가 발생하지 않습니다.

Likely an incorrect or invalid review comment.

Copy link
Collaborator

@ochanhyeok ochanhyeok 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
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: 1

🧹 Nitpick comments (5)
src/main/webapp/static/room/room.js (2)

186-206: 들여쓰기 일관성 문제와 null 검증 추가 권장

다음 사항들을 개선해 주세요:

  1. 들여쓰기 불일치: 이 섹션은 탭을 사용하지만, 파일의 나머지 부분은 스페이스를 사용합니다. 일관성을 위해 스페이스로 통일해 주세요.
  2. null 안전성: Line 191에서 p.hostUserId를 비교하기 전에 존재 여부를 검증하는 것이 좋습니다.
🔎 개선 제안
-        
-        case "HOST_CHANGE": {
-		  appendSystemLog("방장이 변경되었습니다.");
-		  const p = msg.payload;
-		
-		  if (p.hostUserId !== userId) break;
-		
-		  if (document.querySelector("#btn-start")) break;
-		
-		  const btn = document.createElement("button");
-		  btn.type = "button";          
-		  btn.id = "btn-start";
-		  btn.className = "btn-start";
-		  btn.textContent = "🎯 시작하기";
-		
-		  btn.addEventListener("click", startButtonClick);
-		
-		  document.querySelector(".side-nav")?.appendChild(btn);
-		  break;
-		  
-        }
+
+        case "HOST_CHANGE": {
+          appendSystemLog("방장이 변경되었습니다.");
+          const p = msg.payload;
+
+          if (!p.hostUserId || p.hostUserId !== userId) break;
+
+          if (document.querySelector("#btn-start")) break;
+
+          const btn = document.createElement("button");
+          btn.type = "button";
+          btn.id = "btn-start";
+          btn.className = "btn-start";
+          btn.textContent = "🎯 시작하기";
+
+          btn.addEventListener("click", startButtonClick);
+
+          document.querySelector(".side-nav")?.appendChild(btn);
+          break;
+        }

252-305: Optional chaining 추가 및 코드 간결화 권장

다음 사항들을 개선해 주세요:

  1. Optional chaining 누락 (Line 259-262): page.dataset에 접근할 때 optional chaining을 사용하지 않아 page가 null이면 TypeError가 발생할 수 있습니다.
  2. 불필요한 wrapper 함수 (Line 254-256): 화살표 함수로 감싸지 않고 startButtonClick을 직접 전달할 수 있습니다.
  3. 들여쓰기 불일치: 전체적으로 탭 대신 스페이스를 사용해 주세요.
🔎 개선 제안
 const btnStart = document.querySelector("#btn-start");
 
-		btnStart?.addEventListener("click", async () => {
-			startButtonClick()
-		});
-		
-		async function startButtonClick() {
-			  const page = document.querySelector("#room-page");
-		  const roomId = page.dataset.roomId;
-		  const playType = page.dataset.playType;
-		  const contextPath = page.dataset.contextPath || "";
+  btnStart?.addEventListener("click", startButtonClick);
+
+  async function startButtonClick() {
+    const page = document.querySelector("#room-page");
+    const roomId = page?.dataset?.roomId;
+    const playType = page?.dataset?.playType;
+    const contextPath = page?.dataset?.contextPath || "";
+
+    if (!roomId || !playType) {
+      alert("방 정보를 가져올 수 없습니다.");
+      return;
+    }

나머지 함수 본문도 동일하게 스페이스로 들여쓰기를 변경해 주세요.

src/main/java/room/controller/ViewRoomController.java (1)

18-18: 의존성 주입 고려 권장

RoomDAO를 직접 인스턴스화하고 있습니다. 향후 테스트 용이성과 결합도 감소를 위해 의존성 주입 패턴 도입을 고려해 보세요.

src/main/java/room/ws/RoomWebSocketService.java (2)

30-30: 의존성 주입 고려 권장

RoomDAO를 직접 인스턴스화하고 있습니다. 향후 테스트 용이성과 결합도 감소를 위해 의존성 주입 패턴 도입을 고려해 보세요.


86-97: null 체크 잘 구현됨, 로깅 개선 권장

이전 리뷰의 null hostUserId 처리 문제가 Line 90-92의 null 체크로 잘 해결되었습니다. 다만 Line 94의 printStackTrace()는 프로덕션 환경에 적합하지 않으므로 적절한 로거(예: SLF4J, Log4j 등)를 사용해 주세요.

🔎 로깅 개선 제안
  if ("HOST_CHANGE".equals(result)) {
    String hostUserId;
    try {
      hostUserId = roomDao.getHostUserId(roomId);
      if (hostUserId != null) {
        broadcastHostChanged(roomId, hostUserId);
      }
    } catch (Exception e) {
-     e.printStackTrace();
+     // TODO: Replace with proper logger (e.g., SLF4J)
+     System.err.println("[RoomWS] Failed to get hostUserId: " + e.getMessage());
    }
  }

더 나은 방법은 로깅 프레임워크를 도입하는 것입니다.

📜 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 b306014 and 17c2998.

📒 Files selected for processing (3)
  • src/main/java/room/controller/ViewRoomController.java
  • src/main/java/room/ws/RoomWebSocketService.java
  • src/main/webapp/static/room/room.js
🧰 Additional context used
🧬 Code graph analysis (2)
src/main/java/room/ws/RoomWebSocketService.java (1)
src/main/java/room/dao/RoomDAOImpl.java (1)
  • RoomDAOImpl (15-206)
src/main/java/room/controller/ViewRoomController.java (3)
src/main/java/room/dao/RoomDAOImpl.java (1)
  • RoomDAOImpl (15-206)
src/main/java/room/controller/EnterRoomController.java (1)
  • WebServlet (18-58)
src/main/webapp/static/room/room.js (2)
  • roomId (3-3)
  • userId (5-5)
🔇 Additional comments (2)
src/main/webapp/static/room/room.js (1)

5-5: LGTM!

userId 추출 로직이 올바르게 구현되었으며, optional chaining을 일관성 있게 사용하고 있습니다.

src/main/java/room/ws/RoomWebSocketService.java (1)

130-139: LGTM!

broadcastHostChanged 메서드가 올바르게 구현되었습니다. 입력 검증과 페이로드 구성이 적절하며, 기존 broadcast 패턴과 일관성 있게 작성되었습니다.

Copy link
Collaborator

@cl-o-lc cl-o-lc left a comment

Choose a reason for hiding this comment

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

수고하셨습니다!!!

@cl-o-lc cl-o-lc merged commit 4290195 into dev Dec 23, 2025
1 check passed
@cl-o-lc cl-o-lc deleted the FEAT-방장-게임시작-표시 branch December 23, 2025 17:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

FEAT 새로운 기능 추가 또는 기존 기능 확장

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] 방장에게만 게임 시작 표시

3 participants