์ฐพ์๋ณด๊ฒ ๋ ๊ณ๊ธฐ
ํ์ฌ ์งํํ๊ณ ์๋ ํ๋ก์ ํธ์์, ๋ฐฑ์๋์ ํ๋ก ํธ์๋๊ฐ์ ์์กด๋๋ฅผ ๋จ์ดํธ๋ฆฌ๊ณ ์ GraphQL์ ๋์ ํ๊ฒ ๋์๋ค
- GraphQL์ ์ฌ์ฉํ๋ฉด ํ๋ก ํธ์ธก์์ ์ํ๋ ๋ฐ์ดํฐ๋ฅผ ๊บผ๋ด๊ฐ๋ ํ์์ด๊ธฐ ๋๋ฌธ์, RestAPI์ฒ๋ผ ์ฌ๋ฌ ๊ฐ์ ์๋ํฌ์ธํธ๋ฅผ ๋ง๋ค์ง ์์๋ ๋๊ธฐ ๋๋ฌธ์ด๋ค.
- ๊ทธ๋ฐ๋ฐ GraphQL์ ์ฌ์ฉํ๋ฉด Resolver๊ฐ ์ฐ์์ ์ผ๋ก ๋์ํ๋ N + 1 ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ ๊ฒฝ์ฐ๊ฐ ๋น๋ฒํ๋ค๊ณ ํด์ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ์ฐพ๊ธฐ์ํด ์์๋ณด๊ฒ ๋์๋ค.
N + 1 ๋ฌธ์ ๋?
1๋ฒ์ ์ฟผ๋ฆฌ๋ก ๊ฐ์ ธ์ฌ ์ ์๋ ๋ฐ์ดํฐ์ ๋ํด์ ์ฐธ์กฐํ๋ ๋ฐ์ดํฐ N๊ฐ ๊ฐ๊ฐ์ ๋ํด ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ ค N + 1 ๋ฒ์ ์ฟผ๋ฆฌ ๊ฐ ๋ฐ์ํ๋ ๋ฌธ์
- ์๋ฅผ ๋ค์ด, ํ๋์ ๋ธ๋ก๊ทธ์ 3๊ฐ์ ๋๊ธ์ด ์์ฑ๋์ด ์๋ค๊ณ ํด๋ณด์.
- ์ด๋, ๋ธ๋ก๊ทธ์ ๋๊ธ์ ๋ํ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํด์๋ SQL์ Join์ ์ฌ์ฉํ๋ค๊ณ ์๊ฐํด๋ณด๋ฉด ๋ถ๋ช 1๋ฒ์ ์ฟผ๋ฆฌ๋ก ์ถฉ๋ถํ๋ค.
- ๊ทธ๋ฐ๋ฐ GraphQL์ Resolver๋ฅผ ์ฌ์ฉํ๊ฒ ๋๋ฉด, ๋ธ๋ก๊ทธ์ ๋ํ ๊ธ์ ๋จผ์ ๊ฐ์ ธ์ค๊ณ , ๋๊ธ ๊ฐ๊ฐ์ ๋ํด์ ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ ค์ ๋๊ธ์ ๋ํ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ ์ด 4๋ฒ์ ์ฟผ๋ฆฌ๊ฐ ๋ฐ์ํ๊ฒ ๋๋ค. ์ด๊ฒ์ด N + 1 ๋ฌธ์ ์ด๋ค.
- Spring์ JPA์์๋ ์ฆ์ ๋ก๋ฉ์ ํ ๊ฒฝ์ฐ์ ์ด ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค๊ณ ํ๋ค.
- ์ค์ ์งํ์ค์ธ ํ๋ก์ ํธ์์ ๋ก๊ทธ๋ฅผ ์ฐ์ด๋ณธ ๊ฒฐ๊ณผ ์ ๋ง ์ฌ๋ฌ๋ฒ์ ์ฟผ๋ฆฌ๊ฐ ๋ ์๊ฐ๋ ๊ฒ์ ํ์ธํ ์ ์์๋ค.
- ํ๋์ ์ํฐํฐ๊ฐ 5๊ฐ์ ์ํฐํฐ๋ฅผ ์ฐธ์กฐํ๊ณ ์๋ ์ํ์์
ํด๊ฒฐ ๋ฐฉ๋ฒ?
๊ทธ๋ ๋ค๋ฉด N + 1 ๋ฌธ์ ๋ฅผ ์ด๋ค ์์ผ๋ก ํด๊ฒฐํ ์ ์์๊น?
- ์ผ๋จ ํ์ฌ ์ฐ๋ฆฌ๋ ๋ชฝ๊ณ DB๋ฅผ ์ฌ์ฉํ๊ณ ์๊ธฐ ๋๋ฌธ์ ๋ชฝ๊ณ DB ์ฌ์ฉ์ ์ ์ ๋ก ์ฐพ์๋ณด์๋ค.
- ๊ทธ๋ ๊ฒ ํ์ ๋ ํฌ๊ฒ 2๊ฐ์ง ์ ๋์ ํด๊ฒฐ๋ฐฉ๋ฒ์ด ์๋ ค์ ธ ์์๋ค.
- Mongoose์ Populate ๋ฉ์๋ ์ฌ์ฉ
- Dataloader ์ฌ์ฉ
Dataloader ?
Dataloader๋ ๋ฌด์์ผ๊น?
๋ฐ์ดํฐ๋ก๋๋ ์ฌ๋ฌ ๊ฐ์ ์ฟผ๋ฆฌ๋ฅผ ๋ชจ์์ ํ ๋ฒ์ ์ฒ๋ฆฌํด์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค.
GraphQL์ ํ์ ๋ ๊ฐ๋
์ ์๋์ง๋ง, GraphQL์์ N + 1 ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋ง์ด ์ฌ์ฉ๋๋ค๊ณ ํ๋ค.
์ฟผ๋ฆฌ๋ฅผ ํ ๋ฒ์ ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์, ์ฟผ๋ฆฌ๋ฅผ ๋ชจ์๋๋ค๊ฐ $in [id1, id2, id3, id4]
์ด๋ ๊ฒ ์ฒ๋ฆฌํ๋ฉด ํ ๋ฒ์ ์ฟผ๋ฆฌ๋ก ์ฒ๋ฆฌํ ์ ์๋ค.
์ด๋ ๊ฒ ์ฒ๋ฆฌํ๋ ๋ฐฉ์์ ๋ฐฐ์น(batch)๋ผ๊ณ ํ๋ค.
๊ณ ๋ฏผํ๋ ๋ถ๋ถ
๊ทธ๋ ๊ฒ ์ฐ๋ฆฌ๋ N + 1 ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์ ๋ฐ์ดํฐ๋ก๋๋ฅผ ๋์ ํ๊ธฐ๋ก ๊ฒฐ์ ํ๋ค. ๊ทธ ๊ณผ์ ์์ ํท๊ฐ๋ ธ๋ ๋ถ๋ถ์ด ์๋ค.
๋๋ MongoDB๋ฅผ ์ฌ์ฉํ๋ ๊ฒ ์ฒ์์ด๋ผ, MongoDB์์๋ ์ฐธ์กฐํ๋ ๋ฐ์ดํฐ๋ฅผ ์ด๋ค ์์ผ๋ก ๋ถ๋ฌ์ค๋์ง ๊ถ๊ธํ๋ค.
MySQL์ ๊ฒฝ์ฐ์๋ Join์ผ๋ก ์ฐธ์กฐํ๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ์ ์์ง๋ง, MongoDB์์๋ ์ด๋ค ์์ผ๋ก ๋ถ๋ฌ์ค๋์ง๋ฅผ ๋ชฐ๋๋ค.
๋ชฝ๊ตฌ์ค์์ ์ ๊ณตํ๋ populate() ๋ฉ์๋๋ฅผ ์ฌ์ฉํด์ ์ฐธ์กฐํ๋ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ฌ ์ ์๋ค๊ณ ํ๋ค.๊ทธ๋ ๋ค๋ฉด, ๋ชฝ๊ตฌ์ค์ populate๋ฅผ ์ฌ์ฉํ๋ฉด ์ฐธ์กฐํ๋ ๋ชจ๋ ๋ถ๋ฌ์ฌ ์ ์๋๋ฐ, ๋ฐ์ดํฐ๋ก๋๋ ์ด๋์ ๋์ ํด์ผ ํ๋ ๊ฑธ๊น?
๊ตฌ๊ธ์ ๋์์๋ ๋๋ถ๋ถ์ ์์์๋ ๋ฐ์ดํฐ๋ก๋๋ Resolver๋จ์ ๋์ ํ๊ฒ ๋๋ค.
๊ทธ๋ฐ๋ฐ ์๋น์ค๋จ์์ populate() ๋ฉ์๋๋ฅผ ์ฌ์ฉํด์ ๋ฐ์ดํฐ๋ฅผ ๋ชจ๋ ๋ถ๋ฌ์จ ์ํ์ผํ ๋ฐ ๋ฐ์ดํฐ๋ก๋๋ ์ด๋์ ์ฐ์ด๋์ง๊ฐ ์๋ฌธ์ด์๋ค.
Mongoose Populate
๊ทธ ์ ์ Mongoose์ Populate๋ ์ด๋ค ๋ฉ์๋์ธ์ง ๋จผ์ ์์๋ณด์๋ค.
๋ชฝ๊ตฌ์ค ๊ณต์๋ฌธ์๋ฅผ ํตํด์ populate๋ฅผ ์ด๋ป๊ฒ ์ฌ์ฉํ๋์ง, ์ถ๊ฐ์ ์ธ ๊ธฐ๋ฅ์ ์ด๋ค ๊ฒ๋ค์ด ์๋์ง ์์๋ณด์๋ค.
Population
3.2 ์ด์ ๋ฒ์ ์์๋
$lookup
์ด๋ผ๋ operator๋ฅผ ์ฌ์ฉํด์ ์ฐธ์กฐํ๊ณ ์๋ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์์ง๋ง, 3.2 ์ดํ ๋ฒ์ ๋ถํฐ๋populate()
๋ฉ์๋๋ฅผ ์ฌ์ฉํด์ ์ฐธ์กฐํ๋ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ฌ ์ ์๊ฒ ๋์๋ค.
const mongoose = require('mongoose');
const {Schema } = mongoose;
const personSchema =Schema({
_id:Schema.Types.ObjectId,
name:String,
age:Number,
stories: [{ type:Schema.Types.ObjectId, ref: 'Story' }]
});
const storySchema =Schema({
author: { type:Schema.Types.ObjectId, ref: 'Person' },
title:String,
fans: [{ type:Schema.Types.ObjectId, ref: 'Person' }]
});
constStory = mongoose.model('Story', storySchema);
constPerson = mongoose.model('Person', personSchema);
- Person์ id๊ฐ์ ์ด์ฉํด์ Story๋ฅผ ์ฐธ์กฐ
- Story๋ id๊ฐ์ ์ด์ฉํด์ Person ๊ฐ์ฒด๋ฅผ author, fans๋ผ๋ ์ด๋ฆ์ ํ๋๋ก ์ฐธ์กฐ
์ฌ์ฉ๋ฒ
const story =awaitStory.
findOne({ title: 'Casino Royale' }).
populate('author').
exec();
// prints "The author is Ian Fleming"
console.log('The author is %s', story.author.name);
populate()
๋ฅผ ์ด์ฉํด์ Story์์ ์ฐธ์กฐํ๊ณ ์๋ author์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์จ๋ค.- title์ด 'Casino Royale'์ธ Story๋ฅผ author(Person) ์ ๋ณด์ ํจ๊ป ๊ฐ์ ธ์ค๊ฒ ๋๋ค.
Field Selection
์ฐธ์กฐํ๊ณ ์๋ ๋์์ ๋ชจ๋ ํ๋๋ฅผ ๊ฐ์ ธ์ฌ ํ์๊ฐ ์์ ๋, ์ํ๋ ํ๋๋ช ์ ์ง์ ํ ์ ์๋ค.
const story =awaitStory.
findOne({ title: /casino royale/i }).
populate('author', 'name').
exec();// only return the Persons name
// prints "The author is Ian Fleming"
console.log('The author is %s', story.author.name);
// prints "The authors age is null"
console.log('The authors age is %s', story.author.age);
- author์ ๋ํด์ name ์ ๋ณด๋ง ๊ฐ์ ธ์ค๊ฒ ๋ค๊ณ ๋ช ์
- ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ age๋ฅผ ์ถ๋ ฅํด๋ณด๋ฉด null๊ฐ์ด ๋ค์ด์๋ค.
Multiple Paths
Story์ ๊ฒฝ์ฐ์๋ author, fans ๋๊ฐ์ ํ๋์์ Person ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ๊ณ ์๋๋ฐ, ๋ง์ฝ ๋ ํ๋ ๋ชจ๋ ์ฐธ์กฐํ๊ณ ์ถ๋ค๋ฉด
populate()
๋ฅผ ์ฐ๋ฌ์์ ์ฐ๋ฉด ๋๋ค.
await Story.
find({/* ... */ }).
populate('fans').
populate('author').
exec();
- fans, author ๋ชจ๋ ์ฐธ์กฐํด์ ๊ฐ์ ธ์จ๋ค.
๋จ, ๊ฐ์ path์ ๋ํด์ ์ฌ๋ฌ๋ฒ ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ฆด ๊ฒฝ์ฐ, ๋ง์ง๋ง์ ์์ฒญํ ์ฟผ๋ฆฌ๋ง ์ธ์ ๋๋ค.
// The 2nd `populate()` call below overwrites the first because they// both populate 'fans'.awaitStory.
find().
populate({ path: 'fans', select: 'name' }).
populate({ path: 'fans', select: 'email' });
// The above is equivalent to:awaitStory.find().populate({ path: 'fans', select: 'email' });
- ์์ ์์์์ fans์ ๋ํด 2๋ฒ์ ์ฟผ๋ฆฌ๋ฅผ ์์ฒญํ๊ธฐ ๋๋ฌธ์, ์ดํ์ ์์ฒญํ email์ ๋ณด๋ง ๋ถ๋ฌ์ค๊ฒ ๋๋ค.
N + 1 Problem ?
populate()
๋ฅผ ์ฌ์ฉํ๋ฉด N + 1 ๋ฌธ์ ๊ฐ ์ด๋ป๊ฒ ํด๊ฒฐ๋ ๊น
- ๋ ๋์ ์ดํด๋ฅผ ์ํด์ ๊ณต์๋ฌธ์์๋ ๋ค๋ฅธ ์์๋ฅผ ์ฐธ๊ณ ํ๋ค.
์์ ๋ธ๋ก๊ทธ์ ์์๋ฅผ ํตํด N + 1 ๋ฌธ์ ๋ฅผ ์ด๋ป๊ฒ ํด๊ฒฐํ๋์ง ์ดํดํ ์ ์์๋ค.
- ํ์ฌ DB์๋ 2๊ฐ์ ๋ธ๋ก๊ทธ๊ฐ ์กด์ฌํ๊ณ , ๊ฐ ๋ธ๋ก๊ทธ์๋ 3๊ฐ์ ๋๊ธ์ด ๋ฌ๋ ค์๋ ์ํฉ์ ๊ฐ์ ํ๋ค.
- ์ฐ๋ฆฌ๊ฐ ํ์ํ ์ ๋ณด๋ 2๊ฐ์ ๋ธ๋ก๊ทธ, ํด๋น ๋ธ๋ก๊ทธ์ ๋๊ธ๋ค, ๊ทธ๋ฆฌ๊ณ ๋๊ธ์ ์์ฑ์์ ๋ํ ์ ๋ณด์ด๋ค.
populate
๋ฅผ ์ฌ์ฉํ์ง ์์ผ๋ฉด ์ํ๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํด์ ์ด 9๋ฒ์ ์ฟผ๋ฆฌ๋ฅผ ์์ฒญํด์ผ ํ๋ค.- ๋ง์ฝ ๋ธ๋ก๊ทธ์ ์๊ฐ ๋ ๋ง์๊ฑฐ๋, ๋๊ธ์ ์๊ฐ ๋ ๋ง์๋ค๋ผ๋ฉด ๊ทธ์ ๋ฐ๋ผ ์ฟผ๋ฆฌ์ ์๊ฐ ๊ธ์์ ์ผ๋ก ๋์ด๋ ๊ฒ์ด๋ค.
- ๋ํ ๊ฐ์ ์ ์ ๊ฐ ๋๊ธ์ ๋ฌ์๋ค๊ณ ํ๋๋ผ๋, ๋งค๋ฒ ์ฟผ๋ฆฌ๋ฅผ ํตํด ๋ฐ์ดํฐ๋ฅผ ์์ฒญํ๊ธฐ ๋๋ฌธ์ ๋งค์ฐ ๋นํจ์จ์ ์ด๋ค.
populate๋ฅผ ์ฌ์ฉํ๋ฉด blogId ๋๋ userId๋ฅผ ์ทจํฉํด์ $in
์ฐ์ฐ์๋ฅผ ์ฌ์ฉํด์ ํ ๋ฒ์ ์ฒ๋ฆฌํ๋ค
- ๋ฐ๋ผ์ ๋ธ๋ก๊ทธ์ ์๋ ๋๊ธ์ ์๊ฐ ๋ง์๋ ํ ๋ฒ์ ์ฟผ๋ฆฌ๋ก ์ฒ๋ฆฌ๊ฐ ๋๋ค.
popluate
๋ฅผ ์ฌ์ฉํ๋ฉด ์ฟผ๋ฆฌ์ ์๋ฅผ ์ค์ผ ์ ์๊ณ , ๋ฐ์ดํฐ ๋ฒ ์ด์ค์์ ํต์ ํ์๋ฅผ ์ต์ํํ์ฌ ์ฑ๋ฅ์ ํฅ์์ํฌ ์ ์๋ค.- ์ด๋ณด๋ค ์ฟผ๋ฆฌ๋ฅผ ๋ ์ค์ด๋ ค๋ฉด Embed ๋ฐฉ์์ ์ฌ์ฉํ๋ฉด ๋์ง๋ง, Embed ๋ฐฉ์์ Update์์ ์ฑ๋ฅ์ด ์ ํ๋๊ธฐ ๋๋ฌธ์ ์ด๋ฅผ ์ ์๊ฐํด์ ์ฌ์ฉํด์ผ ํ๋ค.
Dataloader vs Populate
๊ทธ๋ ๋ค๋ฉด Dataloader์ Populate ์ค์ ์ด๋ค ๊ฒ์ ์ฌ์ฉํด์ผ ํ ๊น?
์ฌ์ค ์ด ๋ถ๋ถ์ ๋ํด์ ๋ช ๊ฐ์ง ๊ธ์ ์ฐพ์๋ณด์์ง๋ง, ๋ช
์พํ๊ฒ ์ด๊ฒ ๋ ์ข์ผ๋๊น ์ด๊ฑธ ์ฌ์ฉํ์ธ์! ๋ผ๊ณ ํ๋ ๊ธ์ ์์๋ค.
๊ทธ ์ด์ ๋ ์๋ง ๋ ๊ฐ๊ฐ ๋น์ทํ ๊ธฐ๋ฅ์ ํ์ง๋ง, ์์ ํ ๊ฐ์ ๊ธฐ๋ฅ์ ํ์ง ์๊ณ ์ํฉ๋ง๋ค ์ ํํด์ ์ฌ์ฉํด์ผ ํ๊ธฐ ๋๋ฌธ์ผ ๊ฒ์ด๋ผ ์๊ฐํ๋ค.
- Populate๋ฅผ ์ฌ์ฉํด๋ ์ฟผ๋ฆฌ์ ์๋ฅผ ์ค์ผ ์ ์๊ณ , ์ฌ์ฉ๋ฒ์ด ๋จ์ํ๊ณ ๊ฐ๋จํ๋ค.
- ํ์ง๋ง ๋ง์ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ ๊ฒฝ์ฐ์ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ด ์ฆ๊ฐํ ์ ์๋ค.
- ๋ํ ๋ฏธ๋ฆฌ ์๋น์ค๋จ์์ populate๋ฅผ ๊ฑธ์ด๋๋ฉด ํด๋ผ์ด์ธํธ์ธก overfetching ๋ฌธ์ ๋ GraphL์ ํตํด ํด๊ฒฐ๋ ์ง๋ผ๋,
์๋ฒ๋จ์์ overfetching ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
- Dataloader๋ฅผ ์ฌ์ฉํ๋ฉด ์ฟผ๋ฆฌ๋ฅผ ๋ชจ์์ ํ ๋ฒ์ ์ฒ๋ฆฌํ๊ณ , ๋ฏธ๋ฆฌ ๋ก๋๋ ๋ฐ์ดํฐ๋ฅผ ์บ์ฑํ๊ธฐ ๋๋ฌธ์ ์ค๋ณต๋ ์ฟผ๋ฆฌ๋ฅผ ๋ฐฉ์งํ๊ณ , ์ฑ๋ฅ์ ํฅ์์ํฌ ์ ์๋ค.
- ํ์ง๋ง ์บ์ฑ์ ์ฌ์ฉํ ๊ฒฝ์ฐ์, ์๋ณธ ๋ฐ์ดํฐ๊ฐ ์ ๋ฐ์ดํธ ๋๋๋ผ๋ ์บ์ฑ๋ ๋ฐ์ดํฐ๋ฅผ ๋ฐํํ๊ธฐ ๋๋ฌธ์ ๋ฐ์์ด ์ ๋ ๊ฒ์ฒ๋ผ ๋ณด์ผ ์ ์๋ค.
- ๋ํ ๋ณด์์์ ์ด์ ๋ก ์น ์๋ฒ์์๋ ๋งค ์์ฒญ๋ง๋ค ์๋ก์ด ๋ฐ์ดํฐ๋ก๋ ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ ๊ฒ์ ๊ถ์ฅํ๋ค.
'WIL' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Two-Phase Locking (2PL) (0) | 2024.05.10 |
---|---|
๋ ์ธ๋ณด์ฐ ํ ์ด๋ธ (0) | 2024.05.01 |
๋ฌด์ํ ํ๋กํ ์ฝ (1) | 2024.03.08 |