• ABOUT
  • POSTS
  • GUESTBOOK

ยฉ 2025 BlueCool12 All rights reserved.

2026.01.27JavaScript

๐Ÿ“ž ํ•œ ๋ˆˆ์— ์ •๋ฆฌํ•˜๋Š” JS ๋น„๋™๊ธฐ 3๋‹จ๊ณ„: Callback, Promise, Async/Await

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ์‹ฑ๊ธ€ ์Šค๋ ˆ๋“œ๋กœ ๋™์ž‘ํ•˜๋Š” ์–ธ์–ด์ด๋‹ค. ๋”ฐ๋ผ์„œ ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์˜ ์ž‘์—…๋งŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ์šฐ๋ฆฌ๊ฐ€ ์›น์„œํ•‘์„ ํ•  ๋•Œ ์ด๋ฏธ์ง€๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋ฉด์„œ ๋™์‹œ์— ํด๋ฆญ๋„ ํ•  ์ˆ˜ ์žˆ๋Š” ์ด์œ ๋Š” ๋ฐ”๋กœ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ ๋•๋ถ„์ด๋‹ค.

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ์ด ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋ฐœ์ „ํ•ด ์™”๋Š”์ง€ ํ๋ฆ„์„ ์•Œ์•„๋ณด์ž.


1. ๋น„๋™๊ธฐ์˜ ์‹œ์ž‘: ์ฝœ๋ฐฑ ํ•จ์ˆ˜(Callback)

์ฝœ๋ฐฑ ํ•จ์ˆ˜๋Š” ํŠน์ • ์ž‘์—…์ด ๋๋‚œ ๋’ค์— ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ๋‚˜์ค‘์— ํ˜ธ์ถœ๋  ํ•จ์ˆ˜๋ฅผ ์ธ์ž๋กœ ๋„˜๊ฒจ์ฃผ๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ์‹ฑ๊ธ€ ์Šค๋ ˆ๋“œ ์–ธ์–ด์ด๊ธฐ ๋•Œ๋ฌธ์— ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ์ž‘์—…(๋„คํŠธ์›Œํฌ ์š”์ฒญ, ํƒ€์ด๋จธ ๋“ฑ)์„ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ์ฝœ๋ฐฑ์„ ์‚ฌ์šฉํ•œ๋‹ค.

function fetchUser(id, callback) {
console.log('1. ์œ ์ € ์ฐพ๊ธฐ');

setTimeout(() => {
const user = { id: id, name: 'BlueCool' };
callback(user);
}, 2000);

console.log('2. ๋‹ค์Œ ์ž‘์—… ์ˆ˜ํ–‰');
}

fetchUser(1, (user) => {
console.log('3. ์œ ์ € ์ •๋ณด: ', user);
});โ€‹

์œ„ ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋˜๋ฉด ๋จผ์ € fetchUser๊ฐ€ ์‹คํ–‰๋˜๊ณ  setTimeout์„ ๋งŒ๋‚˜๋Š” ์ˆœ๊ฐ„ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์—”์ง„์€ ํƒ€์ด๋จธ๋ฅผ ๋ธŒ๋ผ์šฐ์ €(Web API)์— ๋งก๊ธด๋‹ค.

์ดํ›„ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ๋ฐ”๋กœ ๋‹ค์Œ ์ค„์ธ '2. ๋‹ค์Œ ์ž‘์—… ์ˆ˜ํ–‰'์„ ๋จผ์ € ์ถœ๋ ฅ ํ›„ 2์ดˆ๊ฐ€ ์ง€๋‚˜๋ฉด ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ ํ์— ๋„ฃ์–ด ์œ ์ € ์ •๋ณด๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค.


์ฝœ๋ฐฑ์˜ ํ•œ๊ณ„: ์ฝœ๋ฐฑ ์ง€์˜ฅ (Callback Hell)

์ฝœ๋ฐฑ์€ ์œ ์šฉํ•˜์ง€๋งŒ ๋น„๋™๊ธฐ ์ž‘์—…์ด ์—ฐ์†์ ์œผ๋กœ ํ•„์š”ํ•  ๋•Œ ์น˜๋ช…์ ์ธ ๋‹จ์ ์ด ์กด์žฌํ•œ๋‹ค. ์ด๋ฅผ ํ”ํžˆ '์ฝœ๋ฐฑ ์ง€์˜ฅ'์ด๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.

