๋ฐ˜์‘ํ˜•

TLDR


  1. ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์— ์ปค๋ง ํ™œ์šฉ
  2. ์ปดํฌ๋„ŒํŠธ ์ฑ…์ž„ ๋ถ„๋ฆฌ
  3. ์กฐ๊ฑด๋ฌธ ๋Œ€์‹  ๊ฐ์ฒด map ์‚ฌ์šฉ
  4. React ๋ผ์ดํ”„์‚ฌ์ดํด ์™ธ๋ถ€์— ๋…๋ฆฝ์ ์ธ ๋ณ€์ˆ˜ ๋ฐฐ์น˜

 

 

4 React Tips


1. ์ปค๋ง ํ™œ์šฉ

user ์ƒํƒœ๋Š” name, surname, address 3๊ฐœ ์†์„ฑ์„ ๊ฐ€์ง€๋ฉฐ, ์ด์— ๋Œ€์‘ํ•˜๋Š” 3๊ฐœ์˜ input ํ•„๋“œ๊ฐ€ ํ•„์š”ํ•˜๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ์—์„  ๊ฐ ํ•„๋“œ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ๋ณ„๋„์˜ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๊ฐ๊ฐ ๋งŒ๋“ค์–ด์„œ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์ง€๋งŒ, ์ด ํ•ธ๋“ค๋Ÿฌ๋“ค์€ value๊ฐ€ ํ• ๋‹ฌ๋  ์†์„ฑ ์ด๋ฆ„๋งŒ ๋‹ค๋ฅผ ๋ฟ ๋‚˜๋จธ์ง€ ๋กœ์ง์ด ๋™์ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ฝ”๋“œ ์ค‘๋ณต์ด ๋ฐœ์ƒํ•˜๊ณ  ์žˆ๋‹ค.

export default function App() {
  const [user, setUser] = useState({
    name: "",
    surname: "",
    address: "",
  });

  const handleNameChange = (e) => {
    setUser((prev) => ({
      ...prev,
      name: e.target.value,
    }));
  };

  const handleSurnameChange = (e) => {
    setUser((prev) => ({
      ...prev,
      surname: e.target.value,
    }));
  };

  const handleAddressChange = (e) => {
    setUser((prev) => ({
      ...prev,
      address: e.target.value,
    }));
  };

  return (
    <>
      <input value={user.name} onChange={handleNameChange} />
      <input value={user.surname} onChange={handleSurnameChange} />
      <input value={user.address} onChange={handleAddressChange} />
    </>
  );
}

 

ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ์ปค๋ง์€ n๊ฐœ์˜ ์ธ์ž๋ฅผ ๋ฐ›๋Š” ๋Œ€์‹  n๊ฐœ์˜ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด์„œ ํ•˜๋‚˜์˜ ์ธ์ž๋งŒ ๋ฐ›๋„๋ก ๋งŒ๋“œ๋Š” ํŒจํ„ด์ด๋‹ค.

currying is the technique of translating a function that takes multiple arguments into a sequence of families of functions, each taking a single argument.

WikiPedia

 

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

export default function App() {
  const [user, setUser] = useState({
    name: "",
    surname: "",
    address: "",
  });

  const handleInputChange = (field) => {
    // ๋ฐ˜ํ™˜๋  ํ•ธ๋“ค๋Ÿฌ
    return (e) => {
      setUser((prev) => ({
        ...prev,
        [field]: e.target.value,
      }));
    };
  };

  return (
    <>
      <input value={user.name} onChange={handleInputChange("name")} />
      <input value={user.surname} onChange={handleInputChange("surname")} />
      <input value={user.address} onChange={handleInputChange("address")} />

      {JSON.stringify(user)}
    </>
  );
}

 

 

2. ์ฑ…์ž„ ๋ถ„๋ฆฌ

