티스토리 뷰

728x90
반응형

📝 이 포스팅은 인프런강의의 Zerocho의 Node.js에 TypeScript 적용 강의를 듣고 정리한 글입니다.


[vscode 환경세팅]

먼저 node 프로젝트 생성하고 package.json 이 초기에 세팅된 후에 vscode 환경세팅을 진행해주시면 됩니다.

 

Breadcrumbs 활성화 시키기

Breadcrumbs 는 typescript 파일의 위치를 파악할 수 있는 네비게이션 역할을 합니다.
 ⌘(cmd)   ,  단축키는 vscode 설정(preferences) 로 이동합니다.
검색어에 bread 라고 입력하면 아래사진의 핑크색박스 처럼 비활성화된 상태로 나옵니다.
샐랙트박스를 체크합니다.

 

설치패키지

$ npm i prettier

$ npm i tslint

$ npm i tslint-config-prettier

 

package.json 에 "prettier" 키 값 추가합니다.

// package.json

{	
	... ,
	"prettier": {
    	"printWidth": 80,
        "useTabs": false,
        "tabWidth": 2,
        "bracketSpacing": true,
        "semi": true,
        "singleQuote": false
    }
}

 

현재 프로젝트의 루트디렉토리에서 .vscode 폴더를 생성합니다.

.vscode 폴더안에 settings.json 파일을 생성합니다.

/ (루트디렉토리 = 최상단 디렉토리)
ㄴ .vscode
|  ㄴ settings.json
ㄴ tslint.json
// settings.json

{
  "[javascript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "editor.formatOnSave": false,
  "prettier.tslintIntegration": true,
  "tslint.autoFixOnSave": true
}

* "editor.formatOnSave": false 로 설정한 이유는 내장 lint 와 tslint 사이에 충돌 발생을 막기 때문입니다.
* "prettier.tslintIntegration": true prettier가 prettier-tslint를 사용하여, tslint의 룰을 우선시함을 의미합니다.

루트 디렉토리에 tslint.json 파일 생성합니다.

tslint.json에 타입스크립트 작업할 때의 룰을 추가할 수 있습니다.

// tslint.json

{
    "defaultSeverity": "error",
    "extends": ["tslint:latest", "tslint-config-prettier"],
    "jsRules": {},
    "rules":{
        "semicolon":true,
        "import-spacing": true,
        "whitespace": true
    },
    "trailing-comma": [
        true,
        {
          "multiline": {
            "arrays": "always",
            "exports": "always",
            "functions": "always",
            "imports": "always",
            "objects": "always",
            "typeLiterals": "always"
          },
          "singleline":{
            "arrays": "always",
            "exports": "always",
            "functions": "never",
            "imports": "always",
            "objects": "always",
            "typeLiterals": "always"
          },
          "esSpecCompliant": true
        }
      ],
      "space-before-function-paren": [
        true,
        "never"
      ],
    "extends": ["tslint:recommended", "tslint-config-prettier"]
}

 


[참고 - vscode에 typescript 환경 설정]

더보기

 


- 필자는 npm package manager을 이용했습니다!
- node.js 를 이미 설치되어야 합니다. 필자는 LTS 버젼으로 설치했습니다.

node.js/express 프로젝트에 Typescript 연결 작업

package.json 생성하여 노드프로젝트 생성

$ npm init --y


node.js+typescript 에서 필요한 패키지들

더보기

1. 패키지 라이브러리 설치

package.json의 dependency 영역에 설치

> typescript 설치

$ npm i typescript

 

> @types/node 설치

$ npm i @types/node

 

> express 와 @types/express 설치

$ npm i express @types/express

 

> morgan, cors, cookie-parser, express-session, dotenv, passport, hpp, helmet 설치

$ npm i morgan cors cookie-parser express-session dotenv passport hpp helmet

 

> @types/morgan, cors, cookie-parser, express-session, dotenv, passport, hpp, helmet 설치

$ npm i @types/morgan @types/cors @types/cookie-parser @types/express-session @types/dotenv @types/passport @types/hpp @types/helmet

 

