Skip to content

Commit

Permalink
feat: WebSocket으로 채팅 기능 설계 #81
Browse files Browse the repository at this point in the history
  • Loading branch information
sudago committed Sep 19, 2023
1 parent dc1109b commit 51dbc5c
Show file tree
Hide file tree
Showing 20 changed files with 304 additions and 40 deletions.
4 changes: 4 additions & 0 deletions be/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ dependencies {

//redis
implementation 'org.springframework.boot:spring-boot-starter-data-redis'

// webSocket
implementation 'org.springframework.boot:spring-boot-starter-websocket'
implementation 'org.webjars:stomp-websocket'
}

tasks.named('test') {
Expand Down
30 changes: 30 additions & 0 deletions be/src/main/java/kr/codesquad/chat/controller/ChatController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package kr.codesquad.chat.controller;

import javax.servlet.http.HttpServletRequest;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import kr.codesquad.chat.dto.ChatRoomCreateRequest;
import kr.codesquad.chat.service.ChatService;
import kr.codesquad.util.Constants;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@RestController
public class ChatController {

private final ChatService chatService;

@PostMapping("/")
public ResponseEntity<Void> createChatRoom(@RequestBody ChatRoomCreateRequest chatRoomCreateRequest,
HttpServletRequest request) {
String loginId = (String)request.getAttribute(Constants.LOGIN_ID);
chatService.createRoom(chatRoomCreateRequest, loginId);
return ResponseEntity.status(HttpStatus.CREATED).build();
}

}
15 changes: 15 additions & 0 deletions be/src/main/java/kr/codesquad/chat/dto/ChatMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package kr.codesquad.chat.dto;

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

import kr.codesquad.chat.entity.ChatRoom;

@Mapper
public interface ChatMapper {
ChatMapper INSTANCE = Mappers.getMapper(ChatMapper.class);

@Mapping(target = "senderId", source = "userId")
ChatRoom toChatRoom(ChatRoomCreateRequest chatRoomCreateRequest, Long userId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package kr.codesquad.chat.dto;

import lombok.Getter;

@Getter
public class ChatRoomCreateRequest {
private Long itemId;
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,19 @@
@Getter
@Entity
@NoArgsConstructor
public class Message extends TimeStamped {
public class ChatMessage extends TimeStamped {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private Long chatId;
private Long chatRoomId;
@Column(nullable = false, length = 200)
private String content;

@Builder
public Message(Long id, Long chatId, String content) {
public ChatMessage(Long id, Long chatRoomId, String content) {
this.id = id;
this.chatId = chatId;
this.chatRoomId = chatRoomId;
this.content = content;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@
@Getter
@Entity
@NoArgsConstructor
public class Chat {
public class ChatRoom {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private Long itemId;
@Column(nullable = false, length = 45)
@Column(length = 45)
private String lastMessageId;
@Column(nullable = false)
private Long senderId;

@Builder
public Chat(Long id, Long itemId, String lastMessageId, Long senderId) {
public ChatRoom(Long id, Long itemId, String lastMessageId, Long senderId) {
this.id = id;
this.itemId = itemId;
this.lastMessageId = lastMessageId;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package kr.codesquad.chat.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import kr.codesquad.chat.entity.ChatMessage;

public interface ChatMessageRepository extends JpaRepository<ChatMessage, Long> {

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import org.springframework.data.jpa.repository.JpaRepository;

import kr.codesquad.chat.entity.Chat;
import kr.codesquad.chat.entity.ChatRoom;

public interface ChatRepository extends JpaRepository<Chat, Long> {
public interface ChatRoomRepository extends JpaRepository<ChatRoom, Long> {
int countByItemId(Long itemId);
}

This file was deleted.

26 changes: 26 additions & 0 deletions be/src/main/java/kr/codesquad/chat/service/ChatService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package kr.codesquad.chat.service;

import org.springframework.stereotype.Service;

import kr.codesquad.chat.dto.ChatMapper;
import kr.codesquad.chat.dto.ChatRoomCreateRequest;
import kr.codesquad.chat.repository.ChatMessageRepository;
import kr.codesquad.chat.repository.ChatRoomRepository;
import kr.codesquad.user.entity.User;
import kr.codesquad.user.repository.UserRepository;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Service
public class ChatService {

private final ChatRoomRepository chatRoomRepository;
private final ChatMessageRepository chatMessageRepository;
private final UserRepository userRepository;

public void createRoom(ChatRoomCreateRequest chatRoomCreateRequest, String loginId) {
User user = userRepository.findByLoginId(loginId);
chatRoomRepository.save(ChatMapper.INSTANCE.toChatRoom(chatRoomCreateRequest, user.getId()));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ protected SecurityFilterChain configure(HttpSecurity http) throws Exception {
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/h2-console/**", "/api/locations**").permitAll()
.antMatchers("/h2-console/**", "/api/locations**", "/ws/chat").permitAll()
.antMatchers(HttpMethod.POST, "/api/users").permitAll()
.anyRequest()
.authenticated() //다른 요청은 인증 필요함
Expand Down
23 changes: 23 additions & 0 deletions be/src/main/java/kr/codesquad/core/config/WebSocketConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package kr.codesquad.core.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

import kr.codesquad.core.websocket.WebSocketHandler;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Configuration
@RequiredArgsConstructor
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
private final WebSocketHandler webSocketHandler;

@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(webSocketHandler, "/ws/chat").setAllowedOrigins("*");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
@RequiredArgsConstructor
public class AuthorizationFilter implements Filter {
private static final String[] whiteListUris = {"/h2-console/**", "/api/users", "/api/login",
"/api/reissue-access-token", "/api/oauth/**", "/api/redirect/**", "/redirect/**", "/api/locations**"};
"/api/reissue-access-token", "/api/oauth/**", "/api/redirect/**", "/redirect/**", "/api/locations**",
"/ws/chat"};

private final JwtProvider jwtProvider;
private final RedisUtil redisUtil;
Expand Down
12 changes: 12 additions & 0 deletions be/src/main/java/kr/codesquad/core/websocket/Message.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package kr.codesquad.core.websocket;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Message {
private String roomId;
private String sender;
private String message;
}
29 changes: 29 additions & 0 deletions be/src/main/java/kr/codesquad/core/websocket/MsgController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package kr.codesquad.core.websocket;

import java.util.List;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import lombok.RequiredArgsConstructor;

@RestController
@RequiredArgsConstructor
@RequestMapping("/chat")
public class MsgController {
private final MsgService msgService;

@PostMapping
public MsgRoom createRoom(@RequestParam String name) {
return msgService.createRoom(name);
}

@GetMapping
public List<MsgRoom> findAllRoom() {
return msgService.findAllRoom();
}

}
33 changes: 33 additions & 0 deletions be/src/main/java/kr/codesquad/core/websocket/MsgRoom.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package kr.codesquad.core.websocket;

import java.util.HashSet;
import java.util.Set;

import org.springframework.web.socket.WebSocketSession;

import lombok.Builder;
import lombok.Getter;
import lombok.Setter;

@Setter
@Getter
public class MsgRoom {
private String roomId;
private Set<WebSocketSession> sessions = new HashSet<>();

@Builder
public MsgRoom(String roomId) {
this.roomId = roomId;
}

public void handleActions(WebSocketSession session, Message message, MsgService msgService) {
sessions.add(session);
sendMessage(message, msgService);
}

public <T> void sendMessage(T message, MsgService messageService) {
sessions.parallelStream().forEach(session -> messageService.sendMessage(session, message));
}

}

52 changes: 52 additions & 0 deletions be/src/main/java/kr/codesquad/core/websocket/MsgService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package kr.codesquad.core.websocket;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;

import org.springframework.stereotype.Service;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import com.fasterxml.jackson.databind.ObjectMapper;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
@RequiredArgsConstructor
public class MsgService {
private final ObjectMapper objectMapper;
private Map<String, MsgRoom> msgRooms;

@PostConstruct
private void init() {
msgRooms = new LinkedHashMap<>();
}

public List<MsgRoom> findAllRoom() {
return new ArrayList<>(msgRooms.values());
}

public MsgRoom findById(String roomId) {
return msgRooms.get(roomId);
}

public MsgRoom createRoom(String name) {
String roomId = name;
return MsgRoom.builder().roomId(roomId).build();
}

public <T> void sendMessage(WebSocketSession session, T message) {
try {
session.sendMessage(new TextMessage(objectMapper.writeValueAsString(message)));
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
}
30 changes: 30 additions & 0 deletions be/src/main/java/kr/codesquad/core/websocket/WebSocketHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package kr.codesquad.core.websocket;

import org.springframework.stereotype.Component;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import com.fasterxml.jackson.databind.ObjectMapper;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
@RequiredArgsConstructor
public class WebSocketHandler extends TextWebSocketHandler {

private final MsgService msgService;
private final ObjectMapper objectMapper;

@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String payload = message.getPayload();
log.info("payload:{}", payload);

Message msg = objectMapper.readValue(payload, Message.class);
MsgRoom room = msgService.findById(msg.getRoomId());
room.handleActions(session, msg, msgService);
}
}
Loading

0 comments on commit 51dbc5c

Please sign in to comment.