Search

WebSocket κ³Ό HTTP

νƒœκ·Έ
WebSocket
λͺ©μ°¨

μ›Ήμ†ŒμΌ“ ν”„λ‘œν† μ½œκ³Ό HTTP ν”„λ‘œν† μ½œ

ν”„λ‘œν† μ½œ

ν”„λ‘œν† μ½œμ€ 컴퓨터 λ„€νŠΈμ›Œν¬μ—μ„œ 데이터 κ΅ν™˜μ„ μœ„ν•΄ μ‚¬μš©λ˜λŠ” κ·œμΉ™κ³Ό ν‘œμ€€μ˜ μ§‘ν•©μž…λ‹ˆλ‹€. 즉, ν”„λ‘œν† μ½œμ€ λ„€νŠΈμ›Œν¬ μƒμ—μ„œ 데이터가 μ–΄λ–»κ²Œ, μ–΄λ–€ ν˜•μ‹μœΌλ‘œ μ „μ†‘λ˜μ–΄μ•Ό ν•˜λŠ”μ§€λ₯Ό μ •μ˜ν•˜λŠ” κ·œμ•½μž…λ‹ˆλ‹€. μ»΄ν“¨ν„°λ‚˜ λ„€νŠΈμ›Œν¬ μž₯μΉ˜κ°€ μ„œλ‘œ ν†΅μ‹ ν•˜κΈ° μœ„ν•΄μ„œλŠ” λ™μΌν•œ ν”„λ‘œν† μ½œμ„ 따라야 ν•©λ‹ˆλ‹€. ν”„λ‘œν† μ½œμ€ 톡신 λ„€νŠΈμ›Œν¬μ˜ 기본적인 ꡬ성 μš”μ†Œλ‘œ, λ‹€μ–‘ν•œ λ„€νŠΈμ›Œν¬ ν™˜κ²½κ³Ό μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œ 효율적이고 μ•ˆμ •μ μΈ 데이터 κ΅ν™˜μ„ κ°€λŠ₯ν•˜κ²Œ ν•©λ‹ˆλ‹€.

μ›Ήμ†ŒμΌ“κ³Ό HTTP 그리고 TCP λŠ” μ–΄λ–€ 계측에 μ†ν•΄μžˆλ‚˜μš”?

μ›Ήμ†ŒμΌ“κ³Ό HTTP λͺ¨λ‘ TCP λ₯Ό 기반으둜 ν•˜μ§€λ§Œ, 두 ν”„λ‘œν† μ½œμ΄ TCP 와 μƒν˜Έμž‘μš© ν•˜λŠ” 방식은 μƒλ‹Ήνžˆ λ‹€λ¦…λ‹ˆλ‹€. λ¨Όμ € μ„Έ ν”„λ‘œν† μ½œμ΄ μ–΄λ–€ λ„€νŠΈμ›Œν¬ 계측에 μ†ν•΄μžˆλŠ”μ§€ λΆ€ν„° μ•Œμ•„λ³΄κ² μŠ΅λ‹ˆλ‹€.
β€’
TCP λŠ” 전솑 κ³„μΈ΅μ˜ ν”„λ‘œν† μ½œ μž…λ‹ˆλ‹€.
β€’
HTTP 와 μ›Ήμ†ŒμΌ“μ€ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ κ³„μΈ΅μ˜ ν”„λ‘œν† μ½œ μž…λ‹ˆλ‹€.
 각 계측에 λŒ€ν•œ λ‚΄μš©μ„ μ•Œκ³ μ‹Άλ‹€λ©΄?

TCP 와 HTTP 의 μ—°κ²°κ³Ό 데이터 κ΅ν™˜ - 3 way handshake

β€’
μ΄λ ‡κ²Œ 연결이 μ™„λ£Œλœ 후에 μš”μ²­κ³Ό 응닡이 전솑 되며, 각 μš”μ²­μ€ 독립적 μž…λ‹ˆλ‹€.
β€’
μ—¬λŸ¬ HTTP μš”μ²­μ„ ν•˜λ‚˜μ˜ TCP μ—°κ²°λ‘œ μ²˜λ¦¬ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
β€’
HTTP λŠ” μƒνƒœλ₯Ό μ €μž₯ν•˜μ§€ μ•ŠμœΌλ―€λ‘œ μš”μ²­ κ°„μ˜ μƒνƒœ 정보λ₯Ό μœ μ§€ ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
β—¦
λ•Œλ¬Έμ— 맀 μš”μ²­ λ§ˆλ‹€ 인증/인가 λ₯Ό μœ„ν•œ 토큰 등을 싀어보내야 ν•©λ‹ˆλ‹€.

