Show layout: post title: "우리도 채팅있으면 좋을 것 같아요." description: "Netty를 이용한 채팅 서버 구축 경험기" author: allen.song date: 2020-06-19 15:50:00 +0900 categories: experience published: true
소개저는 신사업부문에서 영상기반 소셜서비스인 Thiiing(띠잉)서비스를 만들고 있어요. 배경채팅 기술을 어떤 방식으로 도입할지에 대해 치열한(!) 논의를 우선 거쳤어요.
저는 직접 만들자는 2안을 주장했고 이유는 아래와 같았어요.
저는 이전에 채팅 서비스를 운영해본 경험이
있었기 때문에 고려할 것과 이슈들에 대해서는 대략적으로 인지하고 있었어요. 기술셋을 고려하기 이전부터 이런 논의가 있을 수 밖에 없던 이유가 있었는데요. 기술셋
Stack Layer
커넥션채팅에서 가장 중요하다고 해도 과언이 아닌 것이 커넥션 관리인데요.
다행스럽게도(!) Netty는 이 부분을 아주 잘 지원해주고 있어서 네트워크 low-level에서 작업해야 할 비용이 크게 줄어들었어요.
Bootstrap웹소켓용 서버, 소켓용 서버/클라이언트를 구동하기 위한 Bootstrap을 먼저 살펴볼게요. WebServer웹 서버를 구동해요.
[1] WebServer는 Application main()에서 실행합니다. SocketServer소켓 서버를 구동해요.
[1] 위 WebServer설정과 유사하며 설정값들만 미세하게 조정했습니다. SocketClient소켓 클라이언트를 구동해요.
[1] 스케일아웃된 채팅 서버들의 IP목록을 레디스에서 가져옵니다. Initializer비지니스 로직을 담당할 핸들러와 코덱을 채널 파이프라인에 등록해주는 단계예요. WebChannelInitializer웹소켓으로 전달된 데이터는 주고 받는 과정에서 코덱으로 파싱되고 인증 및 비지니스 로직을 처리하는 핸들러를 거쳐요.
[1] 비지니스 로직을 처리할 여러 채널 핸들러를 등록하기 위해서
ChannelInitializer사용하여 파이프라인에 추가합니다. SocketChannelInitializer소켓 통신은 소켓 서버와 소켓 클라이언트간에 메시지를 포워딩하게 돼요.
[1] 소켓간 통신시 데이터 포맷은 json을 사용하고 있고, 이 raw데이터를 서비스 내에서 약속한 Payload포맷으로 변환하여 CommandHandler로 흘려줍니다. ClientChannelInitializer소켓 클라이언트도 핸들러를 등록해요.
Codec네트워크 통신으로 전달된 바이너리 데이터를 특정 포맷으로 변환하기 위해서 코덱을 정의해 줘야 해요.
[1] MapperUtil은 저희가 자주 사용하는 ObjectMapper를 더 쉽게 사용할 수 있게 감싼 유틸리티성
클래스입니다. 커맨드지금까지 Netty를 이용해 채팅 서버 구조를 만들어줬다면 이제부터는 비지니스 로직에 집중하는 부분이에요. 포맷데이터를 공통된 포맷으로 맞추면 코덱을 여러개 만들 필요없어서 간편한데요.
[1] 인증 정보를 확인하기 위한 토큰입니다. 커맨드 종류현재 커맨드는 20여개 넘게 정의돼 있는데요.
[1] isReceive필드가 true인 것들은 웹 클라이언트에서 요청온 게 아닌, 서버to서버로 메시지가 포워딩돼서 들어오는 커맨드를 구분하기 위함입니다. Validation아래 CommandHandler를 보기 전에 유효성 체크부분부터 보도록 할게요. ValidationService
[1] 데이터를 정의한 클래스의 각 필드에 대한 제약조건(javax.validation.constraints 패키지)을 체크합니다.
[3] 채팅 메시지는 종류를 매우 다양하게 가질 수 있습니다. 단순하게는 텍스트가 되겠고, 나아가면 이미지, 영상 또는 서비스 도메인에 따라 다양한 디자인 요소가 적용된 메시지로 확장될 수 있습니다.
같은 채팅 메시지 타입도 버전에 따라 다른 포맷을 가질 수 있다는 점은, 유효성 체크를 다르게 챙겨야 한다는 것과도 같습니다.
CommandHandlerNetty는 이벤트 콜백 방식이라 각각의 구현 핸들러들은 Netty에서 이미 만들어 놓은 핸들러를 상속받는 것으로 비지니스로직에 집중할 수 있도록 돼 있어요.
[1] 이 빈을 채널 파이프라인에 추가하기 위해서 @Sharable을 지정합니다. 보안띠잉 채팅 서비스는 보안에 있어서는 개발자가 불편하더라도 최대한 보수적으로 접근하자는 공감대를 가지고 있어요. 임시 토큰저희 서비스는 OAuth2를 이용한 토큰 방식의 인증을 취하고 있고요.
채팅에서도 인증처리는 필수이죠. 데이터접근 제한채팅 운영DB에 대한 접근 권한은 최소한의 인원으로 제한돼 있어요. DB스키마는 운영중에 수정이 필요하기 때문에 피치 못하게 접근하게 되더라도, 그마저도 항상 페어로 작업할 수 있도록 문화를 가져가고 있고요. 대화내용 마스킹운영DB에 대한 접근을 극도로 제한하고 있지만 불가피하게 스키마 변경을 한다든가 할 때 접속하더라도, 유저의 대화가 담긴 필드는 *로 마스킹되어 기본적으로 볼 수 없도록 하고 있어요. 로깅저희가 운영DB에 접근할 일이 거의 없는 가장 큰 이유는 로그를 충분히 잘 정리했기 때문이에요. QA나 CS로 이슈업이 되면 로그만으로도 충분히 트래킹이 가능하거든요. 문서화클라이언트 입장에서 세분화된 여러 커맨드들을 적재 적소에 호출하기 위해서는 가이드가 반드시 필요해요.
마무리Netty를 이용한 채팅 서버 구축 경험의 바탕을 공유하는 것이 핵심이다보니, 아무래도 채팅 본연의 비지니스 로직이 담긴 서비스레벨까지 설명하기에는 내용이 너무 길어지고 정신없어지는 느낌이 들어 제외했어요. 광고실제 동작이 궁금하신 분들은 띠잉앱을 설치해서 사용해보세요 🙂 띠잉 공식 SNS도 운영하고 있어요. |