๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
React

[React] To do list (ํ• ์ผ๋ชฉ๋ก)

by wildOjisan 2025. 5. 12.

my_react_app.zip
0.18MB

๐Ÿ”ง ์‚ฌ์šฉํ•˜๋Š” ๋„๊ตฌ

๋„๊ตฌ                                          ์„ค๋ช…

 

React ์‚ฌ์šฉ์ž ํ™”๋ฉด(UI)์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
TypeScript ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์— ํƒ€์ž…(์ž๋ฃŒํ˜•)์„ ์ถ”๊ฐ€ํ•œ ์–ธ์–ด
useState React์—์„œ ์ƒํƒœ(state)๋ฅผ ์ €์žฅํ•˜๋Š” ๊ธฐ์–ต์žฅ์น˜

 

๐Ÿง  ๊ฐœ๋… ๋จผ์ € ์‚ด์ง๋งŒ ์ดํ•ดํ•˜๊ณ  ๊ฐ€์š”!

โ“ State (์ƒํƒœ)?

  • ์‰ฝ๊ฒŒ ๋งํ•ด ๊ธฐ์–ต ์ €์žฅ์†Œ
  • ์šฐ๋ฆฌ๊ฐ€ ์ž…๋ ฅํ•œ ๋‚ด์šฉ, ์ถ”๊ฐ€๋œ ํ•  ์ผ ๋ชฉ๋ก ๋“ฑ์„ ๊ธฐ์–ตํ•ด์คŒ
  • ์ƒํƒœ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ํ™”๋ฉด๋„ ์ž๋™์œผ๋กœ ๋‹ค์‹œ ๋ณด์—ฌ์คŒ (์ด๊ฒŒ React์˜ ํ•ต์‹ฌ!)

 

๐Ÿงฑ ์ „์ฒด ์ฝ”๋“œ ๊ตฌ์กฐ ํ•œ๋ˆˆ์— ๋ณด๊ธฐ

const [input, setInput] = useState("");

์ด๋ฆ„                                          ๋œป

input ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ๊ธ€์ž
setInput ๊ทธ ๊ธ€์ž๋ฅผ ๊ธฐ์–ต์žฅ์น˜์— ์ €์žฅํ•˜๋Š” ํ•จ์ˆ˜

 

const [todos, setTodos] = useState<Todo[]>([]);

์ด๋ฆ„                                             ๋œป

todos ํ•  ์ผ ๋ชฉ๋ก ์ „์ฒด๋ฅผ ๋‹ด๊ณ  ์žˆ๋Š” ๋ฐฐ์—ด
setTodos ๋ชฉ๋ก์„ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์ˆ˜์ •ํ•˜๋Š” ํ•จ์ˆ˜

 

 

1๏ธโƒฃ ์‚ฌ์šฉ์ž ์ž…๋ ฅ ์ฒ˜๋ฆฌ

<input
  value={input}
  onChange={(e) => setInput(e.target.value)}
  onKeyDown={(e) => {
    if (e.key === "Enter") addTodo();
  }}
/>
<button onClick={addTodo}>์ถ”๊ฐ€</button>

๐Ÿ’ก ์š”์  ์š”์•ฝ

  • value={input}: React์—์„œ input์˜ ๊ฐ’์„ ๊ธฐ์–ตํ•จ
  • onChange: ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•  ๋•Œ๋งˆ๋‹ค input์— ์ €์žฅ
  • onKeyDown: Enter ํ‚ค ๋ˆ„๋ฅด๋ฉด ์ถ”๊ฐ€ ๊ธฐ๋Šฅ ์‹คํ–‰
  • onClick: ๋ฒ„ํŠผ ๋ˆ„๋ฅด๋ฉด ํ•  ์ผ ์ถ”๊ฐ€

 

2๏ธโƒฃ ํ•  ์ผ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ

const addTodo = () => {
  if (!input.trim()) return;
  const newTodo = {
    id: Date.now(), // ์‹œ๊ฐ„์œผ๋กœ ๊ณ ์œ  ๋ฒˆํ˜ธ ์ƒ์„ฑ
    text: input, // ์‚ฌ์šฉ์ž๊ฐ€ ์“ด ๊ธ€
    isEditing: false // ์ง€๊ธˆ ์ˆ˜์ • ์ค‘์ธ์ง€ ์—ฌ๋ถ€
  };
  setTodos([...todos, newTodo]); // ๊ธฐ์กด ๋ชฉ๋ก(todos) ๋’ค์— ๋ถ™์—ฌ์„œ ์ €์žฅํ•ด์š”.
  setInput(""); // ์ž…๋ ฅ์ฐฝ์„ ๋น„์›Œ์ค˜์š”.
};

 

