김데이의 개발공부

[ TIL ] DAY 80 - 개인 프로젝트 : 실시간 통신(알림) 구현 본문

코드잇 Node.js(BE) 부트 캠프/TIL (Today I Learn) 📑

[ TIL ] DAY 80 - 개인 프로젝트 : 실시간 통신(알림) 구현

theday365 2026. 1. 19. 19:15
반응형

🗓️ 수업 일자 : 2026.1.19

✨ 오늘의 수업 평가  [ HARD ]  ⚡🧠💣 머리 과부하 ⚡🧠💣

 

ㅋㅋㅋㅋㅋ 핰ㅋㅋㅋ 짧은 시간 수업 들은 거 가지고 ... 구현하는거.. 진짜.. 와..ㅋㅋㅋㅋㅋㅋ

머리가 터질 거 같아서 오늘 공부한거 GPT한테 정리 해 달라고 요약 요청 할 정도였다 ㅋㅋㅋㅋㅋ

그래도 일단 오늘 구현은 다 끝낸 거 같아서 다행이다..

내일 전체 테스트 하면서 에러 잡아야지 👾

 

 

👩‍💻 [개인 프로젝트] 오늘 작업 내용 💻

- 학원 제공 "모범 답안"으로 전체 코드 리팩토링

- Socket.io를 사용하여 실시간 알림 구현

 

 

📝  오늘 배운 내용  (ㅎ 너무 많은데 정리 안된다)

- Websocket 구현 방식 ( with service.ts )

- 비동기 작업의 map vs forEach

 


1. Websocket 구현 방식 ( with service.ts )

service.ts 와 websocket.ts 의 역할 분리

 

  • service.ts : “언제/누구에게/무슨 알림을 보낼지” 결정하는 곳으로, 실제 작업의 의미와 서비스의 정책이 담기는 작업
  • webSocket.ts : “결정된 알림을 지금 연결된 유저에게 전달”하는 곳, 순수하게 "전송"만 담당

 

역할 분리의 이유

  1. 알림의 규모가 커지는 경우, 대응이 쉬움 
    : websocket.ts 파일에 알림의 규칙까지 모두 작성하는 경우, 메세지의 종류가 늘어남에 따라 해당 규칙을 추가 생성 해야함. 하지만 이를 모듈화 하고, 실제 사용되는 service.ts에서 실제 사용될 데이터를 설정하면 수많은 알림에 대응이 쉬움
  2. 전송 방식이 바뀌어도 서비스가 유지
    : 예를 들어 기존 서비스는 PC 웹브라우저만 사용했으나, 정책 변경으로 모바일 디바이스가 추가되는 경우 Websocket에서 전송 채널 확장(WebSocket/푸시/Email 과 같은 채널 추가)를 통해 쉽게 확장 가능

 

실시간 알림

 

 

websocket.ts

import { Server, Socket } from 'socket.io';
import http from 'http';
import * as cookie from 'cookie';
import jwt from 'jsonwebtoken';
import BadRequestError from './errors/BadRequestError';

// 외부에서 웹서버 통신을 하기 위한 기본 설정 
let ioRef: Server;

export function setupWebSocket(server: http.Server) {
  console.log('🤖 hello! I`m websocket 🤖');

  // server setting
  const io = new Server(server, {...});

  // 외부에서 웹서버를 사용 가능하도록 서버 설정 전달
  ioRef = io;

  // user 정보 확인
  io.use((socket: Socket, next) => {...(with token)...});

  io.on('connection', (socket) => {
    // 연결 후 유저 id로 room 접속
    socket.join(String((socket as any).user.id));
    // 연결되지 않는 경우
    socket.on('disconnect', () => {
      console.log('Client disconnected');
    });
  });
}

// service.ts 파일에서 사용 할 websocket 알림
export function notifyToUser(userId: number, event: string, payload: any) {
  ioRef.to(String(userId)).emit(event, payload);
}

 

 

service.ts

import { notifyToUser } from '@/lib/websocket';

...(중략)...

export async function createComment(data: CreateCommentData){

  ...(중략)...

  // 알림 생성
  notifyToUser(target.targetUserId, 'comment', {
    articleId: articleId!,
    commentId: comment.id,
    message: '댓글이 달렸습니다',
  });
}

 

 

2. 비동기 배열 작업의 map vs forEach

 

  • map
    • 원본 배열을 돌면서 각 요소를 다른 값으로 바꿔서 새 배열을 만듦
    • 데이터의 변환, 새 배열 만들기 같은 구문에 사용
    • Promise.all 함께 사용 : DB 저장을 진행하는데 하나라도 실패하면 롤백 
  • forEach
    • 배열을 돌면서 콜백을 실행하지만, 결과 배열을 만들지 않음
    • 콘솔 로그 / 이벤트 알림 등 실제 동작이 이루어지는 구문에 사용

 

await Promise.all(
  users.map(u => createNotification(u.id))
);

users.forEach(u => notifyToUser(u.id, ...));

 

 


 

📃 내일은 뭘 배울까 🤔

- 개인 미션 테스트

- Jest 테스트 프레임워크

 

 

반응형