Skip to content

Conversation

@sehee123
Copy link
Collaborator

@sehee123 sehee123 commented Jul 14, 2025

🛰️ Issue Number

🪐 작업 내용

pub/room/exit/{roomId} 요청으로 방 퇴장 구현

  1. roomId로 room 불러오기
  • 방이 없다면 optional 로 404 예외처리
  1. room의 플레이어가 1명이고 그 1명이 [나]라면 방 삭제 및 return (응답값은 없음)
  2. 마지막 플레이어가 아니라면 playerSessionMap에서 player삭제
  • 삭제할 player가 없다면 404 예외처리
  1. [나] == 방장 이라면 다른 플레이어를 방장으로 update
  • hashMap은 순서가 없기때문에 랜덤
  • 마찬가지로 교체할 플레이어가 없다면 404 예외처리
  1. 응답 생성
  • systemNoticeResponse, playerListResponse
image 두명 동시 로그인 테스트를 못해서 일단 소스 주석처리하고 응답값만 확인한 결과입니다. 원래는 host가 다른사람이여야하고, players에도 한명은 들어있어야겠죠..

security, 퀴즈 api 적용

  • quiz 서비스 minId가져오는 메소드 및 jpql 생성
  • 퀴즈를 가져올 때 lazy 예외가 발생(강현님 테스트 결과에서는 numberOfQuestion = 0 이여서 예외 발생 안한 것 같습니다.)
  • 예외 해결하기 위해 서비스 트랜잭션안에서 size 조회 .. -> 더 좋은 해결 방법 있으면 알려주세요.
@Transactional(readOnly = true)
    public Quiz getQuizById(Long quizId) {
        Quiz quiz = quizRepository
            .findById(quizId)
            .orElseThrow(() -> new RuntimeException("E404002: 존재하지 않는 퀴즈입니다."));
        quiz.getQuestions().size();
        return quiz;
    }

그 외

  • 퇴장 구현 시 응답시간이 2025-07-04T20:16:10Z UTC로 되어있으므로 LocaldateTime 대신 Instant로 변경 (응답 시에만)

논의사항

재연결 처리 시 세션 id가 다르게 들어오는데 현재 playerSessionMap 은 <sessionId, Player> 로 설계되어있고,
player안에 userId가 들어있으므로 재연결 시 확인로직이 비효율적이라고 생각합니다.
그래서 <UserId, SessionId> Map 이런 세션 관리 맵을 추가로 하나 더 만들고싶은데 어떻게 생각하시나요?
그리고 Player안에 있는 ConnectionState 의 용도를 잘 모르겠습니다.
현재는 룸 입장시 RoomMap에 넣어주고 퇴장 시 roomMap에서 제거를 해주는데 . 이 상태값이 왜 필요한건지 설명해주실 분 괌 🏝️

📚 Reference

✅ Check List

  • 코드가 정상적으로 컴파일되나요?
  • 테스트 코드를 통과했나요?
  • merge할 브랜치의 위치를 확인했나요?
  • Label을 지정했나요?

@sehee123 sehee123 linked an issue Jul 14, 2025 that may be closed by this pull request
3 tasks
@sehee123 sehee123 self-assigned this Jul 14, 2025
@sehee123 sehee123 added the enhancement New feature or request label Jul 14, 2025
@sehee123 sehee123 requested review from LimKangHyun, dlsrks1021, jiwon1217 and silver-eunjoo and removed request for LimKangHyun and jiwon1217 July 14, 2025 13:56
}

@Transactional(readOnly = true)
public Long getQuizMinId() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

[L4-변경제안]

quizRepository.findAll().stream().findFirst(); 이런 형식으로 해당 메서드를 대체할 수 있을 것 같습니다 !

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[L4-변경제안]

quizRepository.findAll().stream().findFirst(); 이런 형식으로 해당 메서드를 대체할 수 있을 것 같습니다 !

인덱스로 제일 작은 id값 가져오는게 더 빠르지않나요 ?.?
findAll()하면 모든 값을 가져오고 stream 돌리는거라서 id만 필요한 상황이라 findAll()은 과하지않나(?)라는 생각이 듭니다!

