TIL

241230 TIL

GoJay 2024. 12. 30. 22:54
  • Udemy <The Web Developer 부트캠프 2024>
    • MongoDB는 NoSQL이지만, SQL에서 테이블 간 관계를 맺고, join으로 필요한 데이터를 조합해 사용하는 것과 유사한 기능이 제공된다.
    • MongoDB에서 관계의 형성은 도큐먼트에 필드를 추가해서 맺어진다. SQL에서 테이블에 column을 추가해서 추가된 열에 관계가 맺어질 테이블의 정보를 저장하는 것과 유사하다.
    • 도큐먼트에 관계를 위한 필드를 추가할 땐 스키마에 { type: Schema.Types.ObjectId, ref: 'User' } 형태로 사용할 수 있다. 여기서 ref에 정의된User 값 위치에는 관계를 형성하고 싶은 콜렉션의 이름을 넣어주면 된다.
    • type에 사용된 Schema.Types.ObjectId는 관계를 형성할 콜렉션에 있는 도큐먼트들이 만들어질 때 Mongo에서 자동으로 부여한 _id 값을 의미한다. 즉, type: Schema.Types.ObjectId와 같이 좁혀주면 관계 형성을 위해 다른 필드가 아닌 '관계를 형성하고 싶은 콜렉션의 도큐먼트 아이디'만을 값으로 들고 있게 되는 것이다.
    • 이렇게 정의하고, 해당 도큐먼트에 새로운 값을 추가할 땐 관계 형성을 위한 필드에 원하는 도큐먼트를 추가해 주면 된다.
    • 예를 들어, Comment 모델의 스키마가 { /* ... 다른 필드 정의들 */, user: { type: Schema.Types.ObjectId, ref: 'User' }} 형태로 정의됐다면, new Comment({ /* ... 추가할 값들 */, user}) 형태로 user 도큐먼트를 값으로 넣어주면 된다(user 변수에 원하는 User 모델의 도큐먼트가 저장되어 있었어야 한다).
    • 위와 같이 필드를 추가해 주면 User 모델의 원하는 도큐먼트의 _id 필드의 값이 Comment 모델 도큐먼트의 user 필드에 추가되게 된다. User 도큐먼트에 어떤 필드가 몇 개나 있든 상관없이, Schema.Types.ObjectId로 타입을 정의했기 때문에 _id만 값으로 저장된다.
    • 나머지 값들은 필요시 관계형 데이터베이스의 join과 유사하게, _id 값을 기반으로 원하는 도큐먼트의 필드를 가져와 사용할 수 있다.
    • 이때 사용되는 메서드가 populate다. populate는 단어 뜻 그대로 관계가 맺어진 모델의 도큐먼트에서 원하는 값을 '채워 넣는' 역할을 한다.
    • populate는 Mongo의 명령어로 원하는 데이터를 조회할 때, 조회된 객체의 메서드로 포함되어 있다. 예를 들어, await Comment.find({}).populate('user')와 같은 형식으로 사용하면, 조회한 모든 코멘트 도큐먼트들 중 user 필드가 있을 경우에 한해서 연결된 User 모델의 도큐먼트의 모든 필드 값을 가져와 함께 조회해 준다.
    • 만약에 User 모델의 도큐먼트에서 모든 필드를 가져오는 게 아니라 일부 필드만 가져오고 싶다면 populate 메서드의 두 번째 매개변수로 원하는 필드 정보를 전달해 주면 된다.
    • 이때, title date author와 같이 두 개 이상의 필드를 선택해서 가져오고 싶으면 하나의 문자열에 띄어쓰기로 구분해서 필드 이름을 나열해 주면 된다(매개변수는 결국 하나이고, 구분자가 띄어쓰기다).
    • 만약에 특정 필드를 빼고 합치고 싶다면 ~title과 같은 형식으로 사용해 주면 된다. ~title ~date ~author처럼 역시 띄어쓰기로 구분해 주면 가져오고 싶지 않은 여러 필드 정보를 전달할 수도 있다.
    • 두 모델을 관계를 형성할 때 어디에 어떤 식으로 연결을 할지는 자유도가 있는 판단의 영역이다. 관계를 단방향으로 엮어서 관리할 수도 있고, 양쪽 모델 모두에 양방향으로 관계를 맺어줘서 관리해 주는 것도 가능하다.
    • NoSQL은 SQL과 달리 자유도가 높게 설계 가능하다는 특징이 있으며, 관계를 어디에 어떤 식으로 정의할지는 그러한 자유도의 영역으로 남겨진 부분 중 하나이다. 좀 더 확장성이 좋고 관리하기 편한 방식으로 그때그때 상황에 맞게 선택하면 되는데, 어떤 상황에서 뭐가 좋은지는 여러 케이스를 경험해 보면서 직접 답을 찾아가는 게 필요하다.
    • 두 모델의 관계를 맺어줬다면, 한쪽 모델의 도큐먼트를 삭제할 때 관계로 연결된 다른 모델의 값을 어떻게 할지도 이슈가 된다. 이 또한 NoSQL 데이터베이스 설계 시 자유도의 영역으로 남아있는 부분이다.
    • 만약에 관계가 연결된 다른 쪽 모델의 도큐먼트가 다른 도큐먼트하고도 다대다로 연결된 상황이라면 한 쪽 모델의 도큐먼트를 지운다고 다른쪽 도큐먼트를 함께 지워주는 게 부자연스러울 수 있다. 하지만, 도큐먼트가 서로만을 참조하고 있는 상황이고, 참조된 상대 쪽 도큐먼트가 삭제됐을 때 관계로 연결된 다른 도큐먼트의 의미가 사라지는 상황이라면 데이터베이스에서 함께 지워주는 게 논리 상 좀 더 좋다.
    • 이런 상황에서 Mongoose에서 제공하는 미들웨어를 사용할 수 있다. Mongoose는 Express와 같이 어떠한 작업(요청-응답) 처리의 전-후에 필요한 작업을 추가할 수 있도록 미들웨어를 제공한다.
    • Mongoose의 미들웨어는 스키마에 pre 또는 post 메서드를 호출해서 정의할 수 있다. Schema.post('작업 내용', '작업 내용 후 수행할 콜백 함수') 형태로 사용할 수 있다.
    • MongoDB에 저장된 도큐먼트의 필드가 배열일 경우, $pull 메서드를 사용할 수 있다. Campground.findByIdAndUpdate(campId, { $pull: { reviews: reviewId } }) 형태로 사용 가능하고, campId에 해당하는 도큐먼트를 찾아서 타입이 배열인 reviews 필드에서 값이 reviewId인 경우를 찾아 배열에서 제거해 준다.

'TIL' 카테고리의 다른 글

250101  (0) 2025.01.01
241225 TIL  (0) 2024.12.26
241223 TIL  (0) 2024.12.25
241222 TIL  (0) 2024.12.23
241221 TIL  (0) 2024.12.21