728x90
반응형

이제껏 백엔드 에서 orm을 사용할때 rails에서는 active record 패턴을, nestjs에서는 data mapper 패턴을 사용하였습니다.

(rails에서는 ActiveRecord를 nestjs에서는 typeorm을 사용하였었습니다)

 

두 패턴간의 특징을 정리해두려 합니다

 

DB를 조회하는 메소드를 모델내에 정의하면 active record 패턴

별도의 repositories 클래스를 생성하여 쿼리 메소드를 작성하는 것은 data mapper 패턴이라 합니다.

 

active record 패턴

DB를 조회하는 메소드를 모델내에 정의하는 패턴을 active record 패턴이라 합니다.

간단하다고 생각합니다. 

개인적인 생각으로는 typeorm 에서는 entity 내부에 관계정의까지 같이 정의하는 것이 좋다고 판단하여 

active record를 적용하면 메서드가 조금만 많아져도 entity가 너무 뚱뚱해져서 코드가 쉽지 않아졌습니다. 

import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from "typeorm"

@Entity()
export class User extends BaseEntity {
    @PrimaryGeneratedColumn()
    id: number

    @Column()
    firstName: string

    @Column()
    lastName: string

    @Column()
    isActive: boolean

    static findByName(firstName: string, lastName: string) {
        return this.createQueryBuilder("user")
            .where("user.firstName = :firstName", { firstName })
            .andWhere("user.lastName = :lastName", { lastName })
            .getMany()
    }
}

 

const timber = await User.findByName("Timber", "Saw")

 

data mapper 패턴

 

별도의 클래스를 생성하여 해당 클래스 내부에 db 조회 메소드들을 몰아 넣습니다.

data mapper 패턴에서도 active record 패턴처럼 모델 내에 메소드를 필요에 의해 정의할 수는 있습니다.

import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from "typeorm"

@Entity()
export class User extends BaseEntity {
    @PrimaryGeneratedColumn()
    id: number

    @Column()
    firstName: string

    @Column()
    lastName: string

    @Column()
    isActive: boolean
}
import { EntityRepository, Repository, getRepository } from 'typeorm';
import { UsersEntity } from './users.entity';

@EntityRepository(UsersEntity)
export class UsersRepository extends Repository<UsersEntity> {
    findByName(firstName: string, lastName: string) {
        return this.createQueryBuilder("user")
            .where("user.firstName = :firstName", { firstName })
            .andWhere("user.lastName = :lastName", { lastName })
            .getMany()
    }
}
const userRepository = connection.getCustomRepository(UserRepository);
const timber = await userRepository.findByName("Timber", "Saw");

 

 

그러면 어떤것을 써야 할까요??

 

둘중 택일을 해야할 때는 유지보수의 측면에서 보는게 적합할것 같습니다.

data mapper 패턴은 규모가 큰 어플리케이션을 다룰때 유지보수에 도움을 줄 수 있습니다.

active record 패턴은 단순합니다. 규모가 작은 어플리케이션을 다룰때 유지보수에 도움을 줄 수 있습니다.

이 두개의 장단점은 정답이 아닙니다. 둘중 하나를 택하여 단순하게 관리 하는 것이 유지보수에 더 나은 경험을 줄것 같습니다.

 

 

 

출처: https://github.com/typeorm/typeorm/blob/master/docs/active-record-data-mapper.md

 

GitHub - typeorm/typeorm: ORM for TypeScript and JavaScript (ES7, ES6, ES5). Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL

ORM for TypeScript and JavaScript (ES7, ES6, ES5). Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, SAP Hana, WebSQL databases. Works in NodeJS, Browser, Ionic, Cordova and Elect...

github.com

 

728x90
반응형

'Node' 카테고리의 다른 글

