프로그래밍 언어/NODE JS

간단한 콘솔 명령어 만들기(4)

· 코딩마이데이

CLI 프로그램 명령어를 입력하면 기본적인 html 또는 익스프레스 라우터 파일 템플릿을 만들어주는 코드입니다. cli html main ./public/html 또는 같은 명령어를 입력하면 public 폴더 안에 있는 html 폴더에 main.html이 생기는 식입니다.

template.js

#!/usr/bin/env node
const fs = require("fs");
const path = require("path");
const readline = require("readline");

let rl;
let type = process.argv[2];
let name = process.argv[3];
let directory = process.argv[4] || ".";

const htmlTemplate = `
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Template</title>
  </head>
  <body>
    <h1>Hello</h1>
    <p>CLI</p>
  </body>
</html>
`;

const routerTemplate = `
const express = require('express');
const router = express.Router();
 
router.get('/', (req, res, next) => {
   try {
     res.send('ok');
   } catch (error) {
     console.error(error);
     next(error);
   }
});
 
module.exports = router;
`;

const exist = (dir) => {
  // 폴더 존제 확인 함수
  try {
    fs.accessSync(
      dir,
      fs.constants.F_OK | fs.constants.R_OK | fs.constants.W_OK,
    );
    return true;
  } catch (e) {
    return false;
  }
};

const mkdirp = (dir) => {
  // 경로 생성 함수
  const dirname = path
    .relative(".", path.normalize(dir))
    .split(path.sep)
    .filter((p) => !!p);
  dirname.forEach((d, idx) => {
    const pathBuilder = dirname.slice(0, idx + 1).join(path.sep);
    if (!exist(pathBuilder)) {
      fs.mkdirSync(pathBuilder);
    }
  });
};

const makeTemplate = () => {
  // 템플릿 생성 함수
  mkdirp(directory);
  if (type === "html") {
    const pathToFile = path.join(directory, `${name}.html`);
    if (exist(pathToFile)) {
      console.error("이미 해당 파일이 존재합니다");
    } else {
      fs.writeFileSync(pathToFile, htmlTemplate);
      console.log(pathToFile, "생성 완료");
    }
  } else if (type === "express-router") {
    const pathToFile = path.join(directory, `${name}.js`);
    if (exist(pathToFile)) {
      console.error("이미 해당 파일이 존재합니다");
    } else {
      fs.writeFileSync(pathToFile, routerTemplate);
      console.log(pathToFile, "생성 완료");
    }
  } else {
    console.error("html 또는 express-router 둘 중 하나를 입력하세요.");
  }
};

const dirAnswer = (answer) => {
  // 경로 설정
  directory = (answer && answer.trim()) || ".";
  rl.close();
  makeTemplate();
};

const nameAnswer = (answer) => {
  // 파일명 설정
  if (!answer || !answer.trim()) {
    console.clear();
    console.log("name을 반드시 입력하셔야 합니다.");
    return rl.question("파일명을 설정하세요. ", nameAnswer);
  }
  name = answer;
  return rl.question(
    "저장할 경로를 설정하세요.(설정하지 않으면 현재경로) ",
    dirAnswer,
  );
};

const typeAnswer = (answer) => {
  // 템플릿 종류 설정
  if (answer !== "html" && answer !== "express-router") {
    console.clear();
    console.log("html 또는 express-router만 지원합니다.");
    return rl.question("어떤 템플릿이 필요하십니까? ", typeAnswer);
  }
  type = answer;
  return rl.question("파일명을 설정하세요. ", nameAnswer);
};

const program = () => {
  if (!type || !name) {
    console.error("사용 방법: cli html|express-router 파일명 [생성 경로]");
  } else {
    makeTemplate();
  }
};

program(); // 프로그램 실행부

 

❶ 생성할 html 코드와 js 코드입니다. 백틱(`)을 사용하면 줄바꿈이 편리합니다.

 

❷ exist와 mkdirp는 편의를 위해 만든 함수입니다. exist 함수는 fS.accessSync 메서드를 통 해 파일이나 폴더가 존재하는지 검사합니다. 존재하지 않으면 에러가 발생하므로 try/catch 문으로 감쌌습니다.
mkdirp 함수는 리눅스 명령어 mdir -p에서 이름을 따온 함수입니다. 현재 경로와 입력한 경로의 상대적인 위치를 파악한 후 순차적으로 상위 폴더부터 만들어 나갑니다. public/html 과 같은 경로를 인수로 제공하면 public 폴더를 만들고, 그 안에 html 폴더를 순차적으로 만듭니다. 3장에서는 웬만하면 fs 모듈의 Sync가 붙은 메서드를 사용하지 말라고 했습니다. 블로킹을 유발해 다른 요청들이 대기하게 만들기 때문입니다. 하지만 CLI 프로그램은 웹 서버가 아니므로 사용해도 딱히 큰 문제를 일으키지 않습니다

 

makeTemplate 함수는 실질적인 프로그램 로직을 담고 있습니다. 유효한 명령어가 들어왔다 면, 디렉터리를 만든 후 type(html 또는 express-router)에 따라 파일을 만들고 파일 안에 템플릿 내용을 입력합니다.


명령어 호출 시 program 함수가 호출되어 내부 로직이 돌아가게 됩니다. 간단히 type과 name 이 있는지 검사한 후 makeTemplate 함수를 호출합니다.


현재 cJi 명령어는 index.js와 연결되어 있습니다. 명령어 실행 시 index.js 대신 template.js가 실행되도록 비꿔야 합니다.

 

package.json

{
  "name": "node-cli",
  "version": "0.0.1",
  "description": "nodejs cli program",
  "main": "index.js",
  "author": "ZeroCho",
  "license": "ISC",
  "bin": {
    "cli": "./template.js"
  }
}

 

예제 프로그램을 실행해보겠습니다. 다만 package.jspn이 바뀌었으므로 이번에는 새로 전역 설치를 해야 합니다.

$ npm i -g

up to date in 4s
$ npx cli
사용 방법: cli html|express-router 파일명 [생성 경로]
$ npx cli js main ./public
html 또는 express-router 둘 중 하나를 입력하세요.
$ npx cli main public/html
html 또는 express-router 둘 중 하나를 입력하세요.
$ npx cli html main public/html
public\html\main.html 생성 완료
$ npx cli express-router index ./routes
routes\index.js 생성 완료
$ npx cli express-router index ./routes
이미 해당 파일이 존재합니다

 

콘솔 명령어를 실행한 디렉터리를 확인해보세요. public, html, routes 폴더가 생기고 그 안에 main.html과 index.js가 들어 있을 것입니다. 파일 생성 경로는 콘솔에 명령어를 입력한 경로를 기준으로 합니다. 이렇게 자주 사용하는 것들은 템플릿으로 만들어두면 좋습니다.

 

위 방식의 단점은 사용자가 명령어와 명령어 옵션 순서를 모두 외우고 있어야 한다는 것입니다. 명령어를 외우고 있다면 빠르게 작업을 수행할 수 있겠지만, 모른다면 사용 방법부터 찾아봐야 합니다.