[유데미x스나이퍼팩토리] 10주 완성 프로젝트 캠프 20일차 - ReactJS: 커스텀 Hook 사용하기

TK·2023년 7월 2일
0
post-thumbnail
  • 일시 : 23.06.30 금요일

🌱과제 리뷰

리액트는 16.8버전부터 새로 추가된 React Hooks라는 기능으로 기존에 Class 바탕의 코드 작성을 대체하게 하였다. 이는 class형 컴포넌트가 갖던 문제점 (길어지는 코드 길이, 중복코드, 가독성 문제 등)을 해결할 수 있었다.

이번 과제는 그 Hooks 중에서도 내가 필요한 기능을 작동하는 Custom Hook을 개발 및 적용하는 것이다. Custom Hook은 코드, 로직의 반복을 최소화하고 재사용성을 높이기 위해 사용한다. 이전에 만든 영화 웹사이트에서 영화API Fetch요청 부분을 Custom Hook으로 나타내어 보았다.

참고자료1 : 공식문서 - Hook의 개요
참고자료2 : 블로그 - Custom Hook이란?

💥문제 발생

처음엔 그냥 'fetch시 공통적으로 사용되는 코드를 쓰면 되지 않을까?'하여 다음과 같은 코드를 만들어 보았다.

// useFetchMovie.js

import { useEffect, useState } from "react";

const useFetchMovie = (url) => {
  const [fetchedMovie, setFetchedMovie] = useState();

  const fetchData = async (url) => {
    const json = await (await fetch(url)).json();
    setFetchedMovie(json);
  };

  useEffect(() => {
    fetchData(url);
  }, [url]);

  return [fetchedMovie];
};
export default useFetchMovie;
// Home.js
import useFetchMovie from "../hooks/useFetchMovie";

const Home = () => {
  const [source] = useFetchMovie(
    `https://yts.mx/api/v2/list_movies.json?minimum_rating=9.0&sort_by=year`
  );
  const movies = source.data.movies;
  return (
    <div>
      <Header />
      <MoviesWrapper>
        {movies.map((movie) => (
          <Movie
            key={movie.id}
            year={movie.year}
            id={movie.id}
            coverImg={movie.medium_cover_image}
            title={movie.title}
            summary={movie.summary}
            genres={movie.genres}
          />
        ))}
      </MoviesWrapper>
    </div>
  );
};
export default Home;

하지만 이 코드를 적용 시켰을 때 첫 렌더링은 잘 나오지만 새로고침 이후로는 다음과 같은 에러가 발생했다.

Uncaught runtime errors라며 '해당 오류는 요소가 DOM에 존재하지 않거나 참조하는 요소가 없을 때 발생'하는 에러였는데 이를 통해 api를 제대로 받아오지 못한다는 것을 알 수 있었다.

🛠️문제 해결

하지만 Custom Hook을 만드는 방법은 무궁무진하다. 바로 다른 방법을 찾아보기 시작했다. 특히 수업에서 배운 useReducer + 강사님이 로딩, 데이터패치, 에러일 경우를 나타내라고 하셨기 때문에 여러 방법을 검색 중 이와 아주 적합한 코드를 발견하였다. 여기에 axios를 추가 설치하여 문법을 더 간소화 하였다. 이를 적용한 내 커스텀훅은 다음과 같다.

import { useReducer, useEffect } from "react";

// 세가지 타입에 따른 분류
function reducer(state, action) {
  switch (action.type) {
    case "LOADING":
      return {
        loading: true,
        data: null,
        error: null,
      };
    case "SUCCESS":
      return {
        loading: false,
        data: action.data,
        error: null,
      };
    case "ERROR":
      return {
        loading: false,
        data: null,
        error: action.error,
      };
    default:
      throw new Error(`Unhandled action type: ${action.type}`);
  }
}

// custom Hook
function useFetchMovie(callback, deps = []) {
  const [state, dispatch] = useReducer(reducer, {
    loading: false,
    data: null,
    error: false,
  });

  const fetchData = async () => {
    dispatch({ type: "LOADING" });
    try {
      const data = await callback();
      dispatch({ type: "SUCCESS", data });
    } catch (e) {
      dispatch({ type: "ERROR", error: e });
    }
  };

  useEffect(() => {
    fetchData();
    // eslint 설정을 다음 줄에서만 비활성화
    // eslint-disable-next-line
  }, deps);

  return [state, fetchData];
}

export default useFetchMovie;
// Home.js
import axios from "axios";
import useFetchMovie from "../hooks/useFetchMovie";

async function getUsers() {
  const response = await axios.get(
    `https://yts.mx/api/v2/list_movies.json?minimum_rating=9.0&sort_by=year`
  );
  return response.data;
}

const Home = () => {
  const [state, refetch] = useFetchMovie(getUsers, []);
  const { loading, data: users, error } = state; 

  if (loading) return <div>로딩중..</div>;
  if (error) return <div>에러가 발생했습니다</div>;
  if (!users) return null;
  
  const movies = users.data.movies;
  return (
    <div>
      <Header />
      <MoviesWrapper>
        {movies.map((movie) => (
          <Movie
            key={movie.id}
            year={movie.year}
            id={movie.id}
            coverImg={movie.medium_cover_image}
            title={movie.title}
            summary={movie.summary}
            genres={movie.genres}
          />
        ))}
      </MoviesWrapper>
    </div>
  );
};
export default Home;

아직 useReducer 훅에 익숙하지 않은 나에게 직접 활용하고 이해할 수 있는 기회가 되었다.

출처 : useAsync 커스텀 Hook 만들어서 사용하기


🌱과제 내용

구현 페이지

(추가예정)

깃허브 주소

코드보기

본 후기는 유데미-스나이퍼팩토리 10주 완성 프로젝트캠프 학습 일지 후기로 작성 되었습니다.

profile
쉬운게 좋은 FE개발자😺

0개의 댓글

관련 채용 정보