5월 25일(React, 쇼핑몰 만들기)

JY·2022년 5월 25일
0
post-thumbnail

Bootstrap


https://react-bootstrap.github.io/

Bootstrap 설치 및 CSS 파일 import

npm install react-bootstrap bootstrap
<link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
  integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
  crossorigin="anonymous"
/>

컴포넌트를 import 해야 하는 것도 잊지 말것!

import { Button } from 'react-bootstrap';

Redux


장바구니 페이지를 만들기 위해 Cart.js 파일을 생성했다.

장바구니 데이터를 state에 보관해두고 데이터 바인딩을 해보자.
그런데 이 state가 Cart 외에 다른 컴포넌트에서도 필요하다면 어디에 만들어야 할까? 👉 당연히 App(최상위 컴포넌트)에 만들어야 하는데, props 전송하는 게 귀찮다!
이때, 사용하는 것이 Redux이다.
리덕스를 사용하면 모든 컴포넌트들이 state를 직접 빼서 사용할 수 있다. (= props를 전송할 필요가 없다.)


1. Redux 설치 및 세팅


React, React DOM 버전이 18.1.0 이상이어야 설치가 가능하다. (package.json에서 확인 가능)

npm install @reduxjs/toolkit react-redux

store.js 생성 👉 쉽게 말해서, state를 보관하는 통이라고 생각하면 된다.

import { configureStore } from '@reduxjs/toolkit'

export default configureStore({
  reducer: { }
}) 

index.js에 store를 사용하겠다고 해야 한다. 👉 <Provider store={store}>

이제 store.js에 있던 state를 App에서 가져다 쓸 수 있다. (<Provider>로 감싸져 있는 모든 자식이 state를 가져다 쓸 수 있는 것)

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { Provider } from 'react-redux';
import store from './store';

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

reportWebVitals();

2. storestate 보관


state 하나를 'slice'라고 부른다.

createSlice()를 상단에서 import 해온 다음, { name : 'state이름', initialState : 'state값' }를 넣으면 state가 하나 생성된다.

let user = createSlice({
  name: 'user',
  initialState: 'kim'
})

이렇게 생성을 했으면, 등록을 해야 사용할 수 있다.

state 등록은 configureStore() 안에 하면 된다. { 작명 : createSlice만든거.reducer }. 여기에 등록한 state는 모든 컴포넌트가 자유롭게 사용할 수 있다.

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

어떻게 사용?
이제 Cart.js에서 useSelector()를 이용하면 된다. useSelector()는 Redux store를 가져와주는 함수이다.

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


🤔 useSelector()를 편하게 쓰려면?
state: store 안에 있던 모든 state를 뜻한다.

let a = useSelector((state) => {return state});



user라는 항목만 저장해서 가져다 쓸 수도 있다.

let a = useSelector((state) => {return state.user});



축약해서 사용 가능!

let a = useSelector((state) => state.user);

간단한 프로젝트에서는 props를 전송하는 것이 코드가 더 간편하다.
그러나 컴포넌트가 많아지면 Redux를 사용하는 것이 더 나을 수 있다! 또한, 반드시 모든 state를 store에 관리해야 하는 것은 아니다. (공유가 필요하지 않은 state라면 필요한 컴포넌트에서 생성해도 된다.)

+) store에 저장한 데이터를 Cart.js에서 사용하기

장바구니 데이터를 store에 저장하고 Cart.js에서 가져와서 출력해보자.

// 장바구니 데이터

[
    {id : 0, name : 'White and Black', count : 2},
    {id : 2, name : 'Grey Yordan', count : 1}
] 

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

...

        <tbody>
          {
            data.map((item, i) => 
              <tr key={data[i].id}>
                <td>{data[i].id}</td>
                <td>{data[i].name}</td>
                <td>{data[i].count}</td>
                <td>변경</td>
              </tr>
            )
          }
        </tbody>

...


3. storestate 변경


