๐Ÿ“…2024. 04. 03 78์ผ์ฐจ


๐ŸŽจ React_TODOLIST

TodoList ์ ์šฉ, ํ• ์ผ ์ถ”๊ฐ€ ํผ

  • ์ฝ”๋“œํŽœ์—์„œ ์‚ฌ์šฉํ•œ TodoList์„ ์ ์šฉ

  • ํ•  ์ผ ์ถ”๊ฐ€ ํผ ๊ตฌํ˜„

		<form onSubmit={onSubmit}>
          <input type="text" name="title" autoComplete="off" placeholder="ํ•  ์ผ ์ž…๋ ฅํ•ด" />
          <button type="submit">์ถ”๊ฐ€</button>
          <button type="reset">์ทจ์†Œ</button>
        </form>

github TodoList ์ ์šฉ, ํ• ์ผ ์ถ”๊ฐ€ ํผ

TODO ์ถ”๊ฐ€ ํผ ๊พธ๋ฏธ๊ธฐ

  • MUI, Button ์ปดํฌ๋„ŒํŠธ ์‚ฌ์šฉํ•ด์„œ ์ถ”๊ฐ€ ํผ ์‚ฌ์šฉ
 <form className="tw-flex tw-flex-col tw-p-4 tw-gap-2" onSubmit={onSubmit}>
        <TextField name="content" autoComplete="off" label="ํ•  ์ผ์„ ์ž…๋ ฅํ•ด" variant="outlined" />
        <Button className="tw-font-bold" variant="contained" type="submit">
          ์ถ”๊ฐ€
        </Button>
 </form>

github TODO ์ถ”๊ฐ€ ํผ ๊พธ๋ฏธ๊ธฐ

Todo์˜ ๋ฆฌ์ŠคํŒ…

  • App ์ปดํฌ๋„ŒํŠธ return ๋ถ€๋ถ„์— ๋ฆฌ์ŠคํŠธํ˜•ํƒœ๋กœ ์ถ”๊ฐ€ UI์— ๋ณด์—ฌ์ฃผ๊ธฐ
      ํ•  ์ผ ๊ฐฏ์ˆ˜ : {todosState.todos.length}
      <nav>
        <ul>
          {todosState.todos.map((todo) => (
            <li key={todo.id}>
              <div className="tw-flex tw-flex-col tw-gap-2">
                <span>๋ฒˆํ˜ธ : {todo.id}</span>
                <span>๋‚ ์งœ : {todo.regDate}</span>
                <span>ํ•  ์ผ : {todo.content}</span>
              </div>
            </li>
          ))}
        </ul>
      </nav>

github Todo์˜ ๋ฆฌ์ŠคํŒ…

Chip ์ ์šฉ

  • Chip MUI ์ ์šฉํ•˜์—ฌ ๋ฆฌ์ŠคํŠธ ๋ณด์—ฌ์ฃผ๊ธฐ
      <nav>
        <ul>
          {todosState.todos.map((todo) => (
            <li key={todo.id}>
              <div className="tw-flex tw-flex-col tw-gap-2 tw-mt-[30px]">
                <Chip label={`๋ฒˆํ˜ธ : ${todo.id}`} variant="outlined"></Chip>
                <Chip label={`๋‚ ์งœ : ${todo.regDate}`} variant="outlined"></Chip>
                <Chip label={`ํ•  ์ผ : ${todo.content}`} variant="outlined" color="primary"></Chip>
              </div>
            </li>
          ))}
        </ul>
      </nav>
  • ํ•  ์ผ์€ Chip ์‚ฌ์šฉํ•˜๋ฉด ์ด์ƒํ•  ๋“ฏ? ๊ทธ๋ƒฅ div๋กœ ๊ฐ์‹ธ๊ณ  ์Šคํƒ€์ผ์„ ์ค˜๋ณด์ž!~!
<div className="tw-p-10 tw-rounded-[20px] tw-shadow tw-whitespace-pre-wrap tw-leading-relaxed">
	ํ•  ์ผ : {todo.content}
</div>

github Chip ์ ์šฉ


๐Ÿ”’๋ฌธ์ œ๋ฐœ์ƒ

  • useEffect ์จ์„œ ์ตœ์ดˆ ํ•œ ๋ฒˆ ๋งŒ ์‹คํ–‰๋˜๊ฒŒ ํ–ˆ๋Š”๋ฐ... ๋‘ ๋ฒˆ ์‹คํ–‰๋จ... ์™œ??

  React.useEffect(() => {
    todosState.addTodo('์Šค์ฟผํŠธ');
    todosState.addTodo('๋ฒค์น˜');
    todosState.addTodo('๋ฐ๋“œ');
  }, []);

