Q. 몽고DB로 테이블 설계를 해봅시다. 회원가입을 한 유저가 게시판에 글을 쓰는 서비스입니다. 게시판 목록 페이지에서는 게시글 제목, 작성자 이름 등이 보이겠죠? 각각의 모델은 어떤 모양새이면 좋을까요? 게시판 글 리스트를 불러오는 api 에서는 몽구스 데이터를 어떻게 가져오면 좋을까요?
위의 질문에 답하기 위해서, 우선 mongodb와 같은 비관계형 데이터베이스 설계에 대해 알아보자.
비관계형 데이터베이스에서 설계란?
비관계형 데이터베이스에서 데이터를 저장하는 최소 단위를 Document라고 부르며, 이러한 document의 구조를 크게 embedded 방식과 references(세부적으로 자식 참조, 부모 참조, 상호 참조) 방식으로 나눈다.
Embedded 방식
관계를 갖는 데이터 집합을 단일 document로 관리하는 방식으로, 구조를 단순화하는 반정규화 모델.
장점
- 단순한 구조
- 조회성능이 좋고, 한 document에 모든 정보가 담겨 있어, 한번에 모두 업데이트 할 수 있음
- 데이터 관리가 직관적이며, 쿼리가 단순
단점
- 데이터의 중복은 데이터 불일치를 발생시킬 수 있음
- 데이터가 증가할 수록, document 크기가 증가하여 디스크 I/O시 성능 저하가 발생하거나, document 최대 크기를 초과하면, 저장이 불가능할 수 있음
- 데이터 관계가 복잡하거나 계층구조를 갖는 경우, 관리가 어려움
결론
- 조회 성능이 중요하고, 중복 데이터 때문에 불일치 문제가 없는 경우
- 업데이트가 과도하게 발생하지 않는 경우
References 방식
Reference 방식은 참조하는 document에 관계를 갖는 다른 document의 식별자를 참조 키로 저장하여 관리하는 정규화 모델
장점
- document를 성격에 따라 분류하여 저장 후 참조하므로, 데이터 중복으로 인한 불일치 문제가 없음
- embedded 방식 대비 document의 크기 증가가 작음
- 새로운 요건 추가가 document 구조 변경에 미치는 영향이 작음
단점
- 참조가 많거나, 대규모 document를 조회하는 경우, 앱에서 2차 쿼리($lookup, populate 등)로 인한 처리량 증가로 조회 성능이 저하될 수 있음
- 참조 정보를 정확하게 관리하지 않는 경우, 참조 정보 소실에 의한 데이터 정합성 문제가 발생할 수 있음
결론
- 업무 중요 요소가 조회 성능 < 데이터 무결성 인 경우 사용 권장
- embedded 방식응 사용하면 디스크 I/O 성능에 문제가 예상되는 경우 권장
- 데이터 관계가 복잡하거나, 계층 구조를 갖는 경우 권장
세부 References 방식
위에서 설명한 기본 참조 방식에서 참조키를 어떤 document에 저장할 것인지에 따라 세부 방식이 나뉨
설계
One-to-One : ObjectId당, 하나만 존재하는 값으로 1:1 관계를 가질 때 key-value로 모델링 가능 / embedded
{
"_id": "ObjectId('mdkalsfmk2')",
"nickname": "junseo",
"campus": "42seoul",
}
{
"_id": "ObjectId('sdkalsfmk3')",
"nickname": "Jisoo",
"campus": "42seoul",
}
One-toFew : project와 같이 ObjectId당 몇 개의 값을 가질 때, 배열 속 데이터 수가 많지 않으면 embedded 방식을 사용
{
"_id": "ObjectId('mdkalsfmk2')",
"nickname": "junseo",
"campus": "42seoul",
"projects": [
{ "name": "42WE", "isDone": false},
{ "name": "Decrypto", "isDone": true}
]
}
One-to-Many 자식참조 : User document에서 Card document의 id 여러개를 참조
// User document
{
"_id": "ObjectId('mdkalsfmk2')",
"nickname": "junseo",
"campus": "42seoul",
"projects": [
{ "name": "42WE", "isDone": false},
{ "name": "Decrypto", "isDone": true}
],
"creditCard": ["ObjectID('1234')", "ObjectID('4321')", "ObjectID('9399')"]
}
// Card document
{
"_id": "ObjectId('4321')",
"bank": "kakao",
"isActive": "true",
"accountNumber": "1234-56-7891234"
}
One-to-Squillions 부모참조 : Chat document에서 ChatRoom document의 id를 부모참조하고 있고, 자식인 Chat document에는 엄청나게 많은 데이터가 하나의 ChatRoom id를 참조
// ChatRoom
{
"_id": "ObjectId('4321')",
"createdAt": ISODate("2021-04-28T09:42:41.382Z"),
"users":"???",
}
//Chat[1]
{
"_id": "ObjectId('328492489')",
"chatRoom_id": "ObjectId('4321')", // 부모의 obj id 를 참조
"createdAt": ISODate("2021-04-29T12:42:41.382Z"),
"content":"피곤쓰",
}
//Chat[2]
{
"_id": "ObjectId('328492490')",
"chatRoom_id": "ObjectId('4321')", // 부모의 obj id 를 참조
"createdAt": ISODate("2021-04-29T12:42:59.382Z"),
"content":"나도 피곤쓰",
}
//Chat[3] ....... Chat[119876]
마무리
이렇게, 비관계형 데이터베이스의 설계방식에 대해 알아보았다.
이제, 처음에 나왔던 질문에 답을 해보도록 한다.
유저가 회원가입을 해서 사용하는 게시판은 유저 데이터의 무결성이 조회보다 중요하며, 유저-게시글-댓글의 계층구조 관계로 보여 Embedded보다는 References 방식이 적합해 보인다.
또, documents간 구성은 User document, Post document, Comment document로 구성을 할 것 같다.
이 후, User(부모) - Post(자식) / Post(부모) - Comment(자식) 사이에 One-to-Squillion 부모 참조로 설계를 할것 같다.
// User
{
"_id": "ObjectId('4321')",
"user": "thisIsId",
"password":"hash로 암호화된 문자열",
}
//Posts
{
"_id": "ObjectId('328492489')",
"user_id": "thisIsId", // 부모의 user 를 참조
"createdAt": ISODate("2021-04-29T12:42:41.382Z"),
"title":"게시판에 인사드립니다.",
"content":"피곤쓰",
}
//Comments
{
"_id": "ObjectId('586741')",
"post_id": "ObjectId('328492489')", // 부모의 obj id 를 참조
"user_id": "anotherId", // 작성자 id 저장
"createdAt": ISODate("2021-04-29T12:42:59.382Z"),
"content":"나도 피곤쓰",
}
결론.
Embedded 방식은 주로 information을 전달하려는 목적의 서비스가 적합해 보인다. 예를 들면, 회사 홈페이지나 학교의 black board 등과 같이 유저의 interaction이 적고, 데이터 업데이트가 잦지 않은 정적 서비스에서 많이 사용될 것 같다.
그 외의 많은 경우(주로 user interaction이 높은 경우)는 거의 References 방식으로 구현하지 않을까 생각된다.
'항해99_10기 > 105일의 TIL & WIL' 카테고리의 다른 글
[3주차] [20221201] Routing (1) | 2022.12.01 |
---|---|
[3주차] [20221201] IP 주소와 port, 그리고 DNS (0) | 2022.12.01 |
[3주차] [20221129] 에러 핸들링 (0) | 2022.11.29 |
[3주차] [20221128] REST API와 RESTful 아키텍처, HTTP PUT과 PATCH 차이점 (0) | 2022.11.28 |
[2주차 WIL] 2022.11.21 ~ 2022.1126 회고 (0) | 2022.11.27 |