μ›Ήμ†ŒμΌ“μ˜ μ‹œμž‘μ€ HTTP μ—°κ²°!

μ›Ήμ†ŒμΌ“ 연결은 HTTP 연결을 톡해 μ‹œμž‘ν•©λ‹ˆλ‹€. μœ„μ™€ 같이 three way handshake λ₯Ό 톡해 연결이 된 후에 ν΄λΌμ΄μ–ΈνŠΈλŠ” μ›Ήμ†ŒμΌ“ 연결을 μš”μ²­ν•©λ‹ˆλ‹€. μ„œλ²„κ°€ 이λ₯Ό μŠΉμΈν•˜λ©΄ HTTP 연결은 μ›Ήμ†ŒμΌ“ μ—°κ²°λ‘œ μ—…κ·Έλ ˆμ΄λ“œ λ©λ‹ˆλ‹€. 과정은 μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€.
 이 과정은 SockJS 와 μŠ€ν”„λ§ ν”„λ ˆμž„μ›Œν¬μ˜ μ›Ήμ†ŒμΌ“ 지원 라이브러리λ₯Ό μ‚¬μš©ν•˜λ©΄ λŒ€λΆ€λΆ„ μžλ™μœΌλ‘œ 처리 λ©λ‹ˆλ‹€. λ•Œλ¬Έμ— κ°œλ°œμžλŠ” 메세지 처리 및 λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 κ΅¬ν˜„μ— μ§‘μ€‘ν•˜λ©΄ λ©λ‹ˆλ‹€.
β€’
ν΄λΌμ΄μ–ΈνŠΈλŠ” Upgrade: websocket κ³Ό Connection:Upgrade λ₯Ό μš”μ²­ 헀더에 λ„£μŠ΅λ‹ˆλ‹€.
β—¦
μ΄λŠ” ν΄λΌμ΄μ–ΈνŠΈκ°€ ν˜„μž¬μ˜ HTTP 연결을 μ›Ήμ†ŒμΌ“ μ—°κ²°λ‘œ μ „ν™˜ν•˜κ³ μž 함을 λ‚˜νƒ€λƒ…λ‹ˆλ‹€.
β—¦
Sec-WebSocket-key λΌλŠ” ν—€λ”λŠ” μ„œλ²„μ˜ 응닡을 κ²€μ¦ν•˜λŠ” 데 μ‚¬μš©ν•˜λŠ” 헀더 μž…λ‹ˆλ‹€.
β€’
μ„œλ²„κ°€ μœ„ ν΄λΌμ΄μ–ΈνŠΈμ˜ μš”μ²­μ„ μˆ˜λ½ν•˜λ©΄ 응닡을 톡해 이λ₯Ό μ•Œλ¦½λ‹ˆλ‹€.
β—¦
Upgrade: websocket κ³Ό Connection:Upgrade 헀더λ₯Ό 포함해야 ν•©λ‹ˆλ‹€.
β—¦
Sec-WebSocket-key λ₯Ό 기반으둜 μƒμ„±λœ νŠΉλ³„ν•œ 값을 Sec-WebSocket-Accept 헀더에 λ‹΄μ•„ μ‘λ‹΅ν•©λ‹ˆλ‹€.
이 과정을 websocket handshake 라고 ν•˜λ©°, 이 과정이 μ™„λ£Œ 되면 HTTP 연결은 μ›Ήμ†ŒμΌ“ μ—°κ²°λ‘œ μ—…κ·Έλ ˆμ΄λ“œ λ©λ‹ˆλ‹€. 즉, ν΄λΌμ΄μ–ΈνŠΈμ™€ μ„œλ²„ μ‚¬μ΄μ˜ 톡신은 μ›Ήμ†ŒμΌ“ ν”„λ‘œν† μ½œμ„ 톡해 μ§„ν–‰λ©λ‹ˆλ‹€. 이λ₯Ό 톡해 ν΄λΌμ΄μ–ΈνŠΈμ™€ μ„œλ²„λŠ” 연결이 μ’…λ£Œλ  λ•ŒκΉŒμ§€ 데이터 κ΅ν™˜μ„ 효율적으둜 μˆ˜ν–‰ν•  수 있게 λ©λ‹ˆλ‹€.

