안녕하세요, 오늘은 소켓에 대해 다뤄보도록 하겠습니다. 그 전에 필요한 대략적인 내용들도 다뤄보고자 합니다.

 

인터넷 계층 ( Internet Layer, Protocol Layer )

인터넷은 기본적으로 계층 구조로 이루어진 프로토콜로 구성되어 있으며, 윗 계층이 아래 계층의 서비스를 이용한다.

 

OSI  7 Layer ? TCP/IP 5 Layer ?

학술적으로 주로 인용되는 layer는 OSI 7 Layer가 맞습니다. (네트워크 프로토콜을 이해하고 설명하는 데 유용)

하지만, 실제로 네트워크 환경에 구현된 레이어는 TCP/IP 5 Layer 입니다. 

 

제가 설명할 HTTP는 어디에 구현된 프로토콜일까요?

정답은 Application Layer 입니다. (즉, 어플리케이션 레이어 프로토콜입니다.)

 

참고로, HTTP는 TCP를 사용합니다.

Transport Layer에 해당하는 프로토콜로 크게 TCP와 UDP로 나눌 수 있는데 신뢰성을 지닌 TCP를 사용합니다. 

(UDP는, 빠른 데이터 전송과 실시간이 중요한 서비스에서 주로 사용하곤 합니다)

 

HTTP 1.1  vs HTTP 2.0

1.0과 3.0은 다루지 않겠습니다. 1.0은 non-persistent 라서 하나의 요청 => 하나의 연결을 필요로 하기 때문에 RTT로 인한 response time이 커서 지금은 거의 사용하지 않습니다. 3.0은 특이하게 TCP가 아닌 UDP를 사용하는 방식이라 설명을 생략합니다.

 

우선 HTTP는 기본적으로 신뢰성을 바탕으로 하며, 상태가 존재하지 않습니다. 

HTTP 1.1 은 request에 대해 순서대로 응답을 보장합니다. (FCFS)

하지만 이로 인해 Hol Blocking 문제가 발생하여, HTTP 2.0이 나왔습니다.

HTTP 2.0 은 object를 frame 단위로 분할아여 순환 전송하기 때문에 늦게 도착한 object가 먼저 처리될 수도 있습니다. 

 

그럼 현재는 HTTP 2.0만 사용할까요? 그건 아닙니다. 아래의 사진을 보세요!

 

서버의 설정에 따라, 어떤 기능을 사용할지 정하고 HTTP 프로토콜의 버전이 세팅된다고 보시면 됩니다. 제가 진행한 프로젝트에서도, 어디에선 1.1을 사용하고 어디에선 h2(2.0)을 사용하는 걸 볼 수 있었습니다.

 

 

HTTPS (TLS + HTTP)

HTTP를 TLS 위에서 돌리는 방식으로, 데이터를 암호화 전송하기 위한 방식입니다. 

TLS 는 Transport Layer Security 의 약자로, application layer에 구현되어 있습니다.
(정확히는 , Transport layer와 application layer 사이)

 

 

 

Socket

소켓은 다방면에서 자주 쓰이는 개념입니다. C++로 게임을 개발할 때, FTP를 구현할 때 등등 다양한 개발 분야에서도 사용됩니다.

Socket을 아주 간단히 정의하면, API의 일종으로 Applicaition Layer와 Transport Layer에 존재하는 일종의 문과 같은 개념입니다.

문을 통과하고 나면 어떤 과정이 일어나는지는 잘 몰라요, 다만 문을 열어서 데이터를 보내거나 받는데 사용하곤 합니다.

 

따라서 소통을 하는 양쪽이 소켓을 하나씩 지녀야하고(만들어두고 있어야하고), 소켓을 통해 데이터를 송수신하며 소통합니다.

(그래서 소켓을 이용한 통신 과정을 프로그래밍하는 것을 소켓 프로그래밍이라고 합니다.)

 

저희는 웹 소켓에 대해 더욱 자세히 다뤄보도록 하겠습니다.

 

Web Socket 이란?

HTTP는 단방향 통신입니다. 클라이언트가 서버에 요청을 보내면, 그제서야 서버가 응답을 주는 형식이죠.

하지만 Web Socket은 양방향 통신입니다. 이런 Web Socket 을 사용하면 실시간 웹 게임, 채팅 기능을 만들 수 잇습니다.

 

과정은 아주 단순합니다. HTTP로 통신을 하다가, 이제 앞으로는 Web Socket을 통해 통신하겠다고 하며 서로 업그레이드를 약속하고(핸드 쉐이크) 연결이 수립되면 서로 연결된 통신망으로 자유롭게 양방향으로 데이터를 주고 받으면 됩니다. 그 후, 더 이상 채널을 유지할 필요가 없을 때 채널을 어느 한쪽에서든 닫으면 됩니다. (네트워크 오류로 인해 명시적으로 close를 안해준 경우도 걱정할 필요 없습니다. 서버에서 클라이언트의 요청이 없을 경우 주기적으로 확인 메세지를 보내보고, 만약 그에 대한 응답이 없을 경우 비정상 close를 인식하고 서버 측에서도 채널을 close 합니다.)

 

 

