Node.js ๋ฐฑ์—”๋“œ ๋ฉด์ ‘ ์งˆ๋ฌธ: ์™„๋ฒฝ ๊ฐ€์ด๋“œ 2026

Node.js ๋ฐฑ์—”๋“œ ๋ฉด์ ‘์—์„œ ๊ฐ€์žฅ ์ž์ฃผ ๋‚˜์˜ค๋Š” 25๊ฐ€์ง€ ์งˆ๋ฌธ. Event loop, async/await, streams, ํด๋Ÿฌ์Šคํ„ฐ๋ง, ์„ฑ๋Šฅ์„ ์ƒ์„ธํ•œ ๋‹ต๋ณ€๊ณผ ํ•จ๊ป˜ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

Node.js ๋ฐฑ์—”๋“œ ๋ฉด์ ‘ ์งˆ๋ฌธ - ์™„๋ฒฝ ๊ฐ€์ด๋“œ

Node.js ๋ฐฑ์—”๋“œ ๋ฉด์ ‘์—์„œ๋Š” ๋Ÿฐํƒ€์ž„ ๋‚ด๋ถ€ ๋ฉ”์ปค๋‹ˆ์ฆ˜์˜ ์ดํ•ด, ๋น„๋™๊ธฐ ํŒจํ„ด์˜ ์ˆ™๋‹ฌ, ๊ณ ์„ฑ๋Šฅ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ค๊ณ„ ๋Šฅ๋ ฅ์ด ํ‰๊ฐ€๋ฉ๋‹ˆ๋‹ค. ์ด ๊ฐ€์ด๋“œ๋Š” ๊ธฐ์ดˆ๋ถ€ํ„ฐ ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์˜ ๊ณ ๊ธ‰ ๊ฐœ๋…๊นŒ์ง€ ๊ฐ€์žฅ ์ž์ฃผ ์ถœ์ œ๋˜๋Š” ์งˆ๋ฌธ์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค.

๋ฉด์ ‘ ํŒ

์ฑ„์šฉ ๋‹ด๋‹น์ž๋Š” ์ด๋ก ๊ณผ ์‹ค์ „ ์˜ˆ์ œ๋ฅผ ๊ฒฐํ•ฉํ•œ ๋‹ต๋ณ€์„ ๋†’์ด ํ‰๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ ์งˆ๋ฌธ์— ๋Œ€ํ•ด ์ฝ”๋“œ๋‚˜ ๊ตฌ์ฒด์ ์ธ ์‚ฌ์šฉ ์‚ฌ๋ก€๋กœ ์„ค๋ช…ํ•˜๋ฉด ์‹ค๋ฌด ๊ฒฝํ—˜์ด ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค.

Node.js ๊ธฐ์ดˆ

์งˆ๋ฌธ 1: Event Loop๋ž€ ๋ฌด์—‡์ด๋ฉฐ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”๊ฐ€?

Event Loop๋Š” Node.js๊ฐ€ ์‹ฑ๊ธ€ ์Šค๋ ˆ๋“œ๋กœ ๋™์ž‘ํ•˜๋ฉด์„œ๋„ ๋น„๋™๊ธฐ ์ž‘์—…์„ ๋…ผ๋ธ”๋กœํ‚น ๋ฐฉ์‹์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋Š” ํ•ต์‹ฌ ๋ฉ”์ปค๋‹ˆ์ฆ˜์ž…๋‹ˆ๋‹ค. JavaScript ์ฝ”๋“œ, ์ฝœ๋ฐฑ, ์ด๋ฒคํŠธ์˜ ์‹คํ–‰์„ ์กฐ์œจํ•ฉ๋‹ˆ๋‹ค.

event-loop-demo.jsjavascript
// Demonstration of Event Loop execution order

console.log('1. Script start (synchronous)');

// setTimeout goes to the Timer Queue
setTimeout(() => {
  console.log('5. setTimeout callback (Timer Queue)');
}, 0);

// setImmediate goes to the Check Queue
setImmediate(() => {
  console.log('6. setImmediate callback (Check Queue)');
});

// Promise goes to the Microtask Queue (priority)
Promise.resolve().then(() => {
  console.log('3. Promise.then (Microtask Queue)');
});

// process.nextTick has the highest priority
process.nextTick(() => {
  console.log('2. process.nextTick (nextTick Queue)');
});

console.log('4. Script end (synchronous)');

// Output order: 1, 4, 2, 3, 5, 6

Event Loop๋Š” ํ๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ ์ •ํ™•ํ•œ ์ˆœ์„œ๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค. ๋จผ์ € ๋™๊ธฐ ์ฝ”๋“œ, ๊ทธ ๋‹ค์Œ nextTick, ๋งˆ์ดํฌ๋กœํƒœ์Šคํฌ(Promise), ํƒ€์ด๋จธ, I/O ์ฝœ๋ฐฑ, setImmediate, ๋งˆ์ง€๋ง‰์œผ๋กœ ๋‹ซ๊ธฐ ์ฝœ๋ฐฑ ์ˆœ์ž…๋‹ˆ๋‹ค.

์งˆ๋ฌธ 2: process.nextTick()๊ณผ setImmediate()์˜ ์ฐจ์ด์ ์€?

์ด ์งˆ๋ฌธ์€ Event Loop์—์„œ์˜ ์‹คํ–‰ ์šฐ์„ ์ˆœ์œ„์— ๋Œ€ํ•œ ์„ธ๋ฐ€ํ•œ ์ดํ•ด๋ฅผ ํ‰๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

nextTick-vs-immediate.jsjavascript
// Behavior comparison

// process.nextTick executes BEFORE the next Event Loop phase
process.nextTick(() => {
  console.log('nextTick 1');
  process.nextTick(() => {
    console.log('nextTick 2 (nested)');
  });
});

// setImmediate executes in the Check phase of the Event Loop
setImmediate(() => {
  console.log('setImmediate 1');
  setImmediate(() => {
    console.log('setImmediate 2 (nested)');
  });
});

// Output: nextTick 1, nextTick 2, setImmediate 1, setImmediate 2

