์ฃผ์š” ์ž‘์—…

  • ํด๋ผ์ด์–ธํŠธ
    • ์Šคํƒ€์ผ ์ž‘์—…
    • css ๋ฒˆ๋“ค๋ง๊ณผ ํด๋ž˜์Šค ์„ ํƒ์ž๋ฅผ ํ†ตํ•œ ์Šคํƒ€์ผ ์ž…ํžˆ๊ธฐ
    • spinner ์„ค์ •๊ณผ state์— ๋”ฐ๋ฅธ spinner ๋ Œ๋”๋ง ํ…Œ์ŠคํŠธ
  • ์„œ๋ฒ„
    • updateTodo์—์„œ ๊ฐ์ฒด๋ฅผ ๋„˜๊ฒจ์ฃผ๋Š” ๋ฐฉ์‹์œผ๋กœ ๋ฆฌํŒฉํ† ๋ง
  • ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
    • useEffect์—์„œ ์ด์ „์˜ ๊ฐ’๊ณผ ํ˜„์žฌ ๊ฐ’์„ ๋น„๊ตํ•˜๊ธฐ ์œ„ํ•ด ๋„ฃ์„ ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„
    • Object.keys()๋Œ€์‹  map ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ๊ฒ€์ƒ‰ ์„ฑ๋Šฅ ์ตœ์ ํ™”

ํ•™์Šต ํ‚ค์›Œ๋“œ

  • firebase functions & firestore
  • NOSQL
  • ์„œ๋ฒ„๋ฆฌ์Šค ์•„ํ‚คํ…์ฒ˜
  • useEffect
  • useRef
  • useContext

๊ณ ๋ฏผ ๋ฐ ํ•ด๊ฒฐ๊ณผ์ •

state์™€ ํ•จ์ˆ˜๋ฅผ ๋™์‹œ์— ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์˜ ์ธ์ž๋กœ ๋„˜๊ฒจ์ค„ ๋•Œ

export default function Mainpage() {
...
  const [isLoading, setIsLoading] = mhReact.useState(false);
  
  async function fetchData() {
    const data = await apicall.get("/todo");
    setTodoList(data);
  }
 
  mhReact.useEffect(async () => {
    await fetchData();
  }, []);
...
 
  return (
	<div>
   ...
   <TodoList
        todoList={todoList}
        fetchData={fetchData}
      />
      <Spinner isLoading={isLoading} />
    </div>
  );
}
 

state์— ๋”ฐ๋ฅธ ํ™”๋ฉด ๋ Œ๋”๋ง์„ ํ…Œ์ŠคํŠธํ•ด๋ณด๊ณ  ์žˆ๋˜ ์™€์ค‘์— TodoList์—์„œ ๊ฐ ์š”์†Œ์— ๋Œ€ํ•ด ์ด์™€ ๊ฐ™์ด X ๋ฒ„ํŠผ์„ ๋งŒ๋“ค์–ด ๋ˆ„๋ฅด๋ฉด ์‚ญ์ œ api ์š”์ฒญ์ด ๊ฐˆ ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค์—ˆ๋‹ค. ํ•˜์ง€๋งŒ ์‚ญ์ œ api ์š”์ฒญ์ด ๊ฐ€๊ณ ๋‚œ ๋’ค์—๋Š” ๋‹ค์‹œ๊ธˆ ๋ฐ์ดํ„ฐ๋ฅผ fetchํ•ด์™€ ์‚ญ์ œํ•ด์˜จ ๋’ค ์ตœ์‹  ์ •๋ณด๋ฅผ ๊ฐฑ์‹ ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์•ž์—์„œ ์‹คํ–‰ํ–ˆ๋˜ fetchData๋ฅผ ๊ฐ€์ ธ์˜ฌ ํ•„์š”๊ฐ€ ์žˆ์—ˆ๋‹ค. ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ์—์„œ setTodoList๋ฅผ ํ†ตํ•ด ํ•ด๋‹น ๋ฆฌ์ŠคํŠธ๋“ค์— ๋Œ€ํ•ด์„œ ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

