React Hook이란?
- 함수형 컴포넌트에서 상태(state)와 생명주기(lifecycle) 기능을 사용할 수 있도록 도입된 기능
- 이전에는 클래스형 컴포넌트에서만 상태와 생명주기 관리를 할 수 있었지만, Hook이 등장하면서 함수형 컴포넌트로도 복잡한 로직을 간단하게 구현이 가능해짐
React Hook의 특징
- 함수형 컴포넌트를 강화 : 상태와 생명주기 관리를 위해 클래스형 컴포넌트를 사용할 필요가 없어짐
- 재사용 가능한 로직 : Custom Hook을 만들어 중복 코드를 줄이고 로직을 재사용 가능
- 가독성 향상 : 로직을 분리하고 구조화할 수 있어 코드가 더 깔끔해짐
- React 16.8 이상에서 사용 가능 :Hook은 React 16.8 버전에서 도입 됨
함수형 컴포넌트 ? 클래스형 컴포넌트 ?
함수형 컴포넌트
- JavaScript 함수처럼 작성
- React Hook(useState, useEffect 등)을 사용하여 상태와 생명주기 관리
- 구조가 간단하고 코드가 짧음
- 예시
- useState Hook을 사용하여 count 상태를 관리
- 버튼 클릭 시 setCount를 호출해 상태를 변경
- 단순하고 읽기 쉬운 코드 구조
import React, { useState } from 'react';
function Counter() {
// useState로 상태 관리
const [count, setCount] = useState(0);
return (
<div>
<p>현재 카운트: {count}</p>
<button onClick={() => setCount(count + 1)}>증가</button>
<button onClick={() => setCount(count - 1)}>감소</button>
</div>
);
}
export default Counter;
클래스형 컴포넌트
- ES6 클래스 문법을 사용하여 작성
- 상태를 관리하려면 this.state를 사용
- 상태를 업데이트하려면 this.setState를 호출
- 생명주기 메서드(componentDidMount, componentDidUpdate 등)를 사용하여 생명주기를 관리
- 예시
- state 객체를 사용하여 상태를 관리
- this.setState 메서드로 상태를 변경
- 클래스 내부의 메서드를 정의하여 버튼 클릭 시 호출
import React, { Component } from 'react';
class Counter extends Component {
// 클래스에서 상태 초기화
state = {
count: 0,
};
// 메서드를 사용해 상태 업데이트
increment = () => {
this.setState({ count: this.state.count + 1 });
};
decrement = () => {
this.setState({ count: this.state.count - 1 });
};
render() {
return (
<div>
<p>현재 카운트: {this.state.count}</p>
<button onClick={this.increment}>증가</button>
<button onClick={this.decrement}>감소</button>
</div>
);
}
}
export default Counter;
차이점
항목 | 함수형 컴포넌트 | 클래스형 컴포넌트 |
구조 | 함수형으로 작성 | 클래스형으로 작성 |
상태 관리 | useState Hook 사용 | this.state와 this.setState 사용 |
생명주기 관리 | useEffect Hook 사용 | 생명주기 메서드 사용 |
코드 간결성 | 간결하고 짧음 | 상대적으로 복잡하고 길어짐 |
React 버전 | React 16.8 이상에서 지원 | 모든 React 버전에서 지원 |
React Hook 규칙
최상위에서만 호출
- 반복문, 조건문, 중첩된 함수 내부에서 호출하면 안 됨
→ 실행 여부가 달라질 수 있어 React가 Hook 호출 순서를 잘못 추적 - React는 Hook이 호출된 순서를 기준으로 상태를 관리하므로, 순서가 바뀌면 버그가 발생
- 잘못된 사용
function MyComponent() {
if (true) { // 조건문 안에서 Hook 호출
const [state, setState] = useState(0); // 오류 발생 가능
}
return <div>안녕하세요!</div>;
}
- 올바른 사용
function MyComponent() {
// 최상위에서만 Hook 호출
const [state, setState] = useState(0);
return <div>안녕하세요!</div>;
}
React 함수에서만 호출
- 함수형 컴포넌트나 Custom Hook에서만 사용 가능
→ React의 상태 관리와 생명주기 로직은 함수형 컴포넌트와 Hook에 맞게 설계 - 일반 JavaScript 함수나 클래스형 컴포넌트에서는 호출하면 안 됨
→ 일반 함수에서 호출하면 React의 상태 관리 메커니즘이 동작하지 않음 - 잘못된 사용
function regularFunction() {
const [state, setState] = useState(0); // 오류 발생
}
- 올바른 사용 (함수형 컴포넌트)
function MyComponent() {
const [state, setState] = useState(0); // 정상 동작
return <div>{state}</div>;
}
- 올바른 사용 (Custom Hook)
function useCustomHook() {
const [value, setValue] = useState(0); // Hook 호출 가능
return [value, setValue];
}
function MyComponent() {
const [customState, setCustomState] = useCustomHook();
return <div>{customState}</div>;
}
React Hook의 장단점
- 장점
- 함수형 컴포넌트에서 상태와 생명주기 관리가 가능
- Custom Hook을 통해 로직을 재사용
- 클래스형 컴포넌트보다 코드가 간결하고 이해하기 쉬움
- Props Drilling 문제를 해결 (useContext 활용)
- 단점
- 복잡한 Hook 조합은 가독성을 떨어뜨릴 수 있음
- 초보자에게 규칙을 준수하며 사용하기가 다소 어렵게 느껴질 수 있음
- 상태가 많아질수록 코드가 비대해질 가능성 있음
React에서 제공하는 주요 Hook
useState : 상태 관리
- 함수형 컴포넌트에서 상태를 관리하는 데 사용
const [state, setState] = useState(initialValue);
- 특징
- state : 현재 상태 값을 나타냄
- setState : 상태를 업데이트하는 함수
- 상태 변경 시 컴포넌트가 다시 렌더링
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0); // 초기값: 0
return (
<div>
<p>현재 카운트: {count}</p>
<button onClick={() => setCount(count + 1)}>증가</button>
</div>
);
}
useEffect : 생명주기 관리
- 컴포넌트가 렌더링 or 업데이트 or 언마운트될 때 실행할 작업을 지정
useEffect(() => {
// 실행할 작업
return () => {
// 정리 작업 (옵션)
};
}, [dependencyArray]); // 의존성 배열
- 특징
- 의존성 배열([])이 없는 경우 : 매 렌더링마다 실행
- 빈 배열([])인 경우 : 처음 한 번만 실행 (componentDidMount와 비슷)
- 배열에 값이 있는 경우 : 값이 변경될 때만 실행
import React, { useState, useEffect } from 'react';
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
const interval = setInterval(() => setCount((prev) => prev + 1), 1000);
return () => clearInterval(interval); // 컴포넌트 언마운트 시 정리
}, []);
return <div>
<p>타이머: {Math.floor(count/60)}분 {count%60}초</p>
<p>타이머: {count}초</p>
</div>;
;
}
export default Timer;
useContext : 전역 상태 관리
- Context API를 활용해 컴포넌트 트리에서 전역 데이터를 쉽게 공유
const value = useContext(MyContext);
- 특징
- Props Drilling(여러 컴포넌트 계층을 통해 props 전달) 문제를 해결
- Context.Provider에서 제공하는 값을 구독
import React, { useContext, createContext } from 'react';
const ThemeContext = createContext('light');
function ThemedComponent() {
const theme = useContext(ThemeContext);
return <p>현재 테마: {theme}</p>;
}
function App() {
return (
<ThemeContext.Provider value="dark">
<ThemedComponent />
</ThemeContext.Provider>
);
}
export default App;
useReducer : 복잡한 상태 관리
- Redux와 유사한 방식으로 상태를 업데이트하는 Hook
- 복잡한 상태 로직이 필요한 경우 유용
const [state, dispatch] = useReducer(reducer, initialState);
- 특징
- reducer : 상태 업데이트 로직을 정의하는 함수
(state, action) => newState - dispatch : 액션을 보내는 함수
- state, dispatch, reducer는 예약어가 아님
→ reducer와 dispatch는 useReducer의 컨벤션으로 자주 사용 되는 이름
- reducer : 상태 업데이트 로직을 정의하는 함수
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:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>카운트: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>증가</button>
<button onClick={() => dispatch({ type: 'decrement' })}>감소</button>
</div>
);
}
export default Counter;
- state, reducer, dispatch 이름 변경
- state => currentState
- reducer 함수 => customReducer 함수
- dispatch => sendAction
import React, { useReducer } from 'react';
const initialState = { count: 0 };
// 리듀서 이름 변경 (자유롭게 설정 가능)
function customReducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error('Unknown action type');
}
}
function Counter() {
const [currentState, sendAction] = useReducer(customReducer, initialState);
return (
<div>
<p>Count: {currentState.count}</p>
<button onClick={() => sendAction({ type: 'increment' })}>증가</button>
<button onClick={() => sendAction({ type: 'decrement' })}>감소</button>
</div>
);
}
export default Counter;
요약
- React Hook은 상태 관리와 생명주기 관리를 함수형 컴포넌트에서 가능하게 해줌
- 주요 Hook
- useState : 상태 관리
- useEffect : 생명주기 관리
- useContext : 전역 데이터 공유
- useReducer : 복잡한 상태 로직 관리
- 규칙을 반드시 준수해야 React가 올바르게 동작
- 장점과 단점을 이해하고 상황에 맞게 사용하면 생산성 높은 개발이 가능
'Language > React' 카테고리의 다른 글
[개념] List와 Key (0) | 2025.01.01 |
---|---|
[개념] 렌더링과 조건부 렌더링 (0) | 2024.12.31 |
[개념] React의 개념, 컴포넌트, JSX, Props, State (1) | 2024.12.26 |
[Node.js] npm과 npx, 전역 설치와 로컬 설치 (0) | 2024.12.17 |
[React] 리액트 프로젝트 생성 시 에러 발생 (1) | 2024.12.17 |