useReducer๋Š” ์ปดํฌ๋„ŒํŠธ์— reducer๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” React Hook์ž…๋‹ˆ๋‹ค.

const [state, dispatch] = useReducer(reducer, initialArg, init?)

๋ ˆํผ๋Ÿฐ์Šค

useReducer(reducer, initialArg, init?)

useReducer๋ฅผ ์ปดํฌ๋„ŒํŠธ์˜ ์ตœ์ƒ์œ„์— ํ˜ธ์ถœํ•˜๊ณ , reducer๋ฅผ ์ด์šฉํ•ด state๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

import { useReducer } from 'react';

function reducer(state, action) {
// ...
}

function MyComponent() {
const [state, dispatch] = useReducer(reducer, { age: 42 });
// ...

์•„๋ž˜์—์„œ ๋” ๋งŽ์€ ์˜ˆ์‹œ๋ฅผ ํ™•์ธํ•˜์„ธ์š”.

๋งค๊ฐœ๋ณ€์ˆ˜

  • reducer: state๊ฐ€ ์–ด๋–ป๊ฒŒ ์—…๋ฐ์ดํŠธ ๋˜๋Š”์ง€ ์ง€์ •ํ•˜๋Š” ๋ฆฌ๋“€์„œ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. ๋ฆฌ๋“€์„œ ํ•จ์ˆ˜๋Š” ๋ฐ˜๋“œ์‹œ ์ˆœ์ˆ˜ ํ•จ์ˆ˜์—ฌ์•ผ ํ•˜๋ฉฐ, state์™€ action์„ ์ธ์ˆ˜๋กœ ๋ฐ›์•„์•ผ ํ•˜๊ณ , ๋‹ค์Œ state๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. state์™€ action์—๋Š” ๋ชจ๋“  ๋ฐ์ดํ„ฐ ํƒ€์ž…์ด ํ• ๋‹น๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • initialArg: ์ดˆ๊ธฐ state๊ฐ€ ๊ณ„์‚ฐ๋˜๋Š” ๊ฐ’์ž…๋‹ˆ๋‹ค. ๋ชจ๋“  ๋ฐ์ดํ„ฐ ํƒ€์ž…์ด ํ• ๋‹น๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ดˆ๊ธฐ state๊ฐ€ ์–ด๋–ป๊ฒŒ ๊ณ„์‚ฐ๋˜๋Š”์ง€๋Š” ๋‹ค์Œ init ์ธ์ˆ˜์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง‘๋‹ˆ๋‹ค.
  • ์„ ํƒ์‚ฌํ•ญ init: ์ดˆ๊ธฐ state๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๊ฐ€ ์ธ์ˆ˜์— ํ• ๋‹น๋˜์ง€ ์•Š์œผ๋ฉด ์ดˆ๊ธฐ state๋Š” initialArg๋กœ ์„ค์ •๋ฉ๋‹ˆ๋‹ค. ํ• ๋‹น๋˜์—ˆ๋‹ค๋ฉด ์ดˆ๊ธฐ state๋Š” init(initialArg)๋ฅผ ํ˜ธ์ถœํ•œ ๊ฒฐ๊ณผ๊ฐ€ ํ• ๋‹น๋ฉ๋‹ˆ๋‹ค.

๋ฐ˜ํ™˜๊ฐ’

useReducer๋Š” 2๊ฐœ์˜ ์—˜๋ฆฌ๋จผํŠธ๋กœ ๊ตฌ์„ฑ๋œ ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

  1. ํ˜„์žฌ state. ์ฒซ๋ฒˆ์งธ ๋ Œ๋”๋ง์—์„œ์˜ state๋Š” init(initialArg) ๋˜๋Š” initialArg๋กœ ์„ค์ •๋ฉ๋‹ˆ๋‹ค (init์ด ์—†์„ ๊ฒฝ์šฐ initialArg๋กœ ์„ค์ •๋ฉ๋‹ˆ๋‹ค).
  2. dispatch ํ•จ์ˆ˜. dispatch๋Š” state๋ฅผ ์ƒˆ๋กœ์šด ๊ฐ’์œผ๋กœ ์—…๋ฐ์ดํŠธํ•˜๊ณ  ๋ฆฌ๋ Œ๋”๋ง์„ ์ผ์œผํ‚ต๋‹ˆ๋‹ค.

์ฃผ์˜ ์‚ฌํ•ญ

  • useReducer๋Š” Hook์ด๋ฏ€๋กœ ์ปดํฌ๋„ŒํŠธ์˜ ์ตœ์ƒ์œ„ ๋˜๋Š” ์ปค์Šคํ…€ Hook์—์„œ๋งŒ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ˜๋ณต๋ฌธ์ด๋‚˜ ์กฐ๊ฑด๋ฌธ์—์„œ๋Š” ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์ƒˆ๋กœ์šด ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ถ”์ถœํ•˜๊ณ  ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๋กœ state๋ฅผ ์˜ฎ๊ฒจ์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • Strict Mode์—์„œ๋Š” ์šฐ์—ฐํ•œ ๋น„์ˆœ์ˆ˜์„ฑ์„ ์ฐพ์•„๋‚ด๊ธฐ ์œ„ํ•ด reducer์™€ init ํ•จ์ˆ˜๋ฅผ ๋‘๋ฒˆ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋งŒ ํ•œ์ •๋œ ๋™์ž‘์ด๋ฉฐ, ๋ฐฐํฌ ๋ชจ๋“œ์—๋Š” ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. reducer์™€ init ํ•จ์ˆ˜๊ฐ€ ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋ผ๋ฉด(๊ทธ๋ž˜์•ผ๋งŒ ํ•˜๋“ฏ์ด) ๋กœ์ง์— ์–ด๋– ํ•œ ์˜ํ–ฅ๋„ ๋ฏธ์น˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ˜ธ์ถœ ์ค‘ ํ•˜๋‚˜์˜ ๊ฒฐ๊ณผ๋Š” ๋ฌด์‹œํ•ฉ๋‹ˆ๋‹ค.

dispatch ํ•จ์ˆ˜

useReducer์— ์˜ํ•ด ๋ฐ˜ํ™˜๋˜๋Š” dispatch ํ•จ์ˆ˜๋Š” state๋ฅผ ์ƒˆ๋กœ์šด ๊ฐ’์œผ๋กœ ์—…๋ฐ์ดํŠธํ•˜๊ณ  ๋ฆฌ๋ Œ๋”๋ง์„ ์ผ์œผํ‚ต๋‹ˆ๋‹ค. dispatch์˜ ์œ ์ผํ•œ ์ธ์ˆ˜๋Š” action์ž…๋‹ˆ๋‹ค.

const [state, dispatch] = useReducer(reducer, { age: 42 });

function handleClick() {
dispatch({ type: 'incremented_age' });
// ...

React๋Š” ํ˜„์žฌ state์™€ dispatch๋ฅผ ํ†ตํ•ด ์ „๋‹ฌ๋œ action์„ ์ œ๊ณต๋ฐ›์•„ ํ˜ธ์ถœ๋œ reducer์˜ ๋ฐ˜ํ™˜๊ฐ’์„ ํ†ตํ•ด ๋‹ค์Œ state๊ฐ’์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

๋งค๊ฐœ๋ณ€์ˆ˜

  • action: ์‚ฌ์šฉ์ž์— ์˜ํ•ด ์ˆ˜ํ–‰๋œ ํ™œ๋™์ž…๋‹ˆ๋‹ค. ๋ชจ๋“  ๋ฐ์ดํ„ฐ ํƒ€์ž…์ด ํ• ๋‹น๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ปจ๋ฒค์…˜์— ์˜ํ•ด action์€ ์ผ๋ฐ˜์ ์œผ๋กœ action์„ ์ •์˜ํ•˜๋Š” type ํ”„๋กœํผํ‹ฐ์™€ ์ถ”๊ฐ€์ ์ธ ์ •๋ณด๋ฅผ ํ‘œํ˜„ํ•˜๋Š” ๊ธฐํƒ€ ํ”„๋กœํผํ‹ฐ๋ฅผ ํฌํ•จํ•œ ๊ฐ์ฒด๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค.

๋ฐ˜ํ™˜๊ฐ’

dispatch ํ•จ์ˆ˜๋Š” ์–ด๋–ค ๊ฐ’๋„ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ฃผ์˜ ์‚ฌํ•ญ

  • dispatch ํ•จ์ˆ˜๋Š” ์˜ค์ง ๋‹ค์Œ ๋ Œ๋”๋ง์— ์‚ฌ์šฉํ•  state ๋ณ€์ˆ˜๋งŒ ์—…๋ฐ์ดํŠธ ํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ dispatch ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ ์งํ›„์— state ๋ณ€์ˆ˜๋ฅผ ์ฝ๋Š”๋‹ค๋ฉด ํ˜ธ์ถœ ์ด์ „์˜ ์ตœ์‹ ํ™”๋˜์ง€ ์•Š์€ ๊ฐ’์„ ์ฐธ์กฐํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

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

  • React๋Š” state์˜ ์—…๋ฐ์ดํŠธ๋ฅผ batchํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์˜ ๋ชจ๋“  ์ฝ”๋“œ๊ฐ€ ์ˆ˜ํ–‰๋˜๊ณ  set ํ•จ์ˆ˜๊ฐ€ ๋ชจ๋‘ ํ˜ธ์ถœ๋œ ํ›„์— ํ™”๋ฉด์„ ์—…๋ฐ์ดํŠธ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ํ•˜๋‚˜์˜ ์ด๋ฒคํŠธ์— ๋ฆฌ๋ Œ๋”๋ง์ด ์—ฌ๋Ÿฌ๋ฒˆ ์ผ์–ด๋‚˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค. DOM ์ ‘๊ทผ ๋“ฑ ์ด๋ฅธ ํ™”๋ฉด ์—…๋ฐ์ดํŠธ๋ฅผ ๊ฐ•์ œํ•ด์•ผํ•  ํŠน์ˆ˜ํ•œ ์ƒํ™ฉ์ด ์žˆ์„ ๊ฒฝ์šฐ flushSync๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


์‚ฌ์šฉ๋ฒ•

์ปดํฌ๋„ŒํŠธ์— reducer ์ถ”๊ฐ€ํ•˜๊ธฐ

state๋ฅผ reducer๋กœ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด useReducer๋ฅผ ์ปดํฌ๋„ŒํŠธ์˜ ์ตœ์ƒ๋‹จ์—์„œ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

import { useReducer } from 'react';

function reducer(state, action) {
// ...
}

function MyComponent() {
const [state, dispatch] = useReducer(reducer, { age: 42 });
// ...

useReducer๋Š” ์ •ํ™•ํžˆ 2๊ฐœ์˜ ํ•ญ๋ชฉ์ด ํฌํ•จ๋œ ๋ฐฐ์—ด ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

  1. state ๋ณ€์ˆ˜์˜ ํ˜„์žฌ state. ์ตœ์ดˆ์—๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ œ๊ณตํ•œ ์ดˆ๊ธฐ state๋กœ ์ดˆ๊ธฐํ™”๋ฉ๋‹ˆ๋‹ค.
  2. dispatch ํ•จ์ˆ˜. ์ƒํ˜ธ์ž‘์šฉ์— ๋Œ€์‘ํ•˜์—ฌ state๋ฅผ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.

ํ™”๋ฉด์„ ์—…๋ฐ์ดํŠธํ•˜๋ ค๋ฉด ์‚ฌ์šฉ์ž๊ฐ€ ์ˆ˜ํ–‰ํ•œ ํ™œ๋™์„ ์˜๋ฏธํ•˜๋Š” action ๊ฐ์ฒด๋ฅผ ์ธ์ˆ˜๋กœํ•˜์—ฌ dispatch ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์„ธ์š”.

function handleClick() {
dispatch({ type: 'incremented_age' });
}

React๋Š” ํ˜„์žฌ state์™€ action์„ reducer ํ•จ์ˆ˜๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. reducer๋Š” ๋‹ค์Œ state๋ฅผ ๊ณ„์‚ฐํ•œ ํ›„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. React๋Š” ๋‹ค์Œ state๋ฅผ ์ €์žฅํ•œ ๋’ค์— ์ปดํฌ๋„ŒํŠธ์™€ ํ•จ๊ป˜ ๋ Œ๋”๋ง ํ•˜๊ณ  UI๋ฅผ ์—…๋ฐ์ดํŠธ ํ•ฉ๋‹ˆ๋‹ค.

import { useReducer } from 'react';

function reducer(state, action) {
  if (action.type === 'incremented_age') {
    return {
      age: state.age + 1
    };
  }
  throw Error('Unknown action.');
}

export default function Counter() {
  const [state, dispatch] = useReducer(reducer, { age: 42 });

  return (
    <>
      <button onClick={() => {
        dispatch({ type: 'incremented_age' })
      }}>
        Increment age
      </button>
      <p>Hello! You are {state.age}.</p>
    </>
  );
}

useReducer๋Š” useState์™€ ๋งค์šฐ ์œ ์‚ฌํ•˜์ง€๋งŒ, state ์—…๋ฐ์ดํŠธ ๋กœ์ง์„ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์—์„œ ์ปดํฌ๋„ŒํŠธ ์™ธ๋ถ€์˜ ๋‹จ์ผํ•จ์ˆ˜๋กœ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ฐจ์ด์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ž์„ธํ•œ ์‚ฌํ•ญ์€ useState์™€ useReducer ๋น„๊ตํ•˜๊ธฐ๋ฅผ ์ฝ์–ด๋ณด์„ธ์š”.


reducer ํ•จ์ˆ˜ ์ž‘์„ฑํ•˜๊ธฐ

reducer ํ•จ์ˆ˜๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค.

function reducer(state, action) {
// ...
}

์ดํ›„ ๋‹ค์Œ state๋ฅผ ๊ณ„์‚ฐํ•  ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ , ๊ณ„์‚ฐ๋œ state๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๋ณดํ†ต์€ ์ปจ๋ฒค์…˜์— ๋”ฐ๋ผ switch ๋ฌธ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. switch๋Š” ๊ฐ case๋ฅผ ์ด์šฉํ•ด ๋‹ค์Œ state๋ฅผ ๊ณ„์‚ฐํ•˜๊ณ  ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

function reducer(state, action) {
switch (action.type) {
case 'incremented_age': {
return {
name: state.name,
age: state.age + 1
};
}
case 'changed_name': {
return {
name: action.nextName,
age: state.age
};
}
}
throw Error('Unknown action: ' + action.type);
}

Actions์€ ๋‹ค์–‘ํ•œ ํ˜•ํƒœ๊ฐ€ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ปจ๋ฒค์…˜์— ๋”ฐ๋ผ ์•ก์…˜์ด ๋ฌด์—‡์ธ์ง€ ์ •์˜ํ•˜๋Š” type ํ”„๋กœํผํ‹ฐ๋ฅผ ํฌํ•จํ•œ ๊ฐ์ฒด๋กœ ์„ ์–ธํ•˜๋Š” ๊ฒƒ์ด ์ผ๋ฐ˜์ ์ž…๋‹ˆ๋‹ค. type์€ reducer๊ฐ€ ๋‹ค์Œ state๋ฅผ ๊ณ„์‚ฐํ•˜๋Š”๋ฐ ํ•„์š”ํ•œ ์ตœ์†Œํ•œ์˜ ์ •๋ณด๋ฅผ ํฌํ•จํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

function Form() {
const [state, dispatch] = useReducer(reducer, { name: 'Taylor', age: 42 });

function handleButtonClick() {
dispatch({ type: 'incremented_age' });
}

function handleInputChange(e) {
dispatch({
type: 'changed_name',
nextName: e.target.value
});
}
// ...

action type ์ด๋ฆ„์€ ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ ์ง€์—ญ์ ์ž…๋‹ˆ๋‹ค. ๊ฐ action์€ ๋‹จ์ผ ์ƒํ˜ธ์ž‘์šฉ์„ ์„ค๋ช…ํ•˜๋ฉฐ, ๋ฐ์ดํ„ฐ์— ์—ฌ๋Ÿฌ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ดˆ๋ž˜ํ•˜๋”๋ผ๋„ ํ•˜๋‚˜์˜ ์ƒํ˜ธ์ž‘์šฉ๋งŒ์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. state์˜ ํ˜•ํƒœ๋Š” ์ž„์˜์ ์ด์ง€๋งŒ, ์ผ๋ฐ˜์ ์œผ๋กœ ๊ฐ์ฒด๋‚˜ ๋ฐฐ์—ด์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ž์„ธํ•œ ๋‚ด์šฉ์€ state ๋กœ์ง์„ reducer๋กœ ์ž‘์„ฑํ•˜๊ธฐ๋ฅผ ์ฝ์–ด๋ณด์„ธ์š”.

์ฃผ์˜ํ•˜์„ธ์š”!

state๋Š” ์ฝ๊ธฐ ์ „์šฉ์ž…๋‹ˆ๋‹ค. state์˜ ๊ฐ์ฒด๋‚˜ ๋ฐฐ์—ด์„ ๋ณ€๊ฒฝํ•˜์ง€ ๋งˆ์„ธ์š”!

function reducer(state, action) {
switch (action.type) {
case 'incremented_age': {
// ๐Ÿšฉ Don't mutate an object in state like this:
state.age = state.age + 1;
return state;
}

๋Œ€์‹  reducer์—์„œ ์ƒˆ๋กœ์šด ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜์„ธ์š”.

function reducer(state, action) {
switch (action.type) {
case 'incremented_age': {
// โœ… Instead, return a new object
return {
...state,
age: state.age + 1
};
}

์ž์„ธํ•œ ๋‚ด์šฉ์„ ๊ณต๋ถ€ํ•˜์‹œ๋ ค๋ฉด ๊ฐ์ฒด State ์—…๋ฐ์ดํŠธํ•˜๊ธฐ์™€ ๋ฐฐ์—ด State ์—…๋ฐ์ดํŠธํ•˜๊ธฐ๋ฅผ ์ฝ์–ด๋ณด์„ธ์š”.

๊ธฐ๋ณธ์ ์ธ useReducer ์˜ˆ์ œ

์˜ˆ์ œ 1 of 3:
ํผ (๊ฐ์ฒด)

์ด ์˜ˆ์ œ์—์„œ๋Š” reducer๋ฅผ ์ด์šฉํ•ด name๊ณผ age ํ•„๋“œ๋ฅผ ๊ฐ€์ง„ ๊ฐ์ฒด๋ฅผ state๋กœ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

import { useReducer } from 'react';

function reducer(state, action) {
  switch (action.type) {
    case 'incremented_age': {
      return {
        name: state.name,
        age: state.age + 1
      };
    }
    case 'changed_name': {
      return {
        name: action.nextName,
        age: state.age
      };
    }
  }
  throw Error('Unknown action: ' + action.type);
}

const initialState = { name: 'Taylor', age: 42 };

export default function Form() {
  const [state, dispatch] = useReducer(reducer, initialState);

  function handleButtonClick() {
    dispatch({ type: 'incremented_age' });
  }

  function handleInputChange(e) {
    dispatch({
      type: 'changed_name',
      nextName: e.target.value
    }); 
  }

  return (
    <>
      <input
        value={state.name}
        onChange={handleInputChange}
      />
      <button onClick={handleButtonClick}>
        Increment age
      </button>
      <p>Hello, {state.name}. You are {state.age}.</p>
    </>
  );
}


์ดˆ๊ธฐ state ์žฌ์ƒ์„ฑ ๋ฐฉ์ง€ํ•˜๊ธฐ

React๋Š” ์ดˆ๊ธฐ state๋ฅผ ์ €์žฅํ•œ ํ›„, ๋‹ค์Œ ๋ Œ๋”๋ง์—์„œ๋Š” ์ด๋ฅผ ๋ฌด์‹œํ•ฉ๋‹ˆ๋‹ค.

function createInitialState(username) {
// ...
}

function TodoList({ username }) {
const [state, dispatch] = useReducer(reducer, createInitialState(username));
// ...

createInitialState(username)์˜ ๋ฐ˜ํ™˜๊ฐ’์ด ์ดˆ๊ธฐ ๋ Œ๋”๋ง์—๋งŒ ์‚ฌ์šฉ๋˜๋”๋ผ๋„ ํ•จ์ˆ˜๋Š” ๋งค ๋ Œ๋”๋ง๋งˆ๋‹ค ํ˜ธ์ถœ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ•จ์ˆ˜๊ฐ€ ํฐ ๋ฐฐ์—ด์ด๋‚˜ ๋ฌด๊ฑฐ์šด ์—ฐ์‚ฐ์„ ๋‹ค๋ฃฐ ๊ฒฝ์šฐ์—๋Š” ์„ฑ๋Šฅ์ƒ ๋‚ญ๋น„๊ฐ€ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ๋Š” useReducer์˜ 3๋ฒˆ์งธ ์ธ์ˆ˜์— ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜ ๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค.

function createInitialState(username) {
// ...
}

function TodoList({ username }) {
const [state, dispatch] = useReducer(reducer, username, createInitialState);
// ...

createInitialState()์ฒ˜๋Ÿผ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด์„œ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, createInitialState ํ•จ์ˆ˜ ์ž์ฒด๋ฅผ ์ „๋‹ฌํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ๊ธฐ์–ตํ•˜์„ธ์š”. ์ด ๋ฐฉ๋ฒ•์„ ์ด์šฉํ•˜๋ฉด ์ดˆ๊ธฐํ™” ์ดํ›„์— ์ดˆ๊ธฐ state๊ฐ€ ๋‹ค์‹œ ์ƒ์„ฑ๋˜๋Š” ์ผ์€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์œ„์˜ ์˜ˆ์ œ์—์„œ๋Š” createInitialState ํ•จ์ˆ˜๊ฐ€ username์„ ์ธ์ˆ˜๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜๊ฐ€ ์ดˆ๊ธฐ state๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ๊ฒƒ์— ์–ด๋–ค ์ธ์ˆ˜๋„ ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค๋ฉด, useReducer์˜ ๋‘๋ฒˆ์งธ ์ธ์ˆ˜์— null์„ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ดˆ๊ธฐํ™” ํ•จ์ˆ˜๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ๊ณผ ์ดˆ๊ธฐ state๋ฅผ ์ง์ ‘ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์˜ ์ฐจ์ด์ 

์˜ˆ์ œ 1 of 2:
์ดˆ๊ธฐํ™” ํ•จ์ˆ˜ ์ „๋‹ฌ

์ด ์˜ˆ์ œ์—์„œ๋Š” ์ดˆ๊ธฐํ™” ๋‹จ๊ณ„์—์„œ๋งŒ ๋™์ž‘ํ•˜๋Š” ํ•จ์ˆ˜์ธ createInitialState๋ฅผ ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ์ธํ’‹์— ์ž…๋ ฅ ํ•  ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ๋ฆฌ๋ Œ๋”๋ง ์ƒํ™ฉ ๋“ฑ์—์„œ๋Š” ํ˜ธ์ถœ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

import { useReducer } from 'react';

function createInitialState(username) {
  const initialTodos = [];
  for (let i = 0; i < 50; i++) {
    initialTodos.push({
      id: i,
      text: username + "'s task #" + (i + 1)
    });
  }
  return {
    draft: '',
    todos: initialTodos,
  };
}

function reducer(state, action) {
  switch (action.type) {
    case 'changed_draft': {
      return {
        draft: action.nextDraft,
        todos: state.todos,
      };
    };
    case 'added_todo': {
      return {
        draft: '',
        todos: [{
          id: state.todos.length,
          text: state.draft
        }, ...state.todos]
      }
    }
  }
  throw Error('Unknown action: ' + action.type);
}

export default function TodoList({ username }) {
  const [state, dispatch] = useReducer(
    reducer,
    username,
    createInitialState
  );
  return (
    <>
      <input
        value={state.draft}
        onChange={e => {
          dispatch({
            type: 'changed_draft',
            nextDraft: e.target.value
          })
        }}
      />
      <button onClick={() => {
        dispatch({ type: 'added_todo' });
      }}>Add</button>
      <ul>
        {state.todos.map(item => (
          <li key={item.id}>
            {item.text}
          </li>
        ))}
      </ul>
    </>
  );
}


ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ…

dispatch๋กœ action์„ ํ˜ธ์ถœํ•ด๋„ ์˜ค๋ž˜๋œ state ๊ฐ’์ด ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.

dispatch ํ•จ์ˆ˜์˜ ํ˜ธ์ถœ์€ ํ˜„์žฌ ๋™์ž‘ํ•˜๊ณ  ์žˆ๋Š” ์ฝ”๋“œ์˜ state๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

function handleClick() {
console.log(state.age); // 42

dispatch({ type: 'incremented_age' }); // Request a re-render with 43
console.log(state.age); // Still 42!

setTimeout(() => {
console.log(state.age); // Also 42!
}, 5000);
}

์ด๋Ÿฌํ•œ ํ˜„์ƒ์€ State๊ฐ€ ์Šค๋ƒ…์ƒท์œผ๋กœ์„œ ์‚ฌ์šฉ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ผ์–ด๋‚ฉ๋‹ˆ๋‹ค. state๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋ฉด ์ƒˆ๋กœ์šด state๋ฅผ ์ด์šฉํ•œ ๋˜ ๋‹ค๋ฅธ ๋ Œ๋”๋ง์ด ์š”์ฒญ๋˜์ง€๋งŒ, ์ด๋ฏธ ์‹คํ–‰์ค‘์ธ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ๋‚ด์˜ state ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋ณ€์ˆ˜์—๋Š” ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋งŒ์•ฝ ๋‹ค์Œ state ๊ฐ’์„ ์•Œ๊ณ  ์‹ถ๋‹ค๋ฉด, reducer ํ•จ์ˆ˜๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•ด์„œ ๋‹ค์Œ ๊ฐ’์„ ๊ณ„์‚ฐํ•ด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const action = { type: 'incremented_age' };
dispatch(action);

const nextState = reducer(state, action);
console.log(state); // { age: 42 }
console.log(nextState); // { age: 43 }

dispatch๋กœ action์„ ํ˜ธ์ถœํ•ด๋„ ํ™”๋ฉด์ด ์—…๋ฐ์ดํŠธ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

React๋Š” ์ด์ „ state์™€ ๋‹ค์Œ state๋ฅผ ๋น„๊ตํ–ˆ์„ ๋•Œ, ๊ฐ’์ด ์ผ์น˜ํ•œ๋‹ค๋ฉด ์—…๋ฐ์ดํŠธ๊ฐ€ ๋ฌด์‹œ๋ฉ๋‹ˆ๋‹ค. ๋น„๊ต๋Š” Object.is๋ฅผ ํ†ตํ•ด ์ด๋ฃจ์–ด์ง‘๋‹ˆ๋‹ค. ์ด๋Ÿฐ ํ˜„์ƒ์€ ๋ณดํ†ต ๊ฐ์ฒด๋‚˜ ๋ฐฐ์—ด์˜ state๋ฅผ ์ง์ ‘์ ์œผ๋กœ ์ˆ˜์ •ํ–ˆ์„ ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

function reducer(state, action) {
switch (action.type) {
case 'incremented_age': {
// ๐Ÿšฉ Wrong: mutating existing object
state.age++;
return state;
}
case 'changed_name': {
// ๐Ÿšฉ Wrong: mutating existing object
state.name = action.nextName;
return state;
}
// ...
}
}

React๋Š” ๊ธฐ์กด์˜ state ๊ฐ์ฒด๊ฐ€ mutation๋œ ์ƒํƒœ๋กœ ๋ฐ˜ํ™˜๋œ๋‹ค๋ฉด ์—…๋ฐ์ดํŠธ๋ฅผ ๋ฌด์‹œํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ํ˜„์ƒ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๊ฐ์ฒด๋‚˜ ๋ฐฐ์—ด์„ mutation์‹œํ‚ค์ง€ ์•Š๊ณ  ๊ฐ์ฒด state๋ฅผ ๋ณ€๊ฒฝํ•˜๊ฑฐ๋‚˜ ๋ฐฐ์—ด state๋ฅผ ๋ณ€๊ฒฝํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

function reducer(state, action) {
switch (action.type) {
case 'incremented_age': {
// โœ… Correct: creating a new object
return {
...state,
age: state.age + 1
};
}
case 'changed_name': {
// โœ… Correct: creating a new object
return {
...state,
name: action.nextName
};
}
// ...
}
}

reducer์˜ state ์ผ๋ถ€๊ฐ€ dispatch๋œ ์ดํ›„์— undefined๊ฐ€ ํ• ๋‹น๋ฉ๋‹ˆ๋‹ค.

๊ฐ๊ฐ์˜ case๊ฐ€ ์ƒˆ๋กœ์šด state๋ฅผ ๋ฐ˜ํ™˜ํ•  ๋•Œ ๊ธฐ์กด์— ์žˆ๋˜ ํ•„๋“œ๋ฅผ ๋ชจ๋‘ ๋ณต์‚ฌํ•˜๋Š”์ง€ ํ™•์ธํ•ด๋ณด์„ธ์š”.

function reducer(state, action) {
switch (action.type) {
case 'incremented_age': {
return {
...state, // Don't forget this!
age: state.age + 1
};
}
// ...

์œ„์˜ ์ฝ”๋“œ์—์„œ๋Š” ...state๊ฐ€ ์—†๋‹ค๋ฉด ๋‹ค์Œ state๋Š” ์˜ค๋กœ์ง€ age ํ•„๋“œ๋งŒ ํฌํ•จํ•˜๊ฑฐ๋‚˜, ์•„๋ฌด๊ฒƒ๋„ ํฌํ•จํ•˜์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.


reducer์˜ ๋ชจ๋“  state๊ฐ€ dispatch๊ฐ€ ์ด๋ฃจ์–ด ์ง„ ํ›„ undefined๊ฐ€ ํ• ๋‹น๋ฉ๋‹ˆ๋‹ค.

state์— ์˜ˆ๊ธฐ์น˜ ์•Š์€ undefined๊ฐ€ ํ• ๋‹น๋˜๊ณ  ์žˆ๋‹ค๋ฉด case ์ค‘ ํ•˜๋‚˜์— return์ด ๋ˆ„๋ฝ๋˜์—ˆ๊ฑฐ๋‚˜ action์˜ ํƒ€์ž…์ด case์™€ ์ง์ง€์–ด์ง€์ง€ ์•Š์•˜์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด์œ ๋ฅผ ์ฐพ๊ธฐ ์œ„ํ•ด switch๋ฌธ ๋ฐ–์—์„œ ์—๋Ÿฌ๋ฅผ throw ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

function reducer(state, action) {
switch (action.type) {
case 'incremented_age': {
// ...
}
case 'edited_name': {
// ...
}
}
throw Error('Unknown action: ' + action.type);
}

์ด ์™ธ์—๋„ ์‹ค์ˆ˜๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ๊ฐ™์€ ์ •์  ํƒ€์ž… ์ฒด์ปค๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


โ€œToo many re-rendersโ€ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

Too many re-renders. React limits the number of renders to prevent an infinite loop.๋ผ๋Š” ์—๋Ÿฌ ๋ฉ”์„ธ์ง€๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ๋Š” ๋ Œ๋”๋ง ๊ณผ์ •์—์„œ dispatch๊ฐ€ ์‹คํ–‰๋  ๋•Œ ์ด๋Ÿฌํ•œ ์ผ์ด ์ผ์–ด๋‚ฉ๋‹ˆ๋‹ค. ๋ Œ๋”๋ง์€ dispatch๋ฅผ ์•ผ๊ธฐํ•˜๊ณ , dispatch๋Š” ๋ Œ๋”๋ง์„ ์•ผ๊ธฐํ•˜๋ฏ€๋กœ ๋ Œ๋”๋ง ๋ฌดํ•œ ๋ฃจํ”„๊ฐ€ ์ผ์–ด๋‚ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ƒํ™ฉ์€ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ž˜๋ชป ํ˜ธ์ถœํ•  ๋•Œ ์ข…์ข… ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

// ๐Ÿšฉ Wrong: calls the handler during render
return <button onClick={handleClick()}>Click me</button>

// โœ… Correct: passes down the event handler
return <button onClick={handleClick}>Click me</button>

// โœ… Correct: passes down an inline function
return <button onClick={(e) => handleClick(e)}>Click me</button>

์˜ค๋ฅ˜์˜ ์›์ธ์„ ์ฐพ์„ ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ์—๋Š” ์–ด๋Š dispatch ํ•จ์ˆ˜์—์„œ ์—๋Ÿฌ๊ฐ€ ์ƒ์„ฑ๋˜๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ์ฝ˜์†”์ฐฝ์˜ ์˜ค๋ฅ˜ ์˜†์— ์žˆ๋Š” ํ™”์‚ดํ‘œ๋ฅผ ํด๋ฆญํ•œ ํ›„ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์Šคํƒ์„ ์ฐพ์•„๋ณด์„ธ์š”.


reducer์™€ ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜๊ฐ€ ๋‘๋ฒˆ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.

React๋Š” ์—„๊ฒฉ ๋ชจ๋“œ์ผ ๋•Œ reducer์™€ ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜๋ฅผ ๋‘๋ฒˆ์”ฉ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ์ด ํ˜„์ƒ์€ ์ฝ”๋“œ ์‹คํ–‰์— ๋ฌธ์ œ๊ฐ€ ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

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

์˜ˆ์‹œ๋กœ, ์•„๋ž˜์˜ ์ˆœ์ˆ˜ํ•˜์ง€ ์•Š์€ reducer ํ•จ์ˆ˜๋Š” state ๋ฐฐ์—ด์— mutation์„ ์ผ์œผํ‚ค๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

function reducer(state, action) {
switch (action.type) {
case 'added_todo': {
// ๐Ÿšฉ Mistake: mutating state
state.todos.push({ id: nextId++, text: action.text });
return state;
}
// ...
}
}

React๋Š” reducer ํ•จ์ˆ˜๋ฅผ ๋‘ ๋ฒˆ ํ˜ธ์ถœํ•˜๋ฏ€๋กœ todo๊ฐ€ ๋‘ ๊ฐœ ์ถ”๊ฐ€๋˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๊ณ , ์ด๋ฅผ ํ†ตํ•ด reducer ํ•จ์ˆ˜ ์ž‘์„ฑ์— ์‹ค์ˆ˜๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์•„๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์‹ค์ˆ˜๋Š” ๋ฐฐ์—ด์„ mutation ํ•˜์ง€ ์•Š๊ณ  ๊ต์ฒดํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ํ†ตํ•ด ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

function reducer(state, action) {
switch (action.type) {
case 'added_todo': {
// โœ… Correct: replacing with new state
return {
...state,
todos: [
...state.todos,
{ id: nextId++, text: action.text }
]
};
}
// ...
}
}

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

์ž์„ธํ•œ ์‚ฌํ•ญ์€ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ˆœ์ˆ˜ํ•˜๊ฒŒ ์œ ์ง€ํ•˜๊ธฐ๋ฅผ ์ฝ์–ด๋ณด์„ธ์š”.