프로젝트 진행 중, 따라쳤던 tsconfig.json에 관하여 [TIL / TypeScript]

알락·2022년 12월 31일
1
post-thumbnail

프로젝트에 백엔드 언어로 TypeScript 이용을 결정하고, 프로젝트 초기 설정을 하는 과정 중 바로 문제에 직면했다. TypeScript가 Compile 하는 도구만 설치한다고 바로 프로젝트에 TypeScript가 적용되는 것이 아니었다. 어떤 것을 검사할 지에 대한 설정을 하는 작업이 필요했다. 하지만 아무것도 모른채로 "TypeScript를 사용하려면 다음과 같이 설정해야합니다"라는 블로그 글만 따라 설정을 하고 나서 제대로 작동한다는 것만 판단을 하고 난 이후에는 별 달리 이 부분을 건드리지 않았다. 지금 프로젝트가 끝나고 정리할 수 있는 시간을 갖는 동안 내가 어떤 설정을 했었는지 알아보기 위해서 이렇게 포스팅을 남겨본다.


tsconfig.json

[프로젝트에 적용된 설정값 tsconfig.json]

{
  "compilerOptions": {
    /* Language and Environment */
    "target": "es2016",

    /* Modules */
    "module": "commonjs",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "moduleResolution": "node",
    "esModuleInterop": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "noImplicitReturns": false,
    "strictPropertyInitialization": false,
  }
}

내가 사용한 설정들은 위와 같다. 적어도 위에서 사용한 설정들의 역할들을 정리해보려고 한다.

target

어떤 자바스크립트 버젼으로 컴파일할지 정하는 필드다. 타입스크립트 공식 문서에서는 대부분의 웹환경에서 es6을 지원하고 있기때문에 es6으로 지정해두는 것을 추천하고 있다. 이외의 버젼으로 컴파일을 하게 된다고 하면, 만들어지는 .js파일이 구동되는 환경이 해당 버젼의 자바스크립트를 확인할 필요가 있어보인다.

module

컴파일되는 파일에 적용되는 모듈형식을 지정한다. 타입스크립트 공식 문서에서는 노드 프로젝트에서 타입스크립트를 사용할 때는, "CommonJs"를 설정하는 것을 추천하고 있다. 대소문자는 구별을 하지 않는 것 같다.

헷갈린 점은 이 필드에서 하는 설정이, 컴파일을 하기 전 작성하는 ts 파일에서 사용하는 module 불러오기 방식을 지정하는 줄 알았다. 하지만 컴파일 후 만들어지는 파일이 작동되는 환경에서, 사용 가능한 module을 지정해주는 것이었다.

outDir

outDir에 경로를 설정해두면, tsconfig.json이 있는 파일위치로부터 상대주소로, 혹은 파일위치와 상관없는 절대주소로 컴파일된 .js 파일이 생성된다. 이번 같은 경우는 따로 명칭을 지정 안 하니까 npm run dev로 지정된 app.ts 파일명과 동일한 app.js 파일이 생성되었다.

npm run dev로 실행시키는 스크립트는 다음과 같다.
[package.json 중]

{
	...생략

	"dev": "nodemon --watch \"src/**/*.ts\" --exec \"ts-node\" src/app.ts",

	...생략
}

rootDir

이 필드에 지정된 디렉터리 경로에서부터 컴파일을 하게된다.

strict

별다른 문제가 없다면, strict 필드에 설정을 하는 값은 true를 권장한다. TypeScript에서 제공하는 주요한 타입 확인 기능을 true로 설정해주기 때문이다. 우선 true로 설정한 이후에 필요하지 않은 타입 확인 기능을 다른 필드에서 안 하게끔 설정해주는 방식을 추천한다.

다음은 strict로 인하여 true로 지정되는 값들이다.

  • alwaysStrict
  • strictNullChecks ★
  • strictFunctionTypes ★
  • strictPropertyInitialization
  • noImplicitAny
  • noImplictThis
  • useUnknownInCatchVariables

moduleResolution

모듈 해석 방식을 지정해주는 필드다. module 필드에서 CommonJS를 지정해줬으면 node로 지정해주는 것도 괜찮을 것 같다.

다음은 moduleResolution 필드에 올 수 있는 값들이다.

  • classic
  • node
  • node16
  • nodenext

esModuleInterop

기본값(false)으로 TypeScript는 CommonJS/AMD/UMD 모듈 형식을 ES6 모듈 형식처럼 다룬다고 한다.

다음은 공식 문서에서 드는 예시다.

  • 컴파일 전 -> 컴파일 후
  • import * as moment from "moment" = const moment = require("moment")
  • import moment from "moment" = const moment = require("moment").default

하지만 위에서 든 예시는 다음과 같은 문제를 발생시킨다고 한다.

  • ES6 모듈 사양은 import에 객체만 올 수 있다. 하지만 TypeScript=require("x")의 형식으로 모듈을 불러오기 때문에 함수를 임포트해와 호출할 수 있게 허용한다. 이는 ES6에서는 허용되지 않기 때문에 컴파일 과정에서 오류를 발생시킨다.
  • ES6 모듈 사양을 제대로 적용한다면, CommonJS/AMD/UMD 모듈 방식을 사용하는 대부분의 라이브러리들이 TypeScript의 엄격한 규제를 충족하지 못한다.

이를테면 esModuleInteropfalse를 이용하면 express 모듈을 불러올 때 다음과 같이 작성해야 한다.

import * as express from "express";

이는 express 프레임워크에 해당하는 모듈이 반환할 때 default를 지정해서 반환하지 않고 있기 때문이다.
esModuleInteroptrue로 설정하면 다음과 같이 불러올 수 있다.

import express from "express";

noImplictReturns

TypeScript 코드 작성 중 함수에 리턴값을 지정할 필요가 없다. 타입스크립트 설정으로 strict가 적용되어 있어도 true 로 지정되지 않아 따로 false를 지정할 필요가 없었는데 설정을 급하게 따라하다보니 필요없는 설정을 해주었다.

strictPropertyInitialization

타입스크립트 설정으로 strict가 적용되어 있으면 true로 설정되는 컴파일러가 확인하는 항목이다. 클래스 프로퍼티를 선언하면, 생성자 함수에서 해당 프로퍼티를 초기화 시켜줘야만 한다.

typeorm을 이용하여 프로젝트와 MySQL을 연동해줬다. DB 스키마를 생성할 때 클래스를 선언해주는데, 생성자 함수를 통해 설정해준 프로퍼티들을 초기화하지 않으니 문제가 발생했다. 문제를 없에기 위하여 false로 지정해줬었는데 true로 해도 다음과 같이 각 프로퍼티에 !를 붙여 작성하면 문제가 발생하지 않는다. !는 해당 프로퍼티에 값이 무조건 할당되어야 한다는 의미다.

[User Entity 클래스 선언 코드]

@Entity()
export default class User {
    @PrimaryGeneratedColumn()
    id!: number
   
    @Column({unique:true})
    address!: string

    @Column()
    name!: string

    @Column()
    createdAt!: Date
}

emitDecoratorMetadata & experimentalDecorators

위 설정 항목은 TypeScript에서 데코레이터 문법을 사용할 수 있게 하는 설정인데, 여기에서 설명하고자 하면 글이 길어지기 때문에 다른 포스팅으로 다뤄보도록 하겠다.

참고

profile
블록체인 개발 공부 중입니다, 프로그래밍 공부합시다!

0개의 댓글