과제

요구사항

다양한 커스텀 훅(Custom Hook)을 설계, 구현 및 문서화 할 것

  1. useFetchMovies
    • 영화 API에 HTTP 요청을 보내고,응답을 받아와서 영화데이터를 처리
    • 반환값으로는 로딩상태,영화데이터,에러 정보등을 포함하는 객체를 반환
  2. useFavoriteMovies
    • 사용자가 영화를 즐겨찾기에 추가하거나 제거할 수 있는 함수를 제공
    • 반환값 자유
  3. useSearchMovies
    • 사용자가 입력한 검색어로 영화를 검색하고, 검색결과를 처리
    • 반환값 자유
  4. usePagination
    • 사용자가 다른페이지로 이동할 수 있도록 페이지번호를 관리
    • 반환값 자유

커스텀 훅(Custom Hook)

커스텀 훅을 만들기전에 의미를 알아보자

커스텀: 관습이라는 사전적 의미이지만, 여기서는 본인의 상황에 맞는 개인 맞춤 정도로 보면 된다
훅: 함수라고 보면 된다(useXxxx)

즉, 개발하는데에 있어 여기저기서 공통적으로 쓰이는 코드를 맞춤으로 설계하여 반복작업을 줄이는 행위이다

코드

useFetchMovies

보통의 프론트 작업에서 데이터를 가져와야 하는 경우,

  1. useEffect에서 빈배열을 두어, 초기렌더링 시 데이터를 요청
  2. 응답이 올 때 까지 로딩화면 표시
  3. 제대로 데이터를 가져올 경우, 데이터를 렌더링
  4. 에러발생의 경우 후속처리

위의 작업을 반복적으로 하게 된다
이와 같은 반복을 줄이기 위해서는 커스텀 훅을 만드는 것이 좋다

// src/hook/useFetchMovies.js
import { useEffect, useState } from "react";

const BASE_URL = "https://yts.mx/api/v2";

const useFetchMovies = (path, params) => {
  const [isLoading, setIsLoading] = useState(false);
  const [data, setData] = useState(null);
  const [error, setError] = useState(null);

  useEffect(() => {
    setIsLoading(true);
    let url = `${BASE_URL}${path}`;
    if (params) {
      const searchParams = new URLSearchParams(params);
      url += "?" + searchParams.toString();
    }
    fetch(url, { method: "GET" })
      .then((res) => res.json())
      .then((json) => {
        // console.log(json.data);
        setData(json.data);
      })
      .catch((err) => setError(err))
      .finally(() => {
        setIsLoading(false);
      });
  }, [path, params]);

  return { data, error, isLoading };
};

export default useFetchMovies;

path: 베이스 url에다가 요청을 보낼 경로(리스트면 리스트, 디테일이면 디테일)
params: 요청 시, 물음표(?) 뒤에 오는 간단한 정보들을 담는다(보통 어떤 아이디, 갯수 등)

현재는 GET요청에 한해 돌아가는 코드를 구현해보았다
더욱 살을 붙여 다른 요청에도 방어가 가능한 코드로 발전시키면 좋을 것 같다

// App.js
const { data, isLoading, error } = useFetchMovies(
  "/list_movies.json",
  `minimum_rating=7.0&sort_by=year&page=${temp.current}`
);

usePagination

게시판등에서, 총게시물이 너무 많을 경우 infinite scroll방식을 채택할 수 있지만 전통적으로는 페이지네이션을 많이 쓴다

이 기능을 구현하려면, 얼마나 많은 데이터수를 가지고 있는지와 한 화면에 뿌려줄 데이터 갯수를 바탕으로 페이지네이션을 구현해야 한다

// src/hook/usePagination.js
import { useState } from "react";

const usePagination = (totalCount = 1, itemsPerPage = 1) => {
  const [currentPage, setCurrentPage] = useState(1);
  const maxPage = Math.ceil(totalCount / itemsPerPage);
  const next = () => {
    setCurrentPage((currentPage) => Math.min(currentPage + 1, maxPage));
  };
  const prev = () => {
    setCurrentPage((currentPage) => Math.max(currentPage - 1, 1));
  };

  const jump = (page) => {
    const pageNumber = Math.max(1, page);
    setCurrentPage(Math.min(pageNumber, maxPage));
  };
  return { next, prev, jump, currentPage, maxPage };
};
export default usePagination;

총 데이터 갯수를 페이지당 렌더링할 갯수로 나눈 값을 올림 하면 총 페이지의 개수가 된다
또한 페이지 이동의 경우, useState로 선언한 currentPage를 컨트롤 해주면 된다

// src/components/Pagination.js
const Pagination = ({ next, prev, jump, currentPage, maxPage }) => {
  const computedStyle = (page) => {
    if (page === currentPage) {
      return {
        color: "red",
      };
    }
  };
  const computedStartPages = () => {
    if (currentPage > 5) {
      return Math.floor((currentPage - 1) / 5) * 5;
    }
    return 0;
  };

  const computedEndPages = () => {
    if (currentPage > 5) {
      return (Math.floor((currentPage - 1) / 5) + 1) * 5;
    }
    return 5;
  };

  return (
    <PaginationWrapper>
      <button onClick={() => prev()}>prev</button>
      {Array(maxPage)
        .fill()
        .map((_, idx) => idx + 1)
        .slice(computedStartPages(), computedEndPages())
        .map((v) => (
          <button style={computedStyle(v)} key={v} onClick={() => jump(v)}>
            {v}
          </button>
        ))}
      <button onClick={() => next()}>next</button>
    </PaginationWrapper>
  );
};
export default Pagination;

회고(라기 보다는 반성)

현재 익숙하지 않은 리액트의 개발을 하려다 보니 과제에 미흡한 점이 많이 있다
추후에 보강하여 개발을 해두어야겠다


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

profile
😂그냥 직진하는 (예비)개발자

0개의 댓글