2022.07.29(Fri)
[TIL] Day66
[SEB FE] Day67
: ์ง์ DB & ์๋ฒ ๊ตฌ์ถํ ํ์์์ด JSON ํ์ผ์ ์ด์ฉํ์ฌ REST API
์๋ฒ๋ฅผ ๊ตฌ์ถํด์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
โย ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๊ณต๋ถํ๋ค๊ฐ ์๋ฒ๊ฐ ํ์ํ๋ค๋ฉด ์ฌ์ฉํ๊ธฐ!! (์ค์ ํ๋ก์ ํธ์์ ์ฌ์ฉ ์ง์)
# json-server ์ ์ญ ์ค์น
$ npm i -g json-server
# json-server๋ฅผ localhost:3001์ผ๋ก ์ด๊ธฐ
$ cd data
$ json-server --watch data.json --port 3001
App.js
react.lazy()
& suspense
์ฌ์ฉํ์ฌ ์ปดํฌ๋ํธ Refactoring โจ
const CreateBlog = lazy(() => import("./blogComponent/CreateBlog"));
...
return (
<BrowserRouter>
<Suspense fallback={<Loading />} />
<Routes>
<Route path="/create" element={<CreateBlog />} />
...
</Routes>
</BrowserRouter>
)
BlogDetail.js
useParams
์ ์ด์ฉํ์ฌ ๊ฐ๋ณ id๋ฅผ ๋ฐ์์ ๊ฐ๋ณ ๋ธ๋ก๊ทธ์ ๋ด์ฉ์ด ๋ณด์ผ ์ ์๋๋ก โจ
import { useNavigate, useParams } from "react-router-dom";
const { id } = useParams(); // params์ id๋ฅผ ๊ตฌ์กฐ๋ถํดํ ๋น์ผ๋ก ๊ฐ์ ธ์ด!
// 'useFetch' Custom Hook ์ฌ์ฉ
const {
data: blog, // data๋ฅผ blog ๋ณ์๋ก ๋ฐ๊ฟ์ ์ฌ์ฉ
isPending,
error,
} = useFetch(`http://localhost:3001/blogs/${id}`);
const navigate = useNavigate(); // url๋ฅผ ์กฐ์ํ ์ ์๋ ๋ฉ์๋
// ๊ธ ์ญ์ event handler
const handleDeleteClick = () => {
fetch(`http://localhost:3001/blogs/${blog.id}`, {
method: "DELETE",
})
.then(() => {
navigate("/"); // home์ผ๋ก redirect
})
.catch((error) => console.error("Error", error));
};
// ํํธ ํด๋ฆญ event handler
const handleLikeClick = () => {
setIsLike(!isLike); // ํํธ ๋๋ฆ์ true/false ์ํ ๋ณ๊ฒฝ
let result = blog.likes;
/* ํํธ๋ฅผ ๋๋ฅด๋ฉด ์ซ์ +1 */
if (isLike === true) {
result = blog.likes + 1;
} else {
if (blog.likes > 0) {
result = blog.likes - 1;
}
result = blog.likes;
}
// ์์ ํ ๋ฐ์ดํฐ ์์ฑ
let putData = {
id: blog.id,
title: blog.title,
body: blog.body,
author: blog.author,
likes: result,
};
fetch(`http://localhost:3001/blogs/${blog.id}`, {
method: "PUT", // ์์
headers: { "Content-type": "application/json" },
body: JSON.stringify(putData),
})
.then(() => {
navigate(`/blogs/${blog.id}`);
})
.catch((error) => console.error("Error", error));
};
CreateBlog.js
๋ฑ๋ก ๋ฒํผ์ ๋๋ฅด๋ฉด ๊ฒ์๋ฌผ์ด ๋ฑ๋ก์ด ๋๋ฉฐ home์ผ๋ก Redirect๋๋๋ก โจ
fetch
& useNavigate
๋ฅผ ์ด์ฉํ์ฌ handleSubmit
event ์์ฑํ๊ธฐ โจ
const [title, setTitle] = useState("");
const [body, setBody] = useState("");
const [author, setAuthor] = useState("๊น์ฝ๋ฉ");
const handleSubmit = (e) => {
e.preventDefault();
const blog = { title, body, author, likes: 0 };
fetch("http://localhost:3001/blogs/", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(blog),
})
.then(() => {
navigate("/");
});
};
UseFetch.js
GET
๋ฉ์๋๋ฅผ ํตํด ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ค๋ useEffect
hook๋ฅผ custom hook์ผ๋ก ๋ง๋ค์ด ์ฌ์ฌ์ฉํ ์ ์๋๋ก โจ
import { useState, useEffect } from "react";
const useFetch = (url) => {
const [data, setData] = useState(null);
const [isPending, setIsPending] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
setTimeout(() => {
fetch(url)
.then((res) => {
if (!res.ok) {
throw Error("could not fetch the data for that resource");
}
return res.json();
})
.then((data) => {
setIsPending(false);
setData(data);
setError(null);
})
.catch((err) => {
setIsPending(false);
setError(err.message);
});
}, 1000);
}, [url]); // url์ด ๋ณ๊ฒฝ๋ ๋๋ง๋ค ๋ฆฌ๋ ๋๋ง
return { data, isPending, error }; // ๊ฐ์ฒด๋ก ์ํ ๊ฐ ๋ฐํ
};
export default useFetch;
handleSubmit
event handler์body
๋ด์ ๋ฐ์ดํฐ ์์ฑ์id
๋ฅผ ๋ฃ์ด์ ๊ณ์ title, body, author ๊ฐ์ด ์์ฑ์ด ์ ๋๊ฑฐ์๋ค.. id๋ ์๋์ผ๋ก ์์ฑํด์ฃผ๊ธฐ ๋๋ฌธ์ ์ ๋ฃ์ด๋ ๋๊ณ , id๋ฅผ ๋นผ์ฃผ๋๊น ๊ฐ์ด ์ ๋์๋ค! ์ด๊ฑธ๋ก 1์๊ฐ ๋๊ฒ ์ฝ์งํจ,,,๐ซ ๊ทธ๋ฆฌ๊ณheaders
๋ ๋ฃ์ด์ฃผ๋๊ฑฐ ์์ง ๋ง๊ธฐ!!
๐คทโโ๏ธย Q: React
๊ฐ Class
์ปดํฌ๋ํธ โ Function
์ปดํฌ๋ํธ๋ก ์ ์ง์ ์ผ๋ก ๋์ด๊ฐ๊ฒ ๋ ์ด์ ๋ ์ํ๊ฐ์ ์ฌ์ฉํ๊ฑฐ๋ ์ต์ ํํ ์ ์๋ ๊ธฐ๋ค์ด ๋ฏธ์งํ๊ธฐ ๋๋ฌธ์ด๋ค?
๐
โโ๏ธย A: NO
ใดwhy? ์ํ๊ฐ์ ์ฌ์ฉํ๊ฑฐ๋ ์ต์ ํํ ์ ์๋ ๊ธฐ๋ฅ๋ค์ด ๋ฏธ์งํ ๊ฒ์ ์ด๊ธฐ์ ํจ์ ์ปดํฌ๋ํธ! ํด๋์ค ์ปดํฌ๋ํธ๋ componentDidMount()
, componentDidUpdate()
โฆ ๋ ๋๋ง ์์ ์ ๋ฐ๋ผ ์ด๋ป๊ฒ ๋์์ํฌ์ง ์ค์ ํ ์ ์๋ ๋ฉ์๋ ์กด์ฌ!
๐คทโโ๏ธย Q: useMemo
๋ฅผ const result = useMemo(() => calculate(value), [value]);
์ ๊ฐ์ ์ฝ๋๋ก ์์ฑํ ๋, ๋ฐฐ์ด ์์ value
๋ ์ต์
์ด๋ค?
๐
โโ๏ธย A: NO
ใดwhy? useMemo
์ 1๏ธโฃ๋ฒ์งธ ๋งค๊ฐ๋ณ์-์ด๋ป๊ฒ ์ฐ์ฐํ ์ง ์ ์ํ๋ ํจ์, 2๏ธโฃ๋ฒ์งธ ๋งค๊ฐ๋ณ์-deps ๋ฐฐ์ด
โ ๋ฐฐ์ด ์์ ๋ฃ์ ๋ด์ฉ์ด ๋ณ๊ฒฝ๋๋ฉด 1๋ฒ์งธ ๋งค๊ฐ๋ณ์์ ํจ์๋ฅผ ํธ์ถํ์ฌ ๊ฐ์ ์ฐ์ฐ
๐ย 2๋ฒ์งธ ๋งค๊ฐ๋ณ์์ ๋ฐฐ์ด์ ๊ฐ ๋ณ๊ฒฝ์ ์ ๋ฌด๋ฅผ ๊ฐ์งํ๋ ๋ถ๋ถ์ผ๋ก ๋ฐ๋์! ์ธ์ํด์ผ ํ ๊ฐ์ ๋ฃ์ด์ค์ผ ํจ.
7.27(์) ์ ๋ ์ React ํ๋ก์ ํธ ์์์ ๋ค์ด๋ฒ ์ํ API๋ฅผ ์ฌ์ฉํด๋ณด๋ ์ฐ์ต์ ํ๋ค. ๋ฏธ๋ฃจ๋ค๊ฐ ์ด์ ์ผ ๊ธฐ๋กํ๋ ๋์ API ์ฌ์ฉ๊ธฐ ูฉ( แ )ู ์ฒ์ 1์๊ฐ์ ๋๋ API๋ฅผ ์ด๋ป๊ฒ ์ฌ์ฉํด์ผ ํ ์ง ๋ชฐ๋ผ์ ์ฐ์์ข์ ์ฝ๋๋ฅผ ์ง์ง๋ ๋ชปํ๊ฒ ์ด์ ๊ตฌ๊ธ๋งํด์ ์ฌ์ฉ๋ฒ ์ข ์ฐพ์๋ณด๋ค๊ฐ ์ด๊ฒ์ ๊ฒ ์ ์ฉํด๋ณด๊ณ 2์๊ฐ ๋๊ฒ ๋์ ์ด๋ค๊ฐ ๋ฐ์ดํฐ ๋ฐ์์ค๋ ๊ฒ๊น์ง ์ฑ๊ณต!โจ
์ถํ์ ์๊ฐ ์์ ๋ ํ๋ฉด ๊ฒ์์ ์ํ ํฌ์คํฐ๋ ์ํ ์ ๋ณด๊ฐ ์ถ๋ ฅ๋๋๋ก ์ถ๊ฐ ๊ตฌํํด๋ณด๊ธฐ ๐
NAVER Developers
์์ ์ ํ๋ฆฌ์ผ์ด์
๋ฑ๋ก(API ์ด์ฉ์ ์ฒญ)ํ๊ธฐ
Client ID
์ Client Secret
ํ์ธํ๊ธฐ!axios
๋ฅผ ์ฌ์ฉํ์ฌ API
๋ฅผ GET
์์ฒญํ๊ธฐ
import React, { useEffect, useState } from "react";
import axios from "axios";
function Home() {
const [data, setData] = useState([]);
const clientID = "my unique clientID";
const clientSecret = "my unique clientSecret";
const apiData = async () => {
await axios
.get("/v1/search/movie.json", {
params: {
query: "์งฑ๊ตฌ", // ๊ฒ์ํ๊ณ ์ ํ๋ ํค์๋
display: 10, // ๋ช ๊ฐ์ data๋ฅผ ์ถ๋ ฅํ ๊ฑด์ง
},
headers: {
"X-Naver-Client-Id": clientID,
"X-Naver-Client-Secret": clientSecret,
},
})
.then((res) => {
console.log(res.data.items);
setData(res.data.items);
})
.catch((error) => {
console.log(error);
});
};
useEffect(() => {
apiData();
}, []);
return (
<>
<h1>์ํ ๊ฒ์</h1>
</>
);
}
export default Home;
โย ์ฌ๊ธฐ๊น์ง ๊ณผ์ ์ ๋ง์น๊ณ ๋๋๊น CORS
์๋ฌ๊ฐ ๋ฐ์ํ๋ค. console์ error๋ฅผ ํ์ธํด๋ณด๋ฉด url
์ด http://localhost:3000/v1/search/movie.json ์๊ธฐ๋ก GET ์์ฒญ์ด ๊ฐ ๊ฒ์ ํ์ธํ ์ ์๋ค. ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋ฐ๋ก proxy
์ค์ ์ ํด์ค์ผ ํ๋ค.
Proxy
์ค์ ํด์ฃผ๊ธฐ
a. package.json
์์ proxy
์ค์
// package.json
"proxy": "https://openapi.naver.com"
์ฒ์์ ์ด ๋ฐฉ๋ฒ์ ์ ์ฉํด๋ดค๋๋ฐ ์ฌ์ ํ ์๋ฌ๊ฐ ํด๊ฒฐ๋์ง ์์์..ใ
๊ทธ๋์ ๋ค๋ฅธ ๋ฐฉ๋ฒ ์๋..!
b. http-proxy-middleware
๋ชจ๋ ์ค์นํ๊ณ setupProxy.js
ํ์ผ ์์ฑ
# ๋จผ์ ๋ชจ๋์ ์ค์นํ๋ค
$ npm install http-proxy-middleware
// setupProxy.js
const { createProxyMiddleware } = require("http-proxy-middleware");
module.exports = function (app) {
app.use(
createProxyMiddleware("/api", {
target: "https://openapi.naver.com",
changeOrigin: true,
})
);
};
๐ปย ๋์ค์ ๋ฐ์์จ ๋ฐ์ดํฐ๋ฅผ ํ์ฉํด์ ์น ์ฌ์ดํธ์ ์ถ๋ ฅํ๊ณ ๊ฒ์๋๋๋ก ์ถ๊ฐ ๊ตฌํํ๊ธฐ