네트워크 통신의 핵심 프로토콜인 TCP와 UDP는 개발자 면접에서 자주 등장하는 주제입니다.
이 글에서는 두 프로토콜의 차이점을 실무 예제와 함께 상세히 설명하고, 면접에서 활용할 수 있는 답변 예시까지 제공합니다.
TCP와 UDP 기본 개념
TCP (Transmission Control Protocol)
TCP는 연결 지향적 프로토콜로, 데이터를 주고받기 전에 연결을 설정하고 데이터 전송이 완료되면 연결을 종료합니다. 데이터의 순서와 무결성을 보장하며, 손실된 패킷은 재전송합니다.
UDP (User Datagram Protocol)
UDP는 비연결 지향적 프로토콜로, 연결 설정 없이 데이터를 전송합니다. 데이터의 도착 순서나 무결성을 보장하지 않지만, 오버헤드가 적어 빠른 전송이 가능합니다.
TCP vs UDP 핵심 차이점
특성 | TCP | UDP |
---|---|---|
연결 방식 | 연결 지향적 (Connection-oriented) | 비연결 지향적 (Connectionless) |
신뢰성 | 높음 (패킷 손실 시 재전송) | 낮음 (패킷 손실에 대응하지 않음) |
순서 보장 | 데이터 순서 보장 | 데이터 순서 보장하지 않음 |
속도 | 상대적으로 느림 | 상대적으로 빠름 |
오버헤드 | 높음 (헤더 크기 20-60 바이트) | 낮음 (헤더 크기 8 바이트) |
흐름/혼잡 제어 | 지원 | 지원하지 않음 |
사용 사례 | 웹, 이메일, 파일 전송 | 스트리밍, 게임, VoIP |
TCP의 동작 원리와 3-way 핸드셰이크
TCP 연결은 '3-way 핸드셰이크'라는 과정을 통해 설정됩니다:
- SYN (Synchronize): 클라이언트가 서버에 SYN 패킷을 보내 연결 요청
- SYN-ACK (Synchronize-Acknowledge): 서버가 클라이언트의 요청을 수락하고 SYN-ACK 패킷 전송
- ACK (Acknowledge): 클라이언트가 서버의 응답을 확인하고 ACK 패킷 전송
이 과정을 통해 두 호스트 간에 신뢰할 수 있는 연결이 설정됩니다.
클라이언트 서버
| |
|------- SYN (seq=x) ----->|
| |
|<-- SYN-ACK (seq=y,ack=x+1) --|
| |
|---- ACK (ack=y+1) ------>|
| |
|--------- 데이터 ---------->|
| |
TCP 연결 종료 (4-way 핸드셰이크)
TCP 연결 종료는 '4-way 핸드셰이크'를 통해 이루어집니다:
- FIN (Finish): 클라이언트가 서버에 FIN 패킷을 보내 연결 종료 요청
- ACK: 서버가 클라이언트의 요청을 확인하고 ACK 패킷 전송
- FIN: 서버가 연결 종료를 준비하고 FIN 패킷 전송
- ACK: 클라이언트가 서버의 연결 종료를 확인하고 ACK 패킷 전송
UDP의 동작 원리와 특징
UDP는 연결 설정 과정 없이 데이터를 전송합니다:
- 데이터그램을 패킷으로 나눔
- 헤더를 추가하여 목적지 정보 포함
- 패킷을 네트워크로 전송
- 수신 측에서는 수신한 패킷을 재조립하지만, 패킷 손실이나 순서 변경에 대응하지 않음
클라이언트 서버
| |
|--------- 데이터 ---------->|
| |
|<-------- 데이터 ----------|
| |
TCP 실무 활용 사례
1. 웹 브라우징 (HTTP/HTTPS)
웹 페이지를 로드할 때 브라우저와 서버는 TCP를 통해 통신합니다. 이는 HTML, CSS, JavaScript와 같은 웹 리소스가 정확히 전송되어야 하기 때문입니다.
// Node.js에서 TCP 서버 구현 예제
const net = require('net');
const server = net.createServer((socket) => {
console.log('클라이언트 연결됨');
socket.on('data', (data) => {
console.log('데이터 수신:', data.toString());
socket.write('데이터를 받았습니다.');
});
socket.on('end', () => {
console.log('클라이언트 연결 종료');
});
});
server.listen(8080, () => {
console.log('서버가 8080 포트에서 실행 중입니다.');
});
2. 데이터베이스 연결
대부분의 데이터베이스 시스템(MySQL, PostgreSQL 등)은 TCP를 사용하여 클라이언트와 서버 간의 연결을 관리합니다. 데이터베이스 쿼리와 결과는 정확하게 전달되어야 하기 때문입니다.
# Python에서 MySQL 데이터베이스 연결 예제
import mysql.connector
# TCP 연결을 사용하는 데이터베이스 연결
conn = mysql.connector.connect(
host="localhost",
user="username",
password="password",
database="mydatabase"
)
cursor = conn.cursor()
cursor.execute("SELECT * FROM users")
results = cursor.fetchall()
for row in results:
print(row)
conn.close()
3. 이메일 전송 (SMTP)
이메일 프로토콜(SMTP, POP3, IMAP)은 모두 TCP를 기반으로 합니다. 이메일이 손실되지 않고 정확하게 전달되어야 하기 때문입니다.
// Java에서 이메일 전송 예제 (SMTP 사용)
import javax.mail.*;
import javax.mail.internet.*;
import java.util.Properties;
public class EmailSender {
public static void main(String[] args) {
Properties props = new Properties();
props.put("mail.smtp.host", "smtp.gmail.com");
props.put("mail.smtp.port", "587");
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
Session session = Session.getInstance(props, new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("username@gmail.com", "password");
}
});
try {
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress("from@gmail.com"));
message.setRecipients(Message.RecipientType.TO, InternetAddress.parse("to@gmail.com"));
message.setSubject("테스트 이메일");
message.setText("안녕하세요, 이것은 테스트 이메일입니다.");
Transport.send(message);
System.out.println("이메일 전송 완료");
} catch (MessagingException e) {
e.printStackTrace();
}
}
}
4. 파일 전송 (FTP, SFTP)
파일 전송 프로토콜은 파일의 무결성을 보장하기 위해 TCP를 사용합니다.
UDP 실무 활용 사례
1. 실시간 스트리밍 (영상, 오디오)
넷플릭스, 유튜브, 스포티파이 같은 스트리밍 서비스는 일부 콘텐츠 전송에 UDP를 활용합니다. 약간의 패킷 손실은 허용할 수 있으며, 지연 시간을 최소화하는 것이 중요하기 때문입니다.
# Python에서 UDP 서버 구현 예제
import socket
# UDP 소켓 생성
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_address = ('localhost', 12345)
sock.bind(server_address)
while True:
data, address = sock.recvfrom(4096)
print(f"수신 데이터: {data.decode()} (from {address})")
# 클라이언트에게 응답 전송
response = "데이터를 받았습니다"
sock.sendto(response.encode(), address)
2. 온라인 게임
온라인 게임에서는 캐릭터 위치, 공격 등의 실시간 업데이트에 UDP를 사용합니다. 약간의 패킷 손실은 게임플레이에 큰 영향을 주지 않으며, 낮은 지연 시간이 더 중요합니다.
// C#에서 Unity 게임 엔진을 사용한 UDP 통신 예제
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
public class UDPGameClient : MonoBehaviour
{
private UdpClient udpClient;
private IPEndPoint serverEndPoint;
private Thread receiveThread;
void Start()
{
udpClient = new UdpClient();
serverEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12345);
receiveThread = new Thread(new ThreadStart(ReceiveData));
receiveThread.IsBackground = true;
receiveThread.Start();
}
void ReceiveData()
{
IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
while (true)
{
byte[] data = udpClient.Receive(ref remoteEndPoint);
string message = Encoding.UTF8.GetString(data);
Debug.Log("서버로부터 수신: " + message);
}
}
public void SendPlayerPosition(Vector3 position)
{
string message = JsonUtility.ToJson(position);
byte[] data = Encoding.UTF8.GetBytes(message);
udpClient.Send(data, data.Length, serverEndPoint);
}
void OnApplicationQuit()
{
if (receiveThread != null) receiveThread.Abort();
udpClient.Close();
}
}
3. VoIP (Voice over IP)
Zoom, Skype, Discord와 같은 음성 통화 애플리케이션은 UDP를 사용합니다. 음성 데이터의 실시간 전송이 중요하며, 약간의 패킷 손실은 통화 품질에 큰 영향을 주지 않습니다.
4. DNS (Domain Name System)
도메인 이름을 IP 주소로 변환하는 DNS 쿼리는 UDP를 사용합니다. 쿼리와 응답이 단순하고 짧기 때문에 UDP의 오버헤드가 적은 특성이 적합합니다.
// Node.js에서 DNS 조회 예제
const dns = require('dns');
dns.lookup('www.example.com', (err, address, family) => {
if (err) throw err;
console.log('주소: %j 패밀리: IPv%s', address, family);
});
dns.resolve4('www.example.com', (err, addresses) => {
if (err) throw err;
console.log('IPv4 주소: %j', addresses);
});
코드로 보는 TCP와 UDP 구현
TCP 클라이언트-서버 예제 (Python)
# TCP 서버
import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('localhost', 9999))
server_socket.listen(5)
print("TCP 서버가 시작되었습니다.")
while True:
client_socket, addr = server_socket.accept()
print(f"클라이언트 연결됨: {addr}")
data = client_socket.recv(1024)
print(f"수신 데이터: {data.decode()}")
client_socket.send("안녕하세요, TCP 서버입니다.".encode())
client_socket.close()
# TCP 클라이언트
import socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect(('localhost', 9999))
client_socket.send("안녕하세요, TCP 클라이언트입니다.".encode())
data = client_socket.recv(1024)
print(f"서버로부터 수신: {data.decode()}")
client_socket.close()
UDP 클라이언트-서버 예제 (Python)
# UDP 서버
import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_socket.bind(('localhost', 9999))
print("UDP 서버가 시작되었습니다.")
while True:
data, addr = server_socket.recvfrom(1024)
print(f"수신 데이터: {data.decode()} (from {addr})")
server_socket.sendto("안녕하세요, UDP 서버입니다.".encode(), addr)
# UDP 클라이언트
import socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_address = ('localhost', 9999)
client_socket.sendto("안녕하세요, UDP 클라이언트입니다.".encode(), server_address)
data, server = client_socket.recvfrom(1024)
print(f"서버로부터 수신: {data.decode()}")
client_socket.close()
면접 질문과 모범 답안
Q1: TCP와 UDP의 주요 차이점은 무엇인가요?
답변 예시:
"TCP와 UDP의 주요 차이점은 연결 방식, 신뢰성, 순서 보장, 속도에 있습니다.
TCP는 연결 지향적 프로토콜로, 3-way 핸드셰이크를 통해 연결을 설정하고 데이터의 순서와 무결성을 보장합니다.
패킷 손실 시 재전송 메커니즘을 통해 높은 신뢰성을 제공하지만, 이로 인해 오버헤드가 발생하여 상대적으로 속도가 느립니다.
반면, UDP는 비연결 지향적 프로토콜로, 연결 설정 없이 데이터를 전송합니다.
데이터의 순서나 무결성을 보장하지 않지만, 오버헤드가 적어 빠른 전송이 가능합니다.
실시간 스트리밍, 게임, VoIP와 같이 약간의 데이터 손실이 허용되지만 지연 시간이 중요한 애플리케이션에 적합합니다."
Q2: TCP의 3-way 핸드셰이크를 설명해보세요.
답변 예시:
"TCP의 3-way 핸드셰이크는 클라이언트와 서버 간에 신뢰할 수 있는 연결을 설정하기 위한 과정입니다. 이 과정은 다음과 같은 3단계로 이루어집니다:
- 첫째, 클라이언트가 서버에 SYN(Synchronize) 패킷을 보내면서 연결을 요청합니다. 이 패킷에는 클라이언트의 초기 시퀀스 번호(ISN)가 포함됩니다.
- 둘째, 서버는 클라이언트의 SYN을 수신하고, SYN-ACK 패킷으로 응답합니다. 이 패킷에는 서버의 초기 시퀀스 번호와 클라이언트의 시퀀스 번호에 1을 더한 값(ACK)이 포함됩니다.
- 마지막으로, 클라이언트는 서버의 SYN-ACK를 수신하고, ACK 패킷으로 응답합니다. 이 패킷에는 서버의 시퀀스 번호에 1을 더한 값이 포함됩니다.
이 3단계가 성공적으로 완료되면, 클라이언트와 서버 간에 신뢰할 수 있는 양방향 연결이 설정되고 데이터 전송이 시작됩니다."
Q3: 어떤 상황에서 TCP 대신 UDP를 선택해야 할까요?
답변 예시:
"UDP를 선택해야 하는 상황은 주로 실시간성이 중요하고 약간의 데이터 손실이 허용되는 애플리케이션입니다:
- 실시간 스트리밍 서비스: 영상이나 음성 스트리밍에서는 패킷 손실로 인한 일시적인 품질 저하보다 지연 시간이 더 중요한 요소입니다.
- 온라인 게임: 게임에서 캐릭터 위치, 공격 등의 실시간 업데이트는 즉각적으로 전달되어야 하며, 약간의 패킷 손실은 게임플레이에 큰 영향을 주지 않습니다.
- VoIP(Voice over IP): 음성 통화에서는 작은 오디오 끊김보다 대화의 실시간성이 더 중요합니다.
- DNS 쿼리: 도메인 이름 조회와 같은 단순하고 짧은 요청-응답 패턴의 통신에서는 UDP의 낮은 오버헤드가 유리합니다.
- IoT 장치: 제한된 리소스를 가진 IoT 장치에서는 TCP의 연결 관리 오버헤드를 줄이기 위해 UDP를 사용하는 경우가 많습니다.
이러한 상황에서는 패킷의 정확한 전달보다 빠른 전송 속도와 낮은 지연 시간이 더 중요하기 때문에 UDP가 적합합니다."
Q4: TCP에서 흐름 제어와 혼잡 제어의 차이점은 무엇인가요?
답변 예시:
"TCP의 흐름 제어와 혼잡 제어는 모두 네트워크 통신의 효율성을 개선하기 위한 메커니즘이지만, 다음과 같은 차이점이 있습니다:
흐름 제어(Flow Control)는 수신자와 송신자 간의 데이터 처리 속도 차이를 관리합니다. 수신자가 처리할 수 있는 속도보다 송신자가 데이터를 빠르게 보내는 것을 방지합니다. 주로 슬라이딩 윈도우 프로토콜을 사용하여 구현되며, 수신자는 'receiver window'를 통해 처리할 수 있는 데이터 양을 송신자에게 알립니다.
반면, 혼잡 제어(Congestion Control)는 네트워크 자체의 과부하를 방지합니다.
네트워크의 전체 트래픽이 네트워크 용량을 초과하지 않도록 데이터 흐름을 조절합니다.
TCP는 혼잡 윈도우(congestion window), 슬로우 스타트(slow start), 혼잡 회피(congestion avoidance), 빠른 재전송(fast retransmit), 빠른 회복(fast recovery) 등의 알고리즘을 통해 혼잡 제어를 구현합니다.
요약하자면, 흐름 제어는 수신자를 오버플로우로부터 보호하는 것이 목적이고, 혼잡 제어는 네트워크 자체를 과부하로부터 보호하는 것이 목적입니다."
프로토콜 선택 가이드라인
어떤 프로토콜을 선택해야 할지 결정할 때 고려해야 할 사항:
TCP를 선택해야 하는 경우:
- 데이터의 정확성과 순서가 중요한 경우
- 모든 데이터가 완전히 전송되어야 하는 경우
- 네트워크 상태가 불안정한 경우
- 보안이 중요한 애플리케이션
UDP를 선택해야 하는 경우:
- 실시간 응답이 중요한 경우
- 일부 데이터 손실이 허용되는 경우
- 서버가 많은 클라이언트를 처리해야 하는 경우
- 단순한 요청-응답 패턴의 통신
결론
TCP와 UDP는 각각 고유한 특성과 장단점을 가진 중요한 전송 계층 프로토콜입니다.
TCP는 신뢰성과 순서 보장을 제공하지만 오버헤드가 크고, UDP는 빠른 전송 속도와 낮은 지연 시간을 제공하지만 신뢰성이 낮습니다.
개발자는 애플리케이션의 요구사항에 따라 적절한 프로토콜을 선택해야 합니다.
웹 브라우징, 이메일, 파일 전송과 같이 데이터의 정확성이 중요한 애플리케이션에는 TCP가 적합하고,
실시간 스트리밍, 게임, VoIP와 같이 지연 시간이 중요한 애플리케이션에는 UDP가 적합합니다.
면접에서 TCP와 UDP에 관한 질문을 받았을 때, 두 프로토콜의 기본 특성뿐만 아니라 실제 활용 사례와 선택 기준에 대한 이해를 보여준다면 좋은 인상을 줄 수 있을 것입니다.
'컴퓨터 과학(CS)' 카테고리의 다른 글
해시(Hash) 함수와 충돌 해결 방법 – CS 면접 대비 실전 예제 (2) | 2025.05.18 |
---|---|
면접에서 자주 나오는 동기화 이슈 – 스레드 안전성과 자바 코드로 설명하기 (4) | 2025.05.13 |
REST vs GraphQL vs gRPC: API 통신 방식의 모든 것 - 장단점, 사용 예시 총정리 (1) | 2025.05.08 |
쓰레드와 프로세스의 차이: 실무 예제 기반으로 완벽 이해 (0) | 2025.05.07 |
데이터 압축 알고리즘: Huffman과 LZW 비교 (1) | 2025.01.26 |