๐ํ๋ก์ ํธ ์๊ฐ
โ ์๋ฌผ์ ํค์ฐ๊ณ ์ถ์ง๋ง ์ด๋ค ์๋ฌผ์ด ๋์๊ฒ ๋ง๋์ง ๋ชจ๋ฅด๋ ์ฌ๋๋ค!!
โ ์ฌ๋ฌ ์๋ฌผ๋ค์ ํ๊บผ๋ฒ์ ๋น๊ตํด๋ณด๊ณ ๊ณ ๋ฅด๊ณ ์ถ์ ์ฌ๋๋ค!!
์ ์ํด ์ ์ํ๊ฒ ๋ ์น์ด๋ค. ๐ค
โป ํ๋ก ํธ์๊ด๋ จ ๊ธฐ์ ์ด ๋ด๊ฒจ ์๋ ํ๊ณ ๋ก์ ๋๋ค.
๐ํ๋ก์ ํธ ์์!
react๋ก ์ฌ์ฉํ ํ๋ก์ ํธ, next.js๋ ๊ณ ๋ฏผ์ ๋ง์ด ํ๋๋ฐ next.js ๋ react ๊ธฐ๋ฐ์ผ๋ก ์๋ํ๋ค๋ ์ ์ ๊ณ ๋ คํ์ฌ, react ์ ๋ํ ๊ธฐ๋ณธ์ ์ธ ์ดํด๋ฅผ ๋ ๊น๊ฒ ๋ค์ง๊ณ ์ react ๋ฅผ ์ ํํ๊ธฐ๋ก ๊ฒฐ์ ํ๋ค.
๐ง๋ฐฐ์ด๊ฒ
โ ์์กด์ฑ ๋ฐฐ์ด(Dependency Array) ๊ณผ ์กฐ๊ฑด๋ถ ํ ๋น์ ์ด์ฉํ ๊ฒ์ ๊ธฐ๋ฅ
์ฌ์ฉ์๊ฐ ์ ๋ ฅํ ๊ฒ์์ด๋ฅผ ๋ณด๋ด๊ณ
navigate("/search_result", {
state: { plants: filteredPlants, searchTerm: searchTerm },
});
๊ฒ์์ด๋ฅผ ๋ฐ์์ ๋ธ๋ผ์ฐ์ ์ ์ถ์ถ
useEffect(() => {
// location.state์์ ๊ฒ์ ๊ฒฐ๊ณผ์ ๊ฒ์์ด๋ฅผ ๊ฐ์ ธ์ด
const { plants: newPlants, searchTerm: newSearchTerm } = location.state || {
plants: [],
searchTerm: "",
};
setPlants(newPlants);
setSearchTerm(newSearchTerm);
}, [location.state]); // location.state๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค useEffect ์คํ
์ฌ์ฉ์๊ฐ ์ ๋ ฅํ ๊ฒ์์ด๋ฅผ ์์กด์ฑ ๋ฐฐ์ด์ ๋ด์ ๋ฐฐ์ด์ ํฌํจ๋ ๊ฐ์ด ๋ณ๊ฒฝ๋ ๋๋ง๋ค useEffect ๋ด๋ถ์ ์ฝ๋ฐฑ ํจ์๊ฐ ์คํ. ์ฆ, location.state๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค ์ฝ๋ฐฑ ํจ์๊ฐ ์คํ๋์ด ์ํ๋ฅผ ์ ๋ฐ์ดํธ๋๋ค.
์ธ์์!! ์์กด์ฑ ๋ฐฐ์ด์ ์ฌ์ฉ์ ๊ฒ์์ด๋ฅผ ๋ด์ ์ ์๊ตฌ๋!! ์ฒ์์ react๋ฅผ ๋ฐฐ์ธ ๋๋ ์ด๋ฐ ๊ธฐ๋ฅ์ด ์ค์ ์น์ ๊ตฌํํ ๋ ์ด๋ป๊ฒ ์ฐ์ผ์ง ๊ฐ์ด ์์กํ๋ ๋ฐ ํผ์ฆ์ฒ๋ผ ๋ง์ถฐ์ง๋ ๊ธฐ๋ฅ๋ค์ด ์ฐธ ์ฌ๋ฐ๋ ๊ฒ ๊ฐ๋ค.
์ฌ๊ธฐ์ ์ฌ์ฉ์์ ํ์ฌ ๊ฒ์์ด๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๋ ๊ฒ์ useLocation์ ํตํด location.state๋ฅผ ์ฐธ์กฐํ์ฌ ์ด์ ํ์ด์ง์์ navigate ์ด์ฉํด ์ ๋ฌ๋ ๊ฒ์ ๊ฒฐ๊ณผ(newPlants)์ ๊ฒ์์ด(newSearchTerm)๋ฅผ ์ถ์ถํ๋ ๊ฒ์ด๋ค.
โ MSW ๊ธฐ๋ฐ์ผ๋ก ํ Mock API
๋ฌธ์
ํ๋ก ํธ์์ ์ฌ์ฐจ์ ์ฐจ ์๋ฌผ ์ถ์ฒ ์ปดํฌ๋ํธ์ ๊ฒ์์ฐฝ ์ปดํฌ๋ํธ์ ui๋ฅผ ๋ค ๋ง๋ค๊ณ api๋ฅผ ์ฐ๋ํ ์ค๋น๊น์ง ๋ง์ณค์ง๋ง, ์ฌ์ด๋ ํ๋ก์ ํธ๋ก ์ธํด ๋ฐฑ์๋ ๊ฐ๋ฐ์์ ๋ค๋ฅธ ์๊ฐ๋๋ก api๊ฐ ๋์ฌ ๋๊น์ง ๋ง๋ฅ ๊ธฐ๋ค๋ฆด ์๋ ์์๋ค!!
ํด๊ฒฐ
MSW ๊ธฐ๋ฐ์ผ๋ก ํ Mock API๋ฅผ ์ด์ฉํด ๋ฐฑ์๋ API ๊ฐ๋ฐ ์๋ฃ ์ ์๋ ํ๋ก ํธ์๋ ๊ธฐ๋ฅ ๊ฐ๋ฐ ๋ฐ ํ ์คํธ๋ฅผ ์งํํ ์ ์๊ฒ ๋์ด, ์ ์ฒด ํ๋ก์ ํธ์ ๊ฐ๋ฐ ์๊ฐ์ ํจ์จ์ ์ผ๋ก ๋จ์ถํ ์ ์์๋ค.
MSW ๋ฌด์์ธ๊ฐ?
๋ฐฑ์๋ ์์ด๋ ๋คํธ์ํฌ ์์ฒญ์ ๋ชจ๋ฐฉํ์ฌ ํ ์คํธํ๊ณ ๊ฐ๋ฐํ ์ ์๊ฒ ํด์ฃผ๋ ๋๊ตฌ๋ค. MSW๋ ์๋น์ค ์์ปค API๋ฅผ ์ฌ์ฉํ์ฌ ์ค์ ๋คํธ์ํฌ ์์ฒญ์ ๊ฐ๋ก์ฑ๊ณ , ๋ฏธ๋ฆฌ ์ ์๋ ์๋ต์ ๋ฐํํจ์ผ๋ก์จ, ๊ฐ๋ฐ ์ค์ด๊ฑฐ๋ ํ ์คํธ ํ๊ฒฝ์์ ๋ฐฑ์๋ ์๋ฒ ์์ด๋ API ํธ์ถ์ ๋ชจ๋ฐฉํ ์ ์๊ฒ ํด์ค๋ค. ์ด๋ฅผ ํตํด, API ์์กด์ฑ ์์ด ํ๋ก ํธ์๋ ๊ฐ๋ฐ๊ณผ ํ ์คํธ๋ฅผ ์งํํ ์ ์์ผ๋ฉฐ, ๋คํธ์ํฌ ์ํ๋ ์ค๋ฅ ์ฒ๋ฆฌ ๋ฑ์ ์ฝ๊ฒ ์๋ฎฌ๋ ์ด์ ํ ์ ์๋ค.
ํ๋ก์ ํธ์ public ๋๋ ํ ๋ฆฌ์ ์๋น์ค ์์ปค ํ์ผ์ ์ด๊ธฐํํ ํ handlers.js ๋ง๋ค์ด์ API ์์ฒญ์ ๊ฐ๋ก์ฑ๊ณ ๋ชจ๋ฐฉํ ์๋ต์ ์ ์ํ๋ฉฐ ๋๋ค.
// src/mocks/handlers.js
import { http, HttpResponse } from "msw";
export const handlers = [
http.get("https://api.example.com/plants", ({ request }) => {
const query = new URL(request.url).searchParams;
const level = query.get("level");
const type = query.get("type");
const place = query.get("place");
const price = query.get("price");
const plants = [
{
id: 0,
name: "๋ชฌ์คํ
๋ผ",
level: "Beginner",
type: "foliage",
place: "all",
price: "1_won",
},
];
return HttpResponse.json({
plants: filteredPlants,
});
}),
];
์ฒ์์ ์ ๋ฐ์ดํธ ์ด์ ๋ฒ์ ์ ์ฌ์ฉํ๋๋ผ ์ ๋ฅผ ๋จน์์ง๋ง ๊ตฌ๊ธ์์ ์ด๊ฒ์ ๊ฒ ๊ฒ์ํ๋๊น ๊ธ์ธ ์ต์ ๋ฒ์ ์ด ๋์๋ค!
์์ธํ ์ค๋ช -> https://2-doooo-2.tistory.com/169
โ ๊ตฌ๊ธ ๋ก๊ทธ์ธ ๊ตฌํ๊ณผ ์ปค์คํ
๋ฌธ์ 1
์ฒ์ ์ด์ฉํ GoogleLogin์ ๋น ๋ฅด๊ฒ ๋ก๊ทธ์ธ์ ๊ตฌํํ ์ ์์ง๋ง ๋์์ธ ์ปค์คํ ์ด ๋์ง ์์๋ค.
๊ทธ๋์ ์ด๋ฐ ๊ธฐ๋ณธ์ ์ธ ๋์์ธ๋ฐ์ ์ฌ์ฉํ์ง ๋ชปํ๋ค.
ํด๊ฒฐ1
custombutton์ผ๋ก ์ฌ์ฉ๊ฐ๋ฅํ Implicit flow ๋ฐฉ๋ฒ์ผ๋ก ์งํ , useGoogleLogin ์ฌ์ฉํด์ ์ปค์คํ ํ๊ธฐ
const GoogleLoginButton = () => {
const signIn = useGoogleLogin({
onSuccess: async (tokenResponse) => {
const userInfo = await axios
.get("https://www.googleapis.com/oauth2/v3/userinfo", {
headers: { Authorization: `Bearer ${tokenResponse.access_token}` },
})
.then((res) => res.data);
const mypage_userInfo = {
name: userInfo.name,
email: userInfo.email,
};
},
});
return (
<div>
{userName ? (
<p>
๋ก๊ทธ์์
</p>
) : (
<div className="googlg_login_before_wrap" onClick={() => signIn()}>
<p className="googlg_login_text">๋ก๊ทธ์ธ</p>
</div>
)}
</div>
);
};
function Login() {
return (
<>
<GoogleOAuthProvider clientId={clientId}>
<GoogleLoginButton />
</GoogleOAuthProvider>
</>
);
}
export default Login;
์ด ๋ฐฉ๋ฒ์ ์ด์ฉํด ๋ก๊ทธ์ธ ๋ฒํผ์ ์๋ก ๋์์ธํ์ง๋ง ๋ฐ๋ก ์๋ก์ด ๋ฌธ์ ์ ์ง๋ฉดํ๋ค.
<GoogleLogin/>์ ๋ฐ๋ก ์ฌ์ฉ์ ์ ๋ณด๊ฐ ๋ด๊ธด token์ credential์ ๋ด์ ๋ฐ๊ธํด์ฃผ์ง๋ง useGoogleLogin ์ ์ฌ์ฉํ๋ฉด access_token์ ๊ฐ์ง๊ณ ๋ค์ Google์ ์์ฒญํด์ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์์ผ ํ๋ค.
๋ฌธ์ 2
๋๋ ์ง๊ธ ๋ฐฑ์๋ ์์ด mock api๋ฅผ ํตํด ๋ฐ์ดํฐ๋ฅผ ๊ณ์ ๋ฐ์์ค๊ณ ์์๋ค. ๊ทธ๋์ ๊ตฌ๊ธ๋ก๊ทธ์ธ๋ ์ด mock api๋ฅผ ์ฌ์ฉํด์ ๊ตฌํํ๋ฉด ๋ฌธ์ ๊ฐ ์์ ๊ฑฐ๋ผ๊ณ ์๊ฐํ๋ค. ํ์ง๋ง accessํ ํฐ์ด ์๋ฒ์ค ์์ปค api๋ก ๋ณด๋ด์ง์ง ์์๋ค.
ํด๊ฒฐ 2
https://react-oauth.vercel.app/
๋๋ ๋ด ์ํฉ์ ๋ง๋ ์ฝ๋๋ฅผ ์ด ์ฌ์ดํธ์ ๋ค์ด๊ฐ์ ๋ค์ ์ฐพ์๋ค. ์ง๊ธ์ ๋ฐฑ์๋ ์์ฒญ ์์ด ์ ์ ์ ๋ณด์ access token๋ง ํ์ํ๋ ๋์๊ฒ ๋ง๋ ๋ ๊ฐ๋จํ ์ฝ๋๊ฐ ์์๋ค. ๊ทธ๋์ ๊ฐ์ ธ์จ ์ฝ๋๊ฐ
onSuccess: async (tokenResponse) => {
const userInfo = await axios
.get("https://www.googleapis.com/oauth2/v3/userinfo", {
headers: { Authorization: `Bearer ${tokenResponse.access_token}` },
})
.then((res) => res.data);
const mypage_userInfo = {
name: userInfo.name,
email: userInfo.email,
};
},
๋ฐฑ์๋๋ก ๋ณด๋ด์ง ์๊ณ ๊ตฌ๊ธ์ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ์์ฒญํด ๋ฐ์์๋ค. ์ด๋, ์์ฒญ ํค๋์ Authorization ์์ฑ์ ์ค์ ํ์ฌ, ์์ ์ป์ access_token์ ์ฌ์ฉํ์ฌ ์์ฒญ์ ์ธ์ฆํ๋ค. "Bearer ${tokenResponse.access_token}" ๊ตฌ๋ฌธ์ Bearer ํ ํฐ ์ธ์ฆ ์คํค๋ง๋ฅผ ์ฌ์ฉํ์ฌ ์ก์ธ์ค ํ ํฐ์ ์ ๋ฌํ ๊ฒ์ด๋ค.
โ .env ์ด์ฉํ ๋ณด์ ์ค์ ํ ๋ฐฐํฌ
๋ฌธ์
API ํค๋ ๋น๋ฐ๋ฒํธ์ ๊ฐ์ ๋ฏผ๊ฐํ ์ ๋ณด๋ ์ง์ ์ฝ์ ํ์ง ์๊ณ process.env.REACT_APP_API_KEY๋ฅผ ํตํด API ํค๋ฅผ ํ๊ฒฝ ๋ณ์์์ ๋ถ๋ฌ์ค๋ ๋ฐฉ์์ ์ฌ์ฉํ๋ค. ํ์ง๋ง ์ด์ ๊ฐ์ ์ค์ ํ vercel๋ก ๋ฐฐํฌ๋ฅผ ํ๊ณ ๋๋ ์ค๋ฅ๊ฐ ๋ฌ๋ค. + react์ react-google-login ๋ฒ์ ์ด ๋ค๋ฅธ ๊ฒ๋ ํ ๋ชซํ๋ค.
ํด๊ฒฐ
์ฒซ๋ฒ์งธ๋ก vercel์์ ํ๊ฒฝ ๋ณ์๋ฅผ ์ค์ ํ ํ๋ก์ ํธ๋ก ์ด๋ํ ํ Settings ๋ฉ๋ด์์ Environment Variable ํ์ด์ง๋ก ์ด๋ํ๋ค.
์ฌ๊ธฐ์ ๋ด๊ฐ ์ค์ ํ ํค์ ๊ฐ์ ๋ฃ์ด์ฃผ๋ฉด ๋๋ค.
๋๋ฒ์งธ๋ก react๊ฐ ๋ค์ด๊ทธ๋ ์ด๋๊ฐ ์๋ผ์ react-google-login ๋ฒ์ ์ญ์ ํ ์๋ ๋ช ๋ น์ด๋ฅผ ํตํด ์ฌ์ค์นํด์คฌ๋ค.
npm install @leecheuk/react-google-login
๋ ์์ธํ ๋ณด๊ธฐ -> https://2-doooo-2.tistory.com/171
โ ๋ง์ดํ์ด์ง ์์ฑ
์ฒ์์ผ๋ก ์ฌ์ฉํด๋ณธ useAuth ์ปค์คํ ํ
useAuth ์ปค์คํ ํ ์ React ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ธ์ฆ ์ํ๋ฅผ ์ ์ญ์ ์ผ๋ก ๊ด๋ฆฌํ๋ ๊ฐ๋ ฅํ ๋ฐฉ๋ฒ์ ์ ๊ณตํ๋ค. ์ด๋ฅผ ํตํด ๊ฐ๋ฐ์๋ ์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด๋ฅผ ์ฝ๊ฒ ์ ๊ทผํ๊ณ , ์ด ์ ๋ณด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ฌ์ฉ์ ๊ฒฝํ์ ๊ฐ์ธํํ๋ฉฐ, ๋ณด์์ ๊ฐํํ ์ ์๋ค.
์ด๋ค ์ปดํฌ๋ํธ์์๋ ์ง ํ์ฌ ๋ก๊ทธ์ธํ ์ฌ์ฉ์์ ์ ๋ณด์ ์ ๊ทผํ ์ ์์ผ๋ฉฐ, ์ด๋ฅผ ํตํด ๋ณด๋ค ๋์ ์ด๊ณ ์ฌ์ฉ์ ์นํ์ ์ธ UI๋ฅผ ๊ตฌ์ฑํ ์ ์๊ฒ ๋๋ค.
๋ฌธ์
ํ์ง๋ง ๋ฌธ์ ๋ useAuth์์ ๋ํ๋์ง ์์๋ค. ๋ฌธ์ ๋ ์ฌ์ฉ์ ํ๋กํ์ ๋ก์ปฌ์คํ ๋ฆฌ์ง์ ์ ์ฅํ์์๋ ์๋ก ๊ณ ์นจ ์ ์ฌ์ฉ์ ํ๋กํ ์ฌ์ง์ด ์ ์ฅ๋์ง ์๋๋ค๋ ๊ฒ์ด์๋ค.
ํด๊ฒฐ
const handlePhotoChange = (event) => {
const file = event.target.files[0];
const reader = new FileReader();
reader.onloadend = () => {
const base64String = reader.result;
setPhoto(base64String);
localStorage.setItem("userPhoto", base64String);
};
if (file) {
reader.readAsDataURL(file);
}
};
localStorage์ ์ ์ฅ๋ ์ฌ์ง์ URL์ด ์ธ์ ๊ธฐ๋ฐ URL์ด๊ธฐ ๋๋ฌธ์ ๋ฐ์ํ ์๋ฌ์ด๋ค. ์ฆ, ํ์ด์ง๋ฅผ ์๋ก๊ณ ์นจํ๋ฉด ์ด URL์ ๋ ์ด์ ์ ํจํ์ง ์๊ฒ ๋์ด ์ด๋ฏธ์ง๊ฐ ์ฌ๋ผ์ง๊ฒ ๋๋ค.
์ ํ๋ ํ์ผ(event.target.files[0])์ FileReader ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ์ฌ ์ฝ์ ํ, ํ์ผ์ ๋ด์ฉ์ Base64 ์ธ์ฝ๋ฉ๋ ๋ฌธ์์ด๋ก ๋ณํํ ํ ์๋ฌ๊ฐ ํด๊ฒฐ๋๋ค.
ํ์ง๋ง ๋๋ถ๋ถ์ ๋ธ๋ผ์ฐ์ ์์ ๋ก์ปฌ ์คํ ๋ฆฌ์ง์ ์ฉ๋์ ์ฝ 5MB๋ก ์ ํ๋์ด ์๋ค. Base64 ์ธ์ฝ๋ฉ์ ์๋ณธ ๋ฐ์ดํฐ๋ณด๋ค ์ฝ 33% ๋ ํฌ๊ธฐ ๋๋ฌธ์, ๊ณ ํ์ง์ ์ฌ์ง์ด๋ ์ฌ๋ฌ ์ฌ์ง์ ์ ์ฅํ๊ธฐ์๋ ๋ถ์ ํฉํ ์ ์๋ค. ๊ทธ๋ฌ๋ ๋ง๋๋ ํ๋ก์ ํธ๋ฅผ ๊ณ ๋ คํด์ ์ฌ์ฉํด์ผ ํ๋ค.
โ recoil์ ์ด์ฉํ ์ฅ๋ฐ๊ตฌ๋
React์ ์ฅ์ ์ค์ ์ฅ์ , ์ฌ๋ฌ ์ปดํฌ๋ํธ ๊ฐ์ ์ํ๋ฅผ ๊ณต์ ํ๊ธฐ ์์ฃผ ์ ํฉํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๊ณ ๊ทธ๊ฒ์ ์ํด ์กด์ฌํ๋๊ฒ Recoil์ด๋ค. Recoil์ ์ฌ์ฉํด ๊ฐ๋จํ๊ฒ ์ํ๋ฅผ ์ ์ญ์ ์ผ๋ก ๊ด๋ฆฌํ ์ ์์๊ณ ์ปดํฌ๋ํธ ์ด๋ ๊ณณ์ด๋ ์ฅ๋ฐ๊ตฌ๋์ ์ํ๋ฅผ ์กฐํํ๊ณ ์์ ํ ์ ์๋ค.
import { useRecoilState } from "recoil";
import { cartState } from "./atoms";
import { useAuth } from "../context/AuthContext";
const useAddToCart = () => {
const [cart, setCart] = useRecoilState(cartState);
const { currentUser } = useAuth();
const handleAddToCart = (plantId, name, image, type, level) => {
if (!currentUser) {
alert("๋ก๊ทธ์ธ์ด ํ์ํ ๊ธฐ๋ฅ์
๋๋ค.");
return;
}
const isItemInCart = cart.some((item) => item.plantId === plantId);
if (isItemInCart) {
alert(`${name}์(๋) ์ด๋ฏธ ์ฅ๋ฐ๊ตฌ๋์ ๋ด๊ฒจ ์์ต๋๋ค.`);
return;
}
const newItem = { plantId, name, image, type, level };
setCart([...cart, newItem]);
window.confirm(`์ฅ๋ฐ๊ตฌ๋์ ${name}์ ๋ด๊ฒ ์ต๋๊น?`);
};
return handleAddToCart;
};
export default useAddToCart;
cart์์๋ ์ญ์ useAuthํ ์ ์ฌ์ฉํด ๋ก๊ทธ์ธ ์ํ์ผ๋๋ง ์ฅ๋ฐ๊ตฌ๋๋ฅผ ์ฌ์ฉํ ์ ์๋๋ก ์ ์ํด ๋น์ฆ๋์ค ๋ก์ง์ ์ ์ฉ์์ผฐ๋ค.
โ๋ฒ์ธ
๊ทธ๋ฅ ๊ฐ์๊ธฐ useAuth๋ ์ ์ญ์ผ๋ก ์ํ๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ํจ์ด๊ณ recoil๋ ์ ์ญ์ผ๋ก ์ํ๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ํ ๊ฑด๋ฐ ๊ทธ๋ฌ๋ฉด ์ฅ๋ฐ๊ตฌ๋๊ธฐ๋ฅ๋ useAuth๋ฅผ ์ด์ฉํ์ง ๋ชปํ๋์ง ๊ถ๊ธํด์ก๋ค. ๊ฒฐ๋ก ์ ๋ถ์ ํฉํ๋ค
Recoil
์ ์: Recoil์ React ์ ํ๋ฆฌ์ผ์ด์ ์ ์ํ ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค. ์ ์ญ ์ํ ๊ด๋ฆฌ๋ฅผ ์ฝ๊ฒ ํ ์ ์๋๋ก ์ค๊ณ๋์์ผ๋ฉฐ, ์ปดํฌ๋ํธ ๊ฐ์ ์ํ ๊ณต์ ๋ฅผ ๊ฐ๋จํ๊ฒ ๋ง๋ค์ด ์ค๋๋ค.
๋ชฉ์ : Recoil์ ์ ํ๋ฆฌ์ผ์ด์ ๋ด์์ ์ฌ๋ฌ ์ปดํฌ๋ํธ ๊ฐ์ ์ํ๋ฅผ ๊ณต์ ํ๊ณ ๊ด๋ฆฌํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค. ๋ณต์กํ ์ํ ๋ก์ง์ ์ฝ๊ฒ ๊ด๋ฆฌํ ์ ์๋๋ก ๋์ต๋๋ค.
๊ธฐ๋ฅ: ์ํ(atom) ์์ฑ, ์ํ ์ ๋ฐ์ดํธ, ํ์ ์ํ(selector) ๊ด๋ฆฌ ๋ฑ ์ ์ญ ์ํ ๊ด๋ฆฌ์ ํ์ํ ๋ค์ํ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
์ฌ์ฉ ์์: ์ฅ๋ฐ๊ตฌ๋ ํญ๋ชฉ ๊ด๋ฆฌ, ์ฌ์ฉ์ ์ค์ ๊ด๋ฆฌ, ์ ํ๋ฆฌ์ผ์ด์ ์ ํ ๋ง ์ํ ๊ด๋ฆฌ ๋ฑ ๋ค์ํ ์ ์ญ ์ํ๋ฅผ ๊ด๋ฆฌํ๋ ๋ฐ ์ฌ์ฉํ ์ ์์ต๋๋ค.
useAuth
์ ์: useAuth๋ ํน์ ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ API๊ฐ ์๋, ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉ์ ์ธ์ฆ ์ํ๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ํด ๋ง๋ค์ด์ง ์ปค์คํ ํ ์ ์์ ์ด๋ฆ์ ๋๋ค. ์ด ํ ์ React ์ปจํ ์คํธ(Context)์ ๊ฐ์ ๋ฉ์ปค๋์ฆ์ ํตํด ๊ตฌํ๋ ์ ์์ผ๋ฉฐ, ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฐ์ ๊ฑธ์ณ ์ฌ์ฉ์์ ๋ก๊ทธ์ธ ์ํ๋ฅผ ๊ด๋ฆฌํ๊ณ ์ ๊ทผํ ์ ์๊ฒ ํด์ค๋๋ค.
๋ชฉ์ : ์ฌ์ฉ์์ ๋ก๊ทธ์ธ ์ํ, ์ธ์ฆ ํ ํฐ, ์ฌ์ฉ์ ์ ๋ณด ๋ฑ ์ธ์ฆ๊ณผ ๊ด๋ จ๋ ๋ฐ์ดํฐ๋ฅผ ๊ด๋ฆฌํ๊ณ , ์ด๋ฅผ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ค๋ฅธ ๋ถ๋ถ์์ ์ฝ๊ฒ ์ ๊ทผํ ์ ์๋๋ก ํ๋ ๊ฒ์ ๋๋ค.
๊ธฐ๋ฅ: ๋ก๊ทธ์ธ, ๋ก๊ทธ์์, ์ฌ์ฉ์ ์ธ์ ํ์ธ, ์ฌ์ฉ์ ์ ๋ณด ์ ๋ฐ์ดํธ ๋ฑ ์ธ์ฆ๊ณผ ๊ด๋ จ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
์ฌ์ฉ ์์: ๋ก๊ทธ์ธ ํ์ด์ง, ๋ณดํธ๋ ๋ผ์ฐํธ ์ ๊ทผ ์ ์ด, ์ฌ์ฉ์ ์ ๋ณด ํ์ ๋ฑ ์ฌ์ฉ์ ์ธ์ฆ์ด ํ์ํ ๊ธฐ๋ฅ์ ๊ตฌํํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
Recoil์ ์ผ๋ฐ์ ์ธ ์ํ ๊ด๋ฆฌ๋ฅผ ์ํ ๋๊ตฌ์ด๋ฉฐ, useAuth๋ ์ธ์ฆ ์ํ ๊ด๋ฆฌ๋ฅผ ์ํ ํน์ ๋ชฉ์ ์ ์ปค์คํ ํ ์ด๋ค. Recoil์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ค์ํ ๋ฐ์ดํฐ๋ฅผ ๊ด๋ฆฌํ๋ ๋ฐ ์ฌ์ฉ๋๊ณ , useAuth๋ ์ฃผ๋ก ์ฌ์ฉ์ ์ธ์ฆ๊ณผ ๊ด๋ จ๋ ๋ฐ์ดํฐ๋ฅผ ๊ด๋ฆฌํ๋ ๋ฐ ์ฌ์ฉ๋๋ค. ์ฅ๋ฐ๊ตฌ๋ ๊ธฐ๋ฅ์ ๊ตฌํํ๋ ๋ฐ๋ ์ฌ๋ฌ ์ปดํฌ๋ํธ์์ ์ ๊ทผํ๊ณ ์์ ํ ํ์๊ฐ ์๊ธฐ ๋๋ฌธ์ Recoil๊ณผ ๊ฐ์ ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ ํฉํ๋ค!!
๐์์ฑํ ํ๋ก์ ํธ
https://starting-plant-nu.vercel.app/
Starting Plant
starting-plant-nu.vercel.app
๐ซจ๋๋์
์ฝ๋๋ฅผ ๊ตฌํํ๋ ๊ณผ์ ๋ณด๋ค ํ๊ณ ํ๋ ๊ณผ์ ์์ ์ ๋ง๋ค. ์ด๋ฌ์ง ํ๋ฉด์ ๋ ๋ง์ ํ์ต์ด ๋๋ ๊ฒ ๊ฐ๋ค. ๋ค์ ํ๋ฒ ๋ณต์ต์ ์ค์์ฑ์ ๊นจ๋ซ๊ฒ ๋๋ ์๊ฐ์ด์๋ค.
์ง๊ธ ํ๋ก์ ํธ๋ ๋ง์กฑ์ค๋ฝ์ง๋ง ์ฌ๊ธฐ์ ๋ ๊ณ ๋ํ ํ๋ ์์ ์ ํ๊ณ ์ถ์ ์์ฌ์ด ๋๋ค!. ๊ณ์ ๋ฆฌํฉํ ๋ง ํด๋ณด๋ฉด์ ํด๋ฆฐ์ฝ๋๋ฅผ ์์ฑํ ์ ์๋๋ก ๋ ๋ ธ๋ ฅํด์ผ๊ฒ ๋ค.
๊ทธ๋ฆฌ๊ณ ๋ฐฑ์๋ ๊ฐ๋ฐ์์ ๋ ๋ง์ ์ํต์ผ๋ก ์ง๊ธ์ ๋น๋ก mock api๋ฅผ ์ฌ์ฉํ์ง๋ง ๋์ค์๋ ์ค์ ๋ฐ์ดํฐ ๋ฒ ์ด์ค๋ ์ฐ๊ฒฐํด์ ์์ ์ ๋๋ง์ณ์ผ๊ฒ ๋ค.
...
์๊ฐ์ด ํ๋ฅด๊ณ ๋ฐฑ์๋ ๊ฐ๋ฐ์์ api ์ฐ๊ฒฐํ๋ ์์ ์ ํตํด ๋ฐ์ดํฐ ์ฐ๊ฒฐ์ ์ฑ๊ณตํ๋ค!!
ํ์ง๋ง ์๋ฒ๋ฅผ ์ผ๋์ผ ์๋์ด ๊ฐ๋ฅํด์ ๋ฐ๋ก ๊ทธ ์ฝ๋๋ฅผ ๋ฐฐํฌํ์ง ์์๋ค. ๋๋ฆ cors์๋ฌ ์์ด ๋ฌด์ฌํ ๋์ด๊ฐ ๋คํ์ด๋ค