실시간 API와 REST의 미래

실시간 API와 REST의 미래

실시간 API 개요

전통적인 요청-응답 방식의 REST API는 클라이언트가 필요할 때만 데이터를 가져오는 Pull 모델이다. 실시간 API는 서버에서 이벤트가 발생하면 즉시 클라이언트에게 전달하는 Push 모델을 지원한다.

┌─────────────────────────────────────────────────────────────────┐
│                    실시간 API 통신 모델                          │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   전통적 REST (Pull)              실시간 API (Push)              │
│  ┌─────────────────────┐       ┌─────────────────────┐         │
│  │  Client    Server   │       │  Client    Server   │         │
│  │    │         │      │       │    │         │      │         │
│  │    │──요청──►│      │       │    │◄──이벤트──│      │         │
│  │    │◄─응답──│      │       │    │◄──이벤트──│      │         │
│  │    │         │      │       │    │◄──이벤트──│      │         │
│  │    │──요청──►│      │       │    │         │      │         │
│  │    │◄─응답──│      │       │                      │         │
│  └─────────────────────┘       └─────────────────────┘         │
│                                                                 │
│   클라이언트가 주기적 요청        서버가 변경 시 즉시 푸시          │
│   → 지연 발생, 리소스 낭비       → 실시간, 효율적                 │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

폴링의 한계

폴링(Polling)은 클라이언트가 주기적으로 서버에 요청을 보내 데이터 변경을 확인하는 방식이다.

┌─────────────────────────────────────────────────────────────────┐
│                      폴링의 문제점                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   Client                                         Server         │
│     │                                              │            │
│     │──── 데이터 있나요? ───────────────────────►│            │
│     │◄─── 없어요 (빈 응답) ─────────────────────│   낭비 #1   │
│     │                                              │            │
│     │──── 데이터 있나요? ───────────────────────►│            │
│     │◄─── 없어요 (빈 응답) ─────────────────────│   낭비 #2   │
│     │                                              │            │
│     │──── 데이터 있나요? ───────────────────────►│            │
│     │◄─── 있어요! {data} ──────────────────────│   성공!     │
│     │                                              │            │
│     │     ... (반복) ...                           │            │
│                                                                 │
│   문제점:                                                       │
│   • 대역폭 낭비 (빈 응답도 네트워크 비용)                         │
│   • 서버 부하 (불필요한 요청 처리)                                │
│   • 실시간성 부족 (폴링 간격만큼 지연)                            │
│   • 확장성 저하 (클라이언트 증가 시 부하 급증)                     │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

폴링의 대안

기술방식방향실시간성
Long Polling응답 대기 연장단방향중간
SSE이벤트 스트리밍단방향 (서버→클라이언트)높음
WebSocket양방향 소켓양방향높음
WebHookHTTP 콜백단방향 (서버→서버)높음

SSE (Server-Sent Events)

HTML5 표준 기술로, 서버에서 클라이언트로 이벤트를 실시간 푸시하는 단방향 통신 방식

SSE 동작 원리

┌─────────────────────────────────────────────────────────────────┐
│                       SSE 동작 흐름                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   Client (Browser)                               Server         │
│     │                                              │            │
│     │──── GET /events ────────────────────────────►│            │
│     │     Accept: text/event-stream               │            │
│     │                                              │            │
│     │◄─── HTTP/1.1 200 OK ────────────────────────│            │
│     │     Content-Type: text/event-stream         │            │
│     │     Connection: keep-alive                  │            │
│     │                                              │            │
│     │         ┌── 연결 유지 (Open) ──┐             │            │
│     │         │                      │             │            │
│     │◄────────│── event: message ────│─────────────│  이벤트 1  │
│     │         │   data: {"id":1}     │             │            │
│     │         │                      │             │            │
│     │◄────────│── event: update ─────│─────────────│  이벤트 2  │
│     │         │   data: {"id":2}     │             │            │
│     │         │                      │             │            │
│     │◄────────│── event: message ────│─────────────│  이벤트 3  │
│     │         │   data: {"id":3}     │             │            │
│     │         └──────────────────────┘             │            │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

