Front-end/기능 공부

[WEB] 웹소켓(WebSocket)이란? 🌐

xeunnie 2024. 9. 23. 04:26
728x90
반응형

웹소켓 통신: 실시간 채팅과 파일 전송 구현하기🌐

현대 웹 애플리케이션에서는 사용자 간의 실시간 소통이 점점 더 중요해지고 있습니다.

채팅, 알림, 실시간 데이터 업데이트 등 다양한 기능이 사용자 경험을 한층 향상시키고 있죠.

 

이러한 요구를 충족시키기 위해 등장한 기술 중 하나가 바로 웹소켓(WebSocket)입니다.

 

웹소켓은 클라이언트와 서버 간의 지속적인 연결을 유지하여, 양방향 통신을 가능하게 해줍니다.

이를 통해 실시간으로 메시지를 주고받거나 파일을 전송하는 등의 기능을 구현할 수 있습니다.


🪁 웹소켓(WebSocket)이란?

웹소켓(WebSocket)은 실시간 양방향 통신을 가능하게 해주는 프로토콜입니다.

 

기존의 HTTP 요청-응답 방식과 달리,

(기존의 HTTP 요청-응답 방식은 각 요청마다 연결을 새로 설정해야 하므로,실시간 소통이 필요한 애플리케이션에서는 한계가 있었습니다.)

 

웹소켓은 한 번 연결되면 유지되며, 클라이언트와 서버 간에 자유롭게 또 지속적으로 데이터를 주고받을 수 있습니다.

이로 인해 클라이언트와 서버 간에 자유롭게 데이터를 주고받을 수 있으며, 필요한 순간에 즉시 통신이 이루어집니다.

 

웹소켓은 다양한 실시간 애플리케이션에서 매우 유용하게 사용됩니다.

 

예를 들어,

채팅 서비스에서는 사용자 간의 메시지를 즉시 전달할 수 있고,

실시간 알림 시스템에서는 중요한 정보를 신속하게 업데이트할 수 있습니다.

또한, 주식 시장의 시세 변동을 실시간으로 반영하여 사용자에게 가장 최신의 정보를 제공하는 데도 활용됩니다.


🚀 웹소켓 vs HTTP, 뭐가 다를까?

  HTTP WebSocket
통신 방식 요청-응답 방식 지속적인 연결 유지
연결 유지 요청 시마다 연결 생성 한 번 연결되면 지속됨
데이터 흐름 클라이언트가 요청해야 서버 응답 클라이언트-서버 간 양방향 통신 가능
성능 매 요청마다 새로운 연결, 오버헤드 발생 연결 유지로 오버헤드 최소화
사용 사례 정적인 웹사이트, API 요청 채팅, 실시간 알림, 스트리밍

🔥 웹소켓이 필요한 이유

  • HTTP는 요청을 보내야지만 응답이 가능하기 때문에, 클라이언트가 서버의 변경 사항을 확인하려면 계속해서 요청을 보내야 함 (폴링, Long Polling 필요)
  • 웹소켓은 서버에서 실시간으로 데이터를 보낼 수 있어 클라이언트가 변경 사항을 즉시 받을 수 있음
  • 빠른 응답 속도와 네트워크 자원 절약 가능

🎛️ 웹소켓의 주요 특징

🌀 양방향 통신:

웹소켓은 클라이언트와 서버 간에 실시간으로 데이터를 주고받을 수 있는 양방향 통신을 지원합니다.

즉, 한쪽에서 메시지를 전송하면 다른 쪽에서도 이를 즉시 수신할 수 있습니다.

 

이러한 통신 방식은 채팅 애플리케이션, 온라인 게임, 실시간 알림 시스템 등에서 매우 유용합니다.

예를 들어, 사용자가 메시지를 보내면 서버는 이를 즉시 받아서 다른 사용자에게 전달하고,

이 과정은 클라이언트와 서버 간의 연결이 끊어지지 않는 한 계속 유지됩니다.

 

