실시간 채팅 앱 만들기 with Socket.io – 완성까지 한번에!

프로젝트 소개 및 목표

실시간 채팅 앱 만들기 with Socket.io는 웹소켓 기술을 활용하여 실시간 양방향 통신을 구현하는 프로젝트입니다. 이 프로젝트를 통해 Node.js와 Socket.io를 사용한 실시간 채팅 시스템의 핵심 원리를 이해하고, 실무에서 바로 활용할 수 있는 풀스택 개발 능력을 키울 수 있습니다. 최종적으로 여러 사용자가 동시에 접속하여 메시지를 주고받고, 사용자 입장/퇴장 알림, 타이핑 인디케이터 등의 기능을 갖춘 완성도 높은 채팅 애플리케이션을 만들어봅니다. 포트폴리오에 추가하기 좋은 프로젝트이며, 실시간 통신의 기초를 다지는 데 최적화된 학습 과정입니다.

필요한 기술 스택

이 프로젝트를 완성하기 위해 필요한 기술 스택은 다음과 같습니다. 백엔드는 Node.jsExpress.js를 사용하여 서버를 구축하고, Socket.io 라이브러리로 실시간 통신을 구현합니다. 프론트엔드는 HTML, CSS, JavaScript를 기본으로 하며, 선택적으로 React나 Vue.js를 활용할 수 있습니다. 데이터베이스는 MongoDB를 사용하여 채팅 기록을 저장하며, 개발 환경으로는 VS Code, npm/yarn 패키지 매니저가 필요합니다. 모든 기술은 초중급 개발자도 쉽게 따라할 수 있도록 구성되어 있습니다.

프로젝트 셋업

먼저 프로젝트 디렉토리를 생성하고 npm을 초기화합니다. 터미널에서 다음 명령어를 실행하세요.

mkdir realtime-chat-app
cd realtime-chat-app
npm init -y

필요한 패키지들을 설치합니다. Express는 웹 서버 구축을, Socket.io는 실시간 통신을 담당합니다.

npm install express socket.io
npm install -D nodemon

프로젝트 구조를 다음과 같이 설정합니다.

realtime-chat-app/
├── server.js
├── public/
│   ├── index.html
│   ├── style.css
│   └── client.js
└── package.json

package.json의 scripts에 개발 서버 실행 명령을 추가합니다.

"scripts": {
  "start": "node server.js",
  "dev": "nodemon server.js"
}

단계별 구현 과정

1단계: Express 서버 및 Socket.io 설정

server.js 파일을 생성하고 기본 서버를 구축합니다. 실시간 채팅 앱 만들기 with Socket.io의 핵심은 서버와 클라이언트 간의 웹소켓 연결입니다.

const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);
const { Server } = require('socket.io');
const io = new Server(server);

// 정적 파일 제공
app.use(express.static('public'));

// 메인 라우트
app.get('/', (req, res) => {
  res.sendFile(__dirname + '/public/index.html');
});

// Socket.io 연결 이벤트
io.on('connection', (socket) => {
  console.log('새로운 사용자 연결:', socket.id);

  // 사용자 입장 알림
  socket.on('user-joined', (username) => {
    socket.username = username;
    io.emit('user-connected', username);
  });

  // 채팅 메시지 수신 및 브로드캐스트
  socket.on('chat-message', (data) => {
    io.emit('chat-message', {
      username: socket.username,
      message: data.message,
      timestamp: new Date().toISOString()
    });
  });

  // 타이핑 인디케이터
  socket.on('typing', () => {
    socket.broadcast.emit('user-typing', socket.username);
  });

  socket.on('stop-typing', () => {
    socket.broadcast.emit('user-stop-typing', socket.username);
  });

  // 사용자 연결 해제
  socket.on('disconnect', () => {
    if (socket.username) {
      io.emit('user-disconnected', socket.username);
    }
    console.log('사용자 연결 해제:', socket.id);
  });
});

const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
  console.log(`서버 실행 중: http://localhost:${PORT}`);
});

2단계: HTML 클라이언트 구조

public/index.html 파일을 생성하여 채팅 인터페이스를 만듭니다.

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>실시간 채팅 앱</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div id="login-container">
    <h2>실시간 채팅 앱</h2>
    <input type="text" id="username-input" placeholder="사용자 이름 입력" />
    <button id="join-btn">입장하기</button>
  </div>

  <div id="chat-container" style="display: none;">
    <div id="chat-header">
      <h3>실시간 채팅</h3>
      <span id="user-info"></span>
    </div>
    <div id="messages"></div>
    <div id="typing-indicator"></div>
    <div id="input-container">
      <input type="text" id="message-input" placeholder="메시지를 입력하세요..." />
      <button id="send-btn">전송</button>
    </div>
  </div>

  <script src="/socket.io/socket.io.js"></script>
  <script src="client.js"></script>
</body>
</html>