🤔 Redux state 변경 step
1. state를 변경해주는 함수를 만든다.
2. 만든 함수를 export한다.
3. 원할 때 그 함수를 실행해달라고 store.js에 요청한다. 👉 dispatch(state변경함수())


'kim'을 'John kim'으로 수정하기

기존에 저장된 데이터

let user = createSlice({
  name: 'user',
  initialState: 'kim'
})

  1. state를 수정해주는 함수 만들기
    기존 state인 kim을 John kim으로 변경해준다.

    let user = createSlice({
      name: 'user',
      initialState: 'kim',
      reducers: {
        changeName() {
          return 'John kim';
        }
      }
    })

    만약, 기존 state가 필요하다면, 다음처럼 하면 된다. (changeName()의 인자로 들어간 state는 기존 state를 의미한다.)

    let user = createSlice({
      name: 'user',
      initialState: 'kim',
      reducers: {
        changeName(state) {
          return 'John' + state;
        }
      }
    })

  2. 만든 함수를 export하기
    user.actions: state 변경 함수가 남는다.
    다음 코드는 오른쪽 자료를 변수로 빼는 문법이라고 생각하면 된다.

    export let { changeName } = user.actions;

  3. 만든 함수 import해서 사용
    이제 이름을 바꾸고 싶을 때 함수를 호출하면 된다.

    버튼을 누르면 'kim'에서 'John kim'으로 변경되도록 버튼에 onClick을 줘서 만들 수 있다.

    useDispatch(): store.js로 요청을 보내주는 함수이다.

    let state = useSelector((state) => {return state});
    let dispatch = useDispatch();
    
    ...
    
    <td><button onClick={() => dispatch(changeName())}>+</button></td>
    
    ...


🤔 이렇게 복잡한 이유..?

🤔 그래서 이 방식이 뭐가 좋은 거지?
컴포넌트가 많은 상황에서 효율적이다.

user를 여러 컴포넌트에서 가져다 쓰는데, 각 컴포넌트에서 직접 수정을 할 수 있는 상황이라고 가정해보자. 갑자기 'kim'이 '123'으로 바뀌었다면, 모든 컴포넌트를 뒤져서 어디서 바뀐 건지 알아내야 한다.

하지만 수정함수를 호출하는 식일 때는 '123'으로 바뀌었으면 store.js만 보면 된다.


4. statearray/object인 경우 변경


array/object의 경우 return문을 사용하지 않고 직접 수정해도 state 변경이 된다. (state.name = "park";)
그래서 문자 하나만 필요하더라도 일부러 {} 안에 담기도 한다.

// store.js

let user = createSlice({
  name: 'user',
  initialState: {
    name: 'kim',
    age: 20
  },
  reducers: {
    changeName1(state) {
      state.name = "park";
    }
  }
})

export let { changeName1 } = user.actions;
// Cart.js

  let state = useSelector((state) => {return state});
  let dispatch = useDispatch();

...

<button onClick={() => dispatch(changeName1())}>1</button>

+) 버튼 누르면 age가 1씩 증가

// store.js

increaseAge(state) {
  state.age += 1;
}
// Cart.js

<button onClick={() => {
  dispatch(increaseAge());
}}>버튼</button>

근데 경우에 따라 증가하게 할 숫자가 다르다면 다음처럼 새로운 함수를 계속 만들어야 할까?

// store.js

increaseAge1(state) {
  state.age += 1;
},
increaseAge2(state) {
  state.age += 5;
},
increaseAge3(state) {
  state.age += 10;
}

No.
함수에 파라미터를 하나 더 주면 된다.

// store.js

increaseAge(state, action) {
  state.age += action.payload;
}
// Cart.js

<button onClick={() => {
  dispatch(increaseAge(5));
}}>버튼</button>

🤔 action

  • action.type 하면 state 변경함수 이름이 나온다.
  • action.payload 하면 파라미터가 나온다.

profile
🙋‍♀️

0개의 댓글