useEffect๋Š” ์™ธ๋ถ€ ์‹œ์Šคํ…œ๊ณผ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋™๊ธฐํ™”ํ•˜๋Š” React Hook์ž…๋‹ˆ๋‹ค.

useEffect(setup, dependencies?)

๋ ˆํผ๋Ÿฐ์Šค

useEffect(setup, dependencies?)

์ปดํฌ๋„ŒํŠธ์˜ ์ตœ์ƒ์œ„ ๋ ˆ๋ฒจ์—์„œ useEffect๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ Effect๋ฅผ ์„ ์–ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import { useEffect } from 'react';
import { createConnection } from './chat.js';

function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');

useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, [serverUrl, roomId]);
// ...
}

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

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

  • setup(์„ค์ •): Effect์˜ ๋กœ์ง์ด ํฌํ•จ๋œ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. ์„ค์ • ํ•จ์ˆ˜๋Š” ์„ ํƒ์ ์œผ๋กœ clean up(์ •๋ฆฌ) ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. React๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ DOM์— ์ถ”๊ฐ€๋œ ์ดํ›„์— ์„ค์ • ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์˜์กด์„ฑ์˜ ๋ณ€ํ™”์— ๋”ฐ๋ผ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง์ด ๋˜์—ˆ์„ ๊ฒฝ์šฐ, (์„ค์ • ํ•จ์ˆ˜์— ์ •๋ฆฌ ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ–ˆ์—ˆ๋‹ค๋ฉด) React๋Š” ์ด์ „ ๋ Œ๋”๋ง์— ์‚ฌ์šฉ๋œ ๊ฐ’์œผ๋กœ ์ •๋ฆฌ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•œ ํ›„ ์ƒˆ๋กœ์šด ๊ฐ’์œผ๋กœ ์„ค์ • ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ปดํฌ๋„ŒํŠธ๊ฐ€ DOM์—์„œ ์ œ๊ฑฐ๋œ ๊ฒฝ์šฐ์—๋„ ์ •๋ฆฌ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

  • dependencies ์„ ํƒ์‚ฌํ•ญ : ์„ค์ • ํ•จ์ˆ˜์˜ ์ฝ”๋“œ ๋‚ด๋ถ€์—์„œ ์ฐธ์กฐ๋˜๋Š” ๋ชจ๋“  ๋ฐ˜์‘ํ˜• ๊ฐ’๋“ค์ด ํฌํ•จ๋œ ๋ฐฐ์—ด๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค. ๋ฐ˜์‘ํ˜• ๊ฐ’์—๋Š” props์™€ state, ๋ชจ๋“  ๋ณ€์ˆ˜ ๋ฐ ์ปดํฌ๋„ŒํŠธ body์— ์ง์ ‘์ ์œผ๋กœ ์„ ์–ธ๋œ ํ•จ์ˆ˜๋“ค์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. ๋ฆฐํ„ฐ๊ฐ€ ๋ฆฌ์•กํŠธ ํ™˜๊ฒฝ์— ๋งž๊ฒŒ ์„ค์ •๋˜์–ด ์žˆ์„ ๊ฒฝ์šฐ, ๋ฆฐํ„ฐ๋Š” ๋ชจ๋“  ๋ฐ˜์‘ํ˜• ๊ฐ’๋“ค์ด ์˜์กด์„ฑ์— ์ œ๋Œ€๋กœ ๋ช…์‹œ๋˜์–ด ์žˆ๋Š”์ง€ ๊ฒ€์ฆํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์˜์กด์„ฑ ๋ฐฐ์—ด์€ ํ•ญ์ƒ ์ผ์ •ํ•œ ์ˆ˜์˜ ํ•ญ๋ชฉ์„ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์•ผ ํ•˜๋ฉฐ [dep1, dep2, dep3]๊ณผ ๊ฐ™์ด ์ž‘์„ฑ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. React๋Š” ๊ฐ๊ฐ์˜ ์˜์กด์„ฑ๋“ค์„ Object.is ๋น„๊ต๋ฒ•์„ ํ†ตํ•ด ์ด์ „ ๊ฐ’๊ณผ ๋น„๊ตํ•ฉ๋‹ˆ๋‹ค. ์˜์กด์„ฑ์„ ์ƒ๋žตํ•  ๊ฒฝ์šฐ, Effect๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง๋  ๋•Œ๋งˆ๋‹ค ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ์ธ์ˆ˜์— ์˜์กด์„ฑ ๋ฐฐ์—ด์„ ์ถ”๊ฐ€ํ–ˆ์„ ๋•Œ, ๋นˆ ๋ฐฐ์—ด์„ ์ถ”๊ฐ€ํ–ˆ์„ ๋•Œ, ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š์•˜์„ ๋•Œ์˜ ์ฐจ์ด๋ฅผ ํ™•์ธํ•ด ๋ณด์„ธ์š”.

๋ฐ˜ํ™˜๊ฐ’

useEffect๋Š” undefined๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

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

  • useEffect๋Š” Hook์ด๋ฏ€๋กœ ์ปดํฌ๋„ŒํŠธ์˜ ์ตœ์ƒ์œ„ ๋˜๋Š” ์ปค์Šคํ…€ Hook์—์„œ๋งŒ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ˜๋ณต๋ฌธ์ด๋‚˜ ์กฐ๊ฑด๋ฌธ์—์„œ๋Š” ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์ƒˆ๋กœ์šด ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ถ”์ถœํ•˜๊ณ  ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๋กœ state๋ฅผ ์ด๋™ํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์™ธ๋ถ€ ์‹œ์Šคํ…œ๊ณผ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋™๊ธฐํ™”ํ•  ํ•„์š”๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ, Effect๋ฅผ ์„ ์–ธํ•  ํ•„์š”๊ฐ€ ์—†์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • Strict Mode๋ฅผ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ, React๋Š” ์‹ค์ œ ์ฒซ ๋ฒˆ์งธ ์„ค์ • ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋˜๊ธฐ ์ด์ „์— ๊ฐœ๋ฐœ ๋ชจ๋“œ์—๋งŒ ํ•œ์ •ํ•˜์—ฌ ํ•œ ๋ฒˆ์˜ ์ถ”๊ฐ€์ ์ธ ์„ค์ • + ์ •๋ฆฌ ์‚ฌ์ดํด์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์ •๋ฆฌ ๋กœ์ง์ด ์„ค์ • ๋กœ์ง์„ ์™„๋ฒฝํžˆ โ€œ๋ฐ˜์˜โ€ํ•˜๊ณ  ์„ค์ • ๋กœ์ง์ด ์ˆ˜ํ–‰ํ•˜๋Š” ์ž‘์—…์„ ์ค‘๋‹จํ•˜๊ฑฐ๋‚˜ ์ทจ์†Œํ•  ์ˆ˜ ์žˆ๋Š”์ง€๋ฅผ ํ™•์ธํ•˜๋Š” ์ŠคํŠธ๋ ˆ์Šค ํ…Œ์ŠคํŠธ์ž…๋‹ˆ๋‹ค. ์ด์— ๋”ฐ๋ผ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธธ ๊ฒฝ์šฐ, ์ •๋ฆฌ ํ•จ์ˆ˜๋ฅผ ๊ตฌํ˜„ํ•˜์‹ญ์‹œ์˜ค.

  • ๋งŒ์•ฝ ์˜์กด์„ฑ์ด ๊ฐ์ฒด์ด๊ฑฐ๋‚˜ ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์— ์„ ์–ธ๋œ ํ•จ์ˆ˜์ผ ๊ฒฝ์šฐ์—๋Š” Effect๊ฐ€ ํ•„์š” ์ด์ƒ์œผ๋กœ ์žฌ์‹คํ–‰๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์ˆ˜์ •ํ•˜๋ ค๋ฉด ๋ถˆํ•„์š”ํ•œ ๊ฐ์ฒด ์˜์กด์„ฑ์ด๋‚˜ ํ•จ์ˆ˜ ์˜์กด์„ฑ์„ ์ œ๊ฑฐํ•˜์„ธ์š”. ๋˜๋Š” state ์—…๋ฐ์ดํŠธ๋ฅผ ์ถ”์ถœํ•˜๊ฑฐ๋‚˜ Effect ๋ฐ–์œผ๋กœ ๋น„ ๋ฐ˜์‘ํ˜• ๋กœ์ง์„ ๋นผ๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • Effect๊ฐ€ ์ƒํ˜ธ์ž‘์šฉ(ํด๋ฆญ๊ณผ ๊ฐ™์€)์— ์˜ํ•ด ์ผ์–ด๋‚˜์ง€ ์•Š๋Š”๋‹ค๋ฉด, React๋Š” ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์—…๋ฐ์ดํŠธ๋œ ํ™”๋ฉด์„ ๊ทธ๋ฆฌ๋„๋ก ํ—ˆ์šฉํ•œ ํ›„ Effect๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ Effect๋กœ ์ˆ˜ํ–‰ํ•˜๋Š” ์ž‘์—…์ด ์‹œ๊ฐ์ ์ธ ํšจ๊ณผ๊ฐ€ ์žˆ๊ณ  ์ง€์—ฐ์ด ๋ˆˆ์— ๋„๊ฒŒ ๋ฐœ์ƒํ•œ๋‹ค๋ฉด(์˜ˆ๋ฅผ ๋“ค์–ด, ํˆดํŒ์„ ์œ„์น˜์‹œํ‚ค๋Š” ๊ฒฝ์šฐ์™€ ๊ฐ™์ด ํ™”๋ฉด์— ์–ด๋–ค ๋ณ€ํ™”๋ฅผ ์ฃผ๋Š” ๊ฒฝ์šฐ), useEffect ๋Œ€์‹  useLayoutEffect๋ฅผ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค.

  • Effect๊ฐ€ ์ƒํ˜ธ์ž‘์šฉ(ํด๋ฆญ๊ณผ ๊ฐ™์€)์— ์˜ํ•ด ์ผ์–ด๋‚œ๋‹ค๊ณ  ํ•˜๋”๋ผ๋„ ๋ธŒ๋ผ์šฐ์ €๋Š” Effect ๋‚ด๋ถ€์˜ state๊ฐ€ ์—…๋ฐ์ดํŠธ๋˜๊ธฐ ์ด์ „์— ํ™”๋ฉด์„ ๋ฆฌํŽ˜์ธํŒ…ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ์˜๋„ํ•œ ์ƒํ™ฉ์ผ ์ˆ˜ ์žˆ์œผ๋‚˜, ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํ™”๋ฉด์„ ๋ฆฌํŽ˜์ธํŒ… ํ•˜๋Š” ๊ฒƒ์„ ๋ธ”๋กœํ‚นํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด useEffect ๋Œ€์‹  useLayoutEffect๋ฅผ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค.

  • Effect๋Š” client ํ™˜๊ฒฝ์—์„œ๋งŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์„œ๋ฒ„ ๋ Œ๋”๋ง์—์„œ๋Š” ๋™์ž‘ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.


