본문 바로가기

프로그래머스 풀스택 데브코스/데브코스 TIL

웹 풀사이클 데브코스 TIL 55일차

Cart 컴포넌트 생성

부트스트랩의 Table을 이용해서 간단한 장바구니 컴포넌트를 생성해보자.

//App.ts
        <Route path='/cart' element = {<Cart></Cart>}></Route>
function Cart(){

    return(
        <Table striped bordered hover>
      <thead>
        <tr>
          <th>#</th>
          <th>상품명</th>
          <th>수량</th>
          <th>변경하기</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>1</td>
          <td>아메리카노</td>
          <td>1</td>
          <td><button>변경</button></td>
        </tr>
        <tr>
          <td>2</td>
          <td>카페라떼</td>
          <td>1</td>
          <td><button>변경</button></td>
        </tr>
      </tbody>
    </Table>
    )
}

Redux를 사용하는 이유

커피 상태는 App Detail Cart 모두에서 사용된다. 이를 프롭스로 전달하는 것도 가능하지만 컴포넌트가 중첩되어있어서 불편하다. 이를 위해 Context Api 방법을 배웠지만 더 좋은 라이브러리인 Redux가 있다. Redux를 사용하면 state가 저장되어있는 파일을 만들고 그곳에 상태들을 저장한다. 그렇게 하면 모든 컴포넌트에서 접근하여 사용할 수 있다.

Redux 설치

npm install @reduxjs/toolkit react-redux 

npm 명령어를 통해 리덕스를 설치한 후 src 폴더에 store.tsx 파일을 만들어준다. 이 파일은 상태들을 담아줄 통같은 존재이다.
그 후 몇가지 세팅을 해주어야한다.

//store.tsx
import {configureStore} from "@reduxjs/toolkit";

export default configureStore({
    reducer : {

    }
})
//index.tsx
import store from './store';
import { Provider } from 'react-redux';


const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <React.StrictMode>
    <Provider store = {store}>
      <BrowserRouter>
      <App />
      </BrowserRouter>
    </Provider>
  </React.StrictMode>
);

index.tsx 에서도 몇가지 세팅을 해주는데 store를 import해주고 Provider도 import 해준다. 그후 App 부분을 Provider로 감싸주고 보관함 설정을 해주면 끝이다.

store에 상태 보관하기

//store.tsx
import {configureStore, createSlice} from "@reduxjs/toolkit";

let user = createSlice({
    name : 'user',
    initialState : '홍길동',
    reducers : {}
})

let stock = createSlice({
    name : 'stock',
    initialState : [100,200,300],
    reducers : {}
})

export default configureStore({
    reducer : {
        user : user.reducer,
        stock : stock.reducer,
    }
})

createSlice 함수를 사용해서 state를 생성하고 등록해준다.

//Cart.tsx
import { useSelector } from "react-redux";\


 let data = useSelector((state)=>{
        return state;
    })
    console.log(data);

다른 파일에서는 useSelector를 사용해서 상태를 가져올 수 있다.

store 데이터 바인딩하기

store에 커피 정보를 저장하고 바인딩해서 사용해보겠다.

//store.tsx
export interface CartItem{
    id : number;
    name: string;
    count: number;
}

export interface CartArray{
    cart : CartItem[];
}


let cart = createSlice({
    name : 'cart',
    initialState : {
        cart: [
        {id : 0, name: '아메리카노', count : 1},
        {id : 1, name: '카페라떼', count : 1}
    ]} as CartArray,
    reducers : {}
})

store에 저장할때 타입스크립트에 맞게 타입과 타입배열을 선언해서 사용한다. as 는 type Assertion 키워드로 변수나 오브젝트의 타입을 지정해 줄 때 사용한다.

//Cart.tsx
interface CartState{
    cart : CartArray;
}

let cartState = useSelector((state : CartState)=>{
    return state;
})

CartState 타입을 선언후 가져온다.

 {
            cartState.cart.cart.map((item, idx)=>{
                return (
                    <tr key={idx}>
                    <td>{item.id}</td>
                    <td>{item.name}</td>
                    <td>{item.count}</td>
                    <td><button>변경</button></td>
                  </tr>
                )
            })
        }

그 후 배열 map 함수를 사용해 처리해 줄 수 있다.

store 데이터 변경하기

store 에 저장된 데이터를 수정하는 것은 다음과 같다.

//store.tsx
let user = createSlice({
    name : 'user',
    initialState : '홍길동',
    reducers : {
        changeName(){
            return '리덕스'
        }
    }
})

export let {changeName} = user.actions;

리덕스의 데이터 수정은 수정함수를 store에 구현하는 방식으로 이루어진다. 그 후 actions 를 통해 export 해준다. actions 는 함수들을 다 반환하는 프로퍼티이고, 비구조화를 통해 받아냈다.

import {changeName} from "../store";
import { useSelector, useDispatch } from "react-redux";

    let dispatch = useDispatch();

이때 함수를 바로 콜할수는 없고 useDispatch 라는 것을 사용해서 불러줘야한다.

<button onClick={()=>{
        dispatch(changeName());
      }}>이름변경</button>
      <div>{cartState.user}</div>

변경함수를 사용하는 예제 코드이다. dispatch 로 함수를 감싸주어야 제대로 작동한다.

리덕스가 복잡한 이유는 무엇일까? 만약 각 컴포넌트에서 직접 store에 접근해 데이터를 변경하면 오류가 발생했을 경우 그 원인을 찾기가 쉽지 않을 것이다. 의존성이 있는 컴포넌트를 모두 살펴봐야 하기 때문이다. 하지만 수정함수를 만들어 두고 각 컴포넌트에서 store에 부탁하는 방식으로 구현하면 오류가 나도 store 하나만 보면 되니까 간편하다. 즉, 변경의 주도권을 store가 가지는것이 효율적이라는 것이다.

store 데이터가 array 나 object 일 때 변경하는 법

배열이나 객체일 때도 비슷한 방식으로 변경할 수 있다.

let user = createSlice({
    name : 'user',
    initialState : {name: '홍길동', age: 20},
    reducers : {
        changeName(){
            return {name: '리덕스', age: 20}
        }
    }
})

initialState 를 객체형태로 바꾸었다. 이를 변경하는 함수에서 새로운 객체를 return 할 수 있다. 하지만 새로운 객체를 리턴하는 방식은 다소 비효율적이다. 그래서 다음과 같이 코드를 수정할 수 있다.

 changeName(state){
            state.name = '리덕스';
        }

함수의 인자로 state를 받아오고 그 state를 직접 수정하는 방식이다. 이렇게 하면 더 간단하고 효율적인 코드가 완성된다.

후기

redux의 사용법에 대해 본격적으로 알아보았다. 다소 복잡하지만 프로젝트가 복잡해지면 유용하게 사용할 수 있을 것 같다.
키워드: 프로그래머스 데브코스, 국비지원교육, 코딩부트캠프