-
[패캠] 타입스크립트 문법 - tsconfig.json 구성 옵션 (20/20, 끝)Front-end 개발 2024. 2. 1. 17:39
💡 본 타입스크립트 문법 시리즈는,
패캠의 '프론트엔드 웹 개발의 모든 것 초격차 패키지 Online'에 있는 TS 강의 노트이다.현재 수강 중인 패스트캠퍼스(fastcampus, 이하 패캠) 프론트엔드 패키지 강의가 있다.
강사님은 김영웅(Heropy) 님이다.
이 글을 쓰면서 강사님의 블로그에 '한눈에 보는 타입스크립트' 자료가 있다는 것을 알게 됬다... (바보😅)
강의를 듣는데, 강의자료가 없어서 일일이 강사님이 말씀을 따라 적으면서 강의 교안(?), 강의 노트를 작성했다.
혹시나 나처럼 패캠에서 타입스립트(TypeScript)를 수강하는 사람들에게 도움이 되면 좋겠다. 🔥
20. tsconfig.json 구성 옵션
타입스크립트 프로젝트를 시작하면 tsconfig.json 파일을 만들어서 제공해야 한다. tsconfig.json의 기본 및 추가적인 옵션들이 어떤 의미를 가지고 있는지 정리해본다.
아래 json 파일은 현재 나의 타입스크립트 프로젝트의 옵션들이다. 이 옵션들은 최소한의 옵션이면서 굉장히 중요한 옵션이기 때문에 꼭 기억해 두어야 한다. (Json 파일은 주석을 지원하지 않지만 이해를 위해 주석으로 표현하여 작성했다.) 이 옵션들을 기억해 두어야 나중에 다시 다른 문서를 보지 않더라도 새로운 타입스크립트 프로젝트를 만들 수 있다.
{ // 컴파일러 옵션 지정 "compilerOptions": { "target": "ES2015", "module": "ESNext", "moduleResolution": "Node", "esModuleInterop": true, "lib": ["ESNext", "DOM"], "strict": true, }, // 컴파일할 파일 경로 목록 "include": [ "src/**/*.ts" ], // 컴파일에서 제외할 파일 경로 목록 "exclude": [ "node_modules" ] }
tsconfig.json 파일은 크게 3가지 옵션(compilerOptions, include, exclude)을 제공하고 있다. 각각의 옵션은 순서대로 컴파일 할 때 필요한 세부 옵션들, 어떤 경로에 있는 타입스크립트 파일을 포함해서 컴파일 할 것 인지, 반대로 어떤 경로는 컴파일에 포함하지 않을 것 인지를 결정한다.
컴파일에 포함할 타입스크립트 경로: include
include는 어떤 타입스크립트 파일들을 컴파일해서 JavaScript로 변환할 것인지 그 파일에 대한 경로가 어디에 있는지 배열 데이터로 목록화하여 제공한다.
아래 코드는 src 폴더 안에 들어있는 하위 경로에 모든 이름을 가질 수 있는 ts라는 확장자를 가진 타입스크립트 파일을 JavaScript로 컴파일을 하겠다고 설정하는 것이다. 만약 src 폴더 말고도 예를 들어 추가적인 api 폴더 경로가 필요하다면 include 옵션의 배열에 타입스크립트 경로를 추가할 수 있다.
... "include": [ "src/**/*.ts", "api/**/*.ts" ], ...
컴파일에서 제외할 타입스크립트 경로: exclude
최종적으로 JavaScript로 컴파일을 하는데, 그때 필요한 경우 제외할 수 있는 파일이나 폴더의 경로를 명시할 수 있다. 기본적으로 node_modules가 컴파일의 대상 경로가 되지 않도록 명시해줄 수 있다.
컴파일 상세 옵션: compilerOptions
compilerOptions는 작성된 타입스크립트 코드를 JavaScript로 변환하기 위해서 어떤 옵션들이 세부적으로 필요한지 결정해주는 옵션이다.
아래 명시해 놓은 각각의 속성의 값들은 전부 다 기본 값에 해당한다. 그래서 따로 속성을 지정하지 않으면 이 값들이 적용된다.
{ // 컴파일러 옵션 지정 "compilerOptions": { // 컴파일될 ES(JS) 버전 명시 - "ES2015" 권장 "target": "ES3", // 모듈 시스템 지정 - "CommonJS", "AMD", "ESNext" "module": "CommonJS", // 모듈 해석 방식 지정 - "Node", "Classic" "moduleResolution": "Node", // ESM 모듈 방식 호환성 활성화 여부 "esModuleInterop": true, // 모든 파일을 모듈로 컴파일, import 혹은 export 키워드 필수 "isolatedModules": false, // 모듈 해석에 사용할 기준 경로 지정 "baseUrl": "./", // 컴파일러가 참조할 타입 선언(d.ts)의 경로를 지정 "typeRoots": ["./node_modules/@types"], // 컴파일에서 사용할 라이브러리 지정 - "ESNext", "DOM" "lib": ["ESNext", "DOM"], // 더 엄격한 타입 검색 활성화 "strict": false, // 암시적 any 타입 검사 활성화 "noImplicitAny": false, // 암시적 this 타입 검사 활성화 "noImplicitThis": false, // }, // 컴파일할 파일 경로 목록 "include": [ "src/**/*.ts" ], // 컴파일에서 제외할 파일 경로 목록 "exclude": [ "node_modules" ] }
- target: 타입스크립트는 항상 JavaScript로 변환이 필요하다. target 옵션은 타입스크립트를 JavaScript로 컴파일할 때 JavaScript의 어떤 버전으로 변환할지 명시할 수 있다. target의 기본 값은 ES3이다. ES3는 1999년도에 나온 JavaScript 버전으로 너무 오래 됐다. 그러다 보니 지원할 수 있는 기능도 굉장히 한정적이기 때문에 되도록이면 다른 버전으로 명시하는 것이 좋다. ES6 버전, 그러니까 2015년도에 나온 ES2015 버전을 사용하는 것을 권장한다. 현대에 와서 전 세계 대부분의 사용자들이 쓰는 웹브라우저는 기본저으로 JavaScript의 ES2015 버전 또 다른 표기로 ES6 버전을 지원한다. 그래서 ES6 버전이 2015년도에 나왔기 때문에 ES6 버전과 ES2015 버전이 같은 개념이다. 그런데 되도록 연도 방식의 표기법을 쓰는 것을 권장한다. target을 ES2015로 설정하면 타입스크립트 코드를 다 작성해서 결과를 확인해보면 그 결과 코드는 ECMAScript의 2015년도 버전의 JavaScript로 컴파일 된다. target을 너무 최신 버전으로 하면 약간 오래된 버전의 브라우저를 사용하는 사용자들에게는 우리의 프로젝트가 문제가 되어 출력이 되지 않을 수 있다. 그렇기 때문에 적당한 수준으로 target의 JavaScript 버전 명시를 하자. 나의 프로젝는 parcel 번들러가 빌드를 해서 dist 폴더에 결과를 만들어 준다. 그랬을 때 dist 폴더 내부에 index 이름 뒤로 해시(hash)가 붙어서 JavaScript 파일이 만들어지는데 이 JavaScript 파일이 내가 작성한 타입스크립트 코드가 변환이 돼서 들어가는 구조이다.
- module: 모듈 시스템을 결정할 수 있는 옵션이다. JavaScript를 쓰는 환경이 크게 두 가지로 나뉜다. 하나는 웹 브라우저가 있고, 하나는 컴퓨터를 제어하는 Node.js 환경이 있다. 각각의 환경에서는 서로 다른 모듈 시스템을 사용하고 있다.
>> Node.js 환경: CommonJS 방식의 모듈 시스템
>> Web Browser 환경: ECMAScript Module(ESM == ESNext) 방식의 모듈 시스템
module 옵션의 기본 값은 CommonJS인데, ESNext 방식으로 바꿔 사용하는 것을 권장한다. ESNext는 ECMAScript의 가장 최신 버전의 모듈 시스템에 해당한다. - moduleResolution: module 옵션을 지정했다면, 해당 옵션을 지정할 수가 있다. 기본 값은 node이며 이 옵션에는 Node나 Classic이라고 값을 쓸 수 있다. 우리에게 익숙한 방식은 Node 방식이다. 우리가 어떤 모듈을 가져오려고 할 때, 파일 이름을 생략 할 수 있다. 예시로 store 폴더 내에 index.ts를 생성하고 아래와 같이 이름 변수를 내보낼 수 있다. 그랬을 때 main.ts 파일에서 import 키워드로 name 변수를 가져와 쓰려고 from 부분에 경로를 적을 때 index.ts라고 전체 경로를 적을 수도 있지만, ts라는 확장자와 폴더 안에 들어 있는 index라는 이름도 생략이 가능하다. 그러니까 폴더의 이름만 작성을 하더라도 원하는 결과를 가지고 올 수 있다.
// store 폴더 하위 index.ts export const name = "Heropy"
Classic은 다른 방식을 사용한다. 우리에게 익숙한 것은 node 방식이기 때문에 특수한 경우가 아니라면 moduleResolution 옵션은 기본 값으로 node를 사용하는 것을 권장한다.// main.ts import { name } from './store/' // ts 확장자와 index는 생략가능 console.log(name) // 'Heropy'
- esModuleInterop: 해당 옵션은 ESM 모듈 방식 호환성 활성화 여부이다. 말이 조금 어려운데, 기본 값은 false이지만 true로 값을 지정하기를 권장한다. common.js 방식 같은 경우에는 ESM 방식과 달리 이름을 가지는 내보내기만 존재하고 기본 내보내기 방식은 없다는 차이점이 있다. 그래서 평소에 사용하는 ESM 방식 같은 경우에는 export default 를 통해서도 데이터를 내보낼 수 있고, 혹은 export에 const 키워드로 이름을 가지는 내보내기도 가능하다.
esModuleInterop: true 인 경우 (Common.js 방식)
이해를 돕기 위해 Node.js에서 활용할 수 있는 Common.js 방식의 JavaScript 파일을 하나 만들고, 이 내용을 main.ts에서 가져와서 사용을 해보고자 한다.
// common.js const heropy = { name: 'Heropy', age: 85 } module.exports = { heropy }
// main.ts import commonjs from './common' // d.ts 파일이 없으면 Error console.log(commonjs.heropy)
아래 예시 코드를 보면 main.ts 파일에서 import 키워드가 있는 경로 부분에서 Error가 발생한다. 이유는 타입스크립트 파일에서 타입 선언이 존재하지 않는 JavaScript 파일을 가져와서 쓰고 있기 때문이다. 즉, 타입 선언 파일인 dts 파일을 제공하지 않으면 에러가 발생한다.
// common.d.ts interface User { name: string age: number } declare const heropy: User export { heropy }
dts 파일을 만들고 타입 선언을 제대로 가지고 오려면 삼중 슬래시 지시자를 통해서 reference 라는 태그를 만들어주고 path 속성으로 타입 선언 파일의 경로를 확장자까지 포함하여 명시해줘야 한다.
// main.ts /// <reference path="./common.d.ts" /> import commonjs from './common' // d.ts 파일이 없으면 Error console.log(commonjs.heropy)
간혹 타입을 제공하더라도 Error가 유지되는 경우가 있는데 MAC에서는 Command+Shift+P, WINDOWS는 Ctrl+Shift+P 키를 눌러서 restart라고 입력을 한다. 그러면 자동 생성 옵션 중에 있는 ‘TypeScript: TS 서버 다시 시작’ 메뉴를 선택하여 타입스크립트를 새로 고침되고 Error가 사라진다.
JavaScript 파일인 common.js 을 브라우저에서 사용하는 ESM 방식이 아닌, Node.js에서 사용하는 Common.js 방식으로 만들었고, 그것을 타입스크립트에서 해석하기 위해서 common.d.ts 파일을 통해 타입 선언을 제공했다. 그리면 import 키워드를 통해서 내용을 잘 가지고 올 수 있게 된다. 하지만 esModuleInterop 옵셥의 값을 변경하면 어떻게 되는지 살펴보자.
esModuleInterop: false 인 경우 (ESM 방식)
해당 옵션 값을 false로 변경하면 다시 가져오는 코드 라인에서 Error 가 발생한다. Common.js 방식은 import 키워드를 통해서 전체 내용을 가지고 오는 것은 할 수 없기 때문이다. 왜냐하면 기본 내보내기(export default)라는 개념이 Common.js 의 모듈 시스템 방식에는 존재하지 않기 때문이다. 기본 내보내기가 가능한 ESM 방식은 전체 내용을 별칭을 사용하여 재사용 가능하다. 그래서 esModuleInterop 이라는 호환성을 제공하겠다는 옵션을 꺼버리면 문제가 발생하게 된다. 이 부분은 JavaScript 모듈 가져오기 내보내기 파트에서 배운 것처럼 전체 내용을 as 키워드를 통해서 commonjs라는 이름의 일종의 변수에 전부 다 할당하는 방식으로 수정한다.
// tsconfig.ts import * as commonjs from './common' // Common.js 방식을 따로 호환되도록 코드 작성 console.log(commonjs.heropy)
Common.js 방식은 이렇게 별 표시를 사용하는 wildcard 문자와 as 키워드를 앞쪽에 붙여줘야 마치 기본 내보내기 방식처럼 내용을 쓸 수 있다. 그런데 우리가 프로젝트를 만들 때 매번 Common.js 방식과 ESM 방식의 패키지 내용을 구분해서 가져오기 어렵다. 그래서 esModuleInterop 이라는 모듈 방식의 호환성을 활성화하는 옵션을 켜게 되면 Common.js 방식의 내용을 가지고 오는 것도 문제가 없다. 그리고 Common.js 방식의 모듈을 ESM의 모듈 방식으로 가지고 오는 것도 문제가 없다.
실제로 개발을 하다 보면 외부에서 npm install로 설치할 수 있는 다양한 패키지들이 ESM 방식과 Common.js 방식이 섞여 있다. 그럴 때마다 구분하지 않고 이렇게 한 가지의 방식으로 내용을 가지고 올 수 있게 만들어줄 수 있다. 그렇기 때문에 되도록 해당 옵션을 true로 활성화 하는 것을 권장한다.
esModuleInterop 옵션은 기본값이 false 이지만, Common.js 방식과 ESM 방식을 모두 호환해서 기본적인 ESM 방식처럼 사용하게 하려면 옵션의 값을 true로 활성화해야 한다.
- isolatedModules: 모든 파일을 모듈로 컴파일 한다. 그래서 import 키워드 혹은 export 키워드가 모든 타입스크립트 파일에 지정이 되어 있어야 한다. 해당 옵션의 기본값은 false이다. JavaScript나 타입스크립트에서 별도의 import나 export 키워드를 사용하지 않으면 그 파일은 모듈로 취급되지 않는다. 그런데 isolatedModules 옵션을 true 값을 주면, 모든 파일은 모듈이어야 한다고 옵션을 제공하기 때문에 import 혹은 export 키워드가 없는 파일은 Error가 발생한다. 즉, 빈 파일의 경우도 Error가 발생하게 되는데 이러한 경우 아무것도 내보낼 필요가 없는 상황일 수도 있겠지만 export 키워드를 통해서 빈 객체 데이터 형식으로 내용을 내보내는 것도 Error를 해결하는 좋은 방법이 될 수 있다. 모든 프로젝트의 파일을 전부 모듈로 바꾸는 것이 여러모로 관리하기 용이할 수 있다. 이런 부분을 조금 더 강제할 수 있는 옵션이라고 보면 된다. 간혹 발생할 수 있는 모듈 구조와 관련된 Error들도 이 옵션을 통해서 어느 정도 방지해줄 수 있기 때문에 특별한 이유가 없다면 되도록 옵션을 켜는 것(true)도 좋은 방법이다.
- baseUrl: 모듈 해석에 사용할 기준 경로를 지정한다. 일반적인 경우는 tsconfig.json 파일이 루트 경로에 위치한다. 상황에 따라서는 이 파일이 프로젝트의 어떤 특정한 폴더 안에 들어 있을 수도 있다. 만약 api라는 폴더 안에 새로운 tsconfig.json 파일이 존재하고, 이 파일을 통해 src 폴더를 컴파일 해야 된다고 한다면 이렇게 json으로 compiler options를 제공해서 baseUrl 옵션으로 기준 경로를 지정할 수 있다. baseUrl을 통해서 src 파일과 tsconfig.json 파일이 동일한 경로 상에 위치하지 않은 경우라면, 컴파일의 대상이 되는 src 파일의 기준 경로가 어디 인지를 명시할 수 있다. 기본 값은 프로젝트 폴더의 루트 경로(./)이다.
{ "compilerOptions": { "baseUrl": "../src/" } }
- typeRoots: 컴파일러가 참조할 타입 선언(d.ts)이 어느 경로에 있는지 지정한다. 기본 값을 보면 배열 데이터 내부에 node_modules 폴더 안에 들어 있는 @types 폴더이다. 그래서 대표적으로 types에 node라는 패키지에 definitely typed 를 설치를 했던 적이 있다. 프로젝트를 만들다 보면 사용하는 패키지에 type 선언을 이렇게 따로 설치해야 되는 경우는 생각보다 자주 있다. 이렇게 @types 폴더 안에서 해당하는 타입 선언 내용을 찾을 수 있게 만들어 줄 수 있다. 혹은 다른 폴더를 내가 직접 지정하고 싶다면 배열 데이터 내부로 들어가서 새로운 경로를 추가 제공할 수도 있다.
"typeRoots": [ "./node_modules/@types", "./src/types" ],
- lib: 라이브러리 옵션으로 타입스크립트를 JavaScript로 컴파일할 때 필요한 내부 라이브러리를 설정할 수 있다. 기본적으로 ESNext, 즉 JavaScript 최신 버전에 해당하는 내용과 대부분의 경우 브라우저에서 동작할 수 있는 JavaScript를 다루기 때문에 타입스크립트가 브라우저의 DOM (Document Object Model) 에 해당하는 내용도 이해할 수 있도록 라이브러리를 제공하는 것을 권장한다.
"lib": ["ESNext", "DOM"],
- strict: 엄격하다는 의미를 가진다. 즉 더 엄격한 타입 검사를 활성화 한다. 기본 값은 false 이다. 타입스크립트를 사용하는 이유 중에 굉장히 중요한 포인트가 기존의 JavaScript 보다 훨씬 더 엄격하게 문법을 쓰기 위함이다. 만약에 strict 부분의 값을 기본 값인 false로 유지하고 타입스크립트를 사용한다면 타입스크립트의 굉장히 중요한 장점 중에 하나가 없어지는 것이기 때문에 되도록이면 strict 값을 꼭 true 로 바꾸기를 권장한다.
strict 옵션 값을 true로 바꾸게 되면 그 이하에 있는 암시적 any 타입 검사, 암시적 this 타입 검사, 기타 등등의 내용들이 전부 다 기본 값인 false에서 true로 바뀌게 된다. 그 이하 상세 내용들을 전부 다 true 값으로 바꾸지 않더라도 strict 부분의 옵션만 true로 바꿔주면 나머지 type 검사는 자동으로 엄격해진다.
// 더 엄격한 타입 검색 활성화 "strict": true, // 암시적 any 타입 검사 활성화 // "noImplicitAny": false, // -> true // 암시적 this 타입 검사 활성화 //"noImplicitThis": false, // -> true // 엄격한 Nullish 타입 검사 활성화 // "strictNullChecks": false, // -> true // 엄격한 함수의 매개변수 타입 검사 활성화 // "strictFunctionTypes": false, // -> true // 엄격한 클래스의 속성 초기화 검사 활성화 // "strictPropertyInitialization": false, // -> true // 엄격한 Bind, Call, Apply 메소드의 인수 검사 활성화 // "strictBindCallApply": false // -> true }
타입스크립트에서 제공하는 엄격한 타입 검사의 옵션들의 전부를 다뤄본 것은 아니다. 훨씬 더 많은 옵션들이 존재하는데, 그 중에서 몇 가지는 strict 옵션을 true로 바꾸더라도 값이 변하지 않는 옵션들도 있다. 그런 것들은 지금 당장 중요한 옵션들은 아니기 때문에 이 정도 기본적인 컴파일 옵션만 이해하더라도 타입스크립트를 사용함에 있어서 전혀 문제가 되지 않는다.
Reference
김영웅 강사님 블로그 中 TS 파트
https://heropy.blog/2020/01/27/typescript/
김영웅 강사님 유튜브 채널
https://www.youtube.com/channel/UCcjhMpoaNvyy0StN9KgtF6w
'Front-end 개발' 카테고리의 다른 글
[멋쟁이빌더] 지원 (탈락 ㅠ) (0) 2024.02.05 [TS] JavaScript에 없는 튜플 (tuple) 타입 (0) 2024.02.03 [패캠] 타입스크립트 문법 - 타입 가져오기와 내보내기 (export, import) (19/20) (0) 2024.02.01 [패캠] 타입스크립트 문법 - 패키지의 타입 선언 (18/20) (0) 2024.02.01 [패캠] 타입스크립트 문법 - 제네릭 인터페이스와 제약 조건 (generic) (17/20) (0) 2024.02.01