์‚ฌ์šฉ๋ฐฉ๋ฒ•

์™ธ๋ถ€ ์‹œ์Šคํ…œ๊ณผ ์—ฐ๊ฒฐ

๋ช‡๋ช‡ ์ปดํฌ๋„ŒํŠธ๋“ค์€ ํŽ˜์ด์ง€์— ํ‘œ์‹œ๋˜๋Š” ๋™์•ˆ ๋„คํŠธ์›Œํฌ๋‚˜ ๋ธŒ๋ผ์šฐ์ € API, ๋˜๋Š” ์„œ๋“œํŒŒํ‹ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€์˜ ์—ฐ๊ฒฐ์ด ์œ ์ง€๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. React์— ์ œ์–ด๋˜์ง€ ์•Š๋Š” ์ด๋Ÿฌํ•œ ์‹œ์Šคํ…œ๋“ค์„ ์™ธ๋ถ€ ์‹œ์Šคํ…œ(external) ์ด๋ผ ๋ถ€๋ฆ…๋‹ˆ๋‹ค.

์ปดํฌ๋„ŒํŠธ๋ฅผ ์™ธ๋ถ€ ์‹œ์Šคํ…œ๊ณผ ์—ฐ๊ฒฐํ•˜๋ ค๋ฉด ์ปดํฌ๋„ŒํŠธ์˜ ์ตœ์ƒ์œ„ ๋ ˆ๋ฒจ์—์„œ useEffect๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

import { useEffect } from 'react';
import { createConnection } from './chat.js';

function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');

useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, [serverUrl, roomId]);
// ...
}

useEffect๋Š” 2๊ฐœ์˜ ์ธ์ˆ˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

  1. ์™ธ๋ถ€ ์‹œ์Šคํ…œ๊ณผ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ์„ค์ • ์ฝ”๋“œ๊ฐ€ ํฌํ•จ๋œ ์„ค์ • ํ•จ์ˆ˜
    • ์™ธ๋ถ€ ์‹œ์Šคํ…œ๊ณผ์˜ ์—ฐ๊ฒฐ์„ ํ•ด์ œํ•˜๋Š” ์ •๋ฆฌ ์ฝ”๋“œ๊ฐ€ ํฌํ•จ๋œ ์ •๋ฆฌ ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  2. ์œ„ ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์—์„œ ๋น„๋กฏ๋œ ๋ฐ˜์‘ํ˜• ๊ฐ’๋“ค์„ ํฌํ•จํ•˜๋Š” ์˜์กด์„ฑ ๋ฐฐ์—ด

React๋Š” ์„ค์ • ํ•จ์ˆ˜์™€ ์ •๋ฆฌ ํ•จ์ˆ˜๊ฐ€ ํ•„์š”ํ•  ๋•Œ๋งˆ๋‹ค ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด๋Š” ์—ฌ๋Ÿฌ ๋ฒˆ ํ˜ธ์ถœ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  1. ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ™”๋ฉด์— ์ถ”๊ฐ€๋˜์—ˆ์„ ๋•Œ ์„ค์ • ์ฝ”๋“œ๊ฐ€ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค (๋งˆ์šดํŠธ ์‹œ).
  2. ์˜์กด์„ฑ์ด ๋ณ€๊ฒฝ๋œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง ๋  ๋•Œ๋งˆ๋‹ค ์•„๋ž˜ ๋™์ž‘์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
    • ๋จผ์ € ์ •๋ฆฌ ์ฝ”๋“œ๊ฐ€ ์˜ค๋ž˜๋œ props์™€ state์™€ ํ•จ๊ป˜ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
    • ์ดํ›„, ์„ค์ • ์ฝ”๋“œ๊ฐ€ ์ƒˆ๋กœ์šด props์™€ state์™€ ํ•จ๊ป˜ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
  3. ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ™”๋ฉด์—์„œ ์ œ๊ฑฐ๋œ ์ดํ›„์— ์ •๋ฆฌ ์ฝ”๋“œ๊ฐ€ ๋งˆ์ง€๋ง‰์œผ๋กœ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค (๋งˆ์šดํŠธ ํ•ด์ œ ์‹œ).

์œ„์˜ ์˜ˆ์ œ๋ฅผ ํ†ตํ•ด ์ˆœ์„œ๋ฅผ ์„ค๋ช…ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์œ„์˜ ChatRoom ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ™”๋ฉด์— ์ถ”๊ฐ€๋˜๋ฉด ์ดˆ๊ธฐ serverUrl๊ณผ roomId๋ฅผ ์ด์šฉํ•ด ์ฑ„ํŒ…๋ฐฉ๊ณผ ์—ฐ๊ฒฐ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ฆฌ๋ Œ๋”๋ง์— ์˜ํ•ด serverUrl ๋˜๋Š” roomId๊ฐ€ ๋ณ€๊ฒฝ๋œ๋‹ค๋ฉด (์˜ˆ๋ฅผ ๋“ค์–ด ์‚ฌ์šฉ์ž๊ฐ€ ๋“œ๋กญ๋‹ค์šด ๋ฉ”๋‰ด๋ฅผ ์ด์šฉํ•ด ๋‹ค๋ฅธ ์ฑ„ํŒ…๋ฐฉ์„ ์„ ํƒํ•  ๊ฒฝ์šฐ) Effect๋Š” ์ด์ „ ์ฑ„ํŒ…๋ฐฉ๊ณผ์˜ ์—ฐ๊ฒฐ์„ ํ•ด์ œํ•˜๊ณ  ๋‹ค์Œ ์ฑ„ํŒ…๋ฐฉ๊ณผ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค. ChatRoom ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ™”๋ฉด์—์„œ ์ œ๊ฑฐ๋œ๋‹ค๋ฉด Effect๋Š” ๋งˆ์ง€๋ง‰ ์ฑ„ํŒ…๋ฐฉ๊ณผ ์ด๋ค„์ง„ ์—ฐ๊ฒฐ์„ ํ•ด์ œํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

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

๊ฐ๊ฐ์˜ Effect๋ฅผ ๋…๋ฆฝ์ ์ธ ํ”„๋กœ์„ธ์Šค๋กœ ์ž‘์„ฑํ•˜๊ณ  ์ •ํ™•ํ•œ ์„ค์ •/์ •๋ฆฌ ์‚ฌ์ดํด์„ ๊ณ ๋ คํ•˜์„ธ์š”. ์ปดํฌ๋„ŒํŠธ์˜ ๋งˆ์šดํŠธ, ์—…๋ฐ์ดํŠธ, ๋งˆ์šดํŠธ ํ•ด์ œ ์—ฌ๋ถ€๋Š” ์ค‘์š”ํ•˜์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ •๋ฆฌ ๋กœ์ง์ด ์„ค์ • ๋กœ์ง๊ณผ ์ •ํ™•ํ•˜๊ฒŒ โ€œ๋ฏธ๋Ÿฌ๋งโ€๋  ๋•Œ, Effect๋Š” ์„ค์ •๊ณผ ์ •๋ฆฌ๋ฅผ ํ•„์š”ํ•œ ๋งŒํผ ๊ฒฌ๊ณ ํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค!

Effect๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์™ธ๋ถ€ ์‹œ์Šคํ…œ๊ณผ ๋™๊ธฐํ™”๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ์™ธ๋ถ€ ์‹œ์Šคํ…œ์€ React์— ์˜ํ•ด ์ปจํŠธ๋กค๋˜์ง€ ์•Š๋Š” ๋ชจ๋“  ์ฝ”๋“œ๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด: An Effect lets you keep your component synchronized with some external system (like a chat service). Here, external system means any piece of code thatโ€™s not controlled by React, such as:

  • setInterval()์— ์˜ํ•ด ๊ด€๋ฆฌ๋˜๋Š” ํƒ€์ด๋จธ ๋˜๋Š” clearInterval().
  • window.addEventListener()์„ ์ด์šฉํ•œ ์ด๋ฒคํŠธ ๊ตฌ๋… ๋˜๋Š” window.removeEventListener().
  • animation.start()์™€ ๊ฐ™์€ ์„œ๋“œ ํŒŒํ‹ฐ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ API ๋˜๋Š” animation.reset().

๋งŒ์•ฝ ์™ธ๋ถ€ ์‹œ์Šคํ…œ๊ณผ ๋ฆฌ์•กํŠธ๋ฅผ ์—ฐ๊ฒฐํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๋ฉด Effect๋ฅผ ์‚ฌ์šฉํ•  ํ•„์š”๊ฐ€ ์—†์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์™ธ๋ถ€ ์‹œ์Šคํ…œ๊ณผ ์—ฐ๊ฒฐ ์˜ˆ์‹œ

์˜ˆ์ œ 1 of 5:
์ฑ„ํŒ… ์„œ๋ฒ„์™€ ์—ฐ๊ฒฐ

์ด ์˜ˆ์‹œ์—์„œ๋Š” ChatRoom ์ปดํฌ๋„ŒํŠธ์˜ Effect๋ฅผ ํ†ตํ•ด chat.js๋กœ ์ •์˜๋œ ์™ธ๋ถ€ ์‹œ์Šคํ…œ๊ณผ ์—ฐ๊ฒฐ์„ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. โ€œOpen chatโ€์„ ๋ˆ„๋ฅด๋ฉด ChatRoom ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋‚˜ํƒ€๋‚ฉ๋‹ˆ๋‹ค. ์ด ์ƒŒ๋“œ๋ฐ•์Šค๋Š” ๊ฐœ๋ฐœ ๋ชจ๋“œ์—์„œ ๋™์ž‘ํ•˜๋ฏ€๋กœ ์ถ”๊ฐ€์ ์ธ ์—ฐ๊ฒฐ-์—ฐ๊ฒฐํ•ด์ œ ์‚ฌ์ดํด์ด ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ๋“œ๋กญ๋‹ค์šด ๋ฉ”๋‰ด๋‚˜ input์„ ์ด์šฉํ•ด roomId ๋˜๋Š” serverUrl๋ฅผ ๋ณ€๊ฒฝํ•˜๊ณ  ์–ด๋–ป๊ฒŒ Effect๊ฐ€ chat์„ ์žฌ์—ฐ๊ฒฐํ•˜๋Š”์ง€ ํ™•์ธํ•ด ๋ณด์„ธ์š”. โ€œClose chatโ€์„ ๋ˆŒ๋Ÿฌ Effect๊ฐ€ ๋งˆ์ง€๋ง‰์— ์—ฐ๊ฒฐ๋˜์—ˆ๋˜ chat์„ ์—ฐ๊ฒฐ ํ•ด์ œํ•˜๋Š” ๊ฒƒ๋„ ํ™•์ธํ•ด ๋ณด์„ธ์š”.

