네트워크 기초 입문
목표: “서버에서 띄웠는데 밖에서 안 열려요”, “DNS가 안 풀려요”, “API가 429를 줘요”, “VPN 켜고 들어오세요” 같은 말을 들었을 때 데이터가 어디를 거쳐 가는지 떠올리고, 명령어 몇 개로 어디서 막혔는지 직접 찾기.
서버·클라이언트·포트·localhost의 첫 소개는 컴퓨터 기본 도구와 개발 환경 입문 §9에 있고, 이 노트는 그 다음 단계다. 컨테이너 포트 연결은 Docker 입문, SSH 터널은 원격 GPU 서버 사용 입문에서 다룬다.
한 줄로 먼저
네트워크 통신은 “어느 컴퓨터로(IP) → 그 안의 어느 프로그램에게(포트) → 어떤 방식으로 나눠 보내서(TCP/UDP) → 무슨 말로 대화할지(HTTP 등)“를 층층이 나눠 맡는 구조다.
| 층 (실용판 4계층) | 맡는 질문 | 이름표 | 대표 기술 | OSI 7계층 이름 |
|---|---|---|---|---|
| 애플리케이션 | 무슨 말로 대화하나? | URL, 도메인 | HTTP, DNS, SSH | 7 응용 (5·6 포함) |
| 전송 | 어느 프로그램에게, 얼마나 확실히? | 포트 | TCP, UDP | 4 전송 |
| 인터넷 | 어느 컴퓨터로, 어떤 길로? | IP 주소 | IP, 라우터 | 3 네트워크 |
| 링크 | 바로 옆 기기까지 어떻게? | MAC 주소 | Wi-Fi, 이더넷 | 2 데이터 링크, 1 물리 |
Mac의 Python ── requests.get("https://api.example.com/v1/models")
↓ DNS: api.example.com → 203.0.113.10
↓ TCP 연결: 192.168.0.23:51234 → 203.0.113.10:443
↓ TLS: 암호화 통로 만들기
↓ HTTP: "GET /v1/models" 전송
집 공유기(NAT) → 통신사 → 인터넷의 라우터들 → API 서버
↑ 응답: 200 OK + JSON0. 왜 알아야 하나
서버 데모가 노트북에서 안 열리는 것, 집에서만 ssh가 안 되는 것, API가 가끔 429로 실패하는 것은 원인이 서로 다른 층에 있다. 이름(DNS)을 못 찾았는지, 길(방화벽·사설망)이 막혔는지, 문(포트)이 닫혔는지, 대화(HTTP)에서 거절당했는지를 가를 수 있으면 절반은 해결된다.
1. 계층 구조를 실용적으로
각 층은 아래층을 믿고 자기 일만 한다. HTTP는 데이터가 Wi-Fi로 가는지 유선으로 가는지 모른다.
보낼 때는 위층 데이터를 아래층이 봉투처럼 감싼다. 이것을 캡슐화(encapsulation) 라고 한다.
[ Wi-Fi 헤더 [ IP 헤더 [ TCP 헤더 [ HTTP 요청 ] ] ] ]
MAC 주소 IP 주소 포트 번호 내용MAC 주소(a4:83:e7:12:34:56)는 장치에 붙은 48비트 번호로, 같은 네트워크의 바로 옆 기기까지만 쓴다. IP 주소는 여러 네트워크를 건너 최종 목적지를 가리킨다. 라우터를 건널 때마다 MAC 봉투는 바뀌지만 IP는 (NAT를 빼면) 그대로다.
교과서의 OSI 7계층은 이론 모델이고, 실제 인터넷은 위의 4계층(TCP/IP 모델)에 가깝다. 다만 현업에서 번호를 이름처럼 쓴다. “L3”는 IP 수준(라우터), “L4 로드밸런서”는 IP와 포트만 보고 분배, “L7 로드밸런서”는 HTTP 경로·헤더까지 보고 분배한다는 뜻이다.
2. IP 주소
IPv4와 IPv6
IPv4는 32비트로, 192.168.0.23처럼 0~255 숫자 4개로 쓴다. 약 43억 개뿐이라 모자라서 128비트 IPv6(2001:db8::1, 연속된 0은 ::로 줄임)가 나왔다. 자기 자신은 IPv4에서 127.0.0.1, IPv6에서 ::1이다. 일상 설정은 아직 IPv4가 대부분이다.
비유 하나. IP 주소는 건물 주소, 포트는 그 건물의 호수다. 데이터는 주소로 건물(컴퓨터)을 찾고, 호수로 방(프로그램)을 찾는다. 한 서버에 SSH(22호), 웹(443호), Jupyter(8888호)가 함께 살 수 있는 이유다.
공인 IP와 사설 IP
- 공인 IP(public IP): 인터넷 전체에서 유일한 주소. 어디서든 찾아갈 수 있다(방화벽이 허락하면).
- 사설 IP(private IP): 집·학교·회사 내부망 안에서만 통하는 주소. 그래서 여러 집이 같은
192.168.0.10을 써도 충돌하지 않는다.
| 사설 범위 (RFC 1918) | CIDR | 흔히 보는 곳 |
|---|---|---|
10.0.0.0 ~ 10.255.255.255 | 10.0.0.0/8 | 학교·회사·클라우드 내부망 |
172.16.0.0 ~ 172.31.255.255 | 172.16.0.0/12 | 회사망, Docker 기본 네트워크(172.17.x.x) |
192.168.0.0 ~ 192.168.255.255 | 192.168.0.0/16 | 집 공유기 |
172.x가 전부 사설은 아니다. 172.16~172.31만 사설이다.
특수 주소도 셋 알아 둔다. 127.0.0.1은 이 컴퓨터 자신, 0.0.0.0은 서버 바인딩에서 “이 컴퓨터의 모든 주소”(§10), 169.254.x.x는 DHCP로 주소를 못 받았을 때 스스로 붙인 주소(= 네트워크 설정 실패 신호)다.
서브넷: /24의 감
서브넷(subnet) 은 “같은 동네”로 묶인 주소 범위다. 같은 서브넷 안의 기기끼리는 라우터를 거치지 않고 바로 통신한다.
192.168.0 . 23 /24 /24 = 앞 24비트(숫자 세 개)가 동네 이름
└─ 동네 ─┘ └ 기기
같은 동네: 192.168.0.1 ~ 192.168.0.254 (서브넷 마스크 255.255.255.0)/24는 주소 256개이고, .0(네트워크 자체)과 .255(브로드캐스트)를 빼면 기기 254대다. /16은 앞 두 숫자가 동네다. 숫자가 작을수록 동네가 크고, /32는 주소 딱 하나다. 방화벽 규칙의 10.0.0.0/8에서만 허용을 읽을 때 이 감각이 필요하다.
3. 라우터, NAT, DHCP: 집 공유기로 이해하기
집 공유기 한 대가 사실 세 가지 일을 한다.
인터넷
│ 공인 IP 203.0.113.45 (통신사가 줌)
┌──────┴──────┐
│ 집 공유기 │ 라우터 + NAT + DHCP 서버
└──────┬──────┘
│ 192.168.0.1 (기본 게이트웨이)
┌─────────┼─────────┐
Mac 휴대폰 TV
192.168.0.23 192.168.0.31 192.168.0.40라우터(router) 는 서로 다른 네트워크를 잇고 다음 행선지를 정한다. Mac은 목적지가 같은 동네가 아니면 일단 기본 게이트웨이(default gateway) 인 공유기로 보낸다.
NAT(Network Address Translation) 는 사설 IP를 공인 IP로 바꿔 내보낸다.
- Mac(
192.168.0.23:51234)이 요청을 보낸다. - 공유기가 출발지를
203.0.113.45:40001로 바꾸고 이 짝을 표에 적는다. - 응답이
:40001로 돌아오면 표를 보고 Mac에게 넘긴다.
그래서 안에서 밖으로 먼저 연결하는 건 되지만, 밖에서 안으로 먼저 들어오는 건 기본적으로 안 된다. 표에 짝이 없기 때문이다. 집 Mac에서 띄운 서버에 친구가 못 들어오는 이유다. 꼭 열려면 공유기에서 포트 포워딩(바깥 8080 → 192.168.0.23:8000)을 설정한다.
DHCP(Dynamic Host Configuration Protocol) 는 새로 붙은 기기에게 IP 주소, 서브넷, 기본 게이트웨이, DNS 서버 주소를 자동으로 나눠 준다. 받은 주소는 임대(lease) 라서 바뀔 수 있다. 서버처럼 주소가 고정돼야 하면 고정 IP를 쓰거나 예약을 건다.
4. DNS: 이름을 주소로
DNS(Domain Name System) 는 api.example.com 같은 이름을 203.0.113.10 같은 IP로 바꿔 준다.
Mac: "api.example.com 주소 뭐야?" (먼저 /etc/hosts와 캐시 확인)
↓ 리졸버(공유기, 통신사, 1.1.1.1 등)가 루트 → .com → example.com 서버에 차례로 물어봄
"203.0.113.10이야. 300초 동안 기억해도 돼(TTL)"TTL(Time To Live) 은 답을 캐시해도 되는 시간이다. DNS를 바꿔도 캐시가 만료될 때까지 옛 주소가 나온다. “어떤 컴퓨터에서만 된다”의 흔한 원인이다.
| 레코드 | 뜻 | 예 |
|---|---|---|
| A | 이름 → IPv4 | api.example.com → 203.0.113.10 |
| AAAA | 이름 → IPv6 | api.example.com → 2001:db8::10 |
| CNAME | 이름 → 다른 이름(별명) | www.example.com → example.github.io |
/etc/hosts
/etc/hosts는 내 컴퓨터만의 전화번호부로, 기본 설정에서 DNS보다 먼저 확인된다(수정에는 sudo). 10.0.1.23 gpu01 한 줄을 적으면 내 컴퓨터에서만 gpu01이 그 IP로 풀린다. 테스트용 이름은 .test처럼 예약된 이름을 쓰고, macOS에서 mDNS가 쓰는 .local은 피한다.
확인 명령
dig api.example.com # 자세한 답 (TTL, 답한 서버)
dig +short api.example.com # IP만
dig @1.1.1.1 api.example.com # 특정 DNS 서버에게 직접 묻기
nslookup api.example.com # 간단 조회dig와 nslookup은 DNS 서버에 직접 묻기 때문에 /etc/hosts는 반영되지 않는다. macOS DNS 캐시는 sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder로 비운다.
5. 포트와 소켓, TCP와 UDP
포트
포트(port) 는 0~65535의 번호다.
| 범위 | 이름 | 예 |
|---|---|---|
| 0 ~ 1023 | 잘 알려진 포트 | 22 SSH, 53 DNS, 80 HTTP, 443 HTTPS |
| 1024 ~ 49151 | 등록된 포트 | 5432 PostgreSQL, 6379 Redis, 8080 개발용 HTTP |
| 49152 ~ 65535 | 임시 포트 | 클라이언트 쪽에서 운영체제가 아무거나 골라 씀 |
Jupyter 8888, TensorBoard 6006, Gradio 7860은 관례일 뿐 강제가 아니다. Linux에서 1023 이하 포트는 보통 root 권한이 필요해서 개발할 때는 큰 번호를 쓴다. 한 포트에는 한 프로그램만 띄울 수 있고, 이미 쓰이면 Address already in use가 난다.
소켓
소켓(socket) 은 프로그램이 네트워크와 데이터를 주고받는 출입구다. 운영체제가 만들어 주고, 프로그램은 파일처럼 읽고 쓴다. TCP 연결 하나는 (프로토콜, 내 IP, 내 포트, 상대 IP, 상대 포트) 다섯 값으로 구별된다. 그래서 서버의 443번 하나로 수많은 클라이언트가 동시에 접속할 수 있다.
TCP와 UDP
| 구분 | TCP | UDP |
|---|---|---|
| 연결 | 먼저 연결을 맺음 | 연결 없이 바로 보냄 |
| 신뢰성 | 순서 보장, 빠진 것은 재전송 | 보장 없음 |
| 쓰는 곳 | HTTP/1.1·HTTP/2, SSH, DB | DNS 조회, 화상 통화, 게임, HTTP/3(QUIC) |
TCP는 연결 전에 세 번 주고받는다(3-way handshake). 클라이언트가 SYN(“연결할래?”), 서버가 SYN-ACK(“좋아, 너도?”), 클라이언트가 ACK(“시작하자”)를 보낸다.
화상 통화는 늦게 온 화면보다 한 프레임 빠진 게 나아서 UDP를 쓴다. JSON은 한 글자라도 빠지면 안 되니 TCP를 쓴다.
6. HTTP: 웹의 대화 규칙
요청과 응답
HTTP(HyperText Transfer Protocol) 는 클라이언트가 요청(request) 을 보내면 서버가 응답(response) 을 돌려주는 규칙이다. HTTP/1.1은 사람이 읽을 수 있는 텍스트다.
GET /v1/models HTTP/1.1 ← 메서드, 경로, 버전
Host: api.example.com ← 헤더들
Accept: application/json
Authorization: Bearer sk-xxxx
← 빈 줄 뒤가 본문 (GET은 보통 없음)
HTTP/1.1 200 OK ← 버전, 상태 코드
Content-Type: application/json
{"data": [{"id": "model-a"}, {"id": "model-b"}]}HTTP/2·HTTP/3는 같은 의미를 이진 형식으로 더 효율적으로 보낸다. HTTP는 상태가 없다(stateless). 서버는 앞 요청을 기억하지 않으므로 로그인 상태는 쿠키나 토큰으로 매번 알려 준다.
메서드
GET은 가져오기, POST는 새로 만들기·처리 요청, PUT/PATCH는 통째로/일부 바꾸기, DELETE는 지우기다. LLM API 호출은 거의 다 POST다. 긴 프롬프트와 설정을 JSON 본문에 담아야 하기 때문이다.
상태 코드
| 분류 | 뜻 | 자주 보는 코드 |
|---|---|---|
| 2xx | 성공 | 200 OK, 201 Created, 204 No Content |
| 3xx | 다른 곳으로 가라 | 301 영구 이동, 302 임시 이동, 304 Not Modified |
| 4xx | 내 요청이 문제 | 400 형식 오류, 401 인증 안 됨, 403 권한 없음, 404 없음, 429 너무 자주 |
| 5xx | 서버 쪽이 문제 | 500 내부 에러, 502 Bad Gateway, 503 일시적 불가, 504 Gateway Timeout |
401은 “누군지 모르겠다”(키 없음·틀림),403은 “누군지는 아는데 이건 안 된다”(권한 부족)다.- 4xx는 요청을 고쳐야 한다. 예외는
429로, 기다렸다 보내면 된다. 5xx는 잠시 뒤 재시도가 의미 있다. - 표준 밖 코드도 있다. 예를 들어 Anthropic API는 과부하 때
529를 준다.
헤더와 쿠키
헤더는 이름: 값 형식의 부가 정보다(이름은 대소문자 무관). Host, Content-Type(JSON은 application/json), Authorization(인증), Retry-After(몇 초 뒤 재시도할지)를 가장 자주 본다.
쿠키(cookie) 는 서버가 브라우저에게 “다음에 같이 보내”라고 맡기는 작은 문자열이다. 로그인이 유지되는 원리다.
응답: Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Lax
이후 요청: Cookie: session=abc123HttpOnly는 JavaScript가 못 읽게, Secure는 HTTPS에서만 보내게 한다. 세션 쿠키를 남이 가져가면 비밀번호 없이 내 행세를 할 수 있으니 비밀번호처럼 다룬다.
7. HTTPS와 TLS
HTTPS는 HTTP를 TLS(Transport Layer Security) 로 암호화한 통로 안에서 주고받는 것이다(기본 포트 443, 옛 이름 SSL). TLS가 하는 일은 둘, 암호화(엿봐도 못 읽음)와 상대 확인(이 서버가 정말 api.example.com의 주인인지)이다.
인증서(certificate) 는 “이 공개키는 api.example.com의 것”이라는 문서에 인증기관(CA) 이 서명한 것이다. 클라이언트는 내 컴퓨터가 믿는 CA까지 서명이 이어지는지, 도메인이 접속한 이름과 같은지, 유효 기간 안인지 본다. 통과하면 둘이 비밀 키를 합의하고 이후 데이터를 전부 암호화한다.
주소창의 자물쇠는 “연결이 암호화됐고, 서버가 이 도메인의 정당한 인증서를 가졌다” 는 뜻이다. “믿을 만한 사이트”라는 뜻이 아니다. 피싱 사이트도 자기 도메인 인증서는 쉽게 받는다.
무엇이 가려지나
| 중간(공용 Wi-Fi, 통신사)에서 | HTTP | HTTPS |
|---|---|---|
본문, 헤더(Authorization·쿠키 포함), URL 경로·쿼리 | 보임 | 가려짐 |
| 접속하는 도메인 이름 | 보임 | 대개 보임 (연결 시작 때 드러남) |
| 상대 IP·포트, 주고받은 양과 시각 | 보임 | 보임 |
API 키를 http://로 보내면 지나가는 길 어디서든 읽힌다. API 호출은 반드시 https://로 한다.
SSL: CERTIFICATE_VERIFY_FAILED는 인증서 확인 실패다. 기관 네트워크가 HTTPS를 중간 검사하면 기관 CA를 따로 설치해야 할 때가 있다. curl -k나 verify=False는 확인을 끄는 것이니 원인을 찾는 동안만 쓴다.
8. URL 구조
URL(Uniform Resource Locator) 은 “어떤 방식으로, 어느 컴퓨터의, 어느 프로그램에게, 무엇을”을 한 줄에 적은 것이다.
https://api.example.com:8443/v1/search?q=gpu&limit=10#results
└─┬─┘ └──────┬──────┘ └┬─┘└───┬───┘ └──────┬──────┘ └──┬──┘
scheme host port path query fragment- scheme:
http,https,ws,wss등. 포트를 생략하면 기본값(http80,https443)을 쓴다. - query는
?뒤의키=값을&로 잇는다. 공백·한글은 퍼센트 인코딩된다(공백%20). - fragment(
#뒤)는 서버로 보내지지 않고 브라우저만 쓴다.
9. REST API와 JSON
웹 API는 프로그램이 프로그램에게 HTTP로 일을 시키는 창구다. LLM용 Python SDK도 속에서는 HTTPS로 JSON을 주고받는다.
REST는 흔한 API 설계 스타일이다. 대상을 URL 경로(/v1/files/abc123)로, 하려는 일을 메서드(GET, DELETE)로, 결과를 상태 코드로 나타낸다.
JSON(JavaScript Object Notation) 은 {}(키-값 묶음)와 [](배열)로 데이터를 적는 텍스트 형식이다. 키는 큰따옴표로 감싸고, 주석과 마지막 쉼표는 허용되지 않는다.
curl로 직접 호출하기
curl은 터미널에서 HTTP 요청을 보내는 도구다. SDK가 이상할 때 같은 요청을 curl로 보내 보면 문제가 어느 쪽인지 가를 수 있다.
# GET
curl https://api.example.com/v1/models \
-H "Authorization: Bearer $API_KEY"
# POST: -d로 본문을 주면 메서드가 자동으로 POST
curl https://api.example.com/v1/chat \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "some-model", "messages": [{"role": "user", "content": "Hello"}]}'
# JSON 보기 좋게 (jq는 brew install jq)
curl -s https://api.example.com/v1/models -H "Authorization: Bearer $API_KEY" | jq .-H 헤더, -d 본문, -X DELETE 메서드 지정, -i 응답 헤더 보기, -L 리다이렉트 따라가기, -v 연결 과정 보기(§12).
API 키와 인증 헤더
- 키를 보내는 헤더는 서비스마다 다르다. OpenAI는
Authorization: Bearer <키>, Anthropic은x-api-key: <키>를 쓴다. 공식 문서를 확인한다. - 키는 코드에 쓰지 말고 환경 변수(
$API_KEY)로 넘긴다. 그러면 셸 기록에도 키 원문이 남지 않는다.
Rate limit과 429
API 제공자는 rate limit(시간당 요청 수·토큰 수 상한)을 둔다. 넘으면 429 Too Many Requests가 온다.
Retry-After헤더가 있으면 그만큼 기다린다.- 없으면 지수 백오프(exponential backoff) 로 1초, 2초, 4초…처럼 간격을 늘려 재시도한다. 약간의 무작위 시간(jitter)을 더하면 동시 재시도가 몰리지 않는다.
- 대량 평가에서는 동시 요청 수 자체를 줄인다.
5xx도 같은 방식으로 재시도하고,400·401·404는 요청을 고친다.
스트리밍과 웹소켓
LLM 답을 한 조각씩 흘려보내는 스트리밍은 보통 SSE(Server-Sent Events) 로 한다. HTTP 응답을 끊지 않고 data: ... 줄을 계속 보내는 방식이다. 웹소켓(WebSocket) 은 HTTP로 시작한 연결을 업그레이드해 양쪽이 계속 메시지를 주고받는 통로다(ws://, 암호화는 wss://).
10. 서버를 띄웠는데 밖에서 안 보일 때
연구실 서버에서 띄운 데모가 노트북에서 http://gpu01:8000으로 안 열릴 때, 원인은 대개 넷 중 하나다.
① 바인딩: 127.0.0.1 vs 0.0.0.0
서버 프로그램은 시작할 때 어느 주소로 들어온 요청을 받을지(bind) 정한다.
| 바인딩 주소 | 누가 접속할 수 있나 |
|---|---|
127.0.0.1 | 그 서버 컴퓨터 자신만 |
0.0.0.0 | 그 컴퓨터의 모든 네트워크 주소로 들어온 요청 |
flask run, uvicorn, Jupyter는 안전을 위해 기본값이 localhost다. 그래서 서버 안에서 curl localhost:8000은 되는데 밖에서는 안 된다.
uvicorn app:app --host 0.0.0.0 --port 8000
jupyter lab --ip=0.0.0.0 --port=8888 --no-browser
# 서버에서 확인
ss -tlnp | grep 8000
# LISTEN ... 127.0.0.1:8000 ... ← 서버 자신만
# LISTEN ... 0.0.0.0:8000 ... ← 모든 주소공용 서버에서 0.0.0.0은 신중하게 쓴다. 그 네트워크의 누구나 접속할 수 있게 된다. 혼자 볼 거라면 127.0.0.1로 띄우고 SSH 터널로 보는 편이 안전하다(원격 GPU 서버 사용 입문).
② 서버의 방화벽
방화벽(firewall) 은 어떤 연결을 들여보낼지 정하는 규칙이다(Ubuntu는 흔히 ufw).
sudo ufw status
sudo ufw allow from 10.0.0.0/8 to any port 8000 proto tcp # 내부망에서만 8000 허용클라우드 서버는 콘솔의 보안 그룹(security group) 에서도 열어야 한다. 공용 서버라면 관리자에게 묻는다. Docker의 -p 8000:8000은 기본적으로 모든 주소에 포트를 열고 방화벽 규칙을 직접 추가해서, ufw 설정과 상관없이 밖에서 보일 수 있다. 서버 안에서만 쓰려면 -p 127.0.0.1:8000:8000으로 쓴다.
③ 네트워크 경로
서버가 10.x.x.x 같은 사설 IP만 가졌거나 학교 방화벽이 외부발 연결을 막으면, 학교 밖에서는 갈 길이 없다. VPN(§11)이나 점프 호스트를 거친 SSH를 쓴다.
④ 에러 메시지로 위치 가르기
| 에러 | 대개 무슨 뜻 | 볼 곳 |
|---|---|---|
Could not resolve host | 이름을 IP로 못 바꿈 | DNS, /etc/hosts, VPN |
Connection refused | 컴퓨터엔 닿았는데 그 포트에서 아무도 안 받음 | 프로그램 상태, 포트 번호, 127.0.0.1 바인딩 |
Connection timed out | 패킷이 중간에서 버려짐 | 방화벽, 사설망 경로, VPN |
HTTP 502/504 | 앞단 프록시는 살았는데 뒤쪽 앱이 응답 안 함 | 뒤쪽 앱과 그 로그 |
refused는 “문을 두드렸더니 거절”, timed out은 “문까지 가지도 못함”이다.
11. VPN, 프록시, 리버스 프록시, 로드밸런서, CDN
VPN(Virtual Private Network) 은 내 컴퓨터와 기관 내부망 사이에 암호화된 터널을 만든다. 연결하면 내부망의 사설 IP를 받아서, 집에서도 연구실 서버(10.0.1.23)에 접속할 수 있다. DNS도 기관 것으로 바뀌어 gpu01.lab.example.ac.kr 같은 내부 이름은 VPN을 켜야만 풀리기도 한다.
프록시(proxy) 는 클라이언트 대신 바깥으로 요청을 보내 주는 중개 서버다. 기관 서버는 HTTP_PROXY, HTTPS_PROXY(예외는 NO_PROXY) 환경 변수로 프록시를 지정하기도 한다. 서버에서 pip install만 멈추면 이 설정을 의심한다. 리버스 프록시(reverse proxy) 는 반대로 서버 앞에서 바깥 요청을 받아 뒤쪽 앱에 넘긴다. nginx가 443번에서 HTTPS를 처리하고 127.0.0.1:8000의 앱으로 넘기는 구성이 대표적이다. 로드밸런서(load balancer) 는 같은 앱을 띄운 여러 서버에 요청을 나눈다. CDN(Content Delivery Network) 은 정적 파일을 세계 곳곳에 복사해 두고 가까운 곳에서 내려준다(Cloudflare 등). 이런 중간 계층이 있을 때 502·504가 나면 “앞단은 살았고 뒤가 문제”라고 읽는다.
12. 진단 도구 치트시트
“이름 → 길 → 문 → 대화” 순서로 확인하면 빠르다.
# 내 쪽 상태
ifconfig # macOS: 내 IP (en0이 보통 Wi-Fi)
ip addr # Linux: 내 IP
ip route # Linux: 기본 게이트웨이 (macOS는 route -n get default)
# 이름과 길
dig +short gpu01.lab.example.ac.kr # DNS가 풀리나?
ping -c 4 10.0.1.23 # 닿나? (ICMP)
traceroute api.example.com # 어떤 라우터를 거쳐 가나? 어디서 끊기나?
# 문(포트)
nc -zv gpu01 8000 # 밖에서: 그 포트가 열렸나? (-z 연결만 시도, -v 결과 출력)
ss -tlnp # Linux 서버 안에서: TCP 대기 포트와 프로세스
lsof -nP -iTCP -sTCP:LISTEN # macOS: 대기 중인 TCP 포트와 프로세스
lsof -i :8000 # 8000번을 잡은 프로세스 (Address already in use일 때)
# 대화(HTTP)
curl -v https://api.example.com/v1/modelsping이 안 된다고 서버가 죽은 것은 아니다. ICMP(ping이 쓰는 신호)만 막혀 있을 수 있으니 nc로 실제 포트를 확인한다.
curl -v 출력은 줄 머리표로 읽는다.
* Connected to api.example.com (203.0.113.10) port 443 ← * : 진행 상황 (DNS, 연결, TLS)
* SSL connection using TLSv1.3 ...
> GET /v1/models HTTP/2 ← > : 내가 보낸 요청
> Host: api.example.com
< HTTP/2 401 ← < : 서버의 응답
< content-type: application/json어디까지 나오고 멈췄는지가 곧 막힌 층이다. Connected가 안 나오면 이름·길·포트, TLS 줄에서 에러면 인증서, 응답 코드가 나왔으면 HTTP 수준의 문제다.
13. 아주 짧은 암기
- IP는 컴퓨터, 포트는 그 안의 프로그램.
- 사설 IP는
10.x,172.16~31.x,192.168.x. 내부망 밖에서는 직접 못 간다. /24는 앞 세 숫자가 같은 동네, 기기 254대. 숫자가 작을수록 동네가 크다.- NAT 뒤에서는 나가는 건 되고 들어오는 건 안 된다.
- DNS는 이름 → IP. 바꿔도 TTL 동안 옛 값이 남는다.
- 2xx 성공, 3xx 이동, 4xx 내 잘못, 5xx 서버 잘못.
429와5xx만 기다렸다 재시도. - 자물쇠는 “암호화 + 도메인 확인”이지 “믿을 만한 사이트”가 아니다.
127.0.0.1에 바인딩하면 그 컴퓨터 자신만 접속한다. 밖에서 보려면0.0.0.0또는 SSH 터널.refused는 문 앞에서 거절,timed out은 문까지 못 감.- 확인 순서: 이름(
dig) → 길(ping) → 문(nc·ss) → 대화(curl -v).
입문자가 조심할 점
- 공용 서버에서 생각 없이
0.0.0.0에 띄우지 않는다. 인증 없는 Jupyter가 열리면 남이 내 계정으로 코드를 실행할 수 있다. - API 키가 새는 길을 막는다.
http://로 보내지 않는다.curl -v출력에는Authorization헤더가 그대로 찍히니 공유 전에 지운다. - 방화벽·포트 포워딩·VPN 설치는 관리자 영역이다. 연구실·학교 서버에서는 기관 규정을 먼저 확인한다.
- 내부 서버에
timed out이 나면 VPN부터 본다. 끊겨 있는 경우가 많다.
출처
- Kurose, J. F. & Ross, K. W. Computer Networking: A Top-Down Approach (8th ed.). Pearson
- IETF RFC 791 (IPv4), 8200 (IPv6), 1918 (사설 주소), 4632 (CIDR), 3927 (
169.254.0.0/16), 5737 (문서용 예시 주소), 6761 (.test), 2131 (DHCP), 1035 (DNS), 9293 (TCP), 768 (UDP). https://www.rfc-editor.org - IANA, Service Name and Transport Protocol Port Number Registry. https://www.iana.org/assignments/service-names-port-numbers/
- IETF RFC 9110 (HTTP Semantics), 6585 (
429), 6265 (쿠키), 8446 (TLS 1.3), 3986 (URI), 8259 (JSON), 6455 (WebSocket). https://www.rfc-editor.org/rfc/rfc9110 - MDN Web Docs, HTTP. https://developer.mozilla.org/en-US/docs/Web/HTTP
- Docker Docs, Packet filtering and firewalls. https://docs.docker.com/engine/network/packet-filtering-firewalls/
- Anthropic Docs, Errors (
529 overloaded_error). https://docs.anthropic.com/en/api/errors - curl manual,
curl(1). https://curl.se/docs/manpage.html - Linux man pages,
ss(8),ip(8),lsof(8),nc(1),dig(1)