[React] Front-End

[FE] Redux (1) 개념 / 사용법 (제로초 Redux 강의 복습)

ddgoori 2021. 7. 14. 17:58

Reducer

- 변수의 개념, INIT_STATE

- ex. JoinAgreeCustomer / RegisterCustomerInfo

- reducer 정의 시 action에 정의된 type 필요

 

Actions

- Reducer에 있는 init_state 변경

- ex. SaveJoinAgreeCustomer / UpdateRegisterCustomerInfo

 

  Dispatch

   - Container에서  Action 호출하여 Reducer값 변경!

        - action.js 에 정의해놓은 필요한 액션을 import 해와야 함

        - dispatch할 때 이 action을 사용하여 reducer의 값을 변경함   

         - 호출되어 변경될 때마다 Reducer는 Redering됨 => 이때, 새로운 값으로 새로 만들어 지는 것

   - Container에서  UseSelector를 통해 Reducer의 값을 호출한다.

  


 

Redux 는 상태관리 도구다.

리액트는 공식 홈페이지 소개를 보면 라이브러리라고 함. 

상태관리와 라우팅을 안해주기 때문! => 하지만, 리액트의 생태계 자체는 프레임워크다.

 

리덕스는 단방향이다.

리덕스는 함수형 프로그래밍을 좋아하는 개발자가 만듬!

 

양방향?

화면을 바꿔도 데이터가 바뀌고

데이터가 바껴도 화면을 바뀜

 

 


Redux의 필요성

 

Component A

  - State

 

Component B

  - State

 

        Component C

             - State

 

가 있을 때, 각각의 컴포넌트에 state 상태가 존재할 때,

하나의 컴포넌트 안에서 상태를 바꾸는게 어렵지 않음.

 

그런데, Component C가 B의 자식이 되었을 때, 머리가 아파지기 시작함

부모가 자식의 상태를 바꾸고 싶고,

자식이 부모의 상태를 바꾸고 싶을 때, 엄청 헷갈림.

 

부모에서 자식으로 내려갈땐 prop만 씀

부모의 부모의 부모를 C에서 바꾸려고 할 때?

Context API가 나온다. 여기서 편의 기능을 더 붙이면 Redux가 된다. 

 

State를 Redux하나에 다 몰아 넣고 관리하면 State를 더이상 쓸 필요가 없다.

리덕스를 쓰면 State를 안 쓸수 있다!

 

컴포넌트 간의 state를 넘나들 때 redux를 쓴다.

만약 상태가 컴포넌트 B에만 포함되어있겠다! 하면 state를 써도 됨.

=> 하나의 컴포넌트 안에서만 state를 쓸 때 state를 사용하고,

=> 컴포넌트를 넘나들 때는 Redux에 몰아 넣는다!


Action과 리덕스의 장단점

 

 

이 전체를 store라고 부름

Store(데이터묶음)

 

그 안에 state가 있고, 일반 객체 모양임

 

Action?은 동작 행동

State를 어떻게 바꿀지에 대한 행동을 적어놓은 것임

a -> b 로 바꾼다는 action을 만듬

실행은 dispatch라고 함.

 

action을 실행하게 되면 a가 b로 바뀐다.

미리 만들어 놓고 때에 따라서 dispatch가 바꿔주는 형태

 

action들이 dispatch(얘도 함수임)될 때마다 기로깅 남아서,

누가 무엇을 바꿨고 누가 뭐를 바꿨고 히스토리가 남아서 에러를 찾아낼 때 편하다.

 

단점: action들을 미리미리 만들어 놔야함.

타임머신 기능을 쓰려면(히스토리) state라는 객체를 매번 새로 만들어 줘야함.

 

불변성 -> Reducer에서 실제로 새로운 객체를 만들어 냄

=> 그래서 Redux의 기존 Store에 들어있는 State가 새로운 객체로 만들어진다.

 

 

Redux의 장점 :

-  에러가 적게난다.

- 기록이 있어서 타임머신 기능 가능.

   밑에서 위로 거슬러 올라갈 수 있다.

    개발하다가 이전 액션으로 돌아가고 싶을 때 갈 수 있다

 


프로젝트 셋팅

 

redux.js.org 공식문서 보셈

 

- npm으로 설치해야함

- 공식문서는 순서대로 날 잡고 쭉 읽으셈

- 관계를 그려본다. 공식문서 꼭 읽어라!

- api 레퍼런스 읽지마셈

 

위 그림을 그대로 아래 코드로 구현한 것

const { createStore } = require('redux');

const reducer = () => {};
const initialState = {
	compA: 'a',
    compB: 12,
    compC: null,
};

