상태관리 라이브러리란?
상태 관리 라이브러리는 웹 애플리케이션에서 데이터의 상태를 효과적으로 관리하고 상태를 업데이트하는데 도움을 주는 도구나 라이브러리이다.
웹 애플리케이션은 사용자 상호 작용, 서버에서 받은 데이터, 라우팅 정보 등 다양한 상태와 데이터를 관리해야하는데, 이를 체계적으로 구조화하고 업데이트하기 위해 상태관리 라이브러리가 사용된다.
상태관리 라이브러리의 주요 특징과 목적
- 중앙화된 상태 저장소 : 상태 관리 라이브러리는 애플리케이션에서 사용되는 데이터와 상태를 중앙화된 저장소에 저장한다. 이 저장소는 애플리케이션의 모든 컴포넌트에서 접근 가능하며, 컴포넌트 간 데이터 공유와 상태 업데이트를 단순화한다.
- 예측 가능한 상태 변경 : 라이브러리는 상태 변경을 예측 가능하고 일관된 방식으로 다룰 수 있는 매커니즘을 제공한다. 이를 통해 상태 업데이트 및 관리가 간단하고 오류를 줄일 수 있다.
- 컴포넌트 간 통신 : 대규모 애플리케이션에서는 다양한 컴포넌트 간의 통신이 필요하다. 상태 관리 라이브러리는 컴포넌트간 데이터 전송을 단순화하고 중앙화된 저장소를 통해 데이터를 교환한다.
- 데이터 흐름 관리 : 상태 관리 라이브러리는 데이터 흐름을 체계적으로 관리한다. 상태 변경과 데이터 흐름을 추적하고 데이터의 일관성을 유지한다.
- 디버깅 및 툴 지원 : 많은 상태 관리 라이브러리는 디버깅 도구 및 브라우저 확장 프로그램을 제공하여 개발자가 애플리케이션의 상태를 모니터링하고 디버깅할 수 있도록 돕는다.
- 성능 최적화 : 일부 라이브러리는 데이터 캐싱 및 최적화 기능을 제공하여 애플리케이션 성능을 향상시킬 수 있다.
인기 있는 상태관리 라이브러리로는 Redux, MobX, VueX(Vue.js용), Recoil(React용), Zustand등이 있다.
이러한 라이브러리는 웹 애플리케이션 개발을 보다 효율적이고 유지 보수 가능하게 만들어주며, 코드의 일관성과 확장성을 향상시키는데 도움을 준다.
상태관리 라이브러리가 필요한 이유
- 복잡한 상태 관리 : 웹 애플리케이션은 다양한 데이터와 상태를 관리해야한다. 이러한 상태는 사용자 입력, 서버 데이터, 라우팅 정보, 컴포넌트 간의 상호 작용 등 다양한 요소에서 발생한다. 이런 복잡한 상태를 효율적으로 관리하려면 전문적인 도구가 필요하다.
- 컴포넌트 간 데이터 공유 : 대규모 애플리케이션에서는 여러 컴포넌트 간에 데이터를 공유하고 상호 작용해야 한다. 상태 관리 라이브러리는 이러한 컴포넌트 간 통신을 단순화하고 중앙화된 저장소에서 데이터를 관리할 수 있도록 도와준다.
- 상태 변화 추적 : 사용자의 상호 작용, 네트워크 요청, 백그라운드 작업 등으로 인해 상태가 변경될 수 있다. 상태 관리 라이브러리는 이러한 상태 변경을 추적하고 쉽게 업데이트할 수 있는 매커니즘을 제공한다.
- 데이터 흐름 관리 : 복잡한 애플리케이션에서 데이터 흐름을 관리하는 것은 어려운 과제이다. 상태관리 라이브러리는 데이터의 흐름을 효과적으로 제어하고 예측 가능한 방식으로 데이터를 변경할 수 있도록 돕는다.
- 캐시 및 최적화 : 일부 상태 관리 라이브러리는 데이터 캐싱 및 최적화 기능을 제공하여 성능을 향상시키고 네트워크 요청을 최소화할 수 있다.
- 디버깅 및 유지보수 : 상태 관리 라이브러리를 사용하면 애플리케이션의 상태를 추적하고 디버깅하기 쉽다. 또한 유지보수와 코드 베이스 관리를 단순화 한다.
모든 상태관리 라이브러리는 Props drilling을 방지한다?
우선 모든 상태관리 라이브러리가 props drilling을 방지하냐는 질문에는 No라는 대답을 할 수 있다.
Props drilling은 리액트 애플리케이션에서 컴포넌트 간에 데이터를 전달하기 위해 중간 컴포넌트를 통과하는 과정을 말한다.
이로 인해 컴포넌트 계층 구조가 깊어지고, 데이터를 필요로 하는 하위 컴포넌트에게 필요한 데이터를 전달하기 위해 props가 계속해서 전달된다.
상태 관리 라이브러리는 props drilling문제를 해결하기 위한 하나의 해결책이며, 이런 라이브러리를 사용하면 중앙화된 상태 저장소를 통해 데이터를 공유하고 컴포넌트간에 데이터를 전달하기 위해 props를 수동으로 전달할 필요가 없어진다. 따라서 상태관리 라이브러리를 사용하면 props drilling을 줄일 수 있다.
그러나 상태관리 라이브러리를 사용하지 않더라도 리액트의 Context API를 활용하면 props drilling을 완화할 수 있다.
Context API는 컴포넌트 트리를 횡단하여 데이터를 전달하는 데 사용될 수 있으며, 중앙화된 상태 저장소를 만드는데 사용될 수 있다.
props drilling 이란?
Props drilling이란 리액트 애플리케이션에서 컴포넌트 간에 데이터를 전달하기 위해 중간 컴포넌트를 통과하는 과정을 가리키는 용어를 말한다.
이러한 상황은 컴포넌트 계층 구조가 깊어지면서 발생할 수 있으며, 데이터를 필요로 하는 하위 컴포넌트에게 데이터를 전달하기 위해 상위 컴포넌트를 통과하는 props의 체인을 의미한다.
props drilling이 발생할수 있는 예제코드를 살펴보자
// 최상위 컴포넌트
function App() {
const data = "Hello, props drilling!";
return (
<div>
<Parent data={data} />
</div>
);
}
// 중간 컴포넌트
function Parent({ data }) {
return (
<div>
<Child data={data} />
</div>
);
}
// 최하위 컴포넌트
function Child({ data }) {
return <div>{data}</div>;
}
위 예시에서 App컴포넌트가 data를 가지고 있으며 이 데이터가 parent컴포넌트를 통과하여 child컴포넌트로 전달된다.
이것을 props drilling의 예시이다.
props drilling은 몇가지 문제를 유발할 수 있다.
- 컴포넌트 구조의 복잡성 : 컴포넌트 계층 구조가 깊어지면 코드가 어려워질 수 있으며, 데이터가 필요한 하위 컴포넌트로 전달하기 위해 많은 중간 컴포넌트를 거쳐야 할 수 있다.
- 성능 문제 : 데이터가 상위 컴포넌트를 통과할때마다 불필요한 렌더링이 발생할 수 있으며, 컴포넌트가 업데이트 될때 중간 컴포넌트도 업데이트될 수 있다.
- 유지보수의 어려움 : 코드가 복잡해지고 컴포넌트간의 의존성이 높아질수록 유지 보수가 어려워질 수 있다.
이러한 이유로 상태관리 라이브러리나 React의 Context API와 같은 도구를 사용하여 props drilling문제를 해결하는 것이 좋을 수 있다.
그럼 어떤 상태관리 라이브러리가 props drilling을 방지할까?
- Redux : Redux는 중앙화된 상태 저장소를 사용하므로 상태를 컴포넌트 간에 직접 전달할 필요가 없다. 상위 컴포넌트에서 상태를 가져와 하위 컴포넌트에게 전달하는 것이 필요하지 않으며, 필요한 데이터를 Redux 스토어에서 가져와 사용할 수 있다.
- MobX : MobX는 관찰 가능한 상태와 컴포넌트 간 자동으로 관찰 및 전달되는 데이터를 지원한다. 이를 통해 데이터가 필요한 컴포넌트로 자동으로 전달되므로 props drilling을 방지할 수 있다.
- Recoil : Recoil은 리액트 상태 관리 라이브러리로 컴포넌트 간의 종속성을 자동으로 추적하고 필요한 데이터를 선택기를 통해 사용할 수 있게 해준다.
- Zustand : Zustand는 컴포넌트 내부에서 상태를 관리하고 컴포넌트간에 상태를 공유하기 쉽게 만들어준다. 이를 통해 중간 컴포넌트를 거치지 않고도 데이터를 전달할 수 있다.
Context API와 Redux를 이용한 props drilling을 해결하는 예제 코드
- Context API를 사용한 예제코드
먼저 Context를 생성해야 한다.
import React, { createContext, useContext, useState } from 'react';
// Context 생성
const DataContext = createContext();
// Context Provider 컴포넌트
export function DataProvider({ children }) {
const [data, setData] = useState("Hello, Context API!");
return (
<DataContext.Provider value={{ data, setData }}>
{children}
</DataContext.Provider>
);
}
// 커스텀 훅 생성 (선택사항)
export function useData() {
return useContext(DataContext);
}
그 다음 Context를 사용한다.
데이터를 필요로 하는 컴포넌트에서 Context를 사용한다.
import React from 'react';
import { DataProvider, useData } from './DataContext';
function ChildComponent() {
const { data } = useData();
return <div>{data}</div>;
}
function ParentComponent() {
return <ChildComponent />;
}
function App() {
return (
<DataProvider>
<ParentComponent />
</DataProvider>
);
}
export default App;
위 예제코드는 Context API를 이용하여 props drilling을 해결하는 예제코드이다.
Context API를 사용하면 데이터를 컴포넌트 간에 전달할 수 있다.
- Redux를 사용한 예제코드
먼저 Redux스토어를 생성하고 상태를 관리한다.
import { createStore } from 'redux';
// 초기 상태 정의
const initialState = {
data: "Hello, Redux!",
};
// 리듀서 함수 정의
function reducer(state = initialState, action) {
switch (action.type) {
case 'UPDATE_DATA':
return { ...state, data: action.payload };
default:
return state;
}
}
// 스토어 생성
const store = createStore(reducer);
export default store;
그 다음 Redux를 사용하여 데이터를 업데이트 해준다.
데이터를 업데이트 하려면 Redux 액션을 디스패치한다.
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
function ChildComponent() {
const data = useSelector((state) => state.data);
const dispatch = useDispatch();
const updateData = () => {
dispatch({ type: 'UPDATE_DATA', payload: 'New data from Redux!' });
};
return (
<div>
<div>{data}</div>
<button onClick={updateData}>Update Data</button>
</div>
);
}
function App() {
return (
<div>
<ChildComponent />
</div>
);
}
export default App;
이렇게 Redux를 사용하면 중앙화된 스토어를 통해 상태를 관리하고 업데이트 할 수 있다.
모든 상태관리 라이브러리는 컴포넌트를 순수함수로 유지 시킨다?
모든 상태관리 라이브러리가 컴포넌트를 순수함수로 유지시키지는 않는다.
상태 관리 라이브러리는 주로 컴포넌트 간의 데이터 공유와 상태 관리에 중점을 두며, 컴포넌트의 순수성과는 직접적으로 관련이 없다.
순수함수에 대해서 간단하게 말하자면,
동일한 입력에 대해서는 항상 동일한 출력을 반환하고, 외부 상태를 변경하지 않고, 외부 자원에 의존하지 않는 특성을 가지며 Side Effect가 없는 함수를 의미한다.
반면에 상태관리 라이브러리는 컴포넌트간에 상태를 공유하고 상태를 변경하는데 사용된다.
컴포넌트의 상태 변화에 따른 리렌더링과 같은 Side Effect를 포함할수있다. 따라서 상태 관리 라이브러리 자체가 컴포넌트를 순수함수로 강제하지는 않는다.
그러나 React와 함께 사용되는 상태관리 라이브러리는 주로 React 컴포넌트와 통합하기 위해 설계되었으며, 리액트의 생명주기 메서드 또는 훅을 사용하여 컴포넌트 부작용을 관리하고 순수함수로 유지하려는 노력을 기울인다.
Redux와 MobX와 같은 라이브러리는 Redux Thunk또는 Redux Saga와 같은 미들웨어를 사용하여 비동기 작업 및 Side Effect를 처리하고 컴포넌트를 순수함수로 유지하려는 시도를 할 수 있다.
미들웨어란?
소프트웨어에서 컴포넌트나 시스템 간에 연결되는 소프트웨어 구성 요소이다.
미들웨어는 한 소프트웨어 시스템의 요청과 응답 사이에 위치하여 특정 작업을 수행하거나 중개한다.
주로 웹 애플리케이션 개발에서 많이 사용되지만, 다른 종류의 소프트웨어에서도 사용될 수 있다.
프레임워크와 라이브러리에서 미들웨어 패턴을 지원하면 개발자가 요청과 응답 처리를 더욱 유연하게 제어할 수 있다.
Node.js의 Express 프레임워크, Redux와 같은 상태관리 라이브러리, 웹 서버, API게이트웨이, 로깅도구 등 다양한 소프트웨어에서 미들웨어 개념이 적용된다.
Redux, Recoli, Zustand의 차이점
- Redux
- Redux는 가장 널리 사용되는 상태 관리 라이브러리 중 하나이며, 리액트 애플리케이션과의 통합이 간편하고 생태계가 크고 강력하다.
- 중앙화된 상태 저장소를 사용하여 애플리케이션 상태를 관리하며, 상태 변경은 Action을 통해 이루어진다.
- 상태의 불변성을 강조하며, 액션을 통해 상태를 변경하고 이력을 추적하는데 용이하다.
- Redux는 디버깅 도구와 미들에어 지원을 통해 개발자 경험을 향상시킨다.
- Redux는 큰 규모의 애플리케이션 또는 다수의 컴포넌트 간 상태 공유가 필요한 경우에 적합하다.
- Recoil
- Recoil은 페이스북에서 개발된 상태 관리 라이브러리로, 리액트 애플리케이션과 통합이 원활하다.
- Recoil은 상태를 원자적상태로 관리하며 컴포넌트 간의 종속성을 자동으로 추적한다.
- 상태의 공유 및 업데이트는 리액트 훅과 선택기 함수를 통해 간단하게 처리할 수 있다.
- Recoil은 상태의 비동기적 로딩 및 서버 사이드 렌더링과 함께 사용하기 쉽다.
- Recoil은 React Concurrent Mode 와 호환디도록 설계되었다.
- Zustand
- Zustand은 상태 관리를 위한 경량 라이브러리로, 리액트 애플리케이션과 통합이 쉽고 간단하다.
- Zustand는 컴포넌트 내부에 상태를 관리하며, useState와 유사한 구문을 사용하여 상태를 업데이트 한다.
- 상태 업데이트가 비동기적으로 수행될 수 있으며, immer.js를 사용하여 불변성을 자동으로 관리한다.
- Zustand는 매우 작은 번들 크기를 가지며, 불필요한 복잡성을 피하고 간단한 컴포넌트에서 사용하기에 적합하다.
- Zustand는 단순한 애플리케이션에서 사용하거나 복잡한 상태 관리 라이브러리의 대안으로 고려될 수 있다.
어떤것을 선택할지는 프로젝트의 규모, 복잡성 및 요구사항에 따라 달라진다.
Redux는 대규모 애플리케이션과 복잡한 데이터 흐름을 다룰 때 좋은 선택일 수 있으며, Recoil과 Zustand는 리액트와의 통합이 간단하고 더 경량한 상태 관리를 원할때 유용하다.
'개발공부 > 기술면접준비' 카테고리의 다른 글
[알고리즘] 재귀를 활용하기 좋은 상황은 언제인지에 대하여 (0) | 2023.09.20 |
---|---|
[네트워크] IP 프로토콜의 한계에 대해서 (1) | 2023.09.20 |
[UI/UX]UI, UX의 개념과 두 개념의 관계에 대해서 (0) | 2023.09.19 |
[네트워크/HTTP] HTTP 프로토콜에 대해서 (0) | 2023.09.19 |
[React]Styled Components의 장점 (0) | 2023.09.17 |