webRTC - Socket
현재 최종 프로젝트중에 webRTC를 활용한 서비스를 구현중에 있다. 예상치 못한 문제에 봉착해 꽤나 애 먹은 문제를 최종적으로 어떻게 해결을 했는지까지의 여정을 한번 적어보려 한다.
배포 전 & 후
배포하기 전, 로컬 상황에서는 총 3개의 서버를 구동시켜야 했다. ( 원래 총 4개인데 AI 관련 서버는 제외 )
메인 서버, 채팅 서버, 프론트 서버
이렇게 3가지 였는데, 그 이유로는 채팅 서비스에 접근하기 위해서는 로그인된, 즉 검증된 유저만 접근이 가능하게끔 로직을 구현했고, 그래서 메인서버에서 토큰을 쿠키 값으로 받아온 이후에 접근을 해야만 했다. 프론트의 포트는 3200, 메인서버는 3000, 채팅서버는 8080이고 게이트웨이가 8000이었다. ( 일부러 구분점을 잡기위해 포트 번호를 달리해주었다. )
포트가 다 다르면, 로컬 상황이더라도 도메인이 다 다르게끔 인식한다로 알고있었기에 당연히 배포 이후에도 CORS 관련 이슈에 대한 부분은 걱정하지 않았고, 쿠키 값에 대한 부분도 염려하지 않았었다. 그러나 웬걸, 채팅서버만 먼저 배포를 진행해서 여러 인원과 테스트를 진행해보려 했는데 헤더에 credential에 대한 부분을 클라이언트와 서버 둘다 처리를 해주었음에도 쿠키값을 받아오지 못하는 상황이 발생했다.그래서 실제론 로그인이 되어있음에도 불구하고, 로그인을 해달라는 오류를 자꾸 던지게 되었고, 그래서 그 부분을 딥하게 파고든 결과 여러 정황을 발견할 수 있었다.
transport 방식을 집중적으로 !
먼저 socket을 클라이언트 단에서 옵션을 줄때 transport 의 설정값이 존재한다. 말 그대로 데이터 전송을 어떻게 할것이냐 에 대한 부분인데 여기서 크게 두가지의 옵션이 있다 보면 된다.
Polling
HTTP 요청을 보내서 받아오는 응답의 간격을 최소화해 마치 실시간 소통인것 처럼 보이게끔 하는 기법으로서 socket io 에서는 그중에 Long Polling 방법을 사용한다.
모든 요청을 Polling 방식으로 하게 될 경우, 서버의 부담이 커질수밖에 없다. 매번 헤더를 읽어내서 데이터를 처리하고 응답을 해주면 당연히 서버 입장에선 헥헥 거릴수밖에 없는것. 그래서 Long Polling 방식을 사용하는데 HTTP Request를 보내면 그에대한 응답을 바로 보내주는것이 아니고, 서버측에서 Pending 상태로 대기를 타다가 이벤트가 발생하면 Response를 날리는 형태이다. 이 방식을 취함으로써 보다 적은 서버에 부담을 주게 되는것인데 이것만으로는 완벽한 형태 라고 볼수 없기에 Websocket 방식이 나오게 된다.
Websocket
Bi - Directional , 즉 양방향으로 소통을 하게된단 의미인데 클라이언트나 서버나 상대방의 허가 여부와 상관없이 데이터전송을 할수있는 커넥션이 이루어진 상태를 얘기한다.
밑의 코드를 보자.
const socket = io("http://localhost:8000/chat", {
transports: ["polling", "websocket"],
withCredentials: true,
extraHeaders: {
AccessToken: accessToken,
RefreshToken: refreshToken,
},
});
transports 안에 보게되면 0번 인덱스에 polling, 1번 인덱스에 websocket 이 들어가있는것을 확인할 수 있는데, 원래는 이 두개의 인덱스가 바뀐 위치에 존재했었다.
배열 내의 0번 인덱스에 어떤 방식이 존재하느냐에 따라 먼저 시도하는 방식이 달라지게 되는데, 이부분을 캐치한것은 socket.io 공식 문서에서 캐치할수있었다.
websocket 방식으로 요청을 먼저 보냈을 경우, 분명 HTTP 요청과 응답은 정상적으로 받아지는걸 확인했으나 왜 쿠키 값이 안담겨져서 전송이 이루어질까? 에 대한 부분이 요점이었다.
클라이언트단에서 withCredentials 의 값을 true 로 줄 경우, 서버단 에서의 cors 옵션에서 origin 부분에 와일드카드 '*' 를 사용하면 안되기에, 직접적으로 허용해줄 경로, 프론트 엔드의 서버 주소를 입력해주었다. 해서 정상적으로 받아와 져야 했음에도 불구하고 쿠키 값은 여전히 안담기는 상황
이때 확인한게 위 공식문서 내용이었고, 먼저 polling 방식으로 요청을 보내서 클라이언트와 서버 간의 handshake가 일어난 후, 연결에 문제가 없을 경우 websocket으로 업그레이드를 해준다 라는것을 확인할 수 있었다.
아래의 사진을 보고 어떤 흐름으로 websocket 으로 Upgrade 하는 메커니즘을 지니는지 알수있었다.
실제로 polling 방식을 인덱스 0번으로 옮겨주자 공식문서와 같이 요청이 5개로 늘어나서 들어가는것을 확인할 수 있었고, polling 방식으로 들어간 요청과 101번 프로토콜이 바뀌는 요청을 기점으로 마지막 polling 의 응답을 받는것까지 완료되는걸 확인했다.
헤더 내부를 확인해보니 upgrade : websocket 이란 요소를 확인할 수 있었고, 그것이 키포인트 라는것을 알게되었다.
배열 내 0번 인덱스를 polling 방식으로 해주자 서버단에서도 정상적으로 쿠키의 값을 헤더에 명시해줄 수 있게 되었고, 다만 위 코드에서 확인한 것과 같이 extraHeaders 라는 옵션으로 추가 헤더를 명시해주었다.
여기서 정말 중요한 점은 !! 공식문서에서 연계해준 Github 내용을 보던중, websocket을 0번 인덱스로 먼저 명시했을경우엔 extraHeaders가 작동하지 않을것이라고 확인할수 있었다. polling 을 먼저 취해줘야 정상적으로 추가 헤더를 명시해줄수있단 점을 확인했고, 결과 또한 그대로 해결을 볼 수 있었다.
(확인 가능한 git hub 주소) https://github.com/socketio/engine.io-client
배포한 서버에서 정상적으로 쿠키 값을 받아오고, 그렇게 채팅서비스를 완벽히 구현할 수 있게되었다. 공식문서가 짱이구나 를 새삼스럽게 느낄수 있는 계기였던것 같다.
정리
1. webRTC 에서의 방식
polling 과 websocket의 차이. websocket 연결이 이루어지기 전 클라이언트와 서버간의 handshake를 통해 websocket으로 연결하기에 안정성이 좋은지를 확인 후 업그레이드 메커니즘을 거친다.
2. websocket 으로만 연결
websocket 방식으로만 연결 한다 해서 문제가 되는것은 아니다. 그렇게 해도 채팅서비스 자체의 구현은 정상적으로 작동을 할것이다. 다만 추가적으로 인증에 대한 로직을 진행코자 한다면 수많은 블로그 글에선 URI에 token 값을 명시해주는 방법을 제시해주는데 아무리 생각해도 보안적으로 생각해봤을때 그것은 옳다 여겨지지 않았다. 중요한 값이 담긴것은 아니지만 어쨋든 내 개인정보가 암호화 되어있는 토큰값을 대놓고 명시한다니, 내 정보 빼가시오~ 하는것과 다름없지 않나.
공식문서의 방식대로 순차적으로 과정을 밟을경우 헤더에 명시할 수 있는 방법을 찾을 수 있었고, polling 방식으로 헤더에 추가적으로 명시해주면 해결을 할 수 있다.
'회고 > TIL' 카테고리의 다른 글
Database - 정규화 (0) | 2023.11.23 |
---|---|
홈서버 구축기 -1 (2) | 2023.09.18 |
DAU & WAU & MAU (0) | 2023.08.29 |
Postgres - Datatype (0) | 2023.08.21 |
Redis - cache (0) | 2023.08.19 |