// 아래 처럼 바꾸면 안됨!! => 아래처럼 편하게 바꾸는걸 포기해서 단점이자 장점, 장점은 추적가능
ininitalState.compA = 'b';

//createStore 두번째 인수로 객체 만들 수 있음
const store = createStore(reducer, initialState);

console.log(store.getState());

 


Action 생성기와 리듀서

- 위 코드 내용에서 이어짐

const { createStore } = require('redux');

/// 1. store 만드는 과정

const reducer = () => {};
const initialState = {
	compA: 'a',
    compB: 12,
    compC: null,
};

//createStore 두번째 인수로 객체 만들 수 있음
const store = createStore(reducer, initialState);

console.log(store.getState());

// 아래 처럼 바꾸면 안됨!! => 아래처럼 편하게 바꾸는걸 포기해서 단점이자 장점, 장점은 추적가능
ininitalState.compA = 'b';

// 코드는 추상적으로 짜는게 좋음 ex. 변수명을 적는 상황 피하기
// ex changeCompAtoB 이런게 구체적
// ex changeCompA가 추상적
// 이를 함수로 만듬


// 2. Action 만들기 (객체)
const changeCompA = (data) => {
	return { //action
    	type: 'CHANGE_COMP_A',
        data,
    }
};

// store.dispatch({
//		 type: 'CHANGE_COMP_A',
//        data: 'b',
// });
// 위 아래는 같은 코드


// 3. dispatch

store.dispatch(changeCompA(data: 'b'));

console.log(store.getState());

 

매번 스테이트가 바뀔 때마다 알 수가 없다!

initialState와 nextState를 찍어봐야 알 수 있는데...

이때 reducer를 사용하면 된다.

 

const { createStore } = require('redux');

// 1. store 만드는 과정

const reducer = (prevState, action) => {
	switch (action.type) {
    	case 'CHANGE_COMP_A':
        	return {
            	compA: action.data,
                compB: 12,
                compC:null,
            };
    }
};

const initialState = {
	compA: 'a',
    compB: 12,
    compC: null,
};

//createStore 두번째 인수로 객체 만들 수 있음
const store = createStore(reducer, initialState);

console.log(store.getState());

// 2. Action 만들기 (객체)
const changeCompA = (data) => {
	return { //action
    	type: 'CHANGE_COMP_A',
        data,
    }
};


// 3. dispatch

store.dispatch(changeCompA(data: 'b'));

console.log(store.getState());

 

Redux의 state: 기본값이 있고

action: 그 기본값을 바꾸는 것

reducer: 그 Action을 dispatch하여 새로운 state를 만들어 주는애 !

=> 기존 것이 수정되는 것이 아니라, 새로운 것으로 대체되는 것

 

실무에서는?? 새로운 것으로 대체된다면, 기존것을 ... 로 복사시킨 후 새로 바뀌는 인수?만 새로 대체되도록 만든다.

아래 코드를 보면 됨

 

case 'UPDATE_LOGIN_CUSTOMER_INFO':
        return {
            ...state,
            loginCustomerInfo: {
              ...state.loginCustomerInfo,  // 기존에 기존거 복사한 뒤 
              				//변경된 부분은새로 만들어서 dispatch한것도 반영되도록 함
              ...action.payload,
            },
        };

 

 

그래서 액션이 많아지면 케이스들이 점점 늘어나게 됨

const { createStore } = require('redux');

// 1. store 만드는 과정

const reducer = (prevState, action) => {
	switch (action.type) {   // 아래 action에서 만든 type
    	case 'CHANGE_COMP_A':
        	return {
            	compA: action.data,  // 아래 action에서 만든 data
                compB: 12,
                compC:null,
            };
            case 'CHANGE_COMP_B':
        	return {
            	compA: 'a',  // 아래 action에서 만든 data
                compB: action.data,
                compC:null,
            };
    }
};

const initialState = {
	compA: 'a',
    compB: 12,
    compC: null,
};

//createStore 두번째 인수로 객체 만들 수 있음
const store = createStore(reducer, initialState);

console.log(store.getState());

// 2. Action 만들기 (객체) => 함수는 액션이 아니라 그 안의 객체가 Action임!
// type: name 등등 뭐 꼭 type이 아니어도 됨
// 이렇게 action의 객체의 요소의 이름이 바뀌면 위 reduce에서도 action.name action.payload 이렇게 바꿔주 바꿔주면 됨!

// action 생성자는 아래것이고 이건 리덕스의 필수 개념은 아님
// 중복을 없애려고 만드는 것
const changeCompA = (data) => {
	return { //action
    	type: 'CHANGE_COMP_A',
        data,
      // payload,  
      // error,
      // result,
    };
};


// 3. dispatch

store.dispatch(changeCompA(data: 'b'));

console.log(store.getState());

 

 

