티스토리 뷰
목차
NoSQL
NoSQL은 Not only SQL의 약자이며, 기존의 관계형 데이터베이스에서 벗어난 저장방식을 가리킨다.
이에 관한 더 자세한 설명 및 SQL과의 비교는 예전에 정리한 적이 있으므로, 해당 게시글로 대체한다.
이 글에서는 Node.js, TypeScript, 그리고 mongoose를 사용하는 환경을 기반으로
mongoDB의 1:N, N:M 관계를 구현하는 법과 그 장단점에 대해 짧게 알아보겠다.
몽고디비에 대한 간단한 설명 역시 다른 글에 정리한 적이 있어서 그것으로 대체하겠다.
들어가기 전에 선요약을 하자면, NoSQL에서 1:N, 그리고 N:N을 구현하는 방식은 크게 두 가지로 나뉜다.
그 방식이란 각각 참조(Reference)와 임베드(Embedded)이며, 자세한 설명은 이제부터 시작해 보자.
Reference
참조 방식은 SQL에서 사용하는 방식인 외래 키(foreign key) 개념과 비슷하게,
한 도큐먼트가 다른 도큐먼트의 식별자를 저장하는 방식이다. 예를 들어 게시글(Feed)과 댓글(Comment)
사이에 1:N 관계가 형성된다면 각 댓글 도큐먼트에 해당 댓글이 달린 게시글의 식별자(feedSeq 등)를 저장해서
댓글을 조회할 때 해당 댓글이 달린 게시글의 정보를 함께 가져올 수 있다.
먼저 1:N 관계를 코드로 예를 들자면 아래와 같다.
import mongoose, { Document, Schema } from 'mongoose';
// 1. Comment Document에 대한 TypeScript 타입 정의
interface IComment extends Document {
feed: Schema.Types.ObjectId;
content: string;
}
// 2. Comment에 대한 MongoDB 스키마 정의
const CommentSchema = new Schema({
feed: {
type: Schema.Types.ObjectId,
ref: 'Feed'
},
content: String
});
// 3. Comment 모델 생성
const Comment = mongoose.model<IComment>('Comment', CommentSchema);
export default Comment;
각 코드의 역할은 주석에 들어있다.
계속해서 N:M의 경우엔 대략 아래와 같이 구현한다. 이 경우엔 학생과 수강과목의 관계이다.
interface IStudent extends Document {
name: string;
subjectIds: string[]; // 참조되는 과목 '_id'의 배열
}
interface ISubject extends Document {
name: string;
studentIds: string[]; // 참조되는 학생 '_id'의 배열
}
주의할 점은 이 경우, 별도의 식별자를 사용하고 있어도 몽고디비의 '_id'를 사용한 참조가 이루어지며,
여기엔 몇 가지 장점이 있다.
- 인덱싱: mongoDB는 기본적으로 '_id' 필드에 대해 인덱싱을 수행하기 때문에 쿼리 성능이 좋다.
- 유일성: '_id'는 데이터베이스 전체에서 유일하기 때문에 잘못 가리킬 위험이 없다.
- 메서드
mongoose에서는 '_id'를 이용한 쿼리 메서드 'Model.findById', 'Model.findByIdAndUpdate',
'Model.findByIdAndDelete' 등을 지원한다. 이는 '_id'를 기반으로 해당 도큐먼트를 쉽게 조회, 수정, 삭제할 수 있게 해 준다. - Population
mongoose는 '_id'를 사용하는 참조에 대해 'populate' 메서드를 제공한다. 이는 참조하는 문서의 '_id'를
해당 도큐먼트의 실제 데이터로 채워 넣는 기능을 하기 때문에 특정 도큐먼트와 관련된 다른 문서의 데이터를 쉽게 불러올 수 있게 된다.
이는 SQL에서의 JOIN 연산과 유사한 기능을 한다.
계속해서 참조 방식 자체의 장단점을 나열하면 다음과 같다.
- 장점
- 데이터 중복 최소화: 데이터 정규화를 통해 중복을 방지한다.
- 데이터 일관성: 연결된 데이터의 변경이 독립적이기 때문에, 데이터 수정이 필요할 경우 해당 도큐먼트만 수정하면 된다.
- 도큐먼트의 정보가 자주 변경되면서 그 양이 상대적으로 많을 때, 그리고 연관 데이터를 함께 조회할 필요가 적을 때 유리하다.
- 모델 간의 관계를 직관적으로 표현할 수 있다.
- 단점
- 추가적인 쿼리가 필요하다. 연관 정보를 얻기 위해서는 '_id'를 가져와 추가 쿼리를 이용해 실제 데이터를 가져와야 한다.
Embed
임베드 방식은 참조 방식과 저장 방식이 반대다.
어떤 식으로 반대냐 하면, 이 방식에선 1:N 중 1의 모델에 N에 해당하는 도큐먼트를 배열에 직접 저장하게 된다.
코멘트 모델 인터페이스와 스키마 구조를 생략하고 바로 임베드 방식을 보면 대략 아래와 같다.
import mongoose, { Document, Schema } from 'mongoose';
import CommentSchema from './Comment';
interface IFeed extends Document {
feedSeq: number;
content: string;
comments: [IFeed];
}
const FeedSchema = new Schema({
feedSeq: Number,
content: String,
comments: [CommentSchema]
});
const Feed = mongoose.model<IFeed>('Feed', FeedSchema);
export default Feed;
여기서 comments 항목이 'CommentSchema'의 배열이 된다.
계속해서 위와 같이 N:M을 구현하면 아래와 같은 모양이 된다.
interface IStudent extends Document {
name: string;
subjects: ISubject[]; // 임베드 된 과목 도큐먼트의 배열
}
interface ISubject extends Document {
name: string;
students: IStudent[]; // 임베드 된 학생 도큐먼트의 배열
}
이런 식으로 임베드 방식은 연관된 Comment 도큐먼트를 Feed 도큐먼트 안에 직접 저장한다.
계속해서 장단점을 정리하자.
- 장점
- 한 번의 쿼리로 Feed와 관련된 모든 Comment를 불러올 수 있어 쿼리 성능이 좋다.
- 데이터의 변경이 적고 관련된 데이터를 함께 읽어와야 할 때 유리하다.
- 단점
- 데이터 중복 발생: 하나의 Comment를 여러 곳에서 사용하는 경우 데이터의 중복이 발생한다.
- 데이터 수정 복잡도 증가: Comment의 내용이 바뀔 경우 모든 곳에서 직접 내용을 변경해주어야 한다.
- 도큐먼트 크기 증가: 데이터의 양에 따라 도큐먼트의 크기가 커질 수 있으며, 그 상한선이 존재하는 디비에선 주의해야 한다.
Summary
방식 | 참조(Reference) | 임베드(Embed) |
장점 | 데이터 중복 최소화 데이터 일관성 보장 - 한 번의 수정으로 모든 곳에 적용 도큐먼트의 수정이 잦고 양이 많은 경우 유리 |
한 번의 쿼리로 모든 정보를 불러올 수 있어 읽기 성능이 좋음 데이터의 변경이 적고 관련 데이터를 함께 읽는 경우 유리 |
단점 | 추가 쿼리가 필요함 | 데이터 중복 발생 데이터 수정 복잡도 증가 도큐먼트 크기 증가 |
이와 같이 어느 것 하나가 절대적으로 좋다고 할 수는 없으며,
도큐먼트와 앱의 특성, 그리고 로직과 구현의 요구사항에 따라 잘 선택해서 사용해야 한다.
'Development > Database' 카테고리의 다른 글
[Database]postgreSQL (0) | 2023.10.18 |
---|---|
[DB]Vector DB (0) | 2023.09.19 |
[Database]mongoDB의 '_id', 또는 auto-increment ID (0) | 2023.07.03 |
[Database]MariaDB (0) | 2023.05.10 |
[Database]WebFlux에서 R2DBC 기본설정 (0) | 2023.05.06 |
[Database]SQLite (0) | 2023.04.07 |
- Total
- Today
- Yesterday
- 동적계획법
- BOJ
- Algorithm
- 리스트
- 면접 준비
- 스프링
- 남미
- java
- 지지
- 칼이사
- 기술면접
- 파이썬
- a6000
- Backjoon
- 여행
- 자바
- 맛집
- 유럽
- 유럽여행
- 스트림
- 알고리즘
- 백준
- 세모
- RX100M5
- 야경
- 세계일주
- Python
- 세계여행
- 중남미
- spring
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |