June 8th 2019
대부분 웹 application의 데이터는 중첩되어 있거나, 상관관계를 가지고 있습니다. 예로, 블로그 포스트를 들 수 있습니다. 블로그에는 많은 포스트가 있을 수 있고, 각 포스트는 다수의 코멘트가 달릴 수 있습니다. 그리고 포스트와 코멘트는 유저에 의해 작성됩니다. 이러한 서비스의 데이터는 아래와 같을 수 있습니다.
const blogPosts = [
{
id: 'post1',
author: { username: 'user1', name: 'User 1' },
body: '......',
comments: [
{
id: 'comment1',
author: { username: 'user2', name: 'User 2' },
comment: '.....'
},
{
id: 'comment2',
author: { username: 'user3', name: 'User 3' },
comment: '.....'
}
]
},
{
id: 'post2',
author: { username: 'user2', name: 'User 2' },
body: '......',
comments: [
{
id: 'comment3',
author: { username: 'user3', name: 'User 3' },
comment: '.....'
},
{
id: 'comment4',
author: { username: 'user1', name: 'User 1' },
comment: '.....'
},
{
id: 'comment5',
author: { username: 'user3', name: 'User 3' },
comment: '.....'
}
]
}
// and repeat many times
];
데이터의 구조가 복잡하고, 일부 데이터는 중복되어 있습니다. 이러한 구조는 아래와 같은 이유로 문제가 될 수 있습니다.
따라서, 데이터를 다룰 때 전달 받은 데이터를 데이터베이스로 간주하고, 그 데이터를 정규화된 구조로 보관하는 것이 추천됩니다.
데이터 정규화의 기본 컨셉은 아래와 같습니다.
위의 데이터를 정규화 하면 아래와 같습니다.
{
posts : {
byId : {
"post1" : {
id : "post1",
author : "user1",
body : "......",
comments : ["comment1", "comment2"]
},
"post2" : {
id : "post2",
author : "user2",
body : "......",
comments : ["comment3", "comment4", "comment5"]
}
},
allIds : ["post1", "post2"]
},
comments : {
byId : {
"comment1" : {
id : "comment1",
author : "user2",
comment : ".....",
},
"comment2" : {
id : "comment2",
author : "user3",
comment : ".....",
},
"comment3" : {
id : "comment3",
author : "user3",
comment : ".....",
},
"comment4" : {
id : "comment4",
author : "user1",
comment : ".....",
},
"comment5" : {
id : "comment5",
author : "user3",
comment : ".....",
},
},
allIds : ["comment1", "comment2", "comment3", "commment4", "comment5"]
},
users : {
byId : {
"user1" : {
username : "user1",
name : "User 1",
},
"user2" : {
username : "user2",
name : "User 2",
},
"user3" : {
username : "user3",
name : "User 3",
}
},
allIds : ["user1", "user2", "user3"]
}
}
위의 자료구조가 raw data에 비해 훨씬 flatter 합니다. 이러한 방식은 아래와 같은 장점을 가져옵니다.
반면, raw 데이터에서 comment 수정을 위해서는, comment 객체, 부모 post 객체, 전체 posts의 배열이 필요하게 되며, 이는 Post components, Comment component의 리렌더를 초래할 수 있습니다.
일반적인 application은 관계형 데이터와 비관계형 데이터가 혼재해 있습니다. 다른 데이터 타입을 정리하는 특별한 룰은 없지만, 일반적인 패턴은 있습니다. 그 중 하나는 관계형 table을 common parent key 아래에 두는 것입니다. 아래는 이 패턴의 예시입니다.
{
simpleDomainData1: {....},
simpleDomainData2: {....},
entities : {
entityType1 : {....},
entityType2 : {....}
},
ui : {
uiSection1 : {....},
uiSection2 : {....}
}
}
entities에 수정을 많이 하는 application의 경우, state에 두 개의 'table'을 둘 수 있습니다. 만약 item이 수정된다면, 그 값을 work-in-progress로 이동 시킵니다. 그리고 아이템이 수정되면, 수정된 값으로 원본 데이터를 교체하는 것입니다.
Redux store의 부분을 데이터베이스의 일부로 이용하기 때문에, 우리는 database design을 적용할 수 있습니다. 예를 들어, many-to-many relationship이라고 한다면, 우리는 intermediate table(a.k.a joint table, associative table)을 사용할 수 있습니다. 일관되게, byId와 allIds 방식으로 접근할 수 있습니다.
{
entities: {
authors : { byId : {}, allIds : [] },
books : { byId : {}, allIds : [] },
authorBook : {
byId : {
1 : {
id : 1,
authorId : 5,
bookId : 22
},
2 : {
id : 2,
authorId : 5,
bookId : 15,
},
3 : {
id : 3,
authorId : 42,
bookId : 12
}
},
allIds : [1, 2, 3]
}
}
}
저자별 책을 찾는 작업을 한다면, joint table에서 single loop으로 쉽게 처리할 수 있습니다.