외부 결제 연동이 갑자기 실패했다는 긴급 알림이 들어왔습니다. 개발팀은 "API 호출이 실패하는 것 같다"고 하는데, 정확히 어디서 왜 실패하는지 모릅니다. 로그를 보니 HTTP 500이 간헐적으로 찍힙니다. 그런데 직접 API를 호출해서 확인해볼 수 있는 인프라 엔지니어가 없습니다.
개발자가 코드를 뜯어보는 동안 인프라 엔지니어가 직접 curl로 API 엔드포인트를 찔러봤다면 5분 안에 원인을 좁힐 수 있었을 것입니다. 이 모듈은 그 5분을 만드는 기초입니다.
- 1HTTP 메서드(GET/POST/PUT/PATCH/DELETE)의 용도와 멱등성 차이를 설명할 수 있다
- 2주요 상태코드(2xx/4xx/5xx)의 의미를 보고 즉시 원인 범위를 좁힐 수 있다
- 3curl로 인증 헤더 포함 GET/POST 요청을 보내고 응답을 확인할 수 있다
- 4jq로 JSON 응답에서 원하는 필드를 파싱할 수 있다
- 5API 응답 시간을 측정해서 성능 이상 여부를 판단할 수 있다
HTTP 메서드와 상태코드
HTTP 메서드 — 동작 의미와 멱등성
REST API를 처음 보면 URL보다 메서드가 더 중요합니다. 같은 /users/123이라도 GET이면 조회, DELETE면 삭제입니다. 메서드를 틀리면 원하는 동작이 일어나지 않거나, 의도치 않은 변경이 발생합니다.
확대
| 메서드 | 용도 | 멱등성 | 요청 바디 |
|---|---|---|---|
| GET | 리소스 조회 | O | 없음 |
| POST | 새 리소스 생성 | X | 있음 |
| PUT | 리소스 전체 교체 | O | 있음 |
| PATCH | 리소스 일부 수정 | 구현에 따라 다름 | 있음 |
| DELETE | 리소스 삭제 | O | 보통 없음 |
멱등성이 중요한 이유: 네트워크 타임아웃 등으로 응답을 못 받았을 때 재시도해도 되는 메서드인지 판단하는 기준이 됩니다. GET, PUT, DELETE는 재시도해도 안전하지만 POST는 중복 생성이 발생할 수 있습니다.
# 요청 시뮬레이션 — 메서드별 curl 패턴
curl -X GET https://api.example.com/users/123
curl -X POST https://api.example.com/users
curl -X PUT https://api.example.com/users/123
curl -X PATCH https://api.example.com/users/123
curl -X DELETE https://api.example.com/users/123
상태코드 — 범주별 의미 파악
API 장애 신고가 들어왔을 때 상태코드는 가장 먼저 확인해야 할 첫 번째 단서입니다. 코드 범주(2xx / 4xx / 5xx)만 보아도 문제가 클라이언트 요청에 있는지, 서버 내부에 있는지 즉시 구분할 수 있어 원인 추적 방향이 달라집니다. 특히 401과 403을 혼동하거나 502와 504를 같은 의미로 보면 점검 방향이 틀려지므로, 범주별 의미를 정확히 파악해두는 것이 인프라 엔지니어의 기본 역량입니다.
확대
API 장애 대응에서 상태코드는 첫 번째 단서입니다. 코드 범주만 봐도 문제가 클라이언트 쪽인지 서버 쪽인지 구분됩니다.
2xx — 성공
| 코드 | 의미 | 주로 쓰이는 상황 |
|---|---|---|
| 200 OK | 요청 성공 | GET, PUT, PATCH 응답 |
| 201 Created | 리소스 생성 성공 | POST 응답, Location 헤더로 새 URL 전달 |
| 204 No Content | 성공하지만 응답 바디 없음 | DELETE, 일부 PUT 응답 |
4xx — 클라이언트 오류 (요청 자체의 문제)
| 코드 | 의미 | 확인할 것 |
|---|---|---|
| 400 Bad Request | 요청 형식/파라미터 오류 | 요청 바디 JSON 형식, 필수 파라미터 |
| 401 Unauthorized | 인증 실패 | Authorization 헤더, 토큰 만료 여부 |
| 403 Forbidden | 권한 없음 | 계정 권한 설정 |
| 404 Not Found | 리소스 없음 | URL 오타, 리소스 삭제 여부 |
| 415 Unsupported Media Type | Content-Type 불일치 | Content-Type 헤더 누락 또는 오타 |
| 422 Unprocessable Entity | 유효성 검사 실패 | 필드 형식, 값 범위 |
| 429 Too Many Requests | 요청 빈도 초과 | Rate Limit 설정, Retry-After 헤더 |
5xx — 서버 오류 (서버 측 문제)
| 코드 | 의미 | 확인할 것 |
|---|---|---|
| 500 Internal Server Error | 서버 내부 오류 | 서버 로그 (인프라 엔지니어 불필요, 앱 로그 확인) |
| 502 Bad Gateway | upstream 서버 비정상 | 뒷단 서비스 상태, 네트워크 연결 |
| 503 Service Unavailable | 서비스 일시 과부하 또는 점검 중 | 서버 부하, 배포 중 여부 |
| 504 Gateway Timeout | upstream 응답 지연 | 뒷단 서비스 응답 시간, 타임아웃 설정 |
요청 헤더 — 자주 쓰는 4가지
curl로 API를 호출했는데 401이 납니다. "Authorization 헤더 넣었는데요?"라고 하면 "어떤 형식으로 넣었어요?"라고 다시 묻습니다. Bearer인지 Basic인지, X-API-Key인지 서비스마다 다르고, Content-Type을 빠뜨리면 415가 납니다. 헤더 4가지의 역할을 구분해서 알아야 curl 테스트 한 번에 원인을 좁힐 수 있습니다.
헤더는 요청의 메타데이터입니다. API 인증, 데이터 형식, 클라이언트 식별 등 요청의 "문맥"을 담습니다. 인프라 엔지니어가 curl로 테스트할 때 헤더를 빠뜨리면 의미 있는 테스트가 되지 않습니다.
# 자주 쓰는 요청 헤더 4가지
Authorization: Bearer <JWT_TOKEN> # Bearer 토큰 인증 (OAuth2)
Authorization: Basic <BASE64> # Basic 인증 (user:pass를 base64 인코딩)
X-API-Key: <API_KEY> # API 키 인증 (서비스마다 헤더 이름이 다를 수 있음)
Content-Type: application/json # 요청 바디가 JSON임을 서버에 알림
Accept: application/json # 응답을 JSON으로 받고 싶다는 클라이언트 의사 표시
Authorization 유형 비교:
| 유형 | 형식 | 주로 쓰이는 곳 |
|---|---|---|
| Bearer | Authorization: Bearer <token> | REST API, OAuth2 (가장 흔함) |
| Basic | Authorization: Basic <base64> | 레거시 시스템, Jenkins, Artifactory |
| X-API-Key | X-API-Key: <key> | 외부 API 연동 (결제사, 공공 API) |
REST 요청 한 건이 서버 안에서 처리되는 순서 — 라우팅부터 응답 직렬화까지 6단계
요청이 네트워크를 타고 서버에 도착하기까지(DNS·TCP·TLS)는 앞선 흐름이 다뤘습니다. 여기서는 그 뒤 — 요청이 서버 프로세스 안에서 라우팅·인증·검증·처리를 거쳐 상태코드를 달고 나오기까지를 봅니다. 이 순서를 알면 응답 코드만 보고도 "요청이 어느 단계에서 멈췄나"를 짚어, 앱 코드를 열기 전에 손볼 곳을 좁힐 수 있습니다. 401은 인증 단, 422는 검증 단, 500대는 비즈니스 단이 막힌 신호입니다.
[클라이언트] POST /v1/orders (Authorization · Content-Type · JSON 바디)
│ 여기까지 도달하는 네트워크 구간은 http-request-flow가 다룸
① 파싱 메서드·경로·헤더를 읽고 Content-Type에 맞춰 바디 해석
│
② 라우팅 경로 → 리소스, 메서드 → 핸들러 매핑
│ 경로 없음 → 404 / 경로는 있는데 그 메서드 없음 → 405
│
③ 인증·인가 토큰·키로 신원 확인 → 없음·만료 → 401 / 권한 부족 → 403
│
④ 검증 필수 필드·타입·값 범위 확인
│ 바디 형식 깨짐 → 400·415 / 값 규칙 위반 → 422
│
⑤ 비즈니스·DB 실제 로직 실행 · DB 조회·쓰기 · 외부 호출
│ 내부 예외·뒷단 장애 → 500·502·503·504
│
⑥ 직렬화·응답 결과를 JSON으로 직렬화 + 상태코드 부여
▼ 조회 200 / 생성 201 / 본문 없음 204
[클라이언트] 상태코드 + 응답 바디 수신
각 단계에서 무슨 일을 하고, 실패하면 어떤 코드로 드러나나:
| 단계 | 하는 일 | 이 단계에서 실패하면 |
|---|---|---|
| ① 파싱 | 메서드·경로·헤더를 읽고 Content-Type에 맞춰 바디 파싱 | 선언과 다른 형식 → 415 / JSON 깨짐 → 400 |
| ② 라우팅 | 경로를 리소스에, 메서드를 핸들러에 매핑 | 경로 자체가 없음 → 404 / 경로는 있으나 그 메서드 핸들러가 없음 → 405(Allow 헤더에 허용 메서드 표시) |
| ③ 인증·인가 | 토큰·키로 신원 확인 후 권한 확인 | 신원 확인 실패(없음·만료) → 401 / 신원은 됐으나 권한 없음 → 403 |
| ④ 검증 | 필수 필드·타입·값 범위를 규칙과 대조 | 형식은 맞지만 값이 규칙 위반 → 422 / 필수 파라미터 누락 → 400 |
| ⑤ 비즈니스·DB | 실제 로직 실행, DB 조회·쓰기, 외부 호출 | 내부 예외 → 500 / 뒷단 비정상·지연 → 502·503·504 |
| ⑥ 직렬화·응답 | 결과를 JSON 등으로 직렬화하고 상태코드 부여 | 성공: 조회 200 · 생성 201 · 본문 없음 204 |
재시도 안전 여부는 ②에서 정해진 메서드에 달렸습니다 — GET·PUT·DELETE는 ⑤가 같은 결과를 내므로 응답을 못 받아 재시도해도 안전하지만, POST는 ⑤에서 매번 새 리소스를 만들어 중복이 생깁니다. 그래서 POST 재시도에는 멱등키(같은 키면 한 번만 처리)가 필요합니다. 정리하면 상태코드는 "요청이 어느 단계까지 갔는가"의 지도입니다 — 401·403이면 ③에서, 422면 ④에서, 500대면 ⑤에서 멈춘 것이므로, curl로 같은 요청을 찔러 코드만 봐도 손볼 단(인증 설정·요청 형식·뒷단 상태)이 곧장 좁혀집니다.
curl로 API 테스트하기
-s(silent)로 진행 표시를 숨기고, -H로 헤더를 추가합니다. | jq .로 응답 JSON을 들여쓰기 형태로 보기 좋게 출력합니다. jsonplaceholder는 실제 테스트용 공개 API입니다 — TOKEN 자리에 아무 문자열을 넣어도 동작합니다.
# 특정 필드만 추출
curl -s https://jsonplaceholder.typicode.com/users | jq '.[0].name'
curl -s https://jsonplaceholder.typicode.com/users | jq '.[].email'
curl -s https://jsonplaceholder.typicode.com/users | jq '.[0] | {id, name, email}'
curl -s -H 'Authorization: Bearer TOKEN' https://jsonplaceholder.typicode.com/users | jq- curl 응답 먼저 확인, 그 다음 | jq . 로 JSON 구조 파싱, 마지막으로 jq 필터로 원하는 필드 추출 — jq 파싱 실패(parse error)이면 응답이 JSON이 아니거나 Content-Type이 text/html인 것(에러 페이지 응답)
- 응답 기준: HTTP 200이고 jq 파싱 성공=정상 API 응답, HTTP 200인데 jq parse error=JSON 형식 오류 또는 에러 페이지 반환, HTTP 401=인증 헤더 누락, 403=권한 부족, 404=엔드포인트 경로 오류
- jq .[] 이 빈 배열을 반환하면 → 실제로 데이터가 없거나 필터 조건이 잘못된 것 — jq length 로 배열 크기 먼저 확인 후 0이면 API 응답 자체에 데이터 없음, 0 이상이면 필터 표현식 재검토
-X POST로 메서드를 지정하고, -H 'Content-Type: application/json'으로 바디 형식을 알립니다. -d '...'에 JSON 바디를 넣습니다. 실제 서비스 API는 201 Created를 반환하면서 생성된 리소스 정보를 응답합니다.
# 응답 상태코드와 헤더까지 확인
curl -s -o /dev/null -w "%{http_code}" \
-X POST -H "Content-Type: application/json" \
-d '{"name":"test","email":"test@example.com"}' \
https://jsonplaceholder.typicode.com/users
# 출력: 201
curl -s -X POST -H 'Content-Type: application/json' -d @/tmp/body.json https://jsonplaceholder.typicode.com/users- -w "%{http_code}" 로 상태 코드 먼저 확인, 그 다음 응답 바디에 id 필드 존재 여부 확인 — 201 Created+id 필드=서버가 새 리소스 생성 완료, 200+id=일부 API는 PUT과 동일하게 처리
- 상태 코드 기준: 201=리소스 생성 성공(정상), 200=멱등 처리, 400=요청 바디 형식 오류(Content-Type 또는 JSON 구조 문제), 422=유효성 검사 실패(필수 필드 누락) — POST에서 200이 아닌 201을 기대하는 API를 200으로 처리하면 클라이언트 로직 오류
- 201인데 응답 바디에 id가 없으면 → API 서버가 생성된 리소스 정보를 응답에 포함 안 한 것 — API 명세서의 Response 스키마 확인 후 Location 헤더에 리소스 URL이 있는지 curl -i 로 헤더 포함 출력 확인
-i(include)는 응답 헤더를 바디와 함께 출력합니다. Content-Type, Date, 캐시 헤더 등을 확인할 수 있습니다. 더 상세한 디버그가 필요하면 -v(verbose)를 씁니다.
# 요청/응답 전체 흐름 디버그 (연결, 헤더, 바디 모두 출력)
curl -v https://jsonplaceholder.typicode.com/users/1 2>&1 | head -40
# 응답 시간 측정 (API 성능 이상 확인)
curl -s -o /dev/null -w "time_total: %{time_total}s\ntime_connect: %{time_connect}s\n" \
https://jsonplaceholder.typicode.com/users/1
curl -i https://jsonplaceholder.typicode.com/users/1- curl -i 첫 줄 응답 코드 먼저 확인, 그 다음 Content-Type 헤더로 응답 형식 확인, 마지막으로 -w time_total 로 응답 시간 측정 — Content-Type이 text/html이면 에러 페이지를 JSON으로 오해하기 쉬우므로 주의
- 응답 시간 기준: time_total 1초 미만=정상, 2~3초=네트워크 지연 또는 서버 처리 지연 시작, 5초 이상=API 타임아웃 설정 확인 필요 — time_connect가 크면 네트워크 지연, time_starttransfer가 크면 서버 처리 지연
- Content-Type: application/json인데 jq parse error가 나면 → 응답 앞에 BOM(바이트 순서 표시)이 있거나 인코딩 문제 — curl -s ... | xxd | head -3 로 응답 첫 바이트 확인
jq는 JSON 데이터를 커맨드라인에서 필터링하는 도구입니다. API 응답에서 필요한 필드만 추출하거나, 배열을 순회하거나, 조건으로 필터링할 수 있습니다.
# 자주 쓰는 jq 패턴
curl -s https://jsonplaceholder.typicode.com/posts | jq '.[0]' # 첫 번째 객체
curl -s https://jsonplaceholder.typicode.com/posts | jq '.[].userId' # 모든 userId 추출
curl -s https://jsonplaceholder.typicode.com/posts | jq '.[] | select(.userId == 1)' # 필터
curl -s https://jsonplaceholder.typicode.com/posts | jq 'length' # 배열 길이
curl -s https://jsonplaceholder.typicode.com/users/1 | jq '.address.city' # 중첩 필드
curl -s https://jsonplaceholder.typicode.com/posts | jq first- jq length 로 배열 크기 먼저 확인, 그 다음 jq first 로 첫 번째 요소 구조 파악, 마지막으로 원하는 필드 추출 — 구조를 모르고 필터 표현식부터 쓰면 null이 출력돼 오해할 수 있음
- 결과 기준: jq length 가 100이상=대용량 응답으로 메모리 주의, jq ".[0].필드" 가 null=해당 필드명이 다른 것(대소문자 오타 또는 API 스키마 확인 필요), jq parse error=응답이 JSON 배열이 아닌 단일 객체
- jq select(.userId == 1) 결과가 빈 출력이면 → 실제로 해당 조건을 만족하는 항목이 없거나 필드 타입 불일치 — jq ".[0].userId" 로 실제 userId 타입 확인(숫자 vs 문자열) 후 조건 수정
OpenAPI 문서 읽기
OpenAPI(Swagger) 문서 — 실무에서 필요한 부분만
외부 결제 API 연동 담당을 맡았는데 문서 링크를 받았습니다. 문서가 100페이지가 넘습니다. 어디서부터 읽어야 할지 모르겠고, 테스트 환경과 운영 환경 URL이 다른데 어디에 있는지도 찾기 어렵습니다. OpenAPI 문서에는 일정한 구조가 있어서, 핵심 항목 6개만 찾을 줄 알면 100페이지를 다 읽지 않아도 curl 테스트를 시작할 수 있습니다.
외부 API 연동 전에 API 명세서를 읽어야 합니다. OpenAPI(구 Swagger) 문서는 표준화된 API 명세 형식입니다. 문서 전체를 다 읽을 필요는 없고, 연동에 필요한 핵심 정보만 빠르게 찾는 법을 익혀두는 것으로 충분합니다.
OpenAPI 문서에서 확인할 항목:
- BaseURL / Servers — API 기본 주소 (스테이징 vs 운영 URL 구분)
- Authentication — 인증 방식 (Bearer, API Key, OAuth)
- Endpoint — 메서드 + 경로 (
POST /v1/payments) - Parameters — 경로 파라미터, 쿼리 파라미터, 필수/선택 구분
- Request Body — 필드명, 타입, 필수 여부
- Responses — 성공(200/201) 응답 스키마, 에러 응답 형식
# Swagger UI가 없는 경우 — OpenAPI YAML/JSON 파일 직접 확인
# 실제 연동 전 curl로 직접 검증
# ① 인증 방식 확인 후 토큰 발급
curl -s -X POST -H "Content-Type: application/json" \
-d '{"client_id":"ID","client_secret":"SECRET"}' \
https://auth.example.com/token | jq .access_token
# ② 발급받은 토큰으로 실제 API 호출
TOKEN=$(curl -s -X POST ... | jq -r .access_token)
curl -s -H "Authorization: Bearer $TOKEN" https://api.example.com/v1/resource
트러블슈팅
원인: Bearer 토큰이 만료됐거나 Authorization 헤더 형식이 잘못됐습니다. 인프라 환경에서는 서버 시간 차이로 토큰이 일찍 만료되는 경우도 있습니다.
# ① 토큰 내용 확인 (JWT는 Base64 디코딩 가능)
TOKEN="eyJhbGci..."
echo $TOKEN | cut -d. -f2 | base64 -d 2>/dev/null | jq .exp
# exp 값을 date 명령으로 변환해 만료 시각 확인
date -d @<exp값>
# ② Authorization 헤더 형식 확인 (Bearer 앞에 공백 주의)
curl -v -H "Authorization: Bearer $TOKEN" https://api.example.com/resource 2>&1 | grep "Authorization"
# ③ 토큰 갱신 (OAuth2 refresh_token 방식)
curl -s -X POST -H "Content-Type: application/json" \
-d '{"grant_type":"refresh_token","refresh_token":"REFRESH_TOKEN"}' \
https://auth.example.com/token
# ④ 서버 시간 동기화 확인 (토큰 유효성 시간 비교)
timedatectl status | grep "Local time"
원인: 요청에 Content-Type: application/json 헤더가 없거나, 잘못된 형식이 지정됐습니다. curl로 테스트할 때 가장 자주 빠뜨리는 헤더입니다.
# ① 현재 보내는 헤더 확인 (-v로 요청 헤더 출력)
curl -v -X POST -d '{"key":"value"}' https://api.example.com/resource 2>&1 | grep ">"
# Content-Type 줄이 없으면 헤더 누락
# ② Content-Type 헤더 추가
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"key":"value"}' \
https://api.example.com/resource
# ③ API가 application/x-www-form-urlencoded를 요구하는 경우
curl -s -X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "key=value&key2=value2" \
https://api.example.com/resource
심화 — 목록을 '끝까지' 읽을 때 무너지는 것들
심화: REST를 실무로 쓰면 만나는 다음 벽 — 페이지네이션과 버전
단건 GET/POST는 쉽습니다. 문제는 "수십만 건짜리 목록을 전량 수집"하거나 "API가 바뀌어도 우리 연동이 안 깨지게" 해야 할 때입니다. REST 스펙 어디에도 안 적혀 있지만, 실무 연동의 안정성은 대부분 이 두 가지에서 갈립니다.
- 컬렉션은 페이지로 온다: 목록 API는 한 번에 다 주지 않고 나눠 줍니다. 흔한 offset/limit(
?offset=200&limit=100)은 "정렬된 목록의 N번째부터"를 매 요청 다시 계산합니다 — 구현이 쉽지만, 수집 도중 원본에 삽입·삭제가 일어나면 경계가 밀려 같은 레코드가 두 페이지에 걸리거나(중복) 건너뛰어집니다(누락). 깊은 offset은 DB가 앞 N개를 다 세느라 느려집니다. - 커서(keyset)가 더 안전하다: 그래서 규모 있는 API는 커서 방식을 씁니다. 응답에
next토큰(보통 마지막 항목의 안정 키)을 주고, 다음 요청은 "마지막으로 읽은 키 다음부터"를 가져옵니다. 절대 위치가 아니라 상대 기준이라, 도중에 데이터가 바뀌어도 중복·누락이 줄고 깊은 페이지도 빠릅니다. - API는 버전으로 진화한다: 필드 삭제·타입 변경 같은 파괴적 변경을 그냥 배포하면 기존 연동이 깨집니다. 그래서 제공자는 경로(
/v1/,/v2/)나 헤더로 버전을 나눠 옛 클라이언트를 살려둡니다. 연동하는 쪽은 "우리가 어느 버전에 묶여 있는지"와 "그 버전의 지원 종료(deprecation) 일정"을 알아야 합니다. - 한계 인식: 목록 순회는 rate limit·타임아웃과도 얽힙니다. 전량 수집은 한 번의 거대한 요청이 아니라 "안정적으로 재개 가능한 여러 작은 요청"으로 설계해야 합니다.
정리하면, 단건 호출이 되는 것과 "대량·장기 연동이 안 깨지는 것"은 다른 문제이고, 그 차이를 만드는 게 페이지네이션 방식과 버전 관리입니다.
상황: ?offset=0&limit=100부터 끝까지 페이지를 올려가며 긁는 배치입니다. 수집 결과에 같은 id가 두 번 들어오거나, 분명 존재하는 id가 빠집니다. 총 건수도 실행할 때마다 조금씩 다릅니다. 활발히 쓰이는(계속 데이터가 들어오는) 목록일수록 심합니다.
원인: offset 페이지네이션이 "정렬된 목록의 offset번째부터"를 매 요청 새로 계산하기 때문입니다. 수집이 여러 페이지에 걸쳐 진행되는 동안 원본에 새 레코드가 삽입되면 뒤 페이지의 경계가 한 칸씩 밀려 방금 읽은 레코드를 다음 페이지에서 또 읽고(중복), 삭제가 일어나면 반대로 건너뜁니다(누락). 정렬 키가 불안정(생성시간이 겹치거나 가변)하면 더 심합니다.
진단: API 스펙에서 커서 기반(next·cursor 토큰) 페이지네이션 지원 여부를 확인합니다. 응답에 안정적 정렬 키(불변·유일한 id 등)와 has_more 표시가 있는지, offset만 지원한다면 "특정 시점 이후로 고정"할 수 있는 필터(created_at 이하 등)가 있는지 봅니다.
해결: 가능하면 커서(keyset) 페이지네이션으로 바꿉니다 — 마지막으로 읽은 안정 키보다 큰 것(id가 마지막 값보다 큰 것)부터 요청하면 삽입·삭제에 흔들리지 않습니다. offset만 지원하는 API라면 수집 창을 시점으로 고정(created_at이 시작 시각 이하인 것만)하거나, updated_at 기반 증분 수집으로 바꿉니다. 마지막 안전망으로 수집 결과를 id로 dedup해 중복을 제거합니다.
실제 업무에서 이 지식이 쓰이는 상황:
외부 API 장애 신고가 들어왔을 때, 인프라 엔지니어가 직접 curl로 해당 엔드포인트를 찔러보는 것이 가장 빠른 1차 진단입니다.
# 외부 API 장애 1차 진단 루틴
# 1. 기본 연결 확인 (DNS, 네트워크)
curl -s -o /dev/null -w "%{http_code}" https://api.paymentgateway.com/health
# 2. 인증 포함 실제 요청 테스트
curl -s -H "X-API-Key: $API_KEY" \
https://api.paymentgateway.com/v1/payments/test \
| jq '{status: .status, message: .message}'
# 3. 응답 시간 측정 (SLA 확인)
curl -s -o /dev/null \
-w "connect: %{time_connect}s | total: %{time_total}s\n" \
https://api.paymentgateway.com/v1/payments/test
# 4. 상태코드별 판단:
# 200/201 → API 정상, 앱 로직 문제일 가능성
# 401/403 → 인증 정보 만료 또는 IP 화이트리스트 문제
# 500/502 → 외부 API 서버 이상, 담당사에 연락
# timeout → 네트워크 구간 문제, 방화벽 확인
이 루틴을 외울 필요는 없습니다. curl 옵션 몇 가지만 익혀두면 API 연계 문제의 70%는 코드를 보기 전에 범위를 좁힐 수 있습니다. ---
명령어·단축키 빠른 참조
이 모듈에서 curl과 jq로 API를 찔러보고 응답을 진단할 때 쓴 옵션을 모았습니다. "예" 열의 조합을 그대로 써도 됩니다.
| 명령어/단축키 | 용도 | 자주 쓰는 예 |
|---|---|---|
curl -X | HTTP 메서드 지정 호출 | curl -X DELETE https://api.example.com/users/123 |
curl -H | 인증·형식 헤더 추가 | -H "Authorization: Bearer $TOKEN" / -H "Content-Type: application/json" |
curl -d | 요청 바디 전송(자동 POST) | -d @body.json 또는 -d '{"name":"a"}' |
curl -w "%{http_code}" | 응답 상태코드만 추출 | curl -o /dev/null -s -w "%{http_code}\n" URL |
curl -w "%{time_total}" | 응답 시간 측정(성능 이상) | curl -o /dev/null -s -w "%{time_connect} %{time_total}\n" URL |
curl -i / -v | 응답 헤더 포함 / 요청·응답 전체 디버그 | curl -i URL / curl -v URL 2>&1 | head -40 |
jq | JSON 응답 파싱·필터 | jq '.[] | select(.userId==1)' / jq length |
jq -r | 따옴표 없는 원시 값 추출(변수 대입용) | TOKEN=$(curl -s ... | jq -r .access_token) |
cut -d. -f2 + base64 -d | JWT payload 디코드 | echo $TOKEN | cut -d. -f2 | base64 -d | jq .exp |
date -d @ | epoch(exp) → 사람이 읽는 시각 변환 | date -d @1730000000 |
timedatectl status | 서버 시간 확인(토큰 만료 오판 방지) | timedatectl status | grep "Local time" |
관련 모듈로 더 깊이:
- 트래픽 제어와 이벤트 기반 연계 구조 — REST 호출 앞단에서 인증·라우팅을 담당하는 게이트웨이 구조
- 써드파티 API와 공공 인프라 연계 실무 — 외부 REST API를 연계할 때의 인증·화이트리스트·장애 대응
- HTTP 요청이 서버에 도달하기까지의 전체 흐름 — REST 호출의 상태코드와 요청 흐름을 더 근본적으로 이해하는 법
다음 모듈에서는 이 API 호출 앞에 위치하는 API Gateway 구조와 Webhook 연계 패턴을 다룹니다.