import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';

function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState('https://localhost:1234');

  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => {
      connection.disconnect();
    };
  }, [roomId, serverUrl]);

  return (
    <>
      <label>
        Server URL:{' '}
        <input
          value={serverUrl}
          onChange={e => setServerUrl(e.target.value)}
        />
      </label>
      <h1>Welcome to the {roomId} room!</h1>
    </>
  );
}

export default function App() {
  const [roomId, setRoomId] = useState('general');
  const [show, setShow] = useState(false);
  return (
    <>
      <label>
        Choose the chat room:{' '}
        <select
          value={roomId}
          onChange={e => setRoomId(e.target.value)}
        >
          <option value="general">general</option>
          <option value="travel">travel</option>
          <option value="music">music</option>
        </select>
      </label>
      <button onClick={() => setShow(!show)}>
        {show ? 'Close chat' : 'Open chat'}
      </button>
      {show && <hr />}
      {show && <ChatRoom roomId={roomId} />}
    </>
  );
}


์ปค์Šคํ…€ Hook์„ Effect๋กœ ๊ฐ์‹ธ๊ธฐ

Effect๋Š” โ€œํƒˆ์ถœ๊ตฌโ€ ์ž…๋‹ˆ๋‹ค. โ€œReact ๋ฐ”๊นฅ์œผ๋กœ ๋‚˜๊ฐ€์•ผ ํ•  ๋•Œโ€์™€ ์œ ์ฆˆ์ผ€์ด์Šค์— ํ•„์š”ํ•œ ๋นŒํŠธ์ธ ์†”๋ฃจ์…˜์ด ์—†์„ ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ Effect๋ฅผ ์ž์ฃผ ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค๋ฉด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์˜์กดํ•˜๊ณ  ์žˆ๋Š” ๊ณตํ†ต์ ์ธ ๋™์ž‘๋“ค์„ ์ปค์Šคํ…€ Hook์œผ๋กœ ์ถ”์ถœํ•ด์•ผ ํ•œ๋‹ค๋Š” ์‹ ํ˜ธ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ์‹œ๋กœ ์•„๋ž˜์˜ useChatRoom ์ปค์Šคํ…€ Hook์€ Effect์˜ ๋กœ์ง์„ ์กฐ๊ธˆ ๋” ์„ ์–ธ์ ์ธ API๋กœ ๋ณด์ผ ์ˆ˜ ์žˆ๋„๋ก ์ˆจ๊ฒจ์ค๋‹ˆ๋‹ค.

function useChatRoom({ serverUrl, roomId }) {
useEffect(() => {
const options = {
serverUrl: serverUrl,
roomId: roomId
};
const connection = createConnection(options);
connection.connect();
return () => connection.disconnect();
}, [roomId, serverUrl]);
}

์ด์ œ ์ด ์ปค์Šคํ…€ Hook์„ ์–ด๋–ค ์ปดํฌ๋„ŒํŠธ์—์„œ๋„ ์ด์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');

useChatRoom({
roomId: roomId,
serverUrl: serverUrl
});
// ...

๋˜ํ•œ React ์ƒํƒœ๊ณ„์—๋Š” ๊ฐ์ข… ๋ชฉ์ ์— ๋งž๋Š” ํ›Œ๋ฅญํ•œ ์ปค์Šคํ…€ Hook๋“ค๋„ ๋งŽ์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

์ด ๋งํฌ๋ฅผ ํ†ตํ•ด ์ปค์Šคํ…€ Hook์— ๋Œ€ํ•ด ๋” ๋งŽ์ด ๊ณต๋ถ€ํ•ด๋ณด์„ธ์š”.

์ปค์Šคํ…€ Hook์—์„œ Effect๋ฅผ ํ™œ์šฉํ•˜๋Š” ์˜ˆ์ œ

์˜ˆ์ œ 1 of 3:
์ปค์Šคํ…€ useChatRoom Hook

์ด ์˜ˆ์ œ๋Š” ์ด์ „ ์˜ˆ์ œ ์ค‘ ํ•˜๋‚˜์™€ ๋™์ผํ•˜์ง€๋งŒ ๋กœ์ง์ด ์ปค์Šคํ…€ Hook์œผ๋กœ ์ถ”์ถœ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

import { useState } from 'react';
import { useChatRoom } from './useChatRoom.js';

function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState('https://localhost:1234');

  useChatRoom({
    roomId: roomId,
    serverUrl: serverUrl
  });

  return (
    <>
      <label>
        Server URL:{' '}
        <input
          value={serverUrl}
          onChange={e => setServerUrl(e.target.value)}
        />
      </label>
      <h1>Welcome to the {roomId} room!</h1>
    </>
  );
}

export default function App() {
  const [roomId, setRoomId] = useState('general');
  const [show, setShow] = useState(false);
  return (
    <>
      <label>
        Choose the chat room:{' '}
        <select
          value={roomId}
          onChange={e => setRoomId(e.target.value)}
        >
          <option value="general">general</option>
          <option value="travel">travel</option>
          <option value="music">music</option>
        </select>
      </label>
      <button onClick={() => setShow(!show)}>
        {show ? 'Close chat' : 'Open chat'}
      </button>
      {show && <hr />}
      {show && <ChatRoom roomId={roomId} />}
    </>
  );
}


๋ฆฌ์•กํŠธ๋กœ ์ž‘์„ฑ๋˜์ง€ ์•Š์€ ์œ„์ ฏ ์ œ์–ดํ•˜๊ธฐ

๊ฐ€๋”์€ ์ปดํฌ๋„ŒํŠธ์˜ prop ๋˜๋Š” state๋ฅผ ์™ธ๋ถ€ ์‹œ์Šคํ…œ๊ณผ ๋™๊ธฐํ™”ํ•ด์•ผํ•  ๋•Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด React ์—†์ด ์ž‘์„ฑ๋œ third-party ์ง€๋„ ์œ„์ ฏ์ด๋‚˜ ๋น„๋””์˜ค ํ”Œ๋ ˆ์ด์–ด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žˆ๋‹ค๋ฉด ์ด ์ปดํฌ๋„ŒํŠธ์˜ state๋ฅผ ํ˜„์žฌ React ์ปดํฌ๋„ŒํŠธ์˜ state์™€ ์ผ์น˜ํ•˜๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด Effect๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด Effect๋Š” map-widget.js์— ์ •์˜๋œ MapWidget ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. Map ์ปดํฌ๋„ŒํŠธ์˜ zoomLevel prop์„ ๋ณ€๊ฒฝํ•  ๋•Œ, Effect๋Š” ํ•ด๋‹น ํด๋ž˜์Šค ์ธ์Šคํ„ด์Šค์˜ setZoom()์„ ํ˜ธ์ถœํ•˜์—ฌ ๋™๊ธฐํ™”๋ฅผ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

import { useRef, useEffect } from 'react';
import { MapWidget } from './map-widget.js';

export default function Map({ zoomLevel }) {
  const containerRef = useRef(null);
  const mapRef = useRef(null);

  useEffect(() => {
    if (mapRef.current === null) {
      mapRef.current = new MapWidget(containerRef.current);
    }

    const map = mapRef.current;
    map.setZoom(zoomLevel);
  }, [zoomLevel]);

  return (
    <div
      style={{ width: 200, height: 200 }}
      ref={containerRef}
    />
  );
}

์ด ์˜ˆ์ œ์—์„œ๋Š” ์ •๋ฆฌ ํ•จ์ˆ˜๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋Š” MapWidget ํด๋ž˜์Šค๊ฐ€ ํด๋ž˜์Šค์— ์ „๋‹ฌ๋œ DOM ๋…ธ๋“œ๋งŒ ๊ด€๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. Map ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํŠธ๋ฆฌ์—์„œ ์ œ๊ฑฐ๋œ ํ›„, ๋ธŒ๋ผ์šฐ์ €์˜ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์—”์ง„์— ์˜ํ•ด DOM ๋…ธ๋“œ์™€ MapWidget ํด๋ž˜์Šค ์ธ์Šคํ„ด์Šค ๋ชจ๋‘๊ฐ€ ์ž๋™์œผ๋กœ ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜์— ์˜ํ•ด ์ •๋ฆฌ๋ฉ๋‹ˆ๋‹ค.


Effect๋ฅผ ์ด์šฉํ•œ ๋ฐ์ดํ„ฐ ํŽ˜์นญ

์ปดํฌ๋„ŒํŠธ์— ๋ฐ์ดํ„ฐ๋ฅผ ํŽ˜์นญํ•˜๊ธฐ ์œ„ํ•ด Effect๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๋ฉด ํ”„๋ ˆ์ž„์›Œํฌ์˜ ๋ฐ์ดํ„ฐ ํŽ˜์นญ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์ด์šฉํ•˜๋Š” ๊ฒƒ์ด Effect๋ฅผ ์ง์ ‘ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ๋” ํšจ์œจ์ ์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋งŒ์•ฝ ์ง์ ‘ Effect๋ฅผ ์ž‘์„ฑํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ํŽ˜์นญํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import { useState, useEffect } from 'react';
import { fetchBio } from './api.js';

