리덕스(Redux)와 컨텍스트 API(Context API)는 리액트(React) 애플리케이션에서 상태 관리를 하기 위한 두 가지 대안적인 방법이다. 하지만 유사한 점들이 많다보니, 처음 봤을 때 어느 때에 더 적절한걸 써야 할지 몰라 Redux와 Context API를 비교하고, 각각의 사용 사례와 코드 예시를 기록하려 한다.
🤔 Redux vs. Context API
Redux는 Flux 아키텍처 패턴을 구현한 라이브러리로, 전역 상태 관리를 위해 사용된다. Redux는 애플리케이션의 상태를 중앙 집중적으로 관리하고, 상태 변경을 예측 가능하도록 만든다. 이를 위해 Redux는 불변성을 유지하고, 상태 변경을 위한 액션(Action)을 사용하며, 이 액션들을 통해 상태를 변경하는 리듀서(Reducer)를 작성한다.
반면, Context API는 React v16.3에서 소개된 기능으로, 컴포넌트 트리에서 전역 데이터를 공유하기 위한 방법이다. Context API는 Provider와 Consumer를 사용하여 상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달한다. 이를 통해 Redux와 같은 전역 상태 관리가 가능하지만, Redux보다는 간단하고 쉽게 구현할 수 있다.
📝 Redux 사용 사례 및 코드 예시
Redux는 대규모 애플리케이션에서 사용되며, 상태를 중앙 집중적으로 관리하기 때문에 상태 변경이 예측 가능하다. Redux는 다음과 같은 상황에서 사용된다.
- 애플리케이션의 상태가 복잡하고, 여러 컴포넌트에서 공유되는 경우
- 애플리케이션의 상태 변경을 예측 가능하게 하고, 디버깅을 용이하게 하는 경우
- 비동기 작업을 처리하고, 상태 변경에 따른 사이드 이펙트를 처리해야 하는 경우
Redux를 사용하기 위해서는 먼저 Redux와 React-Redux를 설치해야 한다.
// store.js
import { createStore } from "redux"; // redux 모듈에서 createStore 함수를 불러옴
import rootReducer from "./reducers"; // reducers 폴더에서 rootReducer를 불러옴
const store = createStore(rootReducer); // rootReducer를 이용하여 store를 생성
export default store; // 생성한 store를 외부에서 사용할 수 있도록 내보냄
// reducers.js
import { combineReducers } from "redux";
import userReducer from "./userReducer"; // userReducer 모듈 불러오기
import postReducer from "./postReducer"; // postReducer 모듈 불러오기
const rootReducer = combineReducers({
user: userReducer, // userReducer를 user 속성에 할당
post: postReducer, // postReducer를 post 속성에 할당
});
export default rootReducer; // rootReducer 내보내기
// userReducer.js
// 초기 상태 정의
const initialState = {
user: {}, // 유저 정보
loading: false, // 로딩 상태
error: null, // 에러 상태
};
// 리듀서 함수 정의
const userReducer = (state = initialState, action) => {
switch (action.type) {
case "FETCH_USER_REQUEST": // 유저 정보 요청 액션
return {
...state,
loading: true, // 로딩 상태 true로 변경
};
case "FETCH_USER_SUCCESS": // 유저 정보 요청 성공 액션
return {
...state,
loading: false, // 로딩 상태 false로 변경
user: action.payload, // 유저 정보 업데이트
};
case "FETCH_USER_FAILURE": // 유저 정보 요청 실패 액션
return {
...state,
loading: false, // 로딩 상태 false로 변경
error: action.payload, // 에러 정보 업데이트
};
default:
return state; // 기본 상태 반환
}
};
export default userReducer; // 리듀서 함수 내보내기
// postReducer.js
// 초기 상태 정의
const initialState = {
posts: [], // 포스트 목록
loading: false, // 로딩 상태
error: null, // 에러 상태
};
// 리듀서 함수 정의
const postReducer = (state = initialState, action) => {
switch (action.type) {
case "FETCH_POSTS_REQUEST": // 포스트 목록 요청 액션
return {
...state,
loading: true, // 로딩 상태 true로 변경
};
case "FETCH_POSTS_SUCCESS": // 포스트 목록 요청 성공 액션
return {
...state,
loading: false, // 로딩 상태 false로 변경
posts: action.payload, // 포스트 목록 업데이트
};
case "FETCH_POSTS_FAILURE": // 포스트 목록 요청 실패 액션
return {
...state,
loading: false, // 로딩 상태 false로 변경
error: action.payload, // 에러 상태 업데이트
};
default:
return state; // 기본 상태 반환
}
};
export default postReducer; // 리듀서 함수 내보내기
// actions.js
// 사용자 정보 요청 액션 생성 함수
export const fetchUserRequest = () => ({
type: "FETCH_USER_REQUEST", // 액션 타입
});
// 사용자 정보 요청 성공 액션 생성 함수
export const fetchUserSuccess = (user) => ({
type: "FETCH_USER_SUCCESS", // 액션 타입
payload: user, // 액션 페이로드
});
// 사용자 정보 요청 실패 액션 생성 함수
export const fetchUserFailure = (error) => ({
type: "FETCH_USER_FAILURE", // 액션 타입
payload: error, // 액션 페이로드
});
// 게시글 정보 요청 액션 생성 함수
export const fetchPostsRequest = () => ({
type: "FETCH_POSTS_REQUEST", // 액션 타입
});
// 게시글 정보 요청 성공 액션 생성 함수
export const fetchPostsSuccess = (posts) => ({
type: "FETCH_POSTS_SUCCESS", // 액션 타입
payload: posts, // 액션 페이로드
});
// 게시글 정보 요청 실패 액션 생성 함수
export const fetchPostsFailure = (error) => ({
type: "FETCH_POSTS_FAILURE", // 액션 타입
payload: error, // 액션 페이로드
});
// User.js
import React, { useEffect } from "react";
import { connect } from "react-redux";
import { fetchUserRequest, fetchUserSuccess, fetchUserFailure } from "../actions";
const User = ({ user, loading, error, fetchUser }) => {
// useEffect hook을 사용하여 컴포넌트가 마운트될 때 fetchUser 함수를 호출한다.
useEffect(() => {
fetchUser();
}, []);
// 로딩 중일 때는 "Loading..."을 화면에 표시한다.
if (loading) {
return <div>Loading...</div>;
}
// 에러가 발생했을 때는 에러 메시지를 화면에 표시한다.
if (error) {
return <div>{error.message}</div>;
}
// 유저 정보를 화면에 표시한다.
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
};
// Redux store의 state를 props로 매핑한다.
const mapStateToProps = (state) => ({
user: state.user.user,
loading: state.user.loading,
error: state.user.error,
});
// Redux store의 dispatch를 props로 매핑한다.
const mapDispatchToProps = (dispatch) => ({
// fetchUserRequest action을 dispatch하고, API를 호출하여 데이터를 가져온 후 fetchUserSuccess 또는 fetchUserFailure action을 dispatch한다.
fetchUser: () => {
dispatch(fetchUserRequest());
fetch("https://jsonplaceholder.typicode.com/users/1")
.then((response) => response.json())
.then((user) => dispatch(fetchUserSuccess(user)))
.catch((error) => dispatch(fetchUserFailure(error)));
},
});
// Redux store와 연결된 User 컴포넌트를 export한다.
export default connect(mapStateToProps, mapDispatchToProps)(User);
Redux에서는 store, reducer, action, dispatch와 같은 개념을 사용하고, 이를 위한 별도의 라이브러리를 사용해야 한다. 또한, Redux는 개발자가 상태 변화를 예측하고, 이에 대한 액션 및 리듀서를 작성해야 한다.
📝 Context API 사용 사례 및 코드 예시
Context API는 전역 데이터 공유를 위해 사용되며, Redux보다 간단하고 쉽게 구현할 수 있다. Context API는 React에서 createContext 함수를 사용하여 Context를 생성하고, Provider 컴포넌트로 해당 Context의 값을 설정할 수 있다. 이후, 해당 Context 값을 사용하는 컴포넌트에서 useContext 훅을 사용하여 값을 가져와 사용할 수 있다.
예시 코드를 통해 Context API의 사용 방법은 대략 이렇다.
// UserContext.js
// React 라이브러리를 불러온다.
import React from "react";
// UserContext 객체를 생성하고, 초기값을 빈 객체로 설정한다.
const UserContext = React.createContext({});
// UserContext 객체를 내보낸다.
export default UserContext;
// App.js
import React from "react";
import UserContext from "./UserContext";
import User from "./User";
const App = () => {
const user = {
name: "John Doe",
email: "johndoe@example.com",
};
return (
// UserContext.Provider를 사용하여 UserContext를 제공합니다.
<UserContext.Provider value={user}>
<User />
</UserContext.Provider>
);
};
export default App;
// User.js
import React, { useContext } from "react";
import UserContext from "./UserContext";
const User = () => {
// UserContext를 가져와서 user 변수에 할당
const user = useContext(UserContext);
return (
<div>
<h1>{user.name}</h1> {/* user의 이름을 출력 */}
<p>{user.email}</p> {/* user의 이메일을 출력 */}
</div>
);
};
export default User;
Context API를 사용하면 Redux와 달리 별도의 라이브러리를 사용하지 않아도 된다. 또한, 상태 변화를 예측하거나 액션 및 리듀서를 작성할 필요가 없기 때문에, 간단한 전역 데이터 공유에 적합하다.
하지만, Context API는 Redux보다 많은 기능을 제공하지 않기 때문에, 상태 관리가 복잡해지면 Redux를 사용하는 것이 더 나은 선택일 수 있다.
✨ 결론
Redux와 Context API는 모두 React에서 전역 상태 관리를 위해 사용되는 방법이다. Redux는 상태 변화를 예측하고, 액션 및 리듀서를 작성해야 하지만, Context API는 간단하게 전역 데이터를 공유할 수 있다. 따라서, 프로젝트의 규모와 복잡도에 따라 적절한 상태 관리 방법을 선택하는 것이 중요해 보인다.
'React' 카테고리의 다른 글
CSR(Client-side Rendering)이란? (+SSR 비교) (0) | 2023.06.28 |
---|---|
[React] useCallback 사용법 정리 (0) | 2023.04.28 |
댓글