Stage 3: Backend Essentials

Node.js · Lesson 02

Built-in Modules

fs, path, os and events from the Node.js standard library.

Node.js2 min readFree lesson

Node ships with a standard library. fs, path, os and events cover most of what a backend needs before you install a single package.

Learning objectives

  • Read, write and inspect files with fs.
  • Build safe paths with path instead of string concatenation.
  • Read system information with os.
  • Emit and listen for custom events with EventEmitter.

1. fs — the file system

Synchronous calls block until the operation finishes. They are fine in scripts and at startup:

const fs = require("node:fs");

fs.writeFileSync("notes.txt", "first line\n", "utf8");
const text = fs.readFileSync("notes.txt", "utf8");
console.log(text);

fs.appendFileSync("notes.txt", "second line\n");
console.log(fs.existsSync("notes.txt")); // true

Asynchronous calls keep the process free while the disk works:

const fs = require("node:fs/promises");

async function main() {
  await fs.writeFile("notes.txt", "hello", "utf8");
  const text = await fs.readFile("notes.txt", "utf8");
  console.log(text);
}

main();

Inside an HTTP server, always use the async version — a synchronous read blocks every other request.

Useful operations:

fs.mkdirSync("data", { recursive: true }); // create folders, ignore if present
fs.readdirSync(".");                        // names in a directory
fs.statSync("notes.txt").size;              // bytes
fs.unlinkSync("notes.txt");                 // delete a file

2. path — never join with "+"

const path = require("node:path");

path.join("content", "posts", "one.md"); // content\posts\one.md on Windows
path.resolve("./data");                  // absolute path from cwd
path.extname("app.js");                  // ".js"
path.basename("/tmp/report.pdf");        // "report.pdf"
path.dirname("/tmp/report.pdf");         // "/tmp"

path.join normalises separators for the current operating system. Manual string concatenation breaks on Windows and on paths that already end with a slash.

__dirname (CommonJS) and import.meta.dirname (ESM) give the folder of the current file — the reliable anchor for reading project files regardless of where the process was started.

3. os — information about the machine

const os = require("node:os");

os.platform();     // "win32", "linux", "darwin"
os.cpus().length;  // logical cores
os.totalmem();     // bytes
os.homedir();      // C:\Users\...

Useful for diagnostics and for choosing sensible defaults (for example, a worker count).

4. events — EventEmitter

Node's core is event-driven. EventEmitter exposes that pattern to your own code:

const { EventEmitter } = require("node:events");

const bus = new EventEmitter();

bus.on("order:created", (order) => {
  console.log(`Send receipt for ${order.id}`);
});

bus.once("ready", () => console.log("Ready once"));

bus.emit("order:created", { id: 42 });
bus.emit("order:created", { id: 43 });

on stays subscribed; once removes itself after the first call. HTTP servers, streams and sockets are all EventEmitters underneath.

Common mistakes

  • Forgetting the "utf8" argument and getting a Buffer back.
  • Using __dirname inside an ES module ("type": "module") — use import.meta.dirname.
  • Blocking the event loop with readFileSync inside a request handler.

Practice

Write the directory report tool in the assignment. It combines fs, path and process.argv into a script you will actually reuse.

Example files

Complete, runnable files used in this lesson. Copy them into your project and experiment.

fs-demo.jsJavaScript
// ===================================================
// 강의 02 예제 — fs 모듈: 파일 읽기 → 단어 수 세기 → 결과 파일에 쓰기
// 실행: node fs-demo.js
// ===================================================

const fs = require('fs');
const path = require('path');

// ① 샘플 텍스트 파일 생성 (데모용)
const inputPath = path.join(__dirname, 'input.txt');
const sampleText = `Node.js는 서버 사이드 JavaScript 런타임입니다.
V8 엔진을 사용하여 빠른 실행 속도를 자랑합니다.
비동기 I/O 처리로 높은 성능을 발휘합니다.
npm을 통해 수많은 패키지를 활용할 수 있습니다.
웹 서버, CLI 도구, API 서버 등 다양하게 활용됩니다.`;

// 입력 파일 생성
fs.writeFileSync(inputPath, sampleText, 'utf-8');
console.log('✔ input.txt 파일 생성 완료');

// ② 파일 읽기 (동기 방식)
const content = fs.readFileSync(inputPath, 'utf-8');
console.log('\n--- 파일 내용 ---');
console.log(content);

// ③ 단어 수 세기
//   줄바꿈을 공백으로 바꾼 후, 공백으로 split → 단어 배열
const words = content
  .replace(/\n/g, ' ')   // 줄바꿈 → 공백
  .split(' ')            // 공백 기준으로 분리
  .filter(w => w.length > 0); // 빈 문자열 제거

const lineCount = content.split('\n').length;   // 줄 수
const wordCount = words.length;                 // 단어 수
const charCount = content.replace(/\s/g, '').length; // 공백 제외 글자 수

console.log('\n--- 분석 결과 ---');
console.log(`줄 수: ${lineCount}`);
console.log(`단어 수: ${wordCount}`);
console.log(`글자 수(공백 제외): ${charCount}`);

// ④ 결과를 output.txt에 저장
const outputPath = path.join(__dirname, 'output.txt');
const resultText = `=== 파일 분석 결과 ===
분석 일시: ${new Date().toLocaleString('ko-KR')}
원본 파일: input.txt

줄 수: ${lineCount}줄
단어 수: ${wordCount}개
글자 수(공백 제외): ${charCount}자
`;

fs.writeFileSync(outputPath, resultText, 'utf-8');
console.log('\n✔ 결과가 output.txt에 저장되었습니다.');