process.nextTick()์€ ํ˜„์žฌ ์ž‘์—… ์งํ›„, Event Loop๊ฐ€ ๊ณ„์†๋˜๊ธฐ ์ „์— ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค. ๊ณผ๋„ํ•œ ์‚ฌ์šฉ์€ Event Loop๋ฅผ ์ฐจ๋‹จํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. setImmediate()๋Š” ๋” ์˜ˆ์ธก ๊ฐ€๋Šฅํ•˜๋ฉฐ ์‹คํ–‰ ์ง€์—ฐ์— ๊ถŒ์žฅ๋ฉ๋‹ˆ๋‹ค.

๊ธฐ์•„ ์ƒํƒœ ์ฃผ์˜

process.nextTick()์˜ ์žฌ๊ท€ ํ˜ธ์ถœ์€ Event Loop๋ฅผ ๊ณ ๊ฐˆ์‹œํ‚ค๊ณ  I/O ์ฒ˜๋ฆฌ๋ฅผ ๋ฐฉํ•ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ค‘์š”ํ•˜์ง€ ์•Š์€ ์ž‘์—…์—๋Š” setImmediate() ์‚ฌ์šฉ์ด ๊ถŒ์žฅ๋ฉ๋‹ˆ๋‹ค.

์งˆ๋ฌธ 3: Node.js๋Š” ๋น„๋™๊ธฐ ์ฝ”๋“œ์—์„œ ์˜ค๋ฅ˜๋ฅผ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋Š”๊ฐ€?

๋น„๋™๊ธฐ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ๋Š” ๋™๊ธฐ ์ฝ”๋“œ์™€ ๊ทผ๋ณธ์ ์œผ๋กœ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ์ ์ ˆํ•œ ์ฒ˜๋ฆฌ ์—†์ด๋Š” ์˜ค๋ฅ˜๊ฐ€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ํฌ๋ž˜์‹œ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

error-handling.jsjavascript
// Asynchronous error handling patterns

// Pattern 1: Callbacks with error-first convention
function readFileCallback(path, callback) {
  const fs = require('fs');
  fs.readFile(path, 'utf8', (err, data) => {
    if (err) {
      // Error is ALWAYS the first argument
      return callback(err, null);
    }
    callback(null, data);
  });
}

// Pattern 2: Promises with catch
async function readFilePromise(path) {
  const fs = require('fs').promises;
  try {
    const data = await fs.readFile(path, 'utf8');
    return data;
  } catch (err) {
    // Centralized error handling
    console.error(`File read error: ${err.message}`);
    throw err; // Re-throw for propagation
  }
}

// Pattern 3: Global handling of unhandled rejections
process.on('unhandledRejection', (reason, promise) => {
  console.error('Unhandled rejection:', reason);
  // In production: log and graceful shutdown
});

// Pattern 4: Handling uncaught exceptions
process.on('uncaughtException', (err) => {
  console.error('Uncaught exception:', err);
  // CRITICAL: always terminate the process after
  process.exit(1);
});

ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ ๋ชจ๋“  Promise๋Š” .catch()๋ฅผ ๊ฐ–๊ฑฐ๋‚˜ try/catch ๋ธ”๋ก ์•ˆ์— ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ธ€๋กœ๋ฒŒ ํ•ธ๋“ค๋Ÿฌ๋Š” ์•ˆ์ „๋ง ์—ญํ• ์„ ํ•˜๋ฉฐ ์ฃผ์š” ์†”๋ฃจ์…˜์ด ์•„๋‹™๋‹ˆ๋‹ค.

๋น„๋™๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ๊ณผ ๋™์‹œ์„ฑ

์งˆ๋ฌธ 4: Node.js์—์„œ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ์™€ ๋™์‹œ ์ฒ˜๋ฆฌ์˜ ์ฐจ์ด๋ฅผ ์„ค๋ช…ํ•˜๋ผ

Node.js๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋™์‹œ์ ์ด์ง€๋งŒ ๋ณ‘๋ ฌ์ ์ด์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค. ์ด ๊ตฌ๋ถ„์€ ์„ฑ๋Šฅ ์ดํ•ด์— ํ•„์ˆ˜์ ์ž…๋‹ˆ๋‹ค.

concurrency-vs-parallelism.jsjavascript
// CONCURRENCY: multiple tasks progress by alternating (single-thread)
async function concurrentTasks() {
  console.time('concurrent');

  // These calls are concurrent, not parallel
  const results = await Promise.all([
    fetch('https://api.example.com/users'),    // Non-blocking I/O
    fetch('https://api.example.com/products'), // Non-blocking I/O
    fetch('https://api.example.com/orders'),   // Non-blocking I/O
  ]);

  console.timeEnd('concurrent'); // ~time of the longest request
  return results;
}

// PARALLELISM: with Worker Threads for CPU-bound tasks
const { Worker, isMainThread, parentPort } = require('worker_threads');

if (isMainThread) {
  // Main thread delegates CPU-intensive work
  async function parallelComputation() {
    console.time('parallel');

    const workers = [
      createWorker({ start: 0, end: 1000000 }),
      createWorker({ start: 1000000, end: 2000000 }),
      createWorker({ start: 2000000, end: 3000000 }),
    ];

    const results = await Promise.all(workers);
    console.timeEnd('parallel');
    return results.reduce((a, b) => a + b, 0);
  }

  function createWorker(data) {
    return new Promise((resolve, reject) => {
      const worker = new Worker(__filename, { workerData: data });
      worker.on('message', resolve);
      worker.on('error', reject);
    });
  }
} else {
  // Code executed in the Worker Thread
  const { workerData } = require('worker_threads');
  let sum = 0;
  for (let i = workerData.start; i < workerData.end; i++) {
    sum += Math.sqrt(i); // CPU-intensive calculation
  }
  parentPort.postMessage(sum);
}

I/O ๋ฐ”์šด๋“œ ์ž‘์—…(๋„คํŠธ์›Œํฌ, ํŒŒ์ผ)์—๋Š” ๋„ค์ดํ‹ฐ๋ธŒ ๋™์‹œ์„ฑ์œผ๋กœ ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. CPU ๋ฐ”์šด๋“œ ์ž‘์—…(๋ฌด๊ฑฐ์šด ๊ณ„์‚ฐ, ์•”ํ˜ธํ™”)์—๋Š” Worker Threads๊ฐ€ ์ง„์ •ํ•œ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ๋ฅผ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