export default function Page() {
const [person, setPerson] = useState('Alice');
const [bio, setBio] = useState(null);

useEffect(() => {
let ignore = false;
setBio(null);
fetchBio(person).then(result => {
if (!ignore) {
setBio(result);
}
});
return () => {
ignore = true;
};
}, [person]);

// ...

ignore ๋ณ€์ˆ˜์˜ ์ดˆ๊ธฐ๊ฐ’์ด false๋กœ ์„ค์ •๋˜๊ณ  ์ •๋ฆฌ ํ•จ์ˆ˜ ๋™์ž‘ ์ค‘์— true๋กœ ์„ค์ •๋˜๋Š” ๊ฒƒ์— ์ฃผ๋ชฉํ•˜์„ธ์š”. ์ด ๋กœ์ง์€ ์ฝ”๋“œ๊ฐ€ โ€œ๊ฒฝ์Ÿ ์ƒํƒœ(race conditions)โ€œ์— ๋น ์ง€์ง€ ์•Š๋„๋ก ๋ณด์žฅํ•ด ์ค๋‹ˆ๋‹ค. ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ๋ณด๋‚ธ ์ˆœ์„œ์™€ ์‘๋‹ต์„ ๋ฐ›๋Š” ์ˆœ์„œ๊ฐ€ ๋‹ค๋ฅด๊ฒŒ ๋™์ž‘ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋Ÿฌํ•œ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

import { useState, useEffect } from 'react';
import { fetchBio } from './api.js';

export default function Page() {
  const [person, setPerson] = useState('Alice');
  const [bio, setBio] = useState(null);
  useEffect(() => {
    let ignore = false;
    setBio(null);
    fetchBio(person).then(result => {
      if (!ignore) {
        setBio(result);
      }
    });
    return () => {
      ignore = true;
    }
  }, [person]);

  return (
    <>
      <select value={person} onChange={e => {
        setPerson(e.target.value);
      }}>
        <option value="Alice">Alice</option>
        <option value="Bob">Bob</option>
        <option value="Taylor">Taylor</option>
      </select>
      <hr />
      <p><i>{bio ?? 'Loading...'}</i></p>
    </>
  );
}

๋˜ํ•œ async / await ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ”๋“œ๋ฅผ ๋‹ค์‹œ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์—ฌ์ „ํžˆ ์ •๋ฆฌ ํ•จ์ˆ˜๋ฅผ ์ œ๊ณตํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

import { useState, useEffect } from 'react';
import { fetchBio } from './api.js';

export default function Page() {
  const [person, setPerson] = useState('Alice');
  const [bio, setBio] = useState(null);
  useEffect(() => {
    async function startFetching() {
      setBio(null);
      const result = await fetchBio(person);
      if (!ignore) {
        setBio(result);
      }
    }

    let ignore = false;
    startFetching();
    return () => {
      ignore = true;
    }
  }, [person]);

  return (
    <>
      <select value={person} onChange={e => {
        setPerson(e.target.value);
      }}>
        <option value="Alice">Alice</option>
        <option value="Bob">Bob</option>
        <option value="Taylor">Taylor</option>
      </select>
      <hr />
      <p><i>{bio ?? 'Loading...'}</i></p>
    </>
  );
}

Effect์—์„œ ์ง์ ‘ ๋ฐ์ดํ„ฐ ํŽ˜์นญ ๋กœ์ง์„ ์ž‘์„ฑํ•˜๋ฉด ๋‚˜์ค‘์— ์บ์‹ฑ ๊ธฐ๋Šฅ์ด๋‚˜ ์„œ๋ฒ„ ๋ Œ๋”๋ง๊ณผ ๊ฐ™์€ ์ตœ์ ํ™”๋ฅผ ์ถ”๊ฐ€ํ•˜๊ธฐ ์–ด๋ ค์›Œ์ง‘๋‹ˆ๋‹ค. ์ž์ฒด ์ œ์ž‘๋œ ์ปค์Šคํ…€ Hook์ด๋‚˜ ์ปค๋ฎค๋‹ˆํ‹ฐ์— ์˜ํ•ด ์œ ์ง€๋ณด์ˆ˜๋˜๋Š” Hook์„ ์‚ฌ์šฉํ•˜๋Š” ํŽธ์ด ๋” ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค.

Deep Dive

Effect์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ํŽ˜์นญํ•˜๋Š” ์ข‹์€ ๋Œ€์•ˆ์€ ๋ฌด์—‡์ธ๊ฐ€์š”?

Effect ๋‚ด๋ถ€์—์„œ fetch ํ˜ธ์ถœ์„ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์€ ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ ์•ฑ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ํŽ˜์นญํ•˜๋Š” ๊ฐ€์žฅ ์ธ๊ธฐ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด๊ฒƒ์€ ๋งค์šฐ ์ˆ˜๋™์ ์ธ ์ ‘๊ทผ ๋ฐฉ์‹์ด๋ฉฐ ํฐ ๋‹จ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • Effect๋Š” ์„œ๋ฒ„์—์„œ๋Š” ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์ดˆ๊ธฐ ์„œ๋ฒ„ ๋ Œ๋”๋ง ๋œ HTML์ด ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๋Š” state๋งŒ์„ ํฌํ•จํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ ์ปดํ“จํ„ฐ๋Š” ๋ชจ๋“  ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋‹ค์šด๋กœ๋“œ ๋ฐ›๊ณ  ์•ฑ์„ ๋ Œ๋”๋งํ•œ ๋‹ค์Œ ๋ฐ์ดํ„ฐ๋ฅผ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ํšจ์œจ์ ์ด์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • Effect ๋‚ด๋ถ€์—์„œ ์ง์ ‘ ํŽ˜์นญ์„ ํ•˜๋Š” ๊ฒƒ์€ ๋„คํŠธ์›Œํฌ ํญํฌ(network waterfalls)๊ฐ€ ์ƒ์„ฑ๋˜๊ธฐ ์‰ฝ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง ํ›„ ์ผ๋ถ€ ๋ฐ์ดํ„ฐ๋ฅผ ํŽ˜์นญํ•˜๊ณ  ๋‚˜์„œ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋ง ๋ฉ๋‹ˆ๋‹ค. ์ดํ›„ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ž์‹ ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ํŽ˜์นญํ•˜๊ธฐ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. ๋„คํŠธ์›Œํฌ์˜ ์†๋„๊ฐ€ ๋น ๋ฅด์ง€ ์•Š๋‹ค๋ฉด ์ด ๋ฐฉ๋ฒ•์€ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ‘๋ ฌ๋กœ ํŽ˜์นญํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ํ›จ์”ฌ ๋Š๋ฆฝ๋‹ˆ๋‹ค.
  • Effect ๋‚ด๋ถ€์—์„œ ์ง์ ‘ ๋ฐ์ดํ„ฐ๋ฅผ ํŽ˜์นญํ•˜๋Š” ๊ฒƒ์€ ์ผ๋ฐ˜์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฏธ๋ฆฌ ๋กœ๋“œํ•˜๊ฑฐ๋‚˜ ์บ์‹ฑํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งˆ์šดํŠธ ํ•ด์ œ๋˜๊ณ  ๋‹ค์‹œ ๋งˆ์šดํŠธ๋˜์—ˆ์„ ๋•Œ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ๊ฐ€์ ธ์™€์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉํ•˜๊ธฐ ๋งค์šฐ ๋ถˆํŽธํ•œ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ๊ฒฝ์Ÿ ์กฐ๊ฑด๊ณผ ๊ฐ™์€ ๋ฒ„๊ทธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค์ง€ ์•Š๋„๋ก fetch ํ˜ธ์ถœ์„ ์ž‘์„ฑํ•  ๋•Œ ์ƒ๋‹นํ•œ ์–‘์˜ ๋ณด์ผ๋Ÿฌ ํ”Œ๋ ˆ์ดํŠธ ์ฝ”๋“œ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ๋‹จ์ ์€ ๋ฆฌ์•กํŠธ๋งŒ ํ•ด๋‹น๋˜๋Š” ๊ฒƒ์ด ์•„๋‹™๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ํŽ˜์นญํ•  ๋•Œ๋„ ํ•ด๋‹น๋ฉ๋‹ˆ๋‹ค. ๋ผ์šฐํŒ…๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋ฐ์ดํ„ฐ ํŽ˜์นญ์€ ์„ธ๋ถ€์ ์ธ ์‚ฌํ•ญ์ด ๋งŽ์œผ๋ฏ€๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ ‘๊ทผ ๋ฐฉ์‹์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

  • ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ, ํ•ด๋‹น ํ”„๋ ˆ์ž„์›Œํฌ์— ๋‚ด์žฅ๋œ ๋ฐ์ดํ„ฐ ํŽ˜์นญ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ํ™œ์šฉํ•˜์„ธ์š”. ํ˜„๋Œ€ ๋ฆฌ์•กํŠธ ํ”„๋ ˆ์ž„์›Œํฌ๋Š” ๋งค์šฐ ํšจ์œจ์ ์ด๋ฉฐ ์œ„์—์„œ ์–ธ๊ธ‰ํ•œ ๋ฌธ์ œ์ ์ด ์—†๋Š” ํ†ตํ•ฉ๋œ ๋ฐ์ดํ„ฐ ํŽ˜์นญ ๊ธฐ๋Šฅ์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๊ทธ๋ ‡์ง€ ์•Š์€ ๊ฒฝ์šฐ, ํด๋ผ์ด์–ธํŠธ ์ธก ์บ์‹œ๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ ์ง์ ‘ ๊ฐœ๋ฐœ์„ ๊ณ ๋ คํ•ด ๋ณด์„ธ์š”. ์ธ๊ธฐ ์žˆ๋Š” ์˜คํ”ˆ์†Œ์Šค ์†”๋ฃจ์…˜์œผ๋กœ๋Š” React Query, useSWR, ๊ทธ๋ฆฌ๊ณ  React Router 6.4+.๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฌผ๋ก  ์ง์ ‘ ์†”๋ฃจ์…˜์„ ๊ฐœ๋ฐœํ• ์ˆ˜๋„ ์žˆ์œผ๋ฉฐ ์ด ๊ฒฝ์šฐ์—๋Š” ์ดํŽ™ํŠธ๋ฅผ ๋‚ด๋ถ€์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋ฉด์„œ๋„ ๋ฐ์ดํ„ฐ ์‚ฌ์ „๋กœ๋“œ ๋˜๋Š” ๋ฐ์ดํ„ฐ ์š”๊ตฌ์‚ฌํ•ญ์„ ๋ผ์šฐํŠธ๋กœ ํ˜ธ์ด์ŠคํŒ…ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ํ†ตํ•ด ์ค‘๋ณต ์š”์ฒญ ๋ฐฉ์ง€, ์‘๋‹ต ์บ์‹ฑ ๋ฐ ๋„คํŠธ์›Œํฌ ํญํฌ ํšจ๊ณผ ๋ฐฉ์ง€๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋งŒ์•ฝ ์ด๋Ÿฌํ•œ ์ ‘๊ทผ ๋ฐฉ์‹์ด ์ ํ•ฉํ•˜์ง€ ์•Š๋‹ค๋ฉด Effect ๋‚ด๋ถ€์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ํŽ˜์นญํ•˜๋Š” ๊ฒƒ์„ ๊ณ„์† ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


๋ฐ˜์‘ํ˜•๊ฐ’ ์˜์กด์„ฑ ์ง€์ •

Effect์˜ ์˜์กด์„ฑ์„ โ€œ์„ ํƒโ€ํ•  ์ˆ˜ ์—†๋‹ค๋Š” ์ ์— ์œ ์˜ํ•˜์„ธ์š”. Effect ์ฝ”๋“œ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋“  ๋ฐ˜์‘ํ˜• ๊ฐ’์€ ์˜์กด์„ฑ์œผ๋กœ ์„ ์–ธ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Effect์˜ ์˜์กด์„ฑ ๋ฐฐ์—ด์€ ์ฝ”๋“œ์— ์˜ํ•ด ๊ฒฐ์ •๋ฉ๋‹ˆ๋‹ค.

function ChatRoom({ roomId }) { // This is a reactive value
const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // This is a reactive value too

useEffect(() => {
const connection = createConnection(serverUrl, roomId); // This Effect reads these reactive values
connection.connect();
return () => connection.disconnect();
}, [serverUrl, roomId]); // โœ… So you must specify them as dependencies of your Effect
// ...
}

serverUrl ๋˜๋Š” roomId๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค Effect๋Š” ์ƒˆ๋กœ์šด ๊ฐ’์„ ์ด์šฉํ•ด ์ฑ„ํŒ…์„ ๋‹ค์‹œ ์—ฐ๊ฒฐํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋ฐ˜์‘ํ˜• ๊ฐ’ ์—๋Š” props์™€ ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์— ์„ ์–ธ๋œ ๋ชจ๋“  ๋ณ€์ˆ˜๋‚˜ ํ•จ์ˆ˜๋“ค์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. roomId์™€ serverUrl์€ ๋ฐ˜์‘ํ˜• ๊ฐ’์ด๋ฏ€๋กœ ์ด๋“ค์„ ์˜์กด์„ฑ์—์„œ ์ œ๊ฑฐํ•˜๋ฉด ์•ˆ ๋ฉ๋‹ˆ๋‹ค. ์ด๋“ค์„ ๋ˆ„๋ฝํ–ˆ์„ ๋•Œ ๋ฆฐํ„ฐ๊ฐ€ ๋ฆฌ์•กํŠธ ํ™˜๊ฒฝ์— ๋งž๊ฒŒ ์„ค์ •๋˜์–ด ์žˆ์—ˆ๋‹ค๋ฉด ๋ฆฐํ„ฐ๋Š” ์ด๊ฒƒ์„ ์ˆ˜์ •ํ•ด์•ผ ํ•˜๋Š” ์‹ค์ˆ˜๋กœ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');

useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => connection.disconnect();
}, []); // ๐Ÿ”ด React Hook useEffect has missing dependencies: 'roomId' and 'serverUrl'
// ...
}

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

