[redux 1탄] 올게 왔다. Redux 다. 질문은 거절한다✋.

const job = '프론트엔드';·2023년 8월 16일
0
post-thumbnail

리덕스(Redux)

'자바스크립트' 애플리케이션을 위한 상태관리 라이브러리

리액트 하면 리덕스 아는거 아니요? 아니요😳?? 아닌데요😳??

  • 이거는 자바스크립트하면 알아야죠. '자바스크립트 애플리케이션을 위한' 상태관리 라이브러린데
  • 하지만 나도 몰랐음..데헷

됐다. 리덕스 쌉가능

이쯤되면 지겹다. ⭐'상태관리'⭐

  • 하지만, 그만큼 최적화! 최적화! 최적화!
  • Context API, useReducer을 이용해서 상태관리? 가능했지.
  • 하지만 규모가 큰 프로젝트에서는? (난, useState만 으로도 좋음..)
  • 여러 종류의 상태를 하나의 객체로써 관리할 때에는 리덕스가 유리함

리덕스는 리액트를 위해 만든 라이브러리가 아님

따라서, install 필요 !

npm install redux react-redux
  • 리덕스랑 리액트 전용 리덕스를 둘 다 설치해줘야 함

리덕스 작동원리가 뭔가요?

  • 순환구조라서 어디서부터 시작된다고 하긴 어렵지만, 그래도 Component를 시작점으로 잡는다면
1. 컴포넌트에서 시작
ex. 할 일 목록에서 할 일을 '추가'하겠다 - 추가? 액션발현
2. 액션 객체가 만들어짐
리듀서 호출 : 처리해줘 리듀서!
3. 리듀서는 요구하는 로직을 처리
4. 스토어 업데이트
스토리지에서 상태가 바뀌면 렌더링됨

그럼 하는 역할이 구체적으로 뭔가요?

액션 사용자가 수행하는 작업 유형을 지정하는 자바스크립트 객체
리듀서 리액트 앱의 변경 사항을 결정하고 업데이트 상태를 반환하는 함수
스토어 전체 상태 트리를 관리하는 저장소

뒤로 넘기지 맙시다🙏🏻. 중간점검

하나. 리덕스는 자바스크립트를 위해 나왔다
두울. 리덕스는 순환구조다

아직까지 이게 끝. 지금부터 이해한 순서대로 정확하고 똑같이 작성해 보겠음.

예제코드 1)

createStore 상태저장소 만들기

import { createStore } from "redux";

  • createStore를 '그냥 리덕스'에서 import함
  • 주의사항 ! 가로로 줄이 있음 이런 경우는 '더 좋은 방법이 새로 나왔어. 그거로 해' 이런 의미라고 함. 하지만 아직까지 createStore가 최고라고 강사님이 얘기해주셨음(강사님의 개인적인 의견이었음).

react-redux요소 import


Provider 상태를 통합해서 묶는 기능
useSelector provider가 상태를 통합적으로 제공하면 고르는 기능
useDispatch 상태의 액션을 제공받는 기능

리듀서 함수 만들기(action의 필수요소? type)

  • 내 상태 객체를 액션에 따라 관리 해주는 역할(useReducer와 같음)
function reducer(currentState, action) {

  if (currentState === undefined) {
    return { number: 1 };
  }

  const newState = { ...currentState };
  if (action.type === "PLUS") {
    newState.number++;
  }
  return newState;
}
  • 첫번째 조건문: 현재 상태가 비어있어? 그럼 초기값은 1이야
  • 두번째 조건문: 있다는 전제하에(왜냐? const newState = { ...currentState }) 있으면, PLUS액션 가지고 있는 상태야? 그럼 현재 상태 값에 1을 더해줘
  • 그리고 상태를 최신으로 업데이트 하겠어.

reducer 상태의 저장고를 만듦(보통 프로젝트에서는 store.js로 상태만 저장함)

const myStore = createStore(reducer);

컴포넌트 구조 살짝 보자면

  • 최상위 App컴포넌트는 Left컴포넌트와 Right컴포넌트를 가지고 있음
const App = () => {
  return (
    <>
      <div>
      	<Left />
		<Right />
      </div>
    </>
  );
};
  • 즉, App컴포넌트에서 Left컴포넌트와 Right컴포넌트에 상태를 넘기겠다. 정도의 의미의 리덕스 관리를 구성하고자 함

Provider로 묶어주기

const App = () => {
  return (
    <>
      <div>
    	<Provider store={myStore}>
          <Left />         
          <Right />
        </Provider>
      </div>
    </>
  );
};
  • 저장해둔 상태 저장고 myStore를 store에 담에서 전달

자식컴포넌트에서 상태 사용하기(useSelector, useDispatch)

useSelector

function Left() {


  //상태객체를 전체받아서 필요한 부분만 리턴
  const number = useSelector((state) => {
    return state.number;
  });
  return (
    <>
      <h1>{number}</h1>
    </>
  );
}
  • 상태 객체를 받아와서 그 중에 원하는 상태만 선택해서 사용하고 싶을때, useSelector

