본문 바로가기

Solution & What I learn

[React - Project] movie | Issue & Solution

반응형

🎬 tmdb API를 활용하여 영화 추천 사이트

 

 

 

Movie

 

movie4e8f82.netlify.app

 

📚 Stack

 

- React

    - styled-components

    -  react-icons

    - react-responsive

    - react-router-dom

- Redux-thunk

- 배포 : netlify

 

📚 구조 & 기능

 

- 오늘의 영화 Top 10 추천

- 선택한 장르별 영화 Top 20 추천

- 영화 선택 저장

     - 선택순, 추천순, 인기순 정렬

 

 

📚 Problem & Solution

 

▶ 구현시 고민한 부분

 

 

1. GenreList Component - 체크박스 기능 컴포넌트 작성 시 고민한 부분

 

function GenreListComponent({ data }) {
  const dispatch = useDispatch();
  const userGenre = JSON.parse(localStorage.getItem("genre"))
    ? JSON.parse(localStorage.getItem("genre"))
    : [];

  const [selectGenres, setselectGenres] = useState(userGenre); //유저가 선택한 장르
  const [isAllChecked, setIsAllChecked] = useState(
    userGenre.length === 0 ? true : false
  ); //"모든장르" input controller

  //전체해제
  const onCancle = () => {
    setIsAllChecked(true);
    setselectGenres([]);
  };

  //"모든장르" 컨트롤
  const handleAll = () => {
    setselectGenres([]);
    setIsAllChecked(true);
  };

  //"모든장르" 제외 컨트롤
  const handleCheck = (e, checked, genre) => {
    setIsAllChecked(false);
    if (checked) {
      setselectGenres([...selectGenres, genre]);
    } else {
      setselectGenres(
        selectGenres.filter(
          (genres) => JSON.stringify(genres) !== JSON.stringify(genre)
        )
      );
    }
  };

  //찾아보기
  const onSearch = () => {
    setGenre(selectGenres);
    dispatch(getGenreMovies(selectGenres.map((genre) => genre.id)));
  };

  return (
    <S.Section>
      <S.Genres>
        <S.Title>장르별 영화 찾기</S.Title>
        <S.GenreBox>
          <S.GenreList>
            <S.Input
              type="checkbox"
              name="allGenres"
              id="allGenres"
              checked={isAllChecked}
              onChange={(e) => handleAll(e.target.checked)}
            />
            <S.Label htmlFor="allGenres">모든 장르</S.Label>
          </S.GenreList>
          {data &&
            data.map((genre) => (
              <S.GenreList key={genre.id}>
                <S.Input
                  type="checkbox"
                  name={genre.name}
                  id={genre.id}
                  checked={
                    JSON.stringify(selectGenres).includes(JSON.stringify(genre))
                      ? true
                      : false
                  }
                  onChange={(e) => handleCheck(e, e.target.checked, genre)}
                />
                <S.Label htmlFor={genre.id}>{genre.name}</S.Label>
              </S.GenreList>
            ))}
        </S.GenreBox>
        <S.ButtonBox>
          <S.Button onClick={onCancle}>전체해제</S.Button>
          <S.Button onClick={onSearch}>찾아보기</S.Button>
        </S.ButtonBox>
      </S.Genres>
    </S.Section>
  );
}

 

 

1) 첫 렌더링 시, 모든 장르 영화가 출력되도록 하기

- 장르선택이 없다면 전체 Top 20 영화를 받아오는 API였기 때문에, 모든 장르의 초깃값을 빈 배열로 설정하여 초기 렌더링 시 모든 장르로 렌더링 될 수 있도록 구현했다.

 

2) 장르 선택 후 찾아보기 클릭 시, 장르에 맞는 영화 보여주기

- 유저가 장르를 선택하고 찾아보기를 클릭하면, 선택된 장르를 배열로 만들어 배열의 요소를 api의 params로 전달될 수 있도록 구현했다.

 

3) 새로고침 시, 모든 장르가 아닌 유저가 선택한 장르의 영화가 다시 렌더링 될 수 있도록 하기

-  로컬 스토리지를 사용하여 유저가 찾아보기 클릭 시 선택했던 장르 배열을 저장하고 새로고침이나 페이지 이동시에도 저장되어있는 장르 배열로 다시 렌더링 할 수 있도록 구현했다.

 

*개선사항 - 체크박스의 checked, defaultChecked가 다르게 구현되기 때문에 이 부분은 조금 더 공부하고 수정이 필요하다고 생각한다.

=> defaultChecked 사용시, view에 바로 반영되지 않음, html에 반영

- 초기 렌더링시 사용

- 후속 렌더링에서 값을 업데이트해야 하는 경우, onChange값 변경을 처리하는 함수를 사용해야 한다.

=> checked 사용 시,  view에 바로 반영, html에 반영이 안 됨

 

 

 

2. My Component - 정렬 작성 시 고민한 부분

 

const handleSelect = (e) => {
  setSort({ value: e.target.value });

  switch (e.target.value) {
    case "pick":
      setSortMovies(JSON.parse(localStorage.getItem("liked")));
      break;
    case "release":
      setSortMovies(likeMovies.sort(dateSort));
      break;
    case "popularity":
      setSortMovies(
        likeMovies.sort((a, b) => a.popularity - b.popularity).reverse()
      );
      break;
    default:
      console.log("error");
  }
};

 

- switch문으로 유저가 선택한 정렬 value에 맞는 코드가 구현되도록 했다. 

 

3. 로컬 스토리지 사용 시 고민한 부분

 

1) 객체 배열에서 객체 찾기

- 처음에는 객체 배열 또한 배열이니까 includes 메서드로 해결이 될 줄 알았는데, 순수 배열에서만 된다는 것을 뒤늦게 알았다. 그래서 JSON.stringify를 이용해서 includes를 사용했다. (이 방법이 정답은 아닌 것 같다.)

 

 

4. 전체적으로 고민한 부분

 

1) 재사용할 수 있는 컴포넌트는 분리될 수 있도록 컴포넌트 구조를 나눴다.

2) container component와 presentational component를 나누어 상태, UI를 관리하는 구조로 구성했다.

3) 유틸 함수로 refactoring 하여 api를 받아오는 부분에서 반복되는 함수를 정리했다.

4) 불필요한 style은 작성하지 않기 위해 고민했다.

 

 

 📚 개선 사항 or 추가할 기능

 

- 테스트 코드를 작성해보고 싶다.

- 검색 기능 넣기 (추가 예정)