const serverUrl = 'https://localhost:1234'; // Not a reactive value anymore

function ChatRoom({ roomId }) {
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => connection.disconnect();
}, [roomId]); // โœ… All dependencies declared
// ...
}

์ด์ œ serverUrl์€ ๋ฐ˜์‘ํ˜• ๊ฐ’์ด ์•„๋‹ˆ๋ฉฐ (๋ฆฌ๋ Œ๋”๋ง๋  ๋•Œ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์„ ๊ฒƒ์ด๋ฏ€๋กœ), ์˜์กด์„ฑ์— ์ถ”๊ฐ€ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. Effect์˜ ์ฝ”๋“œ๊ฐ€ ์–ด๋–ค ๋ฐ˜์‘ํ˜• ๊ฐ’๋„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ๊ทธ ์˜์กด์„ฑ ๋ชฉ๋ก์€ ๋น„์–ด์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ([])

const serverUrl = 'https://localhost:1234'; // Not a reactive value anymore
const roomId = 'music'; // Not a reactive value anymore

function ChatRoom() {
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => connection.disconnect();
}, []); // โœ… All dependencies declared
// ...
}

์˜์กด์„ฑ์ด ๋น„์–ด์žˆ๋Š” Effect๋Š” ์ปดํฌ๋„ŒํŠธ์˜ props๋‚˜ state๊ฐ€ ๋ณ€๊ฒฝ๋˜๋„ ๋‹ค์‹œ ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

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

๊ธฐ์กด์˜ ์ฝ”๋“œ ๋ฒ ์ด์Šค์—์„œ ์•„๋ž˜์™€ ๊ฐ™์ด ๋ฆฐํ„ฐ๋ฅผ ์–ต์ œํ•˜๊ณ  ์žˆ๋Š” ์ผ๋ถ€ Effect๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

useEffect(() => {
// ...
// ๐Ÿ”ด Avoid suppressing the linter like this:
// eslint-ignore-next-line react-hooks/exhaustive-deps
}, []);

์˜์กด์„ฑ์ด ์ฝ”๋“œ์™€ ์ผ์น˜ํ•˜์ง€ ์•Š์„ ๋•Œ ๋ฒ„๊ทธ๊ฐ€ ๋„์ž…๋  ์œ„ํ—˜์ด ํฝ๋‹ˆ๋‹ค. ๋ฆฐํ„ฐ๋ฅผ ์–ต์ œํ•จ์œผ๋กœ์จ Effect๊ฐ€ ์˜์กดํ•˜๋Š” ๊ฐ’์— ๋Œ€ํ•ด React๊ฐ€ โ€˜๊ฑฐ์ง“๋งโ€™์„ ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋ฆฐํ„ฐ๋ฅผ ์†์ด๋Š” ๋Œ€์‹  ์ด๋Ÿฌํ•œ ๊ฐ’๋“ค์ด ๋ถˆํ•„์š”ํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์ฆ๋ช…ํ•˜์„ธ์š”.

๋ฐ˜์‘ํ˜• ๊ฐ’์„ ์˜์กด์„ฑ์œผ๋กœ ์ถ”๊ฐ€ํ•˜๋Š” ์˜ˆ์ œ

์˜ˆ์ œ 1 of 3:
์˜์กด์„ฑ ๋ฐฐ์—ด ์ „๋‹ฌ

์˜์กด์„ฑ์„ ๋ช…์‹œํ•˜๋ฉด Effect๋Š” ์ดˆ๊ธฐ ๋ Œ๋”๋ง ํ›„ ๊ทธ๋ฆฌ๊ณ  ์˜์กด์„ฑ ๊ฐ’ ๋ณ€๊ฒฝ๊ณผ ํ•จ๊ป˜ ๋ฆฌ๋ Œ๋”๋ง์ด ๋œ ํ›„ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

useEffect(() => {
// ...
}, [a, b]); // Runs again if a or b are different

์•„๋ž˜ ์˜ˆ์ œ์—์„œ๋Š” serverUrl์™€ roomId์€ ๋ฐ˜์‘ํ˜• ๊ฐ’์ด๋ฏ€๋กœ ๋‘˜ ๋‹ค ์˜์กด์„ฑ์œผ๋กœ ์ง€์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ฒฐ๊ณผ์ ์œผ๋กœ ๋“œ๋กญ๋‹ค์šด์—์„œ ๋‹ค๋ฅธ ๋ฐฉ์„ ์„ ํƒํ•˜๊ฑฐ๋‚˜ ์„œ๋ฒ„ URL ์ž…๋ ฅ์„ ํŽธ์ง‘ํ•˜๋ฉด ์ฑ„ํŒ…์ด ๋‹ค์‹œ ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ message๋Š” Effect์—์„œ ์‚ฌ์šฉ๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ(์˜์กด์„ฑ์ด ์•„๋‹ˆ๋ฏ€๋กœ), ๋ฉ”์„ธ์ง€๋ฅผ ํŽธ์ง‘ํ•ด๋„ ๋Œ€ํ™”๊ฐ€ ๋‹ค์‹œ ์—ฐ๊ฒฐ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';

function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState('https://localhost:1234');
  const [message, setMessage] = useState('');

  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => {
      connection.disconnect();
    };
  }, [serverUrl, roomId]);

  return (
    <>
      <label>
        Server URL:{' '}
        <input
          value={serverUrl}
          onChange={e => setServerUrl(e.target.value)}
        />
      </label>
      <h1>Welcome to the {roomId} room!</h1>
      <label>
        Your message:{' '}
        <input value={message} onChange={e => setMessage(e.target.value)} />
      </label>
    </>
  );
}

export default function App() {
  const [show, setShow] = useState(false);
  const [roomId, setRoomId] = useState('general');
  return (
    <>
      <label>
        Choose the chat room:{' '}
        <select
          value={roomId}
          onChange={e => setRoomId(e.target.value)}
        >
          <option value="general">general</option>
          <option value="travel">travel</option>
          <option value="music">music</option>
        </select>
        <button onClick={() => setShow(!show)}>
          {show ? 'Close chat' : 'Open chat'}
        </button>
      </label>
      {show && <hr />}
      {show && <ChatRoom roomId={roomId}/>}
    </>
  );
}


Effect์—์„œ ์ด์ „ state๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ state ์—…๋ฐ์ดํŠธํ•˜๊ธฐ

Effect์—์„œ ์ด์ „ state๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ state๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋ ค๋ฉด ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

function Counter() {
const [count, setCount] = useState(0);

useEffect(() => {
const intervalId = setInterval(() => {
setCount(count + 1); // You want to increment the counter every second...
}, 1000)
return () => clearInterval(intervalId);
}, [count]); // ๐Ÿšฉ ... but specifying `count` as a dependency always resets the interval.
// ...
}

count๊ฐ€ ๋ฐ˜์‘ํ˜• ๊ฐ’์ด๋ฏ€๋กœ ๋ฐ˜๋“œ์‹œ ์˜์กด์„ฑ ๋ฐฐ์—ด์— ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ count๊ฐ€ ๋ณ€๊ฒฝ๋˜๋Š” ๊ฒƒ์€ Effect๊ฐ€ ์ •๋ฆฌ๋œ ํ›„ ๋‹ค์‹œ ์„ค์ •๋˜๋Š” ๊ฒƒ์„ ์•ผ๊ธฐํ•˜๋ฏ€๋กœ count๋Š” ๊ณ„์† ์ฆ๊ฐ€ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด์ƒ์ ์ด์ง€ ์•Š์€ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ํ˜„์ƒ์„ ๋ฐฉ์ง€ํ•˜๋ ค๋ฉด c => c + 1 state ๋ณ€๊ฒฝํ•จ์ˆ˜๋ฅผ setCount์— ์ถ”๊ฐ€ํ•˜์„ธ์š”,