⏱️ 낮은 지연 시간

웹소켓은 HTTP 프로토콜과 비교하여 낮은 지연 시간을 자랑합니다.

HTTP는 요청-응답 방식으로 동작하며, 각 요청마다 새로운 연결을 설정해야 합니다.

이 과정에서 연결 설정, 데이터 전송, 응답 수신 등의 시간이 소요되어 지연이 발생합니다.

반면, 웹소켓은 초기 연결이 이루어진 후 지속적인 연결을 유지하므로 추가적인 오버헤드 없이 데이터를 즉시 전송할 수 있습니다.

예를 들어, 게임에서는 실시간으로 사용자 위치 정보를 업데이트해야 하는데, 웹소켓은 이러한 요구에 효과적으로 대응할 수 있습니다.

 

🔗 연결 지속성

웹소켓은 클라이언트와 서버 간의 연결이 한 번 설정되면 해당 연결이 유지됩니다.

이는 클라이언트가 서버와의 연결을 지속적으로 유지하고 필요할 때마다 데이터를 전송할 수 있음을 의미합니다.

이로 인해 매번 새로운 연결을 설정하는 것보다 훨씬 더 효율적으로 데이터 통신을 할 수 있습니다.

예를 들어, 채팅 애플리케이션에서는 사용자가 메시지를 주고받을 때마다 연결을 새로 설정할 필요가 없으므로,

더 부드럽고 빠른 사용자 경험을 제공할 수 있는 거지요.

 

💡정리: WebSocket이 중요한 이유?

풀 듀플렉스(Fullduplex) → 클라이언트와 서버가 동시에 데이터를 송수신

Persistent Connection → 한 번 연결되면 계속 유지됨

낮은 오버헤드 → HTTP Polling보다 리소스 사용이 적음


🛠 웹소켓의 작동 원리

웹소켓은 기본적으로 핸드셰이크(Handshake) 과정을 통해 연결을 설정합니다.

🔦 1 단계 : 웹소켓 핸드셰이크 ( 과정 )

