브라우저는 어떻게 데이터를 받을까? HTTP 요청·응답과 Network 탭 실습
Soshy··조회수 —

React로 개발하다 보면 API를 호출하고, 응답 데이터를 상태에 저장하고, 화면에 표시하는 코드를 자주 만나게 된다.
그런데 코드에 적힌 fetch나 API 함수만 따라가서는 이해하기 어려운 상황이 있다. 요청이 실제로 전송됐는지, 서버가 어떤 응답을 보냈는지, 응답은 정상인데 화면에서 문제가 생긴 것인지 구분해야 하기 때문이다.
이번 글에서는 그 출발점인 HTTP 요청과 응답을 공부한다. Chrome 개발자 도구의 Network 탭에서 페이지 문서 요청과 JavaScript의 데이터 요청을 직접 비교한다.
목표는 요청 하나를 선택하고 다음 질문에 답하는 것이다.
어디로, 어떤 요청을 보냈고, 어떤 응답을 받았는가?
1. 학습 범위와 준비
다음 내용을 차례로 살펴본다.
| 학습 내용 | 확인할 내용 |
|---|---|
| 클라이언트·서버, HTTP 요청과 응답 | 요청·응답의 구성 |
| 페이지 문서 요청 확인 | HTML 응답의 구조 |
| JavaScript로 API 요청 보내기 | 문서 요청과 API 요청의 차이 |
| 실패 응답과 요청 발생 위치 확인 | 상태 코드와 오류의 구분 |
| 관찰 결과 정리와 복습 | 요청부터 응답까지의 흐름 |
준비물은 Chrome 브라우저와 개발자 도구다. 별도의 React 프로젝트나 서버 설치는 필요하지 않다.
실습에는 가짜 데이터를 제공하는 공개 API인 JSONPlaceholder를 사용한다. 회원가입이나 API 키 없이 요청을 보낼 수 있다.
아래 예상 결과와 실제 관찰 결과를 비교하면서 진행한다. 응답 시간이나 일부 헤더는 환경과 시점에 따라 달라질 수 있으므로 직접 확인한 값을 기록한다.
2. 웹 페이지 하나에도 여러 요청이 필요하다
클라이언트와 서버
웹 통신에서 클라이언트는 요청을 보내는 쪽이고, 서버는 요청을 처리해 응답하는 쪽이다. 이번 실습에서는 브라우저가 클라이언트 역할을 한다.
HTTP는 이러한 요청과 응답을 주고받기 위한 규칙이다.
브라우저가 페이지를 표시하는 과정을 단순화하면 다음과 같다. 아래 그림은 순서를 설명하기 위한 예시이며, 실제 요청들은 동시에 진행되기도 한다.
다이어그램을 불러오는 중…
sequenceDiagram
participant B as 브라우저
participant S as 서버
B->>S: 페이지 문서 요청
S-->>B: HTML 응답
B->>S: JavaScript 파일 요청
S-->>B: JavaScript 응답
B->>S: API 데이터 요청
S-->>B: JSON 응답
실제 페이지에서는 CSS, 이미지, 폰트 등도 필요하다. HTML을 읽으며 추가 요청이 발생할 수 있고, 실행된 JavaScript가 데이터를 더 요청할 수도 있다.
따라서 페이지를 한 번 열었다고 해서 요청도 한 번만 발생하는 것은 아니다. MDN HTTP 개요
문서 요청과 API 요청
프론트엔드에서 자주 만나는 두 가지 상황을 비교해보자.
| 상황 | 요청의 목적 | 흔히 받는 데이터 |
|---|---|---|
| 주소창에서 페이지 접속 | 표시할 문서 가져오기 | HTML |
| JavaScript에서 데이터 조회 | 화면에 사용할 데이터 가져오기 | JSON |
둘 다 HTTP 요청이지만, 가져온 결과를 사용하는 방식이 다르다.
HTML 문서는 브라우저가 해석해 페이지를 구성한다. API에서 받은 JSON은 JavaScript가 읽고 화면에 필요한 값으로 사용한다.
API가 항상 JSON을 반환하는 것은 아니며, JavaScript로 HTML을 가져오는 것도 가능하다. 이 글에서는 흔히 사용하는 HTML 문서와 JSON 데이터를 비교한다.
3. 요청과 응답에서 무엇을 읽어야 할까?
요청: 어디에 무엇을 요구했는가?
HTTP 요청에서는 우선 다음 항목을 확인한다.
| 항목 | 확인할 질문 | 예시 |
|---|---|---|
| URL | 어디로 요청했는가? | 아래 URL 참고 |
| 메서드 | 어떤 동작을 요청했는가? | GET |
| 요청 헤더 | 어떤 부가 정보를 보냈는가? | Accept: application/json |
| 요청 본문 | 어떤 데이터를 담아 보냈는가? | 새 글의 제목·내용 등 |
https://jsonplaceholder.typicode.com/todos/1
이 URL은 다음과 같이 나눌 수 있다.
- 스킴:
https— HTTPS를 사용한다. - 호스트:
jsonplaceholder.typicode.com— 요청 대상 호스트다. - 경로:
/todos/1— 요청할 리소스를 나타낸다.
쿼리 문자열이 붙는 URL도 있다.
https://jsonplaceholder.typicode.com/todos?userId=1
여기서 userId=1은 쿼리 매개변수다. 서버는 이 정보를 필터 조건 등으로 사용할 수 있다. 구체적인 의미는 API가 정한다. MDN URL 설명
이번 실습에서는 리소스를 조회하는 GET 메서드를 사용한다. HTTP에는 데이터를 전송할 때 사용하는 POST 등 목적에 따라 여러 메서드가 있다.
요청마다 본문이 있는 것은 아니다. 이 글의 GET 실습에서는 별도의 요청 본문을 보내지 않는다.
응답: 어떤 결과를 돌려받았는가?
응답에서는 다음 세 가지를 확인한다.
| 항목 | 의미 | 예시 |
|---|---|---|
| 상태 코드 | 요청 처리 결과 | 200, 404 |
| 응답 헤더 | 응답에 관한 부가 정보 | Content-Type |
| 응답 본문 | 실제로 전달받은 내용 | HTML, JSON 등 |
특히 Content-Type은 본문의 미디어 타입을 알려준다.
HTML 응답에서 볼 수 있는 형태는 다음과 같다.
Content-Type: text/html; charset=utf-8
JSON 응답에서는 다음과 같은 형태를 볼 수 있다.
Content-Type: application/json; charset=utf-8
요청 헤더의 Accept가 클라이언트가 받을 수 있는 응답 형식을 알린다면, 응답 헤더의 Content-Type은 실제 응답 본문의 형식을 알려준다. MDN Content-Type 설명
위 헤더는 의미를 설명하기 위한 예시다. 실습에서는 서버가 실제로 반환한 값을 확인한다.
상태 코드: 성공 여부를 판단하는 첫 단서
상태 코드는 크게 다음 범주로 나뉜다.
| 범위 | 의미 | 대표 예시 |
|---|---|---|
| 1xx | 정보성 응답 | 100 Continue |
| 2xx | 성공 | 200 OK |
| 3xx | 리다이렉션 등 | 301 Moved Permanently |
| 4xx | 요청과 관련된 오류 | 404 Not Found |
| 5xx | 서버 측 오류 | 500 Internal Server Error |
실습에서는 200과 404를 직접 비교한다.
200은 요청이 성공적으로 처리됐음을 나타낸다. 404는 서버가 요청한 리소스를 찾지 못했음을 나타낸다. MDN HTTP 상태 코드
404라고 해서 곧바로 프론트엔드 개발자의 실수라고 결론 내릴 수는 없다. URL이 잘못됐거나, 데이터가 없거나, 서버 라우팅에 문제가 있을 수도 있다.
또한 200을 받았어도 응답 데이터가 예상과 다르면 화면에서 문제가 생길 수 있다. 상태 코드와 응답 본문을 함께 확인해야 한다.
4. 실습 1: 페이지를 불러오는 요청 읽기
Network 탭 준비하기
- Chrome에서 JSONPlaceholder 홈페이지를 연다.
- 개발자 도구를 연다. macOS는
Command + Option + I, Windows/Linux는Ctrl + Shift + I를 사용한다. - Network 탭을 선택한다.
- 요청 기록 버튼이 활성화되어 있는지 확인한다.
- Disable cache를 체크한다.
- Preserve log는 해제하고, 필터는 All로 둔다.
- 페이지를 새로고침한다.
개발자 도구를 열기 전에 끝난 요청은 목록에 없을 수 있다. Network를 연 뒤 새로고침하는 순서가 중요하다.
Disable cache는 개발자 도구가 열린 동안 브라우저 캐시 사용을 비활성화하는 설정이다. 서버나 CDN의 캐시까지 모두 사라지는 것은 아니다. 실습이 끝나면 체크를 해제한다. Chrome Network 기능 참고
HTML 문서 요청 찾기
Network 상단의 Doc 필터를 선택한다. 목록에서 홈페이지 문서 요청을 클릭한다.
Headers → General 영역에서 다음 항목을 찾는다.
- Request URL: 요청한 전체 주소
- Request Method: 요청 메서드
- Status Code: 응답 상태 코드
이번에 찾는 것은 홈페이지 경로 /에 대한 문서 요청이다.
이어 Response Headers에서 content-type을 찾고, Response 탭에서 본문을 확인한다. HTML 태그와 문서 구조를 볼 수 있을 것이다.
<!DOCTYPE html>
<html>
...
</html>
위 코드는 응답 형태를 설명하기 위한 예시다. 실제 관찰한 값으로 아래 표를 채운다.
| 항목 | 관찰한 내용 |
|---|---|
| Request URL | 직접 기록 |
| Request Method | 직접 기록 |
| Status Code | 직접 기록 |
| 응답 Content-Type | 직접 기록 |
| 확인한 HTML 태그 두 개 | 직접 기록 |
그다음 All 필터로 돌아가 다른 요청도 살펴본다. JavaScript, CSS, 이미지 등 어떤 종류의 리소스가 있는지 확인한다.
Network의 Type은 브라우저가 분류한 요청 종류이고, 응답의 Content-Type은 서버가 보낸 본문의 미디어 타입이다. 따라서 Type과 Content-Type은 서로 다른 정보를 나타낸다. Chrome Network 사용 안내
5. 실습 2: JavaScript에서 API 요청 보내기
요청을 직접 발생시키기
JSONPlaceholder 홈페이지를 연 상태에서 진행한다.
먼저 Network의 Clear 버튼으로 목록을 비운다. 이 버튼은 화면에 표시된 기록을 지우며, 서버 데이터를 삭제하는 기능은 아니다.
개발자 도구의 Console 탭으로 이동해 다음 코드를 입력하고 실행한다.
fetch("/todos/1");
현재 페이지가 JSONPlaceholder 홈페이지이므로, /todos/1은 같은 호스트의 다음 URL을 가리킨다.
https://jsonplaceholder.typicode.com/todos/1
fetch는 브라우저에서 HTTP 요청을 보낼 수 있는 API다. 별도로 메서드를 지정하지 않으면 GET 요청을 보낸다.
실행 직후 Console에는 Promise 객체가 표시될 수 있다. fetch가 비동기 요청의 결과를 Promise로 반환하기 때문이다. 실제 HTTP 응답의 상태와 본문은 Network에서 확인할 수 있다. MDN Fetch 사용법
JSON 응답 확인하기
Network로 돌아가 Fetch/XHR 필터를 선택한다. 새로 발생한 /todos/1 요청을 선택한다.
- Headers → General에서 URL, 메서드, 상태 코드를 확인한다.
- Response Headers에서
content-type을 확인한다. - Response에서 본문을 확인한다.
이 API의 예시 응답은 다음과 같다.
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}
API 응답과 비교하면서 id, title, completed를 찾아본다.
이 데이터는 HTML 태그가 아닌 JSON 형식으로 전달된다.
응답을 받았다고 해서 현재 홈페이지에 할 일 제목이 자동으로 표시되지는 않는다. 화면에 반영하려면 이를 처리하는 코드가 필요하다.
아래 비교표를 완성한다.
| 비교 항목 | 홈페이지 문서 | /todos/1 API |
|---|---|---|
| 발생시킨 행동 | 페이지 새로고침 | fetch 실행 |
| 메서드 | 직접 기록 | 직접 기록 |
| Network Type | 직접 기록 | 직접 기록 |
| 응답 Content-Type | 직접 기록 | 직접 기록 |
| 본문 형식 | HTML | JSON |
| 결과의 사용 | 문서로 표시 | 코드에서 데이터로 사용 |
API URL을 주소창으로 열면 어떻게 될까?
새 탭에서 같은 API URL을 주소창으로 열어본다.
브라우저는 JSON을 화면에 표시할 수 있다. 그렇다고 응답이 HTML로 바뀐 것은 아니다.
새 탭에서도 Network를 열고 새로고침한 뒤 확인한다.
- 주소창 탐색으로 발생한 요청은
document로 분류될 수 있다. - 응답 Content-Type은 여전히 JSON을 나타낸다.
- 본문에는 같은 할 일 데이터가 들어 있다.
요청의 발생 방식과 응답 본문의 형식은 구분해야 한다. document 요청이라고 해서 본문이 반드시 HTML인 것은 아니다.
확인이 끝나면 원래 홈페이지 탭으로 돌아간다.
6. 실습 3: 404 응답은 통신 자체의 실패일까?
존재하지 않는 리소스 요청하기
원래 홈페이지 탭의 Console에서 다음 코드를 실행한다.
fetch("/todos/999999");
Network에서 해당 요청을 선택하고 확인한다.
- 실제 요청 URL은 무엇인가?
- 상태 코드는 무엇인가?
- 응답 본문에는 무엇이 들어 있는가?
- 성공 요청과 응답 시간이 다른가?
존재하지 않는 할 일을 요청하므로 404 응답을 예상한다. 실제 결과가 다르다면 관찰한 상태와 본문을 그대로 기록한다.
404 응답을 받았다는 것은 HTTP 응답을 받았다는 뜻이다. 인터넷 연결이 끊겨 응답 자체를 받지 못한 상황과는 다르다.
fetch는 404만으로 실패 처리되지 않는다
아래 코드도 실행해본다.
fetch("/todos/999999")
.then((response) => {
console.log("status:", response.status);
console.log("ok:", response.ok);
})
.catch((error) => {
console.log("catch:", error);
});
응답이 404라면 다음 출력을 예상할 수 있다.
status: 404
ok: false
HTTP 오류 상태를 받아도 fetch의 Promise가 그 이유만으로 거부되지는 않는다. 네트워크 오류 등과 구분해서 처리해야 한다.
response.ok는 상태 코드가 200~299일 때 true다. MDN Fetch의 응답 상태 확인
fetch를 사용하는 React 코드에서는 HTTP 오류를 예외로 처리하기 위해 다음과 같은 검사를 추가하기도 한다.
if (!response.ok) {
throw new Error(`HTTP 오류: ${response.status}`);
}
이 검사는 HTTP 응답의 수신 여부와 별개로, 상태 코드가 애플리케이션의 성공 조건을 충족하는지 판단한다.
API 라이브러리에 따라 HTTP 오류 상태를 받았을 때 Promise를 거부하는 등 오류 처리 방식이 다를 수 있다. 따라서 위 설명은 브라우저의 fetch를 기준으로 하며, 다른 라이브러리의 오류 처리 방식은 해당 도구의 문서에서 확인해야 한다.
7. React 코드와 연결해서 읽기
React에서 API 데이터를 표시하는 흐름을 단순화하면 다음과 같다.
다이어그램을 불러오는 중…
flowchart TD
A[버튼 클릭 또는 정해진 실행 시점] --> B[API 요청 함수 실행]
B --> C[HTTP 요청 전송]
C --> D[HTTP 응답 수신]
D --> E[상태 코드와 데이터 확인]
E --> F[React 상태 업데이트]
F --> G[변경된 화면 표시]
응답이 왔는데도 화면이 비어 있다면 다음처럼 원인을 나눠볼 수 있다.
| 확인한 사실 | 다음에 살펴볼 곳 |
|---|---|
| 요청이 목록에 없다 | 실행 조건, 이벤트 연결, 필터·기록 설정 |
| 요청은 있지만 실패 응답이다 | URL, 입력값, 인증 정보, 서버 응답 |
| 200이지만 데이터가 예상과 다르다 | API 응답 구조, 요청 조건 |
| 데이터는 맞지만 화면이 다르다 | 데이터 가공, 상태 업데이트, 렌더링 조건 |
Network에서 요청의 Initiator 열이나 상세 탭을 보면 요청이 시작된 원인을 추적하는 데 도움이 된다. JavaScript 요청은 호출 위치나 호출 흐름을 확인할 수 있는 경우가 있다.
Initiator에 표시되는 파일은 빌드된 JavaScript일 수 있다. 원본 소스 연결 여부에 따라 파일 이름이나 호출 위치가 다르게 표시될 수 있다.
실습에서 실행한 /todos/1 요청의 Initiator를 열어 Console에 입력한 fetch 호출 위치가 표시되는지 확인한다.
8. 요청 흐름을 직접 설명하기
실습 결과 정리하기
다음 항목은 요청의 시작부터 응답까지 빠짐없이 설명하기 위한 점검표다. 실습에서 확인한 /todos/1 요청을 기준으로 각 항목에 답해본다.
1. 요청을 발생시킨 행동:
2. 요청 URL:
3. 요청 메서드:
4. 상태 코드:
5. 응답 Content-Type:
6. 응답 본문에서 확인한 값:
7. 요청이 시작된 위치:
8. 예상과 달랐던 점:
마지막에는 다음 문장을 직접 완성한다.
브라우저에서 ______를 실행해 ______ 주소로 ______ 요청을 보냈다. 서버는 ______ 상태와 ______ 형식의 본문을 반환했다. 본문에는 ______ 데이터가 있었다.
앞에서 확인한 세 요청을 비교하며 다음 질문에 답해본다.
- HTML 문서 요청: 응답 본문에서 문서 구조를 확인할 수 있는가?
- JSON API 요청: 문서 요청과 Content-Type, 본문 형식이 어떻게 다른가?
- 404 응답: 성공 응답과 상태 코드, 본문이 어떻게 다른가?
차이를 설명할 때는 Network에서 확인한 상태 코드와 응답 내용을 근거로 삼는다. 응답 시간은 연결 상태와 캐시 등의 영향을 받으므로, 한 번의 측정만으로 서버 성능을 판단하지 않는다.
복습 질문
자료를 잠시 닫고 답해본다.
- 페이지 한 번을 여는데 여러 요청이 발생하는 이유는 무엇인가?
- 요청 헤더와 응답 헤더는 각각 누가 보내는가?
- Content-Type은 무엇을 알려주는가?
- 주소창에서 JSON이 보이면 HTML 응답을 받은 것인가?
- 404 응답과 네트워크 오류는 어떻게 다른가?
- fetch에서 catch가 실행되지 않으면 반드시 성공한 요청인가?
- API 응답이 정상인데 화면이 비어 있다면 다음에 무엇을 확인할 것인가?
각 답변은 앞선 실습에서 확인한 요청과 응답을 근거로 설명한다.
이 실습의 핵심은 Network에서 요청 하나의 URL, 메서드, 상태 코드, 응답 형식을 확인하고 요청부터 응답까지의 흐름을 설명하는 것이다.
실습이 끝나면 Disable cache 체크를 해제한다.
9. 정리
웹 페이지와 API 데이터는 HTTP 요청과 응답을 통해 전달된다. Network에서는 코드 실행으로 실제 요청이 발생했는지, 서버가 어떤 상태와 본문을 반환했는지 확인할 수 있다. 응답을 받은 뒤 데이터를 화면에 반영하는 과정은 JavaScript와 React 코드에서 이어진다.
문제를 추적할 때는 요청의 발생 여부, 응답 상태와 본문, 화면 반영 과정을 차례로 확인한다. 이 구분을 바탕으로 Network에서 확인한 사실과 React 코드의 실행 흐름을 연결할 수 있다.