npm link  (0) 2022.03.04
nestjs 의존성 주입, 싱글턴 패턴  (0) 2022.02.28
npm 만드는 법 && 출시  (0) 2022.02.07
.npmignore  (2) 2022.02.07
algorithm-cli 생성기  (0) 2022.02.07
728x90
반응형

2022.03.04 - [Node] - nestjs 코드레벨에서 까보기  에서 nest의 여러 패키지들을 까볼때 두가지 방법을 썼다

 

일단 둘다 무식하게 console.log를 하나씩 찍어봤다.

nest는 추상화가 잘 되어있다고 코드를 읽으면서 느꼈다.

근데 2~3 depth 넘어가게 되면 계속 흐름을 놓쳤다. (아직 큰 흐름이 크게 안읽힌다... 정진하자)

 

1. nest 라이브러리를 clone 받고나서 npm link를 이용하였다.

2. node_modules 에 설치되어있는 코드에서 console.log를 하나씩 찍었다. 

 

2번을 시도하기 전에 1번으로 계속 시도했었다.

1번의 효율이 너무 안좋아서 2번으로 우회하였다.

 

1번을 시도하면서 알게된 방법이 npm link이다. 

@nest-cli를 적용할때는 npm link를 글로벌로 이용했고 

@nestjs/core, @nestjs/common 을 사용할때는 로컬에서 npm link를 직접 해주었다.

 

서두가 너무 길었다. 바로 본론으로 들어가보자

 


 

symlink a package folder => 패키지 폴더의 바로가기를 생성해준다. symlink는 softlink와 비슷한 걸로 이해했다.

 

공식문서에서는 재빌드 없이 반복적인 테스트와 작업을 진행할 수 있도록 작업을 설치해주는 도구입니다. 와 같이 나와있다.

사용법은 의외로 간단하다.

 


 

두가지 방법이 있다.

 

첫번째 

라이브러리를 작업중인 폴더에서 아래와 같이 입력

// 패키지 폴더여야 함
npm link

해당 라이브러리를 적용할 패키지에서 

// 작업중인 패키지를 적용할 프로젝트의 위치여야함

npm link <패키지 이름> // package.json에 설치되어있는 이름이여야 함

 

두번째

npm link <작업중인 패키지>


ex) 
npm link ../node-redis

 


 

두개의 방법다 적용되는 과정은 같다. 

npm link가 적용되는 과정은 두 단계로 나뉜다.

 

첫번째단계

작업중인 패키지에서 npm link를 입력하면 해당 패키지의 symlink(바로가기)를 전역폴더에 생성한다.

npm ls 명령어를 치면 현재 폴더에 설치되어 있는 패키지들을 확인할 수 있다.

이때 -g 옵션을 주면 전역범위에 설치된 npm 들을 확인할 수 있다.

--depth=0 옵션을 주면 뎁스가 없는 부모 들만 가져올 수 있다.

npm ls -g --depth=0

 

두번째 단계

npm link packagename은 전역으로 설치된 package(npm link로 등록한 패키지)와 현재 node_modules에 있는 package의 symlink를 생성해준다.

 

 

 

npm link를 적용하면 작업중인 패키지를 변경하면 바로바로 작업중인 폴더에서 반영이 된다.

위의 즉시 반영때문에 확인할 때 좀 더 편했다.

 

 

 

https://docs.npmjs.com/cli/v8/commands/npm-link

 

npm-link | npm Docs

Symlink a package folder

docs.npmjs.com

 

728x90
반응형

'Node' 카테고리의 다른 글

data mapper 패턴, active record 패턴 🌱  (0) 2022.04.02
nestjs 의존성 주입, 싱글턴 패턴  (0) 2022.02.28
npm 만드는 법 && 출시  (0) 2022.02.07
.npmignore  (2) 2022.02.07
algorithm-cli 생성기  (0) 2022.02.07
728x90
반응형

via GIPHY

 

싱글턴 패턴 

