게시글

5만명이 선택한 평균 별점 4.9의 제로초 프로그래밍 강좌! 로드맵만 따라오면 됩니다! 클릭
강좌1 - TypeScript - 6년 전 등록

블로그 타입스크립트 전환 후기 - 타입스크립트 환경 설정

안녕하세요. TypeScript 카테고리를 새로 만들었습니다. 이 카테고리에는 제로초 블로그를 타입스크립트로 전환한 후기를 남겨보려 합니다. 사실 타입스크립트 강좌를 쓰려고 했는데 공식 문서가 너무 잘 되어 있어 JS 프로젝트를 타입스크립트로 전환하는 과정을 보여드리는 게 더 좋을 것 같다고 판단했습니다.

타입스크립트 학습 꿀팁을 드리자면 공식 문서 를 한 번 쭈욱 읽은 후 What's New 부분을 추가로 한 번 더 읽어보시면 됩니다. 타입스크립트 기능들이 어떻게 추가되어 왔는지를 알 수 있어 편합니다.

시간이 남는다면 강좌도 한 번 써보겠습니다(나중에 타입스크립트 교과서나 deno 교과서 집필도 한 번... ㅋㅋㅋ).

undefined

타입스크립트 전환 이유

제 블로그는 항상 최신 기술을 접목하는 것을 원칙으로 하고 있었습니다. 그러다가 회사 일을 하고 몸이 지치니 점점 블로그 업데이트가 뜸해 지더군요(게시글은 그래도 1주일에 한 번 포스팅했지만요). 그래서 리프레쉬하고자 블로그를 타입스크립트로 전환해보았습니다. Redux에 reselect도 붙이고요. 

사실 타입스크립트가 굳이 필요하다고 생각하지는 않지만, 타입스크립트를 배우고 적용하는 노력 대비 얻는 이점(약타입에서 강타입으로 가는 이점)이 크다고 판단했습니다. 회사에서도 타입스크립트로 개발하고 있어서 적응에 문제는 크게 없었습니다. 단, 4만 줄이나 되는 블로그의 js 코드를 수정하는 것이 문제였죠. 이제부터 어떻게 수정했는지 보여드리겠습니다.

환경 설정 

일단 js 파일들을 ts로, jsx를 tsx로 바꾸고, 바벨을 타입스크립트로 바꾸는 과정이 필요합니다. 웹팩을 같이 사용하기 때문에 웹팩에 타입스크립트를 처리할 로더도 붙여주고요. 아래 코드들은 실제 코드라기 보다는 전환 과정을 표현하기 위한 코드로 보시면 됩니다.

npm i typescript awesome-typescript-loader

webpack.config.js

{
  module: {
    rules: [{ test: /\.tsx?$/, loader: 'awesome-typescript-loader' }], // 바벨 지우고 추가
  },
  resolve: {
    modules: [path.join(__dirname, '..', 'app'), 'node_modules'],
    extensions: ['.css', '.tsx', '.ts'], // js jsx 대신 ts tsx 추가
  },
}

타입스크립트 세팅도 해줍니다.

tsconfig.json

{
  "compilerOptions": {
    "outDir": "./dist",
    "allowJs": true,
    "target": "es5",
    "lib": [
      "es2015",
      "es2016",
      "es2017",
      "es2018",
      "dom",
      "webworker"
    ],
    "jsx": "react",
    "module": "esnext",
    "moduleResolution": "node",
    "noImplicitReturns": true,
    // "noImplicitThis": true,
    // "noImplicitAny": true,
    // "strictNullChecks": true,
    "downlevelIteration": true,
    "sourceMap": true,
    "typeRoots": [
      "./types",
      "./node_modules/@types"
    ]
  },
  "include": [
    "./**/*"
  ],
  "exclude": [
    "node_modules"
  ]
}

실제로 전환할 때는 allowJs를 true로해서 일부 파일만 ts로 바꾸는 것이 좋습니다. 제 블로그만 해도 코드가 4만 줄인데 그걸 한 번에 다 바꾸는 것은 쉽지 않습니다. 점진적으로 바꿔 나가는 것을 추천드립니다.