3단계: 클라이언트 JavaScript 구현

public/client.js 파일에서 클라이언트 측 Socket.io 로직을 구현합니다.

const socket = io();

let username = '';
let typingTimer;
const typingDelay = 1000;

// DOM 요소
const loginContainer = document.getElementById('login-container');
const chatContainer = document.getElementById('chat-container');
const usernameInput = document.getElementById('username-input');
const joinBtn = document.getElementById('join-btn');
const messagesDiv = document.getElementById('messages');
const messageInput = document.getElementById('message-input');
const sendBtn = document.getElementById('send-btn');
const typingIndicator = document.getElementById('typing-indicator');
const userInfo = document.getElementById('user-info');

// 입장하기
joinBtn.addEventListener('click', () => {
  username = usernameInput.value.trim();
  if (username) {
    socket.emit('user-joined', username);
    loginContainer.style.display = 'none';
    chatContainer.style.display = 'flex';
    userInfo.textContent = `사용자: ${username}`;
    messageInput.focus();
  }
});

// 메시지 전송
sendBtn.addEventListener('click', sendMessage);
messageInput.addEventListener('keypress', (e) => {
  if (e.key === 'Enter') sendMessage();
});

function sendMessage() {
  const message = messageInput.value.trim();
  if (message) {
    socket.emit('chat-message', { message });
    messageInput.value = '';
    socket.emit('stop-typing');
  }
}

// 타이핑 인디케이터
messageInput.addEventListener('input', () => {
  socket.emit('typing');
  clearTimeout(typingTimer);
  typingTimer = setTimeout(() => {
    socket.emit('stop-typing');
  }, typingDelay);
});

// 메시지 수신
socket.on('chat-message', (data) => {
  addMessage(data.username, data.message, data.timestamp);
});

// 사용자 연결/해제 알림
socket.on('user-connected', (user) => {
  addSystemMessage(`${user}님이 입장했습니다.`);
});

socket.on('user-disconnected', (user) => {
  addSystemMessage(`${user}님이 퇴장했습니다.`);
});

// 타이핑 인디케이터 표시
let typingUsers = new Set();

socket.on('user-typing', (user) => {
  typingUsers.add(user);
  updateTypingIndicator();
});

socket.on('user-stop-typing', (user) => {
  typingUsers.delete(user);
  updateTypingIndicator();
});

function updateTypingIndicator() {
  if (typingUsers.size > 0) {
    const users = Array.from(typingUsers).join(', ');
    typingIndicator.textContent = `${users}님이 입력 중...`;
  } else {
    typingIndicator.textContent = '';
  }
}

function addMessage(user, message, timestamp) {
  const messageEl = document.createElement('div');
  messageEl.className = user === username ? 'message my-message' : 'message';
  const time = new Date(timestamp).toLocaleTimeString('ko-KR', { 
    hour: '2-digit', 
    minute: '2-digit' 
  });
  messageEl.innerHTML = `
    ${user}
    

${message}

${time} `; messagesDiv.appendChild(messageEl); messagesDiv.scrollTop = messagesDiv.scrollHeight; } function addSystemMessage(message) { const messageEl = document.createElement('div'); messageEl.className = 'system-message'; messageEl.textContent = message; messagesDiv.appendChild(messageEl); messagesDiv.scrollTop = messagesDiv.scrollHeight; }

4단계: CSS 스타일링

public/style.css 파일로 깔끔한 UI를 구현합니다.

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
}

#login-container {
  background: white;
  padding: 40px;
  border-radius: 10px;
  box-shadow: 0 10px 40px rgba(0,0,0,0.2);
  text-align: center;
}

#login-container h2 {
  margin-bottom: 20px;
  color: #333;
}

#username-input {
  width: 100%;
  padding: 12px;
  margin-bottom: 15px;
  border: 2px solid #ddd;
  border-radius: 5px;
  font-size: 14px;
}

#join-btn {
  width: 100%;
  padding: 12px;
  background: #667eea;
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  font-size: 16px;
  font-weight: bold;
}

#join-btn:hover {
  background: #5568d3;
}

#chat-container {
  width: 90%;
  max-width: 600px;
  height: 80vh;
  background: white;
  border-radius: 10px;
  box-shadow: 0 10px 40px rgba(0,0,0,0.2);
  display: flex;
  flex-direction: column;
}

#chat-header {
  background: #667eea;
  color: white;
  padding: 20px;
  border-radius: 10px 10px 0 0;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

#messages {
  flex: 1;
  overflow-y: auto;
  padding: 20px;
  background: #f9f9f9;
}

.message {
  margin-bottom: 15px;
  padding: 10px 15px;
  background: white;
  border-radius: 10px;
  box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}

.my-message {
  background: #667eea;
  color: white;
  margin-left: 20%;
}

.message strong {
  display: block;
  margin-bottom: 5px;
  font-size: 12px;
}