2. 개발자용 패키지 라이브러리 설치

package.json의 devDependency 영역에 설치

> nodemon 설치

$ npm i -D nodemon

 

> ts-node 설치

$ npm i -D ts-node

tsconfig.json 생성

[+] tsconfig.json 이 무엇인가요?
tsconfig.json 이 위치한 디렉토리가 Typescript 프로젝트의 루트(최상단)이 됩니다.
tsconfig.json은 프로젝트를 컴파일하는데 필요한 루트파일과 컴파일 옵션을 지정합니다.
입력파일 없이 tsc를 호출하면, 컴파일러는 현재 디렉토리에서부터 시작하여 상위디렉토리 체인으로 tsconfig.json 파일을 검색합니다.

[ tsconfig.json 에 대한 참고 자료 ]


프로젝트의 루트디렉토리에 tsconfig.json 을 생성합니다.

{
  "compilerOptions": {
    "strict": true,
    "lib": [
      "es2015",
      "es2016",
      "es2017",
      "es2018",
      "es2019",
      "es2020",
      "es2021"
    ],

    "moduleResolution": "node",
    "esModuleInterop": false,
    "typeRoots": ["./types"],
    "sourceMap": true
  }
}

 

  • strict : typescript 코드 문법 규칙을 엄격하게 검사합니다. warning이어도 에러로 인식합니다.
  • moduleResolution : 컴파일러가 "import가 무엇을 참조하는지"를 알아내기 위해 사용하는 프로세스입니다.

import { a } from "moduleA" 의 예를 들면, a를 검사하기 위해 컴파일러는 무엇을 참조하는지 정확히 알아야됩니다.
그러므로 컴파일러는 moduleA 정의를 검사해야 됩니다.
컴파일러가 모듈 moduleA를 해석할 수 없다면, 오류로그 error TS2307: Cannot find module 'moduleA' 를 발생합니다.
moduleA는 .ts/.tsx 파일에 정의되어 있거나, 코드가 의존하는 *.d.ts에 정의되어 있을 수 있습니다.

컴파일러는 두가지 전략 으로 가져온 모듈을 나타내는 파일의 위치를 찾습니다. 두가지 전략은 'Node' 와 'Classic' 입니다.

moduleResolution - Classic 전략

Classic 전략은 Typescript의 default 해석전략이며, 이전 버젼과의 호환성을 위해 제공됩니다.

// path: /root/src/folder/A.ts
// file-name: A.ts

import { b } from "./moduleB";

위와 같이 A.ts 안에 moduleB의 b를 임포트 했다면 모듈 moduleB 가 무엇인지를 검사합니다.
먼저 A.ts 와 같은 위치에 있고, 확장자가 moduleB.ts 인지, moduleB.d.ts 인지를 확인합니다.
(검사1)
moduleB의 디렉토리가 /root/src/folder/moduleB.ts 인가? /root/src/folder/moduleB.d.ts 인가?

A.ts 의 현재 디렉토리에 위치하지 않는다면, moduleB 파일의 위치를 찾기위해서 디렉토리 트리를 거슬러 올라가서 위치를 찾습니다.
(검사2)
/root/src/folder/ 디렉토리에 moduleB.ts 나 moduleB.d.ts(정의파일)이 있는가?
/root/src/ 디렉토리에 moduleB.ts 나 moduleB.d.ts 가 있는가?
/root/ 디렉토리에 moduleB.ts 나 moduleB.d.ts 가 있는가?
/ 디렉토리에 moduleB.ts 나 moduleB.d.ts 가 있는가?

moduleResolution - Node 전략

Node 전략은 런타임에 Node.js 모듈 해석 메커니즘으로 파일의 위치를 찾습니다.
commonJS 방식을 사용합니다.

[Node 방식]

 

TypeScript 한글 문서

TypeScript 한글 번역 문서입니다

typescript-kr.github.io


 

import * as express from 'express' vs import express from 'express'


