실시간 채팅 서버 개발 - 03 (방 접속하기)
·
NestJS/개발
이전 프로젝트 정리이전 포스트에서는 채팅 서버에서 방을 어떻게 생성하고, Redis를 이용해 방의 상태와 생명주기를 관리하는 구조를 정리했다. 방은 단순히 생성되고 사라지는 데이터가 아니라, WAIT → PLAYING → END 로 이어지는 명확한 상태를 가지며, 각 상태에 따라 TTL을 다르게 적용해 안전하게 관리해야 한다고 생각을 가지며 마무리 하였다. 이를 통해 유령방은 자동으로 정리하면서도, 게임이 진행 중인 방이 TTL 만료로 삭제되는 문제는 방지할 수 있었다. 이제 다음으로 해결해야 할 문제는 "방에 어떻게 입장할 것인가"이다. 방 접속은 단순히 인원 수를 증가시키는 작업처럼 보이지만, 실제로는 다음과 같은 요구사항을 동시에 만족해야 한다.최대 인원 수 초과 방지동시 접속 상황에서도 정확한 인..
실시간 채팅 서버 개발 - 02 (방 생성하기)
·
NestJS/개발
이전 프로젝트 정리앞선 포스트에서는 NestJS 기반 채팅 서버에서 Redis를 도입하고, 기본적인 Redis 연결과 서비스 구조를 구성하는 과정까지 정리했다. 하지만 Redis를 붙였다고 해서 곧바로 소켓 채팅이 완성되는 것은 아니다. 실제 채팅 시스템에서는 소켓 연결 이전에 반드시 HTTP 레벨에서 관리해야 할 상태들이 존재한다. 대표적으로 방 생성, 방 입장 가능 여부 판단, 현재 인원 수 관리와 같은 로직이다. 이러한 작업을 소켓 이벤트 내부에서 처리하게 되면, 연결 시점마다 불필요한 복잡도가 증가하고 예외 처리 또한 까다로워진다. 그래서 이번 포스트에서는 Redis를 활용해 채팅방의 상태를 HTTP API로 먼저 관리하고, 검증이 끝난 이후에만 WebSocket join 이 이루어지도록 전체 흐..
실시간 채팅 서버 개발 - 01 (Redis 및 NestJS 셋팅)
·
NestJS/개발
채팅서버가 필요한 이유이번에 라이어게임을 토이프로젝트로 만들어보면서, 생각보다 가장 먼저 필요해진 기능은 게임 로직이 아니라 채팅 서버였다. 플레이어들이 같은 방에 모이고, 서로 의견을 말하고, 실시간으로 반응을 주고받는 구조상 채팅은 필수다. 처음에는 "간단한 토이프로젝트니까 웹소켓 하나 열어서 메시지만 주고받으면 되지 않을까"라고 생각했다. 하지만 실제로 구현을 시작해보니, 익명 사용자 식별, 방 관리, 메시지 흐름 등 고민해야 할 요소들이 하나둘씩 생겨났다. 이 포스트에서는 라이어게임을 만들면서 필요에 의해 구현하게 된 NestJS 기반 채팅 서버에 대해 정리를 하려고 한다.npm install npm i @nestjs/websockets @nestjs/platform-socket.io socket..
DB조회 방식에 관하여 - 오프셋(offset)방식, 커서(cursor)방식
·
Backend Design
조회 방식에 관한 생각관계형 데이터베이스에 데이터를 저장하고, 이를 "조회" 해야 하는 상황은 대부분의 백엔드 서비스에서 필연적으로 발생한다. 데이터의 양이 많지 않다면 문제는 단순하다. SELECT * FROM table 과 같은 쿼리로 데이터를 한 번에 조회하더라도 성능이나 응답 시간에 큰 영향을 주지 않는다. 데이터가 조금 더 늘어나면 보통 페이지네이션(pagination) 을 도입한다. 데이터를 일정 단위로 나누어 가져오는 방식은 초기 단계의 서비스에서는 충분히 합리적인 선택이다. 하지만 서비스가 성장하면서 데이터의 규모가 수만, 수십만, 수백만 건으로 증가하면 기존의 방식은 점점 한계를 드러낸다. 조회 속도는 느려지고, 불필요한 리소스 소모가 발생하며, 결과적으로 사용자 경험에도 영향을 미치게 ..
Redis/BullMQ 이용하여 연산 작업 따로하기
·
NestJS/개발
연산을 따로 하려고 하는 이유이번 작업을 하고 있는 토이 프로젝트의 전체적인 동작은 다음과 같다.클라이언트가 이미지를 업로드하면NestJS 백엔드 서버에서 이미지 원본과 WebP 변환 파일을 저장하고변환된 이미지의 경로를 PostgreSQL 데이터베이스에 기록한다.현재 구조에서는 파일을 하나만 올리거나 사용자가 많지 않을 경우에는 큰 문제가 발생하지 않는다. 그러나 동시에 여러 사용자가 이미지를 업로드하고 변환 작업이 이루어지면, 빨간색 네모 박스 부분을 잘 생각해야 하는데, CPU 연산이 많은 WebP 변환 과정에서 서버에 부하가 걸릴 가능성이 있다. 이 경우, NestJS 백엔드가 모든 변환 작업을 동기적으로 처리하게 되면 다음과 같은 문제가 발생할 수 있다.응답 지연: 변환이 완료될 때까지 API ..
JWT - Passport 사용하기
·
NestJS/개발
PassportPassport는 Node.js 생태계에서 가장 널리 사용되는 인증(Authentication) 라이브러리 중 하나로, 이미 수많은 실무 애플리케이션에서 검증된 안정성과 확장성을 가지고 있다. NestJS에서는 @nestjs/passport 모듈을 통해 Passport를 프레임워크에 자연스럽게 통합할 수 있으며, 이를 통해 인증 로직을 Nest의 Guard, Strategy, Provider 구조에 맞게 표준화할 수 있다. Passport의 가장 큰 장점은 다양한 인증 방식을 전략(Strategy)이라는 개념으로 분리해 제공한다는 점이다. JWT, Local, OAuth 등 여러 인증 메커니즘을 동일한 패턴으로 처리할 수 있으며, @nestjs/passport는 이러한 Passport의 동..
JWT - 역할 기반 관리하기
·
NestJS/개발
역할 기반 관리란?이전까지는 JWT 토큰만 있으면 누구나 특정 API에 접근할 수 있었다. 하지만 실제 서비스에서 가장 중요한 부분은 사용자의 역할(Role)에 따라 접근 가능한 영역이 다르다는 점이다. 예를 들어 /member/post는 일반 사용자와 관리자가 글을 작성할 수 있는 영역이지만, /admin/post는 관리자만 접근할 수 있는 영역이다. 만약 일반 사용자가 관리자 영역에 접근할 수 있다면 보안상 큰 문제가 발생한다. 이러한 역할 기반 접근 제어를 위해서는 Guard에서 JWT 인증뿐만 아니라 역할 체크까지 함께 수행해야 한다. 즉, 토큰의 유효성을 확인하고, payload에 포함된 역할 정보를 기반으로 요청자가 해당 API를 수행할 권한이 있는지 판단하는 것이 중요하다. 오늘 이부분에 관..
JWT - 토큰 활용하기
·
NestJS/개발
JWT토큰을 활용하는 방법이전 포스팅에서는 사용자가 로그인에 성공했을 때 JWT 토큰을 발급하는 과정에 대해 다뤘다. 하지만 실제 서비스에서는 단순 발급만으로는 충분하지 않다. 클라이언트가 발급받은 토큰을 서버 요청과 함께 전달하면, 서버는 이를 기반으로 사용자가 해당 API를 호출할 권한이 있는지 판단할 수 있다. 이번 포스트에서는 JWT 토큰을 활용한 인증(Authentication)과 인가(Authorization) 과정을 NestJS를 중심으로 살펴보고, 실제 API 요청에서 어떻게 안전하게 권한을 확인할 수 있는지 테스트 해보자. 이전 과정은 3번까지 였다면 이번에 하는 과정은 4번부터 6번까지이다. 헷갈리지 말자.인증 가드 구현하기 부분 공식사이트를 잘 확인해보자. 모든 개발 과정은 공식사이트..
JWT - 로그인 후 발급하기
·
NestJS/개발
JWT(Json Web Token)이란??JWT는 인증(Authentication) 과 인가(Authorization) 를 위해 사용되는 토큰 기반 인증 방식이다. 서버가 로그인에 성공한 사용자에게 토큰을 발급하고, 이후 요청마다 이 토큰을 함께 보내서 “누구인지”를 증명하는 구조이다.서버가 로그인 정보를 메모리 / DB / Redis에 저장클라이언트는 Session ID만 전달서버가 상태(State)를 관리서버 확장 시 세션 동기화 필요로드밸런서 환경에서 복잡모바일 앱, 외부 API 연동에 불리서버는 로그인 시 토큰만 발급이후 요청은 토큰으로만 인증서버는 상태를 저장하지 않음 (Stateless)토큰 탈취 시 위험토큰 강제 만료가 어려움Payload에 정보가 노출됨발급 같은 경우에는 아주 간단하다. 로..
NestJS + Prisma 연결하기 - (공식 문서를 잘 확인하자...)
·
NestJS/개발
공식문서 잘 확인하기 ...개발을 하다 보면 공식문서를 그대로 따라 썼는데도 예상치 못한 문제가 발생할 때가 있다. 나도 NestJS 공식문서의 예제 코드를 아무 생각 없이 가져다 쓰다가 나의 소중한 시간을 날렸다. 문제는 데이터베이스 연결 방식에서 발생했는데, 로그에는 단순히 "DB 접속 불가"라는 메시지만 떴을 뿐, 원인을 바로 알 수 없었다. 도커 컨테이너에 PostgreSQL이 제대로 설치되어 있는지도 확인하고, 모든 환경 설정을 점검했지만 여전히 문제는 해결되지 않았다. 그러던 중 Prisma 공식문서를 살펴보면서, 내가 사용한 파라미터 이름이 잘못되었다는 사실을 알게 되었다. 공식문서에는 연결 방식을 설명하면서 이유가 명확히 나와 있었지만, 나는 그 부분을 깊게 이해하지 않고 넘어간 것이었다...
nginx는 어디에 설치해야 하는건가??
·
Docker
nginx 설치 경로에 대한 의문점Nginx를 설정하려고 인터넷을 찾아보다 보면 묘하게 헷갈리는 지점이 하나 있다. 어떤 글에서는 Nginx를 리눅스 호스트에 직접 설치해서 Docker 컨테이너들 앞단에 두고 있고, 또 어떤 글에서는 Nginx 자체를 Docker 컨테이너로 실행하고 있다. 둘 다 꽤 흔하게 보이는 방식이라, 처음 접하면 "도대체 어느 쪽이 정답이지?"라는 생각이 들 수밖에 없다. 더 헷갈리는 건 두 방식 모두 실제로 잘 동작한다는 점이다. 호스트에 설치한 Nginx도 안정적이고, Docker로 올린 Nginx 역시 문제없이 리버스 프록시 역할을 해낸다. 그러다 보니 단순히 따라 하기만 하면 구조는 만들어지지만, 왜 이런 선택이 나뉘는지에 대해서는 명확하게 이해하지 못한 채 넘어가게 된다..
UUID PK, 진짜 느릴까? BIGINT와 성능 차이 정리
·
PostgreSQL
PK의 타입에 따른 비용에 대한 생각예전에 한 프로젝트에서 PK를 UUID 문자열로 사용한 적이 있었다. 설계 리뷰를 하던 중 한 팀원이 이런 말을 했던 게 아직도 기억난다. "PK를 문자열로 두면, 정수랑 비교할 때 코스트가 더 크지 않나요?" 그때 든 생각이 어차피 "인덱스 타니까 괜찮지 않을까?" "요즘 서비스들 다 UUID 쓰던데?" 라는 생각으로 넘어갔다. 그리고 작은 프로젝트라서 큰 문제가 있지는 않았지만 프로젝트를 진행하면서 알게 됐다. 이 질문은 단순히 문자열 vs 정수 비교 비용에 대한 이야기가 아니라, DB 인덱스 구조, 데이터 저장 방식, 그리고 성능 전반에 대한 문제라는 것을. PK는 단순한 식별자가 아니다. DB 입장에서 PK는 가장 중요한 인덱스이며, 어떤 타입을 선택하느냐에 따..