useDispatch

function Right() {
  //상태의 액션만 필요하기 때문
  const dispatch = useDispatch();
  return (
    <>
      <button
        onClick={() => {
          dispatch({ type: "PLUS" });
        }}
      >
        +
      </button>
    </>
  );
}
  • 상태의 액션이 필요할때는 useDispatch

이해안가죠? 저도 그랬어요. 근본적인 것부터 봅시다.

리덕스 왜 씀? 나는 useReducer랑도 어색해요

props 쓰기 귀찮을 때 (나잖아?)

  • 만약에 부모컴포넌트에 있는 상태를 자식이 가져다 쓰고 싶을때 props를 씀
  • 근데 최상위 부모컴포넌트의 자식 컴포넌트의 자식 컴포넌트의 자식 컴포넌트의 자식 컴포넌트의 자식 컴포넌드...아무튼 중첩된 컴포넌트가 굉장히 많다면

상태관리 간편하게 하고 싶을때 (사실 간편하진 않지)

  • state관리가 용이함
  • 미리 수정방법을 다 넣어놓고 사용하고자 하는 컴포넌트는 수정 요청을 함
  • 버그 발생시 추적이 쉬움

다시, 다시 어려우니깐 천천히

하나. 리듀서 함수 만들기

function reducer(현재상태, action) { 
 조건, 타입 등등
}
  • 자, currentState는 '현재상태' 이거는 useState에서 setState랑 똑같이 상태를 업데이트 시켜줌
  • 그리고 action은 type이 반드시 필요함. 왜냐하면 이 또한 조건이기 때문

두울. reducer 저장고

  • 만들어둔 리듀서 함수를 createStore()에 담은 형태

세엣. Provider로 묶어두고, store에 리듀서 저장고를 담음
네엣. 업데이트 된 상태를 받아서 쓰고싶다? useSelector
다섯. 상태 액션을 받아서 쓰고싶다? useDispatch

여기서 문제. 버튼을 누르면 1씩 더해지는 기능은 이미 만들어졌음. 그러면 버튼을 누르면 1씩 빼는 기능을 만들려면?

리듀서 함수에 마이너스 타입을 추가

function reducer(currentState, action) {

  if (currentState === undefined) {
    return { number: 1 };
  }

  const newState = { ...currentState };
  if (action.type === "PLUS") {
    newState.number++;
  }
  //* 여기 추가 *//
  if (action.type === "MINUS") {
    newState.number--;
  }
  return newState;
}

useDispatch로 액션을 받아와서 이벤트를 줌

function Right() {

  const dispatch = useDispatch();
  return (
    <>
      <button
        onClick={() => {
          dispatch({ type: "PLUS" });
        }}
      >
        +
      </button>

       /* 여기 추가*/
	 <button
        onClick={() => {
          dispatch({ type: "MINUS" });
        }}
      >
        -
      </button>
    </>
  );
}

예제코드2) 조금 더 복잡한 코드를 보자(중첩-중첩-중첩-...-중첩 컴포넌트).

App컴포넌트와 자식컴포넌트

export default function App() {
  return (
    <div id="container">
      <h1>Root</h1>
      <div id="grid">
        <Provider store={store}>
          <Left1></Left1>
          <hr />
          <Right1></Right1>
        </Provider>
      </div>
    </div>
  );
}
  • App컴포넌트는 자식 컴포넌트로 Left1과 Right1을 가지고 있음
  • 통합하기 위해 로 감싸놨음

reducer함수 만들기

  function reducer(currentState, action) {
  if (currentState === undefined) {
    return {
      number: 1,
    };
  }
  const newState = { ...currentState };
  if (action.type === "PLUS") {
    newState.number++;
  }
  if (action.type === "MINUS") {
    newState.number--;
  }
  return newState;
}
  • 액션 타입은 plus와 minus
  • 현재 상태가 존재하지 않다면 1로 리턴(1번이 되라)

저장고에 넣어두기

const store = createStore(reducer);
  • 그래서 App컴포넌트에서 Provider에 store안에 저장고를 담아줌

