본문 바로가기
웹 프론트엔드/React 리액트 (기초)

[React] 리덕스(redux) 기능으로 데이터 관리하기 ④ 해시맵

by 지구인한군 2020. 10. 27.

 이때까지 네 번에 걸쳐 리덕스 관련 포스팅을 했습니다. 기초, 디버깅, 코드 분리까지 어느 정도 리덕스 기능을 사용할 수 있지만, 문제는 어떤 데이터를 담아서 어떻게 사용하는 야입니다. 그래서 이번에는 key값과 object를 사용한 해시맵 구조를 담아보겠습니다. id, pass, name 정도의 유저 데이터를 관리하는 예시로 진행하겠습니다. 먼저 액션 추가 ㄱㄱ~

 

2020/10/16 - [Web프론트엔드/리액트] - [React] 리덕스(redux) 기능으로 데이터 관리하기 ① 기초

2020/10/22 - [Web프론트엔드/리액트] - [React] 리덕스(redux) 기능으로 데이터 관리하기 ② 디버깅

2020/10/23 - [Web프론트엔드/리액트] - [React] 리덕스(redux) 기능으로 데이터 관리하기 ③ 코드 분리

 

// ..\test_project\src\components\actions\userActions.js

// 액션 타입 정의
export const SET_USER = 'userReducer/SET_USER';
export const ADD_USER = 'userReducer/ADD_USER';
export const EDIT_USER = 'userReducer/EDIT_USER';

// 액션 함수 정의
export const setUser = arg => ({
    type: SET_USER,
    data: arg,
});
export const addUser = arg => ({
    type: ADD_USER,
    data: arg,
});
export const editUser = (key, name) => ({
    type: EDIT_USER,
    data: {key, name},
});

 

 액션은 간단합니다. 지난 시간에 했던 액션과 크게 다르지 않으며, 유저 데이터 수정인 editUser의 인자가 조금 다를 뿐입니다. setUser는 초기 데이터 세팅, addUser는 데이터 추가입니다. 그럼 중요한 리듀서 부분을 보겠습니다! 핵심!

 

// ..\test_project\src\components\reducers\userReducer.js

// 액션 타입 불러오기
import { SET_USER, ADD_USER, EDIT_USER } from "../actions/userActions";

// 데이터 정의 및 초기화
const initState = {
  keys: [],   // 키 인덱스 배열
  objs: {},   // 객체형 데이터 (해시맵)
};

// 초기화 데이터로 리듀서 만들기
export default function reducer(state = initState, action) {
  const { type, data } = action;
  switch (type) {

    // 유저 데이터 세팅
    case SET_USER: {
      let i = 0;
      // 인덱스 키 만들기
      const keys = data.map(obj => obj['key'] = i++);
      // 키 매칭해서 객체 만들기
      const objs = data.reduce((nextObjs, obj) => ({
        ...nextObjs,
        [obj['key']]: obj,
      }), {});
      return { ...state, keys, objs };
    }

    // 유저 데이터 추가
    case ADD_USER: {
      // 추가할 키 넣기
      const keys = state.keys;
      let i = keys.length;
      keys.push(...data.map(obj => obj['key'] = i++));
      // 추가할 객체 만들기
      const tempObjs = data.reduce((nextObjs, obj) => ({
        ...nextObjs,
        [obj['key']]: obj,
      }), {});
      // 기존 객체에 붙이기
      const objs = {...state.objs, ...tempObjs }
      return { ...state, keys, objs };
    }
    
    // 유저 데이터 수정
    case EDIT_USER: {
      const { key, name } = data;
      // 키 맞쳐서 수정하기
      return {
        ...state,
        objs:{
          ...state.objs,
          [key]: { ...state.objs[key], name },
        },
      };
    }
    default:
      return state;
  }
}

 

 위에서 만든 액션과 연결된 내용물입니다. 중요한 부분은 해시맵 구조인 initState의 keys 배열과 objs 객체입니다.

쉽게 설명드리면 objs에 JSON string 처럼 객체가 들어가고, keys에 각 유저의 키값이 들어있습니다. 키값으로는 주로 유니크한 유저 ID를 사용하지만, 이번에는 인덱스처럼 순번을 사용했습니다. 자 그럼 index.js에 리듀서 추가합니다.

 

