(6) Sending a request with JavaScript - 오류 처리

반응형

▶ 다음과 같은 openAPI를 사용합니다.

더보기

실습용 서버 호스트

실습용 서버 호스트입니다.

learn.codeit.kr

설문 응답

사용자가 제출한 MBTI와 선호 색상 데이터를 저장합니다.

엔드포인트

  POST /api/color-surveys
   GET /api/color-surveys
   GET /api/color-surveys/:id
DELETE /api/color-surveys/:id
 PATCH /api/color-surveys/:id

설문 응답 객체

속성

id: integer 설문 응답 객체에 대한 고유 식별자

mbti: string 응답자의 MBTI: 16개의 MBTI만 허용하고 대소문자는 구별하지 않습니다.

colorCode: string 응답자의 선호 색상 코드: HEX 코드만 허용하고 # 유무와 대소문자를 구별하지 않습니다.

password: string 설문 응답에 대한 비밀번호: 숫자로 4개로 이뤄진 문자열만 허용합니다. 설문을 삭제, 또는 수정할 때 사용합니다. 읽기 전용 속성이기 때문에 한 번 값이 정해지면 바꿀 수 없습니다.

createdAt: integer 객체가 생성된 시점에 대한 UNIX 타임스탬프

updatedAt: integer 객체가 수정된 시점에 대한 UNIX 타임스탬프

설문 응답 객체 예시

{
  "id": 14,
  "mbti": "ENFP",
  "colorCode": "#CA6952",
  "password": "0123",
  "createdAt": 1667969077,
  "updatedAt": 1667969077
}

설문 응답 생성

엔드포인트

POST /api/color-surveys

파라미터로 넘겨준 값들을 사용해서 새로운 설문 응답 객체를 생성합니다.

바디 파라미터

mbti: string 필수 응답자의 MBTI: 16개의 MBTI만 허용하고 대소문자는 구별하지 않습니다.

colorCode: string 필수 응답자의 선호 색상 코드: HEX 코드만 허용하고 # 유무와 대소문자를 구별하지 않습니다.

password: string 필수 설문에 대한 비밀번호: 숫자로 4개로 이뤄진 문자열만 허용합니다. 설문 응답을 삭제, 또는 수정할 때 사용합니다. 읽기 전용 속성이기 때문에 한 번 값이 정해지면 바꿀 수 없습니다.

리턴 내용

생성에 성공할 시, 설문 응답 객체가 리턴됩니다. 실패 시, 에러 상태 코드와 메세지가 리턴됩니다.

리퀘스트 예시

POST <https://learn.codeit.kr/api/color-surveys>
Content-Type: application/json

{
  "mbti": "ENFP",
  "colorCode": "#CA6952",
  "password": "1234"
}

리스폰스 예시

상태 코드: 201

{
  "id": 14,
  "mbti": "ENFP",
  "colorCode": "#CA6952",
  "createdAt": 1667969077,
  "updatedAt": 1667969077
}

여러 설문 응답 조회

엔드포인트

GET /api/color-surveys

설문 응답 객체 전체 목록을 최신 순서로 리턴합니다.

쿼리 스트링 파라미터

mbti: string 제출자의 MBTI: 16개의 MBTI만 허용하고 대소문자는 구별하지 않습니다. 쿼리 스트링 파라미터로 보내면 해당 MBTI에 대한 설문 응답들만 모아서 확인할 수 있습니다.

limit: integer 리턴받기 원하는 설문 응답 객체 수: 값을 전달하지 않으면 10개의 객체가 리턴됩니다. 10~100 사이 정수만 허용됩니다.

offset: integer 가장 앞 객체부터 건너뛰고 싶은 객체 수: 값을 전달하지 않으면 건너뛰지 않습니다.

리턴 내용

유효한 mbti, limit을 제공했을 시, 설문 응답 객체 목록이 리턴됩니다. 실패 시, 에러 상태 코드와 메세지가 리턴됩니다.

count: integer 서버에 저장된 총 설문 응답 객체 수

next: string 페이지네이션에서 다음 페이지에 해당하는 주소

previous: string 페이지네이션에서 이전 페이지에 해당하는 주소

results: array of objects 리턴된 설문 응답 객체 목록

리퀘스트 예시

GET <https://learn.codeit.kr/api/color-surveys?offset=10&limit=10&mbti=ISTJ>