import { useState, useEffect } from 'react';

export default function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const intervalId = setInterval(() => {
      setCount(c => c + 1); // โœ… Pass a state updater
    }, 1000);
    return () => clearInterval(intervalId);
  }, []); // โœ… Now count is not a dependency

  return <h1>{count}</h1>;
}

c => c + 1์„ count + 1 ๋Œ€์‹  ์ „๋‹ฌํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ, Effect๋Š” ๋” ์ด์ƒ count์— ์˜์กดํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด ์ˆ˜์ •์œผ๋กœ ์ธํ•ด count๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค Effect๊ฐ€ ์ •๋ฆฌ ๋ฐ ์„ค์ •์„ ๋‹ค์‹œ ์‹คํ–‰ํ•  ํ•„์š”๊ฐ€ ์—†๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.


๋ถˆํ•„์š”ํ•œ ๊ฐ์ฒด ์˜์กด์„ฑ ์ œ๊ฑฐํ•˜๊ธฐ

Effect๊ฐ€ ๋ Œ๋”๋ง ์ค‘์— ์ƒ์„ฑ๋œ ๊ฐ์ฒด๋‚˜ ํ•จ์ˆ˜์— ์˜์กดํ•˜๋Š” ๊ฒฝ์šฐ, ๋„ˆ๋ฌด ์ž์ฃผ ์‹คํ–‰๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์ด Effect๋Š” ๋งค ๋ Œ๋”๋ง ํ›„์— ๋‹ค์‹œ ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋ Œ๋”๋ง๋งˆ๋‹ค options ๊ฐ์ฒด๊ฐ€ ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

const serverUrl = 'https://localhost:1234';