// ..\test_project\src\components\reducers\index.js

import countReducer from './countReducer';
import loginReducer from './loginReducer';
import userReducer from './userReducer';

// 리듀서 모아서 내보내기
export default {
    countReducer,
    loginReducer,
    userReducer,
};

 

 count, login 리듀서는 지난 시간의 코드 분리에서 했던 내용이고 이번에는 userReducer 추가합니다. 그럼 본문 ㄱㄱ

 

// ..\test_project\src\components\UserDataRedux.jsx

import React, { PureComponent } from "react";
import { Provider } from "react-redux";
import reduxCreateStore from "./reduxCreateStore";
import { setUser, addUser, editUser } from "./actions/userActions";
import Text from "./Text";
import Button from "./Button";

class UserDataRedux extends PureComponent {
  constructor(props) {
    super(props);

    this.state = {
      count: 20,
    };

    // 콜백 함수 바인딩
    this.addUser = this.addUser.bind(this);
    this.editUser = this.editUser.bind(this);

    // 커스터마이징 함수로 스토어 생성
    this.store = reduxCreateStore();

    // 유저 데이터 세팅
    this.store.dispatch(
      setUser([
        {
          uid: "corona",
          pass: "dead4444",
          name: "넌왜왔니",
        },
        {
          uid: "corona19",
          pass: "ojimasio",
          name: "잘가세요",
        },
      ])
    );
  }

  // 유저 데이터 추가
  addUser() {
    this.store.dispatch(
      addUser([
        {
          uid: "corona" + this.state.count,
          pass: "nothing" + this.state.count,
          name: "없습니다" + this.state.count,
        },
      ])
    );
    this.setState({ count: this.state.count + 1 });
  }

  // 유저 데이터 수정
  editUser(key, name) {
    this.store.dispatch(editUser(key, name));
    this.forceUpdate();
  }

  render() {
    // 유저 리듀서
    const { userReducer } = this.store.getState();
    // 키와 객체 데이터
    const { keys, objs } = userReducer;
    // 키에 맞는 유저 데이터
    const userData = keys.map((key) => objs[key]);
    // 프로바이더를 사용하여 자식에게 데이터 전달
    return (
      <Provider store={this.store}>
        {userData.map((item) => (
          <React.Fragment key={item.key}>
            <Text>ID : {item.uid}</Text>
            <br />
            <Text>PASS : {item.pass}</Text>
            <br />
            <Text>NAME : {item.name}</Text>
            <br />
            <Button
              name={item.uid}
              onClick={() =>
                this.editUser(item.key, "넌 이미 죽어 있다_" + item.uid)
              }
            >
              수정
            </Button>
            <br />
          </React.Fragment>
        ))}
        <br />
        <Button name="add" onClick={this.addUser}>
          추가
        </Button>
      </Provider>
    );
  }
}

export default UserDataRedux;

 

 코드가 길어 보이지만 지난 시간에 했던 내용과 크게 다르지 않습니다. 중요한 부분은 위에서 만든 setUser, addUser, editUser 액션을 사용하는 부분과 render에서 userReducer를 뽑아서 키와 매칭 하여 데이터를 찾는 부분입니다. 특히 editUser에서 키를 찾아 데이터를 수정하는 곳 또한 중요합니다. 그럼 스토리북에 추가해서 확인해보시죠~!

 

초기 유저 데이터 세팅

 

데이터 추가 및 수정

 

 보이시는 것 처럼 데이터 세팅, 추가, 수정 모두 잘 동작하고 있습니다. 화면 오른쪽 디버깅 툴에서도 keys 데이터와 objs 데이터가 잘 확인됩니다. 오늘 배운 내용은 실무에서 정말 중요합니다. 왜냐하면 덕스는 주로 서버와의 데이터 동기화에 사용되는데, 대부분의 데이터는 배열이나 객체 형태로 사용되기 때문입니다. 그럼 오늘은 여기까지~!

 

참 쉽지는 않죠?


2020/10/27 - [Web프론트엔드/리액트] - [React] 리덕스(redux) 기능으로 데이터 관리하기 ⑤ 커넥트

댓글