공통 질문
1. 웹소켓의 주요 특징은 무엇인가요?
2. 웹소켓은 어떻게 동작하나요?
3. 웹소켓과 HTTP의 차이점은 무엇인가요?
4. 웹소켓의 핸드셰이크 과정이란 무엇인가요?
5. 웹소켓의 활용 사례는 무엇인가요?
6. 웹소켓의 단점은 무엇인가요?
7. 웹소켓과 폴링(Polling), 롱 폴링(Long Polling)의 차이는 무엇인가요?
8. 웹소켓과 REST API를 언제 사용해야 하나요?
9. 웹소켓 보안을 어떻게 보장하나요?
10. 웹소켓 연결 상태 코드
11. 웹소켓의 대안 기술은 무엇인가요?
12. 웹소켓을 활용한 채팅 프로젝트
웹소켓(WebSocket)이란?
https://developer.mozilla.org/ko/docs/Web/API/WebSockets_API
웹 소켓 - Web API | MDN
웹 소켓은 사용자의 브라우저와 서버 사이의 인터액티브 통신 세션을 설정할 수 있게 하는 고급 기술입니다. 개발자는 웹 소켓 API를 통해 서버로 메시지를 보내고 서버의 응답을 위해 서버를 폴
developer.mozilla.org
웹 소켓은 사용자의 브라우저와 서버 사이의 인터액티브 통신 세션을 설정할 수 있게 하는 고급 기술입니다.
개발자는 웹 소켓 API를 통해 서버로 메시지를 보내고
서버의 응답을 위해 서버를 폴링하지 않고도 이벤트 중심 응답을 받는 것이 가능합니다.
interface
웹 소켓 서버로 연결하고 연결을 통해 데이터를 보내고 받는 기본 인터페이스
![]() |
리스너![]() |
리스너 - 핑, 퐁![]() |
요청![]() |
jdk.internal.net.http.websocket.WebSocketImpl
![]() |
![]() |
![]() |
핑, 퐁![]() |
![]() |
![]() |
연결이 종료 되었을 때 웹 소켓 객체에 의해 전달된 이벤트
서버로 부터 메시지가 수신 되었을 때 웹 소켓 객체에 의해 전달된 이벤트
자바에서 소켓
client: Socket
클라이언트 코드
import static util.MyLogger.log;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
public class ClientV1 {
private static final int PORT = 12345;
public static void main(String[] args) throws IOException {
log("클라이언트 시작");
Socket socket = new Socket("localhost", PORT);
DataInputStream input = new DataInputStream(socket.getInputStream());
DataOutputStream output = new DataOutputStream(socket.getOutputStream());
log("소캣 연결: " + socket);
// 서버에게 문자 보내기
String toSend = "Hello";
output.writeUTF(toSend);
log("client -> server: " + toSend);
// 서버로부터 문자 받기
String received = input.readUTF();
log("client <- server: " + received);
// 자원 정리
log("연결 종료: " + socket);
input.close();
output.close();
socket.close();
}
}
java.net.Socket
실제 데이터를 주고 받는 객체

생성자

데이터를 보낸다

데이터를 받는다

java.net.SocketImplFactory

서버: ServerSocket
서버 소켓: client <--> Server 간 TCP 연결만 지원한다
- 실제 정보를 주고 받으려면, Soket 객체가 필요하다
서버 코드
import static util.MyLogger.log;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerV1 {
private static final int PORT = 12345;
public static void main(String[] args) throws IOException {
log("서버 시작");
ServerSocket serverSocket = new ServerSocket(PORT);
log("서버 소켓 시작 - 리스닝 포트: " + PORT);
Socket socket = serverSocket.accept();
log("소켓 연결: " + socket);
DataInputStream input = new DataInputStream(socket.getInputStream());
DataOutputStream output = new DataOutputStream(socket.getOutputStream());
// 클라이언트로부터 문자 받기
String received = input.readUTF();
log("client -> server: " + received);
// 클라이언트에게 문자 보내기
String toSend = received + " World!";
output.writeUTF(toSend);
log("client <- server: " + toSend);
// 자원 정리
log("연결 종료: " + socket);
input.close();
output.close();
socket.close();
serverSocket.close();
}
}
java.net.ServerSocket

java.net.SocketImpl