์งˆ๋ฌธ 5: Cluster ๋ชจ๋“ˆ์€ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”๊ฐ€?

Cluster ๋ชจ๋“ˆ์€ ๊ฐ™์€ ํฌํŠธ๋ฅผ ๊ณต์œ ํ•˜๋Š” ์—ฌ๋Ÿฌ Node.js ํ”„๋กœ์„ธ์Šค๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋ชจ๋“  CPU ์ฝ”์–ด๋ฅผ ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค.

cluster-example.jsjavascript
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if (cluster.isPrimary) {
  console.log(`Primary ${process.pid} is running`);
  console.log(`Forking ${numCPUs} workers...`);

  // Fork one worker per CPU core
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  // Handle crashing workers
  cluster.on('exit', (worker, code, signal) => {
    console.log(`Worker ${worker.process.pid} died (${signal || code})`);
    console.log('Starting a new worker...');
    cluster.fork(); // Automatic restart
  });

  // Inter-process communication
  cluster.on('message', (worker, message) => {
    console.log(`Message from worker ${worker.id}:`, message);
  });

} else {
  // Workers share the TCP port
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end(`Handled by worker ${process.pid}\n`);

    // Send stats to primary
    process.send({ type: 'request', pid: process.pid });
  }).listen(8000);

  console.log(`Worker ${process.pid} started`);
}

๋กœ๋“œ ๋ฐธ๋Ÿฐ์‹ฑ์€ ์šด์˜์ฒด์ œ๊ฐ€ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค(Linux/macOS์—์„œ ๋ผ์šด๋“œ ๋กœ๋นˆ). ํ”„๋กœ๋•์…˜์—์„œ๋Š” PM2๊ฐ€ ๋‚ด์žฅ ํด๋Ÿฌ์Šคํ„ฐ ๋ชจ๋“œ๋กœ ์ด ๊ด€๋ฆฌ๋ฅผ ๊ฐ„์†Œํ™”ํ•ฉ๋‹ˆ๋‹ค.

Node.js / NestJS ๋ฉด์ ‘ ์ค€๋น„๊ฐ€ ๋˜์…จ๋‚˜์š”?

์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ, flashcards, ๊ธฐ์ˆ  ํ…Œ์ŠคํŠธ๋กœ ์—ฐ์Šตํ•˜์„ธ์š”.

Streams์™€ Buffers

์งˆ๋ฌธ 6: ํด๋ž˜์‹ ๋ฉ”์„œ๋“œ ๋Œ€์‹  Streams๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๋Š”?

Streams๋Š” ๋ชจ๋“  ๊ฒƒ์„ ๋ฉ”๋ชจ๋ฆฌ์— ๋กœ๋“œํ•˜๋Š” ๋Œ€์‹  ์ฒญํฌ ๋‹จ์œ„๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๋Œ€์šฉ๋Ÿ‰ ํŒŒ์ผ๊ณผ ์ŠคํŠธ๋ฆฌ๋ฐ ์‹œ๋‚˜๋ฆฌ์˜ค์— ํ•„์ˆ˜์ ์ž…๋‹ˆ๋‹ค.

streams-comparison.jsjavascript
const fs = require('fs');

// โŒ BAD: loads entire file into memory
async function readEntireFile(path) {
  const data = await fs.promises.readFile(path); // Blocks if file > RAM
  return processData(data);
}

// โœ… GOOD: chunk-based processing with Stream
function readWithStream(path) {
  return new Promise((resolve, reject) => {
    const chunks = [];
    const readStream = fs.createReadStream(path, {
      highWaterMark: 64 * 1024, // 64KB per chunk
    });

    readStream.on('data', (chunk) => {
      // Progressive processing, constant memory
      chunks.push(processChunk(chunk));
    });

    readStream.on('end', () => resolve(chunks));
    readStream.on('error', reject);
  });
}

// โœ… BEST: pipeline for chaining transformations
const { pipeline } = require('stream/promises');
const zlib = require('zlib');

async function compressFile(input, output) {
  await pipeline(
    fs.createReadStream(input),   // Source
    zlib.createGzip(),            // Transform
    fs.createWriteStream(output)  // Destination
  );
  // Automatic error handling and backpressure management
}

๋ฐ์ดํ„ฐ ํฌ๊ธฐ๊ฐ€ ์ˆ˜ MB๋ฅผ ์ดˆ๊ณผํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ, ๋˜๋Š” ์‹ค์‹œ๊ฐ„ ์ฒ˜๋ฆฌ(์—…๋กœ๋“œ, ๋กœ๊ทธ, ๋„คํŠธ์›Œํฌ ๋ฐ์ดํ„ฐ)์— Streams ์‚ฌ์šฉ์ด ๊ถŒ์žฅ๋ฉ๋‹ˆ๋‹ค.

์งˆ๋ฌธ 7: ๋ฐฑํ”„๋ ˆ์…” ๊ฐœ๋…์„ ์„ค๋ช…ํ•˜๋ผ

๋ฐฑํ”„๋ ˆ์…”๋Š” ๋ฐ์ดํ„ฐ ์ƒ์‚ฐ์ž๊ฐ€ ์†Œ๋น„์ž๋ณด๋‹ค ๋น ๋ฅผ ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๊ด€๋ฆฌํ•˜์ง€ ์•Š์œผ๋ฉด ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ํญ๋ฐœํ•ฉ๋‹ˆ๋‹ค.

backpressure-demo.jsjavascript
const fs = require('fs');

// โŒ Problem: no backpressure handling
function badCopy(src, dest) {
  const readable = fs.createReadStream(src);
  const writable = fs.createWriteStream(dest);

  readable.on('data', (chunk) => {
    // If write() returns false, the internal buffer is full
    // But here reading continues anyway โ†’ memory leak
    writable.write(chunk);
  });
}

