Front-end/React

[React] Hooks: ์ดˆ๋ณด๋ถ€ํ„ฐ ์ „๋ฌธ๊ฐ€๊นŒ์ง€ ์™„๋ฒฝ ์ •๋ณต ๐Ÿช

xeunnie 2024. 12. 7. 01:00
728x90
๋ฐ˜์‘ํ˜•

React Hooks ํ†บ์•„๋ณด๊ธฐ!๐Ÿช

React Hooks๋Š” 2018๋…„์— ๋„์ž…๋œ ๊ธฐ๋Šฅ์œผ๋กœ, ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ์—์„œ ์ƒํƒœ์™€ ๋ผ์ดํ”„์‚ฌ์ดํด์„ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. Hooks๋Š” React ๊ฐœ๋ฐœ ๋ฐฉ์‹์„ ํ˜์‹ ์ ์œผ๋กœ ๋ณ€ํ™”์‹œ์ผฐ์œผ๋ฉฐ, ์˜ค๋Š˜๋‚  ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํด๋ž˜์Šคํ˜• ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋Œ€์ฒดํ•˜๊ฒŒ ๋œ ํ•ต์‹ฌ ์ด์œ ์ž…๋‹ˆ๋‹ค.

 

์ด๋ฒˆ ๊ธ€์—์„œ๋Š” Hooks์˜ ๊ฐœ๋…๋ถ€ํ„ฐ, ์ฃผ์š” Hooks์™€ ํ™œ์šฉ๋ฒ•, ์ฃผ์˜์‚ฌํ•ญ๊นŒ์ง€ ํญ๋„“๊ฒŒ ๋‹ค๋ค„๋ณผ๊ฒŒ์š”!


Hooks๋ž€ ๋ฌด์—‡์ธ๊ฐ€? ๐Ÿค”

Hooks๋Š” React ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ์—์„œ ์ƒํƒœ ๊ด€๋ฆฌ ๋ฐ ๋ผ์ดํ”„์‚ฌ์ดํด ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.

 

Hooks์˜ ํƒ„์ƒ ๋ฐฐ๊ฒฝ

  • ์ฝ”๋“œ ๊ฐ€๋…์„ฑ: ํด๋ž˜์Šคํ˜• ์ปดํฌ๋„ŒํŠธ๋Š” ๋ณต์žกํ•œ ๋กœ์ง์œผ๋กœ ์ธํ•ด ์ฝ”๋“œ๊ฐ€ ๊ธธ๊ณ  ๊ฐ€๋…์„ฑ์ด ๋–จ์–ด์ง€๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์•˜์Šต๋‹ˆ๋‹ค.
  • ์žฌ์‚ฌ์šฉ์„ฑ ๋ถ€์กฑ: ํด๋ž˜์Šคํ˜• ์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” ๋น„์Šทํ•œ ๋กœ์ง์„ ์žฌ์‚ฌ์šฉํ•˜๊ธฐ ์–ด๋ ต๊ณ , HOC๋‚˜ Render Props ๊ฐ™์€ ๋ณต์žกํ•œ ํŒจํ„ด์ด ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ์˜ ํ•œ๊ณ„: Hooks ์ด์ „์—๋Š” ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ์—์„œ ์ƒํƒœ๋‚˜ ๋ผ์ดํ”„์‚ฌ์ดํด ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์—ˆ์Šต๋‹ˆ๋‹ค.

Hooks์˜ ํŠน์ง•

  1. ์™„์ „ํ•œ ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ ์ง€์›: ํด๋ž˜์Šคํ˜• ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•  ํ•„์š” ์—†์ด ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ์—์„œ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Œ
  2. ์ฝ”๋“œ ์žฌ์‚ฌ์šฉ์„ฑ ์ฆ๊ฐ€: ์ปค์Šคํ…€ Hooks๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋กœ์ง์„ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๊ฒŒ ์ถ”์ถœ ๊ฐ€๋Šฅ
  3. ์ ์ง„์  ๋„์ž…: ๊ธฐ์กด ์ฝ”๋“œ๋ฅผ ์™„์ „ํžˆ ๋ฆฌํŒฉํ† ๋งํ•  ํ•„์š” ์—†์ด ํ•„์š”ํ•œ ๋ถ€๋ถ„์—๋งŒ ๋„์ž… ๊ฐ€๋Šฅ
  4. React 16.8 ์ด์ƒ์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅ: Hooks๋Š” React 16.8 ๋ฒ„์ „๋ถ€ํ„ฐ ์ง€์›

์ฃผ์š” Hooks ๐Ÿ—‚๏ธ

1. useState

  • ์—ญํ• : ์ƒํƒœ ๊ด€๋ฆฌ
  • ์‚ฌ์šฉ๋ฒ•:
import React, { useState } from "react";