import * as mhReact from "mhreact";
import { deleteTodo } from "../../features/todo";
import "./index.css";
 
export default function TodoList({ todoList, fetchData }) {
  if (!todoList.length) {
    return <h1>๋กœ๋”ฉ์ค‘...</h1>;
  }
  async function onClickDelete(id) {
    try {
      await deleteTodo(id);
      await fetchData();
    } catch (error) {
      alert("์‚ญ์ œ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์–ด์š”!");
    }
  }
  return (
    <ul class="todo-list">
      {todoList.map((todo) => (
        <div class="todo-item">
          <li>{todo.detail}</li>
          <button
            class="todo-deleteButton"
            onClick={() => onClickDelete(todo.id)}
          >
            โŒ
          </button>
        </div>
      ))}
    </ul>
  );
}
 

๋”ฐ๋ผ์„œ ์ด๋Ÿฐ ์‹์œผ๋กœ fetchData ํ•จ์ˆ˜ ์ž์ฒด๋ฅผ ๊ฐ€์ ธ์™€ delete ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด delete api๋ฅผ ๋ณด๋‚ธ ํ›„์— fetchData๋ฅผ ํ†ตํ•ด์„œ ๋‹ค์‹œ๊ธˆ ์ •๋ณด๋ฅผ ๊ฐฑ์‹ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ์—ˆ๋‹ค.

export function destructuringFunctionalComponentWithRef(element) {
  const duplicated = {};
  Object.entries(element.props.refs).forEach(([ref, val]) => {
    if (typeof val === "function") duplicated[ref] = val();
    else duplicated[ref] = val;
  });
  return element.type(duplicated);
}

ํ•˜์ง€๋งŒ ๋‚ด๊ฐ€ ์„ค๊ณ„ํ•œ fiber๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๊ณผ์ •์—์„œ destructuringFunctionalComponentWithRef()๋Š” ํ˜น์‹œ๋‚˜ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์›ํ•˜๋Š” ๊ฒƒ๋“ค ์ค‘ ์ƒํƒœ๋ฅผ ๋„˜๊ฒจ์ฃผ๊ฒŒ ๋œ๋‹ค๋ฉด, ํ™ค์ˆ˜๋Š” ๋ฃจํŠธ์— ๋ถ™์–ด์žˆ๋Š” ์ƒํƒœ ์ฐธ์กฐ๊ฐ’์„ ๊ฐ€์ ธ์™€ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜์ด๊ธฐ ๋•Œ๋ฌธ์— ํ•ด๋‹น ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰์‹œ์ผœ ๋ฐ˜ํ™˜๋ฐ›์€ ๊ฐ’์„ ์ปดํฌ๋„ŒํŠธ์˜ ํ•จ์ˆ˜ ์ธ์ž๋กœ ๋„ฃ์–ด์คฌ์–ด์•ผ ํ–ˆ๋‹ค. ์ด ๊ณผ์ •๋•Œ๋ฌธ์— ref์— ์„ค์ •ํ•ด๋†“์•˜๋˜ ํ•จ์ˆ˜๋“ค์€ ๋ชจ์กฐ๋ฆฌ ์‹คํ–‰์‹œ์ผœ ์ฃผ๋Š” ๋กœ์ง์œผ๋กœ ์ž‘์„ฑํ•˜์˜€๋‹ค. ์ƒํƒœ์™€ ์ผ๋ฐ˜ ํ•จ์ˆ˜๋ฅผ ๋”ฐ๋กœ ๊ตฌ๋ถ„ํ•  ์ˆ˜๋„ ์—†๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๋”ฐ๋ผ์„œ ์ด๋ฅผ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ•˜๋ฉด ์ข‹์„๊นŒ ์ƒ๊ฐํ•˜๋‹ค๊ฐ€ ํด๋ผ์ด์–ธํŠธ์—์„œ ํ•จ์ˆ˜๋ฅผ ์ฃผ๋Š” ๋ฐฉ์‹์„ ๋ฐ”๊ฟ”๋ณด๋ฉด ๋˜์ง€ ์•Š์„๊นŒ ์ƒ๊ฐํ–ˆ๋‹ค.

