TypeScript란?
🧐 TypeScript, 왜 사용해야 할까?
Javascript는 동적 타입 언어로, 개발환경에서 타입에러가 종종 발생한다. 하지만 TypeScript를 사용하면 컴파일 단계에서 미리 타입체크를 할 수 있어 타입오류를 최소화 할 수 있다. 그 외에 별칭을 사용하여 자동완성 및 작성이 편리하며 에러를 방지할 수 있다고 한다. 한 번 TypeScript를 알아보고자 한다.
TypeScript는 JavaScript를 포함하는 수퍼셋으로 브라우저, 운영체제에 상관없이 이용가능한 오픈소스이다.
- JS에서 타입 검사가 확장, 추가된 언어이다.
- JS의 상위 확장(수퍼셋)으로, 대형 프로젝트를 진행할 때 오류검사가 쉽다.
- 객체지향 프로그래밍에 특화된 프로그래밍 패턴을 지원한다.
- 함수형 프로그래밍에서 타입검사나 추론 등의 기능을 사용한다.
- typescript를 사용하면 JS 작업할 때 보다 개발에서 생기는 에러를 사전에 방지하며 코드의 품질과 개발 생산성을 높일 수 있다.
- 런타임이 존재하지 않는 컴파일 언어이다.
🤷♀️ 런타임이 존재하지 않는 언어?
TypeScript는 컴파일 언어이다.
우선 결론을 먼저 말하면 TypeScript는 컴파일 단계를 통해 TypeScript 코드를 JavaScript코드로 변환해 실행되는 언어를 말한다. TypeScript는 정적 검사와 인터페이스, 클래스 및 여러가지 JS에 없는 구조를 제공한다. 하지만 Node나 브라우저에서 이해할 수 있는 언어의 형태가 아니므로 실행하기 전 컴파일 과정을 거쳐 JS 코드로 변환되어야 한다.
컴파일 과정을 거쳐 코드를 실행시키지만, JavaScript의 런타임 환경과 호환성을 유지하기 위해 별도의 런타임이 존재하지 않는다. TypeScript의 중점은 개발 경험을 향상시키고 오류를 초기에 감지하여 더 나은 코드 구성과 유지 관리성을 제공하는데 있기 때문이다.
TypeScript의 특징
컴파일 언어, 정적 타입 언어
- 자바스크립트는 동적 타입의 인터프리터 언어로, 런타임에서 오류를 발견할 수 있다.
- 하지만 타입스크립트는 정적 타입의 컴파일 언어이며, 타입스크립트 컴파일러 또는 바벨(Babel)을 통해 자바스크립트 코드로 변환된다.
- 코드 작성 단계에서 타입을 체크해 오류를 확인할 수 있고 미리 타입을 결정하기 때문에 실행 속도가 매우 빠르다는 장점이 있다.
- 코드 작성 시 매번 타입을 결정하기 때문에 번거롭고 코드량이 증가하며 컴파일 시간이 오래 걸린다는 단점이 있다.
자바스크립트 슈퍼셋 (Superset)
- 타입스크립트는 자바스크립트의 슈퍼셋, 즉 자바스크립트의 기본 문법에 타입스크립트 문법을 추가한 언어이다.
- 유효한 자바스크립트로 작성한 코드는 확장자를 .js => .ts 로 변경하고 타입스트립트로 컴파일해 변환할 수 있다.
객체 지향 프로그래밍 지원
- 타입스크립트는 ES6에서 새롭게 사용된 문법을 포함하고 있으며, 클래스, 인터페이스, 상속, 모듈 등과 같은 객체지향 프로그래밍을 제공한다.
TypeScript 변수 지정 타입
JS의 경우, ' 변수명 = 초기값 ' 해딩 방법으로 변수를 선언했었지만, TS의 경우에는 ' 변수명 : 타입명 = 초기값 '의 형태로 변수를 선언한다.
const msg = "javascript"; // JS에서 사용했던 변수 선언식
const msgg: string = "message string type";
console.log(msgg); // 결과 : message string type
TypeScript 설치
# package.json 초기화
npm init -y
# 개발 단계에서 사용 -D => --save-dev와 동일
npm install -D typescript
# 설치가 되었는지 버전 확인
npx tsc --version
TypeScript 사용하기
개발 환경에서 확인하기 위해서 node에서 실행하기 위해서는 ts-node를 사용해야 한다. 그렇다면 ts-node가 무엇일까??
🤷♀️ ts-node?
- Javascript로 컴파일하기 전에 node환경에서 직접 실행해볼 수 있는 도구이다.
- 기존 JS 파일은 node를 통해 실행시켰는데, TS 파일은 node가 해석할 수 없기 때문에 타입 해석이 가능한 ts-node로 실행해줘야 한다.
- TypeScript 코드를 실시간으로 변환하여 실행한다. 코드를 실행할 때 타임스크립트 코드를 메모리에 있는 JS로 컴파일 하는 것이다.
ts-node TypeScript 실행환경
- typescript를 컴파일 내부 컴파일러를 통해 메모리 상에 js코드로 변환한다.
- js 파일을 만들지 않고 컴파일한 js코드로 메모리상에 갖고 있는다.
- 컴파일된 js 코드를 nodejs 런타임 환경으로 실행하고 코드 실행 결과를 출력한다.
- 컴파일 단계에서 타입 검사로 코드에서 발생할 오류를 사전에 알려줌으로써 런타임 이전에 잠재적인 문제를 발견할 수 있다.
ts-node 사용하기
# 설치 명령어
# node.js는 js 런타임 환경인데 내장 함수 및 모듈에 대한 타입 정보가 필요하다.
# @types/node: nodejs 타입 정보를 패키지로 설치해서 사용
npm install ts-node @types/node
# 실행 가이드
# js => node 환경에서는 ' node app.js '로 실행했었는데,
# typescript => ts-node 환경에서는 아래와 같은 방법으로 사용한다.
npx ts-node app.ts
tsconfig.json은 타입스크립트를 자바스크립트로 변환 시키는 컴파일 설정을 한꺼번에 정의 해놓는 파일인데, 프로젝트를 컴파일 하는데 필요한 루트 파일, 컴파일러 옵션 등을 상세히 설정할 수 있다. 이 아래에서는 tsconfig에 대해서 알아보고자 한다.
tsconfig.json
- typescript의 컴파일 과정 옵션을 설정할 수 있는 파일이다.
- 보통 tsconfig.json 파일은 TypeScript 프로젝트의 루트 디렉토리에 위치한다.
- tsconfig에서 옵션들을 미리 정의해 놓으면 컴파일 할 때 명령어에 일일히 대상 파일이나 옵션을 지정하지 않아도 된다.
- tsc나 ts-node 명령어를 실행하게 되면, 현재 폴더에 있는 tsconfig 설정 내용을 기준으로 프로젝트에서 소스들을 컴파일 진행하게 된다.
- 잔소리꾼이다. 하위 경로에 규칙이 맞지 않는 구문을 발견하면 수정하라고 알려준다.
# tsconfig.json 생성 명령어
npx tsc --init
디렉토리 지정 방법
./ | 현재 디렉토리에서 모든 파일의 경로. 즉 현재 폴더에 모든 파일을 컴파일 대상을 지정한다. | |
/** | 모든 폴더를 가리킨다. | |
/* | 모든 파일을 가리킨다. | |
ex | src/**/* | src 폴더의 하위 폴더와 폴더 안에 있는 모든 파일의 경로를 지정한다. |
tsconfig.json 파일에서 컴파일 옵션을 설정한다.
- tsconfig 속성은 굉장히 많은 종류가 있지만 아래의 속성은 수업에서 나온 속성으로 다른 속성은 수업을 진행하면서 천천히 숙지하고자 한다.
{
// include : 컴파일을 진행할 폴더를 지정할 수 있다.
"include": ["./*"],
// exclude : 컴파일에서 제외 시킬 폴더를 지정할 수 있다.
"exclude": [],
// compilerOptions : typescript 파일을 컴파일 진행 시 어떤 옵션으로, 어떤 형태로 컴파일을 진행할지 속성을 정의
"compilerOptions": {
// 컴파일된 파일을 내보낼 경로 지정
"outDir": "./dist"
}
}
tsconfig 전역 속성
tsconfig 전역 속성이란, 파일의 최상위에 위치하고 있는 속성들을 일컫는다.
- compilerOptions : typeScript 파일을 컴파일 진행 시 어떤 형태로 컴파일을 진행할 지 속성 정의하는 부분
- include : 컴파일을 진행할 폴더를 지정
- exclude : 컴파일에서 제외할 폴더 지정
compilerOptions 자주 사용하는 속성
- module : 모듈 시스템 지정
- outDir : 내보낼 경로 지정
- target : 번들링 문법 지정
- 예) es5, es6 ... 등의 문법을 지정한다.
- esModuleInterop : true
- (import/export 문법을 자연스럽게 변경 해주는 행위)
- 주로 true로 설정한다.
- (CommonJS 형식과 ES6 형식의 혼용을 자연스럽게 통합 해주는 속성이다.)
- strict : true로 두자. 엄격
- baseUrl : 모듈의 상대 경로를 지정한다.
- paths : 'baseUrl' 경로를 기준으로 상대 위치를 가져오는 매핑 값 (경로를 변수처럼 사용한다.)
package.json 파일에서 명령어를 추가한다.
- tsc 명령어는 typescript 컴파일을 실행하는 명령어이다.
- typescript 코드를 tsconfig.json 파일 설정 값을 가지고 js 코드로 변환해준다.
"scripts" : {
"build" : "tsc"
}
👩🏫 동작예시
src/message.ts 파일
import a from "@user/server/user.service";
const message: string = "hello world";
console.log(message);
console.log(a);
src/message.test.ts 파일
const message2: string = "test";
console.log(message2);
src/user/server/user.service.ts 파일
const variable: string = "user";
export default variable;
⬇️
VSC 의 터미널에서 아래의 순서대로 파일 생성 및 패키지 설치
# 새로운 package.json 파일을 생성
npm init -y
# typescript 패키지 설치 (프로젝트 시작 시 1회만 설치)
npm install -D typescript
# tsconfig.json 파일을 생성
npm tsc --init
⬇️
tsconfig.json 파일에서 타입스크립트 컴파일 속성 정의
tsconfig.json 파일
{
"compilerOptions": {
"module": "CommonJS",
"outDir": "./dist",
"target": "ES6",
"esModuleInterop": true,
"strict": true,
"baseUrl": "./src",
"paths": {
"@user/*": ["user/*"]
}
},
"include": ["src/**/*"],
"exclude": ["**/*.test.ts"]
}
⬇️
package.json에 빌드 속성 추가
....
"scripts": {
...
"build": "tsc"
},
....
⬇️
VSC 의 터미널에서 타입스크립트 컴파일 진행
# 타입스크립트 컴파일
npm run build
⬇️
컴파일된 파일의 내용이다.
dist/message.js 파일
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const user_service_1 = __importDefault(require("@user/server/user.service"));
const message = "hello world";
console.log(message);
console.log(user_service_1.default);
dist/user/server/user.service.js 파일
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const variable = "user";
exports.default = variable;
tsconfig.json 파일에서 설정했던 ' "exclude": ["**/*.test.ts"] '의 내용이 적용되어, message.test.ts 파일이 컴파일되지 않았음을 확인할 수 있다.
dist/message.js 파일의 코드를 보면,
const user_service_1 = __importDefault(require("@user/server/user.service"));
이런 구문을 확인할 수 있다. 하지만, JS 코드 문법으로는 ' @user/server/user.service ' 라는 경로는 읽어올 수 없는 문법 형식이다. 이런 별칭을 사용한 것을 빌드 시에는 컴파일 되지 않고 그대로 들어가게 되는데 이 별칭을 컴파일 시 JS 문법으로 변환해주는 패키지가 있다. tsc-alias 모듈이다.
* 별칭?
- 모듈 별칭은 TypeScript 프로젝트 내에서 모듈에 대한 더 짧거나 의미 있는 경로를 생성하는 것을 의미한다.
- 긴 상대 경로(../../../)나 절대 경로 대신 별칭을 정의하여 특정 디렉터리 경로에 매핑할 수 있다.
- 별칭을 설정하면 코드가 더 읽기 쉽고 유지 관리가 용이하다.
tsc-alias로 별칭 설정하기
tsc-alias은 내장된 TypeScript나 Node.js의 기능은 아니며, TypeScript 프로젝트에서 모듈 별칭을 사용하는 방식을 나타낸다. 특히 대규모 프로젝트에서 가독성을 높이고 경로 참조를 간결하게 관리하기 위해 모듈 경로에 별칭을 설정하는 일반적인 방법이다.
# tsc-alias 모듈 설치
npm install -D tsc-alias
// package.json 추가. 별칭 수정 경로 추가
....
"scripts": {
...
"build": "tsc && tsc-alias"
},
....
다시 터미널에서 ' npm run build ' 후 빌드된 파일을 확인해보면,
dist/message.js 파일
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const user_service_1 = __importDefault(require("./user/server/user.service"));
const message = "hello world";
console.log(message);
console.log(user_service_1.default);
해당 코드로 변경되었다. 아까 확인할 수 없었던 경로 부분이
const user_service_1 = __importDefault(require("./user/server/user.service"));
이렇게 변경되어 JS 문법으로 확인할 수 있는 코드임을 확인할 수 있다.
JS와 TS 문법
// javascript
let num = 20;
const str = "javascript";
const nan = NaN;
const infinity = Infinity;
const bool = true;
const nullValue = null;
const nudefinedValue = undefined;
const obj = {};
const arr = [];
const fn = (a: any) => {
console.log(a);
};
const sum = (a: any, b: any) => {
return a + b;
};
let any;
let unknown;
// typescript
let num2: number = 20;
const str2: string = "javascript";
const nan2: number = NaN;
const infinity2: number = Infinity;
const bool2: boolean = true;
const nullValue2: null = null;
const undefinedValue: undefined = undefined;
const obj2: object = {};
// <> : 제네릭 타입 설정. 배열의 요소가 number라고 지정. (데이터 타입을 매개변수 시킬 수 있다.)
// ex. <number | string> : number 또는 string 타입을 사용할 수 있다는 뜻.
const arr2: Array<number | string> = ["a", 1];
// void: 함수 실행 시 비어있다는 것을 의미. 반환 값이 없는 함수라는 것이다.
// void는 생략할 수 있다.
const fn2 = (a: number): void => {
console.log(a);
};
// 함수명 = ():return의 타입을 지정 => {}
const sum2 = (a: number, b: number): number => {
return a + b;
};
// any: 어떤 타입이든 할당할 수 있다. 되도록 남발하지 말자. 타입의 안정성이 보장 되지 않는다.
const any2: any = "";
console.log(any2.length); // length가 있던, 없던 실행된다.
// unknown : 어떤 타입이든 할당 가능. 타입의 안정성 보장
const unknown2: unknown = "";
if (typeof unknown2 === "string") {
console.log(unknown2.length); // 타입을 알 수 없으니 length의 여부를 알 수가 없어 조건안에서 동작이 가능하다.
}
- TypeScript
- tsconfig.json