// ⑤ 저장된 결과 파일 다시 읽어서 확인
const saved = fs.readFileSync(outputPath, 'utf-8');
console.log('\n--- output.txt 내용 ---');
console.log(saved);

// ⑥ path 모듈 활용 예시
console.log('--- path 모듈 ---');
console.log('현재 디렉토리:', __dirname);
console.log('파일명:', path.basename(inputPath));
console.log('확장자:', path.extname(inputPath));
console.log('디렉토리:', path.dirname(inputPath));

Assignment

Assignment 02 — Directory report

Build a script that summarises any folder.

node fs-demo.js            # report on the current folder
node fs-demo.js ./src      # report on ./src

Requirements

  1. Use process.argv[2] as the target directory, or __dirname when it is omitted. Resolve it with path.resolve.
  2. If the directory does not exist, print an error and exit 1.
  3. List every entry: [DIR] name for folders, [FILE] name (12.34 KB) for files.
  4. Count files and folders and print both totals.
  5. Write the same report to filelist.txt in the script's folder.
  6. Group files by extension and print the groups, for example .js: app.js, math.js.

Hints

const fs = require("node:fs");
const path = require("node:path");

const target = process.argv[2] ? path.resolve(process.argv[2]) : __dirname;
if (!fs.existsSync(target)) {
  console.error(`Not found: ${target}`);
  process.exit(1);
}

for (const entry of fs.readdirSync(target)) {
  const stat = fs.statSync(path.join(target, entry));
  // stat.isDirectory() / stat.isFile() / stat.size
}

Sort the final list before printing so the output is stable between runs.

Done when

  • Running it in an empty folder prints zero counts instead of crashing.
  • The report file contains exactly what the terminal printed.
  • No path is built with string concatenation.
Show the reference solution

Try the assignment yourself first — comparing your attempt with a working solution is where most of the learning happens.

fs-demo.jsJavaScript
// ===================================================
// 과제 02 정답 — 파일 이름 목록 저장기
// 실행: node fs-demo.js
//       node fs-demo.js ./src
// ===================================================

const fs = require('fs');
const path = require('path');

// ① 스캔할 디렉토리 결정
//    process.argv[2]로 폴더 경로를 받으면 그 폴더를 사용
//    없으면 현재 작업 디렉토리(__dirname) 사용
const targetDir = process.argv[2]
  ? path.resolve(process.argv[2])  // 상대경로를 절대경로로 변환
  : __dirname;

// 대상 폴더가 존재하는지 확인
if (!fs.existsSync(targetDir)) {
  console.error(`오류: '${targetDir}' 폴더가 존재하지 않습니다.`);
  process.exit(1);
}

console.log(`스캔 대상: ${targetDir}`);

// ② 디렉토리 내 항목 목록 읽기
const entries = fs.readdirSync(targetDir);

// ③ 파일과 폴더로 분류
let fileCount = 0;  // 파일 개수
let dirCount = 0;   // 폴더 개수
const lines = [];   // 출력 줄 목록

for (const entry of entries) {
  const fullPath = path.join(targetDir, entry); // 전체 경로
  const stat = fs.statSync(fullPath);           // 파일 정보 가져오기

  if (stat.isDirectory()) {
    // 폴더인 경우
    lines.push(`[폴더] ${entry}`);
    dirCount++;
  } else {
    // 파일인 경우 (크기도 함께 표시)
    const sizeKB = (stat.size / 1024).toFixed(2); // bytes → KB
    lines.push(`[파일] ${entry} (${sizeKB} KB)`);
    fileCount++;
  }
}

// ④ 현재 날짜/시간 포맷팅 (한국 형식)
const now = new Date();
const dateStr = now.toLocaleString('ko-KR', {
  year: 'numeric',
  month: '2-digit',
  day: '2-digit',
  hour: '2-digit',
  minute: '2-digit',
  second: '2-digit',
});

// ⑤ 저장할 텍스트 조합
const resultText = [
  '=== 파일 목록 ===',
  `생성 일시: ${dateStr}`,
  `스캔 경로: ${targetDir}`,
  '',
  ...lines,           // 배열의 각 요소를 펼쳐서 추가
  '',
  `총 파일 수: ${fileCount}개`,
  `총 폴더 수: ${dirCount}개`,
].join('\n'); // 줄바꿈으로 연결

// ⑥ filelist.txt에 저장
const outputPath = path.join(__dirname, 'filelist.txt');
fs.writeFileSync(outputPath, resultText, 'utf-8');

// ⑦ 콘솔에 결과 출력
console.log(resultText);
console.log(`\n✔ 목록이 filelist.txt에 저장되었습니다.`);

// ⑧ 도전: 확장자별 그룹화
const extMap = {}; // { '.js': ['a.js', 'b.js'], '.md': ['README.md'] }

for (const entry of entries) {
  const fullPath = path.join(targetDir, entry);
  const stat = fs.statSync(fullPath);

  if (stat.isFile()) {
    const ext = path.extname(entry) || '(확장자 없음)'; // 확장자 추출
    if (!extMap[ext]) extMap[ext] = [];                  // 없으면 배열 초기화
    extMap[ext].push(entry);                             // 파일 추가
  }
}

// 확장자별 그룹 출력
console.log('\n--- 확장자별 파일 그룹 ---');
for (const [ext, files] of Object.entries(extMap)) {
  console.log(`${ext}: ${files.join(', ')}`);
}

Learn this lesson with a mentor

Work through the Build Your Homepage series on your own, or join a guided cohort at our Pitampura campus and online.

Call NowEnroll Now