리액트 훅 완전정복! 초보자를 위한 주요 훅 사용법 가이드
리액트 초보자라면 꼭 알아야 할 필수 훅들!
직접 써보고 '와, 이게 진짜 편하네' 했던
그 기능들만 모았습니다.
안녕하세요!
리액트를 공부하다 보면 꼭 만나게 되는 개념 중 하나가 바로 "훅(Hook)"이죠. 함수형 컴포넌트 안에서 상태를 관리하거나 생명주기 효과를 처리할 때 빠질 수 없는 친구인데요.
이번 글에서는 useState, useEffect, useRef, useMemo, useCallback, useContext 등 자주 사용되는 훅들을 초보자 눈높이에 맞춰 쉽게 설명해드릴게요.
직접 써보며 헷갈렸던 부분까지 모두 정리했으니 끝까지 함께 해주세요!
목차
1. useState 🧠 상태를 다루는 가장 기본 훅
useState는 리액트 훅 중에서 가장 자주 사용되는 기능 중 하나예요.
컴포넌트 안에서 변하는 값, 예를 들면 버튼 클릭 횟수나 입력 필드의 내용 등을 저장하고 관리하는 데 사용됩니다.
기존의 클래스형 컴포넌트에서 this.state
와 this.setState()
를 사용하던 것을 함수형 컴포넌트에서 더 간단하게 구현할 수 있게 해주는 거죠!
📌 기본 문법
const [상태변수, 상태변경함수] = useState(초기값);
🧪 실습 예제
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>현재 카운트: {count}</p>
<button onClick={() => setCount(count + 1)}>증가</button>
</div>
);
}
💡 사용 시 팁
- 여러 개의 상태가 필요한 경우,
useState
를 여러 번 선언해도 전혀 문제 없어요! - 상태가 바뀌면 해당 컴포넌트는 자동으로 다시 렌더링돼요. 이걸 잘 활용해야 렌더링 최적화에 도움이 돼요.
📋 요약 정리
항목 | 내용 |
---|---|
사용 목적 | 컴포넌트 내 상태값 관리 |
초기값 | 숫자, 문자열, 객체 등 다양한 자료형 가능 |
업데이트 방식 | 전용 함수(set함수)를 통해 변경 |
처음 리액트를 접하면 상태라는 개념이 어렵게 느껴질 수도 있지만, useState만 잘 이해해도 리액트 앱의 절반은 완성한 셈이에요!
다음은 useEffect를 통해 컴포넌트의 생명주기를 어떻게 다룰 수 있는지 알아볼게요.
2. useEffect 🔁 생명주기 대체 훅
리액트 함수형 컴포넌트는 생명주기 메서드가 없죠.
그 대신 useEffect를 사용하면 componentDidMount
, componentDidUpdate
, componentWillUnmount
처럼 동작하게 만들 수 있어요.
예를 들어,
컴포넌트가 처음 화면에 나타날 때 데이터를 불러온다거나, 특정 값이 바뀔 때마다 API 요청을 보낸다거나, 또는 컴포넌트가 사라질 때 정리 작업(clean-up)을 해야 할 때 아주 유용합니다.
📌 기본 문법
useEffect(() => {
// 실행할 코드
return () => {
// 정리(clean-up) 코드
};
}, [의존성 배열]);
🧪 실습 예제
import { useEffect, useState } from 'react';
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setSeconds(prev => prev + 1);
}, 1000);
return () => {
clearInterval(interval);
};
}, []);
return <p>{seconds}초 지났습니다.</p>;
}
💡 동작 방식에 따른 분기
의존성 배열 | 실행 시점 |
---|---|
[] | 컴포넌트 최초 렌더링 시 한 번만 |
[count] | count 값이 바뀔 때마다 실행 |
의존성 없음 | 모든 렌더링마다 실행 |
📣 주의할 점
- 의존성 배열을 제대로 설정하지 않으면 무한 루프에 빠질 수 있어요!
- 정리(clean-up) 함수는 컴포넌트가 사라질 때뿐만 아니라 다음 실행 전에 호출되므로 유의해야 해요.
이제 useEffect를 통해 비동기 작업이나 외부 이벤트도 자유롭게 처리할 수 있어요.
다음으로는, DOM을 직접 제어하거나 변경되지 않는 값을 저장할 수 있는 useRef를 알아보러 가볼까요?
3. useRef 🔍 DOM 제어와 값 기억에 강력
useRef는 리액트에서 아주 유용하지만, 처음엔 그 쓰임새가 헷갈릴 수 있는 훅이에요.
주로 DOM 요소를 직접 제어하거나 렌더링 사이에도 유지되어야 하는 값을 저장할 때 사용해요.
이 훅의 특징은 값을 변경해도 컴포넌트를 다시 렌더링하지 않는다는 거예요.
그래서 카운트 같은 상태는 useState
를 쓰지만, 변경되더라도 화면에 영향을 주지 않아야 하는 값은 useRef
를 쓰는 게 더 적절해요.
📌 기본 문법
const myRef = useRef(초기값);
// 접근: myRef.current
🧪 DOM 조작 예제
import { useRef } from 'react';
function FocusInput() {
const inputRef = useRef();
const handleClick = () => {
inputRef.current.focus();
};
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={handleClick}>포커스 주기</button>
</div>
);
}
🧠 상태 저장 예제 (렌더링 방지)
import { useRef, useState, useEffect } from 'react';
function RenderCount() {
const [value, setValue] = useState('');
const count = useRef(1);
useEffect(() => {
count.current += 1;
});
return (
<div>
<input value={value} onChange={(e) => setValue(e.target.value)} />
<p>렌더링 횟수: {count.current}</p>
</div>
);
}
📋 요약 정리
기능 | 설명 |
---|---|
DOM 접근 | ref로 지정한 요소에 직접 접근 가능 |
렌더링 영향 없음 | 값 변경 시 컴포넌트 재렌더링 발생 안 함 |
유지되는 값 | 렌더링 사이에 값을 유지하고 싶을 때 사용 |
useRef는 보이지 않는 메모장 같아요.
화면에 영향을 주지 않으면서 내부 데이터를 보관할 수 있거든요.
이 다음엔 계산이 무거운 작업을 최적화하는 useMemo로 넘어가볼게요!
4. useMemo 📦 계산 결과를 메모이제이션
리액트에서 어떤 연산이 너무 자주 반복되면, 앱이 느려지는 원인이 될 수 있어요.
useMemo는 이런 상황에서 비용이 많이 드는 계산을 기억해두고, 필요할 때만 다시 계산하게 도와줘요.
쉽게 말해서,
어떤 계산된 값이 매번 바뀌지 않는데도 계속 다시 계산되는 게 비효율적이라면,
useMemo
로 감싸서 “얘는 값 안 바뀌면 계산 다시 안 해!”라고 알려주는 거예요.
📌 기본 문법
const memoizedValue = useMemo(() => {
return 복잡한계산(입력값);
}, [의존성값]);
🧪 실습 예제
import { useState, useMemo } from 'react';
function ExpensiveComponent() {
const [number, setNumber] = useState(1);
const [text, setText] = useState('');
const factorial = (n) => {
console.log('계산 중...');
return n <= 1 ? 1 : n * factorial(n - 1);
};
const memoizedFactorial = useMemo(() => factorial(number), [number]);
return (
<div>
<p>{number}! = {memoizedFactorial}</p>
<button onClick={() => setNumber(prev => prev + 1)}>증가</button>
<input value={text} onChange={(e) => setText(e.target.value)} />
</div>
);
}
💡 사용 시 주의할 점
- 무조건 쓰면 성능이 좋아지진 않아요! 진짜 계산이 무거운 경우에만 쓰는 게 좋아요.
- 의존성 배열을 잘못 설정하면 오래된 값을 계속 사용하게 될 수도 있어요.
📋 요약 정리
기능 | 설명 |
---|---|
계산 결과 캐싱 | 의존값이 변하지 않으면 이전 계산 결과 재사용 |
최적화 대상 | 복잡하거나 반복되는 계산 작업 |
렌더링 영향 | 렌더링마다 계산 방지 가능 |
정말 계산이 무거운 로직에만 쓰는 게 핵심이에요.
불필요한 useMemo 남용은 오히려 독!
다음은 콜백 함수 자체를 메모이제이션해서 불필요한 재렌더링을 막는 useCallback 훅을 알아봅시다!
5. useCallback 🔄 콜백 함수를 캐싱
useCallback은 useMemo
와 비슷하지만, 함수 자체를 기억하는 데 초점을 맞춘 훅이에요.
특히 하위 컴포넌트에 함수를 props로 넘길 때 불필요한 재렌더링을 막기 위해 많이 사용돼요.
리액트에서는 함수도 매번 새로 만들어지는 값이에요.
그래서 의도치 않게 하위 컴포넌트가 렌더링 될 필요가 없어도 재렌더링되곤 해요.
이때 useCallback을 사용하면 함수를 고정시켜
서 그런 현상을 막을 수 있어요.
📌 기본 문법
const memoizedCallback = useCallback(() => {
함수내용
}, [의존성값]);
🧪 실습 예제
import { useState, useCallback } from 'react';
function Parent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log('버튼 클릭!');
}, []);
return (
<div>
<p>카운트: {count}</p>
<button onClick={() => setCount(prev => prev + 1)}>증가</button>
<Child onClick={handleClick} />
</div>
);
}
function Child({ onClick }) {
console.log('Child 렌더링');
return <button onClick={onClick}>클릭</button>;
}
💡 언제 써야 할까?
- 하위 컴포넌트가
React.memo
를 사용 중일 때, props로 넘기는 함수가 매번 바뀌면 재렌더링이 발생해요. - useCallback을 쓰면 함수 참조가 고정돼서 렌더링 최적화가 가능해져요.
📋 요약 정리
기능 | 설명 |
---|---|
함수 메모이제이션 | 렌더링 시 동일한 함수 참조 유지 |
최적화 대상 | React.memo와 함께 하위 컴포넌트 렌더링 방지 |
주의사항 | 의존성 배열 정확히 지정해야 효과 있음 |
useCallback은 성능 최적화를 위한 작은 도구예요.
얘를 잘 쓰면 쓸수록 렌더링 횟수를 줄여 앱이 쾌적해집니다.
다음은 컴포넌트 간 전역 상태를 쉽게 공유할 수 있는 useContext를 살펴볼 차례예요!
6. useContext 🌐 전역 상태를 간단하게 공유
리액트 앱이 커지다 보면 여러 컴포넌트에서 공통된 데이터를 사용해야 하는 상황이 많아져요.
예를 들어 사용자 정보, 테마 설정, 로그인 상태 등은 전역 상태로 관리하는 게 편하죠.
이럴 때 사용하는 게 useContext
입니다.
useContext
는 컴포넌트 트리 전반에 걸쳐 값을 전달하는 React Context를 쉽게 읽을 수 있도록 도와주는 훅이에요.
복잡한 props 전달 없이도 데이터를 쏙쏙! 받아올 수 있죠.
📌 기본 구조
// Context 생성
const MyContext = createContext();
// 상위 컴포넌트에서 Provider로 감싸기
<MyContext.Provider value={공유할값}>
<하위컴포넌트 />
</MyContext.Provider>
// 하위 컴포넌트에서 사용
const value = useContext(MyContext);
🧪 실습 예제
import React, { createContext, useContext } from 'react';
const ThemeContext = createContext('light');
function App() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button>현재 테마: {theme}</button>;
}
💡 언제 쓰면 좋을까?
- 테마 설정, 언어 설정 등 앱 전체에 영향을 주는 값이 있을 때
- 로그인 정보처럼 여러 컴포넌트에서 접근이 필요한 상태가 있을 때
📋 요약 정리
기능 | 설명 |
---|---|
전역 상태 공유 | props 없이 하위 컴포넌트에서 상태 사용 가능 |
Context API | createContext + useContext 조합으로 구성 |
성능 유의사항 | 변경 시 모든 하위 컴포넌트 재렌더링 가능성 있음 |
이제 useContext까지 익혔다면, 리액트의 주요 훅들은 거의 섭렵한 거예요! 💪
다음 단계는 지금까지 배운 내용을 정리하고, 각 훅을 실제 프로젝트에서 어떻게 조합해서 사용할 수 있는지 마무리하면서 이야기해볼게요.
지금까지 리액트의 주요 훅들—useState
, useEffect
, useRef
, useMemo
, useCallback
, useContext
—을 하나씩 차근차근 살펴봤어요.
처음에는 각 훅이 언제, 왜, 어떻게 쓰는지 헷갈릴 수 있지만, 실제 프로젝트에서 직접 활용해보면 어느 순간 손에 익는 도구처럼 자연스럽게 쓰게 됩니다.
각 훅은 역할과 목적이 명확하기 때문에 상황에 맞춰 조합하는 연습을 많이 해보는 게 좋아요.
예를 들어 useContext
로 전역 상태를 만들고, 그 안에서 useMemo
나 useCallback
으로 불필요한 렌더링을 줄이는 구조는 실전에서도 많이 사용돼요.
처음 훅을 공부할 땐 “이걸 왜 쓰지?” 싶은 느낌이 들 수 있지만, 리액트를 리액트답게 쓰는 핵심이 바로 이 훅들에 있답니다.
앞으로도 계속해서 다양한 상황에 훅을 적용해보면서 자신만의 개발 노하우를 만들어 보세요! 🚀
'React' 카테고리의 다른 글
리액트 동적 화면 처리 완전 정복 (0) | 2025.04.24 |
---|---|
리액트 이벤트 처리 마스터하기 : 초보자를 위한 실전 가이드 (1) | 2025.04.24 |
리액트 컴포넌트 설계 마스터: Atomic Design, 합성, 상속 완전정복 (0) | 2025.04.23 |
리액트 컴포넌트의 다양한 구성 방법 (0) | 2025.04.23 |
Tailwind CSS 최신버전 설치와 사용법 가이드 (Vite 프로젝트 기준) (1) | 2025.04.23 |