풀스택 프로젝트 개발을 위해 Prisma ORM과 Database로 MongoDB를 채택하여 사용했다.
Book 모델에서 랜덤한 데이터를 받아오기 위해 조사를 하였다.
21년 2월 prisma 깃헙 질문으로 올라온 글을 살펴보았다. (링크) 해당 글에선 다양한 답변이 있었기에 몇 개 살펴보자.
const productsCount = await prisma.product.count();
const skip = Math.floor(Math.random() * productsCount);
return await prisma.product.findMany({
take: 5,
skip: skip,
orderBy: {
sellingCount: 'desc',
},
});
skip을 이용해서 데이터들을 랜덤으로 스킵하면서 5개의 데이터를 받는 코드이다. 이 코드의 문제는 스킵이 마지막에 다다르면 예상치 않게 5개의 데이터를 받아오지 못할 것이다. 이것을 보완한 코드가 있었지만 이 또한 성능면에서 좋지 않다.
또 다른 대안은, 원시 쿼리를 이용하는 것이다.
results = await prisma.$queryRawUnsafe(
// DO NOT pass in or accept user input here
`SELECT * FROM "Post" ORDER BY RANDOM() LIMIT 30;`,
)
하지만 이 코드는 MongoDB를 데이터베이스로 한다면 동작하지 않으며 원시 쿼리를 이용하는 것은 피하는 것이 좋다고 한다. 그래서 prisma 공식 문서를 뒤져 MongoDB에 관한 내용을 찾아보았다. (링크) MongoDB 버전 3.9.0 이상의 경우 Prisma Client는 원시 쿼리를 보낼 수 있는 세 가지 방법을 제공한다!! 그중 aggregateRaw는 MongoDB에서 제공하는 $sample(링크)을 사용할 수 있다. $sample은 선택할 수를 지정하면 해당하는 수(size)만큼 랜덤으로 record를 반환해 준다! 이걸 활용하면 되겠다 싶었다!
const books = await prisma.book.aggregateRaw({
pipeline: [
{ $match: { genreId: params.genreId } },
{ $sample: { size: 1 } },
],
});
$sample만 이용하면 문제없이 들어오는 것을 확인했지만 특정 장르에 해당하는 book 데이터를 받아오려니 빈 배열만 반환했다.. 에러도 없으니 뭐가 문제인가 싶었다. match문에 문제가 있다는 것을 인지했고, 확인해 본 걸과 Book은 genreId를 단순한 문자열 형태의 id를 들고 있는 것이 아닌 ObejectId("") 형태의 데이터를 들고 있었다는 점이다! (Genre와 Book 테이블은 일대다로 연결되어 있기 때문)
const books = await prisma.book.aggregateRaw({
pipeline: [
{ $match: { genreId: { $oid: params.genreId } } },
{ $sample: { size: 1 } },
],
});
드디어!! 특정 장르의 책 데이터를 랜덤으로 받아오는 api를 만들 수 있었다!
랜덤으로 데이터를 받아오는 것은 프로젝트 시작 전부터 조사했던 문제였기에 많은 시간을 쏟긴 했지만 막상 부딪혀보니 생각보다 쉽게(?) 풀린 것 같다. 처음 사용해 보는 prisma, mongoDB지만 나름대로 잘 해결해나가고 있는 것 같다.