Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
ac8c046
[modify] 바뀐 배포 주소에 맞게 핸드쉐이크 주소 변경
jung-min-ju May 4, 2025
dbc7f3a
[add] AI mode 관련 enum 클래스 추가
jung-min-ju May 4, 2025
ddf6538
[add] 방 생성 요청 dto 추가
jung-min-ju May 4, 2025
c7f60c7
[add] 커스텀 예외 처리 클래스 추가
jung-min-ju May 4, 2025
f2d55fb
[add] 예외 코드 클래스 추가
jung-min-ju May 4, 2025
d4d082e
[add] 예외 발생 시 클라이언트에게 전송할 res dto 추가
jung-min-ju May 4, 2025
e740f5c
[add] 필드 검증 실패 시 발생할 필드예외처리 클래스 추가
jung-min-ju May 4, 2025
f300a57
[remove] asyncApi 사용으로 인해 스웨거 관련 클래스들 삭제
jung-min-ju May 4, 2025
48973c6
[add] 게임 모드 enum 클래스 추가
jung-min-ju May 4, 2025
ba14b06
[remove] 필요없는 클래스 삭제
jung-min-ju May 4, 2025
129a766
[add] 전역적으로 쓸 예외모음 클래스 추가
jung-min-ju May 4, 2025
0c6530c
[modify] redis 직렬화 추가
jung-min-ju May 4, 2025
ea040d2
[add] 대기방 crud 컨트롤러 api 생성
jung-min-ju May 4, 2025
609b5ff
[add] 대기방 관련 예외 케이스 클래스 생성
jung-min-ju May 4, 2025
819f78c
[add] 대기방 정보 저장할 enum 클래스 생성
jung-min-ju May 4, 2025
8d7680c
[add] 대기방 pub 로직 담당할 핸들러 클래스 추가
jung-min-ju May 4, 2025
461df96
[add] 대기방 참가시 사용할 dto 클래스 추가
jung-min-ju May 4, 2025
03e5bad
[add] 대기방 도메인 클래스 추가
jung-min-ju May 4, 2025
53a0ee8
[add] RoomField enum 정의 및 Redis 필드 매핑 설정
jung-min-ju May 4, 2025
aad8ec0
[add] 대기방 crud 비즈니스 로직 정의 클래스 추가
jung-min-ju May 4, 2025
2490fc6
[add] 대기방 crud 시, 사용자와 대기방의 관계에 대한 비즈니스 로직 클래스 추가
jung-min-ju May 4, 2025
327c48e
[add] 대기방 업데이트 요청 시 사용할 dto 추가
jung-min-ju May 4, 2025
db43354
[docs] 주석 명확하게 수정
jung-min-ju May 4, 2025
74dd0d3
[add] 대기방 관련 채널 상수 변수 추가
jung-min-ju May 4, 2025
21e50d2
[add] 현재 http 서버 gloablException 코드 추가
jung-min-ju May 5, 2025
983ca2a
[feat] sub/pub 핸들러 예외 처리 추가
jung-min-ju May 5, 2025
8a6f9d0
[modify] 예외code 형식 http 서버와 동일하게 변경
jung-min-ju May 5, 2025
4d926fb
[modify] Room Filed 예외 발생 시 메시지 변경
jung-min-ju May 5, 2025
0cce418
[feat] 소켓 전역 글로벌 예외 처리 클래스 추가
jung-min-ju May 5, 2025
0312b92
[chore] 쓸모없는 주석 삭제
jung-min-ju May 5, 2025
d8af3dc
[modify] ci/cd 업데이트 및 api 명세서 업데이트
jung-min-ju May 5, 2025
a052b9c
[remove] 안쓰는 파일 삭제
jung-min-ju May 9, 2025
afc641f
[add] 채팅 기록 관리할 클래스 구조 잡기
jung-min-ju May 9, 2025
7f02f14
[add] 채팅 핸들러 빈 식별자 추가
jung-min-ju May 9, 2025
db479a6
[refactor] 웹소켓 @Valid를 통한 에러 처리 불가능으로 인해 CreateRoomRequest의 validatio…
jung-min-ju May 9, 2025
cba9949
[add] 메시지 값 반환 함수 추가
jung-min-ju May 9, 2025
31299f3
[refactor] 안쓰는 인터페이스 삭제로 인한 코드 수정
jung-min-ju May 9, 2025
1e07e4c
[add] 빈 식별자 추가
jung-min-ju May 9, 2025
697481b
[add] 1:1 웹소켓 통신을 위한 핸들러 추가
jung-min-ju May 9, 2025
7814eda
refactor: Redis 메시지 처리 로직 리팩토링 및 DTO 변환 안정성 향상
jung-min-ju May 9, 2025
65c3621
refactor: RedisMessageListenerContainer 및 Jackson 메시지 컨버터 제거
jung-min-ju May 9, 2025
05daa42
refactor: Redis Pub/Sub 메시지 처리 구조 개선 및 roomMessageFlow 안정성 보강
jung-min-ju May 9, 2025
9b01607
feat: Redis 메시지 라우팅용 DTO 정의
jung-min-ju May 9, 2025
54c2798
feat: 사용자 권한 enum 클래스
jung-min-ju May 9, 2025
28557be
feat: 방 생성 로직
jung-min-ju May 9, 2025
b7853c2
feat: principal 객체 생성 위한 클래스
jung-min-ju May 9, 2025
6944e9b
chore : WebSocket 첫 연결 시 호출 함수. 추후 사용 계획 주석 처리
jung-min-ju May 9, 2025
ea932ee
remove : 안쓰는 파일 삭제
jung-min-ju May 9, 2025
3f06f54
feat: 채널명 명확하게 고정.
jung-min-ju May 9, 2025
ac762b0
add: 사용자 인증용 StompUserInterceptor 등록
jung-min-ju May 9, 2025
d42ce8a
remove: redis의 key로 쓰일 roomid 삭제
jung-min-ju May 9, 2025
2088077
add: MethodArgumentNotValidException 처리 함수 추가
jung-min-ju May 9, 2025
193bc7e
add: 방 고유 코드 생성 로직 추가.
jung-min-ju May 9, 2025
ae007cd
add : 방 생성 로직 완성
jung-min-ju May 9, 2025
9e612df
modify : id 변수 추가
jung-min-ju May 9, 2025
e784282
add : 방 고유코드 4자리 만드는 클래스 추가.
jung-min-ju May 9, 2025
9a9e2e5
add : 방 로직 관련 예외코드 추가
jung-min-ju May 9, 2025
9e46a89
refact : 쓰지않는 인터페이스 삭제로 인한 수정
jung-min-ju May 9, 2025
3fe7553
chore: Spring Integration 및 Redis 연동 라이브러리 의존성 추가
jung-min-ju May 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-redis' //redis
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.integration:spring-integration-core'
implementation 'org.springframework.integration:spring-integration-redis'
implementation 'org.springframework.integration:spring-integration-redis'


}

