Skip to content

Commit

Permalink
Merge pull request #2 from yudonggeun/chatting
Browse files Browse the repository at this point in the history
Chatting
  • Loading branch information
yudonggeun authored Sep 20, 2023
2 parents 30774b0 + e0dbe91 commit 8e78810
Show file tree
Hide file tree
Showing 78 changed files with 3,510 additions and 101 deletions.
54 changes: 27 additions & 27 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,53 +1,53 @@
plugins {
id 'java'
id 'org.springframework.boot' version '3.1.3'
id 'io.spring.dependency-management' version '1.1.3'
id 'org.asciidoctor.jvm.convert' version '3.3.2'
id 'java'
id 'org.springframework.boot' version '3.1.3'
id 'io.spring.dependency-management' version '1.1.3'
id 'org.asciidoctor.jvm.convert' version '3.3.2'
}

group = 'com.websocket'
version = '0.0.1-SNAPSHOT'

java {
sourceCompatibility = '17'
sourceCompatibility = '17'
}

configurations {
compileOnly {
extendsFrom annotationProcessor
}
compileOnly {
extendsFrom annotationProcessor
}
}

repositories {
mavenCentral()
mavenCentral()
}

ext {
set('snippetsDir', file("build/generated-snippets"))
set('snippetsDir', file("build/generated-snippets"))
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-websocket'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-websocket'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
// developmentOnly 'org.springframework.boot:spring-boot-docker-compose'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
}

tasks.named('test') {
outputs.dir snippetsDir
useJUnitPlatform()
outputs.dir snippetsDir
useJUnitPlatform()
}

tasks.named('asciidoctor') {
inputs.dir snippetsDir
dependsOn test
}
inputs.dir snippetsDir
dependsOn test
}
Binary file added file/addFriend.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added file/chat_info.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added file/chat_room.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added file/chat_room_menu.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added file/chating_room.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added file/erd.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added file/login.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added file/main.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added file/welcome.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file removed index.adoc
Empty file.
70 changes: 70 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# 실시간 채팅 웹 사이트

Version1 : From 23-09-13 To 23-09-20

## 1. 기획 의도

- STOMP 프로토콜을 이용한 실시간 채팅 서비스 기능을 개발하여 STOMP 기술을 익힌다.
- 채팅 기능 구현을 통해서 동기화를 고려한 개발을 진행해본다.
- 메시지 브로커 (RabbitMQ, Kafka) 서비스를 이용해서 아키텍처를 구성한다.

## 2. 개발 기능

- 회원 기능
- 간단 회원가입
- 로그인
- 친구 추가
- 친구 삭제
- 친구 목록 조회
- 채팅 기능
- 채팅 입력
- 채팅 삭제
- 채팅 목록 조회
- 채팅방 기능
- 채팅방 생성
- 채팅방 조회
- 채팅방 나가기
- 친구 초대하기
- 채팅방 배경색 설정
- 채팅 안 읽음 확인

## 3. 아키텍처

### version 1

browser → server → db

### version 2

browser → messageQueue → server → db

