# 3-7-정규화된-데이터-업데이트

앞의 블로그 예제를 다시 사용하자. 먼저, 포스트에 새 댓글을 추가하는 예시를 보자.

## 일반적인 접근

### 간단한 병합

기존 상태 액션의 컨텐츠에 병합 하는 것이다. 이 경우에 얕은 복사가 아닌 깊은 재귀병합을 해야한다. Lodash의 `merge`함수는 이를 처리할 수 있다.

```javascript
import merge from "lodash/merge";

function commentsById(state = {}, action) {
    switch(action.type) {
        default : {
           if(action.entities && action.entities.comments) {
               return merge({}, state, action.entities.comments.byId);
           }
           return state;
        }
    }
}
```

이케하면 리듀서는 최소 작업량을 필요로한다. 하지만 action creator는 액션을 디스패치 하기 전에 적당한 형태로 구성하기 위해 2배의 작업을 필요로 한다.

### 슬라이스 리듀서 구성

슬라이스 리듀서가 중첩된 트리가 있다면 각 슬라이스 리듀서는 액션에 적절히 응답하는 법을 알고 있어야 한다. 일단 포스트 새 댓글을 추가하는 예시를 계속 사용한다고 하자

* 해당 포스트 객체에 댓글 ID추가
* 해당 키를 ID로 하는 새로운 댓글 객체 추가
* 전체 댓글 ID 리스트에 댓글의 ID 추가

이 조각들을 맞춰보자

```javascript
// actions.js

function addComment(postId, commentText) {
  const commentId = generateId("comment");

  return {
    type: 'ADD_COMMENT',
    payload: {
      postId,
      commentId,
      commentText
    }
  }
}

// reducers/post.js

// 해당 post 객체에 댓글 id 추가하기
function addComment(state, action) {
  const {payload} = action;
  const {postId, commentId} = payload;

  const post = state[postId];

  return {
    ...state,
    [postId]: {
      ...post,
      comments: post.comments.concat(commentId)
    }
  }
}

function postsById(state = {}, action) {
  switch(action.type) {
    case "ADD_COMMENT": return addComment(state, action);
    default: return state;
  }
}

function allPosts(state = [], action) {

}

const postsReducer = combineReducers({
  byId: postsById,
  allIds: allPosts
})
```

```javascript
// reducers/comment.js

function addCommentEntry(state, action) {
  const {payload} = action;
  const {commentId, commentText} = payload;

  const comment = {id: commentId, text: commentText};

  return {
    ...state,
    [commentId]: comment
  }
}

function commentsById(state = {}, action) {
  switch(action.type) {
    case "ADD_COMMENT": return addCommentEntry(state, action);
    default: return state;
  }
}

function addCommentId(state, action) {
    const {payload} = action;
    const {commentId} = payload;
    // 새 댓글 ID를 전체 ID 리스트에 추가
    return state.concat(commentId);
}

function allComments(state = [], action) {
    switch(action.type) {
        case "ADD_COMMENT" : return addCommentId(state, action);
        default : return state;
    }
}

const commentsReducer = combineReducers({
    byId : commentsById,
    allIds : allComments
});
```

이 예제는 슬라이스 리듀서와 케이스 리듀서를 어떻게 맞추고 있는지 보여주고 있다. 여기에서 위임에 유의하자. `postsById` 슬라이스 리듀서는 새로운 댓글 ID를 적절한 포스트에 삽입하는 작업을 `addComment`에 위임한다. 반면 commentsById와 allComments 슬라이스 리듀서는 댓글 룩업테이블과 전체 댓글 ID를 적절히 업데이트하는 자신의 케이스 리듀서를 가지고 있다.