1๊ฐœ ์ปดํฌ๋„ŒํŠธ์— ๋„ˆ๋ฌด ๋งŽ์€ ์ฝ”๋“œ ๋ผ์ธ์„ ํฌํ•จํ•˜๋ฉด ์œ ์ง€๋ณด์ˆ˜ํ•˜๊ธฐ ํž˜๋“ค๊ณ  ์ดํ•ดํ•˜๊ธฐ๋„ ์‰ฝ์ง€ ์•Š๋‹ค. ์ด๋Ÿฌํ•œ ์ปดํฌ๋„ŒํŠธ๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ๊ธฐ์ค€์„ ๊ฐ€์ง€๊ณ  ๋…๋ฆฝ์ ์ธ ํ•˜์œ„ ๋ชจ๋“ˆ๋กœ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

 

  • UI ๋ชจ๋“ˆ : ์‹œ๊ฐ์ ์ธ ํ‘œ๋ฉด๋งŒ ๋‹ด๋‹น
  • Logic/Model ๋ชจ๋“ˆ: ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๋งŒ ํฌํ•จ e.g., ์ปค์Šคํ…€ ํ›…
  • Lib ๋ชจ๋“ˆ: ์ปดํฌ๋„ŒํŠธ์— ํ•„์š”ํ•œ ์œ ํ‹ธ๋ฆฌํ‹ฐ ๋ชจ์Œ

 

์•„๋ž˜ ListComponent๋Š” ๋กœ์ปฌ ์ƒํƒœ ๊ด€๋ฆฌ, ๋ฐ์ดํ„ฐ ํŒจ์นญ, ์•„์ดํ…œ ์‚ญ์ œ ๋“ฑ ๋‹ค์–‘ํ•œ ๋กœ์ง์ด ํ˜ผ์žฌ๋˜์–ด ์žˆ๋‹ค. ์ด ์ปดํฌ๋„ŒํŠธ์˜ ๊ด€์‹ฌ์‚ฌ๋ฅผ ๋ถ„๋ฆฌํ•˜์—ฌ ๊ฐœ์„ ํ•ด๋ณด์ž.

export function ListComponent() {
  // ๋กœ์ปฌ ์ƒํƒœ
  const [list, setList] = useState([]);

  // ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜จ ํ›„ ๋กœ์ปฌ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ํ•จ์ˆ˜
  const fetchList = async () => {
    try {
      const resp = await fetch("https://www.url.com/list");
      const data = await resp.json();

      setList(data);
    } catch {
      showAlert({ text: "Something went wrong!" });
    }
  };

  // ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ฒ˜์Œ ๋งˆ์šดํŠธ ๋  ๋•Œ fetch
  useEffect(() => {
    fetchList();
  }, []);

  // ์•„์ดํ…œ ์‚ญ์ œ ๋ฐ ์ƒํƒœ ์—…๋ฐ์ดํŠธ ํ•จ์ˆ˜
  const handleDeleteItem = (id) => {
    return () => {
      try {
        fetch(`https://www.url.com/list/${id}`, {
          method: "DELETE",
        });
        setList((prev) => prev.filter((x) => x.id !== id));
      } catch {
        showAlert({ text: "Something went wrong!" });
      }
    };
  };

  // ๋ฆฌ์ŠคํŠธ ๋ทฐ ๋ Œ๋”๋ง
  return (
    <div className="list-component">
      {list.map(({ id, name }) => (
        <div key={id} className="list-component__item>">
          {/* 30์ž๋ฅผ ์ดˆ๊ณผํ•˜๋Š” ์ด๋ฆ„์€ ... ์ƒ๋žต ๋ถ€ํ˜ธ ์ถ”๊ฐ€ */}
          {name.slice(0, 30) + (name.length > 30 ? "..." : "")}

          <div onClick={handleDeleteItem(id)} className="list-component__icon">
            <DeleteIcon />
          </div>
        </div>
      ))}
    </div>
  );
}

 

๋จผ์ € Model๊ณผ UI ๋ชจ๋“ˆ์—์„œ ์‚ฌ์šฉํ•  ์œ ํ‹ธ๋ฆฌํ‹ฐ ํ•จ์ˆ˜๋ฅผ ๋ถ„๋ฆฌํ•œ๋‹ค.

export async function getList(onSuccess) {
  try {
    const resp = await fetch("https://www.url.com/list");
    const data = await resp.json();

    onSuccess(data);
  } catch {
    showAlert({ text: "Something went wrong!" });
  }
}

