본문 바로가기
Node

algorithm-cli 생성기

by 우보틀 2022. 2. 7.
아래의 포스팅은 algorithm cli를 만들어 보면서 고민했던 내용들에 대한 기록과 프로젝트 구조에 대한 내용입니다.
 
npm을 이용해서 node 패키지를 만들었습니다.

만든이유는 readme에 나와있지만 

반복적입 작업을 하고 싶지 않았습니다. 

알고리즘 풀이를 할때마다 매번 파일 생성하고 같은 라이브러리를 import 해오는데 이 동작을 cli로 수행하면 좀 더 간편해지지 않을까 했습니다.

 

생각지 못한 장애물들이 많아 생각보다는 오래걸렸지만 현재는 편하게 사용하고 있습니다!

cli에서 동작하는 간단한 도구를 만들어보면서 겪었던 것들을 나중을 위해 기록해두고자 합니다.

 

최근에 nestjs 개발을 하면서 @nestjs/cli를 편리하게 썼던 경험이 있었습니다.

그래서 nestjs/cli의 패키지를 직접 살펴보면서 구조에 대해 참고하였습니다.

 


사용했던 패키지 들입니다.

commander : cli 인터페이스를 다루기 위해 

inquirer: cli 환경에서 질문과 그에대한 응답을 듣기 위해 

chalk: cmd에 나타나는 글자에 색을 입히기 위해 

ora: 오래 걸릴시 spinner를 나타내기 위해 

위의 패키지들이 적용된 모습
typescript로 작성하였고 
ora와 chalk 모듈을 사용시 버전의 이슈로 인하여 javascript로 변환이후 계속 실행시 에러가 생겨 
ora -> 5.4.1
chalk -> 3.0.0 

으로 버전을 지정하여 설치후 문제를 해결하였습니다.
정확한 에러 원인은 그 당시 발견하지 못하였었습니다.
 

프로젝트 내에서 동작의 흐름입니다.

 
1. bootstrap으로 실행
2. CommandLoader의 load 메소드 실행
3. GenerateCommand의 load 메소드 실행
4. answer을 인자로 TemplateGenerator의 generate 메소드 실행
5. answer에 따라 template을 가져온 후 파일 생성
 

 
주요 고민 내용은 다음과 같습니다.
1. 쉽게 새로운 사이트와 플랫폼들을 추가할 수 있어야 한다.
2. 유지보수가 쉽게 설계하고 싶었습니다.
3. 기능에 기반한 프로젝트 구조

 

1. 쉽게 새로운 사이트와 플랫폼들을 추가할 수 있어야 한다.
(보완점이 필요한 것 같습니다. 아직 추가를 위해서 생성, 코드레벨에서의 추가를 해주어야 할 부분이 적진 않습니다.)
package의 특성상 여러 사이트와 각 언어별로 플랫폼이 추가되고 수정되는 일이 빈번할 것으로 판단하였습니다.
고민을 많이 했었던 내용이고 지금도 고민하고 있는 내용입니다.
자체적으로 내린 답은 사이트별로 파일을 생성하여 관리하되 템플릿은 사이트별 파일내에 변수로 추가합니다.
 
언어 별로 파일을 생성할까 했었지만 파일이 과도하게 많아지면 오히려 관리하기가 어려워질것으로 판단 적정한 선에서 끊어야 했습니다.
템플릿의 예시는 아래와 같습니다.

템플릿 폴더 구조

template 폴더를 생성후 index 파일을 두어 어떤 템플릿들이 존재하는지 한 눈에 파악할 수 있게 하고싶었습니다.
// src/templates/index.ts

export * from './baekjoon.template';
export * from './programmers.template';
// src/templates/baekjoon.template.ts

export const baekjoonPythonTemplate = (number?: number | string) => `
import sys
input = sys.stdin.readline

def BOJ${number}() :
  pass

BOJ${number}()
`

export const baekjoonJavascriptTemplate = () => `
const fs = require('fs');
const input = fs.readFileSync('/dev/stdin').toString().split(' ');
`

변수명은 `사이트명언어명Template`으로 통일하여 추가는 간편하게 하였습니다. (해당 부분에 대해서는 더 나은 구조가 있지 않을까 생각중합니다.)

 

> 이미 추가된 사이트에 템플릿을 추가하는 경우

1. templates 폴더에 {사이트명}.template.ts를 생성후 템플릿을 추가해줍니다.

2. templates/index.ts 에서 작성한 모듈을 export 해줍니다.

 

> 새로 사이트를 추가하고 템플릿을 추가하는 경우
1. generators 폴더에 {사이트명}.generator.ts를 추가합니다. (추상클래스를 생성해 두었으니 interface의 내부 로직을 구현하면 됩니다.)
2. template.generator.ts의 switch case에 추가해줍니다.

3. templates 폴더에 {사이트명}.template.ts를 생성후 템플릿을 추가해줍니다.

4. templates/index.ts 에서 작성한 모듈을 export 해줍니다.

 


 
2. 유지보수가 쉽게 설계 하고 싶었습니다.
새로운 사이트를 추가해야할때 무조건 추가되어야 하는 클래스는 generator 클래스 입니다.
generator 클래스에 추상클래스를 생성하고 이를 상속받게 하였습니다.

새로 클래스를 추가시 어떠한 메소드를 추가해야하는지 쉽게 알수 있고 정해진 메소드만 구현할 수 있게 하여 변경시에 쉽게 추적할 수 있게 하고싶었습니다.

(2번 부분에서는 공부하면서 좀 더 설명 덧붙이거나 이후의 토이프로젝트에서 활용할 수 있도록 하겠습니다.)

 

 

3. 기능에 기반한 프로젝트 구조

 

파일명에 각자의 역할을 추가해주었고 알맞은 위치에 추가해주었습니다.

파일명을 통해 해당 파일의 기능을 유추할 수 있고 알맞은 위치에 새로운 파일을 생성할 수 있습니다.

질문을 생성하는 함수를 생성하여 코드의 중복을 줄였고 한 곳으로 몰아 관리를 쉽게 할 수 있도록 하였습니다.

// src/questions/question.ts

export const generateInput = (name: string, message: string) => {
  return {
    type: "input",
    name,
    message
  };
};

export const generateSelect = (
  name: string,
  message: string,
  choices: string[]
) => {
  return {
    type: "list",
    name,
    message,
    choices,
  };
};
program
    .command('generate')
    .alias('g')
    .action(() => {
      const questions = [
        generateSelect("site", chalk.default.magenta("which site will you solve"), ["baekjoon", "programmers"]),
        generateSelect("language", chalk.default.magenta("please select programming language"), ["python", "javascript"]),
        generateInput("identifier", chalk.default.magenta("please input problem number or problem title"))
      ];
    })

 

2월 8일 

=> 구조를 수정해야 겠다. 

한 사이트에만 언어를 추가하는 것은 현재 불가능 하고 언어를 추가할때도 변경해야 될곳이 생각보다 많다. 좀 더 단순하게 작업을 할 수 있는 구조가 없을지 고민해봐야겠다. 

일단 java 템플릿은 두 사이트 전부 추가했다.

'Node' 카테고리의 다른 글

npm 만드는 법 && 출시  (0) 2022.02.07
.npmignore  (0) 2022.02.07
console.log vs process.stdout.write  (0) 2022.01.26
서버 기본 세팅  (0) 2022.01.03
NestJs Request LifeCycle  (5) 2021.11.30