props drilling ๋ฐฉ์์ผ๋ก ๊ตฌํํ ์ฝ๋๋ฅผ Context ๋ฅผ ์ฌ์ฉํ์ฌ ๋ฆฌํฉํ ๋งํด ๋ณด๋ ค ํ๋ค.
Context API ์ฌ์ฉ ์ ๊ตฌ์กฐ
์๋ ์ด๋ฏธ์ง๋ ๋ฆฌํฉํ ๋ง ์ props drilling ๋ฐฉ์์ผ๋ก ๊ตฌํํ ์ปดํฌ๋ํธ ๊ตฌ์กฐ์ด๋ค.
Dashboard ์์ ์ฌ์ฉํ์ง ์๋ removedPokemon ์ PokemonCard ๋ฅผ ์ํด์ props ๋ก ์ ๋ฌํด ์ฃผ๊ณ ์๋ค. ๋ํ PokemonList ์์ ์ฌ์ฉํ์ง ์๋ addPokemon ์ props ๋ก ์ ๋ฌํด ์ฃผ๊ณ ์๋ค.
์ด ํ๋ก์ ํธ๋ ๊ฐ๋จํ ๊ตฌ์กฐ๋ก ๋์ด์์ด์ ์ถฉ๋ถํ props drilling ๋ฐฉ์์ผ๋ก ํ์ํ ์ ๋ณด๋ฅผ ์ ๋ฌํด ์ค๋ ๊ด์ฐฎ๊ฒ ์ง๋ง ๋ง์ฝ ์ปดํฌ๋ํธ๊ฐ ๋ช๋ฐฑ ๊ฐ ๋๋ฉด props ๋ฅผ ์ ๋ฌ ์ ๋ฌ ์ ๋ฌ.... ํด์ผ๋ ๊ฒ์ด๋ค.
์ด๋ฅผ ํด์ํ๊ธฐ ์ํด Context ๋ฅผ ์ฌ์ฉํด ๋ณด์๋ค. Context ๋ ๋ถ๋ชจ ์ปดํฌ๋ํธ๊ฐ ์์ ์ปดํฌ๋ํธ์๊ฒ props ๋ฅผ ์ ๋ฌํด ์ฃผ์ง ์์๋ ์ ๋ณด๋ฅผ ๊ฐ์ ธ๋ค ์ฌ์ฉํ ์ ์๋ค! ๐ฎ
Context API ์ฌ์ฉํด์ ๋ฆฌํฉํ ๋ง
1. context ์์ฑ
import React, { createContext, useContext, useState } from 'react';
const PokemonContext = createContext();
export function usePokemonContext() {
return useContext(PokemonContext);
}
export function PokemonProvider({ children }) {
const [selectedPokemon, setSelectedPokemon] = useState([]);
const addPokemon = (newPokemon) => {
setSelectedPokemon((prev) => {
return [...prev, newPokemon];
});
};
const removePokemon = (pokemon) => {
setSelectedPokemon((prev) => prev.filter((p) => p.id !== pokemon.id));
};
return (
<PokemonContext.Provider value={{ selectedPokemon, addPokemon, removePokemon }}>
{children}
</PokemonContext.Provider>
);
}
const PokemonContext = createContext();
createContext() ๋ฅผ ์ฌ์ฉํด์ PokemonContext ๋ผ๋ ์ด๋ฆ์ ์ปจํ ์คํธ๋ฅผ ์์ฑํ๋ค. ์ด ์ปจํ ์คํธ๋ ํ๋ก์ ํธ ์ ์ฒด์์ ์ํ๋ฅผ ๊ณต์ ํ ์ ์๋๋ก ํ๋ค.
export function usePokemonContext() { return useContext(PokemonContext); }
useContext ํ ์ ์ฌ์ฉํ์ฌ ์ปจํ ์คํธ์ ์ฝ๊ฒ ์ ๊ทผํ ์ ์๋๋ก ์ปค์คํ ํ ์ ๋ง๋ค์๋ค. ์ด ํจ์๋ ์ปจํ ์คํธ ๊ฐ์ ๋ฐํํ๋ฉฐ, ์ด๋์๋ ์ด ํ ์ ํธ์ถํ์ฌ ํฌ์ผ๋ชฌ์ ์ถ๊ฐํ๊ฑฐ๋ ์ ๊ฑฐํ๋ ํจ์๋ฅผ ๊ฐ์ ธ๋ค ์ฌ์ฉํ ์ ์๋ค.
export function PokemonProvider({ children }) {...}
PokemonProvider ์ปดํฌ๋ํธ๋ ์ปจํ ์คํธ ๊ณต๊ธ์ ์ญํ ์ ํ๋ค. ์ด ์ปดํฌ๋ํธ ๋ด๋ถ์๋ ํด useState ํ ์ ์ฌ์ฉํ์ฌ ์ ์ํ ์ํ์ addPokemon, removedPokemon ํจ์๋ฅผ ์ ์ํด๋์๋ค.
return (
<PokemonContext.Provider value={{ selectedPokemon, addPokemon, removePokemon }}>
{children}
</PokemonContext.Provider>
);
}
PokemonContext.Provider ๋ฅผ ์ฌ์ฉํด์ ์์ ์ปดํฌ๋ํธ๋ค์๊ฒ value props ๋ก ์ํ์ ์ํ๋ฅผ ์กฐ์ํ ์ ์๋ ํจ์๋ฅผ ์ ๋ฌํ๋ค. ์ ๋ฌ๋ ๊ฐ๋ค์ ์์ ์ปดํฌ๋ํธ์ ์ด๋์๋ ๊ฐ์ ธ๋ค ์ฌ์ฉํ ์ ์๋ค.
2. ๋ถ๋ชจ ์ปดํฌ๋ํธ์ ์ปจํ ์คํธ ์ฌ์ฉ
import React from "react";
import Dashboard from "../components/Dashboard";
import PokemonList from "../components/PokemonList";
import MOCK_DATA from "../mock.js";
import { PokemonProvider } from "../context/PokemonContext.js";
const Dex = () => {
return (
<PokemonProvider>
<Dashboard />
<PokemonList pokemonList={MOCK_DATA} />
</PokemonProvider>
);
};
export default Dex;
์ปจํ ์คํธ๋ฅผ ๋ง๋ค์์ผ๋ฉด ๋ถ๋ชจ ์ปดํฌ๋ํธ์ PokemonProvider ๋ก ์์ ์ปดํฌ๋ํธ๋ฅผ ๊ฐ์ธ์ฃผ๋ฉด ํ์์ ๋ชจ๋ ์์ ์ปดํฌ๋ํธ์์ ๊ฐ์ ์ฌ์ฉํ ์ ์๋ค.
3. ์ฌ์ฉ ์์
const PokemonCard = ({ pokemon, isSelected }) => {
const { addPokemon, removePokemon } = usePokemonContext();
...
}
๋ค์๊ณผ ๊ฐ์ด ๊ตฌ์กฐ๋ถํดํ ๋น์ผ๋ก PokemonProvider ์ปดํฌ๋ํธ์ ์ ์ํ ํจ์๋ค์ ์ฌ์ฉํ ์ ์๋ค.
์๋ ์ด๋ฏธ์ง๋ ๋ฆฌํฉํ ๋ง ํ ๊ตฌ์กฐ๋ฅผ ํํํด๋ณธ ๊ฒ์ด๋ค.
ํ์ํ ์ ๋ณด๋ฅผ props ๋ก ์์์๊ฒ ์ ๋ฌํด ์ฃผ์ง ์์๋ ์์์ context ์์ ๋ฐ๋ก ๊ฐ์ ธ๋ค๊ฐ ์ธ ์ ์๋ค๋ ๊ฒ์ ํํํด ๋ณด์๋ค. ๐
PokemonDetail ์๋ Context ์ฌ์ฉํ๊ธฐ
PokemonDetail ์ '์ถ๊ฐ' ๋ฒํผ์ ๋ฃ์ด์ ํด๋น ํ์ด์ง์์ ๋ฒํผ์ ๋๋ ์ ๋ ์ ํ๋ ํฌ์ผ๋ชฌ์ผ๋ก ํ๊ธฐ ์ํ ์์ ์ ์ถ๊ฐ๋ก ์งํํ๋ค.
์๋๋ Detail ํ์ด์ง์ context ๋ฅผ ์ ์ฉํด ๋ณธ ๊ฒ์ด๋ค. (์ค๋ฅ ๋จ..)
import React from "react";
import styled from "styled-components";
import { useNavigate, useParams } from "react-router-dom";
import { DEX_URL } from "../utils/path.js";
import MOCK_DATA from "../mock.js";
import { usePokemonContext } from "../context/PokemonContext.jsx";
import { PokemonProvider } from "../context/PokemonContext.jsx";
const PokemonDetail = () => {
const navigate = useNavigate();
const pokemonId = useParams().id;
const { addPokemon } = usePokemonContext();
const pokemon = MOCK_DATA.find((p) => {
return p.id === Number(pokemonId);
});
return (
<PokemonProvider>
<StyledSection>
<img src={pokemon.img_url} />
<span>{pokemon.korean_name}</span>
<span>{pokemon.types.join(", ")}</span>
<p>{pokemon.description}</p>
<button
onClick={() => {
console.log("์์ธํ์ด์ง์์ ํฌ์ผ๋ชฌ ์ถ๊ฐ");
}}
>
์ถ๊ฐํ๊ธฐ
</button>
<button
onClick={() => {
navigate(DEX_URL);
}}
>
๋ค๋ก๊ฐ๊ธฐ
</button>
</StyledSection>
</PokemonProvider>
);
};
export default PokemonDetail;
Dex ์์ ํ๋ ๊ฒ ์ฒ๋ผ PokemonProvider ๋ก ๊ฐ์ธ๊ณ usePokemonContext() ๋ก ํ์ํ ๊ฒ ๊บผ๋ด์ ์ฐ๋ฉด ๋๊ฒ ๋ค! ๋ผ๊ณ ์๊ฐํ๋๋ฐ.. ์ค๋ฅ๊ฐ ๋ปฅ๋ปฅ ๋ฌ๋ค. ๊ทธ ์ด์ ๋.. ์ฐธ ์ด์ด์๋ ์ด์ ์ธ๋ฐ..
๋ํ ์ผ ํ์ด์ง๋ ํ ๊ฐ์ ์ปดํฌ๋ํธ๋ก ๊ตฌ์ฑ๋์ด ์๋ค. ๋๋ context ๋ฅผ ๊ทธ PokemonDetail ์ปดํฌ๋ํธ์ return ์ชฝ์ ๊ฐ์๊ณ , PokemonDetail ์ปดํฌ๋ํธ์์ usePokemonContext() ๋ฅผ ์ฌ์ฉํ๊ณ ์์๋ค. ๐ ์ฆ, PokemonContext.Provider ๋ก ๊ฐ์ธ์ง ์ปดํฌ๋ํธ ๊ณ์ธต ๊ตฌ์กฐ ๋ด์์ ํธ์ถํ์ง ์์ ๊ฒ!
์ฒซ ๋ฒ์งธ ์๊ฐํ ํด๊ฒฐ์ฑ ์ผ๋ก PokemonDetail ์ ์์ ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค์ด์ ๊ทธ ์์ ์ปดํฌ๋ํธ์์ context ๋ฅผ ์ฌ์ฉํ๋๋ก ํ๋ฉด ๋๊ฒ ๋ค! ๋ผ๊ณ ์๊ฐํ๋๋ฐ, ๊ณต๋ถํด ๋ณธ ๊ฒฐ๊ณผ ๋ง์ฝ ์ด ๋ฐฉ๋ฒ๋๋ก PokemonContext ๋ฅผ ์ฌ์ฉํ๋ค๋ฉด .. Dex ํ์ด์ง์ PokemonDetail ํ์ด์ง๊ฐ ์๋ก ๋ค๋ฅธ ์ํ๋ฅผ ๊ฐ์ง๊ฒ ๋ ์ ์๋ค๊ณ ํ๋ค. ์๋๋ฉด.. PokemonProvider ๊ฐ ๋ณ๋์ ์ํ๋ฅผ ๊ด๋ฆฌํ๊ธฐ ๋๋ฌธ!
๋ ๋ฒ์งธ๋ก ์๊ฐํ ํด๊ฒฐ์ฑ ์ Dex ์ PokemonDetail ๋ฅผ ๊ฐ์ด Provider ๋ก ๊ฐ์ธ๋ ๊ฒ์ด๋ค. ๊ทธ๋ฌ๋ฉด ๋ ๊ฐ๋ฅผ ๊ณตํต์ผ๋ก ๊ฐ์ง๊ณ ์๋ ์ปดํฌ๋ํธ๋ฅผ ์ฐพ์์ PokemonProvider ๋ก ๊ฐ์ธ๋ฉด ๋๋ค!
App ์ปดํฌ๋ํธ๋ฅผ PokemonProvider ๋ก ๊ฐ์ธ์ฃผ๋ฉด ๋ ๐ฅ
import "./App.css";
import { PokemonProvider } from "./context/PokemonContext";
import Router from "./shared/Router";
function App() {
return (
<PokemonProvider>
<Router />
</PokemonProvider>
);
}
export default App;
๋ฌธ์ ๊ฐ ์๊ธด ๊ตฌ์กฐ
๋ฌธ์ ํด๊ฒฐ ํ ๊ตฌ์กฐ
์ด์ PokemonDetail ์์ PokemonContext ๋ฅผ ์ฌ์ฉํ ์ ์๊ฒ ๋์๊ณ Dex ์ PolemonDetail ๋ ๊ฐ์ ์ปดํฌ๋ํธ์์ ๋์ผํ ์ํ๋ฅผ ๊ณต์ ํ๊ฒ ๋๋ฏ๋ก, Dex ์์ ์ํ๊ฐ ๋ณ๊ฒฝ๋๋ฉด PokemonDetail ์๋ ์ํ๊ฐ ๋ฐ์๋๊ณ ๊ทธ ๋ฐ๋๋ ๊ฐ๋ฅํ๊ฒ ๋์๋ค!!