
๋ค์ด๊ฐ๋ฉฐ
์์ ์๊ฐํ๋ค์ํผ React๋ ์ ์ธํ(Declarative UI) ํ๋ก๊ทธ๋๋ฐ ๊ตฌ์กฐ์ ๋๋ค. ์ด๋ UI๋ฅผ ์ด๋ป๊ฒ ๋ณ๊ฒฝํ ์ง๊ฐ ์๋๋ผ ์ด๋ค ๋ชจ์ต์ผ์ง๋ก ์ ์ํฉ๋๋ค. ์ด๋ค ๋ชจ์ต์ผ์ง์ ๋ํ state๋ง ๊ด๋ฆฌํ๋ฉด React๊ฐ ์์์ ํ๋ฉด์ ์ ๋ฐ์ดํธํด์ค๋๋ค. ๋ฐ๋ผ์ ๋ฆฌ์กํธ ๊ฐ๋ฐํ ๋๋ UI Component์ ์ํ๋ฅผ ๊ด๋ฆฌํ๊ณ ์กฐ์ํ๋๋ฐ ์จ ์ ๊ฒฝ์ ์ฐ๋ฉด ๋ฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ React์ Hook์ ํจ์ํ ์ปดํฌ๋ํธ์์ ์ํ(state)์ ๋ผ์ดํ์ฌ์ดํด(lifecycle)์ ๋ค๋ฃฐ ์ ์๊ฒ ํด์ฃผ๋ ๊ธฐ๋ฅ์ ๋๋ค.
1. useState – ์ํ(State) ๊ด๋ฆฌ
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0); // ์ด๊ธฐ๊ฐ 0
return (
<div>
<p>ํ์ฌ ๊ฐ: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
);
}
๋์ ์๋ฆฌ
- useState(0) → count = 0์ผ๋ก ์ด๊ธฐํ.
- setCount ํธ์ถ ์ React๊ฐ ์ํ๋ฅผ ์ ๋ฐ์ดํธ + ์ปดํฌ๋ํธ ๋ฆฌ๋ ๋๋ง.
- ์๋ก ๋ ๋๋ง๋ ๋ count๋ ์ต์ ๊ฐ์ผ๋ก ๋ฐ์๋จ.
ํน์ง
- ์ํ๊ฐ ๋ณํ๋ฉด ํ๋ฉด์ด ์๋์ผ๋ก ๋ค์ ๊ทธ๋ ค์ง.
- ํ ์ปดํฌ๋ํธ ์์ ์ฌ๋ฌ ๊ฐ์ ์ํ๋ฅผ ๊ฐ์ง ์ ์์.
const [name, setName] = useState("Jisang");
const [age, setAge] = useState(30);
2. useEffect – ๋ถ์ํจ๊ณผ(Side Effect) ์ฒ๋ฆฌ
๊ธฐ๋ณธ ์ค๋ช
React ์ปดํฌ๋ํธ๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ ๋๋ง → ํ๋ฉด ํ์๊น์ง๋ง ๋ด๋นํฉ๋๋ค.
๊ทธ ์ธ์:
- ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ (API ์์ฒญ)
- DOM ์ง์ ์กฐ์
- ํ์ด๋จธ ์ค์
๋ฑ์ **๋ถ์ํจ๊ณผ(= side effect)**๋ผ๊ณ ๋ถ๋ฆ ๋๋ค. ์ด๊ฒ์ ์ฒ๋ฆฌํ๋ Hook์ด useEffect์ ๋๋ค.
import { useState, useEffect } from "react";
function Timer() {
const [count, setCount] = useState(0);
// count๊ฐ ๋ฐ๋ ๋๋ง๋ค ์คํ๋จ
useEffect(() => {
console.log("count๊ฐ ๋ณ๊ฒฝ๋์์ต๋๋ค:", count);
}, [count]);
return (
<div>
<p>{count}์ด</p>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
);
}
useEffect ๊ตฌ์กฐ
useEffect(() => {
// ์คํํ ์ฝ๋
return () => {
// cleanup (์ ๋ฆฌ ์์
)
};
}, [์์กด์ฑ๋ฐฐ์ด]);
- [] (๋น ๋ฐฐ์ด): ๋ง์ดํธ๋ ๋ 1๋ฒ๋ง ์คํ (ex: API ํธ์ถ)
- [count]: count๊ฐ ๋ฐ๋ ๋๋ง๋ค ์คํ
- ์๋ต ์: ๋งค ๋ ๋๋ง๋ง๋ค ์คํ(โ ๏ธ๋น ๋ฐฐ์ด๊ณผ ์๋ต์ ๋ค๋ฆ ์ฃผ์!)
์: ๋ง์ดํธ ์ API ํธ์ถ
useEffect(() => {
fetch("/api/data")
.then(res => res.json())
.then(data => console.log(data));
}, []); // ์ฒ์ ํ ๋ฒ๋ง ์คํ
์: ํ์ด๋จธ ์ค์ & ์ ๋ฆฌ
useEffect(() => {
const timer = setInterval(() => {
console.log("1์ด๋ง๋ค ์คํ");
}, 1000);
return () => clearInterval(timer); // ์ธ๋ง์ดํธ ์ ์ ๋ฆฌ
}, []);
์์กด์ฑ ๋ฐฐ์ด
Effect ์์์ ์ฐธ์กฐํ๋ ๋ชจ๋ ์ธ๋ถ ๊ฐ(๋ณ์, ํจ์)์ ์์กด์ฑ ๋ฐฐ์ด์ ํฌํจํด์ผ ํ๋ค
์ฌ๊ธฐ์ ์ธ๋ถ ๊ฐ์ด๋:
- props
- state
- ์ปดํฌ๋ํธ ๋ฐ๊นฅ์์ ์ ์ธ/์ ์๋ ๋ณ์, ํจ์(useCallback์ผ๋ก ๊ฐ์ธ์ง ์์๋ค๋ฉด ๋งค ๋ ๋๋ง๋ค ์๋ก ์์ฑ๋จ)
๋ฆฌ์ํธ๋ ์ด๋ฒคํธ๋ก ์ธํด re-rendering์ ๋ง์ด ํฉ๋๋ค. re-rendering์ ํน์ ๊ฐ์ ๋ณ๊ฒฝ์ด ์๋ ๊ฒฝ์ฐ ์คํ๋๋ Hook์ด useEffect์ ๋๋ค. ๋ง์ฝ ์์กด์ฑ ๋ฐฐ์ด์ ํ์๋ก ๋ค์ด๊ฐ์ผ ํ์ง๋ง ์๋ค์ด๊ฐ๊ฒ ๋๋ ๊ฒฝ์ฐ์๋ ์ด๋ป๊ฒ ๋๋์ง ์ดํด๋ด ์๋ค.
- ์์กด์ฑ ๋ฐฐ์ด์ ํ์ํ ๋ณ์๊ฐ ์๋ค์ด๊ฐ ๊ฒฝ์ฐ
- re-rendering ์ ํด๋น ๋ณ์๊ฐ ๋ณ๊ฒฝ๋์์์๋ useEffect ํจ์๊ฐ ์คํ๋์ง ์์์ ์ ๋ฐ์ดํธ๊ฐ ๋์ง ์์
- ์์กด์ฑ ๋ฐฐ์ด์ ํ์ํ ํจ์๊ฐ ์๋ค์ด๊ฐ ๊ฒฝ์ฐ
- ์ปดํฌ๋ํธ ๋ด๋ถ์์ ์ ์ธ๋ ํจ์๋ฅผ useEffect์์ ์ฌ์ฉํ์ง๋ง ์์กด์ฑ ๋ฐฐ์ด์ ๋ฃ์ง ์์ผ๋ฉด, re-rendering ํ ๋๋ง๋ค ํจ์๊ฐ ์๋ก ๋ง๋ค์ด์ง → ํ์
- ์ปดํฌ๋ํธ ๋ฐ๊นฅ์ ์ ์ธ๋ ์ ํธ ํจ์๋ผ๋ฉด ์์กด์ฑ ๋ฐฐ์ด์ ๋ฃ์ ํ์ ์์
์ ๋ฆฌ๋ ๊ท์น
- Effect ์์์ ์ฐ๋ ๋ชจ๋ ๋ณ์/ํจ์๋ฅผ ์์กด์ฑ ๋ฐฐ์ด์ ๋ฃ๋๋ค.
- ๋ค๋ง, ๋ถ๋ณ(immutable) ๋ณด์ฅ๋๋ ๊ฒ์ ์๋ต ๊ฐ๋ฅ.
- ์: importํ ์ธ๋ถ ํจ์, ์์, ref (useRef๋ก ์ ์ธ๋ ๋ณ์)
- ํจ์๊ฐ ๋งค๋ฒ ์๋ก ์์ฑ๋๋ ๊ตฌ์กฐ๋ผ๋ฉด → useCallback์ผ๋ก ๊ฐ์ธ๊ณ ๋ฃ๋๋ค.
- ์ปดํฌ๋ํธ ์ธ๋ถ์ ์ ์๋ ํจ์๋ ์์กด์ฑ ๋ฐฐ์ด์ ๋ฃ๋๋ผ๋ ํจ์ ์ฐธ์กฐ๊ฐ ๋ณํ์ง ์์ผ๋ฏ๋ก ์ฌ์ค์ []์ ๋์ผํ๊ฒ ๋์ํ๋ค(ํญ์ ๊ฐ์ ์ฐธ์กฐ๋ผ์).
- useEffect์์ ๋ฆฌํดํ๋ ํจ์๋ ์ปดํฌ๋ํธ ์ธ๋ง์ดํธ๋ ๋ ํธ์ถ๋จ.
- ESLint์ react-hooks/exhaustive-deps ๊ท์น์ ๊ธฐ๋ณธ์ผ๋ก ๋ฐ๋ฅด๋ ๊ฒ ์์ .
3. useCallback
๊ฐ๋
- ํน์ ํจ์๋ฅผ ๋ฉ๋ชจ์ด์ ์ด์ (caching)ํ๋ Hook
- ์์กด์ฑ์ด ๋ฐ๋์ง ์๋ ํ ๊ฐ์ ํจ์ ๊ฐ์ฒด๋ฅผ ์ฌ์ฌ์ฉ
- ์ฆ, ๋ถํ์ํ re-rendering ๋ฐฉ์ง ๋ชฉ์ ์์ ์ฌ์ฉ๋จ
- comst memoizedCallback = useCallback(์ฝ๋ฐฑ ํจ์, ์์กด์ฑ ๋ฐฐ์ด);
- ์์กด์ฑ ๋ฐฐ์ด์ ๋ค์ด๊ฐ์๋ ๋ณ์๊ฐ ๋ณํ์ ๊ฒฝ์ฐ์๋ง ์ฝ๋ฐฑ ํจ์๋ฅผ ๋ค์ ์ ์ํด์ ๋ฆฌํดํจ
์์
import React, { useState, useCallback } from "react";
function Child({ onClick }: { onClick: () => void }) {
console.log("Child ๋ ๋๋ง");
return <button onClick={onClick}>Click</button>;
}
export default function Parent() {
const [count, setCount] = useState(0);
// โ
count๊ฐ ๋ฐ๋ ๋๋ง ์๋ก์ด ํจ์๊ฐ ์์ฑ๋จ
const handleClick = useCallback(() => {
setCount((c) => c + 1);
}, []);
return (
<div>
<p>Count: {count}</p>
<Child onClick={handleClick} />
</div>
);
}
๋ง์ฝ useCallback์ ์ฌ์ฉํ์ง ์์ผ๋ฉด Parent๊ฐ ๋ฆฌ๋ ๋๋ง ๋ ๋๋ง๋ค handleClick ํจ์๊ฐ ์๋ก ์์ฑ๋ฉ๋๋ค. count๊ฐ ๋ฐ๋์ง ์๋ ํ handleClick์ ์๋กญ๊ฒ ๋ง๋ค ํ์๊ฐ ์๊ธฐ ๋๋ฌธ์ useCallback์ผ๋ก ์ฌ์ฌ์ฉํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ count๊ฐ ๋ฐ๋ ๋๋ง ์๋ก์ด ํจ์๊ฐ ์์ฑ๋๋๋ก ํฉ๋๋ค.
4. useMemo
๊ฐ๋
- ๋ฉ๋ชจ์ด์ ์ด์ ๋ value๋ฅผ ๋ฆฌํดํ๋ ํ
- ์ฐ์ฐ๋์ด ๋์ ์์ ์ด ๋งค๋ฒ ๋ ๋๋ง๋ ๋๋ง๋ค ๋ฐ๋ณต๋๋ ๊ฒ์ ํผํ๊ธฐ ์ํด ์ฌ์ฉ
- const memoizedValue = useMemo(๊ฐ ์์ฑ ํจ์, ์์กด์ฑ ๋ฐฐ์ด)
- ์์กด์ฑ ๋ฐฐ์ด์ ๋ค์ด์๋ ๋ณ์๊ฐ ๋ณํ์ ๊ฒฝ์ฐ์๋ง ์๋ก ๊ฐ ์์ฑ ํจ์๋ฅผ ํธ์ถํ์ฌ ๊ฒฐ๊ด๊ฐ ๋ฐํ
์์
const memoizedValue = useMemo(() => {
return complexCalculation(a, b);
}, [a, b]);
useMemo vs useCallback
| ๊ตฌ๋ถ | useMemo | useCallback |
| ๋ฉ๋ชจ์ด์ ์ด์ ๋์ | ๊ฐ (Value) | ํจ์ (Function) |
| ๋ฐํ๊ฐ | ๊ณ์ฐ๋ ๊ฒฐ๊ณผ๊ฐ | ํจ์ ์์ฒด |
| ์ฃผ์ ๋ชฉ์ | ๋ฌด๊ฑฐ์ด ๊ณ์ฐ ๊ฒฐ๊ณผ ์บ์ฑ | ํจ์ ์ฌ์์ฑ ๋ฐฉ์ง |
| ์ฌ์ฉ ์์ | const list = useMemo(() => items.filter(...), [items]) | const onClick = useCallback(() => setCount(c+1), [c]) |
- ๊ณ์ฐ ๊ฒฐ๊ณผ๋ฅผ ์บ์ฑํ๊ณ ์ถ๋ค ๐ useMemo
- ํจ์ ์์ฒด๋ฅผ ์บ์ฑํ๊ณ ์ถ๋ค ๐ useCallback
5. useRef
๊ฐ๋
- mutableํ ๊ฐ์ ์ ์ฅํ๋ Hook
- .current ํ๋กํผํฐ๋ก ์ ๊ทผ
- ์ปดํฌ๋ํธ๊ฐ re-rendering๋๋๋ผ๋ ๊ฐ์ ๋ ํผ๋ฐ์ค ๊ฐ์ฒด๋ฅผ ๋ฐํ
- const refContainer = useRef(์ด๊ธฐ๊ฐ);
์์
1. DOM ์์ ์ง์ ์ ๊ทผ
function InputFocus() {
const inputRef = useRef<HTMLInputElement>(null);
const focusInput = () => {
inputRef.current?.focus(); // DOM ์์ ์ง์ ์ ๊ทผ
};
return (
<>
<input ref={inputRef} type="text" />
<button onClick={focusInput}>Focus</button>
</>
);
}
2. ๋ ๋๋ง๊ณผ ๋ฌด๊ดํ ๊ฐ ์ ์ฅ
function Timer() {
const intervalRef = useRef<number | null>(null);
useEffect(() => {
intervalRef.current = window.setInterval(() => {
console.log("tick");
}, 1000);
return () => {
if (intervalRef.current) clearInterval(intervalRef.current);
};
}, []);
return <p>Timer Running...</p>;
}
3. ์ํ ๋ณ๊ฒฝ ์์ด ๊ฐ ๊ธฐ์ตํ๊ธฐ
function Example() {
const renderCount = useRef(0);
renderCount.current += 1;
return <p>๋ ๋๋ง ํ์: {renderCount.current}</p>;
}
์ ๋ฆฌ
React ์ฝ๋์์ Hook์ ๋ณผ ๋ useState๋ฅผ ๋ง๋๋ฉด ์ด๋ค ์ํ๋ฅผ ๊ด๋ฆฌํ๋์ง ํ์ธํด๋ณด๋ฉด ์ข์ ๊ฒ ๊ฐ์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ useEffect๋ ์ด๋ค ๋ถ๊ฐํจ๊ณผ๋ฅผ ์คํํ๋์ง, ๊ทธ๋ฆฌ๊ณ ์์กด์ฑ ๋ฐฐ์ด์ด ๋ฌด์์ธ์ง๋ฅผ ํ์ธํ๋ฉด UI๋ฅผ ์ฒดํฌํ ๋ ๋์์ด ๋ ๊ฒ ๊ฐ์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ Hook์์ฒด๋ ํจ์ํ ์ปดํฌ๋ํธ์์ ์ํ์ ์๋ช ์ฃผ๊ธฐ๋ฅผ ๋ค๋ฃจ๋ ํ์ค ๋ฐฉ๋ฒ์ด๊ธฐ ๋๋ฌธ์ React์๋ง ๊ตญํ๋๋ ๊ฐ๋ ์ ์๋๋๋ค. ์ด๋ฒคํธ๊ธฐ๋ฐ์ ๋์๋ฐฉ์์์๋ Hook์ ํตํด ์ด๋ฒคํธ๋ฅผ ์ ๋ฌํ๊ฑฐ๋ ์ํ๋ณํ๋ฅผ ์๋ฆฌ๊ธฐ๋ ํฉ๋๋ค.
์ ๋ ์์ง์ State Hook์ด ์ต์ํ์ง ์์ต๋๋ค. ๋ค๋ง ๋ค์ํ ์ฝ๋์ ์คํ๋๋ UI๋ฅผ ๋ณด๋ฉด์ ์ต์ํด์ง๋ ค๊ณ ํ๊ณ ์์ต๋๋ค. ๊ทธ๋๋ ์๋ฒ์ ๋ค๋ฅด๊ฒ UI๋ ๋์ ๋ฐ๋ก ๋ณด์ด๊ธฐ ๋๋ฌธ์ ์ต์ํด์ง๋๋ฐ ์ค๋๊ฑธ๋ฆฌ์ง๋ ์์ ๊ฒ ๊ฐ์ต๋๋ค. React๋ฅผ ๋ค๋ฃจ๋ค๊ฐ ํท๊ฐ๋ฆฌ๋ฉด ๋ค์ ์์ ๋ณด๊ณ ์ต์ํด์ง๋ ํจํด์ ๋ฐ๋ณตํด๋ณด๊ฒ ์ต๋๋ค(Just do it).
'ํ๋ก๊ทธ๋๋ฐ ์ธ์ด > React & NextJS' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| React์ SPA ๊ทธ๋ฆฌ๊ณ Component (0) | 2025.09.10 |
|---|