자, 지금까지 잘 진행되고 있습니다
fetch된 데이터로 데이터를 잘 가져오고 있고
로딩 상태들도 잘 관리하고 있습니다
그럼, 또 자주 마주하는 상황이 있는데
바로 HTTP 요청을 보낼 때 발생하는 에러입니다
왜냐하면 HTTP 요청을 보낼 때
실패하는 요인으로 여러가지가 있는데
사용자의 네트워크 연결이 원활하지 않아서일 수도 있고
백엔드에 문제가 생겼을 수도 있고
아니면 예를 들어 서버가 일시적으로 오프라인일 수도 있고
코드에 버그가 있는 것일 수도 있습니다
이유가 무엇이든 간에 에러가 발생하면
프론트엔드 코드에서는
그 상황에도 미리 대비해두어야 합니다
데이터 fetching에 실패할 수 있다는 걸 항상 염두해 두어야 합니다
데이터 fetching이 실패하는 데에는
크게 두 가지 이유가 있습니다
한 가지는
애초에 요청을 보내는 것에 실패하는 것입니다
예를 들어, 네트워크 연결이 충돌할 수 있습니다
다른 상황은
요청을 보낼 때
백엔드에는 성공적으로 전달되었지만
거기서부터 문제가 발생해
백엔드가 에러 응답을 보내는 경우입니다
그리고 두 상황들을 모두 다룰 줄 알아야 합니다
그럼, 에러 응답을 가지고 시작해봅시다
fetch 함수를 사용해서 HTTP 요청을 보낼 때
응답이 에러 응답인지 확인해 볼 수 있습니다
response.ok 속성을 확인하면 됩니다
그게 True라면
그 뜻은 성공적인 응답을 받았다는 것이고
200-300 정도의 상태 코드를 받습니다
만약 그게 True가 아니라면, 그러니까 False라면
400이나 500 정도의 상태 코드를 받습니다
그러니까 여기서 응답이 괜찮은지를 확인하고
괜찮지 않다면 그 문제를 해결해야 합니다
예를 들어, throw를 써서
여기 error 객체를 만들어줍니다
내장 에러 클래스의 인스턴스(instance)를 만들고
에러 메시지를 설정해줍니다
'장소를 불러오는 데 실패했습니다'로 하겠습니다
그리고 에러를 throw해줍니다
물론 이렇게 한 번에 할 수도 있습니다
그럼 에러를 throw하면
앱이 충돌합니다
원하지 않는 결과입니다
그러니까 에러를 throw할 수 있는 코드를 감싸야 합니다
try catch를 사용해야 에러를 방지할 수 있습니다
async await를 사용할 때 쓸 수 있습 방식입니다
try를 실패할 수 있는 코드에 감쌉니다
이 경우에는 이 코드입니다
그러면 throw된 에러들을
catch 할 수 있습니다
그리고 에러가 발생했을 때
실행되야하는 코드를 정의하면
이것이 앱 충돌을 멈추고
대신, 실행되어야 하는 코드를
정의하게 합니다
이제 물론
그냥 에러가 throw되는 걸 막을 수 있었습니다
그러면 try-catch도 필요 없을 것입니다
하지만 fetch 함수 자체도 실패해서
에러가 throw될 수 있습니다
예를 들어, 네트워크 연결이 없을 때 그렇습니다
그래서 try-catch를 사용해서
에러를 throw하려고 한 것입니다
응답이 불만족스럽다면
어쨌거나 이 catch 블록으로 끝납니다
어떤 문제든 발생하면 그렇게 됩니다
그리고 이 catch 블록에서는
이제 에러를 다룰 수 있습니다
그리고 리액트 앱에서 에러를 다룬다는 건
UI를 업데이트하고 싶다는 뜻이고
사용자에게 에러 메세지를 보여주고 싶다는 것입니다
그리고 그건 무슨 뜻이냐면
세 번째 상태, 즉 에러를 위한 상태가 필요합니다
error 라고 이름을 짓겠습니다
그리고 상태를 업데이트하는 setError라는 함수도 만들겠습니다
데이터를 가져올 땐 흔하게
이 세 가지의 상태들이 함께 동작합니다
data 상태는
fetching하려는 데이터를 저장하고
loading 상태는
사용자에게 현재 데이터가 fetching 중이라고 알려주고
error 상태는
발생할 수 있을 만한 에러를 화면에 띄웁니다
그리고 이제 error 상태가 추가되었으니
여기 catch 블록에서 에러를 설정할 수 있습니다
예를 들어
이 error 객체를 error의 값으로 설정합니다
에러 메시지를 포함한 error 객체가 될 것입니다
예를 들어 여기 설정한 이 에러 메시지입니다
error 상태를 사용해 UI를 업데이트하기 전에
try-catch를 사용할 때
한 가지 더 해야할 것이 있습니다
여기 이미 빨간출이 쳐진 것 보이시나요?
그게 생기는 이유는
여기서 availablePlace를
try 블록 안에 있는 데이터를 기반으로 업데이트하려 하기 때문입니다
이 단계에서 에러가 발생하면
응답 데이터를 절대 받지 못합니다
그러니 여기 설정하는 것은 좋은 생각이 아닙니다
대신, 이 코드를 try 블록으로 이동합니다
왜냐하면 여기에는
이 블록이 다 실행될 쯤엔
모든 데이터가 있을 것이기 때문입니다
한편, setIsFetching을 False로 해둔 것은
try-catch 블록 바깥에 있어야 합니다
왜냐하면 이 loading 상태는
에러가 났든 안 났든 끝내고 싶기 때문이죠
장소 데이터를 가져오는 데 실패하면
더이상 fetching하지 않기 때문입니다
즉 더이상 데이터를 로딩하지 않습니다
에러가 발생했지만, fetching은 더이상 하지 않습니다
그러니 이건 어느 쪽이든 False로 설정해두어야 합니다
그걸로, 이제 error 상태를 사용해
UI를 업데이트할 수 있습니다
그러기 위해선 이 컴포넌트에서
에러가 발생하는지 확인해보겠습니다
만약 에러가 발생하면
다른 error 컴포넌트를 따로 반환하겠습니다
제가 미리 준비해둔 것으로, 첨부되어 있습니다
이 Error.jsx 파일을 추가해야 하는데
components 폴더 안에 첨부되어 있습니다
이건 간단하게
title과 message를 출력하는 컴포넌트입니다
나중에 그걸 없앨 수 있게도 해줍니다
AvailablePlaces 안에서는
출력된 걸 그대로 두겠습니다
대신, 이 error 컴포넌트를 렌더링하겠습니다
그래서 error.jsx로부터 가져와야 합니다
가져와서 렌더링이 완료되면
'에러가 발생했습니다!' 같은 title을 설정해야 합니다
그리고 message를 설정해야 하는데
여기서 message는 error.message이며
이 error 객체에 있습니다
이제 잘 작동하는지 확인하기 위해
에러를 만들어 보겠습니다
유효하지 않은 URL로 요청을 보내 보겠습니다
기억해두셔야 할 게, 백엔드로 요청을 보낼 땐
해당 백엔드 서버가 노출한
리소스와 엔드포인트(endpoint)들에만 접근할 수 있습니다
그래서 이걸 placessss로 보내겠습니다
일부러 s를 더 넣었습니다
그러면 유효하지 않은 URL이 됩니다
그래서 유효하지 않은 HTTP 요청을
벡엔드로 보내는 것입니다
당연히 백엔드가 받아줄 수 없는 요청입니다
저장하고 다시 로딩해보면
'에러가 발생했습니다' 박스가 보입니다
그리고 여기 메시지가 비어있는 게 보이실 겁니다
그래서 여기서 할 수 있는 건
이 error를 catch 할 때
이 error를 객체에 설정해두는 겁니다
그래야 error.message에 접근할 때마다
언제나 message 속성이 있습니다
하지만 대체 텍스트로, 만약 그게 정의되어 있지 않다면
사용자 정의 message를 정의합니다
예를 들어 '장소를 불러오지 못했습니다'
'다시 시도해주세요'
같은 문구를 쓰겠습니다
그래서 이걸 대체 에러 메시지로 설정하고
이제 저장을 하면
여기 대체 메시지가 보입니다
그리고 여기까지가 에러를 다루는 방법이었습니다
이제 이 s들을 지워보겠습니다
저장하고 새로고침을 하면 잘 작동해야 합니다
잘 알아두어야 하는 것은, 여러분이 에러를 다룰 수 있다는 것과
그리고 데이터를 가져올 땐
이 세 가지 상태들이 함께 작동한다는 것입니다