1) ๊ฐ€๋…์„ฑ ํŒŒ๊ดด
๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ  ๊ทธ ๋ฐ์ดํ„ฐ๋กœ ๋‹ค๋ฅธ ์š”์ฒญ์„ ํ•˜๊ณ  ๋˜ ๊ทธ ๊ฒฐ๊ณผ๋กœ ๋ฌด์–ธ๊ฐ€๋ฅผ ํ•ด์•ผ ํ•  ๋•Œ ์ฝ”๋“œ๋Š” ์˜ค๋ฅธ์ชฝ์œผ๋กœ ๋์—†์ด ๋ฐ€๋ ค๋‚˜๋Š” ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•œ๋‹ค.

// ์ฝœ๋ฐฑ ์ง€์˜ฅ
fetchUser(1, (user) => {
fetchPosts(user.id, (posts) => {
fetchComments(posts[0].id, (comments) => {
renderUI(comments, () => {
console.log('ํ™”๋ฉด ์—…๋ฐ์ดํŠธ...');
});
});
});
});

2) ์—๋Ÿฌ ์ฒ˜๋ฆฌ์˜ ์–ด๋ ค์›€
์ฝœ๋ฐฑ ๋ฐฉ์‹์€ ์—๋Ÿฌ ์ฒ˜๋ฆฌ๊ฐ€ ๋งค์šฐ ๋ฒˆ๊ฑฐ๋กญ๋‹ค. ๊ฐ ๋‹จ๊ณ„์˜ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋งˆ๋‹ค ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ ์ฒ˜๋ฆฌํ•  ๋กœ์ง์„ ์ค‘๋ณตํ•ด์„œ ์ž‘์„ฑํ•ด์•ผ ํ•˜๋ฉฐ ์—๋Ÿฌ๊ฐ€ ์–ด๋””์„œ ์‹œ์ž‘๋˜์—ˆ๋Š”์ง€๋„ ์ถ”์ ํ•˜๊ธฐ๊ฐ€ ๋งค์šฐ ํž˜๋“ค๋‹ค.

3) ์ œ์–ด๊ถŒ์˜ ์—ญ์ „ (Inversion of Control)
๊ตฌ์กฐ์ ์œผ๋กœ ์ฝœ๋ฐฑ์„ ๋‹ค๋ฅธ ํ•จ์ˆ˜(ex: ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ)์— ๋„˜๊ฒจ์ฃผ๋Š” ์ˆœ๊ฐ„, ๊ทธ ํ•จ์ˆ˜๊ฐ€ ๋‚ด ์ฝœ๋ฐฑ์„ ์–ธ์ œ, ์–ด๋–ป๊ฒŒ, ๋ช‡ ๋ฒˆ ์‹คํ–‰ํ• ์ง€์— ๋Œ€ํ•œ ์ œ์–ด๊ถŒ์ด ์ƒ๋Œ€๋ฐฉ์—๊ฒŒ ๋„˜์–ด๊ฐ€ ๋ฒ„๋ฆฐ๋‹ค.

์ด๋Š” ๋งŒ์•ฝ ์™ธ๋ถ€ ์‹œ์Šคํ…œ์˜ ์˜ค๋ฅ˜๋กœ ๋‚ด ์ฝœ๋ฐฑ์ด ๋‘ ๋ฒˆ ์‹คํ–‰๋˜๊ฑฐ๋‚˜ ํ˜น์€ ์•„์˜ˆ ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ํ”„๋กœ๊ทธ๋žจ์— ์น˜๋ช…์ ์ธ ์˜ค๋ฅ˜๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ์žˆ๋‹ค.

์ด๋Ÿฌํ•œ ๋‹จ์ ๋“ค์„ ๊ทน๋ณตํ•˜๊ธฐ ์œ„ํ•ด ๋“ฑ์žฅํ•œ ๊ฒƒ์ด ๋ฐ”๋กœ Promise์ด๋‹ค.


2. ํ”„๋กœ๋ฏธ์Šค(Promise)

ํ”„๋กœ๋ฏธ์Šค๋Š” ์ฝœ๋ฐฑ ์ง€์˜ฅ์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ES6์—์„œ ๋„์ž…๋œ ๋น„๋™๊ธฐ ์ž‘์—…์˜ ๊ฒฐ๊ณผ๋ฅผ ๋‹ด๋Š” ๊ฐ์ฒด์ด๋‹ค. ์ž‘์—…์ด ๋๋‚œ ํ›„ ์„ฑ๊ณต ํ˜น์€ ์‹คํŒจ์˜ ๊ฒฐ๊ณผ๋ฅผ ๋Œ๋ ค์ค€๋‹ค๋Š” ์•ฝ์†์„ ์˜๋ฏธํ•œ๋‹ค.