1. 웹소켓의 주요 특징은 무엇인가요?
- 양방향 통신: 클라이언트와 서버가 서로 데이터를 주고받을 수 있음.
- 지속적인 연결: 연결이 한 번 맺어지면 클라이언트와 서버가 종료하거나 명시적으로 닫기 전까지 유지됨.
- 낮은 오버헤드: HTTP 요청/응답 헤더의 오버헤드가 줄어들어 네트워크 효율성이 높음.
- 실시간 데이터 전송: 지연 시간이 낮아 채팅, 실시간 알림, 게임 등에 적합.
- 표준 프로토콜: ws:// 또는 wss://를 사용하여 표준화된 방식으로 구현.
2. 웹소켓은 어떻게 동작하나요?
- 핸드셰이크:
- 클라이언트는 HTTP를 통해 서버에 연결 요청을 보냄.
- 요청 헤더에 Upgrade: websocket과 같은 필드를 포함.
- 연결 수립:
- 서버가 요청을 수락하면 연결이 웹소켓 프로토콜로 업그레이드됨.
- 데이터 교환:
- 연결이 맺어진 상태에서 클라이언트와 서버는 양방향으로 데이터를 교환.
- 연결 종료:
- 클라이언트나 서버가 명시적으로 연결을 종료할 수 있음.
3. 웹소켓과 HTTP의 차이점은 무엇인가요?
4. 웹소켓의 핸드셰이크 과정이란 무엇인가요?
클라이언트가 HTTP 요청을 보냄
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
서버가 요청을 수락:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
5. 웹소켓의 활용 사례는 무엇인가요?
- 채팅 애플리케이션: 양방향 통신을 통해 실시간 메시지 송수신.
- 실시간 알림: SNS, 이메일, 뉴스 등에서 알림 제공.
- 게임: 멀티플레이어 게임에서 실시간 상태 동기화.
- 금융 거래: 주식 시세, 암호화폐 등 실시간 가격 정보 제공.
- IoT: 센서 데이터 실시간 모니터링.
6. 웹소켓의 단점은 무엇인가요?
- 서버 리소스 소모: 지속적인 연결 유지로 서버의 메모리와 CPU 부담이 증가.
- 방화벽 제한: 일부 네트워크 환경에서 웹소켓 연결이 차단될 수 있음.
- 복잡성 증가: HTTP보다 구현 및 디버깅이 복잡.
7. 웹소켓과 폴링(Polling), 롱 폴링(Long Polling)의 차이는 무엇인가요?
웹소켓 - 서버 코드
// Spring WebSocket 서버
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new WebSocketHandler(), "/ws");
}
}
class WebSocketHandler extends TextWebSocketHandler {
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
System.out.println("WebSocket connection established.");
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
System.out.println("Received message: " + message.getPayload());
session.sendMessage(new TextMessage("Hello from server!"));
}
@Override
public void afterConnectionClosed(WebSocketSession session, org.springframework.web.socket.CloseStatus status) {
System.out.println("WebSocket connection closed.");
}
}
웹소켓 - 클라이언트 코드
import javax.websocket.*;
import java.net.URI;
@ClientEndpoint
public class WebSocketClient {
@OnOpen
public void onOpen(Session session) {
System.out.println("Connected to server.");
try {
session.getBasicRemote().sendText("Hello Server!");
} catch (Exception e) {
e.printStackTrace();
}
}
@OnMessage
public void onMessage(String message) {
System.out.println("Received from server: " + message);
}
@OnClose
public void onClose() {
System.out.println("Connection closed.");
}
public static void main(String[] args) throws Exception {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
container.connectToServer(WebSocketClient.class, new URI("ws://localhost:8080/ws"));
}
}
폴링(Polling)
폴링은 클라이언트가 일정 주기로 HTTP 요청을 서버에 보내는 방식입니다.
폴링(Polling)
폴링은 클라이언트가 일정 주기로 HTTP 요청을 서버에 보내는 방식입니다.
폴링 - 서버
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
@RestController
public class PollingController {
@GetMapping("/polling")
public String getPollingData() {
return "Current server time: " + LocalDateTime.now();
}
}
폴링 - 클라이언트
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
public class PollingClient {
public static void main(String[] args) throws InterruptedException {
while (true) {
try {
URL url = new URL("http://localhost:8080/polling");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
Scanner scanner = new Scanner(connection.getInputStream());
while (scanner.hasNext()) {
System.out.println(scanner.nextLine());
}
scanner.close();
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
// 5초마다 요청
Thread.sleep(5000);
}
}
}
롱 폴링(Long Polling)
롱 폴링에서는 클라이언트가 요청을 보내면 서버가 새로운 데이터가 생길 때까지 응답을 지연합니다.
롱폴링- 서버
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.CompletableFuture;
@RestController
public class LongPollingController {
@GetMapping("/long-polling")
public CompletableFuture<String> getLongPollingData() {
// 새로운 데이터가 생길 때까지 응답 지연 (예: 10초 후에 데이터 반환)
return CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(10000); // 10초 지연
} catch (InterruptedException e) {
e.printStackTrace();
}
return "New data available at: " + System.currentTimeMillis();
});
}
}
롱폴링 - 클라이언트
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
public class LongPollingClient {
public static void main(String[] args) {
while (true) {
try {
URL url = new URL("http://localhost:8080/long-polling");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
Scanner scanner = new Scanner(connection.getInputStream());
while (scanner.hasNext()) {
System.out.println(scanner.nextLine());
}
scanner.close();
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
8. 웹소켓과 REST API를 언제 사용해야 하나요?
- 웹소켓 사용 시점:
- 데이터가 실시간으로 교환되어야 하는 경우.
- 이벤트 기반 시스템 (ex. 채팅, 주식 시세).
- REST API 사용 시점:
- 요청-응답 기반의 정적인 데이터 전송.
- CRUD 작업 (ex. 사용자 정보 조회, 데이터 저장).
9. 웹소켓 보안을 어떻게 보장하나요?
- TLS 사용: wss://를 사용하여 데이터 암호화.
- 인증: 핸드셰이크 시 인증 토큰이나 API 키를 포함.
- CORS 설정: 허용된 도메인에서만 접근 가능하도록 설정.
- DoS 공격 방지: 연결 수 제한, 시간 초과 설정.
10. 웹소켓 연결 상태 코드
웹소켓의 연결 종료 시 사용되는 상태 코드:
- 1000: 정상 종료.
- 1001: 서버 종료.
- 1002: 프로토콜 에러.
- 1003: 데이터 형식 에러.
11. 웹소켓의 대안 기술은 무엇인가요?
- Server-Sent Events (SSE):
- 서버에서 클라이언트로 단방향 스트리밍.
- 브라우저 지원이 좋고, HTTP 기반.
- 폴링/롱 폴링:
- 간단한 구현으로 주기적인 서버 데이터 요청 가능.
12. 웹소켓을 활용한 채팅 프로젝트
1) 문제: accept 블로킹
1) 해결: accept 블로킹 - 역할 분리
블로킹이 되는 부분은 별도의 스레드로 나누어 실행
2) 문제: 읽기&쓰기 기다림
채팅은 실시간으로 대화를 주고 받아야한다.
그러나 입력이 있을때까지 무한 대기해야한다.
고객
public class ClientV6 {
private static final int PORT = 12345;
public static void main(String[] args) throws IOException {
log("클라이언트 시작");
try (Socket socket = new Socket("localhost", PORT);
DataInputStream input = new DataInputStream(socket.getInputStream());
DataOutputStream output = new DataOutputStream(socket.getOutputStream())) {
log("소캣 연결: " + socket);
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.print("전송 문자: ");
String toSend = scanner.nextLine();
// 서버에게 문자 보내기
output.writeUTF(toSend);
log("client -> server: " + toSend);
if (toSend.equals("exit")) {
break;
}
// 서버로부터 문자 받기
String received = input.readUTF();
log("client <- server: " + received);
}
} catch (IOException e) {
log(e);
}
}
}
서버
package network.tcp.v6;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import static util.MyLogger.log;
public class ServerV6 {
private static final int PORT = 12345;
public static void main(String[] args) throws IOException {
log("서버 시작");
SessionManagerV6 sessionManager = new SessionManagerV6();
ServerSocket serverSocket = new ServerSocket(PORT);
log("서버 소켓 시작 - 리스닝 포트: " + PORT);
// ShutdownHook 등록
ShutdownHook shutdownHook = new ShutdownHook(serverSocket, sessionManager);
Runtime.getRuntime().addShutdownHook(new Thread(shutdownHook, "shutdown"));
try {
while (true) {
Socket socket = serverSocket.accept(); // 블로킹
log("소켓 연결: " + socket);
SessionV6 session = new SessionV6(socket, sessionManager);
Thread thread = new Thread(session);
thread.start();
}
} catch (IOException e) {
log("서버 소켓 종료: " + e);
}
}
static class ShutdownHook implements Runnable {
private final ServerSocket serverSocket;
private final SessionManagerV6 sessionManager;
public ShutdownHook(ServerSocket serverSocket, SessionManagerV6 sessionManager) {
this.serverSocket = serverSocket;
this.sessionManager = sessionManager;
}
@Override
public void run() {
log("shutdownHook 실행");
try {
sessionManager.closeAll();
serverSocket.close();
Thread.sleep(1000); // 자원 정리 대기
} catch (Exception e) {
e.printStackTrace();
System.out.println("e = " + e);
}
}
}
}
2) 해결: 읽기&쓰기 기다림 - 쓰레드 분리
전체적 설계
'학습 기록 (Learning Logs) > CS Study' 카테고리의 다른 글
리눅스 서버 관리 기초 (0) | 2025.01.13 |
---|---|
sort (0) | 2025.01.09 |
AOP (0) | 2024.12.23 |
[Note]redis (0) | 2024.12.16 |
[Note]In-memory cache-basic (0) | 2024.12.16 |