๐[Network] STOMP(Simple/Streaming Text Messaging Protocol)๋ ๋ฌด์์ผ๊น์?
-
STOMP(Simple/Streaming Text Messaging Protocol)๋ ํ
์คํธ ๊ธฐ๋ฐ์ ๋ฉ์์ง ํ๋กํ ์ฝ๋ก, ํด๋ผ์ด์ธํธ์ ๋ฉ์์ง ๋ธ๋ก์ปค(Message Broker) ๊ฐ์ ํต์ ์ ์ง์ํ๊ธฐ ์ํด ์ค๊ณ๋์์ต๋๋ค.
- ์ผ๋ฐ์ ์ผ๋ก WebSocket๊ณผ ํจ๊ป ์ฌ์ฉ๋๋ฉฐ, Pub/Sub ํจํด์ ๋ฉ์์ง์ ๋ ์ฝ๊ฒ ๊ตฌํํ ์ ์๊ฒ ๋์์ค๋๋ค.
1๏ธโฃ STOMP์ ์ฃผ์ ํน์ง.
1๏ธโฃ ๊ฐ๋จํ ๋ฉ์์ง ํ๋กํ ์ฝ.
- STOMP๋ ๊ฐ๋จํ ๋ฉ์์ง ํ๋ ์ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง๊ณ ์์ด ์ดํดํ๊ณ ๊ตฌํํ๊ธฐ ์ฝ์ต๋๋ค.
- ๋ฉ์์ง ํ๋ ์์ ๋ช ํํ ํค๋์ ๋ณธ๋ฌธ(Body)์ผ๋ก ๊ตฌ์ฑ๋ฉ๋๋ค.
2๏ธโฃ WebSocket ์์ ๋ ์ด์ด ํ๋กํ ์ฝ.
- STOMP๋ WebSocket์ ๊ธฐ๋ฐ์ผ๋ก ๋์ํ์ง๋ง, WebSocket์ ๋จ์ํ ํต์ ๋ฐฉ์์ Pub/Sub(๋ฐํ/๊ตฌ๋ ) ๊ธฐ๋ฅ์ ์ถ๊ฐํ์ฌ ๋ฉ์์ง ์์คํ ์ ๊ตฌํํฉ๋๋ค.
- WebSocket์ ๋จ์ํ ์๋ฐฉํฅ ์ฐ๊ฒฐ๋ง ์ง์ํ์ง๋ง, STOMP๋ฅผ ํตํด ๋ ๋ณต์กํ ๋ฉ์์ง ์ ๋ฌ ํจํด์ ๊ตฌํํ ์ ์์ต๋๋ค.
3๏ธโฃ ์๋ฒ์ ํด๋ผ์ด์ธํธ ๊ฐ์ ๋ฉ์์ง.
- STOMP๋ ์๋ฒ-ํด๋ผ์ด์ธํธ ๋๋ ํด๋ผ์ด์ธํธ-ํด๋ผ์ด์ธํธ ๊ฐ์ ๋ฉ์์ง๋ฅผ ์ ์กํ ์ ์๋๋ก ์ค๊ณ๋์์ต๋๋ค.
- ์ฃผ๋ก ๋ฉ์์ง ๋ธ๋ก์ปค(์: ActiveMQ, RabbitMQ)์ ํจ๊ป ์ฌ์ฉ๋ฉ๋๋ค.
4๏ธโฃ Pub/Sub ํจํด ์ง์.
- ํด๋ผ์ด์ธํธ๋ ํน์ ์ฃผ์ (Topic) ๋๋ ํ(Queue)๋ฅผ ๊ตฌ๋ (Subscribe)ํ์ฌ, ํด๋น ์ฃผ์ ์ ๋ฐํ๋ ๋ฉ์์ง๋ฅผ ๋ฐ์ ์ ์์ต๋๋ค.
2๏ธโฃ STOMP ํ๋ ์ ๊ตฌ์กฐ.
- STOMP ๋ฉ์์ง ํ๋ ์์ ๋ค์๊ณผ ๊ฐ์ ๊ตฌ์กฐ๋ก ๋์ด ์์ต๋๋ค.
์์ ๊ตฌ์ฑ.
1๏ธโฃ ๋ช ๋ น(command)
- ํ๋ ์์ ๋ชฉ์ ์ ์ ์ํฉ๋๋ค.(์: CONNECT, SEND, SUBSCRIBE, UNSUBSCRIBE)
2๏ธโฃ ํค๋(headers)
- ํ๋ ์๊ณผ ๊ด๋ จ๋ ์ถ๊ฐ ์ ๋ณด๋ฅผ ํฌํจํฉ๋๋ค.(์: ๋ชฉ์ ์ง, ์ฝํ ์ธ ํ์ ๋ฑ)
3๏ธโฃ ๋ณธ๋ฌธ(body)
- ๋ฉ์์ง ๋ด์ฉ(์ ํ ์ฌํญ)
์์
- ์๋๋ ๋ฉ์์ง ์ ์ก(SEND) ํ๋ ์ ์์ ์
๋๋ค.
```hash
SEND
destination:/queue/test
content-type:text/plain
Hello, STOMP!
- **๋ช
๋ น :** SEND
- **ํค๋ :** destination:/queue/test, content-type:text/plain
- **๋ณธ๋ฌธ :** Hello, STOMP!
## 3๏ธโฃ Spring์์ STOMP ์ฌ์ฉ ์์ .
- Spring Boot๋ **STOMP**์ **WebSocket**์ ๊ฒฐํฉํ์ฌ ๋ฉ์์ง์ ๊ตฌํํ ์ ์๋๋ก ๊ธฐ๋ถ ์ง์์ ์ ๊ณตํฉ๋๋ค.
### 1๏ธโฃ STOMP ์์กด์ฑ ์ถ๊ฐ.
- build.gradle ๋๋ pom.xml์ ๋ค์ ์์กด์ฑ์ ์ถ๊ฐํฉ๋๋ค.
```groovy
implementation 'org.springframework.boot:spring-boot-starter-websocket'
2๏ธโฃ WebSocket ์ค์ .
- STOMP ์๋ํฌ์ธํธ์ ๋ฉ์์ง ๋ธ๋ก์ปค๋ฅผ ์ค์ ํ๋
@Configuration
ํด๋์ค๋ฅผ ์์ฑํฉ๋๋ค.
```java
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.WebSocketMessageBrokerConfigurer;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic"); // ํด๋ผ์ด์ธํธ ๊ตฌ๋
์ฃผ์
config.setApplicationDestinationPrefixes("/app"); // ํด๋ผ์ด์ธํธ ๋ฐํ ์ฃผ์
}
@Override
public void registerStompEndpoints(org.springframework.web.socket.config.annotation.StompEndpointRegistry registry) {
registry.addEndpoint("/ws") // WebSocket ์๋ํฌ์ธํธ
.setAllowedOrigins("*") // CORS ํ์ฉ
.withSockJS(); // SockJS ์ง์
} } ```
3๏ธโฃ ์ปจํธ๋กค๋ฌ
- STOMP ๋ฉ์์ง๋ฅผ ์ฒ๋ฆฌํ ์ปจํธ๋กค๋ฌ๋ฅผ ์์ฑํฉ๋๋ค.
```java
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
@Controller
public class ChatController {
@MessageMapping("/sendMessage") // ํด๋ผ์ด์ธํธ๊ฐ ๋ฉ์์ง ๋ฐํํ ๊ฒฝ๋ก
@SendTo("/topic/messages") // ๊ตฌ๋
๋ ํด๋ผ์ด์ธํธ์๊ฒ ๋ณด๋ผ ๊ฒฝ๋ก
public String sendMessage(String message) {
return "์๋ฒ ์๋ต: " + message;
} } ```
4๏ธโฃ ํด๋ผ๋ฆฌ์ธํธ JavaScript
- JavaScript๋ฅผ ์ฌ์ฉํ์ฌ STOMP ๋ฉ์์ง๋ฅผ ์ ์กํ๊ณ ์์ ํฉ๋๋ค.
```javascript
const socket = new SockJS(โ/wsโ); // ์๋ฒ WebSocket ์๋ํฌ์ธํธ
const stompClient = Stomp.over(socket);
stompClient.connect({}, () => {
console.log(โWebSocket ์ฐ๊ฒฐ๋จโ);
// ๋ฉ์์ง ๊ตฌ๋
stompClient.subscribe('/topic/messages', (message) => {
console.log('์์ ํ ๋ฉ์์ง:', message.body);
});
// ๋ฉ์์ง ๋ฐํ
stompClient.send('/app/sendMessage', {}, 'Hello from client!'); }); ```
4๏ธโฃ ๊ฒฐ๋ก .
- STOMP๋ WebSocket์ ๋ณด์ํ์ฌ ํจ์จ์ ์ผ๋ก ๋ฉ์์ง๋ฅผ ๋ฐํํ๊ณ ๊ตฌ๋ ํ๋ ๋ฐ ๋์์ ์ฃผ๋ ๊ณ ์์ค ํ๋กํ ์ฝ์ ๋๋ค.
- Spring Boot์์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ง์ํ๋ฏ๋ก, ์ฝ๊ฒ ์ค์๊ฐ ๊ธฐ๋ฅ์ ๊ตฌํํ ์ ์์ต๋๋ค.