HTTP 와 μ›Ήμ†ŒμΌ“μ˜ 데이터 전솑

두 ν”„λ‘œν† μ½œμ€ TPC/IP ν”„λ‘œν† μ½œμ„ μ΄μš©ν•˜κΈ° λ•Œλ¬Έμ— λ°μ΄ν„°λŠ” νŒ¨ν‚·μ˜ ν˜•νƒœλ‘œ μ „μ†‘λ©λ‹ˆλ‹€
HTTP ν”„λ‘œν† μ½œ μ—μ„œλŠ” 주둜 ν…μŠ€νŠΈ 기반의 메세지 ν˜•μ‹μ„ μ‚¬μš©ν•˜μ—¬ 데이터λ₯Ό μ „μ†‘ν•©λ‹ˆλ‹€. μ‹œμž‘μ€„, 헀더, 본문으둜 κ΅¬μ„±λ˜μ–΄ 있죠.
ν•˜μ§€λ§Œ μ›Ήμ†ŒμΌ“μ˜ 경우 μ›Ήμ†ŒμΌ“ ν”„λ ˆμž„μ΄λΌλŠ” ꡬ쑰λ₯Ό μ‚¬μš©ν•˜μ—¬ 데이터 전솑이 이루어 μ§‘λ‹ˆλ‹€. μ΄λŠ” μ‹€μ‹œκ°„ 데이터 톡신에 μ΅œμ ν™”λœ ν¬λ§·μž…λ‹ˆλ‹€.
μ€‘μš” ꡬ성 μš”μ†Œ
β€’
ν”„λ ˆμž„μ˜ μ‹œμž‘κ³Ό 끝을 λ‚˜νƒ€λ‚΄λŠ” ν‘œμ‹œμž
β€’
νŽ˜μ΄λ‘œλ“œ 길이
β€’
λ§ˆμŠ€ν‚Ή
β€’
νŽ˜μ΄λ‘œλ“œ 데이터
ν”„λ ˆμž„ μœ ν˜•
1.
연속 ν”„λ ˆμž„ : κΈ΄ 메세지λ₯Ό μ—¬λŸ¬ ν”„λ ˆμž„μ— λ‚˜λˆ„μ–΄ 전솑할 λ•Œ μ‚¬μš©ν•©λ‹ˆλ‹€.
2.
ν…μŠ€νŠΈ ν”„λ ˆμž„ : UTF-8 ν…μŠ€νŠΈ 데이터λ₯Ό μ „μ†‘ν•˜λŠ” 데 μ‚¬μš©λ©λ‹ˆλ‹€.
3.
λ°”μ΄λ„ˆλ¦¬ ν”„λ ˆμž„ : λ°”μ΄λ„ˆλ¦¬ 데이터λ₯Ό μ „μ†‘ν•˜λŠ” 데 μ‚¬μš©λ©λ‹ˆλ‹€.
4.
μ œμ–΄ ν”„λ ˆμž„ : 연결을 λ‹«κ±°λ‚˜, ν•‘/퐁 λ©”μ‹œμ§€λ₯Ό μ „μ†‘ν•˜λŠ” 데 μ‚¬μš©λ©λ‹ˆλ‹€. 이 ν”„λ ˆμž„μ€ 쀑간에 λ‹€λ₯Έ ν”„λ ˆμž„μ„ μ€‘λ‹¨ν•˜μ§€ μ•Šκ³  전솑될 수 μžˆμŠ΅λ‹ˆλ‹€.

STOMP