๐Ÿ”“๋ฌธ์ œํ•ด๊ฒฐ

ํ•ด๋‹น ์ด์Šˆ๋Š” React 18 ์—์„œ ๋„์ž…๋œ strict mode ๊ธฐ๋Šฅ์— ์˜ํ•ด ๋ฐœ์ƒํ•œ๋‹ค. Next.js ์˜ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋Š” ์ด stirct mode ๊ฐ€ ํ™œ์„ฑํ™”๋˜์–ด ์žˆ๋‹ค.

  • strict mode๋ž€?
    strict mode ๋Š” ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋” ์—„๊ฒฉํ•˜๊ฒŒ ๊ฒ€์‚ฌํ•˜์—ฌ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋ฒ„๊ทธ๋ฅผ ์‚ฌ์ „์— ์ฐพ์•„๋‚ด๋Š” ๋ฐ์— ๋„์›€์„ ์ฃผ๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.

์ด๋ฅผ ํ™œ์„ฑํ™” ํ•˜๊ฒŒ ๋˜๋ฉด,

์ปดํฌ๋„ŒํŠธ์˜ ๋ถˆ์™„์ „ํ•œ ๋ Œ๋”๋ง์„ ๊ฒ€์‚ฌํ•˜๊ธฐ ์œ„ํ•ด, ํ•œ ๋ฒˆ ๋” ๋ Œ๋”๋ง๋œ๋‹ค.

Effect ํด๋ฆฐ์—… ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•˜์ง€ ์•Š์•„ ๋ฐœ์ƒํ•˜๋Š” ๋ฒ„๊ทธ๋ฅผ ๊ฒ€์‚ฌํ•˜๊ธฐ ์œ„ํ•ด, Effect ๋“ค์„ ํ•œ ๋ฒˆ ๋” ์‹คํ–‰ํ•œ๋‹ค.

์ปดํฌ๋„ŒํŠธ์—์„œ ๋” ์ด์ƒ ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” API ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ง€ ๊ฒ€์‚ฌํ•œ๋‹ค.

  • ๊ฒฐ๋ก ์ ์œผ๋กœ
    ํ•ด๋‹น ์ด์Šˆ๊ฐ€ ๋ฐœ์ƒํ•œ ์›์ธ์ด strict mode ๋•Œ๋ฌธ์ž„์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

strict mode ๋น„ํ™œ์„ฑ

๋น„ํ™œ์„ฑํ™” ์ „ ์œ ์˜ํ•ด์•ผํ•  ์ ์ด ๋ช‡ ๊ฐ€์ง€ ์žˆ๋‹ค.

์šฐ์„ , strict mode ๋Š” ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋งŒ ๋™์ž‘ํ•˜๊ณ , ํ”„๋กœ๋•ํŠธ ๋ฐฐํฌ ํ™˜๊ฒฝ์—์„œ๋Š” ๋™์ž‘ํ•˜์ง€ ์•Š๋Š”๋‹ค.

๋˜ํ•œ, strict mode ๋Š” ์ƒ์ˆ ํ–ˆ๋“ฏ ์˜ˆ๊ธฐ์น˜ ๋ชปํ•œ ๋ฒ„๊ทธ๋“ค์„ ์ฐพ์•„๋‚ด๋Š” ๋ฐ์— ๋„์›€์„ ์ค€๋‹ค.

๋”ฐ๋ผ์„œ, ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ ํ•„์ˆ˜์ ์œผ๋กœ ํ•ด๋‹น ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด์•ผ ํ•˜๋Š” ์ƒํ™ฉ์ด ์•„๋‹ˆ๋ผ๋ฉด, ๊ตณ์ด strict mode ๋ฅผ ๋น„ํ™œ์„ฑํ™” ํ•  ํ•„์š”๋Š” ์—†๋‹ค. ์–ด์ฐจํ”ผ ๋ฐฐํฌ ํ™˜๊ฒฝ์—์„œ๋Š” ์›ํ•˜๋Š” ๋Œ€๋กœ Effect ๊ฐ€ ํ•œ ๋ฒˆ๋งŒ ํ˜ธ์ถœ๋œ๋‹ค.