1️⃣ 클라이언트가 서버로 웹소켓 연결 요청 (ws:// 또는 wss:// 사용)

  • ✅ 클라이언트는 웹소켓 서버에 연결 요청을 보냅니다.
  • ✅ 이 요청은 HTTP 프로토콜을 사용하여 이루어지며,
  • ✅ 클라이언트는 Upgrade 헤더를 포함하여 웹소켓 프로토콜로의 전환을 요청합니다.

📌 클라이언트가 서버에 보낸 요청

GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

 

2️⃣ 서버가 요청을 받고, 웹소켓 프로토콜로 전환 (HTTP 101 Switching Protocols 응답)

  • 서버는 이 요청을 수신하고 확인한 후, 웹소켓 프로토콜로 전환합니다.
  • 성공적으로 전환되면, 서버는 클라이언트에게 승인 응답을 보냅니다.
  • 이 응답에는 클라이언트의 요청에 대한 확인과 함께 새로운 연결에 대한 정보가 포함됩니다.

📌 서버의 응답

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

 

이후 클라이언트와 서버는 연결이 유지된 상태에서 자유롭게 데이터를 주고받을 수 있습니다.


📡 2 단계 : 연결 후 데이터 전송

1️⃣ 연결 확립

핸드셰이크가 완료되면 클라이언트와 서버 간의 연결이 확립됩니다.

이 상태에서는 양방향으로 데이터 프레임을 주고받을 수 있습니다.

2️⃣ 실시간 통신

클라이언트와 서버는 서로의 상태를 실시간으로 반영하며, 필요에 따라 데이터를 즉시 전송할 수 있습니다.

예를 들어, 사용자가 채팅 메시지를 보내면, 서버는 이를 즉시 다른 사용자에게 전달합니다.

3️⃣ 데이터 프레임

웹소켓에서는 데이터가 "프레임"이라는 단위로 전송됩니다.

각 프레임에는 메시지의 종류(텍스트, 바이너리 등)와 데이터가 포함되어 있습니다.


🛑 3 단계 : 연결 종료

1️⃣ 종료 요청

연결을 종료하려면 클라이언트 또는 서버가 연결 종료 메시지를 전송합니다.

이는 웹소켓 프로토콜에 따라 정해진 형식으로 이루어집니다

2️⃣ 안전한 종료

연결 종료 메시지를 수신한 쪽은 연결을 안전하게 종료하며, 이 과정에서도 추가적인 핸드셰이크가 필요하지 않습니다.

이는 웹소켓의 효율성을 더욱 높이는 요소입니다.

📌 종료 메시지

Close frame: [code, reason]

이러한 과정을 통해 웹소켓은 클라이언트와 서버 간의 지속적이고 효율적인 통신을 가능하게 합니다.

웹소켓의 이러한 특징은 실시간 기능이 중요한 다양한 애플리케이션에서 큰 장점을 제공합니다.


✍ 웹소켓 문법과 규칙

1️⃣ 웹소켓 연결하기

const socket = new WebSocket('ws://example.com/socket');
  • ws:// → 웹소켓 연결 (보안 필요 시 wss:// 사용)
  • 웹소켓 객체가 생성되면 서버와의 연결 시도 시작

2️⃣ 이벤트 핸들링 (연결, 메시지, 에러, 종료)

socket.onopen = function () {
    console.log('웹소켓 연결 완료! 🎉');
    socket.send('Hello, Server!'); // 서버로 데이터 전송
};

socket.onmessage = function (event) {
    console.log('서버 메시지 수신: ', event.data);
};

socket.onerror = function (error) {
    console.error('웹소켓 오류 발생 ❌', error);
};

socket.onclose = function () {
    console.log('웹소켓 연결 종료 😢');
};

3️⃣ 데이터 보내고 받기

// 서버로 데이터 보내기
socket.send(JSON.stringify({ type: 'message', text: '안녕하세요!' }));

// 서버에서 JSON 데이터를 받을 경우
socket.onmessage = function (event) {
    const data = JSON.parse(event.data);
    console.log('받은 데이터:', data);
};

📌 보낼 수 있는 데이터 타입:

  • 문자열(String)
  • Blob (이미지, 파일 전송)
  • ArrayBuffer (바이너리 데이터 전송)

4️⃣ 웹소켓 연결 닫기

socket.close(); // 정상 종료
socket.close(1000, '정상 종료'); // 종료 코드와 메시지 포함

📌 주요 종료 코드

코드 의미
1000 정상 종료
1001 클라이언트가 브라우저 종료
1006 네트워크 장애
1011 서버 내부 오류

💠 OSI 7계층에서의 웹소켓

웹소켓은 OSI 7계층 모델에서 응용 계층전송 계층에서 중요한 역할을 합니다.

이를 통해 클라이언트와 서버 간의 효율적인 실시간 통신을 가능하게 합니다.

🔩 응용 계층에서의 웹소켓

💬 데이터 형식 및 프로토콜

웹소켓은 클라이언트와 서버 간의 데이터 형식을 정의합니다.

클라이언트는 웹소켓 API를 통해 메시지를 전송하며, 이때 JSON, XML 또는 텍스트 형식으로 데이터를 주고받습니다.

이 데이터 형식은 서로 다른 프로그래밍 언어와 플랫폼 간의 상호 이해를 가능하게 하여 메시지를 효율적으로 처리할 수 있도록 돕습니다.

🖥️ 웹소켓 API

웹소켓은 JavaScript와 같은 클라이언트 사이드 언어에서 사용할 수 있는 API를 제공합니다.

📌 웹소켓 API의 주요 메서드

  • WebSocket.send(): 서버로 데이터를 전송합니다.
  • WebSocket.onmessage: 서버로부터 메시지를 수신했을 때 호출되는 이벤트 핸들러입니다.
  • WebSocket.onopen: 연결이 성공적으로 열렸을 때 호출되는 이벤트 핸들러입니다.
  • WebSocket.onclose: 연결이 닫혔을 때 호출되는 이벤트 핸들러입니다.
  • WebSocket.onerror: 오류가 발생했을 때 호출되는 이벤트 핸들러입니다.

📦 메시징 구조

웹소켓은 프레임 단위로 데이터를 전송합니다.

클라이언트에서 전송된 데이터는 웹소켓 프로토콜에 따라 프레임 형식으로 패키징되어 서버로 전송됩니다.

이 과정은 매우 빠르며 각 프레임은 기본적으로 데이터메타데이터를 포함합니다.

예를 들어, 클라이언트가 "안녕하세요"라는 메시지를 보내면 이 메시지는 웹소켓 프레임으로 감싸져 전송됩니다.

서버는 이 프레임을 해석하여 원래 메시지를 읽고, 필요에 따라 다른 클라이언트에게 전달할 수 있습니다.

🔄 상태 유지 및 세션 관리

웹소켓은 연결이 열려 있는 동안 상태를 유지합니다.

이로 인해 클라이언트와 서버는 지속적으로 데이터를 주고받을 수 있으며, 실시간으로 사용자 상호작용을 처리할 수 있습니다.

예를 들어, 채팅 애플리케이션에서는 사용자가 메시지를 입력할 때마다 서버와 클라이언트 간의 연결이 끊기지 않으므로 사용자 경험이 원활하게 유지됩니다.

이러한 상태 유지는 실시간 상호작용이 중요한 애플리케이션에서 큰 장점으로 작용합니다.


🔄 Pub/Sub 구조란?

Pub/Sub 구조는 발행(Publish)하는 주체 구독(Subscribe)하는 주체를 분리하여 이벤트 기반 메시징을 가능하게 하는 패턴입니다. 채팅 서비스, 주식 데이터 스트리밍, 실시간 알림 시스템 등에서 자주 사용됩니다.

 

📌 Pub/Sub의 핵심 개념

  • Publisher (발행자) → 메시지를 특정 채널(Topic)에 발행
  • Broker (중개자, Message Queue) → 메시지를 적절한 수신자에게 라우팅
  • Subscriber (구독자) → 특정 채널을 구독하고, 메시지를 수신

💡 왜 Pub/Sub 구조를 사용할까?

  • 발행자와 구독자가 직접 연결되지 않아도 된다 → 분산 시스템에서 강력한 확장성 제공
  • 비동기 이벤트 기반 아키텍처를 쉽게 구축 가능
  • 서버 부하 감소 → 다수의 클라이언트가 데이터를 구독하더라도 성능 저하가 적음

🔍 WebSocket 기반 Pub/Sub 동작 원리

WebSocket과 Pub/Sub 구조가 결합되면, 서버는 Publisher(발행자)로, 클라이언트는 Subscriber(구독자)로 동작하게 됩니다. 이를 메시지 브로커(Redis, Kafka 등)와 함께 활용하면 훨씬 더 강력한 시스템을 구축할 수 있습니다.

 

📌 동작 과정

1️⃣ 클라이언트가 WebSocket 서버에 연결

2️⃣ 클라이언트가 특정 채널(Topic)을 구독 (Subscribe)

3️⃣ 서버가 특정 클라이언트에게 메시지를 발행 (Publish)

4️⃣ 메시지 브로커(Redis 등)를 활용해 다수의 클라이언트에게 메시지 전달


📡 프론트엔드 & 백엔드 실시간 웹소켓 통신: 클라이언트부터 S3 파일 전송까지

 

웹소켓을 활용하면 클라이언트와 서버 간 양방향 실시간 통신을  간단하게 구현할 수 있어요.

이번 글에서는 웹소켓(WebSocket)을 활용해 실시간 메시지를 주고받는 퍼블리시-구독(pub/sub) 방식의 채팅 시스템을 구현하고,

AWS S3를 활용한 파일 업로드까지 다뤄볼게요.

🎯 1. 프론트엔드에서 웹소켓 연결하기

웹소켓을 사용하는 방법은 생각보다 간단해요.

브라우저에서 제공하는 WebSocket API를 활용하면 바로 구현할 수 있어요.

🛠 웹소켓 기본 코드

const socket = new WebSocket('ws://서버주소:포트/ws');

// ✅ 웹소켓 연결됨
socket.addEventListener('open', () => {
  console.log('✅ WebSocket 연결 성공!');
  socket.send('안녕하세요, 서버!');
});

// 📩 메시지 수신
socket.addEventListener('message', (event) => {
  console.log('📩 받은 메시지:', event.data);
});

// 🔴 연결 종료
socket.addEventListener('close', () => {
  console.log('🔴 WebSocket 연결 종료');
});

// ⚠️ 오류 발생
socket.addEventListener('error', (event) => {
  console.error('⚠️ WebSocket 오류:', event);
});

👉 이 코드의 핵심은?

  • open: 웹소켓 연결 성공 시 실행
  • message: 서버에서 메시지를 받으면 실행
  • close: 웹소켓 연결이 종료될 때 실행
  • error: 오류가 발생했을 때 실행

📌 2. 웹소켓 상태 관리하기 (React 예제)

웹소켓 연결 상태와 메시지를 관리하려면 상태 관리 라이브러리를 활용하면 좋아요.

✅ React 활용 코드

import React, { useEffect, useState } from 'react';

const useChat = () => {
  const [socket, setSocket] = useState(null);
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    const ws = new WebSocket('ws://서버주소:포트/ws');
    setSocket(ws);

    ws.addEventListener('message', (event) => {
      setMessages((prevMessages) => [...prevMessages, event.data]);
    });

    ws.addEventListener('close', () => {
      console.log('🔴 웹소켓 연결 종료');
    });

    // 컴포넌트 언마운트 시 소켓 연결 종료
    return () => {
      ws.close();
    };
  }, []);

  const sendMessage = (message) => {
    if (socket) {
      socket.send(message);
    }
  };

  return { messages, sendMessage };
};