*ํ”„๋กœ๋ฏธ์Šค์˜ 3๊ฐ€์ง€ ์ƒํƒœ (Lifecycle)
ํ”„๋กœ๋ฏธ์Šค๋Š” ์ž‘์—…์˜ ์ง„ํ–‰ ์ƒํ™ฉ์— ๋”ฐ๋ผ ์„ธ ๊ฐ€์ง€ ์ƒํƒœ๋ฅผ ๊ฐ€์ง„๋‹ค.

  • Pending(๋Œ€๊ธฐ) - ๋น„๋™๊ธฐ ๋กœ์ง์ด ์•„์ง ์™„๋ฃŒ๋˜์ง€ ์•Š์€ ์ดˆ๊ธฐ ์ƒํƒœ
  • Fulfilled(์ดํ–‰) - resolve()๊ฐ€ ํ˜ธ์ถœ๋˜์–ด ์ž‘์—…์ด ์„ฑ๊ณต์ ์œผ๋กœ ๋๋‚œ ์ƒํƒœ
  • Rejected(๊ฑฐ๋ถ€) - reject()๊ฐ€ ํ˜ธ์ถœ๋˜์–ด ์ž‘์—… ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ ์ƒํƒœ
const getData = new Promise((resolve, reject) => {
const success = true;
if (success) resolve("์„ฑ๊ณต ๋ฐ์ดํ„ฐ");
else reject("์—๋Ÿฌ ๋ฐœ์ƒ");
})โ€‹;

getData
.then(data => console.log(data)) // ์„ฑ๊ณต ์‹œ ์‹คํ–‰
.catch(error => console.log(error)) // ์‹คํŒจ ์‹œ ์‹คํ–‰
.finally(() => console.log("์ข…๋ฃŒ")); // ๊ฒฐ๊ณผ์™€ ์ƒ๊ด€์—†์ด ๋ฌด์กฐ๊ฑด ์‹คํ–‰

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์—”์ง„์€ ํ”„๋กœ๋ฏธ์Šค ๊ฐ์ฒด ๋‚ด๋ถ€์— ์šฐ๋ฆฌ๊ฐ€ ์ง์ ‘ ์ ‘๊ทผํ•  ์ˆ˜ ์—†๋Š” Internal Slots๋ฅผ ๋‘์–ด ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•œ๋‹ค.

  • [[PromiseState]]: ํ˜„์žฌ ์ƒํƒœ(pending, fulfilled, rejected)๋ฅผ ์ €์žฅํ•œ๋‹ค.
  • [[PromiseResult]]: resolve()๋‚˜ reject()์— ๋„˜๊ฒจ์ค€ ๊ฒฐ๊ณผ ๋ฐ์ดํ„ฐ๊ฐ€ ๋‹ด๊ธด๋‹ค.


new Promise์˜ ์ธ์ž๋กœ ๋ฐ›๋Š” resolve์™€ reject๋Š” ์—”์ง„์ด ์ œ๊ณตํ•˜๋Š” ์Šค์œ„์น˜์ด๋‹ค.

  • resolve(data) ํ˜ธ์ถœ -> ์ƒํƒœ๋ฅผ fulfilled๋กœ ๋ณ€๊ฒฝ + [[PromiseResult]]์— ๋ฐ์ดํ„ฐ ์ €์žฅ
  • reject(error) ํ˜ธ์ถœ -> ์ƒํƒœ๋ฅผ rejected๋กœ ๋ณ€๊ฒฝ + [[PromiseResult]]์— ์—๋Ÿฌ ์ •๋ณด ์ €์žฅ


ํ”„๋กœ๋ฏธ์Šค์— ๋‹ด๊ธด ๋ฐ์ดํ„ฐ๋ฅผ promise.data์ฒ˜๋Ÿผ ๋ฐ”๋กœ ๊บผ๋‚ผ ์ˆ˜ ์—†๋Š” ์ด์œ ๋Š” ๋น„๋™๊ธฐ์„ฑ ๋•Œ๋ฌธ์ด๋‹ค. ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋˜๋Š” ์ƒํƒœ์—์„œ๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์•„์ง ๋„์ฐฉํ•˜์ง€ ์•Š์•˜์„ ๊ฐ€๋Šฅ์„ฑ์ด ํฌ๊ธฐ ๋•Œ๋ฌธ์— .then()์„ ์‚ฌ์šฉํ•œ๋‹ค.

