ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 1. express 타입 이해...1
    분석과탐구 2023. 1. 7. 07:32

    express의 타입

    npm express에 있는 모듈에서 가리키는 express 사용법은 다음과 같다.

    const express = require('express') // (1)
    const app = express() // (2)
    
    app.get('/', function (req, res) { // (3)
      res.send('Hello World') // (4)
    })
    
    app.listen(3000) // (5)

    위 코드 (1) ~ (5)에서 타입은 어떻게 되는가? express 자체는 자바스크립트로 먼저 작성되었다. 그리고, 타입이 따로 추가된 형태이다. NPM을 가보면 다음과 같이 되어 있다.

    https://www.npmjs.com/package/express

    express 라이브러리를 타입스크립트에서 사용하려면 타입 정보가 필요한 것이다. 이를 위하여 @types/express를 설치하였다. express와 관련된 타입 파일은 다음과 같다.

    처음엔 무슨 말인지 전혀 몰랐다...

    위 코드에서 "declare function .... " 부터 사실 잘 와닿지가 않았다. 살펴볼 필요성을 느꼈다. 첫 번째 줄부터 보았다.

    import * as ...

    우선 상단 4줄은 타입을 임포트 하는 구문이다.

     

    타입을 가져온다.

    "import * as bodyParser from 'body-parser'"는 body-parser 디렉토리에 있는 index.d.ts 파일을 임포트 하는 것을 가리킨다. 그런데... 디렉토리 이름을 지정했는데 파일 이름을 지정하진 않았다. 그럼에도 불구하고 index.d.ts 파일을 잘 가져온다.이것은 Node.js의 컨벤션라고 한다. 디렉토리의 이름을 지정하면 디렉토리 안에 있는 index라는 이름을 가진 파일을 가져오는 것이다.

    타입스크립트에서는 tsconfig.json의 moduleResolution을 "node"로 지정하면 활성화된다. 하지만, tsconfig파일을 가보니 주석으로 되어있다. 그럼에도 불구하고 index.d.ts파일을 가져올 수 있는 이유는 tsconfig에서 module 설정을 commonjs로 하게 되면 자동으로 설정된다.(출처)

    declare function

    "declare function e(): core.Express"는 e라는 함수의 타입을 선언하는데, 인자는 없고 리턴 타입은 core.Express이다.

    인자는 없고 리턴은 core.Express인 함수

    declare 키워드가 있으면, 구현을 요구하지 않는다. "e(): core.Express"처럼 내부 구현은 없이 오로지 타입만이 존재할 뿐이다. 타입스크립트 컴파일러에게 "구현은 다른 곳에 있으니, 지금 선언한 이 타입을 믿고 타입 체크를 진행해 줘"라고 하는 것과 같다. 이를 이용하여 다음 두 가지 효과를 가져온다.

    • 구현 없이 타입만으로 컴파일을 성공할 수 있다. 구현이 없으므로 실행하면 에러가 나겠지만, 컴파일 자체는 성공한다. 정적으로 타입 체크를 할 수 있다.

    컴파일 성공. 실행하면 레퍼런스 에러가 발생한다.

    • 자바스크립트로 작성된 라이브러리에 타입을 지정해 줄 수 있다. 정적으로 타입 체크를 할 수 있다. 구현에 전혀 관여하지 않고도 말이다.

    declare 키워드를 이용한 타입 선언을 ambient declaration이라고 한다. 구현을 정의하지 않는 선언을 가리킨다. ambient declaration은 기존의 자바스크립트 라이브러리를 타입스크립트로 안전하게 마이그레이션 하는 데 도움을 준다. express는 처음에 자바스크립트로 작성한 라이브러리이므로 ambient declaration을 이용하여 타입스크립트로 통합할 수 있다.

    declare namespac

    "delcare namespace e{...}"도 ambient declaration에 속한다. 위와는 다르게 함수가 아니라 네임스페이스를 대상으로 한 것이다. 타입스크립트로 작성되지 않은 자바스크립트 라이브러리의 타입을 노출하기 위한 방법이다. 타입스크립트로 작성되지 않은 라이브러리에는 이미 구현이 있으니, 타입스크립트에서는 구현을 제외한 나머지를 제공하여 마치 타입스크립트로 작성된 라이브러리처럼 사용하게 해주는 것이다.

     

    대부분의 자바스크립트 라이브러리는 소수의 최상위 객체만 노출하므로 네임스페이스를 사용하는 것이 좋다고 한다. 따라서 "delcare namespace e{...}"는 e라는 이름을 가진 네임스페이스고, 여기에는 구현이 존재하지 않고 선언만 있게 된다. 내용의 일부를 확인해 보자.

    delcare namespace e{...} 의 내용

    var로 선언된 변수와 그 타입, query라는 함수의 파라미터와 리턴 타입을 선언하고 export로 내보내고 있다. interface도 볼 수 있다. 

    export =

    마지막으로 "export = e"는 무엇인가? "export = " 구문은 CommonJS와 AMD에서 주로 쓰이던 exports라는 객체 개념을 가져온 것이다. AMD는 모르겠지만... CommonJS는 경험해 보았으니 "export ="로 단일 객체를 외부에서 쓸 수 있도록 내보낸다는 개념은 이해가 된다. "export = "구문으로 지정할 수 있는 대상은 클래스, 인터페이스, 네임스페이스, 함수, 열거형인데... 그렇다면... "export = e"는 함수 e를 가리키는 것인가 네임스페이스 e를 가리키는 것인가?

     

    이것은 declaration merging이라는 타입스크립트의 기법이었다. 타입스크립트 컴파일러가 같은 이름으로 선언된 개별적인 선언 들을 하나의 정의로 합친다고 한다. 즉, 동시에 존재하는 function e()와 namespace e{}를 하나로 합칠 수 있는 개념이다. 합쳐진 정의는 합쳐지기 전의 특징들을 모두 갖는다.

     

    생각해 보면 같은 종류의 선언(네임스페이스, 클래스, 인터페이스 등)을 여러 줄에 걸쳐서 써놓고 합치는 개념은 낯선 것은 아니다. 다만, 서로 다른 종류끼리 섞이는 것은 낯설다. 이것이 가능한 이유는 네임스페이스가 다른 객체와 섞일 수 있는 개념이기 때문이다. 

     

    네임스페이스는 서로 다른 종류의 선언끼리 합칠 수 있도록 해주는 유연한 개념인 것이다. 클래스나 함수와 합쳐져서 그들의 선언을 따라간다. 즉 "네임스페이스 + 함수 => 함수" "네임스페이스 + 클래스 => 클래스"가 되는 것이다. 예제는 다음과 같다.

    https://www.typescriptlang.org/ko/docs/handbook/declaration-merging.html

    예를 들어 위 코드를 js로 바꾼다면 이와 비슷할 것이다.

    네임스페이스와 함수 병합은 위 js랑 비슷하다.

    따라서... 마지막 "export = e" 구문은 함수 e를 모듈로 내보내는데, 이 e는 namespace e와 합쳐진 함수 e인 것이다.

    마무리

    express의 타입을 설명하는 최상위 index.d.ts파일을 살펴보았다. index.d.ts파일을 구성하는 여러 원리들 중에서 Ambient declaration, declaration merging, namespace, export = 를 알 수 있었다.

    참고

    https://www.typescriptlang.org/ko/docs/handbook/modules.html

    https://www.typescriptlang.org/ko/docs/handbook/declaration-files/by-example.html

    https://www.typescriptlang.org/ko/docs/handbook/namespaces.html

    https://stackoverflow.com/questions/44028806/should-the-index-ts-be-resolved-by-typescript-as-default-module-file

    https://stackoverflow.com/questions/43335962/purpose-of-declare-keyword-in-typescript

    https://www.javatpoint.com/typescript-ambients-declaration

    댓글

Designed by Tistory.