REST API와 WebSocket, 어떻게 구분해서 써야 할까?

dobby·2026년 4월 24일

Let's git it BE

목록 보기
4/20

Let's Git It 프로젝트를 개발하면서 실시간 멀티플레이어 게임 서버를 설계하다 마주친 고민을 정리했다.


배경

Let's Git It은 Git 명령어를 게임으로 학습하는 멀티플레이어 플랫폼이다.
Speed Run, Time Attack, 협력 모드 등 실시간 인터랙션이 핵심인 서비스다 보니,
대기실 입장부터 게임 종료까지 모든 흐름에서 "이 이벤트는 REST로 처리해야 할까, WebSocket으로 처리해야 할까?" 라는 질문이 계속 따라왔다.

처음에는 "실시간 게임이니까 WebSocket으로 다 하면 되지 않나?" 라고 생각했다.
그런데 팀 내 리뷰를 거치면서 생각보다 이 둘의 경계가 명확하다는 걸 알게 됐다.


REST API vs WebSocket 기본 개념

먼저 두 기술의 본질적인 차이부터 짚고 가자.

항목REST APIWebSocket
통신 방향클라이언트 → 서버 (단방향 요청-응답)클라이언트 ↔ 서버 (양방향)
연결 방식요청마다 새로운 연결한 번 연결 후 유지
응답 보장HTTP 상태코드로 명확히 보장별도 ACK 없으면 보장 안 됨
적합한 상황명확한 요청-응답, 상태 변경실시간 이벤트, 브로드캐스트

고민 1: 나가기 버튼 — REST? WebSocket?

처음 생각

"어차피 WebSocket 연결이 있으니까, LEAVE 메시지 하나 보내면 되지 않나?"

실제로 초기 명세는 WebSocket으로만 처리하도록 설계했다.

클라이언트 → STOMP SEND /app/room/{roomId}/leave
서버 → 상태 업데이트 + 나머지 플레이어에게 브로드캐스트

문제점 발견

팀 리뷰에서 이런 피드백을 받았다.

"나가기는 REST로 하고, 이후 알림은 WebSocket으로 해야 해"

처음엔 이유를 몰랐다. WebSocket으로도 동일하게 동작하는 것 같았으니까.

핵심 이유: 자발적 나가기 vs 갑자기 튕김 구분

결국 핵심은 서버가 연결 종료의 이유를 알 수 있느냐의 문제였다.

WebSocket 연결이 끊기는 경우는 두 가지다.

① 사용자가 나가기 버튼을 눌렀다  →  의도적 종료
② 사용자 네트워크가 갑자기 끊겼다  →  비정상 종료

WebSocket만 사용할 경우, 서버는 두 경우 모두 SessionDisconnectEvent로만 감지한다.
즉, 연결이 끊겼다는 사실은 알지만, 끊겼는지는 모른다.

서버: WebSocket 연결이 끊겼네
서버: ...이게 나간 건지, 튕긴 건지?

REST API를 사용할 경우, 나가기 버튼을 누르면 HTTP 요청이 먼저 전송된다.

REST 호출 있음 → 자발적 나가기
REST 없이 WS만 끊김 → 비정상 종료 (튕김)

HTTP 요청 자체가 "이건 내가 의도적으로 나간 것" 이라는 증거가 된다.

정리

나가기를 REST로 처리하는 이유는 응답을 받기 위해서가 아니라,
자발적 종료와 비정상 종료를 구분하기 위해서다.


고민 2: 강퇴 — REST? WebSocket?

결론: WebSocket

강퇴는 즉시성브로드캐스트가 핵심이다.

방장 → KICK_REQUEST 전송
서버 → 강퇴 대상에게 /queue/private (KICKED)
서버 → 나머지 전체에게 /topic/room/{roomId} (PLAYER_KICKED)

강퇴는 세 가지 이유로 WebSocket이 적합하다.

  1. 즉시 알림이 필요하다 — 강퇴 대상자의 세션을 즉시 처리해야 한다
  2. 동시 브로드캐스트가 필요하다 — 강퇴된 사람, 남은 사람 모두 동시에 알아야 한다
  3. 게임 이벤트 흐름과 일관성 — 대기실의 다른 이벤트(준비, 채팅 등)와 동일한 채널로 처리된다

REST로 처리하면 결국 알림은 WebSocket으로 따로 보내야 하므로, 굳이 두 채널을 섞을 이유가 없다.


고민 3: 방 입장 — REST? WebSocket?

결론: REST + WebSocket

방 입장은 REST로 처리하고, 입장 확정 이후 서버가 WebSocket으로 브로드캐스트하는 방식이다.

POST /rooms/{roomId}/join  (REST)
  ↓
서버: 인원 초과 체크, 중복 입장 체크, 세션 등록
  ↓
서버 → /topic/room/{roomId} (PLAYER_JOINED) 브로드캐스트

방 입장을 REST로 처리하는 이유는 두 가지다.

  • 입장 가능 여부를 클라이언트가 즉시 알아야 한다 (인원 초과, 게임 중 등)
  • WebSocket 연결 전에 방 입장 검증이 선행되어야 한다

WebSocket 연결은 방 입장 확정 후에 맺는 것이 자연스럽다.
확정되지도 않은 방에 WebSocket을 먼저 연결하는 건 리소스 낭비다.


최종 판단 기준 정리

고민을 거듭하면서 아래 기준으로 판단하게 됐다.

REST API를 쓰는 경우
  ├─ 요청에 대한 성공/실패 응답이 클라이언트에게 필요한 경우
  ├─ 의도적 액션임을 서버가 명시적으로 알아야 하는 경우
  └─ WebSocket 연결 이전에 처리되어야 하는 경우

WebSocket을 쓰는 경우
  ├─ 여러 클라이언트에게 동시에 이벤트를 전달해야 하는 경우
  ├─ 서버가 클라이언트에게 먼저 메시지를 보내야 하는 경우
  └─ 게임 내 실시간 이벤트 흐름과 일관성이 필요한 경우
기능방식이유
방 목록 조회REST단순 조회, 실시간 불필요
방 입장REST → WS 브로드캐스트입장 가능 여부 검증 필요
자발적 나가기REST → WS 브로드캐스트자발적/비정상 종료 구분
강퇴WebSocket즉시성 + 동시 브로드캐스트
준비 상태 변경WebSocket실시간 상태, 전체 공유
게임 시작WebSocket전체 동시 전달 필요
채팅WebSocket실시간 양방향
게임 내 입력WebSocket실시간 처리

마치며

"WebSocket이니까 전부 WebSocket으로"라는 접근이 아니라,
각 이벤트의 의도, 방향성, 보장 수준을 기준으로 판단하는 게 맞다는 걸 이번 설계를 통해 체감했다.

REST와 WebSocket은 경쟁 관계가 아니라 역할이 다른 도구다.
둘을 적절히 조합하는 것이 실시간 서비스 설계의 핵심이라고 생각한다.


이 글은 Let's Git It 프로젝트 개발 과정에서 팀 내 기술 토론을 바탕으로 정리한 내용입니다.

profile
느리게 한걸음

0개의 댓글