리스폰스 예시

상태 코드: 200

{
  "count": 42,
  "next": "<https://learn.codeit.kr/api/color-surveys/?offset=20&limit=10&mbti=ISTJ>",
  "previous": null,
  "results": [
    {
      "colorCode": "#CA6952",
      "createdAt": 1668752533000,
      "updatedAt": 1668752533000,
      "id": 42,
      "mbti": "ISTJ"
    },
    {
      "colorCode": "#CA6952",
      "createdAt": 1668752162000,
      "updatedAt": 1668752162000,
      "id": 41,
      "mbti": "ISTJ"
    },
    {
      "colorCode": "#33A560",
      "createdAt": 1668331218000,
      "updatedAt": 1668331218000,
      "id": 40,
      "mbti": "ISTJ"
    },
    ...
  ]
}

설문 응답 조회

엔드포인트

GET /api/color-surveys/:id

URL 파라미터 :id에 해당하는 설문 응답 객체를 조회합니다.

파라미터

없음

리턴 내용

유효한 고유 식별자를 제공했을 시 설문 응답 객체가 리턴됩니다. 실패 시, 에러 상태 코드와 메세지가 리턴됩니다.

리퀘스트 예시

GET <https://learn.codeit.kr/api/color-surveys/14>

리스폰스 예시

상태 코드: 200

{
  "id": 14,
  "mbti": "ENFP",
  "colorCode": "#CA6952",
  "createdAt": 1667969077,
  "updatedAt": 1667973104
}

설문 응답 삭제

엔드포인트

DELETE /api/color-surveys/:id

URL 파라미터 :id에 해당하는 설문 응답 객체를 삭제합니다.

바디 파라미터

password: string 필수 설문 응답에 대한 비밀번호: 고유 식별자로 특정짓는 설문의 비밀번호와 일치해야 삭제할 수 있습니다.

리턴 내용

유효한 고유 식별자와 비밀번호를 제공했을 시, 삭제된 설문 응답 객체가 리턴됩니다. 실패 시, 에러 상태 코드와 메세지가 리턴됩니다.

리퀘스트 예시

DELETE <https://learn.codeit.kr/api/color-surveys/14>
Content-Type: application/json

{
  "password": "0123"
}

리스폰스 예시

상태 코드: 204

{
  "id": 14,
  "mbti": "ENFP",
  "colorCode": "#CA6952",
  "createdAt": 1667969077,
  "updatedAt": 1667973104
}

설문 수정

엔드포인트

PATCH /api/color-surveys/:id

파라미터로 넘겨준 값들을 사용해서 URL 파라미터 :id에 해당하는 설문 응답 객체를 수정합니다. 제공되지 않은 선택 필드는 수정되지 않습니다.

바디 파라미터

mbti: string 응답자의 MBTI: 16개의 MBTI만 허용하고 대소문자는 구별하지 않습니다.

colorCode: string 응답자의 선호 색상 코드: HEX 코드만 허용하고 # 유무와 대소문자를 구별하지 않습니다.

password: string 필수 설문 응답에 대한 비밀번호: 숫자로 4개로 이뤄진 문자열만 허용합니다. 고유 식별자로 특정짓는 설문의 비밀번호와 일치해야 수정할 수 있습니다.

리턴 내용

유효한 고유 식별자와 비밀번호를 제공했을 시, 수정된 설문 응답 객체가 리턴됩니다. 실패 시, 에러 상태 코드와 메세지가 리턴됩니다.

리퀘스트 예시

PATCH <https://learn.codeit.kr/api/color-surveys/14>
Content-Type: application/json

{
  "mbti": "ISTJ",
  "colorCode": "#000000",
  "password": "0123"
}

리스폰스 예시

상태 코드: 200

{
  "id": 14,
  "mbti": "ISTJ",
  "colorCode": "#000000",
  "createdAt": 1667969077,
  "updatedAt": 1667973104
}

▶ package.json 파일

더보기
{
  "type": "module"
}

▶ 이전 글 살펴보기

(5) Sending a request with JavaScript - API 함수 만들기

https://seop-e.tistory.com/222

 

(5) Sending a request with JavaScript - API 함수 만들기

▶ 다음과 같은 openAPI를 사용합니다. 더보기실습용 서버 호스트실습용 서버 호스트입니다.learn.codeit.kr설문 응답사용자가 제출한 MBTI와 선호 색상 데이터를 저장합니다.엔드포인트 POST /api/color-su