๐Ÿง  ๊ฐœ๋… ์š”์•ฝ

  • Date.now() → ๊ณ ์œ ํ•œ ๋ฒˆํ˜ธ ๋งŒ๋“ค์–ด์ฃผ๋Š” ํŠธ๋ฆญ
  • ...todos, newTodo → ๊ธฐ์กด ๋ชฉ๋ก ๋’ค์— ์ƒˆ ํ•  ์ผ ์ถ”๊ฐ€

 

3๏ธโƒฃ ํ•  ์ผ ๋ชฉ๋ก ๋ณด์—ฌ์ฃผ๊ธฐ

<ul>
  {todos.map((todo) => (
    <li key={todo.id}>
      ...
    </li>
  ))}
</ul>

map() ํ•จ์ˆ˜๋กœ todos ๋ฐฐ์—ด์„ ํ•˜๋‚˜ํ•˜๋‚˜ ๊บผ๋‚ด์„œ <li>๋กœ ๋ณด์—ฌ์คŒ

 

 

4๏ธโƒฃ ์ˆ˜์ • ๋ชจ๋“œ vs ๋ณด๊ธฐ ๋ชจ๋“œ

{todo.isEditing ? (
  <input
    defaultValue={todo.text}
    onBlur={(e) => saveEdit(todo.id, e.target.value)}
    onKeyDown={(e) => {
      if (e.key === "Enter") {
        saveEdit(todo.id, (e.target as HTMLInputElement).value);
      }
    }}
  />
) : (
  <>
    <span>{todo.text}</span>
    <button onClick={() => startEdit(todo.id)}>์ˆ˜์ •</button>
    <button onClick={() => deleteTodo(todo.id)}>์‚ญ์ œ</button>
  </>
)}

์š”์†Œ                                                                              ์„ค๋ช…

isEditing === true ์ž…๋ ฅ์ฐฝ ๋ณด์—ฌ์คŒ (์ˆ˜์ • ์ค‘)
isEditing === false ๊ธ€์ž + ์ˆ˜์ •/์‚ญ์ œ ๋ฒ„ํŠผ ๋ณด์—ฌ์คŒ

 

 

5๏ธโƒฃ ์ˆ˜์ • ๊ธฐ๋Šฅ ์„ค๋ช…

const startEdit = (id: number) => {
  setTodos(todos.map((todo) =>
    todo.id === id ? { ...todo, isEditing: true } : todo
  ));
};

 

  • ์ˆ˜์ • ๋ฒ„ํŠผ ๋ˆ„๋ฅด๋ฉด isEditing: true๋กœ ๋ฐ”๋€œ
  • ๊ทธ๋Ÿฌ๋ฉด input์ฐฝ์ด ๋‚˜ํƒ€๋‚จ!

 

6๏ธโƒฃ ์ˆ˜์ • ์ €์žฅ ๊ธฐ๋Šฅ

const saveEdit = (id: number, newText: string) => {
  setTodos(todos.map((todo) =>
    todo.id === id ? { ...todo, text: newText, isEditing: false } : todo
  ));
};

 

๊ธ€์ž๋ฅผ ๋ฐ”๊พธ๊ณ  Enter ๋ˆ„๋ฅด๊ฑฐ๋‚˜ ํฌ์ปค์Šค๋ฅผ ์žƒ์œผ๋ฉด ์ˆ˜์ • ์™„๋ฃŒ๋จ

 

 

7๏ธโƒฃ ์‚ญ์ œ ๊ธฐ๋Šฅ

const deleteTodo = (id: number) => {
  setTodos(todos.filter((todo) => todo.id !== id));
};

ํ•ด๋‹น id๋ฅผ ๊ฐ€์ง„ ํ•  ์ผ์„ ์ œ์™ธํ•œ ๋‚˜๋จธ์ง€๋งŒ ๋‹ค์‹œ ์ €์žฅ!