Copy link
Collaborator

@jiwon1217 jiwon1217 Jul 15, 2025

Choose a reason for hiding this comment

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

최소인 id 값을 가지는 행을 찾아야하기 때문에 테이블 전체를 도는 것은 똑같다고 생각합니다

Copy link
Collaborator Author

@sehee123 sehee123 Jul 15, 2025

Choose a reason for hiding this comment

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

최소인 id 값을 가지는 행을 찾아야하기 때문에 테이블 전체를 도는 것은 똑같다고 생각합니다

id값은 pk값으로 인덱스가있기 때문에 모든 테이블을 돌지 않는다고 알고있는데 ,, 잘못 알고있는걸까요?

Copy link
Collaborator

@jiwon1217 jiwon1217 Jul 15, 2025

Choose a reason for hiding this comment

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

ORDER BY절을 명시를 안해주면 순서가 보장되지 않는다고 알고 있습니다 ! PK를 지정하면 테이블 데이터 자체는 PK 기준으로 클러스터링되어 저장되지만, 쿼리에서 ORDER BY pk를 명시하지 않으면 반환 결과의 순서는 보장되지 않는다고 알고 있습니다 !
[참고링크] https://hudi.blog/mysql-no-order-by-no-sorting-guarantee/

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

ORDER BY절을 명시를 안해주면 순서가 보장되지 않는다고 알고 있습니다 ! PK를 지정하면 테이블 데이터 자체는 PK 기준으로 클러스터링되어 저장되지만, 쿼리에서 ORDER BY pk를 명시하지 않으면 반환 결과의 순서는 보장되지 않는다고 알고 있습니다 ! [참고링크] https://hudi.blog/mysql-no-order-by-no-sorting-guarantee/

MIN(id)와 같은 쿼리를 처리할 때, 데이터베이스 옵티마이저는 전체 인덱스(또는 테이블)를 스캔하는 대신, B-Tree 인덱스의 루트에서부터 가장 왼쪽의 리프 노드로 직접 이동하여 최솟값을 매우 빠르게 찾아낸다고합니다 !

+) 별개로 orderby 명시 안하면 순서 보장 안되는건 얻어갑니다.. 홍홍

https://dev.mysql.com/doc/refman/8.4/en/mysql-indexes.html
https://mangkyu.tistory.com/286

playerListResponse);
}

public RoomExitData exitRoom(Long roomId, String sessionId) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

[L4-변경제안]
메서드로 분리할 수 있는 로직들은 분리하는 것이 가독성이 좋아보입니다 !

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

안그래도 private static 으로 몇개는 분리해놨는데, 요 부분은 동시성 진행하면서 더 분리해보도록 하겠습니다! 감사합니다 ☺️


if (playerSessionMap.size() == 1 && playerSessionMap.get(sessionId) != null) {
roomRepository.removeRoom(roomId);
return new RoomExitData(destination, null, null, true);
Copy link
Collaborator

Choose a reason for hiding this comment

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

[L4-변경제안]
생성자 형식으로 null을 삽입하는 것 보다 값이 들어갈 필드에 대해서만 Builder를 열어두는 것도 좋다고 생각합니다 !

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

헉쓰! record로 만들어서 생각 못했는데 그 부분이 더 좋아보이네요. 반영하겠습니다. 감사합니다!

@dlsrks1021
Copy link
Collaborator

[L5-참고의견]
ConnectionState는 게임 시작 후 참여자의 연결이 끊겼을 때,
'게임 종료 -> 게임 대기방' 과정에서 참여자를 리스트에서 제거하기 위해 사용됩니다.

@sehee123
Copy link
Collaborator Author

[L5-참고의견] ConnectionState는 게임 시작 후 참여자의 연결이 끊겼을 때, '게임 종료 -> 게임 대기방' 과정에서 참여자를 리스트에서 제거하기 위해 사용됩니다.

헐! 게임중에는 사용자리스트를 유지해야하기때문에 있는 필드군요! 이해완료입니다. 감사합니다 ‼️

}