const ChatComponent = () => {
  const { messages, sendMessage } = useChat();
  const [inputMessage, setInputMessage] = useState('');

  const handleSend = () => {
    sendMessage(inputMessage);
    setInputMessage('');
  };

  return (
    <div>
      <div>
        {messages.map((msg, index) => (
          <div key={index}>{msg}</div>
        ))}
      </div>
      <input
        type="text"
        value={inputMessage}
        onChange={(e) => setInputMessage(e.target.value)}
      />
      <button onClick={handleSend}>전송</button>
    </div>
  );
};

export default ChatComponent;

 

👉 이 코드의 핵심은?

1️⃣ useChat 훅:

  • socket과 messages 상태를 관리합니다.
  • useEffect를 사용하여 웹소켓을 연결하고 메시지 수신 이벤트를 설정합니다.
  • 컴포넌트가 언마운트될 때 웹소켓 연결을 종료합니다.

2️⃣ ChatComponent:

  • 메시지를 입력하고 전송할 수 있는 UI를 제공합니다.
  • handleSend 함수로 메시지를 전송하고 입력 필드를 초기화합니다.

이렇게하면 프론트엔드는 준비 완료입니다!


🚀 3. 백엔드(WebSocket + Spring + Kafka) 설정하기

웹소켓 서버는 Spring Boot + STOMP 프로토콜을 사용해 설정할 수 있어요.

