π[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μμ κΈ°λ³Έμ μΌλ‘ μ§μνλ―λ‘, μ½κ² μ€μκ° κΈ°λ₯μ ꡬνν μ μμ΅λλ€.