nestjs에서는 모듈들(@Module, @Injectable 데코레이터를 사용하여 등록한 것들)에 싱글턴 패턴을 적용하여 사용한다. 

싱글턴 패턴은 뭐고 왜 사용하는 것일까??

 

싱글턴 패턴은 인스턴스를 메모리에 최초로 만들어두고 사용하는 디자인 패턴이다. 

매 요청마다 인스턴스를 생성하면 메모리 관리에 어려울 수 밖에 없다(언제 삭제해줘야하는지 알수가 없음)

 

따라서 nestjs에서는 의존성이 주입된 모듈들을 global scope로 최초 실행시 메모리에 인스턴스를 띄워두고 각 모듈들을 사용해야 하면 메모리에서 꺼내 쓴다. (scope는 설정 가능)

Node 는 싱글스레드로 동작하기 때문에 싱글턴 패턴으로 모듈들을 관리해도 문제가 없다(동시성 이슈가 발생 안함, 데드락과 같은 상황 발생 x)

프로세스 여러개를 띄워도 각자 서로의 싱글턴 객체 들을 가지기 때문에 문제가 없다.

 

 


 

의존성 주입

 

위에서 나온 의존성 주입은 뭘까?? 예전에 위키피디아에서 읽어본 글을 참고 하면 

 

만약 너가 냉장고에 가서 막 문을 여닫고 뭔가를 찾는다면 문제가 발생할 수 있단다.
문을 연채로 돌아갈수도 있고 엄마나 아빠가 너가 먹길 원하지 않는 음식들을 먹을수도 있겠지.
유통기한이 지났거나 이제껏 사본적 없는 물건들을 찾고 있는 것일수도 있어

너가 해야할것은 필요한 것을 말하는 것 뿐이야. '나는 점심에 마실것이 필요해요' 하면 우리가 필요한것을 준비해줄수 있어!!!!

 

=> 설명이 쉽게 이해가 되었다(이게 맞다면)

 

 

nestjs에서 의존성을 주입(나는 이것들을 필요로 해요)하는 부분은 아래 코드에 있다

@Controller('api/faqs')
export class FaqsController {
  constructor(private readonly faqsService: FaqsService) {}
}

constructor에 필요한 것들을 명시해줌으로써 의존성을 주입해 준다.

nest 에서는 constructor에 명시된 것들을 메모리에 생성된 싱글턴 객체중에 찾아서 의존성을 주입해 주는 것이다.

 

> 싱글턴 객체는 여러 모듈들에서 접근할 수 있기 때문에 private readonly를 명시해주어서 혹시 override되거나 하는 과정을 방지하여야 한다.

 

https://docs.nestjs.com/modules#shared-modules

 

Documentation | NestJS - A progressive Node.js framework

