netflix movieSlice 코드 개선 & 설명

완두콩·2023년 6월 1일
0

memo

목록 보기
3/5

현재 작업 중인 netflix 의 movieSlice부분,,
약간 어려운 부분이 있어서 chat Gpt로 코드에 대한 설명을 들었다.
그리고 개선된 방법까지 알고 싶어서 물어봤다.

import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import axios from "axios";
import { TMDB_BASE_URL, API_KEY } from "../../util/constants";

const initialState = {
  movies: [],
  genresLoded: false,
  genres: [],
};

//영화의 장르의 목록을 가져온다.
export const getGenres = createAsyncThunk("movie/genres", async () => {
  const {
    data: { genres },
  } = await axios.get(`${TMDB_BASE_URL}/genre/movie/list?api_key=${API_KEY}`);
  return genres;
});

// 인기 있는 영화 데이터를 가져옴 fetch
//type으로 영화의 유형을 정할 수 잇고
//thunkAPI 매개변수를 통해 현재 Redux 상태를 액세스할 수 있습니다. thunkAPI.getState()를 통해 현재 상태에서 netflix.genres를 가져옴
export const fetchMovies = createAsyncThunk(
  "movie/trending",
  async ({ type }, thunkApi) => {
    const {
      movie: { genres },
    } = thunkApi.getState();
    return getRawData(
      `${TMDB_BASE_URL}/trending/${type}/week?api_key=${API_KEY}`,
      genres,
      true
    );
  }
);
// 주어진 엔드포인트에서 영화 데이터를 가져오는 기능수행
const getRawData = async (api, genres, paging = false) => {
//api 매개변수는 엔드포인트 URL을 나타내며, genres 매개변수는 장르 목록을 전달.
//paging 매개변수는 페이지네이션을 사용할지 여부
  const moviesArr = [];
  //영화 배열의 길이가 60개가 되거나 페이지의 번호가 10을 초과할때까지 진행
  for (let i = 1; moviesArr.length < 60 && i < 10; i++) {
    const {
      data: { results },
    } = await axios.get(`${api}${paging ? `&page=${i}` : ""}`);
    createArrFromRawData(results, moviesArr, genres);
  }

  return moviesArr;
};
//주어딘 데이터 배열과 장르 목록을 기반으로 영화 정보를 가공
const createArrFromRawData = (resultsArr, moviesArr, genres) => {
//결과로 받은 영화를 순회하면서각 장르의 id확인, 해당 장르와 일치하는 장르 이름을 찾아 //영화 장르 배열에 추가 최대 3개
  resultsArr.forEach((movie) => {
    const movieGenres = [];
    movie.genres_ids.forEach((genre) => {
      const name = genres.find(({ id }) => id === genre);
      if (name) movieGenres.push(name.name);
    });

    if (movie.backdrop_path)
      moviesArr.push({
        id: movie.id,
        name: movie?.original_name
          ? movie?.original_name
          : movie.original_title,
        image: movie.backdrop_path,
        genres: movieGenres.slice(0, 3),
      });
  });
};
const movieSlice = createSlice({
  name: "movie",
  initialState,
  extraReducers: (builder) => {
    builder.addCase(getGenres.fulfilled, (state, action) => {
      state.genres = action.payload;
      state.genresLoded = true;
    });
      builder.addCase(fetchMovies.fulfilled, (state, action) => {
        state.movies = action.payload;
      });
  },
});

export default movieSlice.reducer;

수정

  1. generateApiUrl - API URL을 별도의 함수로 구분. 중복을 줄이고 URL 형식을 쉽게 관리할 수 있다.
    --> util폴더 안에 만들어줬다.
import { TMDB_BASE_URL, API_KEY } from "./constants";

const generateApiUrl = (path, queryParams = {}) => {
//path 는 식별자
  const queryString = new URLSearchParams(queryParams).toString();

  return `${TMDB_BASE_URL}/${path}?api_key=${API_KEY}${queryString ? `&${queryString}`: ''}`;
};

export default generateApiUrl;
  1. movieSlice에 generateApiUrl를 적용시켜 바꿔쥬기
    console.log로 찍어 봤을 때 문제 없이 나옴...
export const getGenres = createAsyncThunk("movie/genres", async () => {
  const url = generateApiUrl("/genre/movie/list");
  
  const {
    data: { genres },
  } = await axios.get(url);
  console.log("genres", genres);
  return genres;
});