* as 가 없으면 export default 로 정의되어 있습니다.
* as 가 붙어있는 것은 export default 로 정의 되어있지 않음을 의미합니다.

typescript 파일에서 express 모듈을 불러오는 것은
node_modules/@types/express/index.d.ts 파일을 가리킵니다.
여기서 express 모듈이 어떻게 정의되어있는지 아래 코드를 확인해보면
export default express; 이런식으로 정의되어 있지 않았습니다.

node_modules/@types/express/index.d.ts 소스코드 보기

더보기
// path: node_modules/@types/express/index.d.ts

// Type definitions for Express 4.17
// Project: http://expressjs.com
// Definitions by: Boris Yankov <https://github.com/borisyankov>
//                 China Medical University Hospital <https://github.com/CMUH>
//                 Puneet Arora <https://github.com/puneetar>
//                 Dylan Frankland <https://github.com/dfrankland>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped

/* =================== USAGE ===================

    import express = require("express");
    var app = express();

 =============================================== */

/// <reference types="express-serve-static-core" />
/// <reference types="serve-static" />

import * as bodyParser from 'body-parser';
import * as serveStatic from 'serve-static';
import * as core from 'express-serve-static-core';
import * as qs from 'qs';

/**
 * Creates an Express application. The express() function is a top-level function exported by the express module.
 */
declare function e(): core.Express;

declare namespace e {
    /**
     * This is a built-in middleware function in Express. It parses incoming requests with JSON payloads and is based on body-parser.
     * @since 4.16.0
     */
    var json: typeof bodyParser.json;

    /**
     * This is a built-in middleware function in Express. It parses incoming requests with Buffer payloads and is based on body-parser.
     * @since 4.17.0
     */
    var raw: typeof bodyParser.raw;

    /**
     * This is a built-in middleware function in Express. It parses incoming requests with text payloads and is based on body-parser.
     * @since 4.17.0
     */
    var text: typeof bodyParser.text;

    /**
     * These are the exposed prototypes.
     */
    var application: Application;
    var request: Request;
    var response: Response;

    /**
     * This is a built-in middleware function in Express. It serves static files and is based on serve-static.
     */
    var static: serveStatic.RequestHandlerConstructor<Response>;

    /**
     * This is a built-in middleware function in Express. It parses incoming requests with urlencoded payloads and is based on body-parser.
     * @since 4.16.0
     */
    var urlencoded: typeof bodyParser.urlencoded;

    /**
     * This is a built-in middleware function in Express. It parses incoming request query parameters.
     */
    export function query(options: qs.IParseOptions | typeof qs.parse): Handler;

    export function Router(options?: RouterOptions): core.Router;

    interface RouterOptions {
        /**
         * Enable case sensitivity.
         */
        caseSensitive?: boolean | undefined;

        /**
         * Preserve the req.params values from the parent router.
         * If the parent and the child have conflicting param names, the child’s value take precedence.
         *
         * @default false
         * @since 4.5.0
         */
        mergeParams?: boolean | undefined;

        /**
         * Enable strict routing.
         */
        strict?: boolean | undefined;
    }

    interface Application extends core.Application {}
    interface CookieOptions extends core.CookieOptions {}
    interface Errback extends core.Errback {}
    interface ErrorRequestHandler<
        P = core.ParamsDictionary,
        ResBody = any,
        ReqBody = any,
        ReqQuery = core.Query,
        Locals extends Record<string, any> = Record<string, any>
    > extends core.ErrorRequestHandler<P, ResBody, ReqBody, ReqQuery, Locals> {}
    interface Express extends core.Express {}
    interface Handler extends core.Handler {}
    interface IRoute extends core.IRoute {}
    interface IRouter extends core.IRouter {}
    interface IRouterHandler<T> extends core.IRouterHandler<T> {}
    interface IRouterMatcher<T> extends core.IRouterMatcher<T> {}
    interface MediaType extends core.MediaType {}
    interface NextFunction extends core.NextFunction {}
    interface Request<
        P = core.ParamsDictionary,
        ResBody = any,
        ReqBody = any,
        ReqQuery = core.Query,
        Locals extends Record<string, any> = Record<string, any>
    > extends core.Request<P, ResBody, ReqBody, ReqQuery, Locals> {}
    interface RequestHandler<
        P = core.ParamsDictionary,
        ResBody = any,
        ReqBody = any,
        ReqQuery = core.Query,
        Locals extends Record<string, any> = Record<string, any>
    > extends core.RequestHandler<P, ResBody, ReqBody, ReqQuery, Locals> {}
    interface RequestParamHandler extends core.RequestParamHandler {}
    export interface Response<ResBody = any, Locals extends Record<string, any> = Record<string, any>>
        extends core.Response<ResBody, Locals> {}
    interface Router extends core.Router {}
    interface Send extends core.Send {}
}

