/4week_group_study

๐Ÿ“ˆ ์ฃผ์‹ ๋ฆฌ์ŠคํŠธ ๋งŒ๋“ค๊ธฐ (+ ๊ฒ€์ƒ‰ ๋ฐ ์ข‹์•„์š”, ์„ฑ๋Šฅ ๊ฐœ์„ )

Primary LanguageJavaScript

๐ŸŽ€ ํ”„๋ก ํŠธ์—”๋“œ ๊ทธ๋ฃน ์Šคํ„ฐ๋””

  • ๊ฐœ์š” : ๋งค์ฃผ 1ํšŒ์”ฉ ํ•œ ๋‹ฌ๊ฐ„ ์ง„ํ–‰๋˜๋Š” React ์Šคํ„ฐ๋””
  • ๊ธฐ๊ฐ„ : 9์›” 7์ผ ~ 9์›” 27์ผ (์ด 4ํšŒ)

Start

$npm run dev
$npm run db

๐Ÿ“ฎ 1Week (9์›” 7์ผ ๋ชฉ์š”์ผ) ์š”์•ฝ

  1. ๋ธŒ๋ผ์šฐ์ € ๋ Œ๋”๋ง ์›๋ฆฌ
  2. ์š”์ฆ˜์€ ์™œ ์ œ์ด์ฟผ๋ฆฌ๋ฅผ ์•ˆ์“ธ๊นŒ ?
  3. node & npm
  4. ์‹ฑ๊ธ€ ํŽ˜์ด์ง€ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜(SPA)์ด๋ž€?
  5. ๋ฐ”๋ฒจ์— ๋Œ€ํ•ด์„œ
  6. ์›นํŒฉ์— ๋Œ€ํ•ด์„œ
  7. ES LINT์™€ Prettier
  8. React
  9. ์‹ค์Šต (ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ ๋ฐ ํŒจํ‚ค์ง€ ํ™˜๊ฒฝ๊ตฌ์„ฑ)
  10. ์ปดํฌ๋„ŒํŠธ๋ž€? (์ปดํฌ๋„ŒํŠธ ์‹ค์Šต)
  • ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“œ๋Š” ๊ธฐ๋ฒ• 2๊ฐ€์ง€ (class ๊ธฐ๋ฐ˜ & function ๊ธฐ๋ฐ˜)


๐Ÿ“ฎ 2Week (9์›” 14์ผ ๋ชฉ์š”์ผ) ์š”์•ฝ

  • ์กฐ๊ฑด๋ถ€ํ˜•์‹์„ ์ž‘์—…ํ•  ๋•Œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์ง๊ด€์ ์œผ๋กœ ํ•˜๋Š” ๊ฒƒ์ด ๋ณด๊ธฐ๊ฐ€ ์ข‹๋‹ค.
    ๐ŸŒŸ{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")
);


๐Ÿ“ฎ 3Week (9์›” 21์ผ ๋ชฉ์š”์ผ) ์š”์•ฝ

  • React-Query ์ˆ™์ง€
    • queryClient.invalidateQueries();๋ž€? ์บ์‹ฑ๋œ ๋ฐ์ดํ„ฐ๋ฅผ reset ํ•ด์ฃผ๋Š” ๋œป์ด๋‹ค.
  • api ๋ฐ์ดํ„ฐ๋ฅผ ์–ป์–ด์˜ค๋Š” ํ•จ์ˆ˜๋ช…์€ get~๋ผ๋Š” ์ ‘๋‘์–ด๋ฅผ ๋ถ™ํ˜€ ์ง“์ž!
  • ์ฝœ๋ฐฑํ•จ์ˆ˜๋กœ ๋„˜๊ธด ๊ฒƒ๊ณผ ์•„๋‹Œ ๊ฒƒ์˜ ์ฐจ์ด
  1. ()=> { ํ•จ์ˆ˜() }
  • ์ด๋ ‡๊ฒŒ ๋„˜๊ฒจ์•ผ ํ•จ์ˆ˜๊ฐ€ ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰๋œ๋‹ค.
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(๋†’์ด)์™€ ๋น„์Šทํ•ด์•ผํ•œ๋‹ค.


๐Ÿ“ฎ 4Week (9์›” 27์ผ ์ˆ˜์š”์ผ) ์š”์•ฝ

  • ErrorBoundary
  • Suspense