티스토리 뷰

지난 시간에는 캔버스 API를 활용하여 이미지 크기를 줄이는 함수 downscaleImage를 작성하고, Karma 테스트 러너와 Jasmine 테스트 프레임워크로 자동화된 테스트 환경을 구성하는 것까지 실습하였어요.

 

코드를 아래 저장소에서 복제합니다.

$ git clone "https://github.com/sungchuni/downscaleImage.git" && cd downscaleImage && git checkout v1.1

 

타입스크립트로 변환하면 이 스크립트를 처음 접하는 사람들이 입출력 인터페이스를 더 쉽게 알 수 있을 것입니다. 하지 않을 이유가 있습니까? 우선 typescript와 ts-loader를 설치합니다.

$ npm i -D typescript ts-loader

타입스크립트 컴파일을 위해 루트 폴더에 tsconfig.json이 있어야 합니다. 아래 커맨드를 실행하여 기본 설정 파일을 생성합니다.

$ npx tsc --init
message TS6071: Successfully created a tsconfig.json file.

그리고 기존 downscaleImage.js, downscaleImage.spec.js의 내용을 그대로 복사하여 .ts 확장자의 파일을 생성합니다.

$ cp downscaleImage.js downscaleImage.ts && cp downscaleImage.spec.js downscaleImage.spec.ts

자, 이제 함수의 .ts 파일을 열어봅시다. 만약 vscode를 사용 중이라면, 여기저기에 빨간줄이 그어져 있는 것을 보실 수 있겠습니다. 하나하나 살펴볼까요? 우선 매개변수 file의 형식이 지정되어 있지 않다고 하네요. 지정해줍시다.

다음은 String#startsWith 메소드에 대한 오류입니다. 아니, 이게 언제쩍 메소든데 아직도 없다하니 분명 설정 파일 잘못이겠군요. tsconfig,json을 열고 compilerOptions.target을 적당한 값으로 바꾸어 줍시다. 저는 최신 사람이니 "esnext"로 할게요.

컴파일러 타겟을 변경하니 다른 빨간줄 몇 개도 사라진 것 같습니다. 아마 Promise 같은 것들이겠죠.

 

다음은 HTMLImageElement#decode 메소드의 폴리필 구문입니다. onload 리스너의 콜백으로 빈 프로미스를 리졸브하도록 코드를 만들어두었는데, lib.dom.d.ts에 등록된 decode 메소드의 리턴 타입은 Promise<void>여서 프로미스의 리졸브 함수에 Event 타입의 인자가 전달되는 것이 문제인 것으로 보입니다.

리졸브 함수에 인자가 전달되지 않도록 다시 작성합니다.

다음은 ctx가 문제입니다. 설명을 보니 "개체가 'null'인 것 같습니다. ts(2531)"라고 하는데, 분명 HTMLCanvasElement#getContext는 null을 반환할 수도 있습니다. 적당한 contextType이 입력되지 않았을 때에나요, 그러면 타입 단언을 통해 이를 해결합니다.

마지막으로 HTMLCanvasElement#toBlob 메소드의 폴리필 구문입니다. toBlob 메소드가 없는 IE를 위해 msToBlob가 함수인지를 확인하고, 그 래퍼 메소드를 정의하고 있는데, 타입스크립트 공식의 DOM API 타입 정의 문서인 lib.dom.d.ts에는 해당 메소드가 없어서 오류가 나고 있습니다.

공식 핸드북의 Module Augmentation 기술로 처리할까요? 문서 최상단에 아래와 같이 HTMLCanvasElement#msToBlob 메소드를 선언합니다.

declare global {
  interface HTMLCanvasElement {
    msToBlob(): Blob;
  }
}

좋군요, 잘 됩니다. 오류를 수정하였으니 몇 가지 추가 선언을 해봅시다. 우선 함수 두 번째 매개변수의 인터페이스를 선언하고 싶습니다. 적당히 Option이란 이름으로 지어봅니다.

interface Option {
  mimeType?: string;
  quality?: number;
  returnType?: string;
  targetWidth?: number;
}

물론 옵션 변수이니 객체 내의 프로퍼티도 모두 옵셔널합니다. 이제 함수 선언부에 이를 대입합니다. 아까 이미 file 타입은 작성했었지요? 참, 메소드 바디 시작 부의 File 인스턴스 여부를 확인하는 부분은 삭제합시다. 타입스크립트 컴파일러가 이를 대신하니 필요가 없습니다. (그 외의 타입 검증 절차는 여전히 유효합니다.)

async function downscaleImage(
  file: File,
  {
    mimeType = "image/jpeg",
    quality = 0.8,
    returnType = "blob",
    targetWidth = 1280
  }: Option = {}
): Promise<Blob | string | null | void>

여기까지만해도 뿌듯합니다. 빨간줄도 보이지 않습니다. 이제 Karma에, 아니 karma.conf.js 내의 Webpack 설정 항목을 조금만 더 추가하면 바로 테스트가 가능할 것 같습니다. Webpack 설정의 resolve.extensions, module.rules를 아래와 같이 작성합니다.

{
  mode: "development",
  resolve: {
    extensions: [".ts", ".js"]
  },
  module: {
    rules: [{test: /\.ts$/, loader: "ts-loader"}]
  }
}

되었습니다, 테스트 파일을 downscaleImage.spec.js가 아닌 downscaleImage.spec.ts로 지정하는 것을 잊지 맙시다. 테스트 커맨드를 실행하면, 아마 "should be rejected without File", "should be resolved with returnType option" 두 스펙에서 tsc 컴파일 오류가 날 것입니다. 첫 번째는 이제 의미가 없는 테스트입니다. 두 번째의 경우는 타입스크립트가 downscaleImage의 리턴 타입 추론을 보수적으로 하기 때문인데요, 이 또한 타입 단언을 통해 통과하도록 합시다.

이 정도의 타입 오류는 이제 에디터 수준에서 보고될 터이니, 테스트 절차에 포함할 필요가 없게 되었습니다.
가여운 tsc를 위해 힌트를 줍시다.

이상입니다. 

https://github.com/sungchuni/downscaleImage/releases/tag/v1.2

 

다음은 리팩터링 시간으로 찾아뵐게요.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크