본문 바로가기

javascript/typescript

[타입스크립트] express 를 typescript와 사용하기

 express는 자바스크립트와 사용해도 충분히 좋은 프레임워크이다. 하지만 특정 부분에서 자동완성이 작동하지 않기 시작하면서부터 나 자신에게 기억력 테스트를 요구하게 되며, 이로 인한 사기 저하를 뼈속 깊이 느끼게 된다.

 

 특히 함수를 작성할 때 이런 감정을 느꼈는데. express에서 미들웨어를 분리하는 순간부터 req, res, next에 어떤 값이 들어있는지 빈약한 머리 수준으로는 알 방법이 없어 몇번씩 되돌아 보는 등, 삽질을 한 경험이 있다.

 

 타입스크립트를 사용하면 위와 같은 눈물나는 상황은 나오지 않는다. 타입을 명시하여 자동완성이 잘 지원되기 때문이다. express에 이런 특징을 도입하여 좀더 편하게 사용해보자.


@nodes/express 설치

npm init -y
// 프로젝트를 default로 생성한다
npm install -g typescript
// typescript를 global로 설치한다
npm install express
npm install @types/express
// express 설치

 

기존 express를 설치한 프로젝트에 @types/express을 local로 설치한다.

 

@types 라는 이름으로 시작하는 패키지들은 자바스크립트를 이용해 사용하는 패키지들을 typescript와 사용할 수 있도록 도와준다. 보통 타입 추론 면에서 도움을 주기 위해 기존 패키지에 대한 타입 정보 등이 담겨있다.

 

웹은 아직까지는 타입스크립트 코드를 바로 사용하지 못한다. 그래서 현재 타입스크립트를 웹 상에서 사용하려면 이를 자바스크립트 코드로 컴파일하는 과정이 필요하다.

위와 같은 이유로 타입스크립트를 express와 사용할 때는 타입스크립트 코드를 자바스크립트 코드로 컴파일하는 과정이 필요한데, 컴파일은 타입스크립트 패키지와 함께 제공되는 CLI(Command Line Interface)인 tsc를 통해 가능하다.


tsc, 컴파일 & tsconfig.json

컴파일은 아래 코드 등을 이용하면 가능하다.

tsc --init // tsconfig.json 파일을 생성한다 
tsc // rootDir 아래의 모든 파일을 컴파일한다 -(A)
tsc index.js // index.js 파일만 컴파일한다 -(B)
tsc --watch // 타입스크립트 파일이 변하는지 검사하고, 실시간으로 컴파일한다

이때, (A)와 (B)의 동작은 조금 다르다.

 

(A)의 경우 tsconfig.json의 설정을 읽고, 해당 설정에 따라 컴파일을 진행한다.

(B)의 경우 기본 설정값을 기반으로 컴파일을 진행한다.

 

예를 들어 현재 tsconfig.json의 설정이 다음과 같다고 하자.

 

// tsconfig.json
{
...
    "module": "ESNext", 
    "rootDir": "./src",
    "outDir": "./dist"
...
}

위 설정의 의미는 다음과 같다.

  • "module": "ESNext" : 모듈로 다음 버전의 사양을 사용한다. (default :  "CommonJS" )
  • "rootDir": "./src" : 기본 폴더를 ./src로 설정한다 (default : "./" )
  • "outDir": "./dist" : 컴파일 경로를 "./dist로 설정한다 (default : "./", ts 파일 위치에 js 파일 생성 )

위 설정에 따라 (A) tsc 를 수행하면, 컴파일은 기본 폴더인 src 를 기준으로 수행되며, dist 폴더로 컴파일 결과를 내보낸다. 이때 컴파일은 ESNext에 맞춰 진행한다. 

 

tsc

outDir : dist

 

 

그러나 (B) tsc index.ts 를 실행하면, 위와 다른 결과가 발생한다.

 

