| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | ||||
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |
- 메모리관리
- 정렬
- 코딩공부
- 코딩인터뷰
- 자바개발
- 자바
- 백준
- JVM
- 클린코드
- 프로그래밍기초
- 자료구조
- 객체지향
- 코딩테스트준비
- 개발자취업
- HashMap
- 코딩테스트
- 파이썬
- Java
- 프로그래머스
- 개발공부
- 자바공부
- 가비지컬렉션
- 알고리즘공부
- 자바기초
- 자바프로그래밍
- 예외처리
- 개발자팁
- 멀티스레드
- 코딩테스트팁
- 알고리즘
- Today
- Total
코드 한 줄의 기록
Java 소켓과 HTTP 통신: 기초부터 클라이언트 구현까지 완벽 정리 본문
네트워크 프로그래밍을 배우면서 "소켓"이라는 용어를 처음 접하는 개발자들은 이게 정확히 뭔지, HTTP와는 어떻게 다른지 헷갈리는 경우가 많습니다. 저도 처음엔 그랬거든요. 하지만 현장에서 실제로 서버와 클라이언트를 만들어보면서 이 두 개념의 차이가 얼마나 중요한지 깨달았습니다.
이 글에서는 순수 소켓 프로그래밍부터 시작해서, 현대적인 HTTP 클라이언트 구현까지 단계별로 살펴보겠습니다. 이론만 아니라 실제로 동작하는 코드를 통해, 내가 배운 걸 다른 개발자들도 쉽게 이해할 수 있도록 정리해봤습니다.
소켓(Socket)이란 무엇인가?
먼저 가장 기본적인 질문부터 답해봅시다. 소켓은 정확히 뭘까요?
소켓은 네트워크상에서 두 프로그램 간의 통신을 가능하게 해주는 끝점(endpoint)입니다. 더 정확하게는, IP 주소와 포트 번호로 식별되는 네트워크 통신 지점이라고 생각하면 됩니다.
예를 들어 생각해보면, 내 컴퓨터에서 서버로 데이터를 보낸다고 하면
- 내 IP 주소: 192.168.1.10
- 내 포트: 12345 (임시로 할당된 클라이언트 포트)
- 서버 IP: 192.168.1.100
- 서버 포트: 8080
이런 식으로 '출발지:포트'와 '목적지:포트'의 조합이 하나의 소켓을 형성하는 거죠.
소켓의 종류: TCP와 UDP
소켓 통신에는 두 가지 방식이 있습니다.
- TCP(Transmission Control Protocol)는 연결 기반의 신뢰성 있는 통신을 제공합니다. 데이터가 반드시 순서대로 도착하며, 손실되지 않도록 보장합니다. 3-way handshake라는 연결 과정을 거쳐야 하지만, 안정성이 최우선인 상황에 적합합니다. 웹 서버, 파일 전송, 데이터베이스 연결 등에서 사용됩니다.
- UDP(User Datagram Protocol)은 비연결 기반으로 빠른 전송에 중점을 두고 있습니다. 연결 과정이 없어서 오버헤드가 적지만, 데이터 손실이나 순서 변경이 발생할 수 있습니다. 온라인 게임, 영상 스트리밍, 센서 데이터 수집 같은 실시간성이 중요한 분야에서 주로 사용됩니다.
이 글에서는 TCP 소켓에 집중하며, 대부분의 기본 예제도 TCP 기반입니다.
TCP 소켓 통신의 동작 원리
TCP 기반 소켓 통신이 어떻게 이루어지는지 흐름을 이해하는 것이 중요합니다. 마치 두 사람이 전화통화를 하는 것처럼 생각하면 됩니다.
서버 측 동작
- 소켓 생성: ServerSocket 객체를 만들어 특정 포트에 바인딩합니다
- 리스닝: 클라이언트의 연결 요청을 기다립니다 (accept() 메서드 사용)
- 3-way handshake: 클라이언트와의 연결이 확립됩니다
- 데이터 송수신: InputStream과 OutputStream을 통해 데이터를 주고받습니다
- 연결 종료: 통신이 끝나면 소켓을 닫습니다
클라이언트 측 동작
- 소켓 생성: Socket 객체를 만들어 서버의 IP와 포트를 지정합니다
- 연결 요청: 서버에 연결을 시도합니다
- 3-way handshake: 서버와의 연결이 확립됩니다
- 데이터 송수신: InputStream과 OutputStream을 통해 데이터를 주고받습니다
- 연결 종료: 통신이 끝나면 소켓을 닫습니다
3-way handshake는 TCP에서 연결을 안전하게 확립하기 위한 과정인데, 정확히는
- SYN: 클라이언트가 서버에 연결 요청을 보냅니다
- SYN-ACK: 서버가 요청을 수락하고 응답을 보냅니다
- ACK: 클라이언트가 서버의 응답을 확인합니다
이 과정이 완료되어야 실제 데이터 전송이 가능하며, 전체 흐름은 매우 중요하니 반드시 숙지하세요.
Java 소켓 클라이언트 구현하기
1단계: 간단한 Echo 클라이언트
먼저 서버로 메시지를 보내고 응답을 받는 가장 기본적인 클라이언트 예제입니다.
import java.io.*;
import java.net.Socket;
public class SimpleSocketClient {
public static void main(String[] args) {
String serverIP = "127.0.0.1"; // 서버 IP
int serverPort = 9999; // 서버 포트
try {
// 1. 서버에 연결
Socket socket = new Socket(serverIP, serverPort);
System.out.println("서버 연결됨: " + socket.getRemoteSocketAddress());
// 2. 스트림 생성
OutputStream outputStream = socket.getOutputStream();
InputStream inputStream = socket.getInputStream();
// 3. 텍스트 전송용 래퍼
PrintWriter writer = new PrintWriter(new OutputStreamWriter(outputStream), true);
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
// 4. 메시지 보내기
String message = "Hello from Client!";
writer.println(message);
System.out.println("보낸 메시지: " + message);
// 5. 응답 받기
String response = reader.readLine();
System.out.println("서버 응답: " + response);
// 6. 종료
reader.close();
writer.close();
socket.close();
System.out.println("연결 종료");
} catch (IOException e) {
System.err.println("통신 오류: " + e.getMessage());
}
}
}
이 예제를 실행하려면, 먼저 동작하는 서버가 필요합니다. 서버도 간단히 아래 참고용 코드를 참고하세요.
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class SimpleSocketServer {
public static void main(String[] args) {
int port = 9999;
try {
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("서버 대기중: 포트 " + port);
while (true) {
Socket clientSocket = serverSocket.accept();
System.out.println("클라이언트 접속: " + clientSocket.getRemoteSocketAddress());
InputStream inputStream = clientSocket.getInputStream();
OutputStream outputStream = clientSocket.getOutputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
PrintWriter writer = new PrintWriter(new OutputStreamWriter(outputStream), true);
String message = reader.readLine();
System.out.println("받은 메시지: " + message);
writer.println("Echo: " + message);
reader.close();
writer.close();
clientSocket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2단계: 지속적인 통신 환경
한 번만 통신하는 것이 아니라, 여러 번 교류하는 상황을 위한 클라이언트입니다. 아래 참고하세요.
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
public class InteractiveSocketClient {
private Socket socket;
private PrintWriter writer;
private BufferedReader reader;
public InteractiveSocketClient(String serverIP, int serverPort) throws IOException {
socket = new Socket(serverIP, serverPort);
writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
}
public void start() throws IOException {
Scanner scanner = new Scanner(System.in);
String input;
System.out.println("'exit'입력 시 종료됩니다.");
while (true) {
System.out.print("메시지 입력: ");
input = scanner.nextLine();
if ("exit".equalsIgnoreCase(input))
break;
writer.println(input);
System.out.println("서버 답변: " + reader.readLine());
}
close();
}
private void close() throws IOException {
reader.close();
writer.close();
socket.close();
System.out.println("연결 종료");
}
public static void main(String[] args) {
try {
new InteractiveSocketClient("127.0.0.1", 9999).start();
} catch (IOException e) {
System.err.println("클라이언트 오류: " + e.getMessage());
}
}
}
HTTP와 소켓 차이점 정리
| 항목 | 소켓 통신 | HTTP 통신 |
| 추상화 수준 | 낮음 (운영체제 제공, 개발자 직접 제어) | 높음 (라이브러리/프레임워크 위에서 동작) |
| 통신 방식 | 양방향, 지속 연결 | 요청-응답, 비지속적 (기본) |
| 상태 유지 | 유지 가능 | 기본적으로 유지 안 함 |
| 메시지 형식 | 개발자 정의 | HTTP 표준 기반 |
| 사용 사례 | 게임, 실시간 채팅, IoT | 웹 서비스, API, 데이터 전달 |
Java 네트워크 프로그래밍은 기초 소켓부터 현대적 HTTP까지 이해하는 것이 매우 중요합니다. 상황에 맞는 도구 선택과 올바른 자원 관리 습관이 필요하며, 이를 토대로 안정적이고 효율적인 네트워크 서비스를 구축할 수 있습니다. 이번 글이 여러분의 네트워크 이해와 실습에 도움이 되길 바라며, 더 깊은 내용은 계속 공부하면서 익혀보세요.
자바 파일 및 디렉터리 완벽 가이드: Path와 Files로 배우는 실전 활용법
프로젝트를 진행할 때 파일을 생성하거나, 디렉터리 내 파일을 탐색하거나, 복사·삭제·이동하는 작업이 생각보다 자주 필요하다. 과거에는 File 클래스를 많이 썼지만, 자바 7에서 새로 도입된 N
byteandbit.tistory.com
'JAVA' 카테고리의 다른 글
| Java 파일 유틸리티 개발 시 성능, 자원, 예외처리를 모두 고려하는 방법 (0) | 2025.11.10 |
|---|---|
| Java I/O 완벽 가이드: 바이트와 문자 스트림의 차이를 정확히 이해하기 (0) | 2025.11.09 |
| 자바 파일 및 디렉터리 완벽 가이드: Path와 Files로 배우는 실전 활용법 (0) | 2025.11.06 |
| Java Stream Collectors 완전 정복: groupingBy와 partitioningBy로 데이터 그룹화하기 (0) | 2025.11.06 |
| Java Optional로 NPE를 잡아보자 - 실무 활용 가이드 (0) | 2025.11.05 |