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

프로젝트 소개 및 목표

실시간 채팅 앱 만들기 with Socket.io는 웹소켓 기술을 활용하여 실시간 양방향 통신을 구현하는 프로젝트입니다. 이 가이드를 통해 사용자 간 즉각적인 메시지 교환이 가능한 채팅 애플리케이션을 처음부터 끝까지 직접 구축해볼 수 있습니다. Socket.io는 WebSocket을 기반으로 하며, 연결이 끊어졌을 때 자동 재연결, 폴백 메커니즘 등 다양한 기능을 제공합니다. 본 프로젝트를 완성하면 실시간 통신의 원리를 이해하고, Node.js 백엔드와 프론트엔드 통합 능력을 향상시킬 수 있습니다. 또한 포트폴리오에 추가할 수 있는 실용적인 웹 애플리케이션을 완성하게 됩니다.

필요한 기술 스택

실시간 채팅 앱 만들기 with Socket.io 프로젝트를 구현하기 위해서는 다음의 기술 스택이 필요합니다:

  • Node.js: 서버 사이드 JavaScript 런타임 환경
  • Express.js: Node.js 웹 애플리케이션 프레임워크
  • Socket.io: 실시간 양방향 통신 라이브러리
  • HTML/CSS/JavaScript: 프론트엔드 기본 기술
  • npm: 패키지 관리 도구

추가적으로 MongoDB나 Redis를 활용하면 메시지 저장 및 세션 관리 기능을 확장할 수 있습니다.

프로젝트 셋업

먼저 프로젝트 디렉토리를 생성하고 필요한 패키지를 설치합니다.

# 프로젝트 디렉토리 생성
mkdir realtime-chat-app
cd realtime-chat-app

# package.json 초기화
npm init -y

# 필요한 패키지 설치
npm install express socket.io
npm install --save-dev nodemon

package.json 파일을 열어 scripts 섹션에 개발 서버 실행 명령어를 추가합니다:

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

이제 기본 프로젝트 구조가 준비되었습니다. 다음 단계에서 실제 서버와 클라이언트 코드를 작성하겠습니다.

단계별 구현 과정

1단계: Express 서버 설정

프로젝트 루트에 server.js 파일을 생성하고 기본 Express 서버를 설정합니다:

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);

const PORT = process.env.PORT || 3000;

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

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

server.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

2단계: Socket.io 이벤트 핸들러 구현

서버에서 클라이언트 연결 및 메시지 처리를 위한 이벤트 핸들러를 추가합니다:

// 온라인 사용자 추적
const users = {};

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

  // 사용자 입장
  socket.on('join', (username) => {
    users[socket.id] = username;
    socket.broadcast.emit('user joined', {
      username: username,
      message: `${username}님이 입장했습니다.`
    });
    // 현재 온라인 사용자 목록 전송
    io.emit('user list', Object.values(users));
  });

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

  // 타이핑 중 표시
  socket.on('typing', () => {
    socket.broadcast.emit('typing', {
      username: users[socket.id]
    });
  });

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

  // 사용자 연결 해제
  socket.on('disconnect', () => {
    const username = users[socket.id];
    if (username) {
      delete users[socket.id];
      io.emit('user left', {
        username: username,
        message: `${username}님이 퇴장했습니다.`
      });
      io.emit('user list', Object.values(users));
    }
    console.log('사용자 연결 해제:', socket.id);
  });
});

3단계: 클라이언트 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">
    <h1>실시간 채팅방</h1>
    <input type="text" id="username-input" placeholder="닉네임을 입력하세요" maxlength="20">
    <button id="join-btn">입장하기</button>
  </div>

  <div id="chat-container" style="display: none;">
    <div id="chat-header">
      <h2>채팅방</h2>
      <div id="online-users"></div>
    </div>
    <div id="messages"></div>
    <div id="typing-indicator"></div>
    <div id="input-container">
      <input type="text" id="message-input" placeholder="메시지를 입력하세요..." autocomplete="off">
      <button id="send-btn">전송</button>
    </div>
  </div>

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

4단계: 클라이언트 JavaScript 로직 구현

public/client.js 파일을 생성하여 Socket.io 클라이언트 코드를 작성합니다:

const socket = io();

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 messageInput = document.getElementById('message-input');
const sendBtn = document.getElementById('send-btn');
const messagesDiv = document.getElementById('messages');
const typingIndicator = document.getElementById('typing-indicator');
const onlineUsers = document.getElementById('online-users');

let username = '';
let typingTimer;

// 입장 처리
joinBtn.addEventListener('click', () => {
  username = usernameInput.value.trim();
  if (username) {
    socket.emit('join', username);
    loginContainer.style.display = 'none';
    chatContainer.style.display = 'flex';
    messageInput.focus();
  }
});

usernameInput.addEventListener('keypress', (e) => {
  if (e.key === 'Enter') joinBtn.click();
});

