티스토리 뷰

728x90
반응형

목차

     

    NoSQL

     

    NoSQL은 Not only SQL의 약자이며, 기존의 관계형 데이터베이스에서 벗어난 저장방식을 가리킨다.

     

    이에 관한 더 자세한 설명 및 SQL과의 비교는 예전에 정리한 적이 있으므로, 해당 게시글로 대체한다.

     

    [데이터베이스]SQL vs. NoSQL

     

    [데이터베이스]SQL vs. NoSQL

    NoSQL는 Not only SQL의 약자로, SQL만을 사용하지 않는 DBMS(DataBase Management System)을 말한다. 관계형 데이터베이스를 주로 사용하는 SQL과 달리 여러 유형의 데이터베이스를 사용하는 넓은 범위라고 보

    gnidinger.tistory.com

    이 글에서는 Node.js, TypeScript, 그리고 mongoose를 사용하는 환경을 기반으로

     

    mongoDB의 1:N, N:M 관계를 구현하는 법과 그 장단점에 대해 짧게 알아보겠다.

     

    몽고디비에 대한 간단한 설명 역시 다른 글에 정리한 적이 있어서 그것으로 대체하겠다.

     

    [Database]mongoDB 튜토리얼

     

    [Database]mongoDB 튜토리얼

    목차 개인 프로젝트를 진행하면서 이름만 많이 듣던 Reactive mongo DB를 사용하고 있다. 잘 모르는 채로 더듬더듬 쓰면서 경험치가 쌓이다 보니, 이제야 전반적인 개념이 궁금해져서 정리 시작. Mongo

    gnidinger.tistory.com

    들어가기 전에 선요약을 하자면, 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'를 사용한 참조가 이루어지며,

     

    여기엔 몇 가지 장점이 있다.

     

    1. 인덱싱: mongoDB는 기본적으로 '_id' 필드에 대해 인덱싱을 수행하기 때문에 쿼리 성능이 좋다.
    2. 유일성: '_id'는 데이터베이스 전체에서 유일하기 때문에 잘못 가리킬 위험이 없다.
    3. 메서드
      mongoose에서는 '_id'를 이용한 쿼리 메서드 'Model.findById', 'Model.findByIdAndUpdate',
      'Model.findByIdAndDelete' 등을 지원한다. 이는 '_id'를 기반으로 해당 도큐먼트를 쉽게 조회, 수정, 삭제할 수 있게 해 준다.
    4. Population
      mongoose는 '_id'를 사용하는 참조에 대해 'populate' 메서드를 제공한다. 이는 참조하는 문서의 '_id'를
      해당 도큐먼트의 실제 데이터로 채워 넣는 기능을 하기 때문에 특정 도큐먼트와 관련된 다른 문서의 데이터를 쉽게 불러올 수 있게 된다.
      이는 SQL에서의 JOIN 연산과 유사한 기능을 한다.

    계속해서 참조 방식 자체의 장단점을 나열하면 다음과 같다.

     

    1. 장점

      1. 데이터 중복 최소화: 데이터 정규화를 통해 중복을 방지한다.
      2. 데이터 일관성: 연결된 데이터의 변경이 독립적이기 때문에, 데이터 수정이 필요할 경우 해당 도큐먼트만 수정하면 된다.
      3. 도큐먼트의 정보가 자주 변경되면서 그 양이 상대적으로 많을 때, 그리고 연관 데이터를 함께 조회할 필요가 적을 때 유리하다.
      4. 모델 간의 관계를 직관적으로 표현할 수 있다.
    2. 단점

      1. 추가적인 쿼리가 필요하다. 연관 정보를 얻기 위해서는 '_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 도큐먼트 안에 직접 저장한다.

     

    계속해서 장단점을 정리하자.

     

    1. 장점

      1. 한 번의 쿼리로 Feed와 관련된 모든 Comment를 불러올 수 있어 쿼리 성능이 좋다.
      2. 데이터의 변경이 적고 관련된 데이터를 함께 읽어와야 할 때 유리하다.
    2. 단점

      1. 데이터 중복 발생: 하나의 Comment를 여러 곳에서 사용하는 경우 데이터의 중복이 발생한다.
      2. 데이터 수정 복잡도 증가: Comment의 내용이 바뀔 경우 모든 곳에서 직접 내용을 변경해주어야 한다.
      3. 도큐먼트 크기 증가: 데이터의 양에 따라 도큐먼트의 크기가 커질 수 있으며, 그 상한선이 존재하는 디비에선 주의해야 한다.

     

    Summary

     

    방식 참조(Reference) 임베드(Embed)
    장점 데이터 중복 최소화
    데이터 일관성 보장 - 한 번의 수정으로 모든 곳에 적용
    도큐먼트의 수정이 잦고 양이 많은 경우 유리
    한 번의 쿼리로 모든 정보를 불러올 수 있어 읽기 성능이 좋음
    데이터의 변경이 적고 관련 데이터를 함께 읽는 경우 유리
    단점 추가 쿼리가 필요함 데이터 중복 발생
    데이터 수정 복잡도 증가
    도큐먼트 크기 증가

    이와 같이 어느 것 하나가 절대적으로 좋다고 할 수는 없으며,

     

    도큐먼트와 앱의 특성, 그리고 로직과 구현의 요구사항에 따라 잘 선택해서 사용해야 한다.

    반응형

    'Development > Database' 카테고리의 다른 글

    [Database]postgreSQL  (1) 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
    링크
    «   2024/05   »
    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
    글 보관함