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

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

프로젝트 소개 및 목표

실시간 채팅 앱 만들기 with Socket.io는 웹소켓 기반의 양방향 통신을 구현하는 대표적인 프로젝트입니다. 이 프로젝트를 통해 실시간 메시징 시스템의 핵심 원리를 배우고, 포트폴리오에 추가할 수 있는 실용적인 애플리케이션을 완성할 수 있습니다. Socket.io를 활용하면 HTTP의 한계를 극복하고 서버와 클라이언트 간 즉각적인 데이터 교환이 가능한 채팅 앱을 만들 수 있습니다. 이번 가이드에서는 사용자 인증, 실시간 메시지 전송, 접속자 표시 등 실제 채팅 서비스에 필요한 핵심 기능들을 단계별로 구현해보겠습니다. 최종적으로 여러 사용자가 동시에 접속하여 실시간으로 대화할 수 있는 완전한 채팅 애플리케이션을 완성하게 됩니다.

필요한 기술 스택

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

  • 백엔드: Node.js (v14 이상), Express.js (웹 서버 프레임워크), Socket.io (실시간 통신 라이브러리)
  • 프론트엔드: HTML5, CSS3, Vanilla JavaScript (또는 React), Socket.io-client (클라이언트 라이브러리)
  • 데이터베이스: MongoDB (메시지 저장용, 선택사항) 또는 메모리 기반 저장소
  • 개발 도구: npm 또는 yarn (패키지 매니저), nodemon (개발 서버 자동 재시작)

프로젝트 셋업

먼저 프로젝트 디렉토리를 생성하고 필요한 패키지를 설치합니다. 터미널에서 다음 명령어를 실행하세요:

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

# package.json 초기화
npm init -y

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

# 프로젝트 구조 생성
mkdir public
touch server.js
touch public/index.html public/style.css public/client.js

package.json 파일을 열어 scripts 섹션을 다음과 같이 수정합니다:

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

이제 개발 환경 준비가 완료되었습니다. npm run dev 명령어로 개발 서버를 시작할 수 있습니다.

단계별 구현 과정

1단계: Express 및 Socket.io 서버 구성

먼저 server.js 파일에 기본 서버 코드를 작성합니다. Express로 HTTP 서버를 생성하고 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'));

// 접속한 사용자 관리
const users = new Map();

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

// Socket.io 연결 처리
io.on('connection', (socket) => {
  console.log('새로운 사용자가 접속했습니다:', socket.id);

  // 사용자 입장
  socket.on('join', (username) => {
    users.set(socket.id, username);
    socket.broadcast.emit('user joined', {
      username: username,
      userCount: users.size
    });
    
    // 현재 접속자 수 전송
    io.emit('user count', users.size);
  });

  // 메시지 수신 및 브로드캐스트
  socket.on('chat message', (msg) => {
    const username = users.get(socket.id) || '익명';
    io.emit('chat message', {
      username: username,
      message: msg,
      timestamp: new Date().toLocaleTimeString('ko-KR')
    });
  });

  // 타이핑 중 상태
  socket.on('typing', () => {
    const username = users.get(socket.id);
    socket.broadcast.emit('user typing', username);
  });

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

  // 연결 해제
  socket.on('disconnect', () => {
    const username = users.get(socket.id);
    if (username) {
      users.delete(socket.id);
      io.emit('user left', {
        username: username,
        userCount: users.size
      });
      io.emit('user count', users.size);
    }
    console.log('사용자가 퇴장했습니다:', socket.id);
  });
});