SSE 메시지 형식

# 기본 메시지
data: Hello World\n\n

# 여러 줄 메시지
data: Line 1\n
data: Line 2\n\n

# 이벤트 ID + 타입 지정
id: 12345\n
event: user-update\n
data: {"userId": 123, "status": "online"}\n\n

# 재연결 간격 설정 (밀리초)
retry: 5000\n
data: Connection settings updated\n\n

SSE 필드

필드설명
data이벤트 데이터 (필수)
event이벤트 타입 (기본: message)
id이벤트 ID (재연결 시 Last-Event-ID로 전송)
retry재연결 대기 시간 (ms)

Spring WebFlux SSE 구현

@RestController
@RequestMapping("/api/v1/events")
public class SSEController {

    @GetMapping(produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<String>> streamEvents() {
        return Flux.interval(Duration.ofSeconds(1))
                .map(sequence -> ServerSentEvent.<String>builder()
                        .id(String.valueOf(sequence))
                        .event("heartbeat")
                        .data("Ping " + LocalDateTime.now())
                        .build());
    }

    @GetMapping(value = "/notifications", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<Notification>> streamNotifications() {
        return notificationService.getNotificationStream()
                .map(notification -> ServerSentEvent.<Notification>builder()
                        .id(notification.getId())
                        .event(notification.getType())
                        .data(notification)
                        .retry(Duration.ofSeconds(5))
                        .build());
    }
}

Spring MVC SSE (SseEmitter)

@RestController
@RequestMapping("/api/v1/sse")
public class SseController {

    private final List<SseEmitter> emitters = new CopyOnWriteArrayList<>();

    @GetMapping("/subscribe")
    public SseEmitter subscribe() {
        SseEmitter emitter = new SseEmitter(Long.MAX_VALUE);

        emitter.onCompletion(() -> emitters.remove(emitter));
        emitter.onTimeout(() -> emitters.remove(emitter));
        emitter.onError(e -> emitters.remove(emitter));

        emitters.add(emitter);
        return emitter;
    }

    public void sendToAll(String eventName, Object data) {
        List<SseEmitter> deadEmitters = new ArrayList<>();

        emitters.forEach(emitter -> {
            try {
                emitter.send(SseEmitter.event()
                        .name(eventName)
                        .data(data));
            } catch (IOException e) {
                deadEmitters.add(emitter);
            }
        });

        emitters.removeAll(deadEmitters);
    }
}

JavaScript EventSource API

// SSE 연결
const eventSource = new EventSource('/api/v1/events');

// 기본 메시지 핸들러
eventSource.onmessage = (event) => {
    console.log('Message:', event.data);
};

// 특정 이벤트 타입 핸들러
eventSource.addEventListener('user-update', (event) => {
    const data = JSON.parse(event.data);
    console.log('User update:', data);
});

// 연결 상태 핸들러
eventSource.onopen = () => console.log('Connected');
eventSource.onerror = (error) => console.error('Error:', error);

// 연결 종료
eventSource.close();

WebSocket

단일 TCP 연결에서 전이중(Full-Duplex) 양방향 통신을 제공하는 프로토콜

WebSocket vs HTTP

┌─────────────────────────────────────────────────────────────────┐
│                   WebSocket vs HTTP 비교                        │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   HTTP                              WebSocket                   │
│  ┌─────────────────────┐          ┌─────────────────────┐      │
│  │                     │          │                     │      │
│  │  요청 → 응답 → 종료  │          │  핸드셰이크 → 연결 유지│      │
│  │  요청 → 응답 → 종료  │          │      ↕ 양방향 통신    │      │
│  │  요청 → 응답 → 종료  │          │      ↕ 양방향 통신    │      │
│  │                     │          │         ...          │      │
│  │  매번 연결/해제      │          │  한 번 연결, 계속 사용│      │
│  │  헤더 오버헤드 큼    │          │  2바이트 프레임       │      │
│  │                     │          │                     │      │
│  └─────────────────────┘          └─────────────────────┘      │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

WebSocket 핸드셰이크

# 클라이언트 요청
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

# 서버 응답
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

Spring WebSocket 구현

// WebSocket 설정
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(chatHandler(), "/ws/chat")
                .setAllowedOrigins("*");
    }

