ㅇㅇㅈ Blog

프론트엔드 수행중

0%

React로 Movie-app 만들어 보기(2)

05.29 작업내용

영화 추가 요청..

영화 추가요청을 하기 위한 로직을 작성했다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
export const fetchAsyncMovies = createAsyncThunk(
'search/fetchAsyncMovies',
async ({ title, type, year, number }) => {
const response = await axios
.get(
`http://www.omdbapi.com/?apikey=${OMDB_API_KEY}&s=${title}&type=${type}&y=${year}&page=1`
)
.then((res) => res.data)
.catch((error) => error.message);
const { totalResults } = response;
const total = parseInt(totalResults, 10);
const pageLength = Math.ceil(total / 10);

return response;

// if (pageLength > 1) {
// for (let page = 2; page < pageLength; page++) {
// if (page > number / 10) break;
// const response = await axios
// .get(
// `http://www.omdbapi.com/?apikey=${OMDB_API_KEY}&s=${title}&type=${type}&y=${year}&page=${page}`
// )
// .then((res) => res.data);
// return response;
// }
// }
}

문제는 처음 요청 했을때 state에 값을 저장해놓고 추가 요청이 들어가야 하는데
분리를 할 수가 없다.. 어떻게 해야되나

버튼 active 활성

영화 아이템을 클릭해서 상세 페이지로 넘어가면 버튼이 활성화가 되지 않는다
path 속성을 따로 만들어 movie와 일치하면 active class가 붙게 했다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
const navigations = [
{
name: 'Home',
href: '/',
},
{
name: 'Movie',
href: '/movie/tt2294629',
path: /^\/movie/,
},
];

export default function Header() {
const location = useLocation();

const isMatch = (path) => {
if (!path) return false;
return path.test(location.pathname);
};
return (
<header className='header'>
<div className='nav nav-pills'>
<div className='nav-item'>
{navigations.map((nav) => {
return (
<NavLink
to={nav.href}
key={nav.name}
className={isMatch(nav.path) ? 'active nav-link' : 'nav-link'}
style={{ marginRight: 15 }}
>
{nav.name}
</NavLink>
);
})}
</div>
</div>
</header>
);
}

여기서 react-router-dom 의 useLocation을 사용했는데 location을 콘솔로 찍어보면

pathname이 찍힌다

isMatch 함수를 통해 path가 location.pathname에 들어있는지를 true,false로 반환 받는다

삼항연산자로 active가 붙을지 안붙을지를 작성해준다

06.01 작업내용

영화정보 추가 요청을 위해 새로 slice와 search를 새로 작성 했다

기존에는 searchSlice에서 영화 정보를 요청했었는데 굳이 그럴 필요가 없는거 같다..

처음부터 컴포넌트 내에서 요청을 하고 받아온 데이터를 store에 저장 하기로 변경

Search 컴포넌트 에서 정보요청 함수 작성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import axios from 'axios';
import { useDispatch } from 'react-redux';
import { searchMovie, setLoading, addMovie } from '../store/searchSlice';

const Search = () => {
const dispatch = useDispatch();
const [search, setSearch] = useState({
title: '',
year: '',
type: '',
number: '',
});
const handleChange = (e) => {
const { name, value } = e.target;
setSearch({ ...search, [name]: value });
};

const handleSearchMovies = async (e) => {
e.preventDefault();
dispatch(setLoading());
const res = await axios
.get(
`http://www.omdbapi.com/?apikey=f6573a61&s=${search.title}&type=${search.type}&y=${search.year}&page=1`
)
.then((res) => res.data);

dispatch(searchMovie(res));

};
return (
<StyleForm onSubmit={handleSearchMovies}>
<Form.Control
placeholder='search for movie'
onChange={handleChange}
name='title'
></Form.Control>
{filters.map((select) => {
return (
<Form.Select
name={select.name}
key={select.name}
onChange={handleChange}
>
{select.name === 'year' && <option>All Years</option>}
{select.items.map((items) => {
return (
<option value={items} key={items}>
{items}
</option>
);
})}
</Form.Select>
);
})}
<Button type='submit'>Search</Button>
</StyleForm>
);
};

export default Search;

영화를 검색하고 나면 최초의 요청이 들어가고 10개의 영화 목록을 가져 올 수 있다.

여기서 추가 요청을 작업 해준다

먼저 최초의 요청때 받아온 데이터를 보면

Search 부분에 영화 정보 10개,
그리고 검색한 단어에 해당하는 영화정보의 갯수가 totalResults 에 담겨 있다

그럼 최초의 10개를 제외하면 616개를 더 불러 올 수 있다.
이때 한 번의 요청에 10개의 정보씩을 가져오므로 616개를 모두 불러 올려면 62번의 요청을 더 날리면 된다는 뜻..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const handleSearchMovies = async (e) => {
e.preventDefault();
dispatch(setLoading());
const res = await axios
.get(
`http://www.omdbapi.com/?apikey=f6573a61&s=${search.title}&type=${search.type}&y=${search.year}&page=1`
)
.then((res) => res.data);
console.log(res);
dispatch(searchMovie(res));

const { totalResults } = res
const total = parseInt(totalResults,10)
const pageLength = Math.ceil(total/10)


if (pageLength > 1) {
for (let page = 2; page <= pageLength; page++) {
if (page > search.number / 10) break;
const res = await axios
.get(
`http://www.omdbapi.com/?apikey=f6573a61&s=${search.title}&type=${search.type}&y=${search.year}&page=${page}`
)
.then((res) => res.data);
dispatch(addMovie(res));
}
}
};

데이터 가공

1
2
3
4
5
6
const { totalResults } = res
// 데이터에서 totalResults만 구조분해로 꺼내온다
const total = parseInt(totalResults,10)
// totalResults의 숫자는 number가 아닌 string 이기 때문에 숫자로 변환 해준다
const pageLength = Math.ceil(total/10)
// total 나누기 10을 해주면 우리가 요청 할 수 있는 횟수가 된다. 이때 올림 처리를 해줘야 한다

추가요청
pageLength 즉 우리가 요청할수 있는 횟수가 1보다 크면 로직이 돌아가도록 if문을 설정 한다
최초의 요청으로 1페이지(10개의 목록)는 불러 왔으므로 2페이지부터 조회가 되도록 반복문을 만들어 준다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if(pageLength > 1) {
for(let page = 2; page <= pageLength; page++){
// 변수 명을 통상적으로 i로 작성하지만 여기선 page로 작성했다
if(page > search.number / 10) break;
// search.number는 select에서 선택하는 영화 갯수
// 우리가 선택한 영화의 갯수보다 page가 많아질시 요청을 종료하는 if 문
const res = await axios.get(
`http://www.omdbapi.com/?apikey=f6573a61&s=${search.title}&type=${search.type}&y=${search.year}&page=${page}`
)
.then(res => res.data)
// page 파라미터에 추가로 요청할 페이지 갯수를 넘긴다
dispatch(addMovie(res))
// 그리고 받아온 정보를 store에 저장한다
}
}

searchSlice
searchSlice의 reducers 부분
최초의 요청시에는 searchMovie action으로 영화의 정보들을 담아주고
추가 요청시에는 addMovie action으로 영화의 정보를 추가로 저장 해준다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
reducers: {
searchMovie: (state, { payload }) => {
const { Response, Search, Error } = payload;
return {
...state,
movies: _uniq(Search, 'imdbID'),
message: null,
response: Response,
error: Error,
loading: false,
};
},
addMovie: (state, { payload }) => {
const { Search } = payload;
state.movies = [...state.movies, ..._uniq(Search, 'imdbID')];
},
setLoading: (state) => {
return { ...state, loading: true };
},
},

addMovie 에서 기존의 데이터를 먼저 스프레드로 열어주고 그 뒤에 검색 결과를 열어서 추가 저장 해준다