function Counter() {
  const [count, setCount] = useState(0);
  const increment = () => setCount(count + 1);
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}
  •  ์žฅ์ :
    • ๊ฐ„๊ฒฐํ•œ ์ƒํƒœ ๊ด€๋ฆฌ
    • ํด๋ž˜์Šคํ˜• ์ปดํฌ๋„ŒํŠธ์˜ this.state์™€ this.setState ๋Œ€์ฒด

 

2. useEffect

  • ์—ญํ• : ์ปดํฌ๋„ŒํŠธ์˜ ๋ผ์ดํ”„์‚ฌ์ดํด ๊ด€๋ฆฌ (componentDidMount, componentDidUpdate, componentWillUnmount ๋Œ€์ฒด)
  • ์‚ฌ์šฉ๋ฒ•:
import React, { useState, useEffect } from "react";

function Timer() {
  const [seconds, setSeconds] = useState(0);
  useEffect(() => {
    const interval = setInterval(() => {
      setSeconds((prev) => prev + 1);
    }, 1000);
    return () => clearInterval(interval); // Cleanup
  }, []); // ๋นˆ ๋ฐฐ์—ด๋กœ ์ฒซ ๋ Œ๋”๋ง ์‹œ 1ํšŒ ์‹คํ–‰
  return <p>Seconds: {seconds}</p>;
}
  • ํŠน์ง•:
    • ์˜์กด์„ฑ ๋ฐฐ์—ด: ์–ด๋–ค ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งŒ ์‹คํ–‰ํ• ์ง€ ์ •์˜
    • Cleanup: ๋ฐ˜ํ™˜๊ฐ’์œผ๋กœ ์ •๋ฆฌ(clean-up) ๋กœ์ง์„ ์ž‘์„ฑ

 

3. useContext

  • ์—ญํ• : Context API๋ฅผ ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ๋ฒ•:
import React, { createContext, useContext } from "react";

const ThemeContext = createContext("light");

function ThemedComponent() {
  const theme = useContext(ThemeContext);
  return <p>Current theme: {theme}</p>;
}

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <ThemedComponent />
    </ThemeContext.Provider>
  );
}
  • ์žฅ์ :
    • ๋ณต์žกํ•œ Consumer ์ปดํฌ๋„ŒํŠธ ํŠธ๋ฆฌ ์—†์ด Context ๊ฐ’์— ์ ‘๊ทผ ๊ฐ€๋Šฅ
    • ์ฝ”๋“œ ๊ฐ€๋…์„ฑ ํ–ฅ์ƒ

 

4. useRef

  • ์—ญํ• : DOM ์š”์†Œ ๋˜๋Š” ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•œ ๊ฐ’์„ ์ฐธ์กฐ
  • ์‚ฌ์šฉ๋ฒ•:
import React, { useRef } from "react";

function TextInputFocus() {
  const inputRef = useRef();
  const focusInput = () => {
    inputRef.current.focus();
  };

  return (
    <div>
      <input ref={inputRef} type="text" />
      <button onClick={focusInput}>Focus Input</button>
    </div>
  );
}
  • ํŠน์ง•:
    • useRef๋Š” ์ƒํƒœ์™€ ๋‹ฌ๋ฆฌ ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜์–ด๋„ ๋ฆฌ๋ Œ๋”๋ง์„ ๋ฐœ์ƒ์‹œํ‚ค์ง€ ์•Š์Œ

 

5. useReducer

  • ์—ญํ• : ๋ณต์žกํ•œ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•œ ๋Œ€์ฒด ์˜ต์…˜ (useState ๋Œ€์ฒด)
  • ์‚ฌ์šฉ๋ฒ•:
import React, { useReducer } from "react";

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case "increment":
      return { count: state.count + 1 };
    case "decrement":
      return { count: state.count - 1 };
    default:
      return state;
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: "increment" })}>+</button>
      <button onClick={() => dispatch({ type: "decrement" })}>-</button>
    </div>
  );
}
  • ์žฅ์ :
    • ์ƒํƒœ ์—…๋ฐ์ดํŠธ ๋กœ์ง์„ ์ปดํฌ๋„ŒํŠธ ์™ธ๋ถ€๋กœ ๋ถ„๋ฆฌ ๊ฐ€๋Šฅ
    • ์ƒํƒœ ์—…๋ฐ์ดํŠธ๊ฐ€ ๋ณต์žกํ•œ ๊ฒฝ์šฐ ์ ํ•ฉ

 

6. useMemo & useCallback

  • ์—ญํ• : ๋ถˆํ•„์š”ํ•œ ์—ฐ์‚ฐ ๋ฐ ํ•จ์ˆ˜ ์žฌ์ƒ์„ฑ์„ ๋ฐฉ์ง€ํ•˜์—ฌ ์„ฑ๋Šฅ ์ตœ์ ํ™”
  • ์‚ฌ์šฉ๋ฒ•:
import React, { useState, useMemo, useCallback } from "react";