.then()์€ ๊ฐ์ฒด์˜ ์ƒํƒœ๊ฐ€ fulfilled๊ฐ€ ๋˜๋Š” ์ˆœ๊ฐ„ ๋ฐ์ดํ„ฐ๋ฅผ ๊บผ๋‚ด ์ด ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•ด๋‹ฌ๋ผ๊ณ  ๋ฏธ๋ฆฌ ์˜ˆ์•ฝํ•˜๋Š” ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

ํ•ต์‹ฌ์€ ํ•œ๋ฒˆ Fulfilled ๋‚˜ Rejected ์ƒํƒœ๊ฐ€ ๋˜๋ฉด ์ ˆ๋Œ€ ๋‹ค๋ฅธ ์ƒํƒœ๋กœ ๋ณ€ํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์ด๋‹ค. ์ด๋Š” ์ฝœ๋ฐฑ ๋ฐฉ์‹์—์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์ค‘๋ณต ์‹คํ–‰๊ณผ ๊ฐ™์€ ๋ฌธ์ œ๋ฅผ ์›์ฒœ ์ฐจ๋‹จํ•œ๋‹ค.


ํ”„๋กœ๋ฏธ์Šค ์ฒด์ด๋‹ (Promise Chaining)

ํ”„๋กœ๋ฏธ์Šค์˜ ๊ฐ€์žฅ ํฐ ์žฅ์ ์€ .then()์„ ํ†ตํ•ด ๋น„๋™๊ธฐ ์ž‘์—…์„ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ด๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ฝœ๋ฐฑ ์ง€์˜ฅ์˜ ๊นŠ์€ ๋“ค์—ฌ ์“ฐ๊ธฐ๋ฅผ ์œ„์—์„œ ์•„๋ž˜๋กœ ํ๋ฅด๋Š” ์„ ํ˜•์ ์ธ ์ฝ”๋“œ๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋‹ค.

fetchUser(1)
.then(user => fetchPosts(user.id)) // ์œ ์ €๋ฅผ ๊ฐ€์ ธ์˜จ ํ›„ ํฌ์ŠคํŠธ ํ˜ธ์ถœ
.then(posts => fetchComments(posts[0].id)) // ํฌ์ŠคํŠธ๋ฅผ ๊ฐ€์ ธ์˜จ ํ›„ ๋Œ“๊ธ€ ํ˜ธ์ถœ
.then(comments => renderUI(comments)) // ๋ชจ๋“  ๋ฐ์ดํ„ฐ๊ฐ€ ์ค€๋น„๋˜๋ฉด ํ™”๋ฉด ๋ Œ๋”๋ง
.catch(error => console.error(error)) // ์ค‘๊ฐ„ ์–ด๋””์„œ๋“  ์—๋Ÿฌ๊ฐ€ ๋‚˜๋ฉด ์—ฌ๊ธฐ์„œ ์ฒ˜๋ฆฌ
.finally(() => console.log('์ž‘์—… ์™„๋ฃŒ'));

์ฝœ๋ฐฑ ํ•จ์ˆ˜์˜ ๋‹จ์ ์ด์—ˆ๋˜ ๊ฐ€๋…์„ฑ๊ณผ ์—๋Ÿฌ ์ฒ˜๋ฆฌ๋ฅผ ๋ชจ๋‘ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค. ๋˜ํ•œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ํ‘œ์ค€ ๋‚ด์žฅ ๊ฐ์ฒด์ด๋ฏ€๋กœ fetch, axios ๋“ฑ ํ˜„๋Œ€์ ์ธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์€ ๋ชจ๋‘ ์ด ํ”„๋กœ๋ฏธ์Šค๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค.


3. ๋น„๋™๊ธฐ์˜ ์™„์„ฑ: Async & Await

ํ”„๋กœ๋ฏธ์Šค๋ฅผ ํ†ตํ•ด ์ฝœ๋ฐฑ ์ง€์˜ฅ์„ ํ•ด๊ฒฐํ•˜์˜€์ง€๋งŒ .then() ์ฒด์ด๋‹์ด ๊ธธ์–ด์ง€๋ฉด ์—ฌ์ „ํžˆ ์ฝ”๋“œ์˜ ํ๋ฆ„์„ ํ•œ๋ˆˆ์— ํŒŒ์•…ํ•˜๊ธฐ ์–ด๋ ค์šธ ๋•Œ๊ฐ€ ์žˆ๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ES8์— ๋“ฑ์žฅํ•œ ๊ฒƒ์ด ๋ฐ”๋กœ async/await์ด๋‹ค.