SystemNoticeResponse systemNoticeResponse =
new SystemNoticeResponse(
Copy link
Collaborator

Choose a reason for hiding this comment

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

[L4-변경제안]
메시지 생성 로직은 매퍼 클래스로 분리하면 좋을것 같습니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

다른 객체에서 변환(?) 하는 개념이라고 생각못했는데 듣고보니 옮기는게 맞겠군요. 감사합니다!


// todo security
Player player = new Player(1L, "빵야빵야");
Player player = createPlayer();
Copy link
Collaborator

Choose a reason for hiding this comment

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

[L3-중요질문]
새 플레이어가 방에 입장했을 때, SYSTEM NOTICE타입의 입장메시지가 브로드캐스팅되지 않는것 같습니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

웹소켓 명세에 빠져있는 부분이라 놓쳤군요..! 감사합니다. 추가하겠습니다. 😇

sehee123 added 5 commits July 15, 2025 10:22
# Conflicts:
#	backend/src/main/java/io/f1/backend/domain/quiz/app/QuizService.java
#	backend/src/main/java/io/f1/backend/domain/quiz/dao/QuizRepository.java
# Conflicts:
#	backend/src/main/java/io/f1/backend/domain/game/app/RoomService.java
#	backend/src/main/java/io/f1/backend/domain/game/dto/RoomExitData.java
#	backend/src/main/java/io/f1/backend/domain/game/websocket/GameSocketController.java
#	backend/src/main/java/io/f1/backend/domain/quiz/app/QuizService.java
#	backend/src/main/java/io/f1/backend/domain/quiz/dao/QuizRepository.java
public RoomCreateResponse saveRoom(RoomCreateRequest request, Map<String, Object> loginUser) {
public RoomCreateResponse saveRoom(RoomCreateRequest request) {

Long minQuizId = quizService.getQuizMinId();
Copy link
Collaborator

Choose a reason for hiding this comment

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

[L4-변경제안]
변수에서는 minQuizId고, 메서드명은 QuizMinId인데, 이걸 하나로 통일하는 것이 좋지 않을까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[L4-변경제안] 변수에서는 minQuizId고, 메서드명은 QuizMinId인데, 이걸 하나로 통일하는 것이 좋지 않을까요?

분명.. 통일했다고 생각했는데!! 요런 디테일... 감사합니다. ☺️

Copy link
Collaborator

@jiwon1217 jiwon1217 left a comment

Choose a reason for hiding this comment

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

고생하셨습니다 !


PlayerListResponse playerListResponse = toPlayerListResponse(room);

return new RoomExitData(destination, playerListResponse, systemNoticeResponse, false);
Copy link
Collaborator

Choose a reason for hiding this comment

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

removedRoom이라는 필드가 방이 삭제되었을 때 true로 사용되고 있지 않은 것 같은데 이 필드의 용도는 뭔지 궁금합니다.

Copy link
Collaborator Author

@sehee123 sehee123 Jul 15, 2025

Choose a reason for hiding this comment

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

removedRoom이라는 필드가 방이 삭제되었을 때 true로 사용되고 있지 않은 것 같은데 이 필드의 용도는 뭔지 궁금합니다.

방이 삭제되었을때는 따로 응답 값을 보내주지 보내주지 않아도되고, 삭제가 안되었을때만 응답값을 보내줘야하기에 구분 필드를 넣었습니다. 원래 record로 사용시에는 true로 생성했는데, 빌더로 바꾸면서 true가 빠진 것 같습니다! 알려주셔서.. 감사합니다 ^_^


String destination = roomExitData.getDestination();

if (!roomExitData.isRemovedRoom()) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

아, 여기서 사용되는군요!! 확인했습니다.

Copy link
Collaborator

@silver-eunjoo silver-eunjoo left a comment

Choose a reason for hiding this comment

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

고생하셨습니다 ! 굉장하십니다.

@sehee123 sehee123 merged commit 233cc6d into dev Jul 15, 2025
2 of 3 checks passed
@sehee123 sehee123 deleted the feat/21 branch July 15, 2025 02:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feat] 방 퇴장 구현

6 participants