seop-e.tistory.com


네트워크 요청을 할 때는 다양한 오류가 발생할 수 있는데

fetch 함수를 사용할 때 오류 처리 하는 방법을 알아보자


// api.js

// GET /api/color-surveys를 하는 함수 생성

export async function getColorSurveys(params ={}){
  const url = new URL('https://learn.codeit.krrrrrrrrrrrr/api/color-surveys'); // 잘못된 URL 입력 (오류 예시)
  Object.keys(params).forEach((key) => 
    url.searchParams.append(key, params[key])
    );

  const res = await fetch(url);
  const data = await res.json();

  return data; 
}

// ----------------------------------------------------------------------

// GET /api/color-surveys:id
export async function getColorSurvey(id){
  const res = await fetch(`https://learn.codeit.kr/api/color-surveys/${id}`); 
  const data = await res.json();
  return data;
}

// ----------------------------------------------------------------------

// POST /api/color-surveys
export async function createColorSurvey(surveyData){
  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;
}

위와 같이 api.js파일에서 오류 처리의 예시를 위해

잘못된 URL을 입력하였다.

그리고 main 파일을 실행해보면 다음과 같이 타입에러 - fetch가 실패했다고 나온다.

// main

import { getColorSurveys , getColorSurvey, createColorSurvey } from './api.js';

const data = await getColorSurveys();
console.log(data);

주소를 찾을 수 없기 때문에 오류가 발생하고

fetch가 리턴하는 PromiseRejected 상태가 된다.

 

이런 오류는 try catch문을 이용해서 처리할 수 있다.


api 파일에서 fetch 함수를 사용하여 url을 호출한 res 변수 선언 부분try catch로 감싸거나

main 파일에서 함수를 실제로 호출하는 data 변수 선언 부분try catch로 감싸 줘도 된다.

(try문 안에서 throw되는 오류는 모두 잡아 주기 때문에 상관없기 때문)

두 방법 중에서 필자는 한번 함수를 호출하는 부분을 try catch문으로 감싸보겠다.

 

다음과 같이 코드를 작성한다.

// main

import { getColorSurveys , getColorSurvey, createColorSurvey } from './api.js';

try {
  const data = await getColorSurveys();
  console.log(data);   
} catch (e) {
  console.log('오류가 발생했습니다.');
  console.log(e.message);
}

 

이렇게 try문이 안에서 발생한 오류를 잡아서

catch문 안에 있는 코드가 실행된 것이다.

지금은 콘솔에 오류 메시지를 출력하지만 

실제 웹사이트 코드라면 오류 메시지를 화면에 보여주는 사용자 편의성을 제공할 수 있다.


유효하지 않은 주소를 입력하는 것 외에도

유효하지 않은 헤더 이름을 사용하거나 헤더 값이 이상하면

리퀘스트 자체가 실패해서 fetch가 리턴하는 Promise는 Rejected 상태가 된다.

그런데 여기서 주의해야할 점이 있다.

 

리퀘스트 자체가 실패했을 때만 Promise가 Rejected 상태가 되고

400이나 500 같은 에러 리스폰스가 돌아오는 경우에는

Promise는 Fulfilld 상태가 된다.

예를들어 잘못된 바디 내용을 보내서 400 리스폰스가 돌아오거나

서버 측에서 오류가 발생해서 500 리스폰스가 들어와도

fetch의 Promise는 Fulfilled 상태가 된다는 것이다.

 

[ Rejected 상태와 Fulfilled 상태가 햇갈리다면 다시 복습해보자 - Link ]

 

그래서 이걸 깔끔하게 처리하는 방법은

리스폰스의 상태 코드가 성공을 나타내지 않으면 오류를 발생시키는 것이다.

다음과 같이 api 파일에서 res와 data 변수 사이에 코드를 작성한다.

  const res = await fetch(url);

  if (!res.ok) { // !res.ok : 응답이 성공적이지 않을 때
    throw new Error("데이터를 불러오는데 실패했습니다."); // throw new Error() : 에러 발생
  }

  const data = await res.json();

res.ok는 리스폰스의 상태 코드가 2로 시작하면 true,

그렇지 않으면 false를 리턴한다.

if문 안에서 리스폰스의 실제 상태 코드나 바디 내용을 보고