// next.config.js
const nextConfig = {
    reactStrictMode: false,
};

module.exports = nextConfig;

next.config.js ํŒŒ์ผ์„ ์œ„์™€ ๊ฐ™์ด ์ˆ˜์ •ํ•˜๋ฉด ๋น„ํ™œ์„ฑํ™” ๋!


TextField ์ˆ˜์ •

  • TextField์— multiline minRows={3} maxRows={10} ์†์„ฑ ์ถ”๊ฐ€
<TextField
    multiline
    minRows={3}
    maxRows={10}
    name="content"
    autoComplete="off"
    label="ํ•  ์ผ์„ ์ž…๋ ฅํ•ด"
    variant="outlined"
/>

Box sx, theme light,dark

  • ๋ฆฌ์ŠคํŠธ๋ฅผ Box sx๋กœ ๋ณด์—ฌ์ฃผ๊ธฐ
<Box sx={{ color: 'primary.main' }}>{todo.content}</Box>
  • ํ…Œ๋งˆ ์ƒ‰๊น” ์ถ”๊ฐ€ light, dark
const theme = createTheme({
  palette: {
    typocraphy: {
      fontFamily: ['GmarketSansMedium'],
    },
    type: 'dark',
    primary: {
      main: '#2196f3',
      light: 'rgb(77, 171, 245)',
      dark: 'rgb(23, 105, 170)',
      contrastText: '#fff',
    },
    secondary: {
      main: '#f50057',
    },
  },
});
  • ํ•ด๋‹น ์ƒ‰๊น” ์‚ฌ์šฉํ•  ๋•Œ
color: 'primary.main'

์œ„์— ๋ฐฉ๋ฒ•์œผ๋กœ ์‚ฌ์šฉ๊ฐ€๋Šฅ

github Chip ์ƒ๋‹จ ์—ฌ๋ฐฑ, Box sx, theme light,dark

๋ณด์—ฌ์ฃผ๋Š” todoList์„ ์—ญ์ˆœ(์ตœ์‹ ์ˆœ)์œผ๋กœ ๋ฐ”๊พธ์ž!

 setTodos((todos) => [...todos, newTodo]);

newTodo๊ฐ€ ์•ž์œผ๋กœ ๋จผ์ € ๋ณด์ด๊ฒŒ ๋ฐ”๊พธ๋ฉด ๋  ๋“ฏ

setTodos((todos) => [newTodo, ...todos]);

MUI theme color๋“ค์„ CSS์—์„œ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก CSS ๋ณ€์ˆ˜ํ™”

  • MUI์—์„œ theme color์„ ๋ฝ‘์•„์™€์„œ CSS์—์„œ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด๋ณด์ž!!
React.useEffect(() => {
    const r = document.querySelector(':root');

    muiThemePaletteKeys.forEach((paletteKey) => {
      const themeColorObj = theme.palette[paletteKey];
      // console.log(themeColor);

      for (const key in themeColorObj) {
        // console.log(key);
        if (Object.hasOwnProperty.call(themeColorObj, key)) {
          const colorVal = themeColorObj[key];
          r.style.setProperty(`--mui-color-${paletteKey}-${key}`, colorVal);
        }
      }
    });
  }, []);
  • useEffect ๋‚ด์—์„œ ์ฝ”๋“œ๋Š” HTML ๋ฌธ์„œ์˜ :root ์š”์†Œ๋ฅผ ์„ ํƒ.

  • muiThemePaletteKeys ๋ฐฐ์—ด์—๋Š” MUI ํŒ”๋ ˆํŠธ์˜ ๋‹ค์–‘ํ•œ ๋ถ€๋ถ„์— ํ•ด๋‹นํ•˜๋Š” ํ‚ค๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ๋‹ค.

  • forEach ๋ฃจํ”„๋Š” muiThemePaletteKeys ๋ฐฐ์—ด์˜ ๊ฐ ํ‚ค๋ฅผ ๋ฐ˜๋ณต.

  • ๊ฐ ํ‚ค์— ๋Œ€ํ•ด ์ฝ”๋“œ๋Š” ํ…Œ๋งˆ ํŒ”๋ ˆํŠธ ๊ฐ์ฒด์—์„œ ์ƒ‰์ƒ ๊ฐ’์„ ๊ฒ€์ƒ‰ํ•˜๊ณ  setProperty ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋ฅผ CSS ๋ณ€์ˆ˜๋กœ ์„ค์ •. ์ด๋ฅผ ํ†ตํ•ด CSS ์Šคํƒ€์ผ์‹œํŠธ์—์„œ ์ด๋Ÿฌํ•œ ์ƒ‰์ƒ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

