요약 (TL;DR)
알림, 라이브 피드와 같은 단방향 서버-클라이언트 업데이트에는 Server-Sent Events (SSE)를 사용하세요. 채팅 및 게임과 같은 양방향 통신에는 WebSocket을 사용하세요. SSE는 더 간단하며 HTTP를 통해 작동합니다. WebSocket은 더 복잡하지만 양방향 메시징을 지원합니다. Modern PetstoreAPI는 다양한 실시간 사용 사례를 위해 이 둘을 모두 구현합니다.
소개
API에 실시간 업데이트가 필요합니다. 반려동물의 상태가 “입양 가능”에서 “입양 완료”로 변경될 때 클라이언트는 즉시 이를 알아야 합니다. WebSocket을 사용해야 할까요, 아니면 Server-Sent Events (SSE)를 사용해야 할까요?
대부분의 개발자는 WebSocket이 “더 강력하다”는 이유로 기본으로 사용합니다. 하지만 SSE가 더 나은 선택인 경우가 많습니다. SSE는 더 간단하고, 표준 HTTP를 통해 작동하며, 재연결을 자동으로 처리합니다. WebSocket은 필요하지 않은 복잡성을 추가할 수 있습니다.
Modern PetstoreAPI는 두 프로토콜을 모두 구현합니다. 반려동물 상태 업데이트 및 주문 알림에는 SSE를 사용하고, 라이브 경매 입찰 및 실시간 채팅에는 WebSocket을 사용합니다. 각 프로토콜은 서로 다른 사용 사례에 적합합니다.
이 가이드에서는 SSE와 WebSocket의 차이점을 배우고, Modern PetstoreAPI의 실제 예시를 확인하며, 각 프로토콜을 언제 사용해야 하는지 알아봅니다.
Server-Sent Events (SSE)란 무엇인가요?
SSE는 서버에서 클라이언트로 이벤트를 스트리밍하기 위한 HTTP 기반 프로토콜입니다.
SSE 작동 방식
클라이언트는 연결을 열고 이벤트가 발생할 때마다 수신합니다:
const eventSource = new EventSource('https://petstoreapi.com/v1/pets/notifications');
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Pet update:', data);
};
eventSource.addEventListener('adoption', (event) => {
const data = JSON.parse(event.data);
console.log('Pet adopted:', data.petId);
});
서버가 이벤트를 보냅니다:
GET /v1/pets/notifications
Accept: text/event-stream
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
event: adoption
data: {"petId":"019b4132","userId":"user-456"}
event: status-change
data: {"petId":"019b4127","status":"AVAILABLE"}
SSE 특징
1. 단방향 통신
서버가 클라이언트에 푸시합니다. 클라이언트는 SSE 연결을 통해 메시지를 다시 보낼 수 없습니다(하지만 일반 HTTP 요청은 사용할 수 있습니다).
2. HTTP 기반
표준 HTTP를 사용합니다. 프록시, 방화벽 및 CDN을 통해 작동합니다.
3. 자동 재연결
연결이 끊어지면 브라우저는 자동으로 재연결합니다.
4. 재개를 위한 이벤트 ID
서버는 이벤트 ID를 보낼 수 있습니다. 클라이언트는 마지막으로 수신한 이벤트부터 재개합니다:
id: 123
event: adoption
data: {"petId":"019b4132"}
id: 124
event: status-change
data: {"petId":"019b4127"}
연결이 끊어지면 클라이언트는 재개를 위해 Last-Event-ID: 124 헤더를 보냅니다.
5. 간단한 프로토콜
텍스트 기반 형식입니다. curl로 쉽게 디버그할 수 있습니다:
curl -N -H "Accept: text/event-stream" \
https://petstoreapi.com/v1/pets/notifications
SSE의 한계
1. 단방향만 가능
클라이언트는 SSE를 통해 메시지를 보낼 수 없습니다. 클라이언트-서버 통신을 위해서는 별도의 HTTP 요청이 필요합니다.
2. 텍스트 전용
SSE는 텍스트를 보냅니다. 바이너리 데이터는 base64로 인코딩되어야 합니다.
3. 브라우저 연결 제한
브라우저는 도메인당 SSE 연결 수를 제한합니다(일반적으로 6개). 대부분의 앱에서는 문제가 되지 않습니다.
4. 내장 압축 없음
HTTP 압축은 작동하지만, WebSocket과 같은 프로토콜 수준의 압축은 없습니다.
WebSocket이란 무엇인가요?
WebSocket은 영구 연결을 통한 전이중 양방향 프로토콜입니다.
WebSocket 작동 방식
클라이언트와 서버 모두 언제든지 메시지를 보낼 수 있습니다:
const ws = new WebSocket('wss://petstoreapi.com/auctions/019b4132');
// Send message to server
ws.send(JSON.stringify({
type: 'bid',
amount: 500
}));
// Receive messages from server
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Auction update:', data);
};
ws.onclose = () => {
console.log('Connection closed');
// Manual reconnection logic needed
};
서버는 언제든지 보낼 수 있습니다:
{"type":"bid","userId":"user-456","amount":550}
{"type":"outbid","newAmount":550}
클라이언트는 언제든지 보낼 수 있습니다:
{"type":"bid","amount":600}
{"type":"watch","petId":"019b4132"}
WebSocket 특징
1. 양방향
클라이언트와 서버 모두 언제든지 메시지를 보낼 수 있습니다. 진정한 양방향 통신입니다.
2. 낮은 지연 시간
영구 연결. 메시지당 HTTP 오버헤드가 없습니다. 게임, 채팅, 실시간 협업에 이상적입니다.
3. 바이너리 지원
바이너리 데이터를 직접 보낼 수 있습니다. base64 인코딩이 필요하지 않습니다.
4. 사용자 지정 프로토콜
ws:// 또는 wss:// (보안)를 사용합니다. 초기 핸드셰이크 후에는 HTTP가 아닙니다.
5. 프레임 기반
메시지는 프레임화됩니다. 부분 메시지를 보내고 재조립할 수 있습니다.
WebSocket의 한계
1. 복잡한 설정
WebSocket 서버가 필요합니다. HTTP 엔드포인트보다 더 복잡합니다.
2. 수동 재연결
자동 재연결이 없습니다. 재시도 로직을 직접 구현해야 합니다.
3. 프록시 문제
일부 기업 프록시는 WebSocket을 차단합니다. HTTP 프록시는 ws://를 이해하지 못합니다.
4. 상태 저장
서버는 연결을 추적해야 합니다. 상태 비저장 HTTP보다 확장이 더 어렵습니다.
5. HTTP 기능 없음
핸드셰이크 후에는 HTTP 캐싱, 상태 코드 또는 표준 헤더를 사용할 수 없습니다.
측면 비교
| 기능 | SSE | WebSocket |
|---|---|---|
| 방향 | 서버 → 클라이언트 | 양방향 |
| 프로토콜 | HTTP | 사용자 지정 (ws://) |
| 재연결 | 자동 | 수동 |
| 브라우저 지원 | 모든 최신 브라우저 | 모든 최신 브라우저 |
| 프록시 친화적 | 예 | 때때로 |
| 복잡성 | 간단함 | 복잡함 |
| 바이너리 데이터 | 아니오 (텍스트만) | 예 |
| 지연 시간 | 낮음 | 매우 낮음 |
| 확장성 | 높음 (상태 비저장) | 중간 (상태 저장) |
| 사용 사례 | 알림, 피드 | 채팅, 게임, 협업 |
Modern PetstoreAPI는 이 둘을 어떻게 사용하나요?
Modern PetstoreAPI는 다양한 시나리오를 위해 SSE와 WebSocket을 모두 구현합니다.
반려동물 업데이트를 위한 SSE
엔드포인트: GET /v1/pets/notifications
const events = new EventSource(
'https://petstoreapi.com/v1/pets/notifications?userId=user-456'
);
events.addEventListener('adoption', (e) => {
const data = JSON.parse(e.data);
showNotification(`${data.petName} was adopted!`);
});
events.addEventListener('status-change', (e) => {
const data = JSON.parse(e.data);
updatePetStatus(data.petId, data.status);
});
서버 구현:
app.get('/v1/pets/notifications', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
const userId = req.query.userId;
// Subscribe to pet updates
const subscription = petUpdates.subscribe(userId, (event) => {
res.write(`event: ${event.type}\n`);
res.write(`data: ${JSON.stringify(event.data)}\n\n`);
});
req.on('close', () => {
subscription.unsubscribe();
});
});
사용 사례:
- 반려동물 상태 변경 (입양 가능 → 입양 완료)
- 주문 알림 (주문됨, 배송됨, 배달 완료됨)
- 재고 업데이트
- 가격 변경
라이브 경매를 위한 WebSocket
엔드포인트: wss://petstoreapi.com/auctions/{auctionId}
const ws = new WebSocket('wss://petstoreapi.com/auctions/019b4132');
// Place bid
function placeBid(amount) {
ws.send(JSON.stringify({
type: 'bid',
amount
}));
}
// Receive updates
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
switch (msg.type) {
case 'bid':
updateCurrentBid(msg.amount, msg.userId);
break;
case 'outbid':
showOutbidNotification(msg.newAmount);
break;
case 'auction-end':
showAuctionResult(msg.winner);
break;
}
};
서버 구현:
wss.on('connection', (ws, req) => {
const auctionId = req.params.auctionId;
const auction = auctions.get(auctionId);
ws.on('message', (data) => {
const msg = JSON.parse(data);
if (msg.type === 'bid') {
const result = auction.placeBid(msg.userId, msg.amount);
// Broadcast to all participants
auction.participants.forEach(participant => {
participant.send(JSON.stringify({
type: 'bid',
userId: msg.userId,
amount: msg.amount
}));
});
}
});
});
사용 사례:
- 라이브 경매 입찰
- 지원팀과의 실시간 채팅
- 협업 반려동물 돌봄 계획
- 판매 중 실시간 재고 업데이트
Apidog로 실시간 API 테스트하기
Apidog는 SSE 및 WebSocket API 테스트를 모두 지원합니다.
SSE 테스트
1. SSE 요청 생성:
GET https://petstoreapi.com/v1/pets/notifications
Accept: text/event-stream
2. 이벤트 유효성 검사:
- 이벤트 유형 확인
- JSON 페이로드 유효성 검사
- 재연결 동작 테스트
- 이벤트 ID 확인
3. 테스트 시나리오:
- 연결 끊김
- 서버 재시작
- 이벤트 순서
- 마지막 이벤트부터 재개
WebSocket 테스트
1. WebSocket 연결 생성:
wss://petstoreapi.com/auctions/019b4132
2. 테스트 메시지 전송:
{"type":"bid","amount":500}
{"type":"watch","petId":"019b4132"}
3. 응답 유효성 검사:
- 메시지 형식 확인
- 양방향 흐름 테스트
- 연결 처리 확인
- 오류 시나리오 테스트
4. 테스트 시나리오:
- 여러 동시 연결
- 메시지 순서
- 연결 시간 초과
- 재연결 로직
각각을 언제 사용해야 할까요?
SSE를 사용할 경우:
- 단방향 업데이트 - 서버가 클라이언트에 푸시하며, 클라이언트는 다시 보낼 필요가 없는 경우
- 간단한 설정 - 표준 HTTP 인프라를 사용하고자 하는 경우
- 자동 재연결 - 재시도 로직을 구현하고 싶지 않은 경우
- 프록시 친화적 - 기업 방화벽을 통해 작동해야 하는 경우
- 알림 - 상태 업데이트, 경고, 라이브 피드
예시:
- 반려동물 입양 알림
- 주문 상태 업데이트
- 재고 변경
- 가격 알림
- 뉴스 피드
WebSocket을 사용할 경우:
- 양방향 - 클라이언트와 서버 모두 자주 메시지를 주고받는 경우
- 낮은 지연 시간이 중요 - 게임, 실시간 협업
- 바이너리 데이터 - 이미지, 오디오, 비디오 전송
- 사용자 지정 프로토콜 - 메시지 형식을 완전히 제어해야 하는 경우
- 높은 메시지 빈도 - 초당 수백 개의 메시지
예시:
- 라이브 경매 입찰
- 실시간 채팅
- 멀티플레이어 게임
- 협업 편집
- 라이브 비디오 스트리밍
다음과 같은 이유로 WebSocket을 사용하지 마세요:
❌ “더 진보된 기술이니까” - 이점 없는 복잡성
❌ “모두가 사용하니까” - SSE가 종종 더 간단합니다
❌ “더 빠르니까” - SSE는 대부분의 사용 사례에 충분히 빠릅니다
❌ “양방향이니까” - 정말로 양방향 통신이 필요한가요?
결론
SSE와 WebSocket은 모두 실시간 통신을 가능하게 하지만, 서로 다른 시나리오를 위해 설계되었습니다. SSE는 자동 재연결과 HTTP 호환성을 갖춘 단방향 서버-클라이언트 업데이트에 탁월합니다. WebSocket은 양방향의 낮은 지연 시간 통신에 뛰어납니다.
Modern PetstoreAPI는 두 프로토콜을 효과적으로 사용하는 방법을 보여줍니다. 알림 및 상태 업데이트에는 SSE를, 라이브 경매 및 채팅에는 WebSocket을 사용합니다. 어떤 프로토콜이 “더 좋다”고 생각하는지에 따라 선택하지 말고, 사용 사례에 따라 선택하세요.
다양한 시나리오에서 SSE 및 WebSocket 구현이 올바르게 작동하는지 확인하려면 Apidog를 사용하여 실시간 API를 테스트하세요.
자주 묻는 질문 (FAQ)
SSE는 기업 방화벽을 통과할 수 있나요?
네, 그렇습니다. SSE는 표준 HTTP를 사용하므로 HTTP 프록시 및 방화벽을 통해 작동합니다. WebSocket은 일부 프록시가 차단하는 사용자 지정 프로토콜을 사용합니다.
WebSocket이 SSE보다 빠른가요?
WebSocket은 메시지당 HTTP 오버헤드가 없으므로 지연 시간이 약간 더 낮지만, 대부분의 애플리케이션에서는 그 차이가 미미합니다. SSE는 알림, 피드 및 상태 업데이트에 충분히 빠릅니다.
SSE 재연결은 어떻게 처리하나요?
브라우저가 재연결을 자동으로 처리합니다. 서버에서 이벤트 ID를 보내면 클라이언트는 Last-Event-ID 헤더를 사용하여 마지막으로 수신한 이벤트부터 재개합니다.
모바일 앱에서 SSE를 사용할 수 있나요?
네, 그렇습니다. iOS와 Android는 네이티브 HTTP 클라이언트 또는 라이브러리를 통해 SSE를 지원합니다. SSE는 HTTP가 작동하는 모든 곳에서 작동합니다.
SSE 연결의 최대 시간은 얼마인가요?
정해진 제한은 없습니다. SSE 연결은 무기한 열려 있을 수 있습니다. 일부 프록시 또는 로드 밸런서는 시간 초과(일반적으로 30-60초)를 가질 수 있지만, 브라우저는 자동으로 재연결합니다.
WebSocket은 바이너리 데이터를 보낼 수 있나요?
네, 그렇습니다. WebSocket은 텍스트 및 바이너리 프레임을 모두 지원합니다. base64 인코딩 없이 이미지, 오디오 또는 모든 바이너리 데이터를 보낼 수 있습니다.
브라우저는 몇 개의 SSE 연결을 가질 수 있나요?
브라우저는 도메인당 SSE 연결을 제한합니다(일반적으로 6개). 이는 대부분의 앱에서 1-2개의 SSE 연결만 필요하므로 거의 문제가 되지 않습니다.
SSE를 위해 특별한 서버가 필요한가요?
아니요. 모든 HTTP 서버는 SSE를 처리할 수 있습니다. 올바른 헤더(Content-Type: text/event-stream)를 설정하고 연결을 열어 두기만 하면 됩니다.