더 세밀한 처리를 해 줄 수도 있다.

그러니까 리퀘스트 자체가 실패해도 오류를 throw하고

오류를 나타내는 상태 코드가 돌아와도 오류를 throw하는 것이다.

[ throw 복습하기 - Link ]

 

이렇게 처리해 주는 게 헷갈리지 않고 더 편리하다.


잘못된 주소를 다시 정상으로 원복시키고

유효하지 않은 mbti를 쿼리 파라미터로 보내 보자

// main

import { getColorSurveys , getColorSurvey, createColorSurvey } from './api.js';

try {
  const data = await getColorSurveys({mbti:'T발C냐'}); // 유효하지 않은 mbti
  console.log(data);   
} catch (e) {
  console.log('오류가 발생했습니다.');
  console.log(e.message); 
}

다음과 같이 오류 처리했던 메시지가 잘 출력되는 것을 확인할 수 있다.

만약에 res.ok로 오류 처리하지 않았다면

잘못된 mbti를 전달했기 때문에 상태코드 400이 돌아올것이다.

상태 코드 400이 돌아와도 아무 문제없이 다음 줄(const data = await res.jon())이 실행되는데,

이때 상태 코드 400에 해당하는 리스폰스의 형식도 JSON 이라면

바디가 파싱돼서 그대로 리턴 될 것이다.

함수 안에서 아예 오류가 발생하지 않는 것이다.

 

반면 리스폰스 바디 형식이 JSON이 아니라면

오히려 바디를 파싱하는 과정에서 오류가 발생한다.

이러한 다양한 케이스가 있어서 햇갈릴 수 있기 때문에

리스폰스가 성공적으로 처리되지 않으면 그냥 오류를 throw 하는 것이다.

 

그럼, 나머지도 오류 처리 해보자

// api.js

// GET /api/color-surveys를 하는 함수 생성

export async function getColorSurveys(params ={}){
  const url = new URL('https://learn.codeit.kr/api/color-surveys'); // 잘못된 URL 입력 (오류 예시)
  Object.keys(params).forEach((key) => 
    url.searchParams.append(key, params[key])
    );

  const res = await fetch(url); 

  if (!res.ok) { // !res.ok : 응답이 성공적이지 않을 때
    throw new Error("데이터를 불러오는데 실패했습니다."); // throw new Error() : 에러 발생
  }

  const data = await res.json();

  return data; 
}

// ----------------------------------------------------------------------

// GET /api/color-surveys:id
export async function getColorSurvey(id){
  const res = await fetch(`https://learn.codeit.kr/api/color-surveys/${id}`);
  
  if (!res.ok) { // !res.ok : 응답이 성공적이지 않을 때
    throw new Error("데이터를 불러오는데 실패했습니다."); // throw new Error() : 에러 발생
  }
  
  const data = await res.json();
  return data;
}

// ----------------------------------------------------------------------

// POST /api/color-surveys
export async function createColorSurvey(surveyData){
  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) { // !res.ok : 응답이 성공적이지 않을 때
    throw new Error("데이터를 생성하는데 실패했습니다."); // throw new Error() : 에러 발생
  }

  const data = await res.json(); 
  return data;
}

 

다시 한번 복습해보자면..

 

fetch 오류크게 두 가지 경우가 있다고 했다.

1. URL이 이상하거나, 헤더 정보가 이상해서 리퀘스트 자체가 실패하는 경우

2. 리퀘스트는 성공적인데 상태 코드가 실패를 나타내는 경우

 

여기서 조심해야 할 부분은 첫 번째 경우에만

fetch의 Promise가 Rejected 상태가 되는 것.

그래서 오류를 깔끔하게  처리하는 방법 중 하나는

두 번째 경우에도 수동적으로 오류를 throw 해 주는 것.

 

리퀘스트가 성공적으로 처리되고 

성공적인 리스폰스가 돌아왔을 때만 데이터를 리턴하고

나머지 모든 경우엔 오류를 발생시키는 것이다.

그래야 API 함수를 사용하는 입장에서

try catch로 쉽게 로직을 구분할 수 있다.

 

fetch에 대한 오류를 처리를 할 때는

언제 Promise가 Reject 되는지,

어떤 내용이 리스폰스 바디로 돌아오는지를 잘 생각해볼 필요 있다.

 

 

 

 

 

반응형