우리는 매일 API를 설계하고, 외부 서비스를 호출하며, 코드를 배포합니다.
로컬 환경에서는 완벽했던 기능이 스테이징이나 운영 환경에만 올라가면 예상치 못한 문제들을 일으키는 경험,
모두 한 번쯤은 해보셨을 겁니다.
"내 코드는 문제가 없는데 왜 안 되지?"라는 의문은
대부분 우리가 작성한 코드 '너머'의 영역, 웹의 근간을 이루는 인프라 지식의 부재에서 비롯됩니다.
이 글은 '왜?'라는 질문에 답을 찾아가는 과정입니다.
DNS, HTTP, TCP/IP, URL의 동작 원리를 실제 업무 케이스와 연결하여,
문제의 원인을 체계적으로 추적하고 해결하는 실전 역량을 기르는 것을 목표로 합니다.
1. "방금 배포했는데 왜 접속이 안되죠?" - DNS와 캐시 문제
가장 흔하게 마주하는 상황 중 하나입니다. 개발팀에서 new-feature.my-service.com
이라는 새로운 서브도메인을 생성하고 서버에 코드를 배포했습니다. 하지만 몇몇 동료는 접속이 되는데, 내 자리에서는 계속 "사이트에 연결할 수 없음" 오류가 발생합니다.
이 문제의 90%는 DNS 때문입니다.
DNS(Domain Name System)는 my-service.com
같은 도메인 이름을 11.22.33.44
형태의 서버 IP 주소로 '번역'해주는 시스템입니다. 하지만 매번 이 번역 과정을 거치면 비효율적이므로, 우리 컴퓨터나 중간의 ISP(인터넷 서비스 제공업체)는 한 번 조회한 결과를 일정 시간 동안 저장해둡니다. 이것을 DNS 캐시라고 합니다.
[Case Study]
- 문제 상황: 내가 사용하는 인터넷 공유기(또는 내 PC)의 DNS 캐시가 아직
new-feature.my-service.com
이라는 새로운 정보로 업데이트되지 않았습니다. 그래서 내 PC는 "그런 주소는 없어"라고 스스로 판단하고 서버에 요청을 보내보지도 않는 것입니다. - 원인 분석: DNS 설정이 전 세계로 전파(Propagation)되는 데에는 시간이 걸립니다. 짧게는 몇 분에서 길게는 24시간 이상이 소요될 수 있으며, 그동안 사용자의 네트워크 환경에 따라 접속 여부가 달라질 수 있습니다.
- 해결 및 확인:
- 터미널에서 직접 확인:
nslookup
또는dig
명령어로 실제 DNS 조회가 어떻게 이루어지는지 확인할 수 있습니다.$ nslookup new-feature.my-service.com # 결과에 올바른 IP 주소가 나온다면, 도메인 자체는 잘 등록된 것입니다. # 만약 여기서도 조회가 안된다면 DNS 설정 자체를 확인해야 합니다.
- 로컬 DNS 캐시 삭제: 운영체제별로 DNS 캐시를 강제로 비우는 명령어를 실행해 볼 수 있습니다.
- Windows:
ipconfig /flushdns
- macOS:
sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder
- Windows:
- DNS 서버 변경: KT, SKT 같은 ISP의 DNS 서버 대신
8.8.8.8
(Google)이나1.1.1.1
(Cloudflare) 같은 Public DNS로 잠시 변경하여 테스트해보는 것도 좋은 방법입니다.
- 터미널에서 직접 확인:
2. "API 호출이 실패했어요" - HTTP 상태 코드로 원인 파악하기
프론트엔드 개발자가 "회원가입 API가 동작하지 않아요"라고 문의했습니다. 이때 백엔드 개발자는 무작정 코드를 들여다보기 전에, 실패한 HTTP 요청의 상태 코드를 먼저 확인해야 합니다. 상태 코드는 실패의 원인이 클라이언트에 있는지, 서버에 있는지를 알려주는 가장 중요한 단서입니다.
[Case Study: 회원가입 API POST /users
]
400 Bad Request
: 가장 흔한 경우입니다. 서버가 요청을 이해할 수 없다는 의미입니다.- 의심 지점: 클라이언트가 보낸 JSON Body의 형식이 잘못되었을 가능성이 높습니다.
- 필수 필드 (
email
) 누락 - 필드 값의 타입 오류 (e.g.,
age
필드에 문자열"스무살"
을 보냄) - JSON 문법 자체의 오류 (e.g., 콤마 누락)
- 필수 필드 (
- 액션: 서버의 유효성 검사(Validation) 로직을 확인하고, 클라이언트에게 정확한 요청 DTO 스펙을 다시 안내합니다.
- 의심 지점: 클라이언트가 보낸 JSON Body의 형식이 잘못되었을 가능성이 높습니다.
401 Unauthorized
: 인증 실패. 요청에 유효한 인증 정보가 없다는 의미입니다.- 의심 지점:
Authorization
헤더가 아예 없거나, 헤더에 담긴 토큰(JWT, OAuth 토큰 등)이 만료되었거나, 형식이 잘못되었습니다. - 액션: 클라이언트가 로그인/토큰 갱신 로직을 올바르게 처리하고 있는지, 토큰을 헤더에 정확히 포함하여 보내고 있는지 확인합니다.
- 의심 지점:
403 Forbidden
: 권한 없음. 인증은 되었지만, 해당 리소스에 접근할 권한이 없다는 의미입니다.- 의심 지점: '일반 사용자' 등급의 유저가 '관리자 전용' API를 호출했을 가능성이 높습니다.
- 액션: API의 권한 설정(Role-based access control) 로직을 확인하고, 기획된 정책이 맞는지 검토합니다.
404 Not Found
: 요청한 리소스를 찾을 수 없음.- 의심 지점: 클라이언트가 호출한 API의 엔드포인트(URL)에 오타가 있을 가능성이 가장 높습니다. (e.g.,
/user
vs/users
) - 액션: API 명세서에 정의된 정확한 URL 경로를 공유합니다.
- 의심 지점: 클라이언트가 호출한 API의 엔드포인트(URL)에 오타가 있을 가능성이 가장 높습니다. (e.g.,
500 Internal Server Error
: 명백한 서버의 잘못입니다.- 의심 지점: 서버 코드 어딘가에서 예외(Exception)가 발생했지만, 제대로 처리되지 않았습니다.
NullPointerException
(가장 흔한 범인)- 데이터베이스 커넥션 실패 또는 SQL 구문 오류
- 외부 서비스 호출 실패 후 예외 처리 누락
- 액션: 즉시 서버의 에러 로그(Sentry, CloudWatch Logs 등)를 확인하여 예외가 발생한 지점과 원인을 찾아 수정합니다.
- 의심 지점: 서버 코드 어딘가에서 예외(Exception)가 발생했지만, 제대로 처리되지 않았습니다.
3. "요청이 너무 느리거나 타임아웃이 나요" - TCP/IP와 네트워크 구간
API 서버의 로그를 아무리 확인해도 에러가 없고, 심지어 요청이 들어온 기록조차 없습니다. 하지만 클라이언트에서는 "Request Timeout" 오류가 발생합니다. 이럴 때는 시선을 한 단계 아래, 네트워크 계층으로 내려야 합니다.
HTTP 메시지는 TCP라는 프로토콜에 의해 잘게 쪼개져(패킷) 인터넷을 통해 전송됩니다. TCP는 데이터가 순서대로, 빠짐없이 안전하게 도착하는 것을 보장해주는 역할을 합니다.
[Case Study]
- 문제 상황: 클라이언트와 서버 사이의 네트워크 경로 어딘가에서 패킷이 유실되거나 지연되고 있습니다.
- 의심 지점:
- 방화벽: 서버 앞단의 방화벽(Security Group, WAF 등)이 특정 IP 대역이나 특정 포트로 들어오는 요청을 차단하고 있을 수 있습니다. 특히 사내망에서만 접속을 허용한 테스트 서버에 외부에서 접속하려 할 때 자주 발생합니다.
- 네트워크 지연 (Latency): 클라이언트와 서버의 물리적 거리가 너무 멀거나 (e.g., 한국 사용자 -> 미국 서버), 중간 인터넷 망의 상태가 불안정하여 TCP 핸드셰이크(연결 수립 과정)부터 느려지거나 패킷 전송이 지연될 수 있습니다.
- 로드 밸런서 설정 오류: 여러 대의 서버를 운영하는 경우, 앞단의 로드 밸런서가 특정 서버로 요청을 제대로 전달하지 못하거나, Health Check에 실패하여 해당 서버를 서비스에서 제외했을 수 있습니다.
- 액션:
ping
또는traceroute
명령어로 서버까지의 네트워크 경로와 지연 시간을 확인합니다.- 클라우드 서비스(AWS, GCP 등)를 사용한다면, 로드 밸런서와 방화벽 설정을 검토하여 요청이 차단되는 규칙은 없는지 확인합니다.
- 서버의 네트워크 인터페이스 카드(NIC) 성능이나 커넥션 제한 등 시스템 레벨의 지표를 모니터링합니다.
4. "API 설계가 이상해요" - RESTful 원칙과 URL 구조
잘 설계된 API는 그 자체로 훌륭한 문서 역할을 합니다. URL 구조만 봐도 어떤 리소스를 다루고 어떤 기능을 하는지 예측할 수 있어야 합니다.
[Bad Case]
GET /getPosts
POST /createPost
POST /updatePostById
GET /deletePost?id=123
[Good Case: RESTful API]
GET /posts
: 모든 게시글 목록을 조회합니다.POST /posts
: 새로운 게시글을 생성합니다.GET /posts/123
: ID가 123인 게시글을 조회합니다.PUT /posts/123
: ID가 123인 게시글을 전체 수정합니다.PATCH /posts/123
: ID가 123인 게시글을 일부 수정합니다.DELETE /posts/123
: ID가 123인 게시글을 삭제합니다.
URL 구조의 역할 분담https://api.my-service.com/v1/posts/123?sort=createdAt&order=desc#comments
- Scheme, Host, Port:
https://api.my-service.com
-> 리소스가 어디에 있는지 - Path:
/v1/posts/123
-> 어떤 리소스인지 (명사형, 계층 구조) - Query String:
?sort=createdAt&order=desc
-> 리소스를 어떻게 정렬/필터링/검색할지 (부가 조건) - Fragment:
#comments
-> 가져온 리소스에서 어디를 보여줄지 (클라이언트 전용)
이처럼 명확한 URL 구조는 프론트엔드-백엔드 간의 불필요한 커뮤니케이션 비용을 줄이고, API의 확장성과 가독성을 높여줍니다.
결론: 나무가 아닌 숲을 보는 개발자
우리가 마주하는 대부분의 장애는 코드 한 줄이 아닌, 여러 기술 요소가 얽힌 지점에서 발생합니다. 내 코드의 실행 환경인 웹 인프라에 대한 이해는 단순히 '알아두면 좋은 교양'이 아닙니다. 문제의 진짜 원인을 찾아내고, 더 안정적이고 확장성 있는 시스템을 설계하며, 다른 팀과 원활하게 소통하기 위한 개발자의 핵심 역량입니다.
오늘부터는 에러가 발생했을 때, 코드만 들여다보는 대신 한 걸음 물러나 "DNS는? 방화벽은? HTTP 요청은 올바른가?"라며 전체적인 데이터의 흐름을 그려보는 습관을 들여보는 것은 어떨까요? 그것이 바로 주니어와 시니어를 가르는 결정적인 차이를 만들 것입니다.
'학습 & 성장 (Learning & Growth) > 개발 공부 (Development Study)' 카테고리의 다른 글
[101] 웹 브라우저의 동작 원리: DOM, CSSOM, 그리고 BOM (2) | 2025.02.13 |
---|---|
[101] 웹 개발의 기본 중 기본! 웹 표준을 제대로 배워보자 (0) | 2025.02.12 |
[101] 타입스크립트의 unknown 타입 (1) | 2024.10.09 |
[101] 타입스크립트의 any 타입 (3) | 2024.10.09 |
[YDKJSY] ES6 모듈 패턴과 효율적인 상태 관리: 다양한 모듈 패턴 소개 (6) | 2024.09.04 |