-
Notifications
You must be signed in to change notification settings - Fork 3
[feat] 방 입장 및 validation 체크 #19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 12 commits
4c599cf
1fd7e46
f92279f
64a478f
e5925de
19497f1
0608142
184d05b
2f33efb
1cdb9e1
39481e4
c7ba52c
ed9fcdf
a4b4c1b
a880670
655e3ae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -40,4 +40,9 @@ out/ | |
| .env | ||
|
|
||
| ### .idea ### | ||
| .idea | ||
| .idea | ||
|
|
||
|
|
||
| src/main/resources/static/ws-test.html | ||
| src/main/resources/static/ws-test.js | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package io.f1.backend.domain.game.dto; | ||
|
|
||
| public enum MessageType { | ||
| ROOM_SETTING, | ||
| GAME_SETTING, | ||
| PLAYER_LIST, | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| package io.f1.backend.domain.game.dto; | ||
|
|
||
| import io.f1.backend.domain.game.dto.response.GameSettingResponse; | ||
| import io.f1.backend.domain.game.dto.response.PlayerListResponse; | ||
| import io.f1.backend.domain.game.dto.response.RoomSettingResponse; | ||
|
|
||
| public record RoomInitialData( | ||
| String destination, | ||
| RoomSettingResponse roomSettingResponse, | ||
| GameSettingResponse gameSettingResponse, | ||
| PlayerListResponse playerListResponse) {} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package io.f1.backend.domain.game.dto; | ||
|
|
||
| public interface WebSocketDto<T> { | ||
| MessageType getType(); | ||
|
|
||
| T getMessage(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| package io.f1.backend.domain.game.dto.request; | ||
|
|
||
| public record RoomValidationRequest(Long roomId, String password) {} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| package io.f1.backend.domain.game.dto.response; | ||
|
|
||
| import io.f1.backend.domain.game.dto.MessageType; | ||
| import io.f1.backend.domain.game.dto.WebSocketDto; | ||
|
|
||
| import lombok.AllArgsConstructor; | ||
| import lombok.Getter; | ||
|
|
||
| @Getter | ||
| @AllArgsConstructor | ||
| public class DefaultWebSocketResponse<T> implements WebSocketDto<T> { | ||
| private final MessageType type; | ||
| private final T message; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 바로 WebSocketDto를 클래스로 사용하지 않고, 이를 implement 하는 식으로 구현한 이유가 궁금합니다 ! 추후에 확장 가능성이 있는 건가요?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 우선은 WebSocket으로 주고받는 응답은 반드시 type과 message라는 구조를 가져야 한다는 의도를 명확히 하기 위해서 인터페이스로 분리했습니다. 추후에 확장 가능성을 생각할 때도 interface로 강제해두면 이후 응답 형태가 늘어나도 일관된 구조를 유지할 수 있다는 점에서 인터페이스를 설계하였습니다!
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 옹 ! 이해했습니다. 답변 감사합니다 ! :) |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| package io.f1.backend.domain.game.dto.response; | ||
|
|
||
| public record GameSettingResponse(int round, int timeLimit, QuizResponse quiz) {} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| package io.f1.backend.domain.game.dto.response; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public record PlayerListResponse(String host, List<PlayerResponse> players) {} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| package io.f1.backend.domain.game.dto.response; | ||
|
|
||
| public record PlayerResponse(String nickname, boolean ready) {} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| package io.f1.backend.domain.game.dto.response; | ||
|
|
||
| public record QuizResponse( | ||
| Long quizId, String title, String description, String thumbnailUrl, int numberOfQuestion) {} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| package io.f1.backend.domain.game.dto.response; | ||
|
|
||
| public record RoomSettingResponse(String roomName, int maxUserCount, int currentUserCount) {} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| package io.f1.backend.domain.game.websocket; | ||
|
|
||
| import io.f1.backend.domain.game.app.RoomService; | ||
| import io.f1.backend.domain.game.dto.MessageType; | ||
| import io.f1.backend.domain.game.dto.RoomInitialData; | ||
|
|
||
| import lombok.RequiredArgsConstructor; | ||
|
|
||
| import org.springframework.messaging.Message; | ||
| import org.springframework.messaging.handler.annotation.DestinationVariable; | ||
| import org.springframework.messaging.handler.annotation.MessageMapping; | ||
| import org.springframework.messaging.simp.stomp.StompHeaderAccessor; | ||
| import org.springframework.stereotype.Controller; | ||
|
|
||
| @Controller | ||
| @RequiredArgsConstructor | ||
| public class GameSocketController { | ||
|
|
||
| private final MessageSender messageSender; | ||
| private final RoomService roomService; | ||
|
|
||
| @MessageMapping("/room/enter/{roomId}") | ||
| public void roomEnter(@DestinationVariable Long roomId, Message<?> message) { | ||
|
|
||
| StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message); | ||
| String websocketSessionId = accessor.getSessionId(); | ||
|
|
||
| RoomInitialData roomInitialData = roomService.enterRoom(roomId, websocketSessionId); | ||
| String destination = roomInitialData.destination(); | ||
|
|
||
| messageSender.send( | ||
| destination, MessageType.ROOM_SETTING, roomInitialData.roomSettingResponse()); | ||
| messageSender.send( | ||
| destination, MessageType.GAME_SETTING, roomInitialData.gameSettingResponse()); | ||
| messageSender.send( | ||
| destination, MessageType.PLAYER_LIST, roomInitialData.playerListResponse()); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| package io.f1.backend.domain.game.websocket; | ||
|
|
||
| import io.f1.backend.domain.game.dto.MessageType; | ||
| import io.f1.backend.domain.game.dto.response.DefaultWebSocketResponse; | ||
|
|
||
| import lombok.RequiredArgsConstructor; | ||
|
|
||
| import org.springframework.messaging.simp.SimpMessagingTemplate; | ||
| import org.springframework.stereotype.Component; | ||
|
|
||
| @Component | ||
| @RequiredArgsConstructor | ||
| public class MessageSender { | ||
|
|
||
| private final SimpMessagingTemplate messagingTemplate; | ||
|
|
||
| public <T> void send(String destination, MessageType type, T message) { | ||
| messagingTemplate.convertAndSend( | ||
| destination, new DefaultWebSocketResponse<>(type, message)); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| package io.f1.backend.global.config; | ||
|
|
||
| import lombok.RequiredArgsConstructor; | ||
| import lombok.extern.slf4j.Slf4j; | ||
|
|
||
| import org.springframework.messaging.Message; | ||
| import org.springframework.messaging.MessageChannel; | ||
| import org.springframework.messaging.simp.stomp.StompCommand; | ||
| import org.springframework.messaging.simp.stomp.StompHeaderAccessor; | ||
| import org.springframework.messaging.support.ChannelInterceptor; | ||
| import org.springframework.stereotype.Component; | ||
|
|
||
| @Slf4j | ||
| @Component | ||
| @RequiredArgsConstructor | ||
| public class StompChannelInterceptor implements ChannelInterceptor { | ||
|
|
||
| @Override | ||
| public Message<?> preSend(Message<?> message, MessageChannel channel) { | ||
|
|
||
| StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message); | ||
| StompCommand command = accessor.getCommand(); | ||
| String sessionId = accessor.getSessionId(); | ||
| String destination = accessor.getDestination(); | ||
|
|
||
| if (command == null) { | ||
| throw new IllegalArgumentException("Stomp command required"); | ||
| } | ||
|
|
||
| switch (command) { | ||
| case CONNECT -> log.info("CONNECT : 세션 연결 - sessionId = {}", sessionId); | ||
|
|
||
| case SUBSCRIBE -> { | ||
| if (destination != null && sessionId != null) { | ||
| log.info("SUBSCRIBE : 구독 시작 destination = {}", destination); | ||
| } | ||
| } | ||
|
|
||
| case SEND -> log.info("SEND : 요청 destination = {}", destination); | ||
|
|
||
| case DISCONNECT -> log.info("DISCONNECT : 연결 해제 sessionId = {}", sessionId); | ||
|
|
||
| default -> throw new IllegalStateException("Unexpected command: " + command); | ||
| } | ||
|
|
||
| return message; | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.