본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 11. 29. · 25 Views
네트워크 기초 개념 완벽 가이드
개발자라면 반드시 알아야 할 네트워크 기초 개념을 쉽게 풀어낸 입문서입니다. IP 주소부터 로드밸런싱까지, 실무에서 바로 활용할 수 있는 핵심 지식을 담았습니다.
목차
1. IP 주소와 서브넷 이해
김개발 씨가 처음으로 서버 배포를 맡게 되었습니다. 선배가 "이 서버 IP가 192.168.1.100/24인데, 같은 서브넷에 있는 다른 서버랑 통신 테스트 해봐"라고 했을 때, 김개발 씨는 멍하니 서 있을 수밖에 없었습니다.
IP 주소는 알겠는데, /24는 대체 무슨 뜻일까요?
IP 주소는 인터넷에 연결된 모든 기기의 고유한 주소입니다. 마치 집집마다 있는 우편 주소처럼, 컴퓨터도 각자의 주소가 있어야 데이터를 주고받을 수 있습니다.
서브넷은 이런 IP 주소를 효율적으로 관리하기 위해 네트워크를 작은 단위로 나눈 것입니다.
다음 코드를 살펴봅시다.
// Node.js에서 네트워크 정보 확인하기
const os = require('os');
const networkInterfaces = os.networkInterfaces();
// 모든 네트워크 인터페이스 정보 출력
Object.keys(networkInterfaces).forEach(interfaceName => {
const interfaces = networkInterfaces[interfaceName];
interfaces.forEach(iface => {
// IPv4 주소만 필터링
if (iface.family === 'IPv4') {
console.log(`인터페이스: ${interfaceName}`);
console.log(`IP 주소: ${iface.address}`);
console.log(`서브넷 마스크: ${iface.netmask}`);
}
});
});
김개발 씨는 입사 첫 달에 서버 인프라 팀에 배치되었습니다. 매일 터미널에서 보이는 숫자들이 신기하면서도 어렵게 느껴졌습니다.
특히 192.168.1.100 같은 숫자 조합이 도대체 무슨 의미인지 궁금했습니다. 선배 개발자 박시니어 씨가 화이트보드 앞에 섰습니다.
"IP 주소를 우편 주소로 생각해봐. 서울시 강남구 역삼동 123번지처럼, 컴퓨터도 네트워크에서 자신만의 주소가 필요해." 그렇다면 IP 주소란 정확히 무엇일까요?
쉽게 비유하자면, IP 주소는 마치 아파트 동호수와 같습니다. 101동 1201호라고 하면 어느 건물 어느 층 어느 집인지 정확히 알 수 있듯이, IP 주소도 네트워크에서 특정 컴퓨터를 정확히 찾아갈 수 있게 해줍니다.
IPv4 주소는 0부터 255까지의 숫자 4개를 점으로 구분해서 표현합니다. 그런데 왜 서브넷이라는 개념이 필요할까요?
전 세계 모든 컴퓨터가 하나의 네트워크에 있다면 어떻게 될까요? 누군가 데이터를 보낼 때마다 수십억 대의 컴퓨터를 모두 뒤져야 할 것입니다.
마치 전국의 모든 집을 하나의 동으로 묶어놓은 것처럼 비효율적입니다. 서브넷은 이런 문제를 해결합니다.
큰 네트워크를 작은 단위로 나누어 관리하는 것입니다. 아파트 단지를 동별로 나누듯이, 네트워크도 서브넷으로 나눕니다.
/24는 무엇을 의미할까요? 이것은 서브넷 마스크를 간단히 표현한 것입니다.
IP 주소 32비트 중 앞의 24비트는 네트워크 부분이고, 나머지 8비트는 호스트 부분이라는 뜻입니다. 255.255.255.0과 같은 의미입니다.
위의 코드를 살펴보겠습니다. Node.js의 os 모듈을 사용하면 현재 컴퓨터의 네트워크 정보를 쉽게 확인할 수 있습니다.
networkInterfaces 함수는 모든 네트워크 인터페이스의 정보를 객체로 반환합니다. 여기서 IPv4 주소와 서브넷 마스크를 확인할 수 있습니다.
실제 현업에서는 서버를 구성할 때 IP 주소와 서브넷을 반드시 이해해야 합니다. 예를 들어 회사 내부 네트워크에서 개발 서버와 운영 서버를 다른 서브넷에 배치하면 보안과 관리가 용이해집니다.
주의할 점이 있습니다. 사설 IP 주소와 공인 IP 주소를 혼동하면 안 됩니다.
192.168.x.x나 10.x.x.x는 사설 IP로, 인터넷에서 직접 접근할 수 없습니다. 외부와 통신하려면 공인 IP가 필요합니다.
다시 김개발 씨의 이야기로 돌아가 봅시다. 박시니어 씨의 설명을 들은 후, /24의 의미를 이해한 김개발 씨는 같은 서브넷에 있는 서버들의 IP 범위를 계산할 수 있게 되었습니다.
192.168.1.0부터 192.168.1.255까지가 같은 네트워크라는 것을 알게 된 것입니다.
실전 팁
💡 - 개발 환경에서는 주로 192.168.x.x 또는 10.x.x.x 대역의 사설 IP를 사용합니다
- 서브넷 계산이 어렵다면 온라인 서브넷 계산기를 활용해보세요
2. DNS 동작 원리
김개발 씨가 브라우저에 www.google.com을 입력했습니다. 엔터를 누르는 순간, 구글 페이지가 짠하고 나타납니다.
그런데 문득 궁금해졌습니다. 컴퓨터는 분명 IP 주소로만 통신한다고 배웠는데, 어떻게 google.com이라는 글자만으로 구글 서버를 찾아가는 걸까요?
DNS는 Domain Name System의 약자로, 사람이 읽기 쉬운 도메인 이름을 컴퓨터가 이해하는 IP 주소로 변환해주는 시스템입니다. 마치 전화번호부가 이름으로 전화번호를 찾아주듯이, DNS는 도메인으로 IP 주소를 찾아줍니다.
인터넷의 핵심 인프라 중 하나입니다.
다음 코드를 살펴봅시다.
// Node.js에서 DNS 조회하기
const dns = require('dns');
// 도메인을 IP 주소로 변환 (A 레코드 조회)
dns.resolve4('google.com', (err, addresses) => {
if (err) throw err;
console.log('Google IP 주소들:', addresses);
});
// 역방향 조회 - IP에서 도메인 찾기
dns.reverse('8.8.8.8', (err, hostnames) => {
if (err) throw err;
console.log('8.8.8.8의 호스트명:', hostnames);
});
// DNS 조회 전체 과정 (lookup은 OS의 DNS 캐시도 확인)
dns.lookup('github.com', (err, address, family) => {
console.log('GitHub 주소:', address, 'IPv' + family);
});
김개발 씨는 매일 수십 개의 웹사이트에 접속합니다. 그런데 한 번도 142.250.207.14 같은 IP 주소를 직접 입력한 적이 없습니다.
항상 google.com, github.com처럼 이름만 입력하면 됩니다. 이 마법 같은 일을 가능하게 하는 것이 바로 DNS입니다.
박시니어 씨가 설명을 시작합니다. "DNS를 전화번호부로 생각해봐.
친구 이름만 알면 전화번호부에서 번호를 찾을 수 있잖아. DNS도 똑같아." DNS는 Domain Name System의 약자입니다.
사람이 기억하기 쉬운 도메인 이름을 컴퓨터가 이해할 수 있는 IP 주소로 변환해줍니다. www.google.com을 입력하면 DNS가 이를 142.250.207.14 같은 IP 주소로 바꿔주는 것입니다.
DNS가 없던 시절을 상상해봅시다. 초창기 인터넷에서는 실제로 모든 컴퓨터의 IP 주소를 hosts 파일에 직접 적어두어야 했습니다.
컴퓨터가 수백 대일 때는 가능했지만, 인터넷이 커지면서 불가능해졌습니다. 새로운 서버가 생길 때마다 전 세계 모든 컴퓨터의 hosts 파일을 업데이트해야 했으니까요.
DNS의 동작 과정은 계층적입니다. 먼저 브라우저가 로컬 DNS 캐시를 확인합니다.
최근에 방문한 사이트라면 여기서 바로 IP를 찾을 수 있습니다. 캐시에 없다면 운영체제의 DNS 캐시를 확인합니다.
여기서도 못 찾으면 DNS 리커시브 서버에 질의합니다. 보통 인터넷 서비스 제공업체의 DNS 서버입니다.
이 서버는 루트 DNS 서버부터 시작해서 .com DNS 서버, 그리고 google.com DNS 서버까지 차례로 물어봅니다. 마침내 IP 주소를 알아내면 이를 캐시에 저장하고 브라우저에게 알려줍니다.
위의 코드에서 dns.resolve4는 도메인의 A 레코드를 조회합니다. A 레코드는 도메인을 IPv4 주소로 매핑한 것입니다.
dns.reverse는 반대로 IP 주소에서 도메인을 찾습니다. dns.lookup은 운영체제의 DNS 캐시도 함께 확인하므로 더 현실적인 조회 방법입니다.
실무에서 DNS는 생각보다 자주 문제를 일으킵니다. 도메인을 새로 등록하거나 서버를 이전할 때, DNS 전파 시간 때문에 일부 사용자는 새 서버로, 일부는 구 서버로 접속하는 상황이 발생합니다.
TTL(Time To Live) 설정을 이해하고 적절히 조절하는 것이 중요합니다. 주의할 점이 있습니다.
DNS 캐시 문제로 개발 중 혼란을 겪을 수 있습니다. 로컬에서 hosts 파일을 수정했는데 반영이 안 되거나, 서버 IP를 바꿨는데 예전 IP로 계속 접속되는 경우가 있습니다.
이럴 때는 DNS 캐시를 강제로 비워야 합니다. 다시 김개발 씨의 이야기로 돌아갑니다.
DNS의 원리를 이해한 김개발 씨는 이제 도메인 설정 작업도 자신 있게 할 수 있게 되었습니다. "아, 그래서 DNS 전파에 시간이 걸린다고 했던 거구나!" 하고 고개를 끄덕였습니다.
실전 팁
💡 - DNS 문제가 의심될 때는 nslookup이나 dig 명령어로 직접 확인해보세요
- 개발 중에는 /etc/hosts 파일을 활용하면 DNS 설정 없이 테스트할 수 있습니다
3. 포트와 프로토콜 TCP UDP
김개발 씨가 서버를 띄웠습니다. "서버가 3000번 포트에서 실행 중입니다"라는 메시지를 보며 문득 궁금해졌습니다.
왜 하필 3000번일까요? 포트 번호는 어떤 의미가 있고, 0번부터 65535번까지 있는데 왜 특정 번호들은 특별하게 취급되는 걸까요?
포트는 하나의 IP 주소에서 여러 서비스를 구분하기 위한 논리적인 통로입니다. TCP와 UDP는 데이터를 전송하는 두 가지 방식으로, TCP는 신뢰성을, UDP는 속도를 중시합니다.
마치 하나의 아파트에 여러 가구가 있듯이, 하나의 서버에서 여러 서비스가 각자의 포트에서 동작합니다.
다음 코드를 살펴봅시다.
// TCP 서버 생성 예제
const net = require('net');
// TCP 서버: 신뢰성 있는 연결 기반 통신
const tcpServer = net.createServer((socket) => {
console.log('클라이언트 연결됨');
socket.write('환영합니다!\n');
socket.on('data', (data) => {
console.log('받은 데이터:', data.toString());
socket.write('메시지 수신 확인\n'); // TCP는 수신 확인 가능
});
});
tcpServer.listen(3000, () => {
console.log('TCP 서버가 3000번 포트에서 실행 중');
});
// UDP 예제 (별도 파일)
// const dgram = require('dgram');
// const udpServer = dgram.createSocket('udp4');
김개발 씨는 오늘 처음으로 자신만의 서버를 띄워보았습니다. Express 앱을 실행하니 "3000번 포트에서 실행 중"이라는 메시지가 나왔습니다.
그런데 왜 3000번일까요? 80번이나 8080번은 어떤 차이가 있을까요?
박시니어 씨가 화이트보드에 아파트 그림을 그립니다. "IP 주소가 아파트 주소라면, 포트 번호는 호수야.
101동에 1호, 2호, 3호가 있듯이, 하나의 서버에서 웹 서버는 80번, 이메일 서버는 25번, SSH는 22번 포트를 사용해." 포트는 0번부터 65535번까지 있습니다. 이 중 0~1023번은 잘 알려진 포트(Well-known ports)로, HTTP(80), HTTPS(443), SSH(22), FTP(21) 등 표준 서비스에 예약되어 있습니다.
1024~49151번은 등록된 포트로, MySQL(3306), PostgreSQL(5432), Redis(6379) 등이 사용합니다. 나머지는 자유롭게 사용할 수 있어서 개발 서버에 3000, 8080 등을 많이 씁니다.
이제 TCP와 UDP를 알아봅시다. 둘 다 데이터를 전송하는 프로토콜이지만, 성격이 완전히 다릅니다.
TCP는 등기우편과 같습니다. 보낸 것이 확실히 도착했는지 확인하고, 순서대로 도착하도록 보장합니다.
연결을 먼저 설정하고(3-way handshake), 데이터를 주고받은 후, 연결을 종료합니다. 웹, 이메일, 파일 전송 등 데이터 손실이 있으면 안 되는 서비스에 사용합니다.
UDP는 일반 우편과 같습니다. 보내고 나면 도착 여부를 확인하지 않습니다.
빠르지만 신뢰성이 낮습니다. 실시간 스트리밍, 온라인 게임, DNS 조회 등 약간의 손실보다 속도가 중요한 서비스에 사용합니다.
게임에서 캐릭터 위치가 1초 늦게 표시되는 것보다, 가끔 한 프레임 정도 누락되는 게 나은 것입니다. 위의 코드를 보면 net 모듈로 TCP 서버를 만들고 있습니다.
createServer로 서버를 생성하고, socket 객체를 통해 클라이언트와 양방향 통신을 합니다. TCP이기 때문에 write로 보낸 데이터가 클라이언트에게 확실히 전달됩니다.
실무에서는 대부분 TCP 기반 프로토콜을 사용합니다. HTTP, HTTPS, WebSocket 모두 TCP 위에서 동작합니다.
하지만 실시간 영상 통화나 IoT 센서 데이터 전송에는 UDP가 더 적합할 수 있습니다. 주의할 점이 있습니다.
개발할 때 "포트가 이미 사용 중입니다" 에러를 자주 만납니다. 이전에 실행한 서버가 제대로 종료되지 않았거나, 다른 프로세스가 해당 포트를 사용하고 있는 것입니다.
lsof -i :3000 명령어로 어떤 프로세스가 포트를 점유하고 있는지 확인할 수 있습니다. 다시 김개발 씨의 이야기로 돌아갑니다.
"아, 그래서 배포할 때는 80번이나 443번 포트를 쓰는 거구나!" 김개발 씨가 이해했습니다. 개발할 때는 3000번을 쓰다가, 실제 서비스에서는 표준 포트를 사용해서 사용자가 포트 번호를 입력하지 않아도 되게 하는 것입니다.
실전 팁
💡 - 개발 서버는 3000, 8000, 8080 같은 번호를 자유롭게 사용하세요
- 포트 충돌 시 netstat -tulpn 또는 lsof -i 명령어로 확인하세요
4. HTTP HTTPS 기초
김개발 씨가 크롬 개발자 도구를 열어 네트워크 탭을 확인하고 있습니다. 수많은 요청들이 지나가는데, 어떤 것은 200, 어떤 것은 404, 또 어떤 것은 500이라고 표시됩니다.
선배에게 물어보니 "HTTP 상태 코드야"라고 합니다. HTTP는 도대체 어떻게 동작하는 걸까요?
HTTP는 웹에서 데이터를 주고받는 프로토콜입니다. 클라이언트가 요청을 보내면 서버가 응답을 보내는 단순한 구조입니다.
HTTPS는 HTTP에 암호화를 더한 것으로, 데이터가 중간에 도청되거나 변조되지 않도록 보호합니다. 현대 웹 개발의 기초 중의 기초입니다.
다음 코드를 살펴봅시다.
// Node.js HTTP 서버 기본 예제
const http = require('http');
const server = http.createServer((req, res) => {
// 요청 정보 확인
console.log(`${req.method} ${req.url}`);
// HTTP 상태 코드와 헤더 설정
if (req.url === '/api/users') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ users: ['김개발', '박시니어'] }));
} else if (req.url === '/') {
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.end('<h1>환영합니다</h1>');
} else {
res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end('페이지를 찾을 수 없습니다');
}
});
server.listen(3000);
김개발 씨는 오늘 처음으로 API를 직접 호출해보았습니다. fetch 함수를 사용해서 데이터를 가져왔는데, 네트워크 탭에 보이는 정보들이 신기했습니다.
Request Headers, Response Headers, Status Code... 이것들은 다 무엇일까요?
박시니어 씨가 차근차근 설명합니다. "HTTP를 식당 주문으로 생각해봐.
손님이 메뉴를 주문하면(요청), 주방에서 음식을 만들어서 가져다주잖아(응답). HTTP도 똑같아." HTTP는 HyperText Transfer Protocol의 약자입니다.
1991년에 처음 만들어졌고, 지금까지 웹의 기본 통신 방식으로 사용되고 있습니다. 클라이언트(주로 브라우저)가 서버에게 무언가를 요청하면, 서버가 그에 맞는 응답을 보내는 구조입니다.
HTTP 요청에는 여러 메서드가 있습니다. GET은 데이터를 조회할 때, POST는 새 데이터를 생성할 때, PUT은 데이터를 수정할 때, DELETE는 삭제할 때 사용합니다.
이것을 CRUD 패턴이라고 합니다. 응답에는 상태 코드가 포함됩니다.
200번대는 성공, 300번대는 리다이렉션, 400번대는 클라이언트 오류, 500번대는 서버 오류입니다. 가장 많이 보는 것은 200(성공), 404(찾을 수 없음), 500(서버 오류)입니다.
그런데 HTTP에는 치명적인 약점이 있습니다. 데이터가 평문으로 전송된다는 것입니다.
카페 와이파이에서 로그인하면, 같은 네트워크에 있는 누군가가 비밀번호를 엿볼 수 있습니다. HTTPS는 이 문제를 해결합니다.
HTTP에 SSL/TLS 암호화를 추가한 것입니다. 데이터가 암호화되어 전송되므로 중간에 누가 가로채도 내용을 알 수 없습니다.
현재 대부분의 웹사이트가 HTTPS를 사용하며, 브라우저도 HTTP 사이트에 "안전하지 않음" 경고를 표시합니다. 위의 코드는 기본적인 HTTP 서버입니다.
req 객체에서 요청 정보를 확인하고, res 객체로 응답을 보냅니다. writeHead로 상태 코드와 헤더를 설정하고, end로 본문을 전송합니다.
URL에 따라 다른 응답을 보내는 간단한 라우팅도 구현되어 있습니다. 실무에서는 Express나 Fastify 같은 프레임워크를 사용하지만, 이 기본 원리를 이해하는 것이 중요합니다.
문제가 생겼을 때 HTTP 레벨에서 디버깅할 수 있어야 하기 때문입니다. 주의할 점이 있습니다.
로컬 개발 환경에서는 HTTP를 써도 되지만, 실제 서비스에서는 반드시 HTTPS를 사용해야 합니다. Let's Encrypt 같은 무료 SSL 인증서도 있으니 비용 걱정은 하지 않아도 됩니다.
다시 김개발 씨의 이야기로 돌아갑니다. 이제 김개발 씨는 네트워크 탭을 보며 200, 404, 500의 의미를 정확히 이해합니다.
API가 404를 반환하면 URL이 틀린 것이고, 500이면 서버 코드에 버그가 있는 것입니다.
실전 팁
💡 - 개발자 도구 네트워크 탭을 자주 확인하는 습관을 들이세요
- API 테스트에는 Postman이나 Insomnia 같은 도구가 편리합니다
5. 로드밸런싱 개념
김개발 씨가 개발한 서비스가 대박이 났습니다. 사용자가 급격히 늘어나면서 서버 한 대로는 감당이 안 됩니다.
서버를 추가해야 하는데, 어떻게 해야 사용자들의 요청이 여러 서버로 고르게 분산될까요? 선배가 "로드밸런서를 붙여야지"라고 합니다.
로드밸런싱은 들어오는 트래픽을 여러 서버에 분산시키는 기술입니다. 마치 마트에서 여러 계산대가 손님을 나눠 받듯이, 로드밸런서가 사용자 요청을 여러 서버에 나눠줍니다.
서비스의 가용성과 성능을 높이는 핵심 기술입니다.
다음 코드를 살펴봅시다.
// Nginx 로드밸런서 설정 예시 (nginx.conf)
// upstream backend {
// server 192.168.1.101:3000 weight=3;
// server 192.168.1.102:3000 weight=2;
// server 192.168.1.103:3000 backup;
// }
// Node.js 간단한 라운드로빈 로드밸런서 구현
const http = require('http');
const httpProxy = require('http-proxy');
const servers = [
{ host: 'localhost', port: 3001 },
{ host: 'localhost', port: 3002 },
{ host: 'localhost', port: 3003 }
];
let currentIndex = 0;
const proxy = httpProxy.createProxyServer({});
http.createServer((req, res) => {
// 라운드로빈: 순서대로 서버 선택
const target = servers[currentIndex];
currentIndex = (currentIndex + 1) % servers.length;
console.log(`요청을 ${target.port}번 서버로 전달`);
proxy.web(req, res, { target: `http://${target.host}:${target.port}` });
}).listen(80);
김개발 씨의 쇼핑몰 서비스가 TV에 소개되었습니다. 갑자기 접속자가 폭주하더니 서버가 다운되어 버렸습니다.
사장님이 달려왔습니다. "빨리 서버 더 붙여!
돈은 얼마든지 쓸 테니까!" 박시니어 씨가 침착하게 말합니다. "서버만 추가한다고 되는 게 아니에요.
요청을 어떤 서버로 보낼지 결정하는 로드밸런서가 필요합니다." 로드밸런싱은 마트의 계산대 시스템과 같습니다. 손님이 몰리면 계산대를 더 열고, 안내원이 빈 계산대로 손님을 안내합니다.
로드밸런서가 바로 이 안내원 역할을 합니다. 로드밸런싱에는 여러 알고리즘이 있습니다.
가장 단순한 것은 라운드로빈입니다. 1번 서버, 2번 서버, 3번 서버...
순서대로 돌아가면서 요청을 보냅니다. 위의 코드가 바로 이 방식을 구현한 것입니다.
가중치 라운드로빈은 서버 성능에 따라 비율을 조절합니다. 고성능 서버에는 더 많은 요청을 보냅니다.
최소 연결 방식은 현재 연결이 가장 적은 서버로 요청을 보냅니다. IP 해시 방식은 사용자 IP를 기반으로 항상 같은 서버로 보내서 세션을 유지합니다.
로드밸런싱의 또 다른 중요한 기능은 헬스 체크입니다. 서버가 죽으면 자동으로 감지해서 해당 서버로는 요청을 보내지 않습니다.
서버가 다시 살아나면 자동으로 복구됩니다. 이것이 서비스 고가용성의 핵심입니다.
클라우드 환경에서는 AWS의 ALB(Application Load Balancer), GCP의 Cloud Load Balancing, 또는 Nginx, HAProxy 같은 소프트웨어 로드밸런서를 많이 사용합니다. 위의 코드를 보면 http-proxy 모듈을 사용해서 간단한 로드밸런서를 구현했습니다.
요청이 들어올 때마다 currentIndex를 증가시켜서 다음 서버를 선택하고, proxy.web으로 요청을 해당 서버에 전달합니다. 실무에서는 직접 로드밸런서를 구현하기보다 Nginx나 클라우드 서비스를 사용합니다.
하지만 원리를 이해하면 설정이나 문제 해결이 훨씬 수월해집니다. 주의할 점이 있습니다.
로드밸런싱 환경에서는 세션 관리가 복잡해집니다. 사용자가 로그인한 후 다른 서버로 요청이 가면 로그인 상태가 사라질 수 있습니다.
Redis 같은 외부 세션 저장소를 사용하거나, JWT처럼 상태를 클라이언트에 저장하는 방식으로 해결합니다. 다시 김개발 씨의 이야기로 돌아갑니다.
로드밸런서를 붙이고 서버를 3대로 늘린 후, 서비스는 안정적으로 동작했습니다. "앞으로는 서버 한 대에 의존하면 안 되겠구나." 김개발 씨가 중요한 교훈을 얻었습니다.
실전 팁
💡 - 로드밸런서 없이 단일 서버로 운영하면 해당 서버 장애 시 서비스 전체가 중단됩니다
- 세션 문제를 피하려면 처음부터 stateless 아키텍처로 설계하세요
6. 프록시와 리버스 프록시
김개발 씨가 회사에서 특정 웹사이트에 접속하려고 했는데 막혀있습니다. 회사 프록시 서버가 차단한 것입니다.
그런데 배포 문서를 보니 "리버스 프록시 설정"이라는 항목도 있습니다. 프록시와 리버스 프록시, 둘은 어떻게 다른 걸까요?
프록시는 클라이언트를 대신해서 서버에 요청을 보내는 중개자입니다. 반대로 리버스 프록시는 서버를 대신해서 클라이언트의 요청을 받는 중개자입니다.
둘 다 중개자 역할을 하지만, 누구를 위한 중개인지가 다릅니다.
다음 코드를 살펴봅시다.
// Nginx 리버스 프록시 설정 예시
// location /api {
// proxy_pass http://localhost:3000;
// proxy_set_header Host $host;
// proxy_set_header X-Real-IP $remote_addr;
// }
// Node.js 간단한 리버스 프록시 구현
const http = require('http');
const httpProxy = require('http-proxy');
const proxy = httpProxy.createProxyServer({});
const server = http.createServer((req, res) => {
// URL 경로에 따라 다른 백엔드 서버로 라우팅
if (req.url.startsWith('/api')) {
proxy.web(req, res, { target: 'http://localhost:3000' });
} else if (req.url.startsWith('/images')) {
proxy.web(req, res, { target: 'http://localhost:4000' });
} else {
// 정적 파일 서버
proxy.web(req, res, { target: 'http://localhost:5000' });
}
});
server.listen(80);
console.log('리버스 프록시가 80번 포트에서 실행 중');
김개발 씨가 출근 첫날, 유튜브에 접속하려다 차단 화면을 만났습니다. "회사 정책에 의해 차단되었습니다"라는 메시지가 떴습니다.
이것이 바로 프록시 서버의 동작입니다. 박시니어 씨가 설명합니다.
"프록시를 비서로 생각해봐. 네가 커피를 마시고 싶다고 비서에게 말하면, 비서가 대신 카페에 가서 커피를 사다 주잖아.
카페 입장에서는 누가 커피를 원하는지 몰라." **프록시(Forward Proxy)**는 클라이언트 측의 대리인입니다. 클라이언트가 인터넷에 직접 접속하지 않고 프록시 서버를 통해 접속합니다.
이를 통해 클라이언트의 IP를 숨기거나, 특정 사이트 접근을 차단하거나, 자주 접근하는 콘텐츠를 캐싱할 수 있습니다. 그렇다면 리버스 프록시는 무엇일까요?
리버스 프록시는 반대로 서버 측의 대리인입니다. 클라이언트가 서버에 직접 접속하는 것이 아니라 리버스 프록시에 접속하면, 리버스 프록시가 실제 서버로 요청을 전달합니다.
클라이언트 입장에서는 실제 서버가 어디 있는지 모릅니다. 왜 이런 구조가 필요할까요?
리버스 프록시는 여러 중요한 역할을 합니다. 첫째, 보안입니다.
실제 서버의 IP를 숨길 수 있습니다. 외부에서는 리버스 프록시만 보이므로, 직접적인 공격을 막을 수 있습니다.
둘째, SSL 종료입니다. HTTPS 암호화/복호화를 리버스 프록시에서 처리하면, 백엔드 서버는 HTTP로 간단하게 통신할 수 있습니다.
셋째, 로드밸런싱입니다. 앞서 배운 것처럼 여러 서버로 요청을 분산시킬 수 있습니다.
넷째, 캐싱입니다. 자주 요청되는 콘텐츠를 캐싱해서 백엔드 서버의 부하를 줄일 수 있습니다.
위의 코드는 URL 경로에 따라 다른 백엔드 서버로 라우팅하는 리버스 프록시입니다. /api로 시작하면 API 서버로, /images면 이미지 서버로, 그 외에는 정적 파일 서버로 요청을 전달합니다.
이것이 바로 마이크로서비스 아키텍처의 기초입니다. 실무에서 가장 많이 사용하는 리버스 프록시는 Nginx입니다.
성능이 뛰어나고 설정이 직관적이며, 동시에 웹 서버 역할도 할 수 있습니다. AWS 환경에서는 ALB가 리버스 프록시 역할을 합니다.
주의할 점이 있습니다. 리버스 프록시를 통과하면 클라이언트의 실제 IP가 사라집니다.
서버 입장에서는 프록시의 IP만 보입니다. 실제 클라이언트 IP가 필요하다면 X-Forwarded-For 헤더를 사용해야 합니다.
다시 김개발 씨의 이야기로 돌아갑니다. 회사 프록시가 유튜브를 막은 이유를 이해한 김개발 씨는, 동시에 리버스 프록시가 서비스 아키텍처에서 얼마나 중요한지도 깨달았습니다.
"프론트가 80번 포트로 요청하면 Nginx가 받아서 백엔드 3000번 포트로 넘기는 거구나!"
실전 팁
💡 - Nginx 리버스 프록시 설정을 익혀두면 배포할 때 유용합니다
- X-Forwarded-For 헤더로 실제 클라이언트 IP를 확인하세요
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
서비스 메시 완벽 가이드
마이크로서비스 간 통신을 안전하고 효율적으로 관리하는 서비스 메시의 핵심 개념부터 실전 도입까지, 초급 개발자를 위한 완벽한 입문서입니다. Istio와 Linkerd 비교, 사이드카 패턴, 실무 적용 노하우를 담았습니다.
EFK 스택 로깅 완벽 가이드
마이크로서비스 환경에서 로그를 효과적으로 수집하고 분석하는 EFK 스택(Elasticsearch, Fluentd, Kibana)의 핵심 개념과 실전 활용법을 초급 개발자도 쉽게 이해할 수 있도록 정리한 가이드입니다.
Grafana 대시보드 완벽 가이드
실시간 모니터링의 핵심, Grafana 대시보드를 처음부터 끝까지 배워봅니다. Prometheus 연동부터 알람 설정까지, 초급 개발자도 쉽게 따라할 수 있는 실전 가이드입니다.
분산 추적 완벽 가이드
마이크로서비스 환경에서 요청의 전체 흐름을 추적하는 분산 추적 시스템의 핵심 개념을 배웁니다. Trace, Span, Trace ID 전파, 샘플링 전략까지 실무에 필요한 모든 것을 다룹니다.
CloudFront CDN 완벽 가이드
AWS CloudFront를 활용한 콘텐츠 배포 최적화 방법을 실무 관점에서 다룹니다. 배포 생성부터 캐시 설정, HTTPS 적용까지 단계별로 알아봅니다.