export async function deleteListItem(id, onSuccess) {
  try {
    fetch(`https://www.url.com/list/${id}`, {
      method: "DELETE",
    });
    onSuccess();
  } catch {
    showAlert({ text: "Something went wrong!" });
  }
}

export function trimName(name) {
  return name.slice(0, 30) + (name.length > 30 ? "..." : "");
}

 

์ƒํƒœ ๊ด€๋ฆฌ์™€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋ณ„๋„์˜ ์ปค์Šคํ…€ ํ›…์œผ๋กœ ๋ถ„๋ฆฌํ•œ๋‹ค.

export function useList() {
  const [list, setList] = useState([]);

  // id๋ฅผ ๋ฐ›์•„ ์ด๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•ธ๋“ค๋Ÿฌ ๋ฐ˜ํ™˜
  const handleDeleteItem = useCallback((id) => {
    return () => {
      deleteListItem(id, () => {
        setList((prev) => prev.filter((x) => x.id !== id));
      });
    };
  }, []);

  useEffect(() => {
    getList(setList);
  }, []);

  return {
    list,
    handleDeleteItem,
  };
}

 

๋ฆฌ์ŠคํŠธ ์•„์ดํ…œ์„ ๋ณ„๋„์˜ ์ปดํฌ๋„ŒํŠธ๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ UI ๋ชจ๋“ˆ์„ ์ž‘์„ฑํ•œ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๊ฐ ์•„์ดํ…œ์˜ ๋ Œ๋”๋ง ๋กœ์ง์„ ๋…๋ฆฝ์ ์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

export function ListComponentItem({ name, onDelete }) {
  return (
    <div className="list-component__item>">
      {trimName(name)}

      <div onClick={onDelete} className="list-component__icon">
        <DeleteIcon />
      </div>
    </div>
  );
}

 

์œ ํ‹ธ๋ฆฌํ‹ฐ ํ•จ์ˆ˜, ์ปค์Šคํ…€ ํ›…, UI ์ปดํฌ๋„ŒํŠธ ๋“ฑ์˜ ๋ชจ๋“ˆ์„ ํ†ตํ•ฉํ•œ๋‹ค. ์ด๋กœ์จ ์ฝ”๋“œ์˜ ์‘์ง‘๋„(ํ•˜๋‚˜์˜ ๋ชฉ์ ์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•œ ์ž‘์€ ๊ธฐ๋Šฅ๋“ค์„ ์—ฐ๊ด€์„ฑ์— ๋”ฐ๋ผ ๋ชจ๋“ˆ์— ์ž˜ ๋ชจ์•„๋†จ๋Š”์ง€ ์—ฌ๋ถ€)๊ฐ€ ๋†’์•„์ง€๊ณ  ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ๋” ์šฉ์ดํ•ด์กŒ๋‹ค.

export function ListComponent() {
  const { list, handleDeleteItem } = useList();

  return (
    <div className="list-component">
      {list.map(({ id, name }) => (
        <ListComponentItem
          key={id}
          name={name}
          onDelete={handleDeleteItem(id)}
        />
      ))}
    </div>
  );
}

 

 

3. ๊ฐ์ฒด map ์‚ฌ์šฉ

๋ณ€์ˆ˜์˜ ์ƒํƒœ์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋งํ•ด์•ผ ํ•  ๋•Œ, ์กฐ๊ฑด๋ฌธ์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ ์ƒํƒœ์— ๋งž๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ์กฐ๊ฑด๋ฌธ์ด ๋งŽ์•„์งˆ์ˆ˜๋ก ๊ฐ€๋…์„ฑ์ด ํ˜„์ €ํ•˜๊ฒŒ ๋–จ์–ด์ง€๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค. ๊ฐ์ฒด ๋งต(map)์„ ํ™œ์šฉํ•˜๋ฉด ์ฝ”๋“œ๋ฅผ ๋”์šฑ ๊ฐ„๊ฒฐํ•˜๊ณ  ์„ ์–ธ์ ์œผ๋กœ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

