본문으로 바로가기

 node로 구성한 서버에서 발생할 수 있는 타입 에러를 미연에 방지할 수 있어서 typscript를 도입하면 좋습니다. 물론 deno도 존재하지만 deno가 상용화되기 전에는 node에 TS를 붙여쓰도록 합시다.

 

우선 전체적인 아이디어는, ts로 작성하고, 트랜스파일한 내용을 node로 실행하는 것입니다.

 

설치 및 환경 세팅

 

노드와 npm은 설치가 되었다고 가정합니다.

npm i typescript
npm i @types/node

 

express도 @types버전과 일반 버전 둘 다 설치합시다.

npm i express
npm i @types/express

 

tsconfig.json은 다음과 같이 구성합시다.

컴파일 옵션에 대해서는 다음 핸드북을 참고하도록 합시다. include/exclude에 대한 설정은, 관리하고 싶은 대로 작성.

(https://typescript-kr.github.io/pages/compiler-options.html)

 

{
  "compilerOptions": {
    "strict": true,
    "target": "es5",
    "sourceMap": true,
    "module": "commonjs",
    "outDir": "build",
    "lib": [
      "ES2015", "ES2016", "ES2017", "ES2018","ES2019", "ES2020", "ESNext"
    ],
    "moduleResolution": "Node",
    "esModuleInterop": true
  },
  "include": ["src/**/*"]
}


여기서 esModuleInterop을 true로 줄것인가, default인 false로 줄 것인가에 대해서는 이견이 있습니다.

// esModuleInterop가 false(default)인 경우
import * as express from "express";

// esModuleInterop가 true인 경우
import express from "express

 

현재 우리는 @types/express를 쓰고 있습니다. 이 패키지의 타입을 정의하고 있는 /node_modules/@types/express/index.d.dts 에 들어가서 확인해보면, 

 

해당 라이브러리는 export default를 사용하고 있지 않습니다. 이 말은 export로 내보내는 것이 없으므로 해당 폴더의 내용 전체(*)를 가져오는 방식으로 패키지를 삽입해야 한다는 의미입니다. 

즉,  * as 를 사용해 import해야 한다는 뜻입니다. (import * as express from "express")

 

이러한 수고를 덜기 위해 tsconfig.json에서는 esModuleInterop에 true 값을 주어 해결합니다.

 

 

이제, 트랜스파일과 실행을 자동화하기 위해 tsc-watch를 이용해 include내의 .ts 가 변화를 감지하고 트랜스파일 성공시 build된 index.js를 node로 실행하도록 스크립트를 짜보았습니다.

 

그런데 이보다 ts-node를 통해 바로 확인하는 게 개발단계에서는 더 합리적인 것 같습니다.

"scripts": {
  "start": "tsc-watch --onSuccess \" node build/index.js\"", // 직접 빌드하기
  "dev": "cd src && nodemon --exec ts-node index.ts -e ts,graphql,json" // ts-node로 확인
},

 

express 코드 작성

 

index.ts 

어차피 트랜스파일을 다 해주니까 import/export를 쓰도록 합시다.

import express, { Request, Response, NextFunction, Application } from "express";
import dotenv from "dotenv";

dotenv.config();

const app: Application = express();
const PORT = process.env.PORT;

app.get("/", (req: Request, res: Response, next: NextFunction) => {
  res.send("home");
});

app.listen(PORT, () => console.log(`http://localhost:${PORT}`));

 

여기서, express 내에 정의된 타입을 불러와 타입 정의를 해주었습니다. Request, Response, NextFunction, Application

이 타입들은 @types/express 의 index.d.ts에 정의되어 있습니다.

 

그런데 위와 같은 코드는 무익합니다.

 

왜냐하면 남이 만든 라이브러리에는 특정 변수가 들어갈 위치나 타입이 이미 정해져있기 때문에 관련 타입이 문제를 일으키지 않는 이상은 그대로 두어 타입 추론을 하게끔 만들고, 코더가 스스로 작성한 부분에 타입을 활용하는 것이 좋습니다.

 

타이핑한 내용을 모두 지웠습니다.

import express from "express";
import dotenv from "dotenv";

dotenv.config();

const app = express();
const PORT = process.env.PORT;

app.get("/", (req, res, next) => {
  res.send("home");
});

app.listen(PORT, () => console.log(`http://localhost:${PORT}`));

 

깰끔~

 

 

참고하면 좋은 글

(https://novemberde.github.io/node/2017/10/22/Express-Typescript.html)

(https://github.com/Microsoft/TypeScript-Node-Starter)

(https://velog.io/@jhj46456/Node-with-Ts-simple-express)


darren, dev blog
블로그 이미지 DarrenKwonDev 님의 블로그
VISITOR 오늘 / 전체