-
Notifications
You must be signed in to change notification settings - Fork 0
๐ช 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);
});
});
}
}
- ๐ฑ 1์ฃผ์ฐจ ํ๊ณ
- ๐ฑ 2์ฃผ์ฐจ ํ๊ณ
- ๐ฑ 3์ฃผ์ฐจ ํ๊ณ
- ๐ฑ 4์ฃผ์ฐจ ํ๊ณ
- ๐ฑ 5์ฃผ์ฐจ ํ๊ณ
- ๐ฑ 6์ฃผ์ฐจ ํ๊ณ
- 0๏ธโฃ ๋ฉํ ๋ง ํฅ์คํ ์ผ์ง
- 1๏ธโฃ 1์ฃผ์ฐจ ๋ฉํ ๋ง ์ผ์ง
- 2๏ธโฃ 2์ฃผ์ฐจ ๋ฉํ ๋ง ์ผ์ง
- 3๏ธโฃ 3์ฃผ์ฐจ ๋ฉํ ๋ง ์ผ์ง
- 3๏ธโฃ 5์ฃผ์ฐจ ๋ฉํ ๋ง ์ผ์ง
- ๐ 1์ฃผ์ฐจ ๋ฐํ
- ๐ 2์ฃผ์ฐจ ๋ฐํ
- ๐ 3์ฃผ์ฐจ ๋ฐํ
- ๐ 4์ฃผ์ฐจ ๋ฐํ
- ๐ 5์ฃผ์ฐจ ๋ฐํ
- ๐ ์ต์ข ๋ฐํ
- ๐ Ncloud ์ค์ ๊ณผ์
- ๐ ORM ๊ธฐ์ ์คํ ๋น๊ต
- โ๏ธ Node WebSocket ํ๊ณ ๋ค๊ธฐ
- โ๏ธ TypeORM Datasource mock ๋ง๋ค๊ธฐ
- ๐ฉ FE ๊ธฐ์ ์ ํ์ด์
- โจ ์ฐจํธ์ ๋ฐ์ํ ๊ตฌํ๊ณผ useRef ํ์ ๋ฌธ์
- ๐ฃ ๋ถ๋ชจ ์์์ ์ํ์ ๋ฐ๋ผ ์์ ์์๋ ์คํ์ผ ๋ณํ ๋ถ์ฌํ๊ธฐ
- ๐ค RabbitMQ๋ก ๋ถ์ฐ ์๋ฒ์๊ฒ ๋ฉ์์ง๋ฅผ ๋ถ๋ฐฐํ๊ธฐ
- โ๏ธ oauth ID range ๋ฌธ์
- ๐ custom pipe์์ Nan์ด ๋ฐ์์ง๋ ๋ฌธ์
- ๐ zod ๋์ ํ๊ธฐ
- ๐ช nest Websocket์ ์ธ์ ์ด ์๋๋ค๊ณ ?
- ๐ด nginx websocket ์ฐ๊ฒฐ ์ ๋ฌธ์ ๋ฐ์
- ๐ WebPush ๊ตฌํ