export const fetchMovies = createAsyncThunk(
  "movie/trending",
  async ({ type }, thunkApi) => {
  const url = generateApiUrl(`/trending/${type}/week`);

    const {
      movie: { genres },
    } = thunkApi.getState();
    return getRawData(url, genres, true);
  }
);
  1. fetchMovies와 getRawData 부분의 궁금증.
    return getRawData(url, genres, true);로
    const getRawData = async (api, genres, paging = false)
    이렇게 받아오던 부분이 변경되었는데 paging이라는 매개변수가 사라짐.
 return getRawData(url, genres);
  • paging을 없애준 이유: paging 매개변수를 사용하여 페이지네이션을 활성화하거나 비활성화하는 로직은 없다. 그러므로 불필요해 보이고 페이지네이션은 항상 활성화하고 있음.
  1. thunkAPi - createAsyncThunk 함수 내에서 사용할 수 있는 매개변수로 액션 생성자 함수 내에서 현재 Redux상태를 읽거나 디스패치 할 수 있음.
  • getState() 현재 리덕스 상태를 반환. thunkAPI.getState()를 호출해서 현재 상태를 가져올 수 있다.
  • dispatch(action) 리덕스 스토어에서 액션을 디스패치. thunkAPI.dispatch(action)을 호출하여 액션을 디스패치
export const fetchMovies = createAsyncThunk("movie/trending",   async ({ type }, thunkApi) => {
    const {
      movie: { genres },
    } = thunkApi.getState();
    //현재 저장된 장르를 가져옴..
    
  const apiUrl = generateApiUrl(`trending/${type}/week`);
  const movies = await getRawData(apiUrl, genres);

  return movies;
});

const getRawData = async (api, genres) => {
  const moviesArr = [];
  for (let page = 1; moviesArr.length < 60 && page < 10; page++) {
  const { data: { results } } = await axios.get(api, { params: { page } });
  createArrFromRawData(results, moviesArr, genres);
}
  return moviesArr;
};
 

최종 수정된 코드

import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import axios from "axios";
import generateApiUrl from "../../util/generateApiUrl";

const initialState = {
  movies: [],
  genresLoaded: false,
  genres: [],
};

export const getGenres = createAsyncThunk("movie/genres", async () => {
  const url = generateApiUrl("/genre/movie/list");
  
  const {
    data: { genres },
  } = await axios.get(url);
  console.log("genres", genres);
  return genres;
});

export const fetchMovies = createAsyncThunk(
  "movie/trending",
  async ({ type }, thunkApi) => {
    const {
      movie: { genres },
    } = thunkApi.getState();
    const apiUrl = generateApiUrl(`trending/${type}/week`);
    const movies = await getRawData(apiUrl, genres);
console.log('movies',movies);
    return movies;
  }
);

const getRawData = async (api, genres) => {
  const moviesArr = [];
  for (let page = 1; moviesArr.length < 60 && page < 10; page++) {
    const {
      data: { results },
    } = await axios.get(api, { params: { page } });
    createArrFromRawData(results, moviesArr, genres);
  }
  return moviesArr;
};
 

const createArrFromRawData = (resultsArr, moviesArr, genres) => {
  resultsArr.forEach((movie) => {
    const movieGenres = [];
    movie.genre_ids.forEach((genre) => {
      const name = genres.find(({ id }) => id === genre);
      if (name) movieGenres.push(name.name);
    });

    if (movie.backdrop_path) {
      moviesArr.push({
        id: movie.id,
        name: movie?.original_name
          ? movie?.original_name
          : movie.original_title,
        image: movie.backdrop_path,
        genres: movieGenres.slice(0, 3),
      });
    }
  });
};

const movieSlice = createSlice({
  name: "movie",
  initialState,
  extraReducers: (builder) => {
    builder.addCase(getGenres.fulfilled, (state, action) => {
      // console.log(action.payload);
      state.genres = action.payload;
      state.genresLoaded = true;
    });
    builder.addCase(fetchMovies.fulfilled, (state, action) => {
      console.log('fetchMovies', action.payload);
      
      state.movies = action.payload;
    });
  },
});

export default movieSlice.reducer;

코드를 개선하면서 느낀 점

chat GPT의 답변은 참고용이다.. 완벽하지 않았다.
개선할 곳을 물어보고 한줄 한줄 어느 부분이 어떻게 수정 되었는지 파악하면서도
이상하거나 이해가 안되는 부분은 몇번이고 질문을 해야했고,
틀린 답변을 내놓는 경우도 많았다.
하지만 계속적으로 질문하면서 많은 부분이 공부가 되었고,
기존 코드에서 비효율적이라고 어렴풋이 느꼈지만 혼자서는 어떻게 더 개선해야할 지 전혀 갈피를 잡을 수 없었던 부분에 대해서는 개선의 방법을 찾을 수 있게 되어 큰 도움이 되었다.

profile
공부하자. 기록하자. 쫌!

0개의 댓글