function ChatRoom({ roomId }) {
const [message, setMessage] = useState('');

const options = { // ๐Ÿšฉ This object is created from scratch on every re-render
serverUrl: serverUrl,
roomId: roomId
};

useEffect(() => {
const connection = createConnection(options); // It's used inside the Effect
connection.connect();
return () => connection.disconnect();
}, [options]); // ๐Ÿšฉ As a result, these dependencies are always different on a re-render
// ...

๋ Œ๋”๋ง ์ค‘์— ์ƒ์„ฑ๋œ ๊ฐ์ฒด๋ฅผ ์˜์กด์„ฑ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ํ”ผํ•˜์„ธ์š”. ๋Œ€์‹  ๊ฐ์ฒด๋ฅผ Effect ๋‚ด์—์„œ ์ƒ์„ฑํ•˜์„ธ์š”.

import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';

const serverUrl = 'https://localhost:1234';

function ChatRoom({ roomId }) {
  const [message, setMessage] = useState('');

  useEffect(() => {
    const options = {
      serverUrl: serverUrl,
      roomId: roomId
    };
    const connection = createConnection(options);
    connection.connect();
    return () => connection.disconnect();
  }, [roomId]);

  return (
    <>
      <h1>Welcome to the {roomId} room!</h1>
      <input value={message} onChange={e => setMessage(e.target.value)} />
    </>
  );
}

export default function App() {
  const [roomId, setRoomId] = useState('general');
  return (
    <>
      <label>
        Choose the chat room:{' '}
        <select
          value={roomId}
          onChange={e => setRoomId(e.target.value)}
        >
          <option value="general">general</option>
          <option value="travel">travel</option>
          <option value="music">music</option>
        </select>
      </label>
      <hr />
      <ChatRoom roomId={roomId} />
    </>
  );
}

์ด์ œ options ๊ฐ์ฒด๋ฅผ Effect ๋‚ด์—์„œ ์ƒ์„ฑํ•˜๋ฉด, Effect ์ž์ฒด๋Š” roomId ๋ฌธ์ž์—ด์—๋งŒ ์˜์กดํ•ฉ๋‹ˆ๋‹ค.

์ด ์ˆ˜์ •์œผ๋กœ ์ž…๋ ฅ๋ž€์— ํ…์ŠคํŠธ๋ฅผ ์ž…๋ ฅํ•˜๋”๋ผ๋„ ์ฑ„ํŒ…์ด ๋‹ค์‹œ ์—ฐ๊ฒฐ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ฐ์ฒด์™€๋Š” ๋‹ฌ๋ฆฌ roomId์™€ ๊ฐ™์€ ๋ฌธ์ž์—ด์€ ๋‹ค๋ฅธ ๊ฐ’์œผ๋กœ ์„ค์ •ํ•˜์ง€ ์•Š๋Š” ํ•œ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์˜์กด์„ฑ ์ œ๊ฑฐ์— ๊ด€ํ•œ ์ž์„ธํ•œ ์• ์šฉ์€ ์—ฌ๊ธฐ๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”.


๋ถˆํ•„์š”ํ•œ ํ•จ์ˆ˜ ์˜์กด์„ฑ ์ œ๊ฑฐํ•˜๊ธฐ

Effect๊ฐ€ ๋ Œ๋”๋ง ์ค‘์— ์ƒ์„ฑ๋œ ๊ฐ์ฒด๋‚˜ ํ•จ์ˆ˜์— ์˜์กดํ•˜๋Š” ๊ฒฝ์šฐ, ๋„ˆ๋ฌด ์ž์ฃผ ์‹คํ–‰๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์ด Effect๋Š” ๋งค ๋ Œ๋”๋ง ํ›„์— ๋‹ค์‹œ ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋ Œ๋”๋ง๋งˆ๋‹ค createOptions ํ•จ์ˆ˜๊ฐ€ ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

function ChatRoom({ roomId }) {
const [message, setMessage] = useState('');

function createOptions() { // ๐Ÿšฉ This function is created from scratch on every re-render
return {
serverUrl: serverUrl,
roomId: roomId
};
}

useEffect(() => {
const options = createOptions(); // It's used inside the Effect
const connection = createConnection();
connection.connect();
return () => connection.disconnect();
}, [createOptions]); // ๐Ÿšฉ As a result, these dependencies are always different on a re-render
// ...

๋ฆฌ๋ Œ๋”๋ง๋งˆ๋‹ค ํ•จ์ˆ˜๋ฅผ ์ฒ˜์Œ๋ถ€ํ„ฐ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ ๊ทธ ์ž์ฒด๋กœ๋Š” ๋ฌธ์ œ๊ฐ€ ๋˜์ง€ ์•Š๊ณ , ์ด๋ฅผ ์ตœ์ ํ™”ํ•  ํ•„์š”๋Š” ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด๊ฒƒ์„ Effect์˜ ์˜์กด์„ฑ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ Effect๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง ํ›„๋งˆ๋‹ค ๋‹ค์‹œ ์‹คํ–‰๋˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

๋ Œ๋”๋ง ์ค‘์— ์ƒ์„ฑ๋œ ํ•จ์ˆ˜๋ฅผ ์˜์กด์„ฑ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ํ”ผํ•˜์„ธ์š”. ๋Œ€์‹  Effect ๋‚ด์—์„œ ํ•จ์ˆ˜๋ฅผ ์„ ์–ธํ•˜์„ธ์š”.

import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';

const serverUrl = 'https://localhost:1234';

function ChatRoom({ roomId }) {
  const [message, setMessage] = useState('');

  useEffect(() => {
    function createOptions() {
      return {
        serverUrl: serverUrl,
        roomId: roomId
      };
    }

    const options = createOptions();
    const connection = createConnection(options);
    connection.connect();
    return () => connection.disconnect();
  }, [roomId]);

  return (
    <>
      <h1>Welcome to the {roomId} room!</h1>
      <input value={message} onChange={e => setMessage(e.target.value)} />
    </>
  );
}

export default function App() {
  const [roomId, setRoomId] = useState('general');
  return (
    <>
      <label>
        Choose the chat room:{' '}
        <select
          value={roomId}
          onChange={e => setRoomId(e.target.value)}
        >
          <option value="general">general</option>
          <option value="travel">travel</option>
          <option value="music">music</option>
        </select>
      </label>
      <hr />
      <ChatRoom roomId={roomId} />
    </>
  );
}

createOptions ํ•จ์ˆ˜๊ฐ€ Effect ๋‚ด๋ถ€์— ์„ ์–ธ๋˜์—ˆ์œผ๋ฏ€๋กœ, Effect ์ž์ฒด๋Š” roomId ๋ฌธ์ž์—ด์—๋งŒ ์˜์กดํ•ฉ๋‹ˆ๋‹ค. ์ด ์ˆ˜์ •์„ ํ†ตํ•ด ์ž…๋ ฅ๋ž€์— ์ž…๋ ฅํ•˜๋Š” ๊ฒƒ๋งŒ์œผ๋กœ ์ฑ„ํŒ…์ด ๋‹ค์‹œ ์—ฐ๊ฒฐ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. roomId์™€ ๊ฐ™์€ ๋ฌธ์ž์—ด์€ ๋‹ค๋ฅธ ๊ฐ’์œผ๋กœ ์„ค์ •ํ•˜์ง€ ์•Š๋Š” ํ•œ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์˜์กด์„ฑ ์ œ๊ฑฐ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์—ฌ๊ธฐ๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”.


Effect์—์„œ ์ตœ์‹  props์™€ state๋ฅผ ์ฝ๊ธฐ

๊ฐœ๋ฐœ์ค‘์ด์—์š”

์ด ์„น์…˜์€ ์•ˆ์ • ๋ฒ„์ „์˜ ๋ฆฌ์•กํŠธ์— ๋ฐ˜์˜๋˜์ง€ ์•Š์€ ์‹คํ—˜์  API์— ๋Œ€ํ•ด ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ๋ณธ์ ์œผ๋กœ Effect์—์„œ ๋ฐ˜์‘ํ˜• ๊ฐ’์„ ์ฝ์„ ๋•Œ๋Š” ํ•ด๋‹น ๊ฐ’์„ ์˜์กด์„ฑ์— ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด Effect๊ฐ€ ํ•ด๋‹น ๊ฐ’์˜ ๋ชจ๋“  ๋ณ€๊ฒฝ์— โ€˜๋ฐ˜์‘โ€™ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ ์˜์กด์„ฑ์—์„œ ์›ํ•˜๋Š” ๋™์ž‘์ž…๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ๋•Œ๋กœ๋Š” Effect์—์„œ ์ตœ์‹  props์™€ state๋ฅผ โ€˜๋ฐ˜์‘โ€™ํ•˜์ง€ ์•Š๊ณ  ์ฝ๊ณ  ์‹ถ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ํŽ˜์ด์ง€ ๋ฐฉ๋ฌธ๋งˆ๋‹ค ์‡ผํ•‘ ์นดํŠธ์— ๋‹ด๊ธด ํ•ญ๋ชฉ ์ˆ˜๋ฅผ ๊ธฐ๋กํ•˜๊ณ  ์‹ถ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

function Page({ url, shoppingCart }) {
useEffect(() => {
logVisit(url, shoppingCart.length);
}, [url, shoppingCart]); // โœ… All dependencies declared
// ...
}

url ๋ณ€๊ฒฝ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ํŽ˜์ด์ง€ ๋ฐฉ๋ฌธ์„ ๊ธฐ๋กํ•˜๊ณ  ์‹ถ์ง€๋งŒ shoppingCart๋งŒ ๋ณ€๊ฒฝ๋˜๋Š” ๊ฒฝ์šฐ์—๋Š” ๊ธฐ๋กํ•˜๊ณ  ์‹ถ์ง€ ์•Š๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ• ๊นŒ์š”? shoppingCart๋ฅผ ์˜์กด์„ฑ์—์„œ ์ œ์™ธํ•˜๋ฉด ๋ฐ˜์‘์„ฑ ๊ทœ์น™์„ ์–ด๊ธฐ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. Effect ๋‚ด์—์„œ ํ˜ธ์ถœ๋˜๋Š” ์ฝ”๋“œ์ด์ง€๋งŒ ๋ณ€๊ฒฝ์— ๋ฐ˜์‘ํ•˜์ง€ ์•Š๊ธฐ๋ฅผ ์›ํ•œ๋‹ค๋ฉด useEffectEvent Hook์„ ์‚ฌ์šฉํ•˜์—ฌ Effect Event๋ฅผ ์„ ์–ธํ•˜๊ณ  shoppingCart๋ฅผ ์ฝ๋Š” ์ฝ”๋“œ๋ฅผ ๊ทธ ์•ˆ์— ์ด๋™์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

function Page({ url, shoppingCart }) {
const onVisit = useEffectEvent(visitedUrl => {
logVisit(visitedUrl, shoppingCart.length)
});

useEffect(() => {
onVisit(url);
}, [url]); // โœ… All dependencies declared
// ...
}

Effect Event๋Š” ๋ฐ˜์‘์ ์ด์ง€ ์•Š์œผ๋ฉฐ Effect์˜ ์˜์กด์„ฑ์—์„œ ๋ฐฐ์ œ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Effect Event์—๋Š” ๋น„ ๋ฐ˜์‘ํ˜• ์ฝ”๋“œ(Effect Event ๋กœ์ง์€ ์ตœ์‹  props์™€ state๋ฅผ ์ฝ์„ ์ˆ˜ ์žˆ์Œ)๋ฅผ ๋ฐฐ์น˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. onVisit๋‚ด์˜ shoppingCart๋ฅผ ์ฝ์Œ์œผ๋กœ์จ shoppingCart์˜ ๋ณ€๊ฒฝ์œผ๋กœ ์ธํ•œ Effect์˜ ์žฌ์‹คํ–‰์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.

Effect Event๊ฐ€ ์–ด๋–ป๊ฒŒ ๋ฐ˜์‘ํ˜• ๋ฐ ๋น„ ๋ฐ˜์‘ํ˜• ์ฝ”๋“œ๋ฅผ ๋ถ„๋ฆฌํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋˜๋Š”์ง€์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์—ฌ๊ธฐ๋ฅผ ์ฝ์–ด๋ณด์„ธ์š”.


์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ์—์„œ ๋‹ค๋ฅธ ์ปจํ…์ธ ๋ฅผ ํ‘œ์‹œํ•˜๊ธฐ

์•ฑ์ด ์„œ๋ฒ„ ๋ Œ๋”๋ง์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ (์ง์ ‘ ๋˜๋Š” ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ํ†ตํ•ด) ์ปดํฌ๋„ŒํŠธ๋Š” ๋‘ ๊ฐ€์ง€ ๋‹ค๋ฅธ ํ™˜๊ฒฝ์—์„œ ๋ Œ๋”๋ง๋ฉ๋‹ˆ๋‹ค. ์„œ๋ฒ„์—์„œ๋Š” ์ดˆ๊ธฐ HTML์„ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ๋ Œ๋”๋ง๋˜๊ณ , ํด๋ผ์ด์–ธํŠธ์—์„œ๋Š” React๊ฐ€ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ํ•ด๋‹น HTML์— ์—ฐ๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์‹œ ๋ Œ๋”๋ง ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด hydration์ด ์ž‘๋™ํ•˜๋ ค๋ฉด ์ดˆ๊ธฐ ๋ Œ๋”๋ง ์ถœ๋ ฅ์ด ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ์—์„œ ๋™์ผํ•ด์•ผ ํ•˜๋Š” ์ด์œ ์ž…๋‹ˆ๋‹ค.

๋“œ๋ฌผ๊ฒŒ ํด๋ผ์ด์–ธํŠธ์—์„œ ๋‹ค๋ฅธ ๋‚ด์šฉ์„ ํ‘œ์‹œํ•ด์•ผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์•ฑ์ด localStorage์—์„œ ์ผ๋ถ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๋Š” ๊ฒฝ์šฐ, ์ด๋ฅผ ์„œ๋ฒ„์—์„œ ๊ตฌํ˜„ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ์€ ์ด๊ฒƒ์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

function MyComponent() {
const [didMount, setDidMount] = useState(false);

useEffect(() => {
setDidMount(true);
}, []);

if (didMount) {
// ... return client-only JSX ...
} else {
// ... return initial JSX ...
}
}

์•ฑ์ด ๋กœ๋”ฉ ์ค‘์ธ ๋™์•ˆ ์‚ฌ์šฉ์ž๋Š” ์ดˆ๊ธฐ ๋ Œ๋”๋ง ์ถœ๋ ฅ์„ ๋ณผ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋‹ค์Œ ๋กœ๋”ฉ ๋ฐ hydration์ด ์™„๋ฃŒ๋˜๋ฉด Effect๊ฐ€ ์‹คํ–‰๋˜์–ด didMount๋ฅผ true๋กœ ์„ค์ •ํ•˜๋ฉด์„œ ๋‹ค์‹œ ๋ Œ๋”๋ง์ด ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์ด๋กœ์จ ํด๋ผ์ด์–ธํŠธ ์ „์šฉ ๋ Œ๋”๋ง ์ถœ๋ ฅ์œผ๋กœ ์ „ํ™˜๋ฉ๋‹ˆ๋‹ค. Effect๋Š” ์„œ๋ฒ„์—์„œ ์‹คํ–‰๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ ์ดˆ๊ธฐ ์„œ๋ฒ„ ๋ Œ๋”๋ง ์ค‘์˜ didMount๋Š” false๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

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


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

Effect๊ฐ€ ์ปดํฌ๋„ŒํŠธ ๋งˆ์šดํŠธ ์‹œ 2๋ฒˆ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ Strict Mode๊ฐ€ ํ™œ์„ฑํ™”๋˜๋ฉด React๋Š” ์‹ค์ œ ์„ค์ • ์ด์ „์— ์„ค์ •๊ณผ ์ •๋ฆฌ๋ฅผ ํ•œ๋ฒˆ ๋” ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ Effect์˜ ๋กœ์ง์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๊ตฌํ˜„๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ์ŠคํŠธ๋ ˆ์Šค ํ…Œ์ŠคํŠธ์ž…๋‹ˆ๋‹ค. ์ด์— ๋”ฐ๋ผ ๋ˆˆ์— ๋„๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋ฉด ์ •๋ฆฌ ํ•จ์ˆ˜์— ์–ด๋–ค ๋กœ์ง์ด ๋ˆ„๋ฝ๋˜์—ˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ •๋ฆฌ ํ•จ์ˆ˜๋Š” ์„ค์ • ํ•จ์ˆ˜๊ฐ€ ์ˆ˜ํ–‰ํ•œ ๊ฒƒ์„ ์ค‘์ง€ํ•˜๊ฑฐ๋‚˜ ๋˜๋Œ๋ฆด ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์ธ ์ง€์นจ์œผ๋กœ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์„ค์ •์ด ํ•œ๋ฒˆ ํ˜ธ์ถœ๋˜๋Š” ๊ฒƒ(๋ฐฐํฌ ํ™˜๊ฒฝ๊ณผ ๊ฐ™์ด)๊ณผ ์„ค์ • โ†’ ์ •๋ฆฌ โ†’ ์„ค์ • ์ˆœ์„œ๋กœ ํ˜ธ์ถœ๋˜๋Š” ๊ฒƒ์„ ๊ตฌ๋ณ„ํ•  ์ˆ˜ ์—†์–ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด ๋ฒ„๊ทธ๋ฅผ ์ฐพ๋Š” ๋ฐ ์–ด๋–ป๊ฒŒ ๋„์›€์ด ๋˜๋ฉฐ, ๋กœ์ง์„ ์–ด๋–ป๊ฒŒ ์ˆ˜์ •ํ•˜๋Š”์ง€์— ์ž์„ธํžˆ ์•Œ์•„๋ณด๋ ค๋ฉด ์—ฌ๊ธฐ๋ฅผ ์ฝ์–ด๋ณด์„ธ์š”.


Effect๊ฐ€ ๋งค ๋ฆฌ๋ Œ๋”๋ง๋งˆ๋‹ค ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

๋จผ์ € ์˜์กด์„ฑ ๋ฐฐ์—ด์— ๊ฐ’์„ ์ถ”๊ฐ€ํ–ˆ๋Š”์ง€ ํ™•์ธํ•ด ๋ณด์„ธ์š”.

useEffect(() => {
// ...
}); // ๐Ÿšฉ No dependency array: re-runs after every render!

์˜์กด์„ฑ ๋ฐฐ์—ด์„ ๋ช…์‹œํ–ˆ์Œ์—๋„ Effect๊ฐ€ ์—ฌ์ „ํžˆ ๋ฐ˜๋ณตํ•ด์„œ ์‹คํ–‰๋œ๋‹ค๋ฉด ์˜์กด์„ฑ์ด ๋ Œ๋”๋ง๋งˆ๋‹ค ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์ฝ˜์†”์— ์˜์กด์„ฑ์„ ์ˆ˜๋™์œผ๋กœ ๊ธฐ๋กํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ๋””๋ฒ„๊น…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

useEffect(() => {
// ..
}, [serverUrl, roomId]);

console.log([serverUrl, roomId]);

๊ทธ๋‹ค์Œ ์ฝ˜์†”์—์„œ ๊ธฐ๋ก๋œ ๋‹ค๋ฅธ ๋ Œ๋”๋ง ๋ฐฐ์—ด์„ ๋งˆ์šฐ์Šค ์˜ค๋ฅธ์ชฝ ๋ฒ„ํŠผ์œผ๋กœ ํด๋ฆญํ•˜๊ณ  ๋‘ ๋ฐฐ์—ด ๋ชจ๋‘์— ๋Œ€ํ•ด ์ „์—ญ ๋ณ€์ˆ˜๋กœ ์ €์žฅ์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ ์š”์†Œ๊ฐ€ temp1์ด๊ณ  ๋‘ ๋ฒˆ์งธ ์š”์†Œ๊ฐ€ temp2๋ผ๊ณ  ๊ฐ€์ •ํ•˜๋ฉด ๋ธŒ๋ผ์šฐ์ € ์ฝ˜์†”์„ ์‚ฌ์šฉํ•˜์—ฌ ์–‘์ชฝ ๋ฐฐ์—ด์˜ ๊ฐ ์˜์กด์„ฑ์ด ๋™์ผํ•œ์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Object.is(temp1[0], temp2[0]); // Is the first dependency the same between the arrays?
Object.is(temp1[1], temp2[1]); // Is the second dependency the same between the arrays?
Object.is(temp1[2], temp2[2]); // ... and so on for every dependency ...

๋ Œ๋”๋ง๋งˆ๋‹ค ๋‹ค๋ฅธ ์˜์กด์„ฑ์„ ์ฐพ์•„๋ƒˆ๋‹ค๋ฉด ์ผ๋ฐ˜์ ์œผ๋กœ ๋‹ค์Œ ์ค‘ ํ•˜๋‚˜์˜ ๋ฐฉ๋ฒ•์œผ๋กœ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ตœํ›„์˜ ์ˆ˜๋‹จ์œผ๋กœ (์ด๋Ÿฌํ•œ ๋ฐฉ๋ฒ•๋“ค์ด ๋„์›€์ด ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ), useMemo๋‚˜ useCallback(ํ•จ์ˆ˜์˜ ๊ฒฝ์šฐ)์„ ์ด์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


Effect๊ฐ€ ๋ฌดํ•œ ๋ฐ˜๋ณต๋ฉ๋‹ˆ๋‹ค.

Effect๊ฐ€ ๋ฌดํ•œ ๋ฐ˜๋ณต๋˜๋ ค๋ฉด ๋‹ค์Œ ๋‘ ๊ฐ€์ง€ ์กฐ๊ฑด์ด ์ถฉ์กฑ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค..

  • Effect์—์„œ state๋ฅผ ์—…๋ฐ์ดํŠธํ•จ.
  • ๋ณ€๊ฒฝ๋œ state๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง์„ ์œ ๋ฐœํ•˜๋ฉฐ, ์ด์— ๋”ฐ๋ผ Effect์˜ ์ข…์†์„ฑ์ด ๋ณ€๊ฒฝ๋จ.

๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์ „์— Effect๊ฐ€ ์™ธ๋ถ€ ์‹œ์Šคํ…œ(DOM, ๋„คํŠธ์›Œํฌ, ์„œ๋“œํŒŒํ‹ฐ ์œ„์ ฏ ๋“ฑ)์— ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋Š”์ง€ ์Šค์Šค๋กœ ์ž๋ฌธํ•ด๋ณด์„ธ์š”. Effect์—์„œ ์™œ state๋ฅผ ๋ณ€๊ฒฝํ–ˆ๋‚˜์š”? ๋ณ€๊ฒฝ๋œ state๊ฐ€ ์™ธ๋ถ€ ์‹œ์Šคํ…œ๊ณผ ๋™๊ธฐํ™”๋๋‚˜์š”? ๋˜๋Š” Effect๋ฅผ ํ†ตํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ฐ์ดํ„ฐ ํ๋ฆ„์„ ๊ด€๋ฆฌํ•˜๋ ค๊ณ  ํ•˜๋Š” ๊ฑด๊ฐ€์š”?

์™ธ๋ถ€ ์‹œ์Šคํ…œ์ด ์—†๋‹ค๋ฉด Effect๋ฅผ ์ œ๊ฑฐํ•ด์„œ ๋กœ์ง์„ ๋‹จ์ˆœํ™”ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ๊ณ ๋ คํ•ด๋ณด์„ธ์š”.

๋งŒ์•ฝ ์‹ค์ œ๋กœ ์–ด๋–ค ์™ธ๋ถ€ ์‹œ์Šคํ…œ๊ณผ ๋™๊ธฐํ™” ์ค‘์ด๋ผ๋ฉด Effect๊ฐ€ state๋ฅผ ์–ธ์ œ ์–ด๋–ค ์กฐ๊ฑด์—์„œ ์—…๋ฐ์ดํŠธํ•ด์•ผ ํ•˜๋Š”์ง€์— ๋Œ€ํ•ด ๊ณ ๋ คํ•ด ๋ณด์„ธ์š”. ์ปดํฌ๋„ŒํŠธ์˜ ์‹œ๊ฐ์  ์ถœ๋ ฅ์— ์˜ํ–ฅ์„ ์ฃผ๋Š” state๊ฐ€ ๋ณ€ํ–ˆ๋‚˜์š”? ๋ Œ๋”๋ง์— ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”์ ํ•ด์•ผ ํ•œ๋‹ค๋ฉด ๋ฆฌ๋ Œ๋”๋ง์„ ์•ผ๊ธฐํ•˜์ง€ ์•Š๋Š” ref๊ฐ€ ๋” ์ ํ•ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Effect๊ฐ€ ํ•„์š” ์ด์ƒ์œผ๋กœ state๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š”์ง€(๋ฆฌ๋ Œ๋”๋ง์„ ์•ผ๊ธฐํ•˜์ง€ ์•Š๋„๋ก) ํ™•์ธํ•ด ๋ณด์„ธ์š”.

๋งˆ์ง€๋ง‰์œผ๋กœ Effect๊ฐ€ ์ œ๋Œ€๋กœ ๋œ ์‹œ์ ์— state๋ฅผ ์—…๋ฐ์ดํŠธํ–ˆ์ง€๋งŒ ์—ฌ์ „ํžˆ ๋ฌดํ•œ ๋ฐ˜๋ณต๋˜๋Š” ๊ฒฝ์šฐ, ํ•ด๋‹น state์˜ ์—…๋ฐ์ดํŠธ๊ฐ€ Effect์˜ ์ข…์†์„ฑ์˜ ๋ณ€๊ฒฝ์„ ์•ผ๊ธฐํ–ˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ข…์†์„ฑ ๋ณ€๊ฒฝ์„ ๋””๋ฒ„๊น…ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ฝ์–ด๋ณด์„ธ์š”.


์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งˆ์šดํŠธ ํ•ด์ œ๋˜์ง€ ์•Š์•˜์Œ์—๋„ ์ •๋ฆฌ ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

์ •๋ฆฌ ํ•จ์ˆ˜๋Š” ๋งˆ์šดํŠธ ํ•ด์ œ ์‹œ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋ณ€๊ฒฝ๋œ ์ข…์†์„ฑ์œผ๋กœ ์ธํ•œ ๋ชจ๋“  ๋ฆฌ๋ Œ๋”๋ง ์ „์— ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋Š” React๊ฐ€ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งˆ์šดํŠธ๋œ ์งํ›„์— ํ•œ ๋ฒˆ ๋” ์„ค์ •๊ณผ ์ •๋ฆฌ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

์„ค์ • ์ฝ”๋“œ์™€ ์ƒ์‘ํ•˜๋Š” ์ •๋ฆฌ ์ฝ”๋“œ๊ฐ€ ์—†๋‹ค๋ฉด ๋ณดํ†ต์€ ์ฝ”๋“œ์— ๋ฌธ์ œ๊ฐ€ ์žˆ์„ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์Šต๋‹ˆ๋‹ค.

useEffect(() => {
// ๐Ÿ”ด Avoid: Cleanup logic without corresponding setup logic
return () => {
doSomething();
};
}, []);

์ •๋ฆฌ ๋กœ์ง์€ ์„ค์ • ๋กœ์ง๊ณผ โ€˜๋Œ€์นญโ€™์ด์–ด์•ผ ํ•˜๋ฉฐ ์„ค์ •์ด ์ˆ˜ํ–‰ํ•œ ๊ฒƒ์„ ์ค‘์ง€ํ•˜๊ฑฐ๋‚˜ ๋˜๋Œ๋ฆด ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, [serverUrl, roomId]);

Effect์˜ ์ƒ๋ช…์ฃผ๊ธฐ์™€ ์ปดํฌ๋„ŒํŠธ์˜ ์ƒ๋ช…์ฃผ๊ธฐ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋‹ค๋ฅธ์ง€ ํ™•์ธํ•ด ๋ณด์„ธ์š”.


Effect๊ฐ€ ์‹œ๊ฐ์ ์ธ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋ฉฐ, ์‹คํ–‰๋˜๊ธฐ ์ „์— ๊นœ๋นก์ž„์ด ๋ณด์ž…๋‹ˆ๋‹ค.

Effect๊ฐ€ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํ™”๋ฉด์„ ๊ทธ๋ฆฌ๋Š” ๊ฒƒ์„ ์ฐจ๋‹จํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ useEffect๋ฅผ useLayoutEffect๋กœ ๋Œ€์ฒดํ•˜์„ธ์š”. ์ด๊ฒƒ์€ ๋Œ€๋ถ€๋ถ„์˜ Effect์—๋Š” ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ธŒ๋ผ์šฐ์ € ํŽ˜์ธํŒ… ์ด์ „์— Effect๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•œ ๊ฒฝ์šฐ์—๋งŒ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์‚ฌ์šฉ์ž๊ฐ€ ๋ณด๊ธฐ ์ „์— ํˆดํŒ์˜ ์œ„์น˜๋ฅผ ์ธก์ •ํ•˜๊ณ  ์ง€์ •ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.