Nest is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with TypeScript and combines elements of OOP (Object Oriented Progamming), FP (Functional Programming), and FRP (Functional Reac

docs.nestjs.com

 

 

https://en.wikipedia.org/wiki/Dependency_injection#AngularJS_example

 

Dependency injection - Wikipedia

From Wikipedia, the free encyclopedia Jump to navigation Jump to search Software programming technique In software engineering, dependency injection is a technique in which an object receives other objects that it depends on, called dependencies. Typically

en.wikipedia.org

 

 

728x90
반응형

'Node' 카테고리의 다른 글

data mapper 패턴, active record 패턴 🌱  (0) 2022.04.02
npm link  (0) 2022.03.04
npm 만드는 법 && 출시  (0) 2022.02.07
.npmignore  (2) 2022.02.07
algorithm-cli 생성기  (0) 2022.02.07
728x90
반응형

간단한 node package를 만들고 출시까지 진행하는 방법을 알아보겠습니다.

 

진행 순서는 다음과 같습니다.

 

1. 패키지 만들기

2. 로컬에서 해당 패키지 테스트

3. npm 사이트 회원가입

4. npm cli 이용해서 배포

5. npm으로 다운받고 테스트 해보기


1. 패키지 만들기

1-1 제일 처음에 폴더를 만들고 (이름을 조금 특이하게 지었습니다.)

mkdir npm_my_dist_test
npm init -y

npm init -y 를 하게 되면 package.json 파일이 생성됩니다.

 

잠시 package.json에 대해 살펴보고 가겠습니다.

 

package.json이란??

npm으로 출시를 진행하려면 package.json은 필수입니다. 
다른 이들이 설치와 관리를 쉽게 하기 위해 추가할수 있습니다.
  • 패키지가 의존하고 있는 패키지들을 알려주고
  • semantic 버전 규칙을 사용하여 패키지의 버전을 알려줍니다. 
  • build를 재생산 가능하게 해주어 다른 개발자들과 쉽게 공유할수 있게 해줍니다.

package.json 살펴보기

 

  • name과 version은 필수 입니다.
  • keyword에 단어를 추가해주면 그것으로 검색이 가능합니다.
npm search {keyword}

  • npm bin 

bin에 추가해줌으로써 패키지의 실행 가능한 파일에 접근할 수 있습니다.

package.json의 bin에 아래와 같이 명령어와 로컬 파일의 이름을 추가해줍니다.

"bin": {
  "npm_my_dist_test": "src/index.js"    // 이름 : 실행파일 위치
},

이 패키지가 전역으로 설치가 되면 패키지들은 전역 bin들과 연결이 되고 이로인해 이름으로 실행하는것이 가능해 집니다.

 

* 주의사항

실행 파일에  #!/usr/bin/env node 을 꼭 추가해주어야 합니다.

'node환경에서 실행되는 것이다' 라고 명시해주는 것이고 없으면 실행이 되지 않습니다

#!/usr/bin/env node
console.log("byebye");

 

  • main은 entry point를 지정해 주는 곳입니다. 

해당 모듈을 가져왔을시 실행되는 부분 입니다.

main에 따로 값을 지정해주지 않으면 root folder의 index.js가 default로 지정됩니다.

 

패키지 폴더(npm_my_dist_test) 에서 src/index.js에 아래와 같이 입력해줍니다.

// src/index.js

console.log('hihi')

 


 

2. 로컬에서 만든 패키지 테스트 

 

해당 모듈이 정상적으로 동작하는지 확인해 볼까요??

 

상위 폴더로 가서 테스트 할 폴더를 만들고 우리가 만든 모듈을 불러올겁니다.

저는 testfolder 폴더를 만들고 npm_my_dist_folder를 불러오겠습니다.

 

mkdir testfolder
touch index.js
// index.js

require('../npm_my_dist_test')

hihi가 출력된 모습

패키지가 불러와졌고 작성한대로 실행되는 모습을 확인할 수 있습니다.

 


 

3. npm 사이트 회원가입

 

이제 이 패키지를 npm에 등록하고 npm install npm_my_dist_test 까지 진행해보도록 하겠습니다.

 

제일 먼저 아래의 사이트에서 회원가입을 진행해 줍니다.

https://www.npmjs.com/

 

npm

Bring the best of open source to you, your team, and your company Relied upon by more than 11 million developers worldwide, npm is committed to making JavaScript development elegant, productive, and safe. The free npm Registry has become the center of Java

www.npmjs.com

회원가입 후에 이메일 인증을 해야합니다. (추후에 npm 배포를 진행할 때 문제가 생길수 있습니다.)

 


 

4. npm cli이용해서 배포

 

이제부터는 npm cli를 이용해서 직접 만든 npm을 배포합니다.

 

cli 환경에서 로그인을 진행해줍니다. (npm 사이트에서 회원가입한 유저의 정보입니다.)

npm adduser  혹은 npm login

 

npm publish를 입력하면  성공 혹은 실패를 알려줍니다.

실패했을시에는 에러 메시지에서 친절히 알려주어 안내 문구를 따르시면 됩니다.

성공했을시에는 이메일이 발송되고 30초 정도 뒤에 npm 사이트와 업데이트 된 버전을 새로 받을 수 있습니다.

 

 


 

5. npm으로 다운받고 테스트 해보기

 

이제 배포한 npm을 프로젝트에서 사용해보도록 하겠습니다.

npm install npm_my_dist_test
{
  "name": "testfolder",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "npm_my_dist_test": "^1.0.7"
  }
}