.message p {
  margin: 5px 0;
}

.timestamp {
  font-size: 10px;
  opacity: 0.7;
}

.system-message {
  text-align: center;
  color: #888;
  font-size: 12px;
  margin: 10px 0;
  font-style: italic;
}

#typing-indicator {
  padding: 5px 20px;
  color: #667eea;
  font-size: 12px;
  min-height: 20px;
}

#input-container {
  display: flex;
  padding: 15px;
  border-top: 1px solid #ddd;
}

#message-input {
  flex: 1;
  padding: 12px;
  border: 2px solid #ddd;
  border-radius: 5px;
  margin-right: 10px;
  font-size: 14px;
}

#send-btn {
  padding: 12px 25px;
  background: #667eea;
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  font-weight: bold;
}

#send-btn:hover {
  background: #5568d3;
}

테스트 및 배포

개발 서버를 실행하여 로컬에서 테스트합니다.

npm run dev

브라우저에서 http://localhost:3000 에 접속하여 여러 탭을 열어 실시간 채팅을 테스트합니다. 배포는 Heroku, Render, Vercel 등의 플랫폼을 활용할 수 있습니다. Heroku 배포 예시:

heroku create my-chat-app
git init
git add .
git commit -m "Initial commit"
git push heroku main

환경변수로 PORT를 설정하고, Procfile을 추가하여 프로덕션 환경을 구성합니다. WebSocket을 지원하는 호스팅 서비스를 선택하는 것이 중요합니다. 배포 후에는 여러 디바이스에서 동시 접속 테스트를 진행하여 실시간 통신이 정상 작동하는지 확인합니다.

마무리 및 확장 아이디어

실시간 채팅 앱 만들기 with Socket.io 프로젝트를 완성했습니다! 추가로 구현할 수 있는 기능으로는 채팅방 생성 및 관리, 파일 전송, 이모지 지원, 읽음 표시, 사용자 프로필 이미지, MongoDB를 활용한 채팅 히스토리 저장, JWT 인증을 통한 보안 강화 등이 있습니다. 이 프로젝트를 기반으로 Slack이나 Discord와 같은 고급 메신저 애플리케이션으로 발전시킬 수 있습니다. 포트폴리오에 추가하여 실시간 통신 기술에 대한 이해도를 어필하세요!

📚 함께 읽으면 좋은 글

1

실시간 채팅 앱 만들기 with Socket.io – 완성까지 한번에!

📂 프로젝트 아이디어
📅 2025. 11. 17.
🎯 실시간 채팅 앱 만들기 with Socket.io

2

실시간 채팅 앱 만들기 with Socket.io – 완성까지 한번에!

📂 프로젝트 아이디어
📅 2025. 11. 15.
🎯 실시간 채팅 앱 만들기 with Socket.io

3

실시간 채팅 앱 만들기 with Socket.io – 완성까지 한번에!

📂 프로젝트 아이디어
📅 2025. 11. 11.
🎯 실시간 채팅 앱 만들기 with Socket.io

4

실시간 채팅 앱 만들기 with Socket.io – 완성까지 한번에!

📂 프로젝트 아이디어
📅 2025. 11. 9.
🎯 실시간 채팅 앱 만들기 with Socket.io

5

실시간 채팅 앱 만들기 with Socket.io – 완성까지 한번에!

📂 프로젝트 아이디어
📅 2025. 11. 5.
🎯 실시간 채팅 앱 만들기 with Socket.io

💡 위 글들을 통해 더 깊이 있는 정보를 얻어보세요!

📢 이 글이 도움되셨나요? 공유해주세요!

여러분의 공유 한 번이 더 많은 사람들에게 도움이 됩니다 ✨

🔥 공유할 때마다 블로그 성장에 큰 힘이 됩니다! 감사합니다 🙏

💬 여러분의 소중한 의견을 들려주세요!

이 글에서 가장 도움이 된 부분은 어떤 것인가요?

💡
유용한 정보 공유

궁금한 점 질문

🤝
경험담 나누기

👍
의견 표현하기

⭐ 모든 댓글은 24시간 내에 답변드리며, 여러분의 의견이 다른 독자들에게 큰 도움이 됩니다!
🎯 건설적인 의견과 경험 공유를 환영합니다 ✨

🔔 블로그 구독하고 최신 글을 받아보세요!

📚
다양한 주제
17개 카테고리

정기 업데이트
하루 3회 발행

🎯
실용적 정보
바로 적용 가능

💡
최신 트렌드
2025년 기준

🌟 프로젝트 아이디어부터 다양한 실생활 정보까지!
매일 새로운 유용한 콘텐츠를 만나보세요 ✨

📧 RSS 구독 | 🔖 북마크 추가 | 📱 모바일 앱 알림 설정
지금 구독하고 놓치는 정보 없이 업데이트 받아보세요!

답글 남기기