Node.js์์ AsyncLocalStorage๋ก ์์ฒญ ๋ฒ์ ๋ณ์ ๊ด๋ฆฌ๋ฅผ ๋ง์คํฐํ์ธ์. Prop Drilling์ ์ ๊ฑฐํ๊ณ , ๊ธ๋ก๋ฒ ์ฌ์ฉ์๋ฅผ ์ํด ๋ ๊น๋ํ๊ณ ๊ด์ฐฐ ๊ฐ๋ฅํ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ์ธ์.
์๋ฐ์คํฌ๋ฆฝํธ ๋น๋๊ธฐ ์ปจํ ์คํธ์ ์ดํด: ์์ฒญ ๋ฒ์ ๋ณ์ ๊ด๋ฆฌ์ ๋ํ ์ฌ์ธต ๋ถ์
ํ๋ ์๋ฒ์ฌ์ด๋ ๊ฐ๋ฐ์ ์ธ๊ณ์์ ์ํ ๊ด๋ฆฌ๋ ๊ทผ๋ณธ์ ์ธ ๊ณผ์ ์ ๋๋ค. Node.js๋ก ์์ ํ๋ ๊ฐ๋ฐ์์๊ฒ ์ด ๊ณผ์ ๋ ์ฑ๊ธ ์ค๋ ๋, ๋ ผ๋ธ๋กํน, ๋น๋๊ธฐ์ ํน์ฑ์ผ๋ก ์ธํด ๋์ฑ ์ฆํญ๋ฉ๋๋ค. ์ด ๋ชจ๋ธ์ ๊ณ ์ฑ๋ฅ I/O ๋ฐ์ด๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๋ ๋ฐ ๋งค์ฐ ๊ฐ๋ ฅํ์ง๋ง, ๋ ํนํ ๋ฌธ์ ๋ฅผ ์ผ๊ธฐํฉ๋๋ค. ์ฆ, ๋ฏธ๋ค์จ์ด์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ, ์๋ํํฐ API ํธ์ถ์ ์ด๋ฅด๊ธฐ๊น์ง ๋ค์ํ ๋น๋๊ธฐ ์์ ์ ํตํด ํ๋ฅด๋ ํน์ ์์ฒญ์ ๋ํ ์ปจํ ์คํธ๋ฅผ ์ด๋ป๊ฒ ์ ์งํ ์ ์์๊น์? ํ ์ฌ์ฉ์์ ์์ฒญ ๋ฐ์ดํฐ๊ฐ ๋ค๋ฅธ ์ฌ์ฉ์์ ์์ฒญ์ ์ ์ถ๋์ง ์๋๋ก ์ด๋ป๊ฒ ๋ณด์ฅํ ์ ์์๊น์?
์๋ ๋์ ์๋ฐ์คํฌ๋ฆฝํธ ์ปค๋ฎค๋ํฐ๋ ์ด ๋ฌธ์ ์ ์จ๋ฆํ๋ฉฐ, ์ข ์ข 'ํ๋กญ ๋๋ฆด๋ง(prop drilling)'๊ณผ ๊ฐ์ ๋ฒ๊ฑฐ๋ก์ด ํจํด์ ์์กดํ์ต๋๋ค. ์ด๋ ์ฌ์ฉ์ ID๋ ์ถ์ ID์ ๊ฐ์ ์์ฒญ ํน์ ๋ฐ์ดํฐ๋ฅผ ํธ์ถ ์ฒด์ธ์ ๋ชจ๋ ๋จ์ผ ํจ์๋ฅผ ํตํด ์ ๋ฌํ๋ ๊ฒ์ ๋๋ค. ์ด ์ ๊ทผ ๋ฐฉ์์ ์ฝ๋๋ฅผ ์ด์ง๋ฝํ๊ณ , ๋ชจ๋ ๊ฐ์ ๊ฐํ ๊ฒฐํฉ์ ์์ฑํ๋ฉฐ, ์ ์ง๋ณด์๋ฅผ ๋ฐ๋ณต๋๋ ์ ๋ชฝ์ผ๋ก ๋ง๋ญ๋๋ค.
์ฌ๊ธฐ์ ๋น๋๊ธฐ ์ปจํ
์คํธ(Async Context)๊ฐ ๋ฑ์ฅํฉ๋๋ค. ์ด ๊ฐ๋
์ ์ด ์ค๋๋ ๋ฌธ์ ์ ๋ํ ๊ฒฌ๊ณ ํ ํด๊ฒฐ์ฑ
์ ์ ๊ณตํฉ๋๋ค. Node.js์ ์์ ์ ์ธ AsyncLocalStorage API๊ฐ ๋์
๋๋ฉด์, ๊ฐ๋ฐ์๋ค์ ์ด์ ์์ฒญ ๋ฒ์ ๋ณ์๋ฅผ ์ฐ์ํ๊ณ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ ์ ์๋ ๊ฐ๋ ฅํ ๋ด์ฅ ๋ฉ์ปค๋์ฆ์ ๊ฐ๊ฒ ๋์์ต๋๋ค. ์ด ๊ฐ์ด๋๋ ์๋ฐ์คํฌ๋ฆฝํธ ๋น๋๊ธฐ ์ปจํ
์คํธ์ ์ธ๊ณ๋ก ์ฌ๋ฌ๋ถ์ ์๋ดํ๋ ํฌ๊ด์ ์ธ ์ฌ์ ์ด ๋ ๊ฒ์
๋๋ค. ๋ฌธ์ ๋ฅผ ์ค๋ช
ํ๊ณ , ํด๊ฒฐ์ฑ
์ ์๊ฐํ๋ฉฐ, ๊ธ๋ก๋ฒ ์ฌ์ฉ์ ๊ธฐ๋ฐ์ ์ํ ๋ ํ์ฅ ๊ฐ๋ฅํ๊ณ , ์ ์ง๋ณด์ํ๊ธฐ ์ฌ์ฐ๋ฉฐ, ๊ด์ฐฐ ๊ฐ๋ฅํ ์ ํ๋ฆฌ์ผ์ด์
์ ๊ตฌ์ถํ๋ ๋ฐ ๋์์ด ๋๋ ์ค์ฉ์ ์ธ ์ค์ ์์ ๋ฅผ ์ ๊ณตํฉ๋๋ค.
ํต์ฌ ๊ณผ์ : ๋์์ฑ, ๋น๋๊ธฐ ํ๊ฒฝ์์์ ์ํ ๊ด๋ฆฌ
ํด๊ฒฐ์ฑ ์ ์์ ํ ์ดํดํ๋ ค๋ฉด ๋จผ์ ๋ฌธ์ ์ ๊น์ด๋ฅผ ์ดํดํด์ผ ํฉ๋๋ค. Node.js ์๋ฒ๋ ์์ฒ ๊ฐ์ ๋์ ์์ฒญ์ ์ฒ๋ฆฌํฉ๋๋ค. ์์ฒญ A๊ฐ ๋ค์ด์ค๋ฉด Node.js๋ ์ฒ๋ฆฌ๋ฅผ ์์ํ ๋ค์, ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ๊ฐ ์๋ฃ๋๊ธฐ๋ฅผ ๊ธฐ๋ค๋ฆฌ๊ธฐ ์ํด ์ผ์ ์ค์งํ ์ ์์ต๋๋ค. ๊ธฐ๋ค๋ฆฌ๋ ๋์ ์์ฒญ B๋ฅผ ๋ฐ์ ์์ ์ ์์ํฉ๋๋ค. ์์ฒญ A์ ๋ํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ฒฐ๊ณผ๊ฐ ๋ฐํ๋๋ฉด Node.js๋ ์คํ์ ์ฌ๊ฐํฉ๋๋ค. ์ด ๋์์๋ ์ปจํ ์คํธ ์ ํ์ ์ฑ๋ฅ์ ๋น๊ฒฐ์ด์ง๋ง, ์ ํต์ ์ธ ์ํ ๊ด๋ฆฌ ๊ธฐ์ ์๋ ํฐ ํผ๋์ ์ผ๊ธฐํฉ๋๋ค.
์ ์ญ ๋ณ์๊ฐ ์คํจํ๋ ์ด์
์ด๋ณด ๊ฐ๋ฐ์์ ์ฒซ ๋ฒ์งธ ๋ณธ๋ฅ์ ์ ์ญ ๋ณ์๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ผ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด:
let currentUser; // ์ ์ญ ๋ณ์
// ์ฌ์ฉ์๋ฅผ ์ค์ ํ๋ ๋ฏธ๋ค์จ์ด
app.use((req, res, next) => {
currentUser = await getUserFromDb(req.headers.authorization);
next();
});
// ์ ํ๋ฆฌ์ผ์ด์
๊น์ํ ๊ณณ์ ์๋ ์๋น์ค ํจ์
function logActivity() {
console.log(`Activity for user: ${currentUser.id}`);
}
์ด๊ฒ์ ๋์์ฑ ํ๊ฒฝ์์ ์น๋ช
์ ์ธ ์ค๊ณ ๊ฒฐํจ์
๋๋ค. ๋ง์ฝ ์์ฒญ A๊ฐ currentUser๋ฅผ ์ค์ ํ ํ ๋น๋๊ธฐ ์์
์ ๊ธฐ๋ค๋ฆฌ๋ ๋์, ์์ฒญ B๊ฐ ๋ค์ด์ ์์ฒญ A๊ฐ ๋๋๊ธฐ ์ ์ currentUser๋ฅผ ๋ฎ์ด์ธ ์ ์์ต๋๋ค. ์์ฒญ A๊ฐ ์ฌ๊ฐ๋๋ฉด, ์์ฒญ B์ ๋ฐ์ดํฐ๋ฅผ ์๋ชป ์ฌ์ฉํ๊ฒ ๋ฉ๋๋ค. ์ด๋ ์์ธก ๋ถ๊ฐ๋ฅํ ๋ฒ๊ทธ, ๋ฐ์ดํฐ ์์, ๊ทธ๋ฆฌ๊ณ ๋ณด์ ์ทจ์ฝ์ ์ ๋ง๋ญ๋๋ค. ์ ์ญ ๋ณ์๋ ์์ฒญ์ ์์ ํ์ง ์์ต๋๋ค.
ํ๋กญ ๋๋ฆด๋ง(Prop Drilling)์ ๊ณ ํต
๋ ์ผ๋ฐ์ ์ด๊ณ ์์ ํ ํด๊ฒฐ ๋ฐฉ๋ฒ์ 'ํ๋กญ ๋๋ฆด๋ง' ๋๋ '๋งค๊ฐ๋ณ์ ์ ๋ฌ'์ด์์ต๋๋ค. ์ด๋ ์ปจํ ์คํธ๋ฅผ ํ์๋ก ํ๋ ๋ชจ๋ ํจ์์ ๋ช ์์ ์ผ๋ก ์ธ์๋ก ์ ๋ฌํ๋ ๊ฒ์ ํฌํจํฉ๋๋ค.
๋ก๊น
์ ์ํ ๊ณ ์ ํ traceId์ ์ ํ๋ฆฌ์ผ์ด์
์ ์ฒด์์ ์ธ๊ฐ๋ฅผ ์ํ user ๊ฐ์ฒด๊ฐ ํ์ํ๋ค๊ณ ์์ํด ๋ด
์๋ค.
ํ๋กญ ๋๋ฆด๋ง ์์:
// 1. ์ง์
์ : ๋ฏธ๋ค์จ์ด
app.use((req, res, next) => {
const traceId = generateTraceId();
const user = { id: 'user-123', locale: 'en-GB' };
const requestContext = { traceId, user };
processOrder(requestContext, req.body.orderId);
});
// 2. ๋น์ฆ๋์ค ๋ก์ง ๋ ์ด์ด
function processOrder(context, orderId) {
log('Processing order', context);
const orderDetails = getOrderDetails(context, orderId);
// ... ๋ ๋ง์ ๋ก์ง
}
// 3. ๋ฐ์ดํฐ ์ ๊ทผ ๋ ์ด์ด
function getOrderDetails(context, orderId) {
log(`Fetching order ${orderId}`, context);
return db.query('SELECT * FROM orders WHERE id = ?', orderId);
}
// 4. ์ ํธ๋ฆฌํฐ ๋ ์ด์ด
function log(message, context) {
console.log(`[${context.traceId}] [User: ${context.user.id}] - ${message}`);
}
์ด ๋ฐฉ๋ฒ์ ์๋ํ๊ณ ๋์์ฑ ๋ฌธ์ ๋ก๋ถํฐ ์์ ํ์ง๋ง, ์๋นํ ๋จ์ ์ด ์์ต๋๋ค:
- ์ฝ๋์ ์ด์์ ํจ:
context๊ฐ์ฒด๋ ์ง์ ์ฌ์ฉํ์ง ์์ง๋ง ์์ ์ด ํธ์ถํ๋ ํจ์์ ์ ๋ฌํด์ผ ํ๋ ํจ์๋ค์ ํตํด์๋ ์ด๋์๋ ์ ๋ฌ๋ฉ๋๋ค. - ๊ฐํ ๊ฒฐํฉ: ๋ชจ๋ ํจ์ ์๊ทธ๋์ฒ๊ฐ ์ด์
context๊ฐ์ฒด์ ํํ์ ๊ฒฐํฉ๋ฉ๋๋ค. ์ปจํ ์คํธ์ ์๋ก์ด ๋ฐ์ดํฐ(์: A/B ํ ์คํธ ํ๋๊ทธ)๋ฅผ ์ถ๊ฐํด์ผ ํ๋ ๊ฒฝ์ฐ, ์ฝ๋๋ฒ ์ด์ค ์ ์ฒด์ ๊ฑธ์ณ ์์ญ ๊ฐ์ ํจ์ ์๊ทธ๋์ฒ๋ฅผ ์์ ํด์ผ ํ ์๋ ์์ต๋๋ค. - ๊ฐ๋ ์ฑ ์ ํ: ํจ์์ ์ฃผ๋ ๋ชฉ์ ์ด ์ปจํ ์คํธ๋ฅผ ์ ๋ฌํ๋ ์์ฉ๊ตฌ ์ฝ๋์ ์ํด ๊ฐ๋ ค์ง ์ ์์ต๋๋ค.
- ์ ์ง๋ณด์ ๋ถ๋ด: ๋ฆฌํฉํ ๋ง์ด ์ง๋ฃจํ๊ณ ์ค๋ฅ๊ฐ ๋ฐ์ํ๊ธฐ ์ฌ์ด ๊ณผ์ ์ด ๋ฉ๋๋ค.
์ฐ๋ฆฌ๋ ๋ ๋์ ๋ฐฉ๋ฒ์ด ํ์ํ์ต๋๋ค. ๋ช ์์ ์ธ ์ ๋ฌ ์์ด, ํด๋น ์์ฒญ์ ๋น๋๊ธฐ ํธ์ถ ์ฒด์ธ ๋ด ์ด๋์์๋ ์ ๊ทผํ ์ ์๋ ์์ฒญ ํน์ ๋ฐ์ดํฐ๋ฅผ ๋ด๋ '๋ง๋ฒ ๊ฐ์' ์ปจํ ์ด๋๊ฐ ํ์ํ์ต๋๋ค.
`AsyncLocalStorage`์ ๋ฑ์ฅ: ํ๋์ ์ธ ํด๊ฒฐ์ฑ
Node.js v13.10.0๋ถํฐ ์์ ์ ์ธ ๊ธฐ๋ฅ์ด ๋ AsyncLocalStorage ํด๋์ค๋ ์ด ๋ฌธ์ ์ ๋ํ ๊ณต์์ ์ธ ํด๋ต์
๋๋ค. ์ด๋ฅผ ํตํด ๊ฐ๋ฐ์๋ ํน์ ์ง์
์ ์์ ์์๋ ์ ์ฒด ๋น๋๊ธฐ ์์
์ฒด์ธ์ ๊ฑธ์ณ ์ง์๋๋ ๊ฒฉ๋ฆฌ๋ ์คํ ๋ฆฌ์ง ์ปจํ
์คํธ๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
์ด๊ฒ์ ์๋ฐ์คํฌ๋ฆฝํธ์ ๋น๋๊ธฐ์ , ์ด๋ฒคํธ ๊ธฐ๋ฐ ์ธ๊ณ๋ฅผ ์ํ ์ผ์ข
์ '์ค๋ ๋-๋ก์ปฌ ์คํ ๋ฆฌ์ง'๋ผ๊ณ ์๊ฐํ ์ ์์ต๋๋ค. AsyncLocalStorage ์ปจํ
์คํธ ๋ด์์ ์์
์ ์์ํ๋ฉด, ๊ทธ ์์ ๋ถํฐ ํธ์ถ๋๋ ๋ชจ๋ ํจ์โ๋๊ธฐ์ ์ด๋ , ์ฝ๋ฐฑ ๊ธฐ๋ฐ์ด๋ , ํ๋ก๋ฏธ์ค ๊ธฐ๋ฐ์ด๋ โ๋ ํด๋น ์ปจํ
์คํธ์ ์ ์ฅ๋ ๋ฐ์ดํฐ์ ์ ๊ทผํ ์ ์์ต๋๋ค.
ํต์ฌ API ๊ฐ๋
API๋ ๋๋๋๋ก ๊ฐ๋จํ๊ณ ๊ฐ๋ ฅํฉ๋๋ค. ์ธ ๊ฐ์ง ํต์ฌ ๋ฉ์๋๋ฅผ ์ค์ฌ์ผ๋ก ์๋ํฉ๋๋ค:
new AsyncLocalStorage(): ์คํ ์ด์ ์ ์ธ์คํด์ค๋ฅผ ์์ฑํฉ๋๋ค. ์ผ๋ฐ์ ์ผ๋ก ์ปจํ ์คํธ ์ ํ๋ณ๋ก ํ๋์ ์ธ์คํด์ค(์: ๋ชจ๋ HTTP ์์ฒญ์ ๋ํด ํ๋)๋ฅผ ๋ง๋ค์ด ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฒด์์ ๊ณต์ ํฉ๋๋ค.als.run(store, callback): ์ด๊ฒ์ด ํต์ฌ์ ๋๋ค. ํจ์(callback)๋ฅผ ์คํํ๊ณ ์๋ก์ด ๋น๋๊ธฐ ์ปจํ ์คํธ๋ฅผ ์ค์ ํฉ๋๋ค. ์ฒซ ๋ฒ์งธ ์ธ์store๋ ํด๋น ์ปจํ ์คํธ ๋ด์์ ์ฌ์ฉ ๊ฐ๋ฅํ๊ฒ ๋ง๋ค๊ณ ์ถ์ ๋ฐ์ดํฐ์ ๋๋ค.callback๋ด์์ ์คํ๋๋ ๋ชจ๋ ์ฝ๋(๋น๋๊ธฐ ์์ ํฌํจ)๋ ์ดstore์ ์ ๊ทผํ ์ ์์ต๋๋ค.als.getStore(): ์ด ๋ฉ์๋๋ ํ์ฌ ์ปจํ ์คํธ์์ ๋ฐ์ดํฐ(store)๋ฅผ ๊ฒ์ํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.run()์ ์ํด ์ค์ ๋ ์ปจํ ์คํธ ์ธ๋ถ์์ ํธ์ถ๋๋ฉดundefined๋ฅผ ๋ฐํํฉ๋๋ค.
์ค์ฉ์ ์ธ ๊ตฌํ: ๋จ๊ณ๋ณ ๊ฐ์ด๋
์ด์ ์ ํ๋กญ ๋๋ฆด๋ง ์์ ๋ฅผ AsyncLocalStorage๋ฅผ ์ฌ์ฉํ์ฌ ๋ฆฌํฉํ ๋งํด ๋ณด๊ฒ ์ต๋๋ค. ํ์ค Express.js ์๋ฒ๋ฅผ ์ฌ์ฉํ๊ฒ ์ง๋ง, ์์น์ ๋ชจ๋ Node.js ํ๋ ์์ํฌ๋ ๋ค์ดํฐ๋ธ http ๋ชจ๋์์๋ ๋์ผํฉ๋๋ค.
1๋จ๊ณ: ์ค์ `AsyncLocalStorage` ์ธ์คํด์ค ์์ฑ
์คํ ์ด์ ๋จ์ผ ๊ณต์ ์ธ์คํด์ค๋ฅผ ์์ฑํ๊ณ ์ ํ๋ฆฌ์ผ์ด์
์ ์ฒด์์ ์ฌ์ฉํ ์ ์๋๋ก ๋ด๋ณด๋ด๋ ๊ฒ์ด ๋ชจ๋ฒ ์ฌ๋ก์
๋๋ค. asyncContext.js๋ผ๋ ํ์ผ์ ๋ง๋ค์ด ๋ณด๊ฒ ์ต๋๋ค.
// asyncContext.js
import { AsyncLocalStorage } from 'async_hooks';
export const requestContextStore = new AsyncLocalStorage();
2๋จ๊ณ: ๋ฏธ๋ค์จ์ด๋ก ์ปจํ ์คํธ ์ค์
์ปจํ
์คํธ๋ฅผ ์์ํ๊ธฐ์ ์ด์์ ์ธ ์ฅ์๋ ์์ฒญ ์๋ช
์ฃผ๊ธฐ์ ๊ฐ์ฅ ์ฒซ ๋ถ๋ถ์
๋๋ค. ๋ฏธ๋ค์จ์ด๋ ์ด๋ฅผ ์ํด ์๋ฒฝํฉ๋๋ค. ์์ฒญ ํน์ ๋ฐ์ดํฐ๋ฅผ ์์ฑํ ๋ค์ ๋๋จธ์ง ์์ฒญ ์ฒ๋ฆฌ ๋ก์ง์ als.run() ์์ ๋ํํ ๊ฒ์
๋๋ค.
// server.js
import express from 'express';
import { requestContextStore } from './asyncContext.js';
import { v4 as uuidv4 } from 'uuid'; // ๊ณ ์ ํ traceId ์์ฑ์ ์ํด
const app = express();
// ๋ง๋ฒ์ ๋ฏธ๋ค์จ์ด
app.use((req, res, next) => {
const traceId = req.headers['x-request-id'] || uuidv4();
const user = { id: 'user-123', locale: 'en-GB' }; // ์ค์ ์ฑ์์๋ ์ธ์ฆ ๋ฏธ๋ค์จ์ด์์ ๊ฐ์ ธ์ต๋๋ค
const store = { traceId, user };
// ์ด ์์ฒญ์ ๋ํ ์ปจํ
์คํธ ์ค์
requestContextStore.run(store, () => {
next();
});
});
// ... ๋ผ์ฐํธ์ ๋ค๋ฅธ ๋ฏธ๋ค์จ์ด๋ ์ฌ๊ธฐ์ ์์นํฉ๋๋ค
์ด ๋ฏธ๋ค์จ์ด์์, ๋ชจ๋ ๋ค์ด์ค๋ ์์ฒญ์ ๋ํด traceId์ user๋ฅผ ํฌํจํ๋ store ๊ฐ์ฒด๋ฅผ ์์ฑํฉ๋๋ค. ๊ทธ๋ฐ ๋ค์ requestContextStore.run(store, ...)๋ฅผ ํธ์ถํฉ๋๋ค. ๋ด๋ถ์ next() ํธ์ถ์ ์ด ํน์ ์์ฒญ์ ๋ํ ๋ชจ๋ ํ์ ๋ฏธ๋ค์จ์ด์ ๋ผ์ฐํธ ํธ๋ค๋ฌ๊ฐ ์๋ก ์์ฑ๋ ์ด ์ปจํ
์คํธ ๋ด์์ ์คํ๋๋๋ก ๋ณด์ฅํฉ๋๋ค.
3๋จ๊ณ: Prop Drilling ์์ด ์ด๋์๋ ์ปจํ ์คํธ์ ์ ๊ทผํ๊ธฐ
์ด์ ์ฐ๋ฆฌ์ ๋ค๋ฅธ ๋ชจ๋๋ค์ ๊ธ์ง์ ์ผ๋ก ๋จ์ํ๋ ์ ์์ต๋๋ค. ๋ ์ด์ context ๋งค๊ฐ๋ณ์๊ฐ ํ์ํ์ง ์์ต๋๋ค. ๋จ์ํ requestContextStore๋ฅผ ๊ฐ์ ธ์์ getStore()๋ฅผ ํธ์ถํ๋ฉด ๋ฉ๋๋ค.
๋ฆฌํฉํ ๋ง๋ ๋ก๊น ์ ํธ๋ฆฌํฐ:
// logger.js
import { requestContextStore } from './asyncContext.js';
export function log(message) {
const context = requestContextStore.getStore();
if (context) {
const { traceId, user } = context;
console.log(`[${traceId}] [User: ${user.id}] - ${message}`);
} else {
// ์์ฒญ ์ปจํ
์คํธ ์ธ๋ถ์ ๋ก๊ทธ๋ฅผ ์ํ ๋์ฒด ์ฒ๋ฆฌ
console.log(`[NO_CONTEXT] - ${message}`);
}
}
๋ฆฌํฉํ ๋ง๋ ๋น์ฆ๋์ค ๋ฐ ๋ฐ์ดํฐ ๋ ์ด์ด:
// orderService.js
import { log } from './logger.js';
import * as db from './database.js';
export function processOrder(orderId) {
log('Processing order'); // ์ปจํ
์คํธ ํ์ ์์!
const orderDetails = getOrderDetails(orderId);
// ... ๋ ๋ง์ ๋ก์ง
}
function getOrderDetails(orderId) {
log(`Fetching order ${orderId}`); // ๋ก๊ฑฐ๊ฐ ์๋์ผ๋ก ์ปจํ
์คํธ๋ฅผ ๊ฐ์ ธ์ต๋๋ค
return db.query('SELECT * FROM orders WHERE id = ?', orderId);
}
์ฐจ์ด๋ ํ๋๊ณผ ๋ ์ฐจ์ด์ ๋๋ค. ์ฝ๋๋ ๊ทน์ ์ผ๋ก ๋ ๊นจ๋ํ๊ณ , ์ฝ๊ธฐ ์ฌ์ฐ๋ฉฐ, ์ปจํ ์คํธ์ ๊ตฌ์กฐ๋ก๋ถํฐ ์์ ํ ๋ถ๋ฆฌ๋์์ต๋๋ค. ์ฐ๋ฆฌ์ ๋ก๊น ์ ํธ๋ฆฌํฐ, ๋น์ฆ๋์ค ๋ก์ง, ๋ฐ์ดํฐ ์ ๊ทผ ๋ ์ด์ด๋ ์ด์ ์์ํ๊ณ ๊ทธ๋ค์ ํน์ ์์ ์ ์ง์คํ ์ ์์ต๋๋ค. ๋ง์ฝ ์์ฒญ ์ปจํ ์คํธ์ ์๋ก์ด ์์ฑ์ ์ถ๊ฐํด์ผ ํ๋ค๋ฉด, ๊ทธ๊ฒ์ด ์์ฑ๋๋ ๋ฏธ๋ค์จ์ด๋ง ๋ณ๊ฒฝํ๋ฉด ๋ฉ๋๋ค. ๋ค๋ฅธ ์ด๋ค ํจ์ ์๊ทธ๋์ฒ๋ ๊ฑด๋๋ฆด ํ์๊ฐ ์์ต๋๋ค.
๊ณ ๊ธ ์ฌ์ฉ ์ฌ๋ก ๋ฐ ๊ธ๋ก๋ฒ ๊ด์
์์ฒญ ๋ฒ์ ์ปจํ ์คํธ๋ ๋จ์ง ๋ก๊น ๋ง์ ์ํ ๊ฒ์ด ์๋๋๋ค. ์ ๊ตํ ๊ธ๋ก๋ฒ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๋ ๋ฐ ํ์์ ์ธ ๋ค์ํ ๊ฐ๋ ฅํ ํจํด์ ๊ฐ๋ฅํ๊ฒ ํฉ๋๋ค.
1. ๋ถ์ฐ ์ถ์ ๋ฐ ๊ด์ฐฐ ๊ฐ๋ฅ์ฑ
๋ง์ดํฌ๋ก์๋น์ค ์ํคํ
์ฒ์์ ๋จ์ผ ์ฌ์ฉ์ ์์
์ ์ฌ๋ฌ ์๋น์ค์ ๊ฑธ์ณ ์์ฒญ ์ฒด์ธ์ ์ ๋ฐํ ์ ์์ต๋๋ค. ๋ฌธ์ ๋ฅผ ๋๋ฒ๊น
ํ๋ ค๋ฉด ์ด ์ ์ฒด ์ฌ์ ์ ์ถ์ ํ ์ ์์ด์ผ ํฉ๋๋ค. AsyncLocalStorage๋ ํ๋์ ์ธ ์ถ์ ์ ์ด์์
๋๋ค. API ๊ฒ์ดํธ์จ์ด๋ก ๋ค์ด์ค๋ ์์ฒญ์ ๊ณ ์ ํ traceId๋ฅผ ํ ๋นํ ์ ์์ต๋๋ค. ์ด ID๋ ๋น๋๊ธฐ ์ปจํ
์คํธ์ ์ ์ฅ๋๊ณ , ๋ค์ด์คํธ๋ฆผ ์๋น์ค๋ก์ ๋ชจ๋ ์ธ๋ถ API ํธ์ถ์ (์: HTTP ํค๋๋ก) ์๋์ผ๋ก ํฌํจ๋ฉ๋๋ค. ๊ฐ ์๋น์ค๋ ๋์ผํ ์์
์ ์ํํ์ฌ ์ปจํ
์คํธ๋ฅผ ์ ํํฉ๋๋ค. ์ค์ ์ง์ค์ ๋ก๊น
ํ๋ซํผ์ ์ด๋ฌํ ๋ก๊ทธ๋ฅผ ์์งํ์ฌ ์ ์ฒด ์์คํ
์ ๊ฑธ์น ์์ฒญ์ ์ข
๋จ ๊ฐ ํ๋ฆ ์ ์ฒด๋ฅผ ์ฌ๊ตฌ์ฑํ ์ ์์ต๋๋ค.
2. ๊ตญ์ ํ(i18n) ๋ฐ ํ์งํ(l10n)
๊ธ๋ก๋ฒ ์ ํ๋ฆฌ์ผ์ด์
์ ๊ฒฝ์ฐ, ๋ ์ง, ์๊ฐ, ์ซ์, ํตํ๋ฅผ ์ฌ์ฉ์์ ํ์ง ํ์์ผ๋ก ํ์ํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค. ์ฌ์ฉ์์ ์์ฒญ ํค๋๋ ์ฌ์ฉ์ ํ๋กํ์์ ์ฌ์ฉ์์ ๋ก์ผ์ผ(์: 'fr-FR', 'ja-JP', 'en-US')์ ๋น๋๊ธฐ ์ปจํ
์คํธ์ ์ ์ฅํ ์ ์์ต๋๋ค.
// ํตํ ํฌ๋งทํ
์ ํธ๋ฆฌํฐ
import { requestContextStore } from './asyncContext.js';
function formatCurrency(amount, currencyCode) {
const context = requestContextStore.getStore();
const locale = context?.user?.locale || 'en-US'; // ๊ธฐ๋ณธ๊ฐ์ผ๋ก ๋์ฒด
return new Intl.NumberFormat(locale, {
style: 'currency',
currency: currencyCode,
}).format(amount);
}
// ์ฑ ๊น์ํ ๊ณณ์์์ ์ฌ์ฉ
const priceString = formatCurrency(199.99, 'EUR'); // ์ฌ์ฉ์์ ๋ก์ผ์ผ์ ์๋์ผ๋ก ์ฌ์ฉํฉ๋๋ค
์ด๋ locale ๋ณ์๋ฅผ ๋ชจ๋ ๊ณณ์ ์ ๋ฌํ์ง ์๊ณ ๋ ์ผ๊ด๋ ์ฌ์ฉ์ ๊ฒฝํ์ ๋ณด์ฅํฉ๋๋ค.
3. ๋ฐ์ดํฐ๋ฒ ์ด์ค ํธ๋์ญ์ ๊ด๋ฆฌ
๋จ์ผ ์์ฒญ์ด ํจ๊ป ์ฑ๊ณตํ๊ฑฐ๋ ์คํจํด์ผ ํ๋ ์ฌ๋ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ธฐ๋ฅผ ์ํํด์ผ ํ ๋ ํธ๋์ญ์ ์ด ํ์ํฉ๋๋ค. ์์ฒญ ํธ๋ค๋ฌ์ ์์ ๋ถ๋ถ์์ ํธ๋์ญ์ ์ ์์ํ๊ณ , ํธ๋์ญ์ ํด๋ผ์ด์ธํธ๋ฅผ ๋น๋๊ธฐ ์ปจํ ์คํธ์ ์ ์ฅํ ๋ค์, ํด๋น ์์ฒญ ๋ด์ ๋ชจ๋ ํ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํธ์ถ์ด ์๋์ผ๋ก ๋์ผํ ํธ๋์ญ์ ํด๋ผ์ด์ธํธ๋ฅผ ์ฌ์ฉํ๋๋ก ํ ์ ์์ต๋๋ค. ํธ๋ค๋ฌ์ ๋์์ ๊ฒฐ๊ณผ์ ๋ฐ๋ผ ํธ๋์ญ์ ์ ์ปค๋ฐํ๊ฑฐ๋ ๋กค๋ฐฑํ ์ ์์ต๋๋ค.
4. ๊ธฐ๋ฅ ํ ๊ธ๋ง ๋ฐ A/B ํ ์คํ
์์ฒญ ์์ ์ ์ฌ์ฉ์๊ฐ ์ด๋ค ๊ธฐ๋ฅ ํ๋๊ทธ๋ A/B ํ ์คํธ ๊ทธ๋ฃน์ ์ํ๋์ง ๊ฒฐ์ ํ๊ณ ์ด ์ ๋ณด๋ฅผ ์ปจํ ์คํธ์ ์ ์ฅํ ์ ์์ต๋๋ค. API ๋ ์ด์ด๋ถํฐ ๋ ๋๋ง ๋ ์ด์ด๊น์ง ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ค๋ฅธ ๋ถ๋ถ๋ค์ ์ปจํ ์คํธ๋ฅผ ์ฐธ์กฐํ์ฌ ์ด๋ค ๋ฒ์ ์ ๊ธฐ๋ฅ์ ์คํํ ์ง ๋๋ ์ด๋ค UI๋ฅผ ํ์ํ ์ง ๊ฒฐ์ ํ ์ ์์ผ๋ฉฐ, ๋ณต์กํ ๋งค๊ฐ๋ณ์ ์ ๋ฌ ์์ด ๊ฐ์ธํ๋ ๊ฒฝํ์ ์์ฑํ ์ ์์ต๋๋ค.
์ฑ๋ฅ ๊ณ ๋ ค์ฌํญ ๋ฐ ๋ชจ๋ฒ ์ฌ๋ก
์ผ๋ฐ์ ์ธ ์ง๋ฌธ์ ์ฑ๋ฅ ์ค๋ฒํค๋๊ฐ ์ผ๋ง๋ ๋๋๊ฐ์
๋๋ค. Node.js ํต์ฌ ํ์ AsyncLocalStorage๋ฅผ ๋งค์ฐ ํจ์จ์ ์ผ๋ก ๋ง๋ค๊ธฐ ์ํด ์๋นํ ๋
ธ๋ ฅ์ ๊ธฐ์ธ์์ต๋๋ค. ์ด๊ฒ์ C++ ์์ค์ async_hooks API ์์ ๊ตฌ์ถ๋์์ผ๋ฉฐ V8 ์๋ฐ์คํฌ๋ฆฝํธ ์์ง๊ณผ ๊น์ด ํตํฉ๋์ด ์์ต๋๋ค. ๋๋ถ๋ถ์ ์น ์ ํ๋ฆฌ์ผ์ด์
์์ ์ฑ๋ฅ ์ํฅ์ ๋ฌด์ํ ์ ์์ผ๋ฉฐ, ์ฝ๋ ํ์ง๊ณผ ์ ์ง๋ณด์์ฑ์ ์์ฒญ๋ ์ด๋์ ์ํด ํจ์ฌ ๋ ์์๋ฉ๋๋ค.
์ด๋ฅผ ํจ๊ณผ์ ์ผ๋ก ์ฌ์ฉํ๋ ค๋ฉด ๋ค์ ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ๋ฐ๋ฅด์ญ์์ค:
- ์ฑ๊ธํด ์ธ์คํด์ค ์ฌ์ฉ: ์์ ์์ ๋ณด์ฌ์ค ๋ฐ์ ๊ฐ์ด, ์ผ๊ด์ฑ์ ๋ณด์ฅํ๊ธฐ ์ํด ์์ฒญ ์ปจํ
์คํธ์ ๋ํด ๋จ์ผ, ๋ด๋ณด๋ด๊ธฐ๋
AsyncLocalStorage์ธ์คํด์ค๋ฅผ ์์ฑํ์ญ์์ค. - ์ง์
์ ์์ ์ปจํ
์คํธ ์ค์ : ํญ์ ์ต์์ ๋ฏธ๋ค์จ์ด๋ ์์ฒญ ํธ๋ค๋ฌ์ ์์ ๋ถ๋ถ์์
als.run()์ ํธ์ถํ์ญ์์ค. ์ด๋ ์ปจํ ์คํธ์ ๋ํ ๋ช ํํ๊ณ ์์ธก ๊ฐ๋ฅํ ๊ฒฝ๊ณ๋ฅผ ๋ง๋ญ๋๋ค. - ์คํ ์ด๋ฅผ ๋ถ๋ณ(Immutable)์ผ๋ก ์ทจ๊ธ: ์คํ ์ด ๊ฐ์ฒด ์์ฒด๋ ๋ณ๊ฒฝ ๊ฐ๋ฅํ์ง๋ง, ๋ถ๋ณ์ผ๋ก ์ทจ๊ธํ๋ ๊ฒ์ด ์ข์ ๊ดํ์
๋๋ค. ์์ฒญ ์ค๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐํด์ผ ํ๋ ๊ฒฝ์ฐ, ๋ค๋ฅธ
run()ํธ์ถ๋ก ์ค์ฒฉ๋ ์ปจํ ์คํธ๋ฅผ ๋ง๋๋ ๊ฒ์ด ๋ ๊น๋ํ ์ ์์ง๋ง, ์ด๋ ๋ ๊ณ ๊ธ ํจํด์ ๋๋ค. - ์ปจํ
์คํธ๊ฐ ์๋ ๊ฒฝ์ฐ ์ฒ๋ฆฌ: ๋ก๊ฑฐ์์ ๋ณด์ฌ์ค ๊ฒ์ฒ๋ผ, ์ ํธ๋ฆฌํฐ๋ ํญ์
getStore()๊ฐundefined๋ฅผ ๋ฐํํ๋์ง ํ์ธํด์ผ ํฉ๋๋ค. ์ด๋ฅผ ํตํด ๋ฐฑ๊ทธ๋ผ์ด๋ ์คํฌ๋ฆฝํธ๋ ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ค์ ์คํ๋ ๋์ ๊ฐ์ด ์์ฒญ ์ปจํ ์คํธ ์ธ๋ถ์์ ์คํ๋ ๋ ์ ์์ ์ผ๋ก ์๋ํ ์ ์์ต๋๋ค. - ์ค๋ฅ ์ฒ๋ฆฌ๋ ๊ทธ๋ฅ ์๋ํฉ๋๋ค: ๋น๋๊ธฐ ์ปจํ
์คํธ๋
Promise์ฒด์ธ,.then()/.catch()/.finally()๋ธ๋ก ๋ฐasync/await์try/catch๋ฅผ ํตํด ์ฌ๋ฐ๋ฅด๊ฒ ์ ํ๋ฉ๋๋ค. ํน๋ณํ ์กฐ์น๋ฅผ ์ทจํ ํ์๊ฐ ์์ต๋๋ค. ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด ์ปจํ ์คํธ๋ ์ค๋ฅ ์ฒ๋ฆฌ ๋ก์ง์์ ๊ณ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
๊ฒฐ๋ก : Node.js ์ ํ๋ฆฌ์ผ์ด์ ์ ์๋ก์ด ์๋
AsyncLocalStorage๋ ํธ๋ฆฌํ ์ ํธ๋ฆฌํฐ ๊ทธ ์ด์์
๋๋ค. ์ด๋ ์๋ฒ์ฌ์ด๋ ์๋ฐ์คํฌ๋ฆฝํธ์์ ์ํ ๊ด๋ฆฌ์ ๋ํ ํจ๋ฌ๋ค์ ์ ํ์ ๋ํ๋
๋๋ค. ๊ณ ๋๋ก ๋์์ ์ธ ํ๊ฒฝ์์ ์์ฒญ ๋ฒ์ ์ปจํ
์คํธ๋ฅผ ๊ด๋ฆฌํ๋ ์ค๋๋ ๋ฌธ์ ์ ๋ํด ๊นจ๋ํ๊ณ , ๊ฒฌ๊ณ ํ๋ฉฐ, ์ฑ๋ฅ์ด ๋ฐ์ด๋ ํด๊ฒฐ์ฑ
์ ์ ๊ณตํฉ๋๋ค.
์ด API๋ฅผ ์์ฉํจ์ผ๋ก์จ ๋ค์์ ํ ์ ์์ต๋๋ค:
- Prop Drilling ์ ๊ฑฐ: ๋ ๊นจ๋ํ๊ณ ์ง์ค๋ ํจ์๋ฅผ ์์ฑํฉ๋๋ค.
- ๋ชจ๋์ ๋์ปคํ๋ง: ์์กด์ฑ์ ์ค์ด๊ณ ์ฝ๋๋ฅผ ๋ฆฌํฉํ ๋งํ๊ณ ํ ์คํธํ๊ธฐ ์ฝ๊ฒ ๋ง๋ญ๋๋ค.
- ๊ด์ฐฐ ๊ฐ๋ฅ์ฑ ํฅ์: ๊ฐ๋ ฅํ ๋ถ์ฐ ์ถ์ ๋ฐ ๋ฌธ๋งฅ์ ๋ก๊น ์ ์ฝ๊ฒ ๊ตฌํํฉ๋๋ค.
- ์ ๊ตํ ๊ธฐ๋ฅ ๊ตฌ์ถ: ํธ๋์ญ์ ๊ด๋ฆฌ ๋ฐ ๊ตญ์ ํ์ ๊ฐ์ ๋ณต์กํ ํจํด์ ๋จ์ํํฉ๋๋ค.
Node.js์์ ํ๋์ ์ด๊ณ , ํ์ฅ ๊ฐ๋ฅํ๋ฉฐ, ์ ์ธ๊ณ๋ฅผ ์ธ์ํ๋ ์ ํ๋ฆฌ์ผ์ด์
์ ๊ตฌ์ถํ๋ ๊ฐ๋ฐ์์๊ฒ ๋น๋๊ธฐ ์ปจํ
์คํธ๋ฅผ ๋ง์คํฐํ๋ ๊ฒ์ ๋ ์ด์ ์ ํ ์ฌํญ์ด ์๋๋ผ ํ์ ๊ธฐ์ ์
๋๋ค. ๊ตฌ์ ํจํด์ ๋์ด์ AsyncLocalStorage๋ฅผ ์ฑํํจ์ผ๋ก์จ, ๋ ํจ์จ์ ์ผ ๋ฟ๋ง ์๋๋ผ ํจ์ฌ ๋ ์ฐ์ํ๊ณ ์ ์ง๋ณด์ํ๊ธฐ ์ฌ์ด ์ฝ๋๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.