// โœ… Solution: respect the writable signal
function goodCopy(src, dest) {
  const readable = fs.createReadStream(src);
  const writable = fs.createWriteStream(dest);

  readable.on('data', (chunk) => {
    const canContinue = writable.write(chunk);

    if (!canContinue) {
      // Pause reading until buffer drains
      readable.pause();
    }
  });

  writable.on('drain', () => {
    // Buffer drained, resume reading
    readable.resume();
  });

  readable.on('end', () => writable.end());
}

// โœ… BEST: pipe() handles everything automatically
function bestCopy(src, dest) {
  const readable = fs.createReadStream(src);
  const writable = fs.createWriteStream(dest);

  // pipe() handles backpressure natively
  readable.pipe(writable);
}

pipe() ๋˜๋Š” pipeline() ๋ฉ”์„œ๋“œ๊ฐ€ ๋ฐฑํ”„๋ ˆ์…”๋ฅผ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๋ณต์žกํ•œ ๊ฒฝ์šฐ์—๋Š” pause/resume ๋กœ์ง์„ ์ˆ˜๋™์œผ๋กœ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

์„ฑ๋Šฅ๊ณผ ์ตœ์ ํ™”

์งˆ๋ฌธ 8: ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ์‹๋ณ„ํ•˜๊ณ  ์ˆ˜์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์€?

๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋Š” Node.js์—์„œ ํ”ํžˆ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ํ”„๋กœ๋•์…˜์—์„œ ๊ฐ์ง€ํ•˜๊ณ  ์ˆ˜์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•„๋Š” ๊ฒƒ์ด ํ•„์ˆ˜์ ์ž…๋‹ˆ๋‹ค.

memory-leak-patterns.jsjavascript
// โŒ Leak 1: closures that retain references
function createLeakyHandler() {
  const hugeData = Buffer.alloc(100 * 1024 * 1024); // 100MB

  return function handler(req, res) {
    // hugeData remains in memory as long as handler exists
    res.end('Hello');
  };
}

// โœ… Fix: limit the scope
function createSafeHandler() {
  return function handler(req, res) {
    // Data created and released on each request
    const data = fetchData();
    res.end(data);
  };
}

// โŒ Leak 2: event listeners not cleaned up
class LeakyClass {
  constructor() {
    // Added on each instantiation, never removed
    process.on('message', this.handleMessage);
  }
  handleMessage(msg) { /* ... */ }
}

// โœ… Fix: explicit cleanup
class SafeClass {
  constructor() {
    this.boundHandler = this.handleMessage.bind(this);
    process.on('message', this.boundHandler);
  }
  handleMessage(msg) { /* ... */ }

  destroy() {
    // Mandatory cleanup
    process.removeListener('message', this.boundHandler);
  }
}

// Diagnostics with native tools
function diagnoseMemory() {
  const used = process.memoryUsage();
  console.log({
    heapUsed: `${Math.round(used.heapUsed / 1024 / 1024)}MB`,
    heapTotal: `${Math.round(used.heapTotal / 1024 / 1024)}MB`,
    external: `${Math.round(used.external / 1024 / 1024)}MB`,
    rss: `${Math.round(used.rss / 1024 / 1024)}MB`,
  });
}

// Enable manual garbage collector for testing
// node --expose-gc app.js
if (global.gc) {
  global.gc();
  diagnoseMemory();
}

ํ”„๋กœ๋•์…˜์—์„œ๋Š” clinic.js, Chrome DevTools์˜ ํž™ ์Šค๋ƒ…์ƒท, ๋˜๋Š” DataDog์ด๋‚˜ New Relic ๊ฐ™์€ APM ์†”๋ฃจ์…˜์ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

์งˆ๋ฌธ 9: Node.js API ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ•์€?

์ด ์งˆ๋ฌธ์€ ์—ฌ๋Ÿฌ ์ˆ˜์ค€์˜ ์ตœ์ ํ™” ๊ธฐ๋ฒ•์— ๋Œ€ํ•œ ์ง€์‹์„ ํ‰๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

performance-optimization.jsjavascript
// 1. CACHING: reduce expensive calls
const NodeCache = require('node-cache');
const cache = new NodeCache({ stdTTL: 300 }); // 5-minute TTL

async function getCachedUser(id) {
  const cacheKey = `user:${id}`;
  let user = cache.get(cacheKey);

  if (!user) {
    user = await db.users.findById(id);
    cache.set(cacheKey, user);
  }

  return user;
}

// 2. CONNECTION POOLING: reuse DB connections
const { Pool } = require('pg');
const pool = new Pool({
  max: 20,                // Max simultaneous connections
  idleTimeoutMillis: 30000,
  connectionTimeoutMillis: 2000,
});

// 3. COMPRESSION: reduce response size
const compression = require('compression');
app.use(compression({
  filter: (req, res) => {
    // Only compress if > 1KB
    return compression.filter(req, res);
  },
  threshold: 1024,
}));

// 4. BATCHING: group operations
async function batchInsert(items) {
  const BATCH_SIZE = 1000;

  for (let i = 0; i < items.length; i += BATCH_SIZE) {
    const batch = items.slice(i, i + BATCH_SIZE);
    await db.items.insertMany(batch);
  }
}

// 5. LAZY LOADING: load on demand
async function getUserWithPosts(userId, includePosts = false) {
  const user = await db.users.findById(userId);

  if (includePosts) {
    user.posts = await db.posts.findByUserId(userId);
  }

  return user;
}

์ตœ์ ํ™”๋Š” ํ”„๋กœํŒŒ์ผ๋ง์— ๊ธฐ๋ฐ˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ตœ์ ํ™” ์ „์— ์ธก์ •ํ•˜์—ฌ ์‹ค์ œ ๋ณ‘๋ชฉ ์ง€์ ์„ ์‹๋ณ„ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

80/20 ๋ฒ•์น™

์„ฑ๋Šฅ ๋ฌธ์ œ์˜ 80%๋Š” ์ฝ”๋“œ์˜ 20%์—์„œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ํ”„๋กœํŒŒ์ผ๋ง์„ ํ†ตํ•ด ๋งน๋ชฉ์ ์œผ๋กœ ์ตœ์ ํ™”ํ•˜๊ธฐ ์ „์— ์ด๋Ÿฌํ•œ ์ค‘์š” ์˜์—ญ์„ ์‹๋ณ„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ณด์•ˆ

