devops2025-03-15·8·153/348

npm scripts 커스텀 스크립트 작성

Writing powerful custom npm scripts for development workflows, build processes, and automation tasks in Node.js projects.

npm scripts 커스텀 스크립트 작성

Introduction

npm scripts are the most underutilized feature in Node.js projects. They provide a cross-platform way to run commands without additional dependencies. This post covers writing effective npm scripts for common development tasks.

Environment

node --version
# v20.11.0

npm --version
# 10.2.4

# Check available scripts
npm run

Problem

Complex development workflows require multiple manual steps:

# Manual process
npm run lint
npm run test
npm run build
npm run deploy
# Forgot a step? Manual errors!

Analysis

npm scripts run in the context of node_modules/.bin, so you can use installed packages directly:

{
  "scripts": {
    "build": "webpack",           // Uses local webpack
    "test": "jest"                // Uses local jest
  }
}

Special lifecycle scripts:

preinstall → install → postinstall
prepare → prepublish → publish
pretest → test → posttest

Solution

Solution 1: Basic script organization

{
  "scripts": {
    "start": "node dist/server.js",
    "dev": "nodemon src/server.ts",
    "build": "tsc && node dist/build.js",
    "test": "jest",
    "test:watch": "jest --watch",
    "test:coverage": "jest --coverage",
    "lint": "eslint src/",
    "lint:fix": "eslint src/ --fix",
    "format": "prettier --write src/"
  }
}

Solution 2: Compound scripts

{
  "scripts": {
    "validate": "npm run lint && npm run test",
    "prebuild": "npm run validate",
    "build": "tsc",
    "postbuild": "echo 'Build complete!'",
    "predeploy": "npm run build",
    "deploy": "node scripts/deploy.js"
  }
}

Solution 3: Environment-specific scripts

{
  "scripts": {
    "start": "node dist/server.js",
    "start:dev": "NODE_ENV=development nodemon src/server.ts",
    "start:prod": "NODE_ENV=production node dist/server.js",
    "start:debug": "NODE_ENV=development node --inspect src/server.ts"
  }
}

Solution 4: Cross-platform scripts

{
  "scripts": {
    "clean": "rimraf dist",
    "build": "tsc",
    "start": "node dist/server.js",
    "dev": "concurrently \"npm run dev:server\" \"npm run dev:client\"",
    "dev:server": "nodemon src/server.ts",
    "dev:client": "cd client && npm run dev"
  },
  "devDependencies": {
    "rimraf": "^5.0.0",
    "concurrently": "^8.2.0"
  }
}

Solution 5: Custom script with arguments

{
  "scripts": {
    "migration": "node scripts/migrate.js",
    "seed": "node scripts/seed.js"
  }
}
// scripts/migrate.js
const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');

const command = process.argv[2];
const database = process.env.DB_NAME || 'myapp_dev';

async function runMigration(action) {
  switch (action) {
    case 'create':
      const name = process.argv[3];
      if (!name) {
        console.error('Usage: npm run migration create ');
        process.exit(1);
      }
      const timestamp = Date.now();
      const filename = `${timestamp}-${name}.sql`;
      fs.writeFileSync(
        path.join(__dirname, '../migrations', filename),
        `-- Migration: ${name}\n\n`
      );
      console.log(`Created migration: ${filename}`);
      break;
    
    case 'run':
      console.log(`Running migrations on ${database}...`);
      // Run migration logic
      break;
    
    default:
      console.error('Unknown action:', action);
      process.exit(1);
  }
}

runMigration(command);

Solution 6: Watch mode with nodemon

{
  "scripts": {
    "dev": "nodemon",
    "dev:debug": "nodemon --inspect"
  },
  "nodemonConfig": {
    "watch": ["src"],
    "ext": "ts,js,json",
    "ignore": ["src/**/*.spec.ts"],
    "exec": "ts-node src/server.ts"
  }
}

Solution 7: Docker scripts

{
  "scripts": {
    "docker:build": "docker build -t myapp .",
    "docker:run": "docker run -p 3000:3000 myapp",
    "docker:dev": "docker-compose -f docker-compose.dev.yml up",
    "docker:down": "docker-compose down"
  }
}

Solution 8: Git hooks with scripts

{
  "scripts": {
    "prepare": "husky install",
    "lint-staged": "lint-staged"
  },
  "lint-staged": {
    "*.{js,ts}": ["eslint --fix", "prettier --write"],
    "*.{json,md}": ["prettier --write"]
  },
  "devDependencies": {
    "husky": "^9.0.0",
    "lint-staged": "^15.0.0"
  }
}

Lessons Learned

  1. Use pre and post hooks - Automate setup and cleanup
  2. Keep scripts simple - Complex logic belongs in dedicated scripts
  3. Use concurrently for parallel tasks - Run frontend and backend together
  4. Document scripts in README - Help new team members understand the workflow
  5. Use npm-run-all for sequential/parallel script execution

This blog does not accept any external sponsorships, affiliate marketing, or ad revenue.