dependencies에 우리가 배포한 앱이 추가된 것을 확인할 수 있습니다.

 

// index.js

require('npm_my_dist_test');

을 입력하고 node index.js를 실행하면 아래와 같이 실행됨을 확인할 수 있습니다!

 


코드 확인해볼수 있는 곳 

https://github.com/woobottle/npm_test

 

woobottle/npm_test

Contribute to woobottle/npm_test development by creating an account on GitHub.

github.com

 

출처 : https://docs.npmjs.com/cli/v8/configuring-npm/package-json#bin

 

package.json | npm Docs

Specifics of npm's package.json handling

docs.npmjs.com

 

728x90
반응형

'Node' 카테고리의 다른 글

npm link  (0) 2022.03.04
nestjs 의존성 주입, 싱글턴 패턴  (0) 2022.02.28
.npmignore  (2) 2022.02.07
algorithm-cli 생성기  (0) 2022.02.07
console.log vs process.stdout.write  (0) 2022.01.26
728x90
반응형

최근 npm을 하나 만들면서 npmignore에 대해 궁금중이 생겼습니다.

 

.npmignore은 package에 포함되지 않을 내용을 지정할 수 있습니다.
.npmignore이 존재하지 않을시에는 .gitignore의 내용을 패키지에 포함 시키지 않습니다.

 

제가 겪었던 상황은 다음과 같습니다. 

.npmignore에 추가했지만 npm으로 배포를 했을때 npmignore의 내용들이 포함이 되어서 배포가 되었습니다.

예시 화면

 

제가 내린 결론은 다음과 같습니다. (공식적인 정답은 찾지 못하였습니다)

공식문서에서의 내용
package.json의 files에 포함된 파일들은 .npmignore와 .gitignore로 인해 제외될수 없습니다.

 

1. package.json에 있는 entry_point들의 내용들과 files의 리스트에 포함된 내용은 npmignore에 포함되어 있더라도 배포 됩니다.

(main, bin, files에 포함된 파일들)

2. npmignore에 있는 내용들은 entry_point내에 존재하지 않아야만 배포되지 않습니다.

 

여기서 entry_point는 실행파일 + files에 포함된 파일들로 정의하겠습니다.
실행파일은 main에 정의된 실행파일과 bin내에 포함된 실행파일입니다.

 

테스트 해보았던 경우들에 대해 사진과 함께 설명하겠습니다.

테스트는 npm pack 명령어를 통해 확인하였습니다.
npm pack을 입력하면 패키지의 압축파일(tarball => .tar, .tgz 압축파일)을 생성해줍니다.
이 압축파일이 우리가 npm을 다운받았을때 다운받아지는 파일 입니다.

 

파일의 구조는 이미지로 첨부하겠습니다.

1. entry_point에 추가 x, npmignore에 추가 x

=> 아무것도 포함하지 않았기 때문에 전체가 압축되는 것을 확인할 수 있습니다

 

2. entry_point에 추가 x, npmignore에 추가 o

=> npmignore에 포함된 내용들이 압축되지 않은것을 확인할 수 있습니다.

 

3. entry_point에 추가 o, npmignore에 추가 x => 1번의 경우와 동일

4. entry_point에 추가 o, npmignore에 추가 o

=> npmignore에 포함되어있지만 files에서 해당 폴더들을 참조하고 있어 같이 압축된 것을 확인할 수 있습니다

 