์งˆ๋ฌธ 10: Node.js API๋ฅผ ์ผ๋ฐ˜์ ์ธ ๊ณต๊ฒฉ์œผ๋กœ๋ถ€ํ„ฐ ๋ณดํ˜ธํ•˜๋Š” ๋ฐฉ๋ฒ•์€?

๋ณด์•ˆ์€ ๋ฉด์ ‘์—์„œ ๋ฐ˜๋ณต์ ์œผ๋กœ ๋‚˜์˜ค๋Š” ์ฃผ์ œ์ž…๋‹ˆ๋‹ค. OWASP ์ทจ์•ฝ์ ์— ๋Œ€ํ•œ ์ง€์‹์„ ๋ณด์—ฌ์ฃผ๋Š” ๊ฒƒ์ด ๊ธฐ๋Œ€๋ฉ๋‹ˆ๋‹ค.

security-best-practices.jsjavascript
const express = require('express');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const mongoSanitize = require('express-mongo-sanitize');
const xss = require('xss-clean');

const app = express();

// 1. SECURITY HEADERS with Helmet
app.use(helmet());

// 2. RATE LIMITING against brute-force attacks
const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100,                  // 100 requests per IP
  message: 'Too many requests, please try again later',
  standardHeaders: true,
  legacyHeaders: false,
});
app.use('/api/', limiter);

// 3. SANITIZATION against NoSQL injections
app.use(mongoSanitize());

// 4. XSS PROTECTION
app.use(xss());

// 5. STRICT INPUT VALIDATION
const { body, validationResult } = require('express-validator');

app.post('/api/users',
  [
    body('email').isEmail().normalizeEmail(),
    body('password').isLength({ min: 8 }).escape(),
    body('name').trim().escape(),
  ],
  (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }
    // Continue processing
  }
);

// 6. SQL INJECTION PROTECTION (with parameters)
async function safeQuery(userId) {
  // โœ… Parameterized query
  const result = await pool.query(
    'SELECT * FROM users WHERE id = $1',
    [userId]
  );
  return result.rows;
}

// โŒ NEVER string concatenation
async function unsafeQuery(userId) {
  // Vulnerable to SQL injection
  const result = await pool.query(
    `SELECT * FROM users WHERE id = ${userId}`
  );
}

ํ”„๋กœ๋•์…˜์—์„œ๋Š” ์ œํ•œ์ ์ธ CORS, ํ•„์ˆ˜ HTTPS, ๋ณด์•ˆ ๋กœ๊น…, ์‹œํฌ๋ฆฟ ๋กœํ…Œ์ด์…˜, ์ •๊ธฐ์ ์ธ ์˜์กด์„ฑ ๊ฐ์‚ฌ(npm audit)๋„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์•„ํ‚คํ…์ฒ˜์™€ ๋””์ž์ธ ํŒจํ„ด

์งˆ๋ฌธ 11: Node.js์—์„œ Repository ํŒจํ„ด์„ ์„ค๋ช…ํ•˜๋ผ

Repository ํŒจํ„ด์€ ๋ฐ์ดํ„ฐ ์ ‘๊ทผ์„ ์ถ”์ƒํ™”ํ•˜๊ณ  ํ…Œ์ŠคํŠธ์™€ ์œ ์ง€๋ณด์ˆ˜๋ฅผ ์šฉ์ดํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

repository-pattern.jsjavascript
// Abstract interface (for TypeScript, or documentation)
class UserRepository {
  async findById(id) { throw new Error('Not implemented'); }
  async findByEmail(email) { throw new Error('Not implemented'); }
  async create(userData) { throw new Error('Not implemented'); }
  async update(id, userData) { throw new Error('Not implemented'); }
  async delete(id) { throw new Error('Not implemented'); }
}

// Concrete implementation with Prisma
class PrismaUserRepository extends UserRepository {
  constructor(prisma) {
    super();
    this.prisma = prisma;
  }

  async findById(id) {
    return this.prisma.user.findUnique({ where: { id } });
  }

  async findByEmail(email) {
    return this.prisma.user.findUnique({ where: { email } });
  }

  async create(userData) {
    return this.prisma.user.create({ data: userData });
  }

  async update(id, userData) {
    return this.prisma.user.update({
      where: { id },
      data: userData,
    });
  }

  async delete(id) {
    return this.prisma.user.delete({ where: { id } });
  }
}

// Implementation for testing
class InMemoryUserRepository extends UserRepository {
  constructor() {
    super();
    this.users = new Map();
    this.idCounter = 1;
  }

  async findById(id) {
    return this.users.get(id) || null;
  }

  async create(userData) {
    const user = { id: this.idCounter++, ...userData };
    this.users.set(user.id, user);
    return user;
  }
  // ... other methods
}

// Service using the repository (dependency injection)
class UserService {
  constructor(userRepository) {
    this.userRepository = userRepository;
  }

  async getUser(id) {
    const user = await this.userRepository.findById(id);
    if (!user) throw new Error('User not found');
    return user;
  }
}

์ด ํŒจํ„ด์„ ํ†ตํ•ด ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ˆ˜์ •ํ•˜์ง€ ์•Š๊ณ  ์˜์†์„ฑ ๊ตฌํ˜„์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์งˆ๋ฌธ 12: ์ž‘์—… ํ ์‹œ์Šคํ…œ์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์€?

ํ๋Š” ๋ฌด๊ฑฐ์šด ์ž‘์—…์„ ์ง€์—ฐ์‹œํ‚ค๊ณ  ์•ˆ์ •์ ์ธ ์‹คํ–‰์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.

job-queue.jsjavascript
const Queue = require('bull');

// Create queue with Redis as backend
const emailQueue = new Queue('email', {
  redis: {
    host: 'localhost',
    port: 6379,
  },
  defaultJobOptions: {
    attempts: 3,          // Number of attempts
    backoff: {
      type: 'exponential',
      delay: 2000,        // Initial delay between attempts
    },
    removeOnComplete: 100, // Keep last 100 completed jobs
  },
});