export = e;


export default 로 정의되지 않았으므로, @types/express 모듈을 불러올 때
import * as express from "express" 로 정의해야합니다.


ts-node

ts-nodeTypescript 실행 엔진 이며, Node.js 의 REPL 입니다.
REPL이란, Read Evaluate Print Loop 의 줄임말이며
직역하면, 사용자가 특정 코드를 입력하면 해당코드를 평가, 실행결과를 반환, 출력, 반복해주는 환경을 의미합니다.
Node.js에서의 자바스크립트 코드를 즉석으로 바로 실행해서 결과를 확인해볼 수 있음을 의미합니다.

ts-node는 typescript 를 javascript 로 변환시킵니다.
또한 다른 Node.js 툴과 라이브러리를 함께 사용할 수 있도록 합니다.
사전 컴파일(pre-compiling) 없이 Node.js에서도 Typescript로 직접 실행시킬 수 있도록 합니다.

$ npx ts-node index.ts

* npm이 아닌 npx 로 한 이유는, 글로벌 설치를 막기 위해서 입니다.
위의 코드의 실행결과는, index.js 를 생성시킵니다.
ts-node가 node.js 환경에서 동작할 수 있도록 typescript를 javascript 로 변환시킵니다.


npm 과 npx 의 차이점이 무엇일까요?

npm은 로컬에 모듈을 설치를 해야 동작이 됩니다.
반면에, npx는 로컬에 모듈을 설치하지 않아도 최신버젼을 바로 실행시켜 버젼에 구애받지 않습니다.
npm은 패키지를 관리만 하고 실행을 할 수 없습니다. 반면에, npx는 npm 패키지 실행기 이므로 npm을 실행 시킬 수 있습니다.

npm 의 글로벌 모듈(-g)의 도입 목적은, 매 프로젝트마다 모듈 설치를 하지 않기 위해서 였습니다.
매번 모듈설치를 하는 귀찮음을 해결할 수 있습니다. npm으로 글로벌 모듈 설치하는 명령어는 아래와 같습니다.

$ npm i 모듈명 -g


하지만 글로벌로 모듈을 설치하는게 마냥 좋은 방법은 아닙니다. 대표적으로 글로벌 모듈이 좋지 않은 이유는 세가지 입니다.
1) 모듈이 업데이트 되어있는지 확인이 어렵다.
2) 업데이트 진행했을 때 변동사항이 생겨 다른 프로젝트에도 영향을 끼칠 수 있다.
3) create-react-app 같은 보일러 플레이트에는 치명적이다.

npm 5.2 버젼부터 npx를 기본패키지로 제공되기 시작했습니다. npx도 모듈의 일종입니다.
npx는 특히 3번째 문제인 create-react-app 보일러플레이트 모듈의 문제점을 해결해줬는데
create-react-app 을 설치할 경우 매번 최신버젼만을 가져와서 설치해주기 때문에
지금 어떤 버젼을 사용하고 있는지 신경쓸 필요가 없어졌습니다.

npm을 통해 모듈을 로컬에 설치해야 실행시킬 수 있다는 문제점을 보완해줬습니다.
npx는 모듈을 로컬에 저장하지 않고 매번 최신 버젼의 파일만을 임시로 불러와 실행시킵니다.

[참고자료]



728x90
반응형
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함