// ๋ณ€๊ฒฝ ์ „
<TodoList
        todoList={todoList}
        fetchData={fetchData}
      />
      
// ๋ณ€๊ฒฝ ํ›„
<TodoList
        todoList={todoList}
        fetchData={() => {
          return fetchData;
        }}
      />

๊ธฐ์กด์—๋Š” ํ•จ์ˆ˜ ์ž์ฒด๋ฅผ ๋„ฃ์–ด์คฌ์—ˆ์ง€๋งŒ, ์ด๋ฒˆ์—๋Š” ํ•จ์ˆ˜๋ฅผ wrapper ํ•จ์ˆ˜๋กœ ๊ฐ์‹ธ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋‚ด๊ฐ€ ๋„˜๊ฒจ์ฃผ๊ณ ์ž ํ•˜๋Š” ํ•จ์ˆ˜ ์ž์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜๋ฅผ ์ธ์ž๋กœ ๋„˜๊ฒจ์ฃผ์—ˆ๋‹ค. fetchData ์ธ์ž๋Š” createElement ๊ณผ์ •์—์„œ ์‚ฌ์šฉ์ž๊ฐ€ ์ •์˜ํ•œ ์ปค์Šคํ…€ property์ด๊ธฐ ๋•Œ๋ฌธ์— props์˜ ref๋กœ ๊ฐ€๊ฒŒ ๋˜๊ณ , ์ด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ fiber๋ฅผ ๋งŒ๋“œ๋Š” ๊ณผ์ •์—์„œ destructuringFunctionalComponentWithRef()์„ ์‹คํ–‰์‹œํ‚ค๊ฒŒ ๋˜๋Š”๋ฐ, ํ•ด๋‹น ํ•จ์ˆ˜๋Š” ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜๋Š” ํ•จ์ˆ˜๊ฐ€ ์•„๋‹Œ ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜์ด๊ธฐ ๋•Œ๋ฌธ์— ์‹คํ–‰ ๊ณผ์ •์—์„œ ์‹คํ–‰ํ•  ํ•จ์ˆ˜๋งŒ ๋‚จ๊ฒŒ ๋˜์–ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

๋ฆฌ๋ทฐ ์š”์ฒญ

์•ˆ๋…•ํ•˜์„ธ์š” ๋ฉ˜ํ† ๋‹˜! ๊ณ ์ƒ ๋งŽ์œผ์‹ญ๋‹ˆ๋‹คใ…Žใ…Ž

๋ถ€์กฑํ•˜์ง€๋งŒ ํด๋ผ์ด์–ธํŠธ๋„ ๋ฒˆ๋“ค๋งํ•œ ํŒŒ์ผ์„ ๋ณด๋‚ด๋„๋ก ํ•ด์„œ ๋ฐฐํฌํ–ˆ์Šต๋‹ˆ๋‹ค! https://asia-northeast3-todo-24f54.cloudfunctions.net/api/

์˜ค๋Š˜ ์งˆ๋ฌธ๋“œ๋ฆฌ๊ณ  ์‹ถ์€ ๊ฒƒ์ด ํ•œ๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ํ™”์š”์ผ ๋ฆฌ๋ทฐ์—์„œ ๋ฉ˜ํ† ๋‹˜๊ป˜์„œ ์ปค์Šคํ…€ ์ด๋ฒคํŠธ๋ฅผ ๋ง์”€ํ•ด์ฃผ์…”์„œ ์ปค์Šคํ…€ ์ด๋ฒคํŠธ์— ๋Œ€ํ•ด์„œ ์กฐ๊ธˆ ์ฐพ์•„๋ณด์•˜์Šต๋‹ˆ๋‹ค.

