타입스크립트 올인원 : Part2 - 세션5 - 2

안지환·2023년 12월 16일
0

강의

목록 보기
5/10
post-custom-banner

⭐️ Overview

@types/express

Express는 웹 및 모바일 애플리케이션을 위한 강력한 기능 세트를 제공하는 최소한의 유연한 Node.js 웹 애플리케이션 프레임워크입니다.


TypeScript Express는 DT 라이브러리로 되어 있습니다.

npm install --save @types/express

Memo:
최신 Express 버전에는 bodyParser가 내장이 되어 있습니다. 따로 설치를 할 필요가 없습니다.

Express 내장 모듈은 export = ecommonJS로 되어 있습니다. 때문에 Module Export에 E 변수명으로 되어 있어도 변수명을 사용자 임의로 정할 수 있습니다.

Express 내부 구조는 다음과 같습니다.

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;
  ...
}

Express는 express-serve-static-core 내부에 다양한 기능을 내장하고 있습니다.

import * as core from "express-serve-static-core";

내부 구조는 Node.js의 구조와 유사하며, http 모듈을 기반으로 리퀘스트와 리스폰스를 확장한 정도로 이루어져 있습니다.

덕분에 Express를 사용함으로써 보다 쉽게 웹 애플리케이션을 구축할 수 있습니다.

Express MiddleWare

declare global {
    namespace Express {
        // These open interfaces may be extended in an application-specific manner via declaration merging.
        // See for example method-override.d.ts (https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/method-override/index.d.ts)
        interface Request {}
        interface Response {}
        interface Locals {}
        interface Application {}
    }
}

Declare Global를 만든 이유는 내부에서 내용을 수정 할 수 있습니다.

Express 미들웨어는 오버로딩으로 되어 있습니다.

export interface IRouterMatcher<
    T,
    Method extends "all" | "get" | "post" | "put" | "delete" | "patch" | "options" | "head" = any,
> {
    <
        Route extends string,
        P = RouteParameters<Route>,
        ResBody = any,
        ReqBody = any,
        ReqQuery = ParsedQs,
        LocalsObj extends Record<string, any> = Record<string, any>,
    >(
        // (it's used as the default type parameter for P)
        path: Route,
        // (This generic is meant to be passed explicitly.)
        ...handlers: Array<RequestHandler<P, ResBody, ReqBody, ReqQuery, LocalsObj>>
    ): T;
    <
        Path extends string,
        P = RouteParameters<Path>,
        ResBody = any,
        ReqBody = any,
        ReqQuery = ParsedQs,
        LocalsObj extends Record<string, any> = Record<string, any>,
    >(
        // (it's used as the default type parameter for P)
        path: Path,
        // (This generic is meant to be passed explicitly.)
        ...handlers: Array<RequestHandlerParams<P, ResBody, ReqBody, ReqQuery, LocalsObj>>
    ): T;
....

handlers는 배열 타입으로 되어 있습니다. 그리고 RequestHandler는 Express 미들웨어입니다.

export interface RequestHandler<
    P = ParamsDictionary,
    ResBody = any,
    ReqBody = any,
    ReqQuery = ParsedQs,
    LocalsObj extends Record<string, any> = Record<string, any>,
> {
    // tslint:disable-next-line callable-types (This is extended from and can't extend from a type alias in ts<2.2)
    (
        req: Request<P, ResBody, ReqBody, ReqQuery, LocalsObj>,
        res: Response<ResBody, LocalsObj>,
        next: NextFunction,
    ): void;
}

RequestHandler 구조는 req, res, next 익숙한 구조로 되어 있습니다.
때문에 Middleware 타입은 RequestHandler 타입입니다.

req,res 속성 타이핑

const middleware: RequestHandler<
  { paramType: string }, 
  { message: string }, 
  { bodyType: number }, 
  { queryType: boolean }, 
  { localType: unknown }
> = (req, res, next) => {
  req.params.paramType,
  req.body.bodyType;
  req.query.queryType;
  res.locals.localType;
  res.json({
    message: 'hello',
  })
};

d.ts에서 declare global

declare는 TypeScript에서 전역 객체에 새로운 속성이나 타입을 추가할 때 사용하는 구문입니다. 일반적으로 TypeScript는 전역 스코프에서 새로운 변수나 속성을 추가할 때 컴파일러가 해당 식별자를 인식하지 못하고 오류를 발생시키는데, 이를 방지하기 위해 declare global을 사용할 수 있습니다.

declare는 기능이 많습니다. declare를 단순히 선언 할 수도 있고 NameSpace, Global, Module 등 확장으로 사용 할 수도 있습니다.

declare global {
    namespace Express {
        // These open interfaces may be extended in an application-specific manner via declaration merging.
        // See for example method-override.d.ts (https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/method-override/index.d.ts)
        interface Request {}
        interface Response {}
        interface Locals {}
        interface Application {}
    }
}

errorMiddleware를 생성하겠습니다.

const errorMiddleware: ErrorRequestHandler = (err: Error, req, res, next) => {
  console.log(err.status)
}

ErrorRequestHandler의 내부구조는 다음과 같습니다.

export type ErrorRequestHandler<
    P = ParamsDictionary,
    ResBody = any,
    ReqBody = any,
    ReqQuery = ParsedQs,
    LocalsObj extends Record<string, any> = Record<string, any>,
> = (
    err: any,
    req: Request<P, ResBody, ReqBody, ReqQuery, LocalsObj>,
    res: Response<ResBody, LocalsObj>,
    next: NextFunction,
) => void;

err는 any 타입으로 Error 객체를 사용합니다. 그렇지만 err.status가 인식이 되지 않아 에러가 발생합니다.

이 경우에 interface를 정의합니다.

interface Error {
  status: string
}

err.status를 인식합니다. 이 Error를 전역에서 사용하려고 global로 지정하겠습니다.

declare global {
  interface Error {
    status: string
  }
}

export {}

index.d.ts 파일을 생성해서 global로 인식하려면 export를 해야만 Error 객체를 인식 할 수 있습니다.

profile
BackEnd Developer
post-custom-banner

0개의 댓글