// 메시지 전송
function sendMessage() {
  const message = messageInput.value.trim();
  if (message) {
    socket.emit('chat message', { message });
    messageInput.value = '';
    socket.emit('stop typing');
  }
}

sendBtn.addEventListener('click', sendMessage);
messageInput.addEventListener('keypress', (e) => {
  if (e.key === 'Enter') sendMessage();
});

// 타이핑 표시
messageInput.addEventListener('input', () => {
  socket.emit('typing');
  clearTimeout(typingTimer);
  typingTimer = setTimeout(() => {
    socket.emit('stop typing');
  }, 1000);
});

// 메시지 수신
socket.on('chat message', (data) => {
  const messageEl = document.createElement('div');
  messageEl.className = data.username === username ? 'message own' : 'message';
  messageEl.innerHTML = `
    <strong>${data.username}</strong>
    <p>${data.message}</p>
    <span class="timestamp">${new Date(data.timestamp).toLocaleTimeString()}</span>
  `;
  messagesDiv.appendChild(messageEl);
  messagesDiv.scrollTop = messagesDiv.scrollHeight;
});

// 사용자 입장/퇴장
socket.on('user joined', (data) => {
  addSystemMessage(data.message);
});

socket.on('user left', (data) => {
  addSystemMessage(data.message);
});

// 온라인 사용자 목록
socket.on('user list', (users) => {
  onlineUsers.innerHTML = `온라인: ${users.length}명 - ${users.join(', ')}`;
});

// 타이핑 표시
socket.on('typing', (data) => {
  typingIndicator.textContent = `${data.username}님이 입력 중...`;
});

socket.on('stop typing', () => {
  typingIndicator.textContent = '';
});

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

5단계: 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 h1 {
  margin-bottom: 20px;
  color: #333;
}

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

#login-container button, #send-btn {
  width: 100%;
  padding: 12px;
  background: #667eea;
  color: white;
  border: none;
  border-radius: 5px;
  font-size: 16px;
  cursor: pointer;
  transition: background 0.3s;
}

#login-container button:hover, #send-btn:hover {
  background: #5568d3;
}

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

#chat-header {
  padding: 20px;
  background: #667eea;
  color: white;
  border-radius: 10px 10px 0 0;
}

#online-users {
  font-size: 14px;
  margin-top: 5px;
  opacity: 0.9;
}

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

.message {
  margin-bottom: 15px;
  padding: 10px;
  background: white;
  border-radius: 8px;
  max-width: 70%;
}

.message.own {
  margin-left: auto;
  background: #667eea;
  color: white;
  text-align: right;
}

.message.system {
  text-align: center;
  background: #e0e0e0;
  font-size: 14px;
  max-width: 100%;
}

.timestamp {
  font-size: 12px;
  opacity: 0.6;
}

#typing-indicator {
  padding: 5px 20px;
  font-size: 14px;
  color: #666;
  font-style: italic;
  min-height: 25px;
}

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

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

#send-btn {
  width: auto;
  padding: 12px 30px;
}

테스트 및 배포

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

npm run dev

브라우저에서 http://localhost:3000에 접속하여 여러 탭을 열어 실시간 채팅이 정상 동작하는지 확인합니다. 배포는 Heroku, Render, 또는 Railway를 추천합니다. Heroku 배포 예시:

# Procfile 생성
echo "web: node server.js" > Procfile

# Git 초기화 및 배포
git init
git add .
git commit -m "Initial commit"
heroku create
git push heroku main

환경 변수 PORT는 자동으로 설정되므로 별도 설정이 필요 없습니다. CORS 설정이 필요한 경우 Socket.io 초기화 시 옵션을 추가합니다.

마무리 및 확장 아이디어

이제 실시간 채팅 앱 만들기 with Socket.io 프로젝트가 완성되었습니다! 기본적인 채팅 기능을 구현했으며, 다음과 같은 기능을 추가하여 확장할 수 있습니다:

  • MongoDB를 활용한 채팅 히스토리 저장
  • 이미지 및 파일 전송 기능
  • 다중 채팅방(Room) 기능
  • 사용자 인증 및 프로필 시스템
  • 메시지 읽음 표시
  • 이모티콘 및 리액션 기능

이 프로젝트를 통해 실시간 통신의 핵심 개념을 익히고 포트폴리오를 강화할 수 있습니다.

📚 함께 읽으면 좋은 글

1

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

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

2

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

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

3

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

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

4

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

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

5

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

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

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

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

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

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

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

실시간 채팅 앱 만들기 with Socket.io 관련해서 궁금한 점이 더 있으시다면 언제든 물어보세요!

💡
유용한 정보 공유

궁금한 점 질문

🤝
경험담 나누기

👍
의견 표현하기

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

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

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

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

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

💡
최신 트렌드
2025년 기준

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

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

답글 남기기