    @Bean
    public WebSocketHandler chatHandler() {
        return new ChatWebSocketHandler();
    }
}
// WebSocket 핸들러
public class ChatWebSocketHandler extends TextWebSocketHandler {

    private final Set<WebSocketSession> sessions = ConcurrentHashMap.newKeySet();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        sessions.add(session);
        log.info("New connection: {}", session.getId());
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) {
        String payload = message.getPayload();
        log.info("Received: {}", payload);

        // 모든 클라이언트에게 브로드캐스트
        sessions.forEach(s -> {
            try {
                s.sendMessage(new TextMessage("Echo: " + payload));
            } catch (IOException e) {
                log.error("Send failed", e);
            }
        });
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
        sessions.remove(session);
        log.info("Connection closed: {}", session.getId());
    }
}

STOMP over WebSocket

// STOMP 설정 (메시지 브로커 패턴)
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketStompConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/topic", "/queue");
        registry.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws")
                .setAllowedOrigins("*")
                .withSockJS();
    }
}
// STOMP 컨트롤러
@Controller
public class ChatController {

    @MessageMapping("/chat.send")
    @SendTo("/topic/messages")
    public ChatMessage sendMessage(ChatMessage message) {
        return message;
    }

    @MessageMapping("/chat.join")
    @SendTo("/topic/users")
    public ChatMessage addUser(@Payload ChatMessage message,
                               SimpMessageHeaderAccessor headerAccessor) {
        headerAccessor.getSessionAttributes().put("username", message.getSender());
        return message;
    }
}

JavaScript WebSocket API

// WebSocket 연결
const socket = new WebSocket('ws://localhost:8080/ws/chat');

// 연결 성공
socket.onopen = () => {
    console.log('Connected to WebSocket');
    socket.send(JSON.stringify({ type: 'join', user: 'John' }));
};

// 메시지 수신
socket.onmessage = (event) => {
    const message = JSON.parse(event.data);
    console.log('Received:', message);
};

// 에러 처리
socket.onerror = (error) => {
    console.error('WebSocket Error:', error);
};

// 연결 종료
socket.onclose = (event) => {
    console.log('Disconnected:', event.code, event.reason);
};

// 메시지 전송
socket.send(JSON.stringify({ type: 'message', content: 'Hello!' }));

// 연결 닫기
socket.close();

WebHook

서버 간 이벤트 알림을 위한 HTTP 콜백 메커니즘

WebHook 동작 원리

┌─────────────────────────────────────────────────────────────────┐
│                      WebHook 동작 흐름                           │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   1. 웹훅 등록                                                   │
│   ┌──────────────┐                    ┌──────────────┐         │
│   │  Subscriber  │─── POST /webhooks ─►│   Provider   │         │
│   │  (구독자 서버) │  {url: callback}    │ (이벤트 발생) │         │
│   └──────────────┘◄─── 201 Created ───└──────────────┘         │
│                                                                 │
│   2. 이벤트 발생 시                                               │
│   ┌──────────────┐                    ┌──────────────┐         │
│   │  Subscriber  │◄── POST /callback ──│   Provider   │         │
│   │              │   {event: "..."}    │  이벤트 발생! │         │
│   │              │                     │              │         │
│   │ 이벤트 처리   │─── 200 OK ─────────►│ 전송 완료 확인│         │
│   └──────────────┘                    └──────────────┘         │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

WebHook 페이로드 예시