function ExpensiveComponent({ compute, number }) {
  console.log("Rendering...");
  return <p>Result: {compute(number)}</p>;
}

function App() {
  const [count, setCount] = useState(0);
  const [value, setValue] = useState(10);

  const compute = useCallback((number) => number * 2, []);
  const expensiveValue = useMemo(() => value * 100, [value]);

  return (
    <div>
      <ExpensiveComponent compute={compute} number={value} />
      <p>Expensive Value: {expensiveValue}</p>
      <button onClick={() => setCount(count + 1)}>Re-render</button>
    </div>
  );
}
  • ํŠน์ง•:
    • useMemo: ๊ฐ’์˜ ์บ์‹ฑ
    • useCallback: ํ•จ์ˆ˜์˜ ์žฌ์ƒ์„ฑ ๋ฐฉ์ง€

Hooks ์‚ฌ์šฉ ์‹œ ์ฃผ์˜์‚ฌํ•ญโš ๏ธ

  1. Hooks๋Š” ์ตœ์ƒ์œ„์—์„œ๋งŒ ํ˜ธ์ถœ
    • ์กฐ๊ฑด๋ฌธ, ๋ฐ˜๋ณต๋ฌธ, ์ค‘์ฒฉ๋œ ํ•จ์ˆ˜ ๋‚ด์—์„œ ํ˜ธ์ถœํ•˜๋ฉด ์•ˆ ๋ฉ๋‹ˆ๋‹ค.
    • React๊ฐ€ Hooks ํ˜ธ์ถœ ์ˆœ์„œ๋ฅผ ๋ณด์žฅํ•˜์ง€ ๋ชปํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
  2. React ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ๋งŒ ์‚ฌ์šฉ
    • ํด๋ž˜์Šคํ˜• ์ปดํฌ๋„ŒํŠธ ๋˜๋Š” ์ผ๋ฐ˜ ํ•จ์ˆ˜์—์„œ๋Š” ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
  3. ์˜์กด์„ฑ ๋ฐฐ์—ด ๊ด€๋ฆฌ
    • useEffect๋‚˜ useMemo์—์„œ ์˜์กด์„ฑ ๋ฐฐ์—ด์„ ์ž˜๋ชป ์„ค์ •ํ•˜๋ฉด ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋™์ž‘์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Hooks๋กœ ์ž‘์„ฑํ•œ ์‹ค๋ฌด ์˜ˆ์ œ ๐Ÿ’ผ

React ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋‹ค์ˆ˜์˜ Hooks๋ฅผ ์กฐํ•ฉํ•ด ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์˜ˆ์ œ: ํ•  ์ผ ๊ด€๋ฆฌ ์•ฑ

import React, { useState, useReducer, useRef } from "react";

function reducer(todos, action) {
  switch (action.type) {
    case "add":
      return [...todos, { id: Date.now(), text: action.payload, done: false }];
    case "toggle":
      return todos.map((todo) =>
        todo.id === action.payload ? { ...todo, done: !todo.done } : todo
      );
    case "delete":
      return todos.filter((todo) => todo.id !== action.payload);
    default:
      return todos;
  }
}

function TodoApp() {
  const [todos, dispatch] = useReducer(reducer, []);
  const [text, setText] = useState("");
  const inputRef = useRef();
  const addTodo = () => {
    if (text.trim()) {
      dispatch({ type: "add", payload: text });
      setText("");
      inputRef.current.focus();
    }
  };

  return (
    <div>
      <input
        ref={inputRef}
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="Add a task"
      />
      <button onClick={addTodo}>Add</button>
      <ul>
        {todos.map((todo) => (
          <li key={todo.id}>
            <span
              style={{ textDecoration: todo.done ? "line-through" : "none" }}
              onClick={() => dispatch({ type: "toggle", payload: todo.id })}
            >
              {todo.text}
            </span>
            <button onClick={() => dispatch({ type: "delete", payload: todo.id })}>
              Delete
            </button>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default TodoApp;

Hooks์˜ ์žฅ์  ์š”์•ฝ ๐Ÿ—‚๏ธ

  • ์ฝ”๋“œ ๊ฐ„๊ฒฐ์„ฑ
  • ๋กœ์ง ์žฌ์‚ฌ์šฉ์„ฑ ์ฆ๊ฐ€
  • ํด๋ž˜์Šคํ˜• ์ปดํฌ๋„ŒํŠธ์˜ ๋ณต์žกํ•œ ํŒจํ„ด ์ œ๊ฑฐ
  • ์ƒํƒœ ๊ด€๋ฆฌ ๋ฐ ์„ฑ๋Šฅ ์ตœ์ ํ™”

 

๐ŸŒท์ „์„ค์˜ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜์–ด๋ด…์‹œ๋‹น! ๐ŸŒท

728x90
๋ฐ˜์‘ํ˜•