function Account({ type }) {
  let Component = UsualAccount;

  if (type === "vip") {
    Component = VipAccount;
  }

  if (type === "moderator") {
    Component = ModeratorAccount;
  }

  if (type === "admin") {
    Component = AdminAccount;
  }

  return (
    <div className="account">
      <Component />
      <AccountStatistics />
    </div>
  );
}

 

์•„๋ž˜์ฒ˜๋Ÿผ ๊ฐ์ฒด ๋งต ACCOUNTS_MAP์„ ์‚ฌ์šฉํ•˜์—ฌ type๊ณผ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งคํ•‘ํ•˜๋ฉด ์กฐ๊ฑด๋ฌธ ์—†์ด๋„ ํ•„์š”ํ•œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค. type์ด ๊ฐ€์ง€๋Š” ์ƒํƒœ์˜ ์ข…๋ฅ˜๊ฐ€ ๋งŽ์•„์งˆ ์ˆ˜๋ก ์ด ๋ฐฉ๋ฒ•์ด ํŠนํžˆ ์œ ์šฉํ•˜๋‹ค.

const ACCOUNTS_MAP = {
  vip: VipAccount,
  usual: UsualAccount,
  admin: AdminAccount,
  moderator: ModeratorAccount,
};

function Account({ type }) {
  const Component = ACCOUNTS_MAP[type];

  return (
    <div className="account">
      <Component />
      <AccountStatistics />
    </div>
  );
}

 

 

4. ๋…๋ฆฝ์ ์ธ ๋ณ€์ˆ˜ ๋ฐฐ์น˜

๋ฆฌ์•กํŠธ์˜ ๋ผ์ดํ”„์‚ฌ์ดํด ๋ฉ”์„œ๋“œ๋ฅผ ํ•„์š”๋กœ ํ•˜์ง€ ์•Š๋Š” ๋กœ์ง์€ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋ง๋  ๋•Œ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ์žฌ์„ ์–ธ๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๊ณ , ์˜์กด์„ฑ์ด ๋” ๋ช…ํ™•ํ•ด์ ธ์„œ ์ฝ”๋“œ ๊ฐ€๋…์„ฑ์ด ์ข‹์•„์ง„๋‹ค.

function useItemsList() {
  const defaultItems = [1, 2, 3, 4, 5];
  const [items, setItems] = useState(defaultItems);

  const toggleArrayItem = (arr, val) => {
    return arr.includes(val) ? arr.filter((el) => el !== val) : [...arr, val];
  };

  const handleToggleItem = (num) => {
    return () => {
      setItems(toggleArrayItem(items, num));
    };
  };

  return {
    items,
    handleToggleItem,
  };
}

 

defaultItems ๋ณ€์ˆ˜์™€ toggleArrayItem ํ•จ์ˆ˜๋Š” ํŠน์ • ์ปดํฌ๋„ŒํŠธ์— ์ข…์†๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋…๋ฆฝ์ ์ธ ์œ ํ‹ธ๋ฆฌํ‹ฐ๋กœ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•„์š”์‹œ ๋‹ค๋ฅธ ๊ณณ์—์„œ ์‰ฝ๊ฒŒ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

const DEFAULT_ITEMS = [1, 2, 3, 4, 5];

const toggleArrayItem = (arr, val) => {
  return arr.includes(val) ? arr.filter((el) => el !== val) : [...arr, val];
};

function useItemsList() {
  const [items, setItems] = useState(DEFAULT_ITEMS);

  const handleToggleItem = (num) => {
    return () => {
      setItems(toggleArrayItem(items, num));
    };
  };

  return {
    items,
    handleToggleItem,
  };
}

 

 

๋ ˆํผ๋Ÿฐ์Šค


 

4 React Tips to Instantly Improve Your Code

Solid knowledge of React is one of the most valuable skills for a front-end developer. Many companies are constantly searching for React…

javascript.plainenglish.io

 

 


๊ธ€ ์ˆ˜์ •์‚ฌํ•ญ์€ ๋…ธ์…˜ ํŽ˜์ด์ง€์— ๊ฐ€์žฅ ๋น ๋ฅด๊ฒŒ ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค. ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”
๋ฐ˜์‘ํ˜•