// GitHub WebHook 예시
{
    "action": "opened",
    "number": 123,
    "pull_request": {
        "id": 456,
        "title": "Fix bug",
        "user": {
            "login": "developer"
        }
    },
    "repository": {
        "full_name": "org/repo"
    }
}

WebHook 수신 구현

@RestController
@RequestMapping("/api/v1/webhooks")
public class WebhookController {

    @PostMapping("/github")
    public ResponseEntity<Void> handleGithubWebhook(
            @RequestHeader("X-GitHub-Event") String eventType,
            @RequestHeader("X-Hub-Signature-256") String signature,
            @RequestBody String payload) {

        // 시그니처 검증
        if (!verifySignature(payload, signature)) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
        }

        // 이벤트 타입별 처리
        switch (eventType) {
            case "push":
                handlePushEvent(payload);
                break;
            case "pull_request":
                handlePullRequestEvent(payload);
                break;
            default:
                log.info("Unhandled event: {}", eventType);
        }

        return ResponseEntity.ok().build();
    }

    private boolean verifySignature(String payload, String signature) {
        String expected = "sha256=" + HmacUtils.hmacSha256Hex(webhookSecret, payload);
        return MessageDigest.isEqual(expected.getBytes(), signature.getBytes());
    }
}

WebHook 발신 구현

@Service
public class WebhookService {

    private final WebClient webClient;
    private final WebhookRepository webhookRepository;

    public void sendWebhook(String eventType, Object payload) {
        List<Webhook> subscribers = webhookRepository.findByEventType(eventType);

        subscribers.forEach(webhook -> {
            sendWithRetry(webhook, eventType, payload);
        });
    }

    private void sendWithRetry(Webhook webhook, String eventType, Object payload) {
        int maxRetries = 3;
        int retryCount = 0;

        while (retryCount < maxRetries) {
            try {
                webClient.post()
                        .uri(webhook.getCallbackUrl())
                        .header("X-Webhook-Event", eventType)
                        .header("X-Webhook-Signature", generateSignature(payload))
                        .bodyValue(payload)
                        .retrieve()
                        .toBodilessEntity()
                        .block(Duration.ofSeconds(10));

                log.info("Webhook sent successfully to {}", webhook.getCallbackUrl());
                return;

            } catch (Exception e) {
                retryCount++;
                log.warn("Webhook failed (attempt {}): {}", retryCount, e.getMessage());

                if (retryCount < maxRetries) {
                    try {
                        Thread.sleep(1000L * retryCount); // Exponential backoff
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                    }
                }
            }
        }

        log.error("Webhook failed after {} attempts: {}", maxRetries, webhook.getCallbackUrl());
    }
}

실시간 통신 기술 비교

