API는 처음 보면 전문가만 다루는 문서처럼 느껴집니다. 하지만 본질은 단순합니다. 무엇을 보내면 무엇이 돌아오는지 미리 정해 둔 약속에 가깝습니다.
그래서 입문 단계에서는 인증 방식, 상태 코드 표, 헤더 목록을 한꺼번에 외우려 하기보다 요청과 응답의 구조부터 눈에 익히는 편이 낫습니다. 이 글에서는 실제로 오가는 텍스트 한 왕복을 통째로 펼쳐 놓고, 각 줄이 무엇을 뜻하는지 짚어 보겠습니다.
요청은 네 조각으로 되어 있습니다
HTTP 요청은 형태가 정해진 텍스트 덩어리입니다. 맨 첫 줄에 무엇을 어디에 요청하는지가 들어가고, 그다음 줄부터 헤더가 이어지고, 빈 줄 하나로 헤더가 끝났음을 알리고, 필요하면 그 아래에 본문이 붙습니다. 라이브러리를 쓰든 브라우저를 쓰든 결국 이 네 조각이 만들어져 나갑니다.
초보자가 API를 어렵게 느끼는 이유 중 하나는 이 텍스트를 한 번도 눈으로 본 적이 없기 때문입니다. 함수 호출 한 줄 뒤에 무엇이 만들어지는지 모르니, 문제가 생겨도 어디를 봐야 할지 감이 안 잡힙니다. 아래는 사용자 한 명을 조회하는 요청 전문입니다.
RAW REQUEST
GET /v1/users/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer <access-token>
User-Agent: curl/8.4.0
빈 줄까지가 요청 헤더입니다. GET에는 보통 본문이 없습니다.
첫 줄의 GET은 무엇을 하려는지를 나타냅니다. 읽기만 할 때 씁니다. /v1/users/1은 대상입니다. 여기서 1은 사용자 번호이고, 이 부분이 문서에 /v1/users/{id}처럼 적혀 있는 경우가 많습니다. 뒤의 HTTP/1.1은 이 텍스트가 따르는 규격의 버전입니다.
Host는 어느 서버에 보내는지, Accept는 어떤 형식으로 받고 싶은지를 알립니다. Authorization은 내가 누구인지 증명하는 값입니다. 토큰 방식이면 대개 Bearer 다음에 발급받은 문자열이 붙습니다. 이 줄 하나가 빠지면 뒤에서 볼 401 응답이 돌아옵니다.
보내는 것이 있으면 본문이 생깁니다
읽기만 하는 요청과 무언가를 만들거나 바꾸는 요청은 모양이 다릅니다. 새 사용자를 등록한다면 보낼 데이터가 있으니 본문이 붙고, 그 본문이 어떤 형식인지 알려 주는 Content-Type 헤더가 요청 쪽에도 생깁니다. 문서에서 "요청 예시"라고 적힌 부분은 거의 이 모양입니다.
REQUEST WITH BODY
POST /v1/users HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer <access-token>
{
"name": "Mina",
"email": "mina@example.com"
}
보낼 데이터가 생기면 헤더 아래에 빈 줄 하나를 두고 본문이 붙습니다.
여기서 눈여겨볼 것은 id가 없다는 점입니다. 번호는 서버가 붙이니 보낼 이유가 없습니다. 문서를 볼 때 내가 채워 보내는 값과 서버가 채워 돌려주는 값을 구분해 두면, 어떤 필드를 필수로 넣어야 하는지가 훨씬 빨리 정리됩니다. 이 구분을 안 해 두면 서버가 만들어야 할 값을 억지로 넣어 보내다가 400 응답을 받게 됩니다.
응답은 첫 줄과 본문을 따로 봅니다
RAW RESPONSE
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Cache-Control: no-store
{
"id": 1,
"name": "Mina",
"email": "mina@example.com",
"created_at": "2026-03-11T09:20:00Z"
}
상태 줄, 헤더, 빈 줄, 본문. 요청과 같은 구조입니다.
첫 줄의 200 OK는 요청 처리가 끝났다는 신호입니다. 여기서 알아야 할 것은 숫자 하나하나의 뜻이 아니라 앞자리입니다. 2로 시작하면 성공, 3으로 시작하면 다른 곳을 보라는 안내, 4로 시작하면 내가 보낸 요청에 문제가 있다는 뜻, 5로 시작하면 서버 쪽에서 터졌다는 뜻입니다. 이 네 갈래만 구분해도 원인을 찾는 방향이 정해집니다.
Content-Type은 본문을 어떻게 해석해야 하는지 알려 줍니다. application/json이라고 적혀 있는데 파싱이 실패한다면, 실제로는 HTML 오류 페이지가 돌아오고 있는 경우가 많습니다. 응답 본문을 그대로 출력해 보면 몇 초 만에 확인됩니다.
본문의 JSON은 키와 값의 묶음입니다. 문서를 읽을 때는 여기서 어떤 키가 항상 있는지, 어떤 키가 없을 수도 있는지를 구분해 두는 것이 실전에서 가장 오래 갑니다. 값 하나를 꺼내려다 프로그램이 죽는 대부분의 상황이 이 구분을 안 해 둔 데서 시작합니다.
curl -i https://api.example.com/v1/users/1처럼 -i를 붙이면 응답 헤더까지 같이 출력됩니다. 요청 쪽까지 보고 싶으면 -v를 씁니다. 라이브러리로 감싸기 전에 이 명령으로 한 번 왕복시켜 보면, 문제가 내 코드에 있는지 서버 응답에 있는지 바로 갈립니다.
성공 응답보다 실패 응답을 먼저 읽습니다
API 연동에서 시간을 잡아먹는 쪽은 항상 실패한 요청입니다. 그런데 많은 입문자가 상태 코드 숫자만 보고 검색창으로 갑니다. 대부분의 API는 본문에 진짜 원인을 적어 보냅니다.
ERROR RESPONSE
HTTP/1.1 401 Unauthorized
Content-Type: application/json
{
"error": {
"code": "token_expired",
"message": "Access token has expired.",
"request_id": "req_8f21c"
}
}
401이라는 숫자보다 token_expired가 훨씬 많은 것을 말해 줍니다.
같은 401이라도 토큰이 만료된 것과 애초에 헤더를 안 보낸 것은 고치는 방법이 완전히 다릅니다. 상태 코드는 분류이고, 본문이 진단입니다. request_id 같은 값이 있다면 그대로 복사해 두는 것이 좋습니다. 문의할 일이 생겼을 때 이 값 하나가 대화를 크게 줄여 줍니다. 에러 메시지를 읽는 습관 자체가 낯설다면 에러 메시지를 처음 읽을 때를 먼저 보셔도 좋습니다.
샘플 한 쌍만 보고 넘어가면 나중에 깨집니다
앞에서 샘플 한 쌍부터 읽으라고 했지만, 그것은 시작하는 방법이지 끝내는 방법이 아닙니다. 한 번 성공했다고 연동이 끝났다고 판단하면, 실제 데이터가 들어오는 순간 아래 지점에서 무너집니다.
목록은 한 번에 다 오지 않습니다. 사용자 한 명을 가져오는 요청은 단순하지만, 목록 요청은 대개 일부만 돌려주고 다음 쪽을 가리키는 값을 함께 줍니다. 샘플에 세 건만 들어 있으면 이 구조가 보이지 않습니다. 목록 API를 볼 때는 응답에 next, cursor, total 같은 키가 있는지 먼저 확인해야 합니다.
날짜는 형식과 시간대가 함께 옵니다. 위 예시의 2026-03-11T09:20:00Z에서 끝의 Z는 협정 세계시를 뜻합니다. 이것을 그대로 화면에 찍으면 사용자에게는 몇 시간 어긋난 시각으로 보입니다. 반대로 시간대 표시가 아예 없는 값이 오는 API도 있는데, 그때는 문서에서 기준을 확인해야 합니다.
없는 것에도 종류가 있습니다. 키가 아예 없는 경우, 키는 있고 값이 null인 경우, 빈 배열이 온 경우는 각각 다른 상황입니다. 세 가지를 같은 것으로 다루면 나중에 원인을 찾기 어려운 버그가 됩니다.
문서와 실제 응답이 다르면 실제 응답이 맞습니다. 문서는 사람이 쓰고, 코드가 바뀌어도 같이 안 바뀌는 일이 흔합니다. 두 개가 어긋날 때는 한 번 찍어 본 실제 응답을 기준으로 삼고, 문서 쪽 오류라고 판단되면 그때 문의하면 됩니다.
마지막으로, 한 번 잘 도는 요청이라도 토큰 만료, 요청 수 제한, 네트워크 지연이라는 조건 아래에서는 다르게 동작합니다. 이 세 가지는 처음부터 완벽히 다룰 필요는 없지만, "성공한 경로 하나만 확인했다"는 사실은 인지하고 있어야 합니다.
토큰이나 API 키를 코드에 그대로 적어 두고 저장소에 올리는 일이 반복됩니다. 값은 환경 변수로 빼고, 저장소에는 이름만 남기세요. 관련해서는 첫 프로젝트의 보안 기본에 정리해 두었습니다.
오늘 30분으로 해 볼 것
- 공개된 API 하나를 골라
curl -i로 요청하고, 상태 줄과 헤더와 본문의 경계를 눈으로 확인합니다. - 같은 요청에서 인증 헤더를 일부러 빼고 다시 보내, 실패 응답의 본문을 읽어 봅니다.
- 응답 JSON에서 항상 있는 키와 없을 수도 있는 키를 나눠 메모에 적습니다.
- 브라우저 개발자 도구 Network 탭에서 아무 사이트나 열고, 요청 하나의 Headers와 Response를 위 예시와 대응시켜 봅니다.
- 그 응답을 코드로 받아 값 하나만 꺼내 출력하는 스크립트를 씁니다. 그 이상은 오늘 하지 않습니다.
비동기 호출 문법에서 막힌다면 JavaScript 비동기 가이드가, 요청이 브라우저에서 서버까지 가는 경로 자체가 궁금하다면 웹사이트를 열 때 벌어지는 일이 이어지는 글입니다. 받아 온 데이터를 어디에 쌓을지 고민이 시작됐다면 데이터베이스를 일찍 배워야 하는 이유로 넘어가면 됩니다.
API 문서가 어렵게 보일수록, 실제로 오간 요청과 응답 텍스트 한 왕복을 눈으로 확인하는 편이 빠릅니다.