목차
Fetch API
Fetch API는 AJAX 요청을 위한 현대적인 방법이다. 기존 XMLHttpRequest(XHR)보다 더 간결하고 강력한 인터페이스를 제공한다. 요청을 보낼 때는 fetch() 함수를 사용하고, 호출하는 즉시 요청한다.
fetch(url, options)
- url: 요청할 서버 URL 주소
- options: 요청 방식, 헤더, 본문 등을 포함하는 객체(옵션)
Fetch API는 Promise를 반환하고 응답은 Response 객체로 제공한다. 이 응답(response) 객체는 다양한 메서드를 통해 데이터를 추출할 수 있다. fetch() 함수의 응답 결과를 확인하려면 .then() 메서드를 사용한다.
fetch('https://api.github.com/users/b1ackitty/repos')
.then((response) => console.log(response))
fetch()의 응답은 XHR의 응답과 다르게 읽기 가능한 스트림(readable stream)으로 body 속성에 담겨 있다. 이 데이터를 JavaScript에서 사용하려면 응답 객체가 가진 .json() 메서드를 사용해 JavaScript 객체로 변환해야 한다.
fetch('https://api.github.com/users/yamoo9/repos')
.then((response) => response.json())
.then((data) => console.log(data))
.json() 메서드 외에도 아래와 같은 메서드를 사용해 다양하게 데이터를 처리할 수 있다. 일반적으로 API를 사용할 때는 JSON 또는 Text 포맷만 사용된다.
메서드 | 반환 형식 | 사용 사례 |
.json() | JSON 객체 | JSON API 응답 처리 |
.text() | 텍스트 | HTML, XML, 일반 텍스트 처리 |
.blob() | Blob 객체 | 이미지, 오디오, 비디오 등 바이너리 데이터 |
.arrayBuffer() | ArrayBuffer | 바이너리 데이터의 저수준 처리 |
.formData() | FormData 객체 | 폼 데이터 처리 |
Promise
Promise는 JavaScript에서 비동기 작업을 처리하기 위한 객체로, 미래에 어떤 값이 반환될 것임을 약속하는 객체다. Promise는 다음 세 가지 상태 중 하나를 가진다.
- Pending: 초기 상태. 약속이 이행되거나 거부되지 않은 상태
- Fulfilled: 약속이 이행된 상태(성공적으로 완료됨)
- Rejected: 약속이 거부된 상태(실패함)
Promise 객체를 생성하려면 Promise 생성자(Constructor, 다른 객체를 생성하는 함수)로 콜백을 전달한다. 콜백은 resolve, reject 매개 변수를 전달 받는다. resolve와 reject 매개변수는 모두 함수 타입이다. resolve() 함수가 호출되면 Promise가 이행 상태가 되고, .then() 메서드를 실행한다. 반대로 reject() 함수가 호출되면 Promise가 거절 상태가 되고, .catch() 메서드를 실행한다. 즉, Promise가 해결(resolve)되는지, 거부(reject)되는지 결정을 위한 조건이 필요하다.
new Promise((resolve, reject) => {
// 비동기 작업 수행
if (조건) {
// 작업 성공 (Promise 이행)
resolve('😁')
} else {
// 작업 실패 (Promise 거부)
reject('😢')
}
})
위 코드를 3항 연산자(Ternary operator) 식을 사용하면 다음과 같이 작성할 수 있다.
new Promise((resolve, reject) => {
// 비동기 작업 수행
조건 ? resolve('😁') : reject('😢')
})
Fetch API의 fetch() 함수를 호출하면 Promise 객체를 반환하기 때문에 콜 체인(Chained then calls)을 사용할 수 있다. Promise의 .then() 메서드에서 반환된 값은 다음 .then() 메서드에서 사용할 수 있다. 그리고 .then() 메서드에 사용된 콜백을 이름이 부여된 콜백으로 리팩토링하면 코드가 훨씬 이해하기 쉽다.
fetch('https://api.github.com/users/yamoo9/repos')
.then((response) => response.json())
.then(massageTheData)
.then(convertDataToHTMLString)
.then(renderList)
// ------------------------------------------------------------
// 각 기능을 별도 함수로 분리
// 응답 데이터 정리
function massageTheData(data) {
return data.map((repo) => ({
name: repo.name,
url: repo.html_url,
isPublic: !repo.private,
}))
}
// 데이터 -> HTML 문자열 변환
function convertDataToHTMLString(data) {
return data.reduce((html, { url, name, isPublic }) => {
html += `
<li>
<a href="${url}">
${name} (#${isPublic ? '공개' : '비공개'})
</a>
</li>
`
return html
}, '')
}
// DOM에 리스트 렌더링
function renderList(htmlString) {
const list = document.createElement('ul')
list.innerHTML = htmlString
document.body.append(list)
}
Promise 콜 체인에서 오류가 발생하면 해당 오류는 .catch() 메서드의 콜백을 호출한다. 오류를 생성한 .then() 메서드 뒤에 나온 .then() 콜 체인은 모두 건너뛴다. Promise에도 .finally() 메서드가 있다. 이 메서드는 이행, 거절 여부에 상관 없이 항상 실행된다.
Request/Response
HTTP 통신은 클라이언트와 서버 사이의 요청(Request)과 응답(Response)으로 이루어진다.
요청(Request)
HTTP 요청(Request)은 다음 네 가지 주요 부분으로 구성된다.
- 엔드포인트(Endpoint): 루트 엔드포인트 (root endpoint), 패스 (path), 쿼리 파라미터 (query parameters)로 구성된 요청을 보내는 URL
https://api.github.com/users/b1ackitty/repos?sort=pushed
- 루트 엔드포인트: https://api.github.com
- 패스: /users/b1ackitty/repos
- 쿼리 파라미터: ?sort=pushed - 메서드(Method): 요청을 보내는 방식. 조회(GET), 생성(POST), 수정(PUT, PATCH), 삭제(DELETE)
- 헤더(Headers): 서버에 추가로 제공하는 정보
- 바디(Body): 서버로 전송할 데이터. POST, PUT, PATCH 요청할 때만 사용
응답 (Response)
서버의 응답(Response)은 다음 세 가지 주요 부분으로 구성된다.
- 상태(Status): 요청 성공 여부
- 헤더(Headers): 브라우저에 추가 정보 제공
- 바디(Body): 서버가 보내는 데이터
HTTP 상태 코드는 다음과 같이 정리할 수 있다.
- 2xx: 석세스(Success) - 성공
- 3xx: 리디렉션(Redirection) - 이동
- 4xx: 클라이언트 오류(Client Error) - 사용자 측 잘못
- 5xx: 서버 오류(Server Error) - 서버 측 잘못
오늘 하루를 돌아보며
와아... 오늘 배운 내용은 정말 어려웠다. 내용이 어려우니까 집중도 덩달아 잘 안 된 것 같다. 그래도 블로그를 쓰려고 수업 노트를 다시 살펴보니까 조금은 이해가 되는 것 같다. 뭔가 페칭이나 프로미스를 사용해서 서버에 요청하고 응답받는 예제들을 많이 사용해보고, 어디에 어떻게 쓰는지 해보면 조금 더 이해되지 않을까 싶다. 우선 지금은 개념만이라도, 대충 이렇게 돌아가는구나 정도라도 이해해 보자!