-
Notifications
You must be signed in to change notification settings - Fork 3
๐ช nest Websocket์ ์ธ์ ์ด ์๋๋ค๊ณ ?
๋ถ์ผ | ์์ฑ์ | ์์ฑ์ผ |
---|---|---|
BE | ๊น๋ฏผ์ | 24๋ 11์ 13์ผ |
nestjs์์ websocket์์ ์ฟ ํค๋ฅผ ์ ์กํ์ ๋ ์ธ์ ๊ฐ์ด controller์ ์ ๋ฌ๋์ง ์๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
nestjs์์ websocket server๋ http server์ ๋ณ๋๋ก ๋ถ๋ฆฌ๊ฐ ๋๊ธฐ ๋๋ฌธ์ http์ ์ค์ ํ ๋ฏธ๋ค์จ์ด๋ค์ด ๊ณต์ ํ์ง ์๋๋ค. (์ฌ์ง์ด ๋ฏธ๋ค์จ์ด์ ์ ๋ฌ๋๋ ๋งค๊ฐ๋ณ์๋ ๋ค๋ฅด๋ค.)
ํด๋น ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์ ์ฉ ๊ฐ๋๋ฅผ ๋ง๋ค์๋ค.
๋จผ์ HTTP์ Websocket ์๋ฒ์ ์ธ์ ์ ๊ณต์ ํ๊ธฐ ์ํด MemoryStore๋ฅผ Provider๋ก ์์ฑํ๋ค.
export const MEMORY_STORE = Symbol('memoryStore');
@Module({
providers: [
{
provide: MEMORY_STORE,
useFactory: () => {
return new MemoryStore();
},
},
],
exports: [MEMORY_STORE],
})
export class SessionModule {}
์ดํ express-session์์ ํด๋น ์คํ ๋ฆฌ์ง๋ฅผ ์ฌ์ฉํ๋๋ก ํ๋ค.
// main.ts
const store = app.get(MEMORY_STORE);
app.use(session({ ...sessionConfig, store }));
์น ์์ผ์์ ์ฟ ํค ๊ฐ์ ์ ๋ฌ๋ฐ์ผ๋ฉด ์ฟ ํค๋ฅผ ํด์ํ์ฌ ์ธ์ ํค ๊ฐ์ ๊ฐ์ ธ์์ผ ๋๋ค. ์ด๋ ์ค์ ๋ passport์์ ๋ํ๋๋ ์ฟ ํค ๊ฐ์ ํ์์ ๋ค์๊ณผ ๊ฐ๋ค.
- ์ฟ ํค ํ์ -
[์ฟ ํค์ด๋ฆ] = s:[์ฟ ํค๊ฐ].[๋ฌด๊ฒฐ์ฑ ํ์ธ์ฝ๋]
์ด๋ ๋ฌด๊ฒฐ์ฑ ํ์ธ์ฝ๋๋ ์ธ์ ํค์ ๊ฐ์ secret์ผ๋ก ํด์ํ ํ ์ฝ๋๋ก, ๋ค์ด์จ ์ฟ ํค๊ฐ ์๋ฒ์์ ์์ฑํ ์ฟ ํค์ธ์ง ํ์ธํ๋ ์ฉ๋๋ก ์ฌ์ฉ๋๋ค. ๋ฐ๋ผ์ ์๋์ ์ฝ๋๋ฅผ ํตํด์ ์ธ์ ํค๋ฅผ ๊ฐ์ ธ์ค๋๋ก ํ๋ค.
const DEFAULT_SESSION_ID = 'connect.sid';
export const websocketCookieParse = (socket: Socket) => {
if (!socket.request.headers.cookie) {
throw new WsException('not found cookie');
}
const cookies = cookie.parse(socket.request.headers.cookie);
const sid = cookies[sessionConfig.name || DEFAULT_SESSION_ID];
return getSessionIdFromCookie(sid);
};
const getSessionIdFromCookie = (cookieValue: string) => {
if (cookieValue?.startsWith('s:')) {
const [id, signature] = cookieValue.slice(2).split('.');
const expectedSignature = crypto
.createHmac('sha256', sessionConfig.secret)
.update(id)
.digest('base64')
.replace(/=+$/, '');
if (expectedSignature === signature) {
return id;
}
throw new WsException('Invalid cookie signature');
}
throw new WsException('Invalid cookie format');
};
์ฟ ํค๋ฅผ ํตํด์ ๊ฐ์ ธ์จ ์ธ์ ํค ๊ฐ์ ํตํ ์ธ์ฆ์ ์ ์ฉ ๊ฐ๋์์ ์งํํ๋๋ก ํ๋ค.
export interface SessionSocket extends Socket {
session?: User;
}
export interface PassportSession extends SessionData {
passport: { user: User };
}
@Injectable()
export class WebSocketSessionGuard implements CanActivate {
constructor(
@Inject(MEMORY_STORE) private readonly sessionStore: MemoryStore,
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const socket: SessionSocket = context.switchToHttp().getRequest();
const cookieValue = websocketCookieParse(socket);
const session = await this.getSession(cookieValue);
socket.session = session.passport.user;
return true;
}
private getSession(cookieValue: string) {
return new Promise<PassportSession>((resolve, reject) => {
this.sessionStore.get(cookieValue, (err: Error, session) => {
if (err || !session) {
reject(new WsException('forbidden chat'));
}
resolve(session as PassportSession);
});
});
}
}
- ๐ฉ FE ๊ธฐ์ ์ ํ์ด์
- โจ ์ฐจํธ์ ๋ฐ์ํ ๊ตฌํ๊ณผ useRef ํ์ ๋ฌธ์
- ๐ฃ ๋ถ๋ชจ ์์์ ์ํ์ ๋ฐ๋ผ ์์ ์์๋ ์คํ์ผ ๋ณํ ๋ถ์ฌํ๊ธฐ
- ๐ zod ๋์ ํ๊ธฐ
- ๐ useInfiniteQuery๋ฅผ ์ฌ์ฉํ ๊ทธ๋ํ ๋ฌดํ์คํฌ๋กค ๊ตฌํ
- ๐ซ ์ฌ์ฉ์์ ์์ ๋ณํ ์๋ ๊ทธ๋ํ ์คํฌ๋กค ๊ตฌํํ๊ธฐ
- ๐งช ์๋ง์ ๊ทธ๋ํ ๋ฐ์ดํฐ ์์ฒญ์ ์ด๋ป๊ฒ ์ค์ผ๊น
- ๐ ๋คํฌ๋ชจ๋์์ ์๋ก๊ณ ์นจ ์ ๋ผ์ดํธ๋ชจ๋๊ฐ ์ ๊น ๋ณด์ด๋ ๋ฌธ์
- ๐ ์น์์ผ์ ์ฑํ ๋ฐ์ดํฐ์ REST API์ ์ฑํ ๋ฐ์ดํฐ๋ฅผ ํจ๊ป ๊ด๋ฆฌํ๊ธฐ
- ๐ก BE ๊ธฐ์ ์ ํ ์ด์
- โ๏ธ Node WebSocket ํ๊ณ ๋ค๊ธฐ
- โ๏ธ TypeORM Datasource mock ๋ง๋ค๊ธฐ
- โ๏ธ oauth ID range ๋ฌธ์
- ๐ custom pipe์์ Nan์ด ๋ฐ์์ง๋ ๋ฌธ์
- ๐ช nest Websocket์ ์ธ์ ์ด ์๋๋ค๊ณ ?
- ๐ด nginx websocket ์ฐ๊ฒฐ ์ ๋ฌธ์ ๋ฐ์
- ๐ WebPush ๊ตฌํ
- ๐ง ์ฐ์ ์์ ํ๋ก ์์ฒญ ์ ์ดํ๊ธฐ
- ๐ websocket์ด ๋ฆ๊ฒ ํ ๋น๋์ด ๋ฐ์๋๋ ๋ฌธ์
- ๐ฅณ typeorm์ ์ด์ฉํ FCM ์๋ฆผ ์๋น์ค
- ๐ฆ ๋ค์ค ์ ์ ๋์์ฑ ์ ์ด โ ์ฑ๊ธํค, ๋ฎคํ ์ค
- ๐ ๊ทธ๋ํ ๋ฐ์ดํฐ๋ฅผ ์ค์๊ฐ์ผ๋ก ์ ๊ณตํ๊ธฐ์ํ ์ ๋ต
- ๐ ๏ธ ์ธํ๋ผ ๊ธฐ์ ์คํ ์ ํ ์ด์
- ๐ Ncloud ์ค์ ๊ณผ์
- ๐ ORM ๊ธฐ์ ์คํ ๋น๊ต
- ๐ค RabbitMQ๋ก ๋ถ์ฐ ์๋ฒ์๊ฒ ๋ฉ์์ง๋ฅผ ๋ถ๋ฐฐํ๊ธฐ
- ๐ข private DB ์๋ฒ์ ์ ์ํ์ง ๋ชปํ๋ ํ์
- ๐ 1์ฃผ์ฐจ ๋ฐํ
- ๐ 2์ฃผ์ฐจ ๋ฐํ
- ๐ 3์ฃผ์ฐจ ๋ฐํ
- ๐ 4์ฃผ์ฐจ ๋ฐํ
- ๐ 5์ฃผ์ฐจ ๋ฐํ
- ๐ ์ต์ข ๋ฐํ