target은 es5로 해서 바벨을 대체했고, lib은 타입스크립트가 기본적으로 제공하는 타입들을 활성화시키는 것입니다. 블로그가 PWA이기 때문에 webworker도 넣었습니다. jsx는 리액트를 쓰기 때문에 필요하고요. module은 exnext로 해서 dynamic import(코드 스플리팅)를 쓸 수 있게 하였습니다. moduleResolution은 노드 기반이라 node를 썼고요. downlevelIteration은 이터레이터를 호환하기 위한 설정입니다.

noImplicit 시리즈가 타입스크립트의 핵심입니다. 이것들을 다 true로 켜 놓아야 진정 타입스크립트를 한다고 할 수 있습니다. 하지만 처음 전환 과정에서는 수정할 것들이 너무 많아지기 때문에 일단 주석 처리해두고, 세팅 완료 후 주석을 풀고 작업할 예정입니다.

typeRoots를 보시면 types와 node_modules/@types 폴더가 있습니다. 타입스크립트는 여기서 타입 정의들을 읽습니다. types는 제가 만든 타입 정의이고요. @types는 보통 외부 라이브러리들의 정의입니다.

이제 기존 라이브러리들에 타입을 추가해야 합니다. 직접 추가할 수는 당연히 없고, 타입스크립트 커뮤니티에서 유명한 패키지들은 다 타입을 만들어 두었습니다. 바로 DefinitelyTyped입니다. npm에서 @types로 시작하는 패키지들을 다운받으면 됩니다.

package.json

{  
  "dependencies": {
    "@types/classnames": "^2.2.6",
    "@types/cookie-parser": "^1.4.1",
    "@types/cors": "^2.8.4",
    "@types/crypto-js": "^3.1.43",
    "@types/draft-js": "^0.10.25",
    "@types/express": "^4.16.0",
    "@types/express-session": "^1.15.11",
    "@types/gravatar": "^1.4.28",
    "@types/helmet": "0.0.41",
    "@types/hpp": "^0.2.1",
    "@types/method-override": "0.0.31",
    "@types/mongoose": "^5.2.17",
    "@types/morgan": "^1.7.35",
    "@types/node": "^10.10.3",
    "@types/passport": "^0.4.6",
    "@types/passport-facebook": "^2.1.8",
    "@types/passport-google-oauth2": "^0.1.2",
    "@types/passport-kakao": "^0.2.0",
    "@types/passport-local": "^1.0.33",
    "@types/passport-naver": "^0.2.0",
    "@types/passport-twitter": "^1.0.34",
    ...
  }
}

이런 식으로 기존 패키지들을 거의 다 설치하면 됩니다. 가끔가다 axios나 moment처럼 스스로 타입을 제공하는 라이브러리들이 있지만, 많지는 않습니다.

마지막으로 ts-node를 설치합니다. ts 파일을 바로 노드에서 돌릴 수 있게 해주는 패키지입니다. 성능 문제가 있으니 개발용으로만 쓰고, 배포 시에는 타입스크립트를 자바스크립트로 컴파일 한 후 실행해야 합니다.

npm i -D ts-node

nodemon.json

{
  "ext": "css json ts tsx",
  "exec": "npm run clean && npm run build:dev && ts-node server/index"
}

이제 세팅은 끝났습니다. 확장자만 js에서 ts로 바꿨기 때문에 엄청난 에러들이 발생합니다. js 코드를 타입스크립트 기반으로 바꾸는 과정이 필요합니다. 그 과정을 차례로 알아보겠습니다. 다음 포스트에서 import export 부분부터 시작합니다.

조회수:
0
목록
투표로 게시글에 관해 피드백을 해주시면 게시글 수정 시 반영됩니다. 오류가 있다면 어떤 부분에 오류가 있는지도 알려주세요! 잘못된 정보가 퍼져나가지 않도록 도와주세요.
Copyright 2016- . 무단 전재 및 재배포 금지. 출처 표기 시 인용 가능.
5만명이 선택한 평균 별점 4.9의 제로초 프로그래밍 강좌! 로드맵만 따라오면 됩니다! 클릭

댓글

4개의 댓글이 있습니다.
일 년 전
안녕하세요.
혹시 현재 만들어진 블로그는
프론트엔드 프레임워크를 이용해서 만든건가요(vue,react)?
일 년 전
react입니다.
5년 전
아이고 앞에 글이 안지워지네요 ㅜㅜ