=> src 폴더는 같이 압축되지 않은것을 확인할 수 있습니다. entry_point에 포함되어 있지 않아 npmignore로 인해 포함되지 않은것을 확인할 수 있습니다.

=> entry_point의 main에서는 dist폴더를 bin에서는 src폴더를 참조하고 있어 두 개의 폴더가 같이 압축된 것을 확인할 수 있습니다.

 


 

아래는 npm 문서에서 ignore에 대한 설명이 나와있는 부분을 번역한 내용입니다.

Keeping files out of your Package 

패키지 외부에 파일을 관리하기위해 .npmignore 파일을 사용하세요. 만약 .npmignore 파일이 없고 .gitignore 파일이 있다면 npm은 .gitignore 파일에 있는 것들을 무시할것입니다. .gitignore 파일로 인해 제외된 것들중 일부를 포함하고 싶다면 빈 .npmignore 파일을 만들고 override 할수 있습니다. git과 같이 npm은 root directory 뿐만 아니라 패키지의 모든 하위 디렉토리들에서 .npmignore과 .gitignore파일들을 찾습니다.

 

.npmignore 파일들은 .gitignore 파일들과 같은 규칙을 따릅니다 :

  • 빈 줄과 #으로 시작하는 줄은 무시됩니다(주석 같은 의미)
  • glob 패턴을 따릅니다.
  • 패턴을 /로 끝내서 디렉토리를 지정할 수 있습니다.
  • !로 시작하여 패턴을 부정할수 있습니다.

기본적으로 아래의 경로와 파일들은 무시됩니다. .npmignore에 포함할 필요가 없습니다 :

  • .*.swp
  • ._*
  • .DS_Store
  • .git
  • .gitignore
  • .hg
  • .npmignore
  • .npmrc
  • .lock-wscript
  • .svn
  • .wafpickle-*
  • config.gypi
  • CVS
  • npm-debug.log

추가적으로 node_modules에 있는 번들 의존성을 제외한 모든것들은 무시되어집니다. npm은 자동적으로 이것들을 다루어서 .npmignore에 node_modules를 포함시키지 않아도 됩니다.

 

아래의 경로와 파일들은 절대 무시되지 않습니다. .npmignore에 포함시키는 것은 무의미한 일입니다 :

  • package.json
  • README (and its variants)
  • CHANGELOG (and its variants)
  • LICENSE / LICENCE

프로젝트의 구조에 따라 .npmignore를 유지하는 것이 골치아플경우 package.json의 files 속성을 추가하는것이 좋을수도 있습니다.

files 속성은 패키지에 포함되어야 하는 file과 directory들의 이름으로 구성된 배열입니다. 금지 리스트를 다루는 것보다 어떤것이 허가되는지 직접 선택하는 것이 쉬울수도 있습니다.

Testing whether your .npmignore or files config works

배포되었을때 어떤 파일들이 추가되는지 점검하고 싶다면 배포 방식과 같이 작업 폴더에 tarball 압축파일을 생성하는 npm pack 명령어를 통해 개발 환경에서  확인할수 있습니다.


files

optional files는 패키지가 의존성으로서 설치되었을때 포함되어야 하는 file 패턴들의 배열입니다. 파일 패턴들은 .gitignore의 문법과 유사한 문법을 따르지만 파일, 폴더, glob 패턴을 포함하여 pack되었을때 tarball에 파일들이 포함되도록 만들어 줍니다.

file은 기본값은 모든 파일들을 포함하는 "*"입니다.  몇몇 특별한 파일들과 폴더들은 file 배열에 존재하는것과 상관없이 포함되거나 제외됩니다.

.npmignore파일을 패키지의 root나 서브폴더에서 제공해줌으로써 file들이 포함되어 지는것을 방지할 수 있습니다. 패키지의 root에서는 files를 override 하지 않지만 서브폴더 에서는 override 됩니다. .npmignore 파일은 .gitignore와 같이 동작합니다. 만약 .gitignore파일이 있고 .npmignore파일이 없다면 .gitignore내의 내용들이 대신 사용됩니다.

 