Web Socket을 사용해보자! 

사실 웹소켓은 클라이언트 측에서는 크게 할 일이 많지는 않습니다. 대부분의 세팅과 설정은 서버에서 진행이 됩니다.

(데이터 베이스 연결, 소켓 연결, 보내주는 데이터 설정 등등.. )

 

저는 프론트엔드 측면에서 소켓 프로그래밍을 활용한 예시를 다뤄볼게요.

실제로 제가 진행한 프로젝트인데, 다양한 참여자들이 방에 접속을 해서 퀴즈를 맞춰야하는 실시간 서비스였고 방장이 "다음으로" 버튼을 클릭하면 모든 참여자가 다음 문제를 볼 수 있도록 만들어줘야 했습니다. 양방향 통신이 필요하기에, 큰 주저 없이 소켓을 도입하기로 했습니다.

 

소켓 프로그래밍은 크게 2가지 행위로 나뉩니다.

on (듣기) : 상대방으로부터 메세지를 받기- 받은 후 취할 행동 정의

emit (말하기) : 상대방에게 메세지 보내기

 

프론트엔드 측면에서 소켓 프로그래밍의 자세한 과정은 다음과 같습니다.

0. 우선 프론트엔드 개발자와 백엔드 개발자는 주고 받은 메세지의 형식을 결정합니다. (어떤 메세지명으로 주고 받을건지)

//방에 대한 정보(참가자 리스트)가 담겨 있음
socketRef.current.on("roomUpdate", (updatedRoom) => {
            setRoom(updatedRoom);
 });
socketRef.current.emit("joinRoom", { roomId, accessToken, isHost });

 

1. 프론트엔드에서, 백엔드 개발자가 알려준 <ip주소:포트넘버> 로 소켓 연결을 진행합니다. 

import { io, Socket } from "socket.io-client";

const socketRef = useRef<Socket | null>(null);

useEffect(() => {
  socketRef.current = io("https://talkspark-dev-api.p-e.kr/socket.io/", {
    transports: ["websocket"],
},[]);

 

저는 socket.io-client 라는 라이브러리를 사용해서 간단하게 연결을 진행했는데요,

원하신다면 내장되어 있는 WebSocket 클래스를 사용해서 연결을 진행해보셔도 좋습니다. 

 

크게 설명하지 않아도 이해가 되시죠? io의 인자에 연결할 주소를 적어넣어 소켓 연결을 생성하면 됩니다.

transport는 클라이언트와 서버 간의 통신 방식을 정의하는 옵션인데, polling 등의 방식도 존재하나 websocket만 사용할 것이므로 websocket만 적어두었습니다. 

 

마지막으로 당부드리고 싶은 말씀은, 소켓을 저장하는 변수는 useRef를 통해 관리하셔야 한다는 점입니다.

사실 필수적인 부분은 아니지만 소켓 연결이 리렌더링 때마다 새롭게 설정되는 것은 리액트의 동작 원리를 제대로 이해하지 못한 코드라고 생각합니다. useRef 자체가 하나의 참조를 유지함으로써 해당 값을 매번 선언-할당하는 것을 방지하고 값(혹은 연결된 요소)를 유지하기 위함이니까요. 이럴 때 사용해야하는게 아닌가 싶습니다. (물론 useEffect의 마운트시에만 혹은 전역적으로 socket을 ref 없이 선언해도 동작은 잘할겁니다! 다만 목적성의 드러남이 약간은 부실한 코드가 아닐까요..?)

 

프론트엔드 개발자가 해야하는 건 이정도입니다! 이제 필요할 때 서버에 emit 메서드로 데이터를 잘 전송하고,

on 메서드(마치 setInterval 과 같은, 저장되어 있는 리스너 같은 개념으로 이해해도 됩니다) 로 서버로부터 도착한 데이터를 받아들여 적절한 동작을 취하는 코드를 작성하면 됩니다. 데이터를 잘 관리하는 것은 기본적인 api 통신하고 크게 다를 바가 없어보이네요!

 

 

Web Socket 의 단점, 적절한 대안책 고민(SSE)

하지만 이런 웹 소켓 방식에도 단점은 있습니다.

지속적으로 Polling 하는 방식에 비해서는 부담이 덜하긴 하지만, 연결을 유지해야하기 때문에 부담되는 것은 마찬가지입니다.

그래서 꼭 웹소켓이 필요한지 고민해보는 시간이 필요합니다.

 

만약 서버에서 프론트로 단방향 통신이 필요한 경우라면 굳이 양방향 통신이 가능한 웹 소켓을 사용할 필요가 없습니다.

오히려 서버에서 프론트의 요청 없이도 데이터를 전송할 수 있는 SSE (Sever Sent Events) 를 사용하는게 더 적절하죠.

 

 

COMMENT