STOMPλŠ” Simple (or Streaming) Text Oriented Messaging Protocol 의 μ•½μžλ‘œ, κ°„λ‹¨ν•œ ν…μŠ€νŠΈ 기반의 λ©”μ‹œμ§• ν”„λ‘œν† μ½œμž…λ‹ˆλ‹€. STOMP λŠ” HTTP 와 μœ μ‚¬ν•œ ν˜•μ‹μ˜ ν”„λ ˆμž„μœΌλ‘œ κ΅¬μ„±λ˜μ–΄ μžˆλŠ”λ°μš”, 각 ν”„λ ˆμž„μ—λŠ” λͺ…λ Ήμ–΄(CONNECT, SUBSCRIBE, SEND), 헀더, λ°”λ””κ°€ ν¬ν•¨λ©λ‹ˆλ‹€. λ˜ν•œ STOMPλŠ” λ°œν–‰/ꡬ독(Publish/Subscribe) 및 μš”μ²­/응닡(Request/Response) νŒ¨ν„΄μ„ μ§€μ›ν•©λ‹ˆλ‹€.

λ™μž‘ κ³Όμ •

WebSocket Client Messages

ν΄λΌμ΄μ–ΈνŠΈκ°€ 메세지λ₯Ό 보내면(SEND) 이 메세지듀은 μ„œλ²„μ˜ νŠΉμ • endpoint 둜 λΌμš°νŒ… λ©λ‹ˆλ‹€. λ©”μ„Έμ§€μ—λŠ” destination 헀더가 ν¬ν•¨λ˜μ–΄ μžˆμ–΄, 메세지가 μ–΄λ””λ‘œ λ³΄λ‚΄μ§ˆμ§€ μ§€μ •ν•©λ‹ˆλ‹€. κ·Έλ¦Όμ—μ„œμ˜ /app/a λŠ” μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ νŠΉμ • μ•‘μ…˜μ„ λ‚˜νƒ€λ‚΄κ³ , /topic/a λŠ” νŠΉμ • μ£Όμ œμ— λŒ€ν•œ λ°œν–‰μ„ λ‚˜νƒ€λƒ…λ‹ˆλ‹€.

Message handling

@SimpAnnotaionMehtodMessageHandler λŠ” μ• ν”Œλ¦¬μΌ€μ΄μ…˜ λ©”μ„œλ“œμ— μ–΄λ…Έν…Œμ΄μ…˜μ„ 기반으둜 메세지λ₯Ό λ§€ν•‘ν•˜λŠ” ν•Έλ“€λŸ¬ μž…λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, @MessageMapping μ–΄λ…Έν…Œμ΄μ…˜μ΄ 뢙은 λ©”μ„œλ“œκ°€ /app/a 둜 μ˜€λŠ” 메세지λ₯Ό μ²˜λ¦¬ν•©λ‹ˆλ‹€. @StompBrokerRelayMessageHandler λŠ” 메세지λ₯Ό μ™ΈλΆ€ 메세지 브둜컀둜 λΌμš°νŒ… ν•©λ‹ˆλ‹€. 이 경우 /topic 으둜 μ‹œμž‘ν•˜λŠ” λͺ©μ μ§€λ₯Ό 가진 λ©”μ„Έμ§€λŠ” μ™ΈλΆ€ STOMP 메세지 브둜컀둜 전달 λ©λ‹ˆλ‹€. 메세지 λΈŒλ‘œμ»€λΆ€ν„°μ˜ 응닡은 response channel 을 톡해 μ„œλ²„λ‘œ λŒμ•„μ˜€λ©°, 후에 ν΄λΌμ΄μ–ΈνŠΈμ— μ „λ‹¬λ©λ‹ˆλ‹€.

이해가 잘 μ•ˆκ°€λŠ”λ°μš”β€¦

μœ„μ˜ λ‚΄μš©μ΄ κΈ€λ‘œλ§Œ λ˜μ–΄μžˆμ–΄μ„œ 잘 이해가 μ•ˆκ°ˆ 수 μžˆμŠ΅λ‹ˆλ‹€. 이λ₯Ό μœ„ν•΄ 이전 ν”„λ‘œμ νŠΈμ˜ μ½”λ“œ 일뢀λ₯Ό κ°€μ Έμ™”μŠ΅λ‹ˆλ‹€. ν•΄λ‹Ή κΈ°λŠ₯을 톡해 νŠΉμ • μœ μ €μ˜ ν”Œλž˜λ„ˆμ— μ΄ˆλŒ€λœ μ—¬λŸ¬ μœ μ €λ“€μ΄ μ‹€μ‹œκ°„μœΌλ‘œ ν”Œλž˜λ„ˆλ₯Ό μƒμ„±ν• μˆ˜ μžˆμŠ΅λ‹ˆλ‹€.