package.json의 files에 포함된 파일들은 .npmignore와 .gitignore로 인해 제외될수 없습니다.

특정 파일들은 설정과 상관없이 항상 포함됩니다.

  • package.json
  • README
  • LICENSE / LICENCE
  • The file in the "main" field

README & LICENSE 는 어떤 확장자든 가질수 있습니다.

반대로 몇몇 파일들은 무조건 무시됩니다. :

  • .git
  • CVS
  • .svn
  • .hg
  • .lock-wscript
  • .wafpickle-N
  • .*.swp
  • .DS_Store
  • ._*
  • npm-debug.log
  • .npmrc
  • node_modules
  • config.gypi
  • *.orig
  • package-lock.json (use npm-shrinkwrap.json if you wish it to be published)

 

출처 :  https://docs.npmjs.com/cli/v8/using-npm/developers#testing-whether-your-npmignore-or-files-config-works

 

developers | npm Docs

Developer Guide

docs.npmjs.com

 

728x90
반응형

'Node' 카테고리의 다른 글

nestjs 의존성 주입, 싱글턴 패턴  (0) 2022.02.28
npm 만드는 법 && 출시  (0) 2022.02.07
algorithm-cli 생성기  (0) 2022.02.07
console.log vs process.stdout.write  (0) 2022.01.26
서버 기본 세팅  (0) 2022.01.03
728x90
반응형
아래의 포스팅은 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 템플릿은 두 사이트 전부 추가했다.

728x90
반응형

'Node' 카테고리의 다른 글

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

최근 node 패키지를 하나 만들다가 process.stdout.write을 이용하는 경우가 생겼었습니다.

단순히 출력을 해주는 기능 같은데 기존에 자주 사용하던 console.log와는 무엇이 다른 것일까요?

 

위의 결과에서 어떤것이 console.log, process.stdout.write를 이용한 것 일까요

console.log("console.log")
console.log("vs")
console.log("process.stdout.write");
process.stdout.write("console.log ")
process.stdout.write("vs ");
process.stdout.write("process.stdout.write");

 

개행이 들어간것이 console.log를 이용

들어가지 않은 것이 process.stdout.write를 이용한 것입니다.

 

결론만 말하자면 개행이 들어간것이 차이입니다.

 

node 문서에 나와있는 console.log에 대한 설명입니다.

 

 

 

출처: https://nodejs.org/docs/v0.3.1/api/process.html

 

process - Node.js Manual & Documentation

Node.js Manual & Documentation process The process object is a global object and can be accessed from anywhere. It is an instance of EventEmitter. Event: 'exit' function () {} Emitted when the process is about to exit. This is a good hook to perform consta

nodejs.org

 

728x90
반응형

'Node' 카테고리의 다른 글

.npmignore  (2) 2022.02.07
algorithm-cli 생성기  (0) 2022.02.07
서버 기본 세팅  (0) 2022.01.03
NestJs Request LifeCycle  (5) 2021.11.30
Package subpath './package.json' is not definded by "exports" in ~~~  (1) 2021.11.14
728x90
반응형

sudo apt-get install curl

curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -

sudo apt-get install -y nodejs

sudo apit-get install build-essential

sudo apt-get install build-essential

sudo npm install -g pm2

sudo npm install -g yarn

sudo npm install -g n

728x90
반응형

'Node' 카테고리의 다른 글

algorithm-cli 생성기  (0) 2022.02.07
console.log vs process.stdout.write  (0) 2022.01.26
NestJs Request LifeCycle  (5) 2021.11.30
Package subpath './package.json' is not definded by "exports" in ~~~  (1) 2021.11.14
About. Typeorm  (0) 2021.11.10

+ Recent posts