!팁:

디자인 패턴 공부하는 것보다 중복제거 연습하다보면 그렇게 됨

불변성과 가변성

 

기존 state를 spread를 통해 얕은 복사한 후 내가 바꾸고 싶은 것만 action.data로 만들어줌.

그럼 새로운 객체는 유지되면서 내가 바꾸고 싶은 것만 바꿀 수 있음.

 

* 오타 상황을 대비해 case에 default로 리턴값 만들어주기.

 

const { createStore } = require('redux');


const reducer = (prevState, action) => { 
	switch (action.type) {   // 아래 action에서 만든 type
    	case 'CHANGE_COMP_A':
        	return {
            	compA: action.data,  // 아래 action에서 만든 data
                compB: 12,
                compC:null,
            };
            case 'CHANGE_COMP_B':
        	return {
            	compA: 'a',  // 아래 action에서 만든 data
                compB: action.data,
                compC:null,
            };

    }
};


// spread로 얕은 복사해서 중복 줄이기!

cons reducer = (prevState, action) => { //리듀서는 리턴을 무적권 해줘야함!
	switch (action.type) {
		case 'CHANGE_COMP_A' :
        return {
        	...prevState;
            compA: action.data;
        };
		case 'CHANGE_COMP_B' :
        return {
        	...prevState;
        	compB: action.data;
        };
        default: //위 case명에서 오타내는 경우를 위해 반드시 무언가를 리턴해야함
        	return prevState;
	}
};

 

 

1. 스토어 만들고

2. 액션만들고

3. 액션을 디스패치하고

4. 그렇게 디스패치된 액션은 미리 만들어 놓은 리듀서에 걸려서 다음 state를 만들어 짐

 

결과 => 새로운 

 

단점: reducer가 엄청나게 길어짐! 케이스가 많으니깐. 액션도 마찬가지로 길어짐

 

 

예제)

const { createStore } = require('redux');

// REDUCER (액션과 리듀서는 짝!)
const reducer = (prevState, action) => {
	switch (action.type) {   // 아래 action에서 만든 type
    		case 'logIn':
        	return {
				...prevState,
                user: action.data,
                
            };
            case 'logOut':
        	return {
				...prevState,
                user: null,

            };
            case 'addPost':
            return {
            	...prevState,
				posts: [...prevState.posts, action.data], 
            	// 새로운 데이터는 뒤에 추가, 기존 데이터는 spread! => 바뀔 부분만 바뀜
            };
    }
};

// 액션 creator 만들 때 팁
// inital state중심으로 생각하고, 기존 state를 어떻게 바꾸고 싶다는 것만 생각!

const initialState = {
	user: null,
    posts: [],
};

// 배열 사용시, 불변성 유지하면서 다음 단계 어떻게 넘어갈지...

const nextState = {
	...initialState,
    posts: [action.data],
};

const nextState = {
	...initalState,
    posts: [...initalState.posts, action.data], //불변성 유지
}

// ACTION

const store = createStore(reducer, initialState);

const logIn = (data) => {
	return { 
    	type: 'LOG_IN',
        data,
    };
};

const logOut = () => { //로그아웃할 때 정보 필요없음
	return {
    	type: 'LOG_OUT',
    };
};

const addPost = (data) => {
	return {
    	type: 'ADD_POST',
        data,
    }
};

// -------------------------------------- React화면에서 실행하는 것

// DISPATCH

store.dispatch(logIn(data: {
	id: 1,
    name: 'dadong',
    admin: true,
}));

store.dispatch(addPost(data: {
	userId: 1,
    id: 1,
    content: '안녕 다동아!',
}));

store.dispatch(logOut());

 

 

 

핵심

 

-> 하나의 컴포넌트(나자신) 및 부모자식간 왔다갔다 하는 데이터 => State

-> 3개 이상, 조상 자손 컴포넌트 간 데이터 => Redux 로 글로벌하게 관리

   - 페이지가 전환될 때도 global

 

ex. 게시판 리스트할 때? 리덕스에 담음.

Why? 뒤로 가기 누르면 다시 돌아올때,,, Redux에 캐싱기능이 있어서

다시 안불러와도 됨. 물론,, http의 캐싱을 사용해도 됨. 

 

 

 

 

 

 

출처: 제로초 강의 Redux vs MobX

 

Redux vs MobX (둘 다 배우자!) - 인프런 | 강의

리덕스와 몹엑스 뭘 선택할지 고민되시나요? 일단 둘 다 배우고나서 고민합시다!, [사진] 리덕스 vs 몹엑스 뭘 선택할지 고민된다면 일단 둘 다 배우자! 🗒 강의소개 React 프로그래밍을 할 때, 상

www.inflearn.com