실시간 이벤트 전송 방법
Polling
Polling 작동 방식: 클라이언트가 주기적으로 서버에 HTTP 요청을 보내 이벤트 데이터를 전달받는 방식입니다.
장점:
- 이해하고 구현하기 쉬운 단순한 형태.
- HTTP 같은 범용적인 프로토콜을 사용하여 다양한 플랫폼과 기기에서 활용 가능.
- 기존 인프라와 호환성이 좋음.
단점:
- 클라이언트가 지속적으로 요청을 보내기 때문에 서버 부담이 큼.
- HTTP 요청/응답 과정에서 오버헤드 발생.
- 실시간 응답을 기대하기 어려움.
사용 사례: IoT 장치. IoT 장치들은 주로 센서를 통해 데이터를 수집하고 이를 중앙 서버나 클라우드에 전송합니다. 예를 들어, 스마트 홈에서 센서(온도, 습도, 조명, 모션 등)의 상태를 주기적으로 확인하여 조명을 제어하거나 에어컨을 켜거나 끄는 등의 액션을 취합니다. 대시보드 갱신에도 유용합니다.
Tip: HTTP Overhead란 정보의 신뢰성 판단을 위한 헤더 등으로 인해 데이터량이나 처리시간이 증가하는 현상을 말합니다.
구현 예시 (Java):
package com.example.polling;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableScheduling
public class PollingApplication {
public static void main(String[] args) {
SpringApplication.run(PollingApplication.class, args);
}
@Scheduled(fixedRate = 5000) // 5초마다 실행
public void pollServer() {
try {
RestTemplate restTemplate = new RestTemplate();
String serverUrl = "<http://your-server-endpoint>";
// 서버에 요청 보내기
String response = restTemplate.getForObject(serverUrl, String.class);
// 데이터를 사용하여 콘솔에 출력
System.out.println("Received: " + response);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Long-polling
Long-polling 작동 방식:
- 클라이언트가 서버에 HTTP 요청을 보냄.
- 서버에 응답에 대한 사용 가능한 데이터가 없으면 기다림.
- 서버에서 해당 클라이언트로 전달할 이벤트가 있으면 응답 메시지를 보냄.
- 연결이 종료되면 클라이언트는 곧바로 다시 HTTP 요청을 보냄.
장점:
- 일반 Polling 방식보다는 서버의 부담이 적음.
- 클라이언트가 즉시 업데이트를 받을 수 있음.
단점:
- 이벤트 간격이 좁으면 일반 Polling과 큰 차이가 없음.
- 다수의 클라이언트가 동시에 이벤트를 받으면 서버 부담이 급증.
- Hang 걸린 것처럼 응답을 보류하기 때문에 Hang Get이라고도 불림.
- 무한 iframe에 script 태그를 추가하는 꼼수가 필요함.
구현 예시 (Java):
package com.example.longpolling;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.web.client.RestTemplate;
import java.util.concurrent.CompletableFuture;
@SpringBootApplication
@EnableAsync
public class LongPollingApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(LongPollingApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
longPolling();
}
@Async
public void longPolling() {
while (true) {
try {
CompletableFuture.runAsync(() -> {
try {
RestTemplate restTemplate = new RestTemplate();
String serverUrl = "<http://your-server-endpoint>";
// 서버에 요청 보내기
String response = restTemplate.getForObject(serverUrl, String.class);
// 데이터를 사용하여 UI 업데이트 (예: 콘솔 출력)
System.out.println("Received: " + response);
} catch (Exception e) {
e.printStackTrace();
}
}).get(); // CompletableFuture의 get() 호출로 동기화
// 잠시 대기 후 다시 요청 (필요한 경우 대기 시간을 조정)
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
WebSocket
작동 방식:
- 양방향 통신: 클라이언트와 서버 간 실시간 양방향 데이터 전송을 지원합니다. 서버는 클라이언트에게 이벤트를 푸시할 수 있고, 클라이언트도 서버에 메시지를 보낼 수 있습니다.
- 초기 핸드셰이킹: 최초 접속은 HTTP 요청을 통해 핸드셰이킹 과정을 거쳐 이루어집니다. 이후 ws 프로토콜을 통해 WebSocket 포트에 접속합니다.
- 연결 유지: 연결이 지속되며, 서버와 클라이언트 간의 실시간 데이터 전송이 가능해집니다.
장점:
- 실시간 양방향 통신: 클라이언트와 서버 간의 실시간 데이터 전송이 가능하며, 채팅방처럼 양방향 통신이 가능합니다.
- 기존 HTTP 포트 사용: 80, 443 포트를 사용하여 추가 방화벽 설정이 필요 없으며, HTTP 규격인 CORS 적용이나 인증 등의 과정을 기존과 동일하게 적용할 수 있습니다.
단점:
- 전이중 연결 필요: WebSocket 프로토콜을 처리하기 위해 전이중 연결과 새로운 WebSocket 서버가 필요하여 구현 비용이 높습니다.
사용 사례:
- 실시간 채팅: 예를 들어, 카카오톡 같은 채팅 애플리케이션.
- 주식 트레이딩 데이터: 실시간 주식 거래 정보 제공.
- 크립토 실시간 차트: 암호화폐 거래의 실시간 데이터 시각화.
Tip:
- Socket.io: Node.js 기반 기술로, 자체 스펙의 서버와 클라이언트를 통해 브라우저에 구애받지 않고 실시간 통신을 가능하게 합니다.
구현 예시 (Java):
import javax.websocket.ClientEndpoint;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.OnClose;
import javax.websocket.Session;
import javax.websocket.ContainerProvider;
import javax.websocket.WebSocketContainer;
import javax.websocket.ContainerProvider;
import javax.websocket.WebSocketContainer;
import java.net.URI;
@ClientEndpoint
public class WebSocketClient {
@OnOpen
public void onOpen(Session session) {
System.out.println("WebSocket is open now.");
}
@OnMessage
public void onMessage(String message) {
System.out.println("Message from server: " + message);
}
@OnClose
public void onClose(Session session, javax.websocket.CloseReason closeReason) {
System.out.println("WebSocket is closed now.");
}
public static void main(String[] args) {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
try {
URI uri = new URI("ws://server-endpoint");
container.connectToServer(WebSocketClient.class, uri);
} catch (Exception e) {
e.printStackTrace();
}
}
}
설명
- @ClientEndpoint: 이 어노테이션은 이 클래스가 WebSocket 클라이언트의 엔드포인트임을 나타냅니다.
- @OnOpen: WebSocket 연결이 열릴 때 호출되는 메소드입니다.
- @OnMessage: 서버로부터 메시지를 받을 때 호출되는 메소드입니다.
- @OnClose: WebSocket 연결이 닫힐 때 호출되는 메소드입니다.
- main 메소드: WebSocket 컨테이너를 생성하고 서버에 연결합니다.
SSE (Server-Sent Events)
SSE 작동 방식:
- 서버에서 클라이언트로 실시간 데이터를 스트리밍하는 HTML5 표준 기술입니다.
- 서버에서 클라이언트로 단방향 통신을 통해 실시간으로 데이터를 푸시합니다.
- 서버는 클라이언트의 요청에 대해 지속적으로 데이터를 전송하며, 클라이언트는 이 데이터를 수신하여 UI를 업데이트할 수 있습니다.
- HTTP API만으로 동작하며, 구현이 간단하고 HTTP 프로토콜을 사용합니다.
장점:
- 실시간 데이터 스트리밍: 서버에서 클라이언트로 실시간으로 데이터를 전송할 수 있습니다.
- 자동 재접속 지원: 연결이 끊어질 경우 자동으로 재연결을 시도합니다.
- HTTP 헤더 오버헤드 적음: 데이터 전송 시 헤더로 인한 오버헤드가 적습니다.
- HTTP/2 멀티플렉싱 지원: HTTP/2를 통해 멀티플렉싱이 가능하여 여러 데이터 스트림을 효율적으로 처리할 수 있습니다.
- 브라우저 호환성: IE를 제외한 대부분의 브라우저에서 지원되며, IE는 polyfill을 통해 지원 가능합니다.
단점:
- 양방향 통신 불가능: 클라이언트에서 서버로 데이터를 전송할 수 없으므로 양방향 통신이 필요한 경우 적합하지 않습니다.
사용 사례:
- 피드 구독: Twitter 피드, Facebook 친구 요청 등.
- 뉴스 업데이트: 실시간으로 뉴스 기사를 전송.
- 알림 기능: 사용자에게 실시간 알림을 전송.
Tip:
- SSE는 주로 클라이언트가 서버로부터 데이터를 수신만 하는 경우에 유용합니다. 피드 구독, 뉴스 업데이트, 알림 등 실시간으로 데이터를 수신하는 기능을 구현할 때 적합합니다.
구현 예시 (Java):
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException;
import java.time.LocalTime;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("/subscribe")
public class SseController {
@GetMapping
public SseEmitter streamEvents() {
SseEmitter emitter = new SseEmitter();
// Create a new thread to send events to the client
Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {
try {
emitter.send(SseEmitter.event()
.name("message")
.data("Current time: " + LocalTime.now()));
} catch (IOException e) {
emitter.completeWithError(e);
}
}, 0, 1, TimeUnit.SECONDS);
// Handle client disconnection
emitter.onCompletion(() -> System.out.println("Client disconnected"));
emitter.onError((e) -> System.out.println("Error occurred: " + e.getMessage()));
emitter.onTimeout(() -> System.out.println("Connection timed out"));
return emitter;
}
}
WebSocket vs Server-Sent Events
Feature WebSocket Server-Sent Events
| 브라우저 지원 | 대부분 지원 | 대부분 모던 브라우저 지원 |
| 통신 방향 | 양방향 | 단방향 (서버 -> 클라이언트) |
| 리얼타임 | O | O |
| 데이터 형태 | Binary, UTF-8 | UTF-8 |
| 자동 재접속 | X | O |
| 최대 동시 접속 수 | 서버 셋업에 따라 다름 | HTTP: 브라우저 당 6개, HTTP2: 100개 |
| 프로토콜 | WebSocket | HTTP |
| 배터리 소모량 | 큼 | 작음 |
| 방화벽 친화적 | X | O |
📚 출처:
'백엔드' 카테고리의 다른 글
| 백엔드 신입 개발자가 쌓아야 하는 역량은? - 자료구조/알고리즘/코딩테스트 (0) | 2024.08.06 |
|---|