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
인 경우를 찾아 배열에서 제거해 준다.
- MongoDB는 NoSQL이지만, SQL에서 테이블 간 관계를 맺고,