Front-End
리덕스 / Context API 본문
이 부분이 제일 어렵고
저는 이해가 안되던 부분입니다.
제 공부를 위해 정리하는 것이니 참고용으로만 봐주시유!
1. Context API
리액트에서는 Props와 State는 부모 컴포넌트와 자식 컴포넌트 또는 한 컴포넌트 안에서 데이터를 다루기위해 사용된다.
이 Props와 State를 사용하게 되면
부모 컴포넌트에서 자식 컴포넌트로
즉,
위에서 아래로 한쪽으로 데이터가 흐르게된다.
만약 다른 컴포넌트 쪽에서 한쪽으로 흐르고 있는 데이터를 사용하고 싶을 경우에,
아니면 다른 컴포넌트에서 사용하고 있는 데이터를 현재의 데이터 흐름에 넣고 싶을때는
공통 부모 컴포넌트에 State를 만들고 사용하고자 하는 데이터를 Props를 전달해서 해야한다.
하지만 매번 공통 부모 컴포넌트를 수정하고 Props로 전달하는 건 사실 복잡하다.
그래서 나온 것이 Context API
Context는 부모 컴포넌트로부터 자식 컴포넌트로 전달되는 데이터 흐름과 관계없이
전역적인 데이터를 다룰 때 사용한다.
전역 데이터를 Context에 저장하고, 데이터가 필요한 컴포넌트에서 해당 데이터를 불러와 사용할 수 있다.
Context API 사용을 위해서는 Provider와 Consumer를 사용해야한다.
Context에 저장된 데이터를 사용하기 위해서는
공통 부모 컴포넌트에 Context의 Provider를 사용하여 데이터를 제공해야 하며,
데이터를 사용하려는 컴포넌트에서 Context의 Consumer를 사용하여 실제로 데이터를 사용합니다.
근데 사실 Context 보다는 리덕스를 더 많이 사용해서
리덕스를 자세히 정리해보겠습니다.
2. 리덕스 ( Redux )
사실 리덕스와 Context API는 비슷합니다.
똑같이 상태관리를 위해 사용합니다.
일단
둘의 차이를 알아봅시다.
1. 미들웨어
리덕스에는 미들웨어(Middleware)라는 개념이 존재합니다.
리덕스의 미들웨어를 사용하면 액션 객체가 리듀서에서 처리되기 전에
우리가 원하는 작업들을 수행 할 수 있습니다.
1. 특정 조건에 따라 액션이 무시되게 만들 수 있습니다.
2. 액션을 콘솔에 출력하거나, 서버쪽에 로깅을 할 수 있습니다.
3. 액션이 디스패치 됐을 때 이를 수정해서 리듀서에게 전달되도록 할 수 있습니다.
4. 특정 액션이 발생했을 때 이에 기반하여 다른 액션이 발생되도록 할 수 있습니다.
5. 특정 액션이 발생했을 때 특정 자바스크립트 함수를 실행시킬 수 있습니다.
미들웨어는 주로 비동기 작업을 처리 할 때 많이 사용됩니다.
2. 유용한 함수와, Hooks
우리가 이전에 Context API 와 useReducer 를 사용 할 때에는
Context 도 새로 만들고,
Context 의 Provider 설정도 하고
각 Context 를 편하게 사용하기 위해 전용 커스텀 Hook 을 따로 만들어서 사용하기도 했지만,
리덕스에서는 이와 비슷한 작업을 편리하게 해줄 수 있는 여러 기능들이 존재합니다.
connect 함수를 사용하면 리덕스의 상태 또는 액션 생성 함수를 컴포넌트의 props 로 받아올 수 있고,
useSelector, useDispatch, useStore 과 같은
Hooks를 사용하면 손쉽게 상태를 조회하거나 액션을 디스패치 할 수도 있습니다.
connect 함수와 useSelector 함수에는 내부적으로 최적화가 잘 이루어져있어서
실제 상태가 바뀔때만 컴포넌트가 리렌더링됩니다.
반면에 Context API를 사용할 때에는 그러한 최적화가 자동으로 이루어져있지 않기 때문에
Context 가 지니고 있는 상태가 바뀌면 해당 Context 의 Provider 내부 컴포넌트들이 모두 리렌더링 됩니다.
3. 하나의 커다란 상태
Context API 를 사용해서 글로벌 상태를 관리 할 때에는
일반적으로 기능별로 Context를 만들어서 사용하는 것이 일반적이라고 합니다.
반면 리덕스에서는 모든 글로벌 상태를 하나의 커다란 상태 객체에 넣어서 사용하는 것이 필수입니다.
때문에 매번 Context를 새로 만드는 수고로움을 덜 수 있습니다.
보통 회사에서는 저희가 만드는 프로젝트와 달리
규모가 크기 때문에 리덕스를 많이 사용하게 됩니다.
리덕스를 사용하면 컴포넌트의 상태 업데이트 관련 로직을 다른 파일로 분리시켜서
더욱 효과적으로 관리할 수 있습니다.
컴포넌트 끼리 똑같은 상태를 공유해야 할 때도 여러 컴포넌트를 거치지 않고
손쉽게 상태 값을 전달하거나 업데이트 할 수 있습니다.
개념을 미리 익혀야합니다.
액션 ( Action )
상태에 어떠한 변화가 필요할 때, 액션이라는 것이 발생합니다.
이는 하나의 객체로 표현된다.
{
type : 'JEONG_SEO'
}
액션 객체는 type 필드를 반드시 가지고 있어야한다.
이 값을 액션의 이름이라고 생각하면 된다.
type = 액션의 이름
그 외의 값들은 나중에 상태 업데이트 때 참고해야할 값이고, 작성자 마음대로 넣을 수 있다.
액션 생성 함수 ( Action Creator )
액션 객체를 만들어주는 함수다.
단순하게 파라미터를 받아와서 액션 객체 형태로 만들어준다.
function addTodo(data) {
return {
type: "ADD_TODO",
data
};
}
// 화살표 함수로도 만들 수 있습니다.
export const changeInput = text => ({
type: "CHANGE_INPUT",
text
});
어떤 변화를 일으켜야 할 때마다 액션 객체를 만들어야 하는데
매번 만들기 번거롭고, 실수로 정보를 놓칠 수도 있으니,
이런 일을 방지하기 위해 이를 함수로 만들어서 관리합니다.
보통 함수 앞에 export 키워드를 붙여서 다른 파일에서 불러와서 사용합니다.
리덕스를 사용할 때 액션 생성함수를 사용하는 것이 필수적이지는 않아요~
추가적으로
액션 이름은 문자열 형태로, 주로 대문자로 작성하며 액션 이름은 교유해야 합니다.
리듀서 ( Reducer )
변화를 일으키는 함수다.
액션을 만들어서 발생시키면 리듀서가 현재 상태와 전달 받은 액션 객체를 파라미터로 받아 와서,
그 두 값을 참고로 하여 새로운 상태를 만들어 반환해 줍니다.
간단히 설명하면,
현재의 상태, 전달 받은 액션을 참고하여 새로운 상태를 만들어서 반환합니다.
function counter(state, action) {
switch (action.type) {
case 'INCREASE':
return state + 1;
case 'DECREASE':
return state - 1;
default:
return state;
}
}
default 부분에서
리덕스의 리듀서는 기존 state를 반환하도록 작성합니다.
추가적으로
리듀서 함수가 맨 처음 호출될 때는 state 값이 undefined입니다.
해당 값이 undefined로 주어졌을 때는 initialState를 기본 값으로 설정하기 위해
함수의 파라미터 쪽에 기본값을 설정해야 합니다.
// state가 undefined일 때 initialState를 기본값으로 사용
function reducer(state = initialState, action) {
...
}
리듀서에서는 상태의 불변성을 유지하면서 데이터에 변화를 일으켜 주어야 합니다.
이 작업을 위해서는 spread 연산자( ... )를 사용하면 편합니다.
스토어 ( Store )
프로젝트에 리덕스를 적용하기 위해 만듭니다.
한 개의 프로젝트는 단 하나의 스토어만 가질 수 있습니다.
스토어 안에는, 현재의 앱 상태와, 리듀서가 들어가있고, 추가적으로 몇가지 내장 함수들이 있습니다.
디스패치 (dispatch)
디스패치는 스토어의 내장함수 중 하나입니다.
디스패치는 액션을 발생 시키는 것 이라고 이해하시면 됩니다.
dispatch 라는 함수에는 액션을 파라미터로 전달합니다.
dispatch(action) 이런식으로 말이죠.
이 함수가 호출되면
스토어는 리듀서 함수를 실행시켜서 해당 액션을 처리하는 로직이 있다면 액션을 참고하여 새로운 상태를 만들어줍니다.
구독 (subscribe)
구독 또한 스토어의 내장함수 중 하나입니다.
subscribe 함수는, 함수 형태의 값을 파라미터로 받아옵니다.
subscribe 함수에 특정 함수를 전달해주면, 액션이 디스패치 되었을 때 마다 전달해준 함수가 호출됩니다.
const listener = () => {
console.log('상태가 업데이트 됨');
}
const unsubscirbe = store.subscribe(listener);
unsubscirbe(); // 추후 구독을 비활성화할 때 함수를 호출
리액트에서 리덕스를 사용하게 될 때 보통 이 함수를 직접 사용하는 일은 별로 없습니다.
그 대신에 react-redux 라는 라이브러리에서 제공하는 connect 함수 또는 useSelector Hook 을 사용하여
리덕스 스토어의 상태에 구독합니다.
아 그리고 헷갈릴만한게 있는데
리덕스는 리액트에 종속되어 있는 라이브러리가 아닙니다.
마지막으로 리덕스의 세 가지 규칙을 알아야합니다.
반드시 지켜야하니 꼭 읽어봐주세요.
1. 단일 스토어
즉, 하나의 애플리케이션 안에는 하나의 스토어
하나의 애플리케이션에선 단 한개의 스토어를 만들어서 사용합니다.
여러개의 스토어를 사용하는것은 사실 가능하기는 하나, 권장되지는 않습니다.
특정 업데이트가 너무 빈번하게 일어나거나,
애플리케이션의 특정 부분을 완전히 분리시키게 될 때 여러개의 스토어를 만들 수도 있습니다.
하지만 그렇게 하면, 개발 도구를 활용하지 못하게 되고, 상태관리가 복잡해질 수 있습니다.
2. 읽기 전용 상태
리덕스 상태는 읽기 전용입니다.
기존에 리액트에서 setState를 사용하여 state를 업데이트 할 때도 객체나 배열을 업데이트하는 과정에서
불변성을 지켜 주기 위해 spread 연산자를 사용하거나 immer와 같은 불변성 관리 라이브러리를 사용했는데,
리덕스도 마찬가지 입니다.
상태를 업데이트 할때 기존의 객체는 건드리지 않고 새로운 객체를 생성해야 합니다.
리덕스에서 불변성을 유지하는 이유는 내부적으로 데이터가 변경되는 것을 감지하기 위해
얕은 비교 검사를 하기 때문입니다.
객체의 변화를 감지할 때 객체의 깊숙한 안쪽까지 비교하는 것이 아니라 겉핥기 식으로 비교하여
좋은 성능을 유지할 수있다.
3. 리듀서는 순수한 함수
변화를 일으키는 함수 리듀서
순수한 함수는 다음 조건을 만족해야 합니다.
- 리듀서 함수는 이전 상태와 액션 객체를 파라미터로 받습니다.
- 파라미터 외의 값에는 의존하면 안 됩니다.
- 이전 상태는 절대로 건드리지 않고, 변화를 준 새로운 상태 객체를 만들어서 반환합니다.
- 똑같은 파라미터로 호출된 리듀서 함수는 언제나 똑같은 결과 값을 반환해야 합니다.
'리액트' 카테고리의 다른 글
리덕스 미들웨어 (1) | 2023.06.06 |
---|---|
되짚어 가기 (0) | 2023.06.01 |
리액트 라우터 (0) | 2023.03.18 |
immer 불변성 유지하기 (1) | 2023.03.14 |
컴포넌트 성승 최적화 (1) | 2023.03.10 |