위에서 언급했듯이, (B)는 기본 설정값에 기반하여 컴파일을 진행한다. 따라서, 우리가 정해 둔 tsconfig.json의 내용은 무효화된다. 만약 특정 옵션이 필요하다면 CLI 에 해당 옵션을 함께 제공해야 한다.

tsc src/index.ts

index.js의 모습. 옵션이 적용되지 않아 ES 모듈 스타일이 적용되지 않았다.

 

 

tsc src/index.ts --outDir "./ko"  // outDir 옵션을 같이 넘겨준 모습

ko 폴더에 파일이 컴파일된다

 

나머지 컴파일러 옵션들은 타입스크립트 공식 문서를 통해 찾아볼 수 있다.

 

Documentation - tsc CLI Options

A very high-level overview of the CLI compiler options for tsc

www.typescriptlang.org


npm CLI 옵션 건드리기

npm을 이용하여 프로그램을 실행하기 위해서는 CLI 옵션을 변경할 필요가 있다.

앞서 언급한대로 ts 파일 컴파일 -> 서버 실행 순서로 명령이 진행되도록 스크립트를 작성하자.

 

이때 새로운 코드를 반영하는 상황에 대해 서버를 2가지 경우로 나눌 수 있다.

  • 서버를 멈추는 경우: 서버를 멈추고 tsc로 컴파일 한 후 다시 기존 방식으로 실행
  • 서버를 멈추지 않는 경우: tsc-watch 또는 cocurrently를 이용

전자의 경우는 기존의 설정과 거의 다를게 없다. tsc를 통해 ts 파일을 컴파일한 후 서버를 다시 시작한다.

후자의 경우, 서버의 실행은 반드시 컴파일 이후에 수행된다는 명확한 순서관계가 존재한다. 이 순서를 구현하기 위해 많은 방법이 있을 수 있지만, 2가지를 소개한다.

 

1. tsc-watch

 

tsc-watch

The TypeScript compiler with onSuccess command

www.npmjs.com

tsc-watch는 기존 nodemon과 유사한 기능을 제공한다.

내부적으로 tsc(혹은 --compiler 옵션을 이용하여 교체 가능)를 이용하여 코드를 컴파일한 이후 --On~ 시리즈의 코드를 실행하는 방식으로 구현되었다. 이때 우리가 CLI에 입력하게 되는 컴파일 옵션들 및  --On~ 이후의 코드들은 cross-spawn 노드 패키지의 spawn을 통해 각각의 프로세스로 작동하게 되며, 특히 --On~ 코드들은 tsc 관련 프로세스에 대해 process.on('exit', ~) 으로 등록되어 해당 프로세스가 끝난 이후에 동작할 수 있도록 구현되어 있다.

 

사용 자체는 간단하다.

npm install tsc-watch //해당 패키지 설치
...

// in package.json
"scripts": {
	...
    "start": "tsc-watch --onSuccess \"node ./dist/index.js\"",
    ...
  },

위와 같은 코드를 작성하면 tsc 관련 작업이 끝난 후(onSuccess), "node ./dist/index.js" 코드를 spawn을 통해 새 프로세스를 생성하여 실행하게 된다.

 

--on~ 이후 코드들은 모두 spawn에 의해 생성되어 각각 실행된다.

 

2. concurrently

 

concurrently

Run commands concurrently

www.npmjs.com

concurrently는 이름 그대로 CLI 상에서 concurrently하게 코드를 실행하기 위해 만들어진 npm 패키지이다. 내부적으로 spawn-command를 통해 동시성을 구현했다. tsc-watch는 이벤트를 이용하여 컴파일 후 코드를 실행하는 반면, 이 방식은 &&을 이용하여 순서를 보장한다.

// in package.json
"scripts" : {
 	"start:watch2":"tsc && concurrently \"tsc -w\" \"nodemon ./dist/index.js\""
}

 

이 정도만 되도 타입스크립트와 express를 어느정도 같이 써먹을 수 있는 수준이 된다고 느낀다.

타입스크립트의 즐거움을 느끼자!