// 서버 시작
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
  console.log(`서버가 포트 ${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" class="container">
    <h1>💬 실시간 채팅 앱</h1>
    <input type="text" id="username-input" placeholder="닉네임을 입력하세요" maxlength="20">
    <button id="join-btn">입장하기</button>
  </div>

  <!-- 채팅 화면 -->
  <div id="chat-container" class="container" style="display: none;">
    <div class="chat-header">
      <h2>채팅방</h2>
      <span id="user-count" class="user-count">접속자: 0명</span>
    </div>
    
    <ul id="messages" class="messages"></ul>
    
    <div id="typing-indicator" class="typing-indicator"></div>
    
    <form id="message-form" class="message-form">
      <input id="message-input" autocomplete="off" placeholder="메시지를 입력하세요...">
      <button type="submit">전송</button>
    </form>
  </div>

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

3단계: CSS 스타일링

public/style.css 파일에 모던한 디자인을 적용합니다:

* {
  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;
}

.container {
  background: white;
  border-radius: 12px;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
  overflow: hidden;
}

#login-container {
  padding: 40px;
  text-align: center;
  width: 400px;
}

#login-container h1 {
  margin-bottom: 30px;
  color: #333;
}

#username-input {
  width: 100%;
  padding: 15px;
  border: 2px solid #e0e0e0;
  border-radius: 8px;
  font-size: 16px;
  margin-bottom: 20px;
  transition: border-color 0.3s;
}

#username-input:focus {
  outline: none;
  border-color: #667eea;
}

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

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

#chat-container {
  width: 600px;
  height: 700px;
  display: flex;
  flex-direction: column;
}

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

.user-count {
  background: rgba(255, 255, 255, 0.2);
  padding: 5px 15px;
  border-radius: 20px;
  font-size: 14px;
}

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

.messages li {
  padding: 12px 16px;
  margin-bottom: 10px;
  border-radius: 8px;
  animation: fadeIn 0.3s;
}

.messages li.system {
  background: #e3f2fd;
  color: #1976d2;
  text-align: center;
  font-size: 14px;
}

.messages li.user {
  background: white;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.message-username {
  font-weight: bold;
  color: #667eea;
  margin-right: 8px;
}

.message-time {
  font-size: 12px;
  color: #999;
  margin-left: 8px;
}

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

.message-form {
  display: flex;
  padding: 20px;
  background: white;
  border-top: 1px solid #e0e0e0;
}

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

#message-input:focus {
  outline: none;
  border-color: #667eea;
}

.message-form button {
  padding: 12px 30px;
  background: #667eea;
  color: white;
  border: none;
  border-radius: 8px;
  font-weight: bold;
  cursor: pointer;
  transition: background 0.3s;
}

.message-form button:hover {
  background: #5568d3;
}

@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

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

public/client.js 파일에 클라이언트 로직을 작성합니다:

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 messageForm = document.getElementById('message-form');
const messageInput = document.getElementById('message-input');
const messages = document.getElementById('messages');
const userCount = document.getElementById('user-count');
const typingIndicator = document.getElementById('typing-indicator');

let username = '';
let isTyping = false;
let typingTimeout;

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

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

// 메시지 전송
messageForm.addEventListener('submit', (e) => {
  e.preventDefault();
  const msg = messageInput.value.trim();
  if (msg) {
    socket.emit('chat message', msg);
    messageInput.value = '';
    socket.emit('stop typing');
    isTyping = false;
  }
});

// 타이핑 감지
messageInput.addEventListener('input', () => {
  if (!isTyping) {
    isTyping = true;
    socket.emit('typing');
  }
  
  clearTimeout(typingTimeout);
  typingTimeout = setTimeout(() => {
    socket.emit('stop typing');
    isTyping = false;
  }, 1000);
});

// 메시지 수신
socket.on('chat message', (data) => {
  const li = document.createElement('li');
  li.className = 'user';
  li.innerHTML = `
    ${data.username}
    ${escapeHtml(data.message)}
    ${data.timestamp}
  `;
  messages.appendChild(li);
  messages.scrollTop = messages.scrollHeight;
});

// 사용자 입장/퇴장
socket.on('user joined', (data) => {
  addSystemMessage(`${data.username}님이 입장했습니다.`);
});

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

// 접속자 수 업데이트
socket.on('user count', (count) => {
  userCount.textContent = `접속자: ${count}명`;
});

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

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

// 유틸리티 함수
function addSystemMessage(text) {
  const li = document.createElement('li');
  li.className = 'system';
  li.textContent = text;
  messages.appendChild(li);
  messages.scrollTop = messages.scrollHeight;
}

function escapeHtml(text) {
  const div = document.createElement('div');
  div.textContent = text;
  return div.innerHTML;
}

테스트 및 배포

로컬 테스트

개발 서버를 실행하여 실시간 채팅 앱 만들기 with Socket.io 프로젝트를 테스트합니다:

# 개발 서버 실행
npm run dev

브라우저에서 http://localhost:3000에 접속한 후, 여러 개의 탭이나 창을 열어 실시간 메시징이 제대로 작동하는지 확인합니다. 다음 기능들을 테스트해보세요:

  • 여러 사용자 동시 접속 및 메시지 교환
  • 타이핑 인디케이터 표시
  • 접속자 수 업데이트
  • 사용자 입장/퇴장 알림

프로덕션 배포

Heroku, Railway, Render 등의 플랫폼에 배포할 수 있습니다. Render 배포 예시:

# Git 저장소 초기화
git init
git add .
git commit -m "Initial commit: 실시간 채팅 앱"

# GitHub에 푸시 후 Render 대시보드에서 연결
# 환경 변수 설정: PORT=3000

배포 후에는 HTTPS를 통해 안전한 WebSocket 연결(WSS)이 자동으로 설정됩니다.

마무리 및 확장 아이디어

이번 가이드를 통해 실시간 채팅 앱 만들기 with Socket.io 프로젝트를 완성했습니다. 기본적인 실시간 메시징 시스템이 구축되었으며, 이를 바탕으로 다양한 기능을 추가할 수 있습니다.

추가 기능 아이디어:

  • 채팅방 생성 및 관리 (다중 룸 지원)
  • 개인 메시지(DM) 기능
  • 파일 및 이미지 공유
  • 메시지 검색 및 히스토리 저장 (MongoDB 연동)
  • 사용자 프로필 및 아바타
  • 이모지 및 리액션 기능
  • 음성/영상 채팅 (WebRTC 연동)
  • 읽음 표시 및 메시지 상태
  • 관리자 권한 및 사용자 차단 기능

이 프로젝트는 포트폴리오에 추가하기 좋은 실전 프로젝트이며, Socket.io의 핵심 개념을 이해하는 데 큰 도움이 됩니다. 실시간 통신 기술은 채팅뿐만 아니라 협업 도구, 게임, 알림 시스템 등 다양한 분야에 활용될 수 있습니다.

📚 함께 읽으면 좋은 글

1

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

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

2

React + Node.js 풀스택 앱 배포하기 – 완성까지 한번에!

📂 프로젝트 아이디어
📅 2025. 10. 9.
🎯 React + Node.js 풀스택 앱 배포하기

3

React + Node.js 풀스택 앱 배포하기 – 완성까지 한번에!

📂 프로젝트 아이디어
📅 2025. 10. 7.
🎯 React + Node.js 풀스택 앱 배포하기

4

30분만에 만드는 Todo App 완성 가이드 – 완성까지 한번에!

📂 프로젝트 아이디어
📅 2025. 10. 5.
🎯 30분만에 만드는 Todo App 완성 가이드

5

React + Node.js 풀스택 앱 배포하기 – 완성까지 한번에!

📂 프로젝트 아이디어
📅 2025. 10. 5.
🎯 React + Node.js 풀스택 앱 배포하기

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

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

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

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

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

여러분은 실시간 채팅 앱 만들기 with Socket.io에 대해 어떻게 생각하시나요?

💡
유용한 정보 공유

궁금한 점 질문

🤝
경험담 나누기

👍
의견 표현하기

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

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

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

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

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

💡
최신 트렌드
2025년 기준

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

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

답글 남기기