tw-text-[--mui-color-warning-main] 

์ด๋Ÿฐ์‹์œผ๋กœ ์‚ฌ์šฉ๊ฐ€๋Šฅ!!

github mui theme color๋“ค์„ CSS์—์„œ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก CSS ๋ณ€์ˆ˜ํ™”

์˜ค๋Š˜์˜ ๊ฐœ๋…

css๋ณ€์ˆ˜

  • CSS ๋ณ€์ˆ˜ ๊ธฐ๋Šฅ

์‚ฌ์šฉ์ž ์ง€์ • ์†์„ฑ(CSS ๋ณ€์ˆ˜, ์ข…์† ๋ณ€์ˆ˜)์€ CSS ์ €์ž‘์ž๊ฐ€ ์ •์˜ํ•˜๋Š” ๊ฐœ์ฒด๋กœ, ๋ฌธ์„œ ์ „๋ฐ˜์ ์œผ๋กœ ์žฌ์‚ฌ์šฉํ•  ์ž„์˜์˜ ๊ฐ’์„ ๋‹ด๋Š”๋‹ค. ์‚ฌ์šฉ์ž ์ง€์ • ์†์„ฑ์€ ์ „์šฉ ํ‘œ๊ธฐ๋ฒ•์„ ์‚ฌ์šฉํ•ด ์ •์˜ํ•˜๊ณ , (--main-color: black;) var() ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค. (color: var(--main-color);)

๋ณต์žกํ•œ ์›น์‚ฌ์ดํŠธ ๊ฐ™์€ ๊ฒฝ์šฐ ์—„์ฒญ๋‚œ ์–‘์˜ CSS๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š”๋ฐ, ์œ ์ง€๋ณด์ˆ˜๋ฅผ ํ•˜๋‹ค ๋ณด๋ฉด ์—ฌ๋Ÿฌ ๊ณณ์—์„œ ์‚ฌ์šฉํ•œ ์ž„์˜์˜ ๊ฐ’์„ ํ•œ๊บผ๋ฒˆ์— ๋ฐ”๊ฟ”์•ผ ํ•  ๋•Œ๊ฐ€ ์žˆ๋‹ค. ๊ทธ ๊ฐ’๋“ค์„ ํ•˜๋‚˜์”ฉ ์ฐพ์•„์„œ ์ผ์ผ์ด ๋ฐ”๊พธ๋ ค๋ฉด ์ƒ๋‹นํžˆ ํ”ผ๊ณคํ•  ๊ฒƒ์ด๊ณ , ๋•Œ์— ๋”ฐ๋ผ ์‹ค์ˆ˜๋„ ๋งŽ์„ ๊ฒƒ์ด๋‹ค.
๊ทธ๋ž˜์„œ ์—ฌํƒ€ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์ฒ˜๋Ÿผ ๋ณ€์ˆ˜๋ฅผ ์ด์šฉํ•˜๋ฉด, ์š”์†Œ ์ „๋ฐ˜์— ๊ฑธ์ณ ์‚ฌ์šฉ๋˜๋Š” ์ž„์˜์˜ ๊ฐ’์„ ๋ณ€์ˆ˜์— ์ €์žฅํ•˜๊ณ  ํ˜ธ์ถœํ•˜๋ฉด ๋ณด๋‹ค ํšจ์œจ์ ์ธ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ด ๊ฐ€๋Šฅํ•ด์ง„๋‹ค.

css๋ณ€์ˆ˜ ์„ ์–ธ ๋ฐ ํ˜ธ์ถœ

:root {
	--main-font-color: #000f22;  /* CSS ์ „์—ญ ๋ณ€์ˆ˜ ์„ ์–ธ */
}

div {
	color: var(--main-font-color);   /* CSS ๋ณ€์ˆ˜ ์‚ฌ์šฉ */
}

CSS ๋ณ€์ˆ˜(--variable) ์‚ฌ์šฉ๋ฒ• & ์‘์šฉ ์ •๋ฆฌ

tailwind JIT

tailwind์˜ ์กฐ๊ฑด์  ๋””์ž์ธ

0๊ฐœ์˜ ๋Œ“๊ธ€