// Producer: add jobs to the queue
async function sendWelcomeEmail(userId, email) {
  await emailQueue.add('welcome', {
    userId,
    email,
    template: 'welcome',
  }, {
    priority: 1,          // High priority
    delay: 5000,          // 5-second delay
  });
}

// Consumer: process jobs
emailQueue.process('welcome', async (job) => {
  const { userId, email, template } = job.data;

  // Update progress
  job.progress(10);

  const html = await renderTemplate(template, { userId });
  job.progress(50);

  await sendEmail(email, 'Welcome!', html);
  job.progress(100);

  return { sent: true, email };
});

// Event handling
emailQueue.on('completed', (job, result) => {
  console.log(`Job ${job.id} completed:`, result);
});

emailQueue.on('failed', (job, err) => {
  console.error(`Job ${job.id} failed:`, err.message);
});

// Recurring jobs (cron)
emailQueue.add('newsletter', { type: 'weekly' }, {
  repeat: {
    cron: '0 9 * * MON', // Every Monday at 9am
  },
});

Bull + Redis๊ฐ€ ๊ฐ€์žฅ ์ธ๊ธฐ ์žˆ๋Š” ์†”๋ฃจ์…˜์ž…๋‹ˆ๋‹ค. ๋” ๊ฐ„๋‹จํ•œ ์š”๊ตฌ์‚ฌํ•ญ์—๋Š” agenda๋‚˜ bee-queue๊ฐ€ ๊ฒฝ๋Ÿ‰ ๋Œ€์•ˆ์ž…๋‹ˆ๋‹ค.

๊ณ ๊ธ‰ ์งˆ๋ฌธ

์งˆ๋ฌธ 13: ๋„ค์ดํ‹ฐ๋ธŒ N-API ๋ชจ๋“ˆ์€ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”๊ฐ€?

N-API๋ฅผ ํ†ตํ•ด Node.js ๋ฒ„์ „ ๊ฐ„ ์•ˆ์ •์ ์ธ API๋ฅผ ๊ฐ€์ง„ C/C++ ๋„ค์ดํ‹ฐ๋ธŒ ๋ชจ๋“ˆ์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

native-module.cppcpp
// Native module for CPU-intensive calculations

#include <napi.h>

// Synchronous function exposed to JavaScript
Napi::Number Fibonacci(const Napi::CallbackInfo& info) {
  Napi::Env env = info.Env();

  // Argument validation
  if (info.Length() < 1 || !info[0].IsNumber()) {
    Napi::TypeError::New(env, "Number expected")
      .ThrowAsJavaScriptException();
    return Napi::Number::New(env, 0);
  }

  int n = info[0].As<Napi::Number>().Int32Value();

  // Iterative Fibonacci calculation
  long long a = 0, b = 1;
  for (int i = 0; i < n; i++) {
    long long temp = a + b;
    a = b;
    b = temp;
  }

  return Napi::Number::New(env, static_cast<double>(a));
}

// Module initialization
Napi::Object Init(Napi::Env env, Napi::Object exports) {
  exports.Set(
    Napi::String::New(env, "fibonacci"),
    Napi::Function::New(env, Fibonacci)
  );
  return exports;
}

NODE_API_MODULE(native_module, Init)
javascript
// Usage from JavaScript
const native = require('./build/Release/native_module');

// 10x faster than JavaScript equivalent
const result = native.fibonacci(50);

๋„ค์ดํ‹ฐ๋ธŒ ๋ชจ๋“ˆ์€ ์ง‘์ค‘์ ์ธ ๊ณ„์‚ฐ, ๊ธฐ์กด C/C++ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ†ตํ•ฉ, ๋˜๋Š” ์‹œ์Šคํ…œ API ์ ‘๊ทผ์— ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

์งˆ๋ฌธ 14: V8 Garbage Collector๋ฅผ ์„ค๋ช…ํ•˜๋ผ

GC์˜ ์ดํ•ด๋Š” ์ผ์‹œ ์ •์ง€์™€ ๋ฉ”๋ชจ๋ฆฌ ์†Œ๋น„๋ฅผ ์ตœ์†Œํ™”ํ•˜๋Š” ์ฝ”๋“œ ์ž‘์„ฑ์— ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.

gc-optimization.jsjavascript
// V8 GC uses two spaces: Young and Old Generation

// 1. Young Generation: short-lived objects
function shortLivedObjects() {
  for (let i = 0; i < 1000; i++) {
    const temp = { data: i }; // Allocated then collected quickly
  }
  // Minor GC (Scavenge) very fast
}

// 2. Old Generation: objects that survive multiple GCs
const cache = new Map(); // Survives, promoted to Old Generation

// โŒ Problematic pattern: many promoted objects
function createManyLongLived() {
  const objects = [];
  for (let i = 0; i < 100000; i++) {
    objects.push({ id: i, data: new Array(100).fill(0) });
  }
  return objects; // All promoted to Old Gen = slow major GC
}

// โœ… Optimized pattern: object reuse
class ObjectPool {
  constructor(factory, size = 100) {
    this.pool = Array.from({ length: size }, factory);
    this.available = [...this.pool];
  }

  acquire() {
    return this.available.pop() || this.pool[0];
  }

  release(obj) {
    // Reset and return to pool
    Object.keys(obj).forEach(k => obj[k] = null);
    this.available.push(obj);
  }
}

// GC monitoring
const v8 = require('v8');

function getHeapStats() {
  const stats = v8.getHeapStatistics();
  return {
    totalHeap: `${Math.round(stats.total_heap_size / 1024 / 1024)}MB`,
    usedHeap: `${Math.round(stats.used_heap_size / 1024 / 1024)}MB`,
    heapLimit: `${Math.round(stats.heap_size_limit / 1024 / 1024)}MB`,
  };
}

--max-old-space-size ํ”Œ๋ž˜๊ทธ๋กœ ๋ฉ”๋ชจ๋ฆฌ ์ง‘์•ฝ์  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ Old Generation ์ œํ•œ์„ ๋Š˜๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์งˆ๋ฌธ 15: ๊ทธ๋ ˆ์ด์Šคํ’€ ์…ง๋‹ค์šด์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์€?