Expand Down
12 changes: 12 additions & 0 deletions src/main/java/spring/socket_server/common/auth/Role.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package spring.socket_server.common.auth;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Getter
public enum Role {
USER("ROLE_USER"), ADMIN("ROLE_ADMIN"), GUEST("ROLE_GUEST");

private final String value;
}
22 changes: 22 additions & 0 deletions src/main/java/spring/socket_server/common/auth/StompPrincipal.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package spring.socket_server.common.auth;

import java.security.Principal;

public class StompPrincipal implements Principal {
private final String name;
private final Role role;

public StompPrincipal(String name, Role roles) {
this.name = name;
this.role = roles;
}

@Override
public String getName() {
return name;
}

public Role getRoles() {
return role;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package spring.socket_server.common.auth;

import org.springframework.http.server.ServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.DefaultHandshakeHandler;

import java.security.Principal;
import java.util.Map;

//TODO: 이후 WebSocket 연결 시 Authorization 헤더의 JWT를 파싱해 userId와 Role을 설정하도록 수정 예정
// 현재는 구동만을 위한 간략화 코드.
public class StompPrincipalHandshakeHandler extends DefaultHandshakeHandler {

@Override
protected Principal determineUser(ServerHttpRequest request,
WebSocketHandler wsHandler,
Map<String, Object> attributes) {

// 1) user-id 헤더 또는 쿼리 파라미터로부터 사용자 이름을 꺼냄
String userId = request.getHeaders().getFirst("user-id");

// 2) 역할 정보
Role role = Role.USER;
return new StompPrincipal(userId, role);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package spring.socket_server.common.auth;

import lombok.extern.slf4j.Slf4j;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;

import java.security.Principal;
import java.util.UUID;

@Slf4j
// 2) STOMP CONNECT 프레임을 가로채는 Interceptor
public class StompUserInterceptor implements ChannelInterceptor {

@Override
public Message<?> preSend (Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor =
MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);

if (accessor != null && StompCommand.CONNECT.equals(accessor.getCommand())) {

String rawUserId = accessor.getFirstNativeHeader("user-id");

String userId = (rawUserId == null || rawUserId.isBlank())
? "guest-" + UUID.randomUUID()
: rawUserId;

Principal principal = () -> userId;
accessor.setUser(principal);
}
return message;
}


}

49 changes: 11 additions & 38 deletions src/main/java/spring/socket_server/common/config/RedisConfig.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package spring.socket_server.common.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
Expand All @@ -9,17 +10,9 @@
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import spring.socket_server.domain.chat.handler.ChattingPubSubHandler;
import spring.socket_server.domain.game.handler.GamePubSubHandler;

import static spring.socket_server.common.constants.WebSocketConstants.CHAT_PREFIX;
import static spring.socket_server.common.constants.WebSocketConstants.GAME_PREFIX;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;

@EnableCaching
@Configuration
Expand All @@ -41,34 +34,6 @@ public RedisConnectionFactory redisConnectionFactory() {
return lettuceConnectionFactory;
}

//Redis Pub/Sub에서 메시지를 리스닝하는 컨테이너 -> (구독자가 어떤 메시지를 받을지 관리)
@Bean
public RedisMessageListenerContainer redisMessageListenerContainer(
RedisConnectionFactory connectionFactory,
MessageListenerAdapter chatListenerAdapter,
MessageListenerAdapter gameListenerAdapter) {

RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);

//컨테이너는 각 채널의 리스닝어뎁터를 받음 -> 해당 컨테이너가 모든 채널 관리가 가능함!
container.addMessageListener(chatListenerAdapter, new PatternTopic(CHAT_PREFIX+"*")); //전체 채팅 채널
container.addMessageListener(gameListenerAdapter, new PatternTopic(GAME_PREFIX + "*"));//인게임 관련 채널

return container;
}

// Redis에서 메시지를 수신하면 RedisSubscriber 클래스의 onMessage 메서드를 호출하도록 설정
@Bean
public MessageListenerAdapter chatListenerAdapter(ChattingPubSubHandler chattingSubscriber) {
return new MessageListenerAdapter(chattingSubscriber, "onMessage");
}

@Bean
public MessageListenerAdapter gameListenerAdapter(GamePubSubHandler gameSubscriber) {
return new MessageListenerAdapter(gameSubscriber, "onMessage");
}

@Bean
@Primary
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
Expand All @@ -84,7 +49,7 @@ public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory redisC
return redisTemplate;
}

@Bean
@Bean("jsonRedisTemplate")
public RedisTemplate <String, Object> redisTemplateObject(RedisConnectionFactory connectionFactory) {
RedisTemplate <String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
Expand All @@ -102,5 +67,13 @@ public RedisTemplate <String, Object> redisTemplateObject(RedisConnectionFactory
return template;
}

@Bean
public MappingJackson2MessageConverter jackson2MessageConverter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setObjectMapper(new ObjectMapper());
converter.setSerializedPayloadClass(String.class); // JSON 문자열 처리
return converter;
}


}
Loading