WebSocketConfig.java

μ›Ήμ†ŒμΌ“ μ„€μ •κ³Ό κ΄€λ ¨λœ 클래슀 μž…λ‹ˆλ‹€.
@Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { /* destination 에 /app/ 이 μ•žμ— λΆ™μœΌλ©΄ μ•„ μ—¬κΈ°μ„œ νŠΉμ • μ•‘μ…˜μ„ μˆ˜ν–‰ν•˜λŠ” κ΅¬λ‚˜ ν•˜κ³  μƒκ°ν•˜λ©΄ 될 것 κ°™μŠ΅λ‹ˆλ‹€. destination 에 /topic/ 이 μ•žμ— λΆ™μœΌλ©΄ μ•„ 이곳에 처리된 데이터가 μ˜€λŠ” κ΅¬λ‚˜ ν•˜κ³  μƒκ°ν•˜λ©΄ 될 것 κ°™μŠ΅λ‹ˆλ‹€. */ config.enableSimpleBroker("/topic"); config.setApplicationDestinationPrefixes("/app"); } ... }
Java
볡사

ν”„λ‘ νŠΈμ—”λ“œμ—μ„œμ˜ μ›Ήμ†ŒμΌ“ μ—°κ²° μš”μ²­

ν”„λ‘ νŠΈμ—”λ“œμ—μ„œ νŠΉμ • ν”Œλž˜λ„ˆμ— μ›Ήμ†ŒμΌ“ μ—°κ²° μš”μ²­μ„ ν•˜κ³ , μ›Ήμ†ŒμΌ“ 연결에 μ„±κ³΅ν•˜λ©΄ μ•„λž˜μ™€ 같은 λ‚΄μš©μ΄ μ½˜μ†”μ— μ°νž™λ‹ˆλ‹€.
μ΄λŠ” νŠΉμ • μœ μ €μ˜ ν”Œλž˜λ„ˆλ₯Ό μ˜λ―Έν•˜λŠ” destination 을 κ΅¬λ…ν•˜κ³  μžˆλ‹€λŠ” λœ»μž…λ‹ˆλ‹€.

TodoListController.java

ν”Œλž˜λ„ˆμ— λ“€μ–΄κ°€μ„œ μ—¬ν–‰λ‚ μ§œλ₯Ό μƒμ„±ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.
@Controller @AllArgsConstructor @RequestMapping("todolist") public class TodoListController { private final TodoListService todoListService; private final SimpMessagingTemplate simpMessagingTemplate; // 메세지 브둜컀(/app/upload-todolist/{tpIdx}) 와 맀핑을 ν•©λ‹ˆλ‹€. @MessageMapping("/upload-todolist/{tpIdx}") public void upload( @DestinationVariable("tpIdx") Long tpIdx, TodoListRegistRequest dto ) throws Exception { /* κ΄„ν˜Έ μ•ˆμ˜ destination 을 SUBSCRIBE ν•˜κ³  μžˆλŠ” λͺ¨λ“  μ‚¬λžŒλ“€μ—κ²Œ μ•žμ—μ„œ 보내 처리된 데이터λ₯Ό λ³΄λ‚΄κ² λ‹€λŠ” λœ»μž…λ‹ˆλ‹€. */ simpMessagingTemplate.convertAndSend("/topic/planner-message/" + tpIdx, Map.of("type","upload-todolist","msg",todoListService.createTodoList(dto, tpIdx))); } ...
Java
볡사
λ‹€μŒκ³Ό 같이 λ‚ μ§œλ₯Ό 생성 ν–ˆμ„ λ•Œ 개발자 도ꡬλ₯Ό 열어보면 μ•„λž˜μ™€ 같은 λ‚΄μš©μ„ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.
λͺ‡λ²ˆ ν•˜λ‹€λ³΄λ©΄ 금방 μ΅μˆ™ν•΄ μ§„λ‹΅λ‹ˆλ‹€.