네트워크 요청을 할 땐 다양한 오류가 발생할 수 있다.
// api.js
export async function getColorSurveys(params = {}) {
const url = new URL('https://learn.codeit.krrrrr/api/color-surveys'); //일부러 오류만들기
object.keys(params).forEach((key) =>
url.searchParams.append(key, params[key])
);
const res = await fetch(url);
const data = await res.json();
return data;
}
export async function getColorSurvey(id) {
const res = await fetch(`https://learn.codeit.kr/api/color-surveys/${id}`);
const data = await res.json();
return data;
}
export async function createColorSurvey(sueveyData) {
const res = await fetch('https://learn.codeit.kr/api/color-surveys', {
method: 'POST',
body: JSON.stringify(surveyData),
headers: {
'Content-Type': 'application/json'
}
});
const data = await res.json();
return data;
}
일부러 오류를 발생하게 오타를 만든뒤 main.js 파일을 실행해보면 fetch가 실패했다고 나온다.
주소를 찾을 수 없기 때문에 오류가 발생하고 fetch가 리턴하는 promise가 rejected 상태가 된다. 이 오류는 이제 너무나도 익숙한 try catch문을 이용해 처리할 수 있다.
왼쪽의 드래그 한 부분을 try catch로 감싸거나 함수를 실제로 호출하는 부분을 try catch로 감싸줘도 된다. try문 안에서 throw되는 오류는 모두 잡아주기 때문에 상관이 없다.
//main.js
import { getColorSurveys, getColorSurvey, createColorSurvey } from './api.js';
try {
const data = await getColorSurvey();
console.log(data);
} catch (e) {
console.log('오류가 발생했습니다:');
console.log(e.message);
}
함수를 호출하는 부분을 try catch문으로 감싸주고 코드를 다시 실행해보면
이번엔 그냥 fetch failed라고 나온다. try문이 안에서 발생한 오류르 잡아서 catch문 안에 있는 코드가 실행된 것이다. 지금은 콘솔에 오류 메세지를 출력하지만 실제 웹사이트 코드라면 오류 메세지를 화면에 보여줄 수 있을 것이다.
유효하지 않은 주소를 입력하는 것 외에도 유효하지 않은 헤더 이름을 사용하거나 헤더 값이 이상하면 리퀘스트 자체가 실패해서 fetch가 리턴하는 promise는 rejected 상태가 된다. 주의해야할 점은 리퀘스트 자체가 실패했을 때만 promise가 rejected 상태가 되고 400이나 500같은 에러 리스폰스가 돌아오는 경우에는 promise는 fulfilled 상태가 된다.
그러니까 예를 들어 잘못된 바디 내용을 보내서 400리스폰스가 돌아오거나 서버 측에서 오류가 발생해서 500 리스폰스가 돌아와도 fetch의 promise는 fulfilled 상태가 된다. 그래서 이걸 깔끔하게 처리하는 방법은 리스폰스의 상태 코드가 성공을 나타내지않으면 오류를 발생시키는 것이다.
// api.js
export async function getColorSurveys(params = {}) {
const url = new URL('https://learn.codeit.krrrrr/api/color-surveys'); //일부러 오류만들기
object.keys(params).forEach((key) =>
url.searchParams.append(key, params[key])
);
const res = await fetch(url);
//오류 발생
if (!res.ok) {
throw new Error('데이터를 불러오는데 실패했습니다.');
}
const data = await res.json();
return data;
}
export async function getColorSurvey(id) {
const res = await fetch(`https://learn.codeit.kr/api/color-surveys/${id}`);
const data = await res.json();
return data;
}
export async function createColorSurvey(sueveyData) {
const res = await fetch('https://learn.codeit.kr/api/color-surveys', {
method: 'POST',
body: JSON.stringify(surveyData),
headers: {
'Content-Type': 'application/json'
}
});
const data = await res.json();
return data;
}
여기서 추가한 res.ok는 리스폰스 상태 코드가 2로 시작하면 true, 그렇지 않으면 false를 리턴한다. if문 안에서 리스폰스의 실제 상태 코드나 바디내용을 보고 더 세밀한 처리를 해줄 수도 있다. 리퀘스트 자체가 실패해도 오류를 throw하고 오류를 나타내는 상태 코드가 돌아와도 오류를 throw 하는 것이다.
이렇게 처리를 하는게 헷갈리지 않고 함수를 사용할 때 더 편리하다.
일부러 만들었던 주소 오류는 수정을 하고, 유효하지 않은 mbti를 쿼리 파라미터로 보내본다면,
//main.js
import { getColorSurveys, getColorSurvey, createColorSurvey } from './api.js';
try {
const data = await getColorSurvey({ mbti: 'EEEE' });
console.log(data);
} catch (e) {
console.log('오류가 발생했습니다:');
console.log(e.message);
}
방금 작성한 메세지가 잘 출력되는걸 확인 할 수 있다. 만약 if (!res.ok) {throw new Error('데이터를 불러오는데 실패했습니다.');} 이 코드가 없었다면 잘못된 mbti를 전달했기 때문에 상태코드 400이 돌아올것이다. 상태코드 400이 돌아와도 아무 문제없이 다음줄이 실행되는데 이 때 상태코드 400에 해당하는 리스폰스의 형식도 JSON이라면 바디가 파싱되서 그대로 리턴된다. 함수 안에서 아예 오류가 발생하지 않는 것이다. 반면 리스폰스 바디 형식이 JSON이 아니라면 오히려 바디를 파싱하는 과정에서 오류가 발생한다.
이렇게 다양한 케이스가 있어서 헷갈릴 수 있기 때문에 리스폰스가 성공적으로 처리되지 않으면 그냥 오류를 throw 하는 것이다. 나머지 코드도 비슷하게 바꿔주면 이렇게 할 수 있다.
// api.js
export async function getColorSurveys(params = {}) {
const url = new URL('https://learn.codeit.kr/api/color-surveys');
object.keys(params).forEach((key) =>
url.searchParams.append(key, params[key])
);
const res = await fetch(url);
//오류 발생
if (!res.ok) {
throw new Error('데이터를 불러오는데 실패했습니다.');
}
const data = await res.json();
return data;
}
export async function getColorSurvey(id) {
const res = await fetch(`https://learn.codeit.kr/api/color-surveys/${id}`);
if (!res.ok) {
throw new Error('데이터를 불러오는데 실패했습니다.');
}
const data = await res.json();
return data;
}
export async function createColorSurvey(sueveyData) {
const res = await fetch('https://learn.codeit.kr/api/color-surveys', {
method: 'POST',
body: JSON.stringify(surveyData),
headers: {
'Content-Type': 'application/json'
}
});
if (!res.ok) {
throw new Error('데이터를 생성하는데 실패했습니다.');
}
const data = await res.json();
return data;
}
마무리 복습하기
fetch 오류는 크게 두가지가 있다.
- URL이 이상하거나 헤더 정보가 이상해서 리퀘스트 자체가 실패하는 경우
- 리퀘스트는 성공적인데 상태코드가 실패를 나타내는 경우
여기서 조심해야 할 부분은 첫번째 경우에만 fetch의 promise가 rejected 상태가 된다는 것이다. 그래서 오류를 깔끔하게 처리하는 방법 중 하나는 두번째 경우에도 수동적으로 오류를 throw 해주는 것이다. 리퀘스트가 성공적으로 처리되고, 성공적인 리스폰스가 돌아왔을 때만 데이터를 리턴하고 나머지 모든 경우엔 오류를 발생시키는 것이다.
그래야 API 함수를 사용하는 입장에서 try catch로 쉽게 로직을 구분할 수 있다.
fetch에 대한 오류를 처리할 때는 언제 promise가 rejected 되는지, 어떤 내용이 리스폰스 바디로 돌아오는지를 잘 생각해보도록 하자.
'코린이 개념잡기 > 비동기 자바스크립트' 카테고리의 다른 글
리퀘스트: axios 문법 (2) | 2024.12.27 |
---|---|
리퀘스트: 브라우저에서 리퀘스트 확인 (1) | 2024.12.27 |
리퀘스트: API 함수 만들기 (1) | 2024.12.27 |
리퀘스트: POST 리퀘스트 보내는 방법 (0) | 2024.12.26 |
Query String, Path / Query Parameter (0) | 2024.12.26 |