중첩된 컴포넌트에서 상태가져다 쓰기

 function Left1() {
  return (
    <div>
      <h1>Left1 </h1>
      <Left2></Left2>
    </div>
  );
}
function Left2() {
  console.log("2");
  return (
    <div>
      <h1>Left2 : </h1>
      <Left3></Left3>
    </div>
  );
}
function Left3() {
  console.log("3");
  const number = useSelector((state) => state.number);
  return (
    <div>
      <h1>Left3 : {number}</h1>
    </div>
  );
  • App의 자식은 Left1과 Right1 이었음. 그런데 Left3이 가져다 쓴다니?
  • 이래서 리덕스 씀
  • props로 계속 전달해주지 않아도 멀리 떨어져 있는 Left3컴포넌트는 상태를 useSelector만 있다면 가져다 쓸 수 있음
  • 왜냐? 부모의 부모님 Left1이 로 묶여서 상태값을 전달받기 때문.
  function Right1() {
  return (
    <div>
      <h1>Right1</h1>
      <Right2></Right2>
    </div>
  );
}
function Right2() {
  return (
    <div>
      <h1>Right2</h1>
      <Right3></Right3>
    </div>
  );
}
function Right3() {
  const dispatch = useDispatch();
  return (
    <div>
      <h1>Right3</h1>
      <input
        type="button"
        value="+"
        onClick={() => {
          dispatch({ type: "PLUS" });
        }}
      ></input>
      <input
        type="button"
        value="-"
        onClick={() => {
          dispatch({ type: "MINUS" });
        }}
      ></input>
    </div>
  • Right3도 마찬가지
  • 상태 액션을 가져다 쓸 때, 부모의 부모인 Right1이 로 묶여서 상태를 전달 받기 때문에 useDispatch를 만들어주면 사용가능 !

예제코드3) 마지막으로 순서대로 천천히 작성

createStore import / Provider, useSelector, useDispatch import

import { Provider, useSelector, useDispatch } from "react-redux";

reducer함수 만들기

function reducer(currentState, action) {
  if (currentState === undefined) {
    return {
      isSignIn: false,
    };
  }
  const newState = { ...currentState };
  if (action.type === "SignIn") {
    newState.isSignIn = true;
    newState.userName = action.userName;
  }
  return newState;
}
  • reducer는 현재상태(currentState)와 action을 인수로 받고 있음
  • 첫번째 조건, 현재 상태가 undefined(없다면)라면 현재상태는false로 리턴해라
  • 현재상태를 최신상태로 업데이트
  • 두번째 조건, 만약에 action.type이 SignIn이라면 isSignIn의 상태는 true로 그리고, userName의 상태는 action.userName의 상태가 되라
  • 그걸 최신상태로 리던해라

저장고에 담기

const store = createStore(reducer);

상태를 사용할 컴포넌트들을 Producer로 감싸기

        <Provider store={store}>
          <Form></Form>
          <Service />
        </Provider>
  • 그렇다면, 저장고에 저장된 상태를 Form 컴포넌트와 Service 컴포넌트에서도 사용할 수 있음(그 이하 자식 컴포넌트들도 사용가능)

Form 컴포넌트: 상태 자체를 바꾸기 위함

const Form = () => {
 
  const dispatch = useDispatch();
  return (
    <form
      onSubmit={(e) => {
        e.preventDefault();
        dispatch({
          type: "SignIn",
          userName: e.target.userName.value,
        });
      }}
    >
      <input
        name="userName"
        placeholder="사용자 이름을 입력하세요"
        autoComplete="off"
      />
      <button type="submit">Sign In</button>
    </form>
  );
};
  • 상태자체를 바꿔줄 생각이기 때문에 useDispatch 사용
  • form태그 안에는 사용자 이름을 입력할 input과 그 값을 넘겨줄 submit타입의 button이 있음
  • input창에 사용자 이름을 입력하고 그 value값을 담아서 버튼을 누르면 onSubmit함수를 통해 값을 전달
  • type이 SignIn이라고 액션값을 정해놨기 때문에 reducer 함수의 조건식에 따라 isSignIn은 true로 바뀌고, 입력받은 action.userName으로 현재상태가 바뀜

Service 컴포넌트: 상태자체를 가져다 씀

const Service = () => {

  const userName = useSelector((state) => state.userName);

  return (
    <>
      {userName ? (
        <p>{userName}! 환영합니다!</p>
      ) : (
        <p>로그인 후 이용할 수 있습니다</p>
      )}
    </>
  );
};
  • 현재 상태 자체를 가져다 쓸 것이므로 useSelector사용
  • useSelector에 콜백함수로 state를 담아 전달하면 state.userName을 반환
  • 만약, userName이 true(비어있지 않으면 true임 왜냐, 비어있으면 false로 반환하기로 reducer함수에서 정해놨으니깐)라면, '{userName}님! 환영합니다!'반환하고, false라면 '로그인 후 이용할 수 있습니다'반환

끝인데?

두려워 하지말자. 여기까지만 알면된다. 왜냐. 요즘 리덕스 안쓰는 추..ㅅ...ㅔ..

  • 누가 아 리덕스 그거....이러면 '아아 나 그거알아' 정도를 해주면 되쟎슴? 사실 이거 면접에서 물어봄. 이러면 해야겠지? 알아야겠지?
  • 누군가 얘길했다. 왜 리덕스를 사용해보지 않았느냐고 물어보면 '그정도로 규모가 큰 프로젝트를 해본 경험이 없습니다. 리덕스는 대형 프로젝트에 적합하지 않나요?' 라고 하랬다.
profile
`나는 ${job} 개발자`

0개의 댓글