๋ฐฐ์ด ์ํ์ ๋ถ๋ณ์ฑ state
์ถ๊ฐ: ์ ์์๋ฅผ ๋ฐฐ์ด์ ์ถ๊ฐํ ๋๋ ์คํ๋ ๋ ์ฐ์ฐ์(... )๋ฅผ ์ฌ์ฉํด ๊ธฐ์กด ๋ฐฐ์ด์ ๋ณต์ฌํ๊ณ ์ ์์๋ฅผ ์ถ๊ฐ
const [items, setItems] = useState(['์ฌ๊ณผ', '๋ฐ๋๋']);
const addItem = (item) => {
setItems([...items, item]);
};
์คํ๋ ๋ ์ฐ์ฐ์๋ก ์ด์ ์ items๋ฅผ ์๋ก์ด ๋ฐฐ์ด์ ๋ฃ๊ณ , ์๋ก์ด ์์๋ฅผ ๋ฎ์ด์ฐ๊ธฐ ํ์์ผ๋ก ์๋ก์ด ๋ฐฐ์ด์ ์์ฑํด state๋ฅผ ์ ๋ฐ์ดํธ ํด์ค๋ค.
โจ ์ ๊ฑฐ: ํน์ ์์๋ฅผ ์ ๊ฑฐํ ๋๋ filter ๋ฉ์๋๋ฅผ ์ฌ์ฉํด ํด๋น ์์๋ฅผ ์ ์ธํ ์ ๋ฐฐ์ด์ ์์ฑํด์.
const [items, setItems] = useState(['์ฌ๊ณผ', '๋ฐ๋๋']);
const removeItem = (targetItem) => {
setItems(items.filter(item => item !== targetItem));
};
ํด๋นํ์ง ์๋ item์ ํํฐ๋งํ๋ค.
โจ ์์ : ํน์ ์์๋ฅผ ์์ ํ ๋๋ map ํจ์๋ฅผ ์ฌ์ฉํด ํด๋น ์์๋ง ์ ๋ฐ์ดํธ๋ ์ ๋ฐฐ์ด์ ์์ฑํด์.
const [items, setItems] = useState(['์ฌ๊ณผ', '๋ฐ๋๋']);
const updateItem = (targetItem, newItem) => {
setItems(items.map(item => item === targetItem ? newItem : item));
};
map์ ์ฌ์ฉํ๋ฉด ์๋ก์ด ๋ฐฐ์ด์ ๋ฆฌํดํ๊ธฐ๋๋ฌธ์ ์ด๋ฅผ ์ด์ฉํด ๋ฐฐ์ด์ ์์ ํด์ค๋ค.
<Link/>
๊ธฐ์กด์ aํ๊ทธ๋ Page๋ฅผ ๋คํธ์ํฌ์์ ์๋ก ๋ฐ์์ค๋ ๋ฐฉ์์ ์ฌ์ฉํ๊ณ ์๋ค. ์ฆ html์ aํ๊ทธ๋ก ๊ฒฝ๋ก ์ด๋์๋ง๋ค, ์๋ก ๋ฐ์์ค๊ณ ์๋ค๋๊ฒ์ด๋ค. ๋ฆฌ์กํธ๋ฅผ ์ฌ์ฉํ์ง๋ง ๊ธฐ์กด์ MPA๋ฐฉ์์ธ๊ฒ์ด๋ค.
๐๐ป Linkํ๊ทธ๋ฅผ ์ฌ์ฉํด์ผํ๋ค.
import { Link } from "react-router-dom";
import styled from "styled-components";
<MovieList to={`/movies/${id}`}></MovieList>
// styled-component ์ ์ฉ ๋ด์ฉ
const MovieList = styled(Link)`
padding: 0 20px;
`;
styled-component๋ฅผ ์ฌ์ฉํ ๋ styled(Link)๋ฅผ ์ฌ์ฉํ ๋ ์๋ ๊ฒ ์ฌ์ฉํ๋ฉด ๋๋ค!
๋ผ์ฐํ
โญ๏ธ useEffect์ defendency๋ฅผ ์จ์ค์ผํ๋ ์ด์
useEffect(() => {
fetch(
`https://api.themoviedb.org/3/movie/${movieId}?language=ko-KR`,
options
)
.then((response) => response.json())
.then((movie) => setMovie(movie));
}, [movieId]);
useEffect๋ defendency์ ๋ฃ์ ๊ฐ์ด ๋ฐ๋๋๋ง๋ค ๋ ๋๋ง์ ํด์ฃผ๋ ๋ฐฉ์์ธ๋ฐ,
์๋ฅผ ๋ค์ด, Link๋ก id+1์ธ ์ํ ์์ธํ์ด์ง๋ก ์ด๋ํ๋ค๊ณ ํ์. defendency๊ฐ ๋ง์ฝ ๋น๋ฐฐ์ด์ด์๋ค๋ฉด ๋งํฌ๋ง ๋ฐ๋๋ฟ ๋คํธ์ํฌ ์์ฒญ ์์ฒด๊ฐ ์ด๋ฃจ์ด์ง์ง ์์ ์๋ก์ด ์ ๋ณด๋ ๋ฐ์์ค์ง์๊ณ ๋ ๋๋ง ์์ฒด๊ฐ ๋์ง ์์ ํ๋ฉด์ ๊ทธ๋๋ก์ผ๊ฒ์ด๋ค
์ฆ, ๊ธฐ์กด์ ํ์ด์ง๊ฐ ์ธ๋ง์ดํธ๋๊ณ ์๋ก์ดํ์ด์ง๊ฐ ๋ง์ดํธ๋์ง ์์๊ฒ!
๐๐ป ๊น๋นกํ์ง ๋ง๊ณ defendency๋ฅผ ์ ์์ฑํด์ฃผ์!
๐ API key ๊ด๋ฆฌ
git์ ์ฌ๋ฆฌ๊ฑฐ๋, ํ์ผ์ ๊ณต์ ํ ๋ api key๋ ๋ ธ์ถ์ด๋๋ฉด ์๋๊ธฐ ๋๋ฌธ์ .envํ์ผ์ ์จ๊ฒจ
//.env
REACT_APP_TMDB_ACCESS_TOKEN='API key'
.envํ์ผ์ api๋ฅผ ์จ๊ฒจ๋๋ค
# misc
.DS_Store
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
๊ทธ๋ฆฌ๊ณ ์ด๊ฒ์ .gitignoreํ์ผ์ ๋ฑ๋กํด git์ ์ฌ๋ฆฌ์ง ์๋๋ค!
const options = {
method: "GET",
headers: {
accept: "application/json",
Authorization: `Bearer ${process.env.REACT_APP_TMDB_ACCESS_TOKEN}`,
},
};
fetchํ ๋ ์ด์ ๊ฐ์ด ์์ฑํด์ฃผ๋ฉด๋๋๋ฐ,
process.env
ํ๊ฒฝ ๋ณ์(environment variables)์ ์ ๊ทผํ๋ ๋ฐ ์ฌ์ฉ๋๋ ๊ฐ์ฒด๋ก, ์ด๋ฅผ ์ฌ์ฉํด key๊ฐ์ ์ ์ด์คฌ๋๋ฐ
์ด ์ฝ๋๋ฅผ ๋ณผ ๊ฐ๋ฐ์๋ค์๊ฒ .envํ์ผ์ด ํ์ํ๋จ ์๋ฏธ๋ก .env.template ๋ฐฉ์์ผ๋ก ํ๊ธฐํด์ค๋ค๊ณ ํ๋ค.
API ๊ด๋ฆฌํ์ผ
//movie.api.js
const TMDB_ACCESS_TOKEN = process.env.REACT_APP_TMDB_ACCESS_TOKEN;
const options = {
method: "GET",
headers: {
accept: "application/json",
Authorization: `Bearer ${TMDB_ACCESS_TOKEN}`,
},
};
const getMovies = async (type) => {
const endpoints = {
nowPlaying:
"https://api.themoviedb.org/3/movie/now_playing?language=ko-KR®ion=KR&page=1",
topRated:
"https://api.themoviedb.org/3/movie/top_rated?language=ko-KR®ion=KR&page=1",
};
const response = await fetch(endpoints[type], options);
const data = await response.json();
const movies = data.results;
return movies;
};
const getMovie = async (movieId) => {
const endpoint = `https://api.themoviedb.org/3/movie/${movieId}?language=ko-KR`;
const response = await fetch(endpoint, options);
const data = await response.json();
return data;
};
const moviesAPI = {
getMovies,
getMovie,
};
export default moviesAPI;
fetchํจ์ ์ ์ํด์ ๋ฐ๋ณต์ ์ผ๋ก ํธ์ถํ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ wrapperํจ์๋ก getMovies,getMovie๋ฅผ ์ ์ํ๋ค.
//api.js
import moviesAPI from "./movies/movies.api";
const api = {
movies: moviesAPI,
};
//const api = {
// movies: {getMovies,getMovie}
//};
export default api;
//getMovies ํธ์ถ
useEffect(() => {
Promise.all([
api.movies.getMovies("nowPlaying"),
api.movies.getMovies("topRated"),
]).then(([nowPlaying, topRated]) => setMovies({ nowPlaying, topRated }));
}, []);
//getMovie ํธ์ถ
useEffect(() => {
api.movies.getMovie(movieId).then((movie) => setMovie(movie));
}, [movieId]);
api์ ๊ฐ์ฒด์์ ๊บผ๋ด์ ํจ์๋ฅผ ํธ์ถํ๋ค.
context-API
์ ์ญ์ํ ๊ด๋ฆฌ๋ฅผ ์ํ ๋ฐฉ์ ์ค ํ๋๋ก, context๋ ๋ค๋ฅธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ฒ๋ผ ์ธ๋ถ์ ๋ฐ๋ก store์ ๋ง๋ค์ด ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ ๊ฒ์ด ์๋๋ผ, ํ์ํ ๋งํผ ๋ญํ ์ด๋ก ๊ฐ์ผ๋ค.
โจ ํ์ํ ์ปจํ ์คํธ๋ฅผ ๊ฐ์ธ์ผํ๋ค!
ex) movie, music ๋ฐ๋ก๋ฐ๋ก ๊ฐ์ธ์ ์ฌ์ฉํ๋ค.
src/contexts/auth.context.js ์ด๋ฐ์์ผ๋ก ์ฌ์ฉํ์
1. context ์์ฑ
const AuthContext = createContext(initalValue);
์ด๊ธฐ๊ฐ์ ๋งค๊ฐ๋ณ์๋ก ๋ฃ์ด context๋ฅผ ์์ฑํ๋๋ฐ, ์ด ์ด๊ธฐ๊ฐ์ jsx์์
2. useContext๋ก ์์ฑํ context ์ฌ์ฉ
const { isLoggedIn, logOut } = useContext(AuthContext);
3. context ์ ์ฉ ๋ฒ์ ์ค์
<AuthContext.Provider value={true}>
<Routes>
<Route
element={
<DefaultLayout
isLoggedIn={isLoggedIn}
setIsLoggedIn={setIsLoggedIn}
/>
}
>
<Route path="/" element={<HomePage />} />
<Route
path="/sign-in"
element={
<SignInPage
isLoggedIn={isLoggedIn}
setIsLoggedIn={setIsLoggedIn}
/>
}
/>
<Route path="/movies/:movieId" element={<MoviesDetailPage />} />
</Route>
</Routes>
</AuthContext.Provider>
);
}
export default App;
์ ์ญ์์ ์ฌ์ฉํ๊ธฐ ์ํด ์ต์์์์ Provider๋ก ๊ฐ์ธ๊ณ ์๋ค.
const { createContext, useState, useContext } = require("react");
const initalValue = {
isLoggedIn: false,
setIsLoggedIn: () => {},
};
//์ด๊ธฐ๊ฐ์ ๋งค๊ฐ๋ณ์์
const AuthContext = createContext(initalValue);
//component๋ฅผ ๋ง๋ค์ด์ค๋ค?
//์๋ ์ปดํฌ๋ํธ๋๊น useState๊ฐ๋ฅํจ
export function AuthProvider({ children }) {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const value = {
isLoggedIn,
setIsLoggedIn,
};
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
export const useAuth = () => useContext(AuthContext);
export default AuthContext;
โ๏ธ initalValue๋ ์์ด๋ ๋น์ฅ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ง ์์ง๋ง, ํ์ ์คํฌ๋ฆฝํธ๋ฅผ ์ฌ์ฉํ๋ค๋ฉด ํ์ํ๋ค!
//wrappingํ์ง ์๋๋ค๋ฉด, ์ด๋ ๊ฒ ๊บผ๋ด์ ์ฌ์ฉํด์ผํ๋ค.
const { isLoggedIn, logOut } = useContext(AuthContext);
//wrapping ํ๋ค๋ฉด
const { isLoggedIn, logOut } =useAuth();
useContext๋ฅผ ๊ณ์ ์ฌ์ฉํด์ ํธ์ถํด์ผํ๊ธฐ ๋๋ฌธ์ useAuth๋ก ๋ํํด์ ๊ณ์ ์ฌ์ฉํ๋ค.