또한, Kafka를 활용해 메시지를 비동기적으로 처리하면 확장성이 훨씬 좋아집니다.

✅ 웹소켓 설정 코드

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic"); // 메시지 브로커 설정
        config.setApplicationDestinationPrefixes("/app"); // 클라이언트 메시지 라우팅
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws").setAllowedOrigins("*").withSockJS(); // 클라이언트 엔드포인트
    }
}

👉 이 코드의 핵심은?

    • /ws 엔드포인트로 클라이언트가 연결
    • /app으로 들어오는 메시지를 /topic으로 브로드캐스트
    • STOMP 프로토콜을 활용해 메시지를 구독/발행

📨 4. Kafka를 활용한 메시지 처리

 

웹소켓을 단순히 연결하는 것뿐만 아니라, Kafka를 활용해 메시지를 비동기적으로 관리하면 훨씬 효율적이에요.

✅ Kafka 프로듀서 코드

@Autowired
private KafkaTemplate<String, String> kafkaTemplate;

@MessageMapping("/sendMessage")
public void sendMessage(String message) {
    kafkaTemplate.send("chat-topic", message); // 메시지를 카프카에 저장
}

 

📌 클라이언트가 /app/sendMessage로 메시지를 보내면

→ Kafka의 chat-topic에 메시지가 저장됨