## 4. ERD 설계
![erd image](/file/erd.png)
[erd cloud link](https://www.erdcloud.com/d/44AHnBQQtTh4HtfwD)

## 5. 사용 기술

version 1

- `spring boot` `spring websocket` `STOMP` `JPA`

version 2

- `rabbitMQ`


## 6. 개발 UI

| 이름 | 상세 | 화면 |
|---------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------|
| 로그인 페이지 | 닉네임과 비밀번호를 통한 로그인 페이지로서 모든 입력 값을 입력해야 로그인 버튼이 동작한다. | ![로그인](/file/login.png) |
| 회원가입 페이지 | 닉네임과 비밀번호를 통한 회원가입 페이지로서 모든 입력 값을 입력해야 회원가입 버튼이 동작한다. | ![회원가입](/file/welcome.png) |
| 친구추가 페이지 | 친구의 닉네임으로 친구를 추가할 수 있다. | ![친구추가](/file/addFriend.png) |
| 서비스 간단 설명 페이지 | 1. 채팅방 만들기 : `채팅방 만들기` 버튼을 클릭하면 채팅방 추가를 위한 인터페이스 제공한다. 말풍선 아이콘을 클릭하면 채팅방을 생성한다.<br/><br/> 2. 채팅방 변경 : 채팅방 목록에서 채팅방을 클릭하면 해당 채팅방으로 이동한다. <br/><br/> 3. 채팅 삭제 : 스스로 생성한 하얀색 채팅은 클릭시 삭제된다.<br/><br/> 4. 친구 초대: 친구의 닉네임 입력후 초대 아이콘을 클릭하면 친구 해당 채팅방에 초대한다. | ![간단설명](/file/chat_info.png) |







1 change: 1 addition & 0 deletions src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test
2 changes: 2 additions & 0 deletions src/main/java/com/websocket/demo/ChatApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@SpringBootApplication
@EnableJpaAuditing
public class ChatApplication {

public static void main(String[] args) {
Expand Down
31 changes: 31 additions & 0 deletions src/main/java/com/websocket/demo/WebSocketConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.websocket.demo;

import com.websocket.demo.interceptor.ChatHandshakeInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

@Slf4j
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

@Autowired
ChatHandshakeInterceptor chatHandshakeInterceptor;
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}

@Override
public void registerStompEndpoints(StompEndpointRegistry registry){
registry.addEndpoint("/chatting")
.addInterceptors(chatHandshakeInterceptor);

}
}
45 changes: 45 additions & 0 deletions src/main/java/com/websocket/demo/api/ApiController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.websocket.demo.api;

import com.websocket.demo.request.*;
import com.websocket.demo.response.ApiResponse;
import com.websocket.demo.service.ChatService;
import com.websocket.demo.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

import static com.websocket.demo.response.ApiResponse.success;

@RestController
@RequestMapping
@RequiredArgsConstructor
public class ApiController {

private final ChatService chatService;
private final UserService userService;

@GetMapping("/chat")
public ApiResponse getChattingList(@ModelAttribute FindChatListRequest request) {
return success(chatService.findChatList(request));
}

@GetMapping("/room")
public ApiResponse getRoomList(@SessionAttribute("user") LoginRequest userInfo) {
return success(chatService.findRoomList(userInfo.getNickname()));
}

@PostMapping("/room")
public ApiResponse createRoomList(@RequestBody CreateRoomRequest request) {
return success(chatService.createRoom(request));
}

@PutMapping("/room")
public ApiResponse updateRoomConfig(@RequestBody UpdateRoomConfigRequest request, @SessionAttribute("user") LoginRequest userInfo) {
return success(chatService.updateRoom(request, userInfo.getNickname()));
}

@DeleteMapping("/friend")
public ApiResponse deleteFriend(@RequestBody DeleteFriendRequest request, @SessionAttribute("user") LoginRequest userInfo){
userService.removeFriendByNickname(userInfo.getNickname(), request.getFriendNickname());
return success(null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.websocket.demo.controller;

import com.websocket.demo.request.*;
import com.websocket.demo.response.ChatStompResponse;
import com.websocket.demo.service.ChatService;
import lombok.RequiredArgsConstructor;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Controller;

@Controller
@RequiredArgsConstructor
public class ChatWebsocketController {

private final ChatService chatService;
private final SimpMessagingTemplate simpMessagingTemplate;

@MessageMapping("/chat/new")
public void newChat(CreateChatRequest request) {
var message = ChatStompResponse.createChat(chatService.createChat(request));
sendTo(request.getRoomId(), message);
}

@MessageMapping("/chat/delete")
public void deleteChat(DeleteChatRequest request, SimpMessageHeaderAccessor accessor) {
var nickname = (String) accessor.getSessionAttributes().get("nickname");
var message = ChatStompResponse.deleteChat(chatService.deleteChat(request, nickname));
sendTo(request.getRoomId(), message);
}

@MessageMapping("/room/out")
public void roomOut(RoomOutRequest request, SimpMessageHeaderAccessor accessor) {
var nickname = (String) accessor.getSessionAttributes().get("nickname");
var message = ChatStompResponse.getOutRoom(chatService.getOutRoom(request, nickname));
sendTo(request.getId(), message);
}

@MessageMapping("/room/in")
public void roomIn(InViteUserRequest request, SimpMessageHeaderAccessor accessor) {
var host = (String) accessor.getSessionAttributes().get("nickname");
var message = ChatStompResponse.friendComeInRoom(chatService.inviteUser(request, host));
sendTo(request.getRoomId(), message);
sendTo(request.getNickname(), message);
}

@MessageMapping("/room/check")
public void readChat(CheckRoomRequest request, SimpMessageHeaderAccessor accessor){
var host = (String) accessor.getSessionAttributes().get("nickname");
var message = ChatStompResponse.readChat(chatService.checkRoom(request, host));
sendTo(request.getRoomId(), message);
}

private void sendTo(Long roomId, Object message){
simpMessagingTemplate.convertAndSend("/topic/chat-" + roomId, message);
}

private void sendTo(String nickname, Object message){
simpMessagingTemplate.convertAndSend("/topic/chat-" + nickname, message);
}
}
55 changes: 55 additions & 0 deletions src/main/java/com/websocket/demo/controller/PageController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.websocket.demo.controller;

import com.websocket.demo.request.FindChatListRequest;
import com.websocket.demo.request.LoginRequest;
import com.websocket.demo.response.ChatInfo;
import com.websocket.demo.response.FriendInfo;
import com.websocket.demo.response.RoomInfo;
import com.websocket.demo.service.ChatService;
import com.websocket.demo.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttribute;

import java.util.List;

@Controller
@RequestMapping
@RequiredArgsConstructor
@Slf4j
public class PageController {

private final ChatService chatService;
private final UserService userService;
@RequestMapping("/")
public String mainPage(@SessionAttribute(name = "user", required = false) LoginRequest loginInfo, Model model,
@RequestParam(required = false) Long roomId) {
if(loginInfo == null) return "login";
String nickname = loginInfo.getNickname();
List<RoomInfo> roomList = chatService.findRoomList(nickname);
List<FriendInfo> friends = userService.friendList(nickname);

FindChatListRequest request = new FindChatListRequest();
request.setRoomId(roomId);

List<ChatInfo> chatList = chatService.findChatList(request);
RoomInfo roomInfo = null;
for (RoomInfo info : roomList) {
if(info.getId().equals(roomId)) {
roomInfo = info;
}
}

model.addAttribute("nickname", nickname);
model.addAttribute("roomList", roomList);
model.addAttribute("friends", friends);
model.addAttribute("chatList", chatList);
model.addAttribute("targetRoom", roomInfo);

return "index";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public String loginUser(@ModelAttribute LoginRequest request, HttpServletRequest
@PostMapping("/create")
public String createUser(@ModelAttribute CreateUserRequest request){
try {
if (userService.create(request)) return "redirect:/";
if (userService.create(request)) return "login";
} catch (RuntimeException e){
return "createUser";
}
Expand All @@ -51,6 +51,12 @@ public String loginPage() {
return "login";
}

@GetMapping("/logout")
public String logout(HttpServletRequest request){
request.getSession().setMaxInactiveInterval(0);
return "login";
}

@GetMapping("/friend")
public String addFriendPage() {
return "addFriend";
Expand Down
Loading

0 comments on commit 8e78810

Please sign in to comment.