๊ทธ๋ ˆ์ด์Šคํ’€ ์…ง๋‹ค์šด์€ ์ง„ํ–‰ ์ค‘์ธ ์š”์ฒญ์„ ์™„๋ฃŒํ•˜๊ณ  ์„œ๋ฒ„ ์ค‘์ง€ ์ „์— ์—ฐ๊ฒฐ์„ ์ ์ ˆํžˆ ๋‹ซ๋Š” ๊ฒƒ์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

graceful-shutdown.jsjavascript
const http = require('http');

const server = http.createServer((req, res) => {
  // Simulate a long request
  setTimeout(() => {
    res.writeHead(200);
    res.end('Done');
  }, 2000);
});

// Tracking active connections
let connections = new Set();

server.on('connection', (conn) => {
  connections.add(conn);
  conn.on('close', () => connections.delete(conn));
});

// Graceful shutdown function
async function shutdown(signal) {
  console.log(`${signal} received, starting graceful shutdown...`);

  // 1. Stop accepting new connections
  server.close(() => {
    console.log('HTTP server closed');
  });

  // 2. Close idle connections
  for (const conn of connections) {
    conn.end();
  }

  // 3. Close DB connections, queues, etc.
  await Promise.all([
    database.disconnect(),
    redisClient.quit(),
    messageQueue.close(),
  ]);

  // 4. Safety timeout
  setTimeout(() => {
    console.error('Forced shutdown after timeout');
    process.exit(1);
  }, 30000);

  console.log('Graceful shutdown completed');
  process.exit(0);
}

// Listen for termination signals
process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));

// Start server
server.listen(3000, () => {
  console.log('Server running on port 3000');
});

์ปจํ…Œ์ด๋„ˆ(Docker, Kubernetes)๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ ๊ทธ๋ ˆ์ด์Šคํ’€ ์…ง๋‹ค์šด์€ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ์— ํ•„์ˆ˜์ ์ž…๋‹ˆ๋‹ค.

Node.js / NestJS ๋ฉด์ ‘ ์ค€๋น„๊ฐ€ ๋˜์…จ๋‚˜์š”?

์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ, flashcards, ๊ธฐ์ˆ  ํ…Œ์ŠคํŠธ๋กœ ์—ฐ์Šตํ•˜์„ธ์š”.

ํ–‰๋™ ์งˆ๋ฌธ

์งˆ๋ฌธ 16: ํ•ด๊ฒฐํ•œ ์„ฑ๋Šฅ ๋ฌธ์ œ๋ฅผ ์„ค๋ช…ํ•˜๋ผ

์ด ์งˆ๋ฌธ์€ ์‹ค๋ฌด ๊ฒฝํ—˜์„ ํ‰๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. STAR ํ˜•์‹(์ƒํ™ฉ, ๊ณผ์ œ, ํ–‰๋™, ๊ฒฐ๊ณผ)์œผ๋กœ ๋‹ต๋ณ€์„ ๊ตฌ์„ฑํ•˜๋Š” ๊ฒƒ์ด ๊ถŒ์žฅ๋ฉ๋‹ˆ๋‹ค.

๊ตฌ์กฐํ™”๋œ ๋‹ต๋ณ€ ์˜ˆ์‹œ:

text
Situation: A reporting API was timing out on requests
            exceeding 100,000 records.

Task:       Reduce response time from 45s to under 5s.

Action:
1. Profiling with clinic.js โ†’ identified JSON serialization as bottleneck
2. Implemented streaming with Transform streams
3. Database-side pagination
4. Added Redis caching for frequent queries

Result:     Response time reduced to 2s, memory usage decreased by 10x.

์งˆ๋ฌธ 17: ์˜์กด์„ฑ๊ณผ ๊ทธ ์—…๋ฐ์ดํŠธ๋ฅผ ์–ด๋–ป๊ฒŒ ๊ด€๋ฆฌํ•˜๋Š”๊ฐ€?

package.json - Versioning best practicesjavascript
{
  "dependencies": {
    // โœ… Exact versions for production
    "express": "4.18.2",

    // โœ… Caret for compatible minor updates
    "lodash": "^4.17.21",

    // โŒ Avoid latest or *
    // "some-lib": "*"
  },
  "devDependencies": {
    // Quality tools
    "npm-check-updates": "^16.0.0"
  },
  "scripts": {
    // Vulnerability check
    "audit": "npm audit --audit-level=moderate",

    // Interactive update
    "update:check": "ncu",
    "update:apply": "ncu -u && npm install"
  },
  "engines": {
    // Specify required Node.js version
    "node": ">=20.0.0"
  }
}

package-lock.json ์‚ฌ์šฉ, Dependabot ๋˜๋Š” Renovate๋ฅผ ํ†ตํ•œ ์ž๋™ํ™”, ๊ฐ ๋ฉ”์ด์ € ์—…๋ฐ์ดํŠธ ์ „ ํšŒ๊ท€ ํ…Œ์ŠคํŠธ๋ฅผ ์–ธ๊ธ‰ํ•˜๋Š” ๊ฒƒ์ด ๊ถŒ์žฅ๋ฉ๋‹ˆ๋‹ค.

๊ฒฐ๋ก 

Node.js ๋ฐฑ์—”๋“œ ๋ฉด์ ‘์—์„œ๋Š” ๋‚ด๋ถ€ ๋ฉ”์ปค๋‹ˆ์ฆ˜์˜ ์ด๋ก ์  ์ดํ•ด์™€ ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์˜ ์‹ค์ œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋Šฅ๋ ฅ์ด ๋ชจ๋‘ ํ‰๊ฐ€๋ฉ๋‹ˆ๋‹ค. Event Loop, ๋น„๋™๊ธฐ ํŒจํ„ด, ์ตœ์ ํ™” ๊ธฐ๋ฒ•์˜ ์ˆ™๋‹ฌ์€ ์‹œ๋‹ˆ์–ด ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž ํฌ์ง€์…˜์—์„œ ๊ธฐ๋Œ€๋˜๋Š” ๊ธฐ๋ฐ˜์„ ํ˜•์„ฑํ•ฉ๋‹ˆ๋‹ค.