🔄 5. 프론트엔드에서 메시지 구독하기

이제 클라이언트에서 Kafka로부터 메시지를 받아 웹소켓을 통해 UI를 업데이트해볼까요?

✅ 채팅 UI에 메시지 표시

function displayMessage(message) {
  const messageContainer = document.getElementById('messageContainer');
  const newMessage = document.createElement('div');
  newMessage.textContent = message;
  messageContainer.appendChild(newMessage);
}
  1.  

Kafka에서 메시지를 받아온 후, displayMessage() 함수를 호출하면 화면에 메시지가 추가돼요!


📂 6. 파일 업로드 (AWS S3 연동)

실시간 채팅에서 파일 공유 기능을 추가하고 싶다면 AWS S3를 활용하면 좋아요.

 

✅ 클라이언트에서 파일 업로드 요청

const fileInput = document.getElementById('fileInput');
const file = fileInput.files[0];

const formData = new FormData();
formData.append('file', file);

fetch('http://서버주소/api/upload', {
  method: 'POST',
  body: formData,
});

 

📌 FormData를 활용해 파일을 서버로 전송

 

✅ Spring에서 파일을 S3에 업로드

@PostMapping("/api/upload")
public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) {
    String fileUrl = s3Service.uploadFile(file);
    return ResponseEntity.ok(fileUrl);
}

 

 

📌 클라이언트에서 보낸 파일을 S3에 저장하고 파일 URL 반환


📈 성능 최적화 및 고급 활용법

아래는 웹소켓 통신의 부하를 줄이고 속도와 성능을 개선하는 백엔드 방법입니다!

✅ 1) 다중 서버에서 WebSocket Pub/Sub 구현

  • Redis의 클러스터링(Clustering) 기능을 사용하여 여러 개의 WebSocket 서버가 메시지를 공유할 수 있습니다.
  • Kafka + WebSocket을 활용하면 대량의 데이터를 처리하는 실시간 시스템을 구축할 수 있습니다.

✅ 2) 메시지 브로커 활용

  • Redis Pub/Sub: 단순한 메시징 시스템 구현 가능
  • Kafka: 대량의 이벤트 스트리밍 시스템 구축 가능
  • RabbitMQ: 메시지 큐잉을 활용한 대기열 기반 메시징 가능

✅ 3) WebSocket의 대체 기술

  • SSE (Server-Sent Events): 단방향 스트리밍 (서버 → 클라이언트)
  • GraphQL Subscriptions: WebSocket을 GraphQL API와 함께 사용

🎯 7. 마무리 & 정리

 

🚀 프론트엔드

✅ WebSocket은 양방향 실시간 통신이 가능

✅ Pub/Sub 패턴은 분산 시스템에서 확장성을 보장

실시간 채팅, 주식 데이터, 알림 시스템 등에 필수적으로 사용됨

 

🛠 백엔드

✅ Spring Boot + STOMP로 웹소켓 서버 구축

Redis, Kafka 등의 메시지 브로커와 결합하면 성능 최적화 가능

✅ AWS S3로 파일 업로드

 

🌷 전설의 개발자가 되어봅시당! 🌷

728x90
반응형