┌─────────────────────────────────────────────────────────────────┐
│                   실시간 통신 기술 비교                          │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│              SSE              WebSocket           WebHook       │
│  ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐ │
│  │   서버 → 클라이언트│  │   ◄── 양방향 ──►│  │   서버 → 서버   │ │
│  │      단방향      │  │                 │  │     단방향      │ │
│  └─────────────────┘  └─────────────────┘  └─────────────────┘ │
│                                                                 │
│  • HTTP 기반         • TCP 기반          • HTTP POST 콜백      │
│  • 자동 재연결       • 직접 재연결 구현   • 재시도 로직 필요    │
│  • 텍스트만 전송     • 바이너리 지원      • JSON 페이로드       │
│  • 브라우저 지원     • 브라우저 + 서버    • 서버 간 통신        │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
특성SSEWebSocketWebHook
통신 방향단방향 (서버→클라이언트)양방향단방향 (서버→서버)
프로토콜HTTPWebSocket (ws://, wss://)HTTP
연결 유지OOX (요청 시에만)
자동 재연결O (브라우저 내장)X (직접 구현)N/A
바이너리 지원X (텍스트만)OO
사용 사례알림, 피드채팅, 게임서비스 통합

기술 선택 가이드

┌─────────────────────────────────────────────────────────────────┐
│                     기술 선택 가이드                             │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   질문                                      권장 기술            │
│  ┌────────────────────────────────────┬───────────────────┐    │
│  │ 서버→클라이언트 단방향 실시간?       │ SSE               │    │
│  │ 예: 알림, 주식 시세, 뉴스 피드       │                   │    │
│  ├────────────────────────────────────┼───────────────────┤    │
│  │ 클라이언트↔서버 양방향 실시간?       │ WebSocket         │    │
│  │ 예: 채팅, 협업 도구, 게임            │                   │    │
│  ├────────────────────────────────────┼───────────────────┤    │
│  │ 서버 간 이벤트 알림?                 │ WebHook           │    │
│  │ 예: CI/CD 트리거, 결제 알림          │                   │    │
│  ├────────────────────────────────────┼───────────────────┤    │
│  │ 방화벽/프록시 제약이 심한 환경?      │ SSE (HTTP 기반)   │    │
│  ├────────────────────────────────────┼───────────────────┤    │
│  │ 바이너리 데이터 전송 필요?           │ WebSocket         │    │
│  └────────────────────────────────────┴───────────────────┘    │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

REST의 대안과 미래

GraphQL

Facebook이 개발한 쿼리 언어로, 클라이언트가 필요한 데이터만 정확히 요청할 수 있다.

┌─────────────────────────────────────────────────────────────────┐
│                    REST vs GraphQL                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   REST                               GraphQL                    │
│  ┌─────────────────────────┐       ┌─────────────────────────┐ │
│  │ GET /users/1            │       │ query {                 │ │
│  │ GET /users/1/posts      │       │   user(id: 1) {         │ │
│  │ GET /users/1/followers  │       │     name                │ │
│  │                         │       │     posts { title }     │ │
│  │ → 3번의 요청            │       │     followers { name }  │ │
│  │ → Over-fetching 가능    │       │   }                     │ │
│  │                         │       │ }                       │ │
│  │                         │       │ → 1번의 요청, 필요한것만│ │
│  └─────────────────────────┘       └─────────────────────────┘ │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
특성RESTGraphQL
엔드포인트리소스별 여러 개단일 엔드포인트
데이터 페칭Over/Under-fetching 가능필요한 것만 정확히
버저닝URL 버전 (/v1/, /v2/)스키마 진화
캐싱HTTP 캐싱 활용별도 구현 필요
학습 곡선낮음높음

gRPC

Google이 개발한 고성능 RPC 프레임워크로, Protocol Buffers를 사용해 바이너리 직렬화

┌─────────────────────────────────────────────────────────────────┐
│                    REST vs gRPC                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   REST/JSON                          gRPC/Protobuf              │
│  ┌─────────────────────────┐       ┌─────────────────────────┐ │
│  │ {                       │       │ message User {          │ │
│  │   "id": 123,            │       │   int32 id = 1;         │ │
│  │   "name": "John",       │       │   string name = 2;      │ │
│  │   "email": "j@mail.com" │       │   string email = 3;     │ │
│  │ }                       │       │ }                       │ │
│  │                         │       │                         │ │
│  │ → 텍스트 기반, 사람 가독│       │ → 바이너리, 더 빠름     │ │
│  │ → HTTP/1.1             │       │ → HTTP/2, 스트리밍      │ │
│  └─────────────────────────┘       └─────────────────────────┘ │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
특성RESTgRPC
프로토콜HTTP/1.1HTTP/2
포맷JSON (텍스트)Protobuf (바이너리)
스트리밍제한적네이티브 지원
코드 생성선택적필수 (IDL)
브라우저네이티브 지원gRPC-Web 필요
사용 사례공개 API마이크로서비스 간 통신

마이크로서비스와 REST

모노리스 vs 마이크로서비스

┌─────────────────────────────────────────────────────────────────┐
│              모노리스 vs 마이크로서비스 아키텍처                   │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   Monolithic                         Microservices              │
│  ┌─────────────────────────┐       ┌─────────────────────────┐ │
│  │ ┌─────────────────────┐ │       │ ┌───┐ ┌───┐ ┌───┐ ┌───┐│ │
│  │ │   User Service      │ │       │ │ U │ │ O │ │ P │ │ N ││ │
│  │ ├─────────────────────┤ │       │ │ s │ │ r │ │ a │ │ o ││ │
│  │ │   Order Service     │ │       │ │ e │ │ d │ │ y │ │ t ││ │
│  │ ├─────────────────────┤ │       │ │ r │ │ e │ │ m │ │ i ││ │
│  │ │   Payment Service   │ │       │ │   │ │ r │ │ e │ │ f ││ │
│  │ ├─────────────────────┤ │       │ │   │ │   │ │ n │ │ y ││ │
│  │ │   Notification      │ │       │ │   │ │   │ │ t │ │   ││ │
│  │ └─────────────────────┘ │       │ └───┘ └───┘ └───┘ └───┘│ │
│  │                         │       │   ↕     ↕     ↕     ↕  │ │
│  │     Single Deployment   │       │      REST / gRPC       │ │
│  │     Single Database     │       │   각자 독립 DB 가능     │ │
│  └─────────────────────────┘       └─────────────────────────┘ │
│                                                                 │
│   • 배포 단위: 전체          • 배포 단위: 서비스별              │
│   • 확장: 전체 복제          • 확장: 필요한 서비스만            │
│   • 장애: 전체 영향          • 장애: 해당 서비스만              │
│   • 기술: 단일 스택          • 기술: 서비스별 선택 가능         │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

마이크로서비스 장점

장점설명
단순성각 서비스가 단일 책임, 이해하기 쉬움
독립 배포다른 서비스 영향 없이 배포 가능
확장성필요한 서비스만 선택적 확장
기술 자유서비스별 최적 기술 스택 선택
장애 격리한 서비스 장애가 전체로 전파 안 됨
팀 자율성서비스별 독립 팀 운영 가능

API Gateway 패턴

┌─────────────────────────────────────────────────────────────────┐
│                    API Gateway 패턴                             │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   Clients                  API Gateway            Services      │
│  ┌──────────┐            ┌──────────────┐      ┌──────────┐   │
│  │  Web     │────────────│              │──────│  User    │   │
│  └──────────┘            │              │      └──────────┘   │
│  ┌──────────┐            │   • 라우팅    │      ┌──────────┐   │
│  │  Mobile  │────────────│   • 인증     │──────│  Order   │   │
│  └──────────┘            │   • Rate Limit│      └──────────┘   │
│  ┌──────────┐            │   • 로깅     │      ┌──────────┐   │
│  │  Partner │────────────│   • 캐싱     │──────│  Payment │   │
│  └──────────┘            │              │      └──────────┘   │
│                          └──────────────┘                      │
│                                                                 │
│   단일 진입점, 횡단 관심사 처리, 서비스 추상화                     │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

핵심 용어 정리

용어설명
Polling클라이언트가 주기적으로 서버에 요청하는 방식
Long Polling응답이 있을 때까지 연결 유지하는 폴링
SSEServer-Sent Events, 서버→클라이언트 단방향 스트리밍
WebSocket전이중 양방향 실시간 통신 프로토콜
WebHook이벤트 발생 시 등록된 URL로 HTTP 콜백
STOMPSimple Text Oriented Messaging Protocol
PubSub발행-구독 메시징 패턴
GraphQL클라이언트 주도 쿼리 언어
gRPCGoogle의 고성능 RPC 프레임워크
Protocol Buffers구조화된 데이터 바이너리 직렬화 포맷
마이크로서비스독립 배포 가능한 소규모 서비스 아키텍처
API Gateway마이크로서비스 단일 진입점
QoSQuality of Service, 서비스 품질