- ๊ฐ์ : ๋งค์ฃผ 1ํ์ฉ ํ ๋ฌ๊ฐ ์งํ๋๋ React ์คํฐ๋
- ๊ธฐ๊ฐ : 9์ 7์ผ ~ 9์ 27์ผ (์ด 4ํ)
$npm run dev
$npm run db
- ๋ธ๋ผ์ฐ์ ๋ ๋๋ง ์๋ฆฌ
- ์์ฆ์ ์ ์ ์ด์ฟผ๋ฆฌ๋ฅผ ์์ธ๊น ?
- node & npm
- ์ฑ๊ธ ํ์ด์ง ์ดํ๋ฆฌ์ผ์ด์ (SPA)์ด๋?
- ๋ฐ๋ฒจ์ ๋ํด์
- ์นํฉ์ ๋ํด์
- ES LINT์ Prettier
- React
- ์ค์ต (ํ๋ก์ ํธ ์์ฑ ๋ฐ ํจํค์ง ํ๊ฒฝ๊ตฌ์ฑ)
- ์ปดํฌ๋ํธ๋? (์ปดํฌ๋ํธ ์ค์ต)
- ์ปดํฌ๋ํธ๋ฅผ ๋ง๋๋ ๊ธฐ๋ฒ 2๊ฐ์ง (class ๊ธฐ๋ฐ & function ๊ธฐ๋ฐ)
- ์กฐ๊ฑด๋ถํ์์ ์์
ํ ๋๋ ์๋์ ๊ฐ์ด ์ง๊ด์ ์ผ๋ก ํ๋ ๊ฒ์ด ๋ณด๊ธฐ๊ฐ ์ข๋ค.
๐{isDark === true ? darkmodeStyle : lightmodeStyle}
๐
<div style={isDark === true ? darkmodeStyle : lightmodeStyle}>
<div onClick={darkmodeToggle}>
{isDark === true ? (
<img
src="https://cdn2.iconfinder.com/data/icons/weather-color-2/500/weather-02-64.png"
alt="ํ์"
/>
) : (
<img
src="https://cdn2.iconfinder.com/data/icons/weather-color-2/500/weather-10-64.png"
alt="๋ฌ"
/>
)}
</div>
-
๐ ์ฃผ์ ๋ฆฌ์คํธ ๋ง๋ค๊ธฐ
- fetch๋ก ์ฃผ์ ๋ฆฌ์คํธ api ๋ถ๋ฌ์ค๊ธฐ (stocks)
- ๊ฒ์ input ์ปดํฌ๋ํธ์ ์ฃผ์ ๋ฆฌ์คํธ ์ ๋ ฅํ๋ฉด ํด๋น ์ข ๋ชฉ ๋ํ๋ด๊ธฐ
include
์filter
ํจ์ ์์ง
const searchResult = search === "" ? stocks : stocks.filter((stock) => { return stock.title .toLocaleLowerCase() .includes(search.toLocaleLowerCase()); });
search(์ ๋ ฅํ ๊ฐ)์ด ๋น๊ฐ์ด๋ฉด ์ผ๋ฐ ์ฃผ์๋ฆฌ์คํธ๋ฅผ ๋ณด์ฌ์ค๋ผ, ๊ฐ์ด ์๋ค๋ฉด ์ ๋ ฅํ ๊ฐ์ ์ํ (
includes
) ๊ฐ์ ๋ชจ๋toLocaleLowerCase()
์๋ฌธ์๋ก ๋ฐ๊พผ ํ,filter
ํํฐ๋ง ํ์ฌ ๊ฑธ๋ฌ๋์จ ๊ฐ๋ค์ ๋ณด์ฌ์ค๋ผ ๐ ๋๋ฌธ์๋ ๋ค ์๋ฌธ์๋ก ํต์ผํ์ฌ ๋ชจ๋ ๊ฐ๋ค์ ๋์ผํ๊ฒ ๋ง๋ค์ด์ ๊ฒ์ ๊ฒฐ๊ณผ๋ฅผ ์ถ์ถํ์โ๏ธ -
๋คํฌ๋ชจ๋/๋ผ์ด๋๋ชจ๋ ๊ตฌํํ๊ธฐ (๋ก์ปฌ์คํ ๋ฆฌ์ง ์ฌ์ฉ)
- ๐ก์ค๋ฌด์์๋ ์์คํ ํ ๋ง์ ๋ง๊ฒ ํ ๋ง๋ฅผ ๋ฐ๊ฟ ๊ฒ์ธ๊ฐ? ์์คํ ํ ๋ง๋ ๋คํฌ์ธ๋ฐ ์น์ฑ ํ ๋ง๋ ๋ผ์ดํธ... ๋๋ฌด ๋ฐ๋ก ๋ ธ๋ ๋ฏํ ๋๋์ด ๋ค๊ธฐ๋ ํ ๊ฒ์. ๊ธฐํ์ ๋ฐ๋ผ ๋ค๋ฅด๊ฒ ์ง๋ง ์๋ง ์์คํ ํ ๋ง๋ฅผ ๋ฐ๋ผ ๊ทธ๊ฑฐ์ ๋ง๋ ํ ๋ง๋ก ์ ์ฉํ๋ ๊ฒ์ด ๋ฒ ์คํธ์ผ ๊ฒ์
-
๋๋ฆฌ์ฌ ์ฐ์ฐ์ (??)
- ๋๋ฆฌ์ฌ๋ ์์ ๊ฒ์ด ์๋๋ฉด ๊ทธ๋ฅ ๋ค์ ๊ฐ์ผ๋ก ์ทจ๊ธํด๋ผ ๋ผ๋ ๋ป.
const [isDark, setIsDark] = useState(() => JSON.parse(localStorage.getItem("@theme") ?? "false") );
-
useState
์ํ ๊ด๋ฆฌ hooks์()=>
์ด์ฒ๋ผ ์ง์ฐ์ด๊ธฐํ๋ฅผ ์ฌ์ฉํ ์๋ ์๋ค. ์ง์ฐ ์ด๊ธฐํ๋? (Lazy loading) ์ต์ด์ ๋จ ํ ๋ฒ๋ง ์คํํด์ค!
const [isDark, setIsDark] = useState(() =>
JSON.parse(localStorage.getItem("@theme") ?? "false")
);
- React-Query ์์ง
queryClient.invalidateQueries();
๋? ์บ์ฑ๋ ๋ฐ์ดํฐ๋ฅผreset
ํด์ฃผ๋ ๋ป์ด๋ค.
- api ๋ฐ์ดํฐ๋ฅผ ์ป์ด์ค๋ ํจ์๋ช
์
get~
๋ผ๋ ์ ๋์ด๋ฅผ ๋ถํ ์ง์! - ์ฝ๋ฐฑํจ์๋ก ๋๊ธด ๊ฒ๊ณผ ์๋ ๊ฒ์ ์ฐจ์ด
()=> { ํจ์() }
- ์ด๋ ๊ฒ ๋๊ฒจ์ผ ํจ์๊ฐ ํ ๋ฒ๋ง ์คํ๋๋ค.
onClickLike={() => {
onClickLike({ ...stock, isLike: !stock.isLike })
}}
- ์ด๋ ๊ฒ ๋๊ธฐ๊ฒ ๋ ๊ฒฝ์ฐ ํจ์๊ฐ ์ฌ๋ฌ๋ฒ ๋ฐ๋ณตํ๋ฉฐ ์คํ๋๋ค.
onClickLike={onClickLike({ ...stock, isLike: !stock.isLike })}
ํ์ง๋ง, ์ฌ๊ธฐ์ ์๋ onClickLike
์ผ๋ก ๋ฐ๋ props์ ํจ์ ์ด๋ฒคํธ๊ฐ ์๋์ ๊ฐ์ ์์ด๋ผ๋ฉด ํ ๋ฒ๋ง ์คํํ ์ ์๋ค.
const fn์ด๋ฒคํธํจ์๋ช
= () => {
๐retrun (
)
}
- api ๋ถ๋ฌ์ฌ ๋๋! if๋ฌธ์ผ๋ก ํ์คํ๊ฒ ๋ฐ์ดํฐ๋ฅผ ์ ์ดํ์.
const handleLikeButtonClick = async (item) => {
const res = await updateStock(item);
๐if (res.ok === true) {
queryClient.invalidateQueries("stocksList");
window.alert("์
๋ฐ์ดํธ๊ฐ ์ฑ๊ณตํ์์ต๋๋ค.");
}
};
์ด์ฒ๋ผ if (res.ok === true) {
์ง๊ด์ ์ผ๋ก ๋ํ๋ด์ด res.ok
๊ฐ ๋ง๋ค๋ฉด ์บ์ฑ๋ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์๋ผ.
- ์ค์ผ๋ ํค UI๋ฅผ ์ฌ์ฉํ์ฌ ํผํฌ๋จผ์ค ์ฑ๋ฅ ๊ฐ์ ํ์
- ๊ฐ๋ฐ์๋๊ตฌ -> ํผํฌ๋จผ์ค -> ๊ฒ์ฌ๋ฒํผ ํด๋ฆญ
์ค์ผ๋ ํค UI๋ฅผ ํ๊ณ ๋ ๋ค ๊ฐ์ ๋ ๊ฒฐ๊ณผ์ด๋ค.
์ฌ๊ธฐ์ CLS (Cumulative Layout Shift) ๊ฐ 0์ ์๋ ดํด์ผ ์ข์ ์ฑ๋ฅ์ด๋ผ๊ณ ๋งํ ์ ์๋ค. ๋ธ๋ผ์ฐ์ ๋ ๋๋ง์์ ์ผ์ด๋๋ ๋ ์ด์์์ด ์ ๊ฒ ์ผ์ด๋ฌ๋ค๋ ๊ฒ์ด๋ค.
๐ ์ค์ผ๋ ํค UI๋ ์ด๋ป๊ฒ ๋ง๋ค์ด์ผ ์ข์ ๊ฒ์ผ๊น ?
์ค์ ๋ฐ์ดํฐ ์ฝํ ์ธ UI์ ๋์ด๊ฐ๊ณผ ๋ฐ์ดํฐ๊ฐ ๋์ค๊ธฐ ์ ์ ๋ก๋ฉ๋ ๋ ๋ณด์ฌ์ง๋ ์ค์ผ๋ ํค UI์ height(๋์ด)์ ๋น์ทํด์ผํ๋ค.
- ErrorBoundary
- Suspense