์ค€๋น„ ์ฒดํฌ๋ฆฌ์ŠคํŠธ

  • โœ… Event Loop์˜ ๋™์ž‘๊ณผ ๊ทธ ๋‹จ๊ณ„๋ฅผ ์ดํ•ดํ•œ๋‹ค
  • โœ… ์ฝœ๋ฐฑ, Promise, async/await์˜ ์ฐจ์ด๋ฅผ ์ˆ™๋‹ฌํ•œ๋‹ค
  • โœ… ๋น„๋™๊ธฐ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ํŒจํ„ด์„ ์•ˆ๋‹ค
  • โœ… Streams์™€ ํด๋ž˜์‹ ๋ฉ”์„œ๋“œ์˜ ์‚ฌ์šฉ ์‹œ์ ์„ ์•ˆ๋‹ค
  • โœ… ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ์‹๋ณ„ํ•˜๊ณ  ์ˆ˜์ •ํ•œ๋‹ค
  • โœ… OWASP ๋ณด์•ˆ ๋ชจ๋ฒ” ์‚ฌ๋ก€๋ฅผ ์ ์šฉํ•œ๋‹ค
  • โœ… ํด๋Ÿฌ์Šคํ„ฐ๋ง๊ณผ ๊ทธ๋ ˆ์ด์Šคํ’€ ์…ง๋‹ค์šด์„ ๊ตฌํ˜„ํ•œ๋‹ค
  • โœ… ํ”„๋กœํŒŒ์ผ๋ง ๋„๊ตฌ(clinic.js, Chrome DevTools)๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค

์—ฐ์Šต์„ ์‹œ์ž‘ํ•˜์„ธ์š”!

๋ฉด์ ‘ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ์™€ ๊ธฐ์ˆ  ํ…Œ์ŠคํŠธ๋กœ ์ง€์‹์„ ํ…Œ์ŠคํŠธํ•˜์„ธ์š”.

๊ธฐ์ˆ ์  ์ค€๋น„๋Š” ์‹ค์ „ ํ”„๋กœ์ ํŠธ๋กœ ๋ณด์™„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ”„๋กœ๋•์…˜ API ๊ตฌ์ถ•, Node.js ์˜คํ”ˆ์†Œ์Šค ํ”„๋กœ์ ํŠธ ๊ธฐ์—ฌ, LeetCode ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ์˜ ๋ฌธ์ œ ํ’€์ด๊ฐ€ ์ด๋Ÿฌํ•œ ์ง€์‹์„ ๊ฒฌ๊ณ ํžˆ ํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.

ํƒœ๊ทธ

#nodejs
#interview
#backend
#javascript
#technical interview

๊ณต์œ 

๊ด€๋ จ ๊ธฐ์‚ฌ

๋ชจ๋˜ํ•œ ๋ฐฑ์—”๋“œ ์Šคํƒ ๊ตฌ์ถ•์„ ์œ„ํ•œ NestJS์™€ Prisma

NestJS + Prisma: Node.js๋ฅผ ์œ„ํ•œ ๋ชจ๋˜ ๋ฐฑ์—”๋“œ ์Šคํƒ

NestJS์™€ Prisma๋กœ ๋ชจ๋˜ํ•œ ๋ฐฑ์—”๋“œ API๋ฅผ ๊ตฌ์ถ•ํ•˜๊ธฐ ์œ„ํ•œ ์™„์ „ํ•œ ๊ฐ€์ด๋“œ์ž…๋‹ˆ๋‹ค. ์„ค์ •, ๋ชจ๋ธ, ์„œ๋น„์Šค, ํŠธ๋žœ์žญ์…˜ ๋ฐ ๋ชจ๋ฒ” ์‚ฌ๋ก€๋ฅผ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

NestJS๋กœ ์™„์ „ํ•œ REST API๋ฅผ ๊ตฌ์ถ•ํ•˜๊ธฐ ์œ„ํ•œ ๊ฐ€์ด๋“œ

NestJS: ์™„์ „ํ•œ REST API ๊ตฌ์ถ• ๊ฐ€์ด๋“œ

NestJS๋กœ ์ „๋ฌธ์ ์ธ REST API๋ฅผ ๊ตฌ์ถ•ํ•˜๋Š” ์™„๋ฒฝ ๊ฐ€์ด๋“œ์ž…๋‹ˆ๋‹ค. ์ปจํŠธ๋กค๋Ÿฌ, ์„œ๋น„์Šค, ๋ชจ๋“ˆ ๊ตฌ์„ฑ, class-validator๋ฅผ ํ™œ์šฉํ•œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ, ์—๋Ÿฌ ํ•ธ๋“ค๋ง์„ ์‹ค์ „ ์ฝ”๋“œ๋กœ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

Node.js 24 URLPattern ํผ๋ฏธ์…˜ ๋ชจ๋ธ ๋ฉด์ ‘ ๋Œ€๋น„ 2026

Node.js 24 ํ•ต์‹ฌ ๊ธฐ๋Šฅ ์™„์ „ ๋ถ„์„: URLPattern, ํผ๋ฏธ์…˜ ๋ชจ๋ธ, ๋ฉด์ ‘ ๋Œ€๋น„ ๊ฐ€์ด๋“œ (2026๋…„ํŒ)

Node.js 24 LTS(Krypton)์˜ ์ฃผ์š” ์‹ ๊ธฐ๋Šฅ์ธ URLPattern, ํผ๋ฏธ์…˜ ๋ชจ๋ธ, ๋ช…์‹œ์  ๋ฆฌ์†Œ์Šค ๊ด€๋ฆฌ๋ฅผ ์‹ค์ „ ์ฝ”๋“œ ์˜ˆ์ œ์™€ ๋ฉด์ ‘ ๋Œ€๋น„ ์งˆ๋ฌธ์œผ๋กœ ์ƒ์„ธํ•˜๊ฒŒ ๋ถ„์„ํ•œ๋‹ค.