๋น„๋™๊ธฐ ์ฝ”๋“œ๋ฅผ ๋งˆ์น˜ ๋™๊ธฐ ์ฝ”๋“œ์ฒ˜๋Ÿผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ํŽธ๋ฆฌํ•œ ๋ฌธ๋ฒ•์  ์„คํƒ•(Syntactic Sugar)์ด๋‹ค.

*ํ•ต์‹ฌ ๊ฐœ๋…

  • async: ํ•จ์ˆ˜ ์•ž์— ๋ถ™์ด๋ฉฐ, ์ด ํ‚ค์›Œ๋“œ๊ฐ€ ๋ถ™์€ ํ•จ์ˆ˜๋Š” ํ•ญ์ƒ ํ”„๋กœ๋ฏธ์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • await: async ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋ฉฐ ํ”„๋กœ๋ฏธ์Šค๊ฐ€ settled(์ดํ–‰ ๋˜๋Š” ๊ฑฐ๋ถ€) ์ƒํƒœ๊ฐ€ ๋  ๋•Œ๊นŒ์ง€ ํ•จ์ˆ˜์˜ ์‹คํ–‰์„ ์ž ์‹œ ์ผ์‹œ ์ •์ง€ํ•œ๋‹ค.
async function processData() {
try {
// await๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋น„๋™๊ธฐ ๊ฒฐ๊ณผ๋ฅผ ๋ณ€์ˆ˜์— ์ง์ ‘ ํ• ๋‹น
const user = await fetchUser(1); // 1. ์œ ์ € ์ •๋ณด ๋„์ฐฉ๊นŒ์ง€ ๋Œ€๊ธฐ
const posts = await fetchPosts(user.id); // 2. ํฌ์ŠคํŠธ ๋กœ๋“œ๊นŒ์ง€ ๋Œ€๊ธฐ
console.log(posts);
} catch (error) {
// .catch() ๋Œ€์‹  try-catch ๋ฌธ์œผ๋กœ ์—๋Ÿฌ ์ฒ˜๋ฆฌ
console.error('์—๋Ÿฌ ๋ฐœ์ƒ: ', error);
} finally {
console.log('๋ชจ๋“  ์ž‘์—… ์ข…๋ฃŒ');
}
}

์•ž์—์„œ ๋‹ค๋ค˜๋˜ .then()์ด ๋ฐ์ดํ„ฐ๊ฐ€ ์˜ค๋ฉด ์‹คํ–‰ํ•˜๋ผ๋Š” ์˜ˆ์•ฝ ์‹œ์Šคํ…œ์ด์—ˆ๋‹ค๋ฉด await๋Š” ํ”„๋กœ๋ฏธ์Šค๊ฐ€ fulfilled ์ƒํƒœ๊ฐ€ ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ ธ๋‹ค๊ฐ€ [[PromiseResult]]์— ๋‹ด๊ธด ๊ฐ’์„ ๊ทธ ์ž๋ฆฌ์—์„œ ๋ฐ”๋กœ ๊บผ๋‚ด์ฃผ๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

๋•๋ถ„์— ๋ฒˆ๊ฑฐ๋กœ์šด ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ์“ฐ์ง€ ์•Š๊ณ ๋„ ๋น„๋™๊ธฐ ๋ฐ์ดํ„ฐ๋ฅผ ์ผ๋ฐ˜ ๋ณ€์ˆ˜์— ๋‹ด๋“ฏ์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด์ „ ๊ธ€
๐ŸŒณ ๋น„์„ ํ˜• ์ž๋ฃŒ๊ตฌ์กฐ์˜ ํ•ต์‹ฌ - ํŠธ๋ฆฌ(Tree) ๊ตฌ์กฐ ์™„์ „ ์ •๋ณต
๋‹ค์Œ ๊ธ€
๐Ÿงฉ NestJS์™€ ๋ชจ๋“ˆ ์‹œ์Šคํ…œ ์ดํ•ดํ•˜๊ธฐ
์žฅ์‹์šฉ ๋กœ๊ณ