리액트에서의 HTTP 요청 (2)
오늘은 어제 배운 HTTP 요청에 대해 좀 더 깊게 들어가 살펴보았다.
어제 사용한 app 컴포넌트에는 HTTP 요청을 했을 때 서버와의 통신에 문제가 생겼을 때 사용자에게 상황을 알려줄 수 있는 코드를 추가하고, 스타워즈API에서 사실상 정적인 데이터를 전달받는 대신 사용자가 입력한 내용을 서버에 저장하고 전달받을 수 있는 코드도 추가했다.
우선 서버와의 통신에 오류가 발생했을 경우의 상태값과 상태변경함수를 만들고 초기값은 null로 해주었다. 기존에 함수였던 fetchMoviesHandler는 상수명으로 바꿨고, 이 상수의 값으로 이전값을 저장하고 불필요한 계산이 이루어지지 않도록 useCallback 훅을 사용해서 비동기함수를 생성해주었다. 이 함수의 실행으로 setError 함수는 null 값을 유지하고, try-catch문을 사용해서 try문에서 response 상수를 불러오고, 이 response 함수 내의 fetch 메서드의 대상 URL과의 통신이 부적절하여 response 객체의 내장된 속성인 ok 속성에서 값이 false가 나오면 Something went wrong!이라는 문구의 에러가 출력되도록 했다.
💡 response 객체와 ok 속성: response 객체는 웹 요청을 보내고 받은 응답을 나타내는 자바스크립트의 객체이다. 이 객체에는 요청에 대한 여러 속성과 메서드가 있는데, 그 중에서 응답이 성공적으로 완료되었는지를 나타내는 ok 속성이 있다. 속성값은 불리언형태로 true나 false값으로 나오며, HTTP 상태 코드가 200번대인지를 확인하여 결정한다. (200번대 상태 코드는 일반적으로 성공적인 요청을 나타낸다.)
그리고 여기서 정적인 데이터 대신 사용자가 추가한 내용을 서버에 저장하고, 이를 전달받기 위해 구글의 파이어베이스를 사용했다. 파이어베이스는 대부분의 기능이 무료로 제공되며, 코드없이 이미 구축되어 있는 백엔드 어플리케이션을 이용할 수 있어 굉장히 편리하다.
위 fetch의 첫 인자로 추가한 URL이 파이어베이스의 내 테스트서버 주소이며, 영화정보를 담고싶기에 movies.json을 직접 입력해주었다. 이렇게 하면 서버에는 사용자가 추가한 내용이 movies 객체에 저장된다.
그후, 이 서버에 저장될 내용은 json 형태여야 하기에, data라는 상수를 선언해서 response를 json형태로 저장하도록 했다. loadedMovie는 사용자가 추가한 정보들이 담길 배열이며, 초기값은 빈 배열로 해주었다.
그리고 fetch 메서드를 통해 서버에 저장될 정보들은 객체로 저장된다. 그래서 for in 루프를 사용하여, data 객체의 각 속성이 데이터베이스의 key에 저장되므로, key를 선언하여 이 객체 내에 id, title, openingText, releaseDate 키와 값들을 설정해주었다. id를 제외한 3개의 키의 값은 key를 받아 해당 key에 맞는 값을 가지도록 했다. 이렇게 만든 객체는 빈 배열인 loadedMovie 내 값으로 지정해주었고, push 메서드로 값이 추가될 때마다 맨 뒤에 하나씩 추가되도록 했다.
리스트에 나올 영화는 이 loadedMovie로 변경해주었고, 만약 이 과정에서 통신 오류가 발생할 때를 대비해서 catch 메서드에서 error 상태를 바꿔서 위에서 출력한 문구가 나타나도록 해주었다.
그리고, 이렇게 만든 fetchMoviesHandler를 컴포넌트가 실행될 때 함께 실행하여 초기 영화 데이터를 가져오기 위해 useEffect 훅을 사용했고, 두 번째 인자로 fetchMoviesHandler를 전달하여 이 상수의 함수가 업데이트될 때마다 이 함수가 다시 실행되도록 했다.
그리고 비동기함수인 addMovieHandler도 추가해주었는데, 사용자가 입력한 내용을 서버에 전달하기 위해 버튼을 누르면 리스트에 추가될 때 실행되는 함수이다. 이 함수의 response에는 같은 URL이 있는 fetch 메서드를 만들고, 두 번째 인자로 객체를 전달해주었는데, 사용자가 전달하고 이를 저장하여 반환받기 위해 POST method를 사용하고, 이 POST 요청의 본문(body)은 movie 매개변수에 있는 입력된 영화 데이터를 JSON 형식의 문자열로 서버에 전달한 것을 받도록 해주었다. 또, headers 객체를 사용해서 요청의 헤더에 content-type 키와 application/json 값을 지정하여 JSON 형식의 데이터를 전달한다는 것을 명시해주었다.
그 아래에서는 또 다른 data 상수를 만들어서 addMovieHandler 함수의 response를 json 형식으로 저장하도록 했고, 이 값을 콘솔에도 나타날 수 있게 해주었다. 그리고 어제 반환문에 로딩중일 때와 아닐 때로 나누어 코드를 작성한 것을 조금 수정해주었는데, content 변수를 만들어서 영화를 찾을 수 없다는 문구를 기본값으로 설정해주었다.
이 변수를 조건문에서 사용했는데, 영화 데이터가 존재하면 변수값으로 영화리스트가 표출되게 했고, 에러가 발생하면 위에서 설정한 error의 문구가 출력되도록 했으며, 로딩중일 때는 로딩중이라는 문구가 나타나도록 했다.
위 이미지는 어제 만든 파일에 사용자 입력양식을 추가한 것을 보여주는데, 이 양식에 데이터를 입력하여 add Movie를 누르면 서버로 전송되고, 기존의 fetch moives 버튼을 누르면 업데이트된 리스트를 보여주게 되었다.
위 이미지는 입력된 데이터를 버튼을 클릭하여 불러온 모습이다. 이렇게 업데이트된 데이터들은 개발서버를 종료해도 초기화되지 않는다. 구글 파이어베이스의 서버에 저장되어 있기 때문이다.
위 이미지는 파이어베이스 서버에 입력한 데이터가 저장되어 있는 모습이다.
위 이미지는 서버에서 데이터를 전달받은 것(data 상수)을 나타낸 것인데, 저렇게 암호명처럼 된 이름의 객체로 전달이 된다.
리액트의 커스텀 훅
커스텀 훅은 저번에 잠깐 다뤘는데, 리액트 함수 컴포넌트에서 상태 관리 로직을 재사용하기 위한 방법으로, use를 붙여서 사용하는 훅이다. 커스텀 훅을 사용하면 상태와 로직을 캡슐화해서, 여러 컴포넌트에서 동일 로직을 사용하고자 할 때 반복되는 코드를 줄일 수 있다.
이 커스텀 훅은 몇 가지 특성이 있는데, 커스텀 훅은 일반적으로 리액트의 내장된 훅을 사용하거나 다른 커스텀 훅을 활용하여 구성된다. 그리고 리액트의 규칙에 따라 훅 함수 내부에서만 호출되어야 하며, 다른 함수나 조건문, 루프 등에서 훅을 호출해선 안된다. 또, 컴포넌트에서 호출되는 시점에 상태를 초기화하고, 필요한 이벤트핸들러나 상태 갱신 로직을 커스텀 훅에 포함할 수 있다.
위 코드는 다른 파일의 코드이며, 위에 올린 파일과는 전혀 다른 파일의 코드이다. 이 커스텀 훅에는 원래 두 개의 컴포넌트에 나누어져 있던 동일 로직의 코드를 useCounter 훅 안에 넣었다. 한 개는 숫자가 1초에 1씩 증가하는 로직이고, 다른 한 개는 1초에 1씩 감소하는 로직이다.
이 useCounter 훅은 forwards 매개변수를 기본값 true로 설정해서 호출될 때 증가하는지 감소하는지 지정할 수 있다. 만약 위 코드처럼 true면 1씩 증가하고, false면 1씩 감소하는 식이다. 그리고 함수 내에서 counter 상태값과 상태변경 함수를 만들고 기본 상태값은 0으로 설정했다. 그리고 useEffect 훅을 사용하여 컴포넌트의 렌더링과 관계없이 비동기적인 작업을 수행하도록 해주었는데, setInterval 함수를 사용하여 1초마다 실행되는 타이머를 설정해주었다. 이 타이머 콜백 함수 안에 조건문으로 forwards가 true일 때와 아닐 때 counter 값에 어떤 작용이 일어나는지 설정해주었고, 1초에 한번씩 진행되도록 했다. 그리고 반환함수에서 clearInterval을 호출하여 컴포넌트가 언마운트되거나 forwards 값이 변경될 때 타이머를 정리하도록 했다.
💡 setInterval: 일정한 시간 간격으로 콜백 함수를 반복적으로 실행하는 함수이다. setInterval(callback, delay) 식으로 구성되며, 첫 인자인 callback은 반복적으로 실행될 함수를 넣고, 두 번째 인자인 delay에는 콜백 함수 간의 시간 간격을 밀리초 단위로 지정한다.
💡 clearInterval: setInterval에 의해 설정된 반복 작업을 중지한다. clearInterval(setInterval) 식으로 구성되는데, 괄호 안의 인자에는 setInterval 함수명을 적는다.
마지막으로 이 counter 값을 반환하도록 했다.
위에서 커스텀 훅을 사용하여 두 컴포넌트에서 사용한 로직을 하나로 합치니, 기존의 두 컴포넌트(ForwardCounter, BackwardCounter)에서는 코드가 간결해진 걸 확인할 수 있다. 기존의 컴포넌트에서는 이 useCounter 훅을 불러와서 연결만 해주는 것으로 끝이 났다.
위 이미지는 이 커스텀 훅이 적용된 웹페이지를 보여주고 있다. 윗칸은 1씩 증가하며 아랫칸은 1씩 감소하고 있다.

'React' 카테고리의 다른 글
사용자 입력양식 코드 정리 (0) | 2023.06.15 |
---|---|
리액트에서의 양식 및 사용자 입력작업 (0) | 2023.06.14 |
리액트에서의 HTTP 요청 (0) | 2023.06.01 |
useCallback, state 스케쥴링, 클래스형 컴포넌트 (0) | 2023.05.26 |
리액트의 작동원리, 자식 컴포넌트 재평가, react.memo() (0) | 2023.05.25 |