서버(자바 파일) 관점에서 본 코드 흐름

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
plugins {
    id 'org.springframework.boot' version '2.3.4.RELEASE'
    id 'io.spring.dependency-management' version '1.0.10.RELEASE'
    id 'java'
}
 
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
 
repositories {
    mavenCentral()
    mavenLocal()
}
 
 dependencies {
 
    implementation 'org.springframework.boot:spring-boot-starter'
    implementation('org.springframework.boot:spring-boot-starter-test'
    compile('org.springframework.boot:spring-boot-starter-web')
    compile('org.springframework.boot:spring-boot-starter-websocket')
    compile('org.webjars:sockjs-client:1.1.2')
    compile('org.webjars:webjars-locator:0.30')
    runtime('org.springframework.boot:spring-boot-devtools')
    testCompile('org.springframework.boot:spring-boot-starter-test')
    compile('org.springframework.boot:spring-boot-starter-test')
    compile('org.apache.tomcat.embed:tomcat-embed-jasper')
 
 
}
 
test {
    useJUnitPlatform()
}
 
cs
gradle
1
2
3
4
5
6
7
8
server.port = 8016
 
spring.mvc.view.prefix=/WEB-INF/view/
 
spring.mvc.view.suffix=.jsp
 
}
 
cs
application.properties

자바 패키지 구조입니다.

 

chat패키지 구조입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.dms.chat;
 
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;
 
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
   
    ChatHandler chatHandler = new ChatHandler();
 
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(chatHandler, "/ws/multiRoom").setAllowedOrigins("*").withSockJS();
    }
}
 
cs

WebSocketConfig 클래스입니다.

chatHandler클래스를 웹소켓 핸들러로 정합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.dms.chat;
 
import org.springframework.web.socket.CloseStatus;
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;
 
public class ChatHandler extends TextWebSocketHandler {
    
     ObjectMapper objectMapper = new ObjectMapper();
     ChatRoomRepository repository = new ChatRoomRepository();
    
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {  
        String payload = message.getPayload();
        ChatMessage chatMessage = objectMapper.readValue(payload, ChatMessage.class);
        ChatRoom chatRoom = repository.getChatRoom(chatMessage.getChatRoomId());   
        chatRoom.handleMessage(session, chatMessage, objectMapper);
 
    }
    
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        repository.remove(session);
        
    }
    
}
 
cs

chatHandler 클래스 입니다. 

room.jsp 에서 sock.send()가 실행되면 데이터가 오게되는 곳입니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.dms.chat;
 
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import org.springframework.web.socket.WebSocketSession;
 
public class ChatRoomRepository {
 
    Map<String, ChatRoom> chatRoomMap = new HashMap<String, ChatRoom>(); 
    /*home 컨트롤러에서 모델로 사용하기위해서 public static을 붙였습니다.*/
    public static Collection<ChatRoom> chatRooms;
    
    public ChatRoomRepository() {
        for(int i=0;i<2;i++) {  
            String uuid = UUID.randomUUID().toString();
            ChatRoom chatRoom = new ChatRoom(uuid);
            chatRoomMap.put(chatRoom.getId(), chatRoom);
            System.out.println("chatRoom 클래스를 복제하고 있습니다.");
            System.out.println("chatRoom -> "+chatRoom);
           }            
         chatRooms = chatRoomMap.values();
    }
        
    public ChatRoom getChatRoom(String id) {
        return chatRoomMap.get(id);
    }
    
    public Map<String, ChatRoom> getChatRooms() {
        return chatRoomMap;
    }
        
    public void remove(WebSocketSession session) {
        this.chatRooms.parallelStream().forEach(chatRoom -> chatRoom.remove(session));
    } 
    
}
 
cs

ChatRoomRepository 클래스 입니다.

chatHandler 클래스에서 repository 객체를 만들면 생성자가 실행됩니다.

그러므로 자동으로 초기화 됩니다.

chatRooms변수는 Map의 values 메소드로 해당 Map의 values만을 꺼냅니다.

 

++) map은 key, value로 데이터를 저장하는 자료구조 입니다.

이 코드에서는 key는 roomId(uuid) 입니다.

그리고, value는 chatRoom 클래스입니다.

 

++) i<2 이므로 채팅방을 2개가 만들어집니다. 

즉, chatRoom 클래스가 두개가 만들어집니다.

클래스마다 방의 접속자를 관리하기 위해서 만듭니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package com.dms.chat;
 
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
 
public class ChatRoom {
     String id;
     Set<WebSocketSession> sessions = new HashSet<>();
 
    public ChatRoom(String room_id) { 
        this.id = room_id;
    }
   
    public void handleMessage(WebSocketSession session, ChatMessage chatMessage, ObjectMapper objectMapper) throws JsonProcessingException {
        if (chatMessage.getType().equals("JOIN")) 
            join(session); 
        else
            send(chatMessage, objectMapper);
    }
 
    private void join(WebSocketSession session) {
        sessions.add(session);
    }
    
    private <T> void send(T messageObject, ObjectMapper objectMapper) throws JsonProcessingException {
        TextMessage message = new TextMessage(objectMapper.writeValueAsString(messageObject));
  
        sessions.parallelStream().forEach(session -> {
            try {
                session.sendMessage(message);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        });
    }
    
    public void remove(WebSocketSession target) {
      String targetId = target.getId();
      sessions.removeIf(session -> session.getId().equals(targetId));
     }
 
    public String getId() {
        return id;
    }
 
    public Set<WebSocketSession> getSessions() {
        return sessions;
    }
 
}
 
cs

ChatRoom 클래스 입니다.

ChatRoomRepository 클래스의 chatRoomMap 변수를 채우는데 사용합니다.

사용자가 방에 들어오면, 사용자 정보(세션)가 join 메소드를 통해 sessions 변수에 담깁니다.

사용자가 메시지를 보내면 send 메소드를 통해 sessions 변수에 있는 모든 사용자들에게 메시지를 보냅니다.

 ++) 메시지 공유대상은 같은 방에 있는 사용자들 입니다.

 

※예시※

a가 방에 들어가서 join을 통해  session 변수에 a를 넣었습니다.

b가 방에 들어가서 뷰에서 메시지를 보냈습니다.

그러면, 메시지가 chatHandler -> chatRoom 순으로 클래스를 거칩니다.

 sessions.parallelStream().forEach(session -> { session.sendMessage(message) } )

위 코드가 실행됩니다.

  

다음 포스팅은 컨트롤러와 뷰를 소개하겠습니다.

+ Recent posts