다른 라우터에서 유저 정보를 접근하기 위해 custom request
이런 방법을 사용한다는 말씀인가요?

class CustomRequest extends Request {
user!: User
}

에러처리 미들웨어의 구체적인 예를 들면 token을 확인하고 유효하지 않다면 에러를 생성하고 error에 여러가지 정보를 담은 후 next(error)를 호출 하여 마지막 에러 핸들러에서 응답하고 싶습니다.

(req: CustomRequest, res: Response, next: NextFunction) => {
try {
req.user = jwt.verify();
next();
} catch(error) {
const error = new Error("토큰 만료");
error.status = 419 // 추가 정보를 errorHandler에 보내기 어려움
next(error);
}
}

에러를 핸들링하는 에러 핸들러
(err: Error, req: CustomRequest, res: Response, next: NextFunction) => {
// verifyToken 미들웨어에서 err.status에 접근 불가능
const statusCode: number = err.status || 500;
res.status(statusCode).json(err.message);
}

1. custom request는 이런식으로 사용한다는 말씀인가요?
2. 그럼 error 처리도 error를 상속 받는 custom error 객체를 생성하는 방법이 좋을까요?

감사합니다.
5년 전
네 CustomRequest 사용합니다. 미들웨어에서 에러 발생 시 저는 바로 그 자리에서 res.status(에러코드).send(에러메시지) 해버립니다.
5년 전
늦은시간까지 답변주셔서 감사합니다!!
편히 주무세요~~
5년 전
안녕하세요!

nodejs 교과서를 읽고난 후 typescript로 서버 개발을 하려고 자유도 높은 javascript에서 typesscript로 프로젝트를 진행하려고하니 여러가지 문제점이 발생합니다.

몇가지 조언을 얻고 싶어 질문 드립니다. 제 프로젝트에서 인증방식을 jwt를 사용하려고 합니다. 로그인할 때 인증이 되면 passport와 같이 request 객체에 user 객체를 넣어주는 미들웨어를 구현하고 싶습니다. 그런데 미들웨어에 user 객체를 넣어주면 다른 미들웨어에서 request 객체의 type 때문에 user 객체를 사용하지 못합니다.

이럴 때는 custom request 객체를 만들고 모든 라우터에 custom request 객체를 사용하는 것이 좋은 방법일까요? 아니면 type assertion과 ! 문법을 사용하는 방법이 좋은 방법일까요?

인증 라우터와 함께 에러 처리에 미들웨어도 어떤 방법이 좋을까요(error status code를 함께 보내고 싶습니다.)

감사합니다.
5년 전
저는 custom request 객체를 만들어서 사용합니다. 에러 처리 미들웨어는 무슨 말씀이신지 잘 모르겠스빈다.
5년 전
예를 들면

import * as express from "express";
import { Request, Response, NextFunction } from "express"
import { User } from "entity/User";

// 다른 라우터에서 유저 정보를 접근하기 위한 custom request
class CustomRequest extends Request {
user: User
}

// request header token을 확인하고 유효한 토큰이라면
// request 객체에 user 객체를 넣어주어 다음 라우터에서
// user 정보를 사용할 수 있도록 하는 미들웨어입니다.
// 만약 토큰이 유효지 않다면 next(error)를 호출 합니다.
function verifyToken(req: CustomRequest, res: Response, next: NextFunction) {
try {
req.user = jwt.verify();
next();
} catch(error) {
const error = new Error("토큰 만료");
error.status = 419 // 추가 정보를 errorHandler에 보내기 어려움
next(error);
}
}

// 에러를 핸들링하는 에러 핸들러
app.use(
(err: Error, req: CustomRequest, res: Response, next: NextFunction) => {
// verifyToken 미들웨어에서 err.status에 접근 불가능
const status: number = err.status || 500;
})

구체적인 예입니다.
5년 전
node.js 와 typescript를 함께 쓸수 있나욤?
같이 써보고 싶은데 어떻게 하면 좋을까요?
5년 전
타입스크립트로 코드 작성 -> 자바스크립트 변환 후 노드 실행
이러한 과정을 거치면 됩니다. 노드 실행 전에 tsc(자바스크립트로 변환) 후 node 자바스크립트파일명 하시면 됩니다.
위의 내용처럼 ts-node 설치하셔도 됩니다.