웹 애플리케이션에서 특정 상황에 따라 실시간으로 데이터를 클라이언트에 전달하고 싶을 때, **웹소켓(WebSocket)**을 활용하는 방법이 있습니다. 실시간 데이터 전송을 처리할 수 있는 다양한 방법들이 있지만, 저는 양방향 통신을 가능하게 해주는 웹소켓을 사용하기로 결정했습니다.
■ HTTP와 WebSocket의 차이점
HTTP의 한계:
HTTP는 클라이언트 → 서버 요청-응답 모델로 동작합니다. 클라이언트가 서버에 요청을 보내면 서버가 응답을 보내는 방식입니다. 이 구조는 단방향 통신이므로 서버는 클라이언트가 요청하지 않으면 데이터를 보낼 수 없습니다.
즉, 서버가 클라이언트에 데이터를 푸시(push)하는 방식은 지원되지 않습니다.
WebSocket: HTTP의 한계를 넘다
WebSocket은 양방향 통신을 가능하게 해주며, 한 번 연결되면 클라이언트와 서버가 실시간으로 데이터를 주고받을 수 있는 연결을 유지합니다. 이는 HTTP에서 발생하는 실시간 통신의 한계를 해결하기 위한 기술로, 서버에서 클라이언트로 실시간 데이터를 푸시할 수 있게 만들어졌습니다.
HTTP는 요청-응답 방식으로만 동작하는 반면, WebSocket은 양방향 실시간 통신을 지원하여, 서버가 클라이언트의 요청 없이도 데이터를 전송할 수 있습니다. 이 특성 덕분에 채팅 시스템, 실시간 알림, 게임 등과 같은 실시간 애플리케이션에서 WebSocket이 매우 유용하게 사용됩니다.
■ WebSocket의 기본 흐름
일반적으로 WebSocket 연결은 다음과 같은 흐름으로 이루어집니다:
- 클라이언트 → HTTP 요청: 클라이언트는 WebSocket 연결을 시작하기 위해 HTTP 요청을 보냅니다 (GET /ws).
- 서버 → WebSocket 연결: 서버는 HTTP 요청을 WebSocket 프로토콜로 업그레이드하여 연결을 수립합니다.
- WebSocket → 양방향 통신: WebSocket 연결이 수립되면 클라이언트와 서버는 실시간으로 데이터를 주고받을 수 있습니다.
- 서버 → WebSocket 메시지 → 클라이언트: 서버는 특정 클라이언트에게 데이터를 실시간으로 전송할 수 있습니다.
■ HTTP-Only 쿠키와 WebSocket에서 JWT 토큰 사용하기
저는 WebSocket 연결 시 클라이언트의 인증 정보를 WebSocket 세션으로 전달해야 했습니다(웹소켓에서는 http 정보를 담지 않는게 기본). 이때, **HTTP-Only 쿠키에 저장된 JWT(Json Web Token)**을 사용하여 클라이언트를 인증하는 방식으로 처리했습니다. 그런데 WebSocket 연결 시 HTTP 요청에서 받은 JWT 토큰을 WebSocket 세션으로 어떻게 전달할까요?
**HandshakeInterceptor**를 implements하여, WebSocket 연결 시 HTTP 요청에서 데이터를 추출하고 이를 WebSocket 세션에 전달하는 **CustomHandshakeInterceptor**를 만들면 됩니다. 이 과정을 통해, WebSocket 메시징에서 인증 정보를 활용할 수 있게 됩니다.
## CustomHandshakeInterceptor 코드
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;
import java.util.Map;
// HTTP 쿠키를 웹소켓 세션에 저장하는 과정
public class CustomHandshakeInterceptor implements HandshakeInterceptor {
@Override
public boolean beforeHandshake(
ServerHttpRequest request,
ServerHttpResponse response,
WebSocketHandler wsHandler,
Map<String, Object> attributes
) {
if (request instanceof ServletServerHttpRequest) {
HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();
HttpSession session = servletRequest.getSession();
// 쿠키 또는 기타 데이터를 WebSocket 세션으로 전달
attributes.put("sessionId", session.getId());
attributes.put("jwt-token", getJwtFromCookies(servletRequest));
}
return true;
}
@Override
public void afterHandshake(
ServerHttpRequest request,
ServerHttpResponse response,
WebSocketHandler wsHandler,
Exception exception
) {
// 핸드셰이크 후 처리
}
private String getJwtFromCookies(HttpServletRequest request) {
if (request.getCookies() != null) {
for (Cookie cookie : request.getCookies()) {
if ("jwt-token".equals(cookie.getName())) {
return cookie.getValue();
}
}
}
return null;
}
}
■ 결론: 왜 WebSocket인가?
HTTP의 단방향 통신 모델은 실시간 데이터 전송에 한계가 있었습니다. 이 한계를 극복하기 위해 WebSocket이 등장하여, 양방향 실시간 통신을 가능하게 했습니다. WebSocket을 사용하면 서버에서 클라이언트로 실시간 데이터를 푸시할 수 있으며, 이를 통해 채팅, 알림 시스템, 실시간 게임 등 다양한 애플리케이션에서 유용하게 활용할 수 있습니다.
**CustomHandshakeInterceptor**를 사용하면 WebSocket 연결 시 인증 정보를 세션에 전달할 수 있어, 실시간 메시징을 처리하는 데 중요한 역할을 합니다.
'WEB' 카테고리의 다른 글
| 엣지 컴퓨팅? (1) | 2025.12.31 |
|---|---|
| API 호출 방식(JavaScript vs Next.js) (7) | 2025.08.13 |
| [동기/비동기] 프론트엔드(React)와 백엔드(Java)에서 언제 사용해야 할까? (1) | 2024.10.02 |
| [web] Safari에서 Universal Link와 서버 리다이렉션의 작동 원리 분석 (0) | 2024.08.12 |
| [web] 딥링크(Universal Link) (0) | 2024.07.29 |