// CustomEvent ์ƒ์„ฑ
const catFound = new CustomEvent("animalfound", {
  detail: {
    name: "cat",
  },
});
const dogFound = new CustomEvent("animalfound", {
  detail: {
    name: "dog",
  },
});
 
// ์ ํ•ฉํ•œ ์ด๋ฒคํŠธ ์ˆ˜์‹ ๊ธฐ ๋ถ€์ฐฉ
obj.addEventListener("animalfound", (e) => console.log(e.detail.name));
 
// ์ด๋ฒคํŠธ ๋ฐœ์†ก
obj.dispatchEvent(catFound);
obj.dispatchEvent(dogFound);
 
// ์ฝ˜์†”์— "cat"๊ณผ "dog"๊ฐ€ ๊ธฐ๋ก๋จ
 

์ด๋Ÿฐ ์‹์œผ๋กœ Custom Event์— ๋Œ€ํ•ด์„œ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ƒ์„ฑํ•œ ๊ฐ์ฒด๋ฅผ dispatchEvenet๋ฅผ ํ†ตํ•ด ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์†กํ•˜๋ฉด ํ•ด๋‹น ์ปค์Šคํ…€ ์ด๋ฒคํŠธ๋ฅผ ๊ตฌ๋…ํ•œ ๊ณณ์— ํ•ด๋‹น ์ด๋ฒคํŠธ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๋ฐœํ–‰-๊ตฌ๋… ํŒจํ„ด์„ ๋‹ฎ์•˜๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์—ฌ์ญค๋ณด๊ณ  ์‹ถ์€ ๊ฒƒ์€ ์ด๋Ÿฌํ•œ ์ปค์Šคํ…€ ์ด๋ฒคํŠธ๊ฐ€ ํ˜„์—…์—์„œ ๋งŽ์ด ์“ฐ์ด๋Š”์ง€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค. ์ €๋Š” ์ด์ œ๊นŒ์ง€ ํ•œ๋ฒˆ๋„ ์‚ฌ์šฉํ•ด๋ณธ ์ ์ด ์—†์–ด์„œ ์ƒ์†Œํ•œ ๊ฐœ๋…์ด๊ธฐ๋„ ํ•˜์ง€๋งŒ, ๋ฐœํ–‰-๊ตฌ๋…๊ณผ ๋น„์Šทํ•œ ํŒจํ„ด์„ ํ†ตํ•ด ์ „์—ญ์—์„œ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์žฅ๋ฐ”๊ตฌ๋‹ˆ๊ฐ™์€ ๊ธฐ๋Šฅ๊ณผ ๊ฐ™์€ ๊ณณ์—์„œ ์œ ์šฉํ•  ๊ฒƒ ๊ฐ™๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ํ˜„์—…์—์„œ๋Š” ์ด๋ฅผ ํ™œ์šฉํ•˜๋Š”์ง€, ํ•œ๋‹ค๋ฉด ์–ด๋–ค ์‹์œผ๋กœ ํ™œ์šฉํ•˜๋Š”์ง€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค!

๋ฒŒ์จ ๋งˆ์ง€๋ง‰ ๋ฆฌ๋ทฐ๋„ค์š”,,๐Ÿฅฒ ์ด์ œ๊นŒ์ง€ ์ œ๊ฐ€ ๊ผผ๊ผผํ•˜๊ฒŒ ์ƒ๊ฐํ•˜์ง€ ๋ชปํ–ˆ๋˜ ๊ฒƒ๋“ค์„ ๋งŽ์ด ์ง‘์–ด์ฃผ์…”์„œ ์ €๋งŒ์˜ ์•ˆํ‹ฐํŒจํ„ด์„ ๋ฐœ๊ฒฌํ•  ์ˆ˜๋„ ์žˆ์—ˆ๋˜ ์‹œ๊ฐ„์ด์—ˆ๋˜ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ํ•ญ์ƒ ์ •์„ฑ๋“ค์—ฌ ๋ฆฌ๋ทฐํ•ด์ฃผ์…”์„œ ์ •๋ง ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!