React + typescript + redux-saga 환경에서 stomp client를 개발한 경험을 공유합니다.
yarn add react-stomp @stomp/stompjs sockjs-client @types/sockjs-client
initializeStompChannel 을 호출하여 stomp 연결
function* initializeStompChannel(): any {
yield startStomp();
}
function* startStomp(): any {
const stompClient = yield call(createStompConnection)
channel = yield call(createEventChannel, stompClient);
let isRunning = true
while (isRunning) {
const { message, timeout } = yield race({
timeout: delay(6 * 10000),
message : take(channel),
});
if (timeout) isRunning = false;
yield put(receivedStompMsg(message));
}
}
Stomp 연결 설정
function createStompConnection() {
const stompUrl: string = {URL}
return new Promise((resolve, reject) => {
const initConnection = () => {
const stompClient = new Client({
webSocketFactory : function () {
return new SockJS(stompUrl)
}
})
stompClient.reconnectDelay = 5000
stompClient.heartbeatIncoming = 4000
stompClient.heartbeatOutgoing = 4000
const header = {
login: "",
password: "",
priority: '9'
}
const connectionCallback = () => {
resolve(stompClient);
}
stompClient.onConnect = connectionCallback
stompClient.activate()
}
initConnection();
});
}
Redux-Saga 에서 특정 이벤트가 발생했을 때 처리를 위해 EventChannel을 사용
function createEventChannel(stompClient: CompatClient) {
return eventChannel(emit => {
const onReceivedMessage = (iMessage: IMessage) => { emit(iMessage); }
const subscribeMessage = () => {
stompClient.subscribe('{topic}', onReceivedMessage)
};
subscribeMessage();
return function unsubscribe() {
stompClient.deactivate()
};
}, buffers.expanding(1000) || buffers.none());
}
...
export const receivedStompMsg = (message: object) => ({
type: RECEIVED_STOMP_MSG,
payload: message
})
...
startStomp에 writeTask 추가
function* startStomp(): any {
...
const stompClient = yield call(createStompConnection)
lastWriteTask = yield fork(sendStomp, stompClient)
...
}
send 구현
아래에서는 json object 를 string 으로 변환해 전송
function* sendStomp(stompClient: CompatClient) {
while (true) {
const { payload } = yield take(SEND_STOMP_MSG)
const data: Idata = {
type: "test data",
data: `$payload`
}
stompClient.publish({
destination: `{url}`,
body: JSON.stringify(data),
headers: header
})
}
}
# types.ts
export interface Idata {
type: string,
data: string
}
gradle 의존성 추가
작성 기준 2.6.6 버전 사용
dependencies {
...
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-websocket")
}
WebSocketConfig 추가
@Configuration
@EnableWebSocketMessageBroker
class WebSocketConfig: WebSocketMessageBrokerConfigurer {
override fun registerStompEndpoints(registry: StompEndpointRegistry) {
registry.addEndpoint("/message").setAllowedOrigins("localhost:3000").withSockJS();
}
override fun configureMessageBroker(registry: MessageBrokerRegistry) {
registry.enableSimpleBroker("/topic", "/queue")
registry.setApplicationDestinationPrefixes("/app")
}
}
SocketController 추가
@RestController
@Component
class SocketController(private val simpleMessageTemplate: SimpMessagingTemplate) {
@MessageMapping("/message")
fun sendMessage(@Payload messageData: MessageData, @DestinationVariable id: Int) {
this.simpleMessageTemplate.convertAndSend("{topic}", messageData)
}
}
data class MessageData(
val type: String,
val data: String
)
필요에 따라 WebSocketEventListener 추가
@Component
class WebSocketEventListener() {
@EventListener
fun handleWebSocketConnectListener(event: SessionConnectEvent) {
}
@EventListener
fun handleWebSocketDisconnectListener(event: SessionDisconnectEvent) {
}
}