네트워크 기초 입문

목표: “서버에서 띄웠는데 밖에서 안 열려요”, “DNS가 안 풀려요”, “API가 429를 줘요”, “VPN 켜고 들어오세요” 같은 말을 들었을 때 데이터가 어디를 거쳐 가는지 떠올리고, 명령어 몇 개로 어디서 막혔는지 직접 찾기.

서버·클라이언트·포트·localhost의 첫 소개는 컴퓨터 기본 도구와 개발 환경 입문 §9에 있고, 이 노트는 그 다음 단계다. 컨테이너 포트 연결은 Docker 입문, SSH 터널은 원격 GPU 서버 사용 입문에서 다룬다.


한 줄로 먼저

네트워크 통신은 “어느 컴퓨터로(IP) → 그 안의 어느 프로그램에게(포트) → 어떤 방식으로 나눠 보내서(TCP/UDP) → 무슨 말로 대화할지(HTTP 등)“를 층층이 나눠 맡는 구조다.

층 (실용판 4계층)맡는 질문이름표대표 기술OSI 7계층 이름
애플리케이션무슨 말로 대화하나?URL, 도메인HTTP, DNS, SSH7 응용 (5·6 포함)
전송어느 프로그램에게, 얼마나 확실히?포트TCP, UDP4 전송
인터넷어느 컴퓨터로, 어떤 길로?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 + JSON

0. 왜 알아야 하나

서버 데모가 노트북에서 안 열리는 것, 집에서만 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.25510.0.0.0/8학교·회사·클라우드 내부망
172.16.0.0 ~ 172.31.255.255172.16.0.0/12회사망, Docker 기본 네트워크(172.17.x.x)
192.168.0.0 ~ 192.168.255.255192.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로 바꿔 내보낸다.

  1. Mac(192.168.0.23:51234)이 요청을 보낸다.
  2. 공유기가 출발지를 203.0.113.45:40001로 바꾸고 이 짝을 표에 적는다.
  3. 응답이 :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이름 → IPv4api.example.com → 203.0.113.10
AAAA이름 → IPv6api.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

구분TCPUDP
연결먼저 연결을 맺음연결 없이 바로 보냄
신뢰성순서 보장, 빠진 것은 재전송보장 없음
쓰는 곳HTTP/1.1·HTTP/2, SSH, DBDNS 조회, 화상 통화, 게임, 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=abc123

HttpOnly는 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, 통신사)에서HTTPHTTPS
본문, 헤더(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 등. 포트를 생략하면 기본값(http 80, https 443)을 쓴다.
  • 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가 온다.

  1. Retry-After 헤더가 있으면 그만큼 기다린다.
  2. 없으면 지수 백오프(exponential backoff) 로 1초, 2초, 4초…처럼 간격을 늘려 재시도한다. 약간의 무작위 시간(jitter)을 더하면 동시 재시도가 몰리지 않는다.
  3. 대량 평가에서는 동시 요청 수 자체를 줄인다. 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/models

ping이 안 된다고 서버가 죽은 것은 아니다. 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부터 본다. 끊겨 있는 경우가 많다.

출처