바닐라 JS의 동적 스크립트 임포팅

바닐라 JS로 어떤 라이브러리를 동적으로 포함하고 해당 라이브러리가 완전히 로딩되었을때 코드를 실행하는 방식

const url = "https://~.min.js";
const htmlMeshScript = document.createElement("script");

htmlMeshScript.type = "module";
htmlMeshScript.src = url;

document.body.appendChild(htmlMeshScrip);

htmlMeshScript.onload = (event) => {
  console.log("loaded");
};

TypeScript의 배열로 Union Type 정의하기

다음과 같은 배열이 있다.

const SideTypes = [ "FRONT", "BACK", "DOUBLE" ] as const;

위의 배열 객체를 가지고 FRONT와 BACK 또는 DOUBLE 문자열을 갖는 Union Type을 정의하는 코드는 다음과 같다. (as const를 반드시 붙여야 한다.)

type OptionType = {
  side: typeof SideTypes[number];
};

즉, 위의 코드는 아래와 코드와 동일하다. 하지만 아래처럼하면 배열 객체의 값과 유니온 타입을 정의하기 위한 값에 대한 코드가 중복되는 문제가 있다. 이는 반드시 고쳐야 하는 아주 나쁜 리펙토링 대상이다.

type OptionType = {
  side: "FRONT" | "BACK" | "DOUBLE";
};

이제 OptionType은 다음처럼 사용될 수 있다.

const options: OptionType = {
  side: "FRONT"
};

이제 lil-gui 라이브러리에서 다음처럼 효과적인 코드로 작성될 수 있다.

const gui = new GUI();
gui.add(options, "side", SideTypes).onChange(v => {
  if(v === "FRONT") {
    material.side = THREE.FrontSide;
  } else if(v === "BACK") {
    material.side = THREE.BackSide;
  } else if(v === "DOUBLE") {
    material.side = THREE.DoubleSide;
  }
});

위의 코드가 적용된 실행 화면인데, 화면의 우측 상단의 GUI를 위해 적용된 코드다.

TypeScript로 보는 GoF의 디자인 패턴

TypeScript로 보는 GoF의 디자인패턴에 대한 강좌입니다. GoF의 23개의 패턴 모두를 설명하고 있고 각 패턴에 대한 실습을 TypeScript 언어와 클래스다이어그램을 통해 설명합니다. 이제 막 TypeScript 언어를 학습했고 TypeScript 언어에 대한 실습이 필요하다면 이 강좌를 통해 소프트웨어의 설계 방법인 디자인패턴을 학습함과 동시에 구체적인 실습도 진행할 수 있습니다.




























Typescript에서 인터페이스의 구현과 객체 리터럴(Object Literal) 할당

타입스크립트에서 인터페이스에는 미묘한 점이 존재합니다. 먼저 다음과 같은 인터페이스가 존재합니다.

interface Dip2K {
    action(): void
}

클래스로 구현해 보면 다음과 같습니다.

class Dip2KImpl implements Dip2K {
    alias: string = 'GISDEVELOPER'
    action() { console.log('인간은 행위로 정의된다.') }
}

새로운 구성요소로써 alias가 추가되었습니다. 네, 충분히 이해할 수 있습니다.

이제 객체 리터럴로 할당해 보겠습니다. 먼저 여기서 타입스크립트의 인터페이스에 대한 가장 핵심적인 목표는 ‘타입검사’라는 점을 떠올리셔야 합니다.

const Dip2KObj: Dip2K = {
    alias: 'GISDEVELOPER',
    action() { console.log('인간은 행위로 정의된다.') }
}

에러가 납니다. 에러 내용은 Object literal may only specify known properties, and ‘alias’ does not exist in type ‘Dip2K’ 입니다. 즉 인터페이스에는 없는 alias라는 속성은 지정할 수 없음을 나타냅니다. 타입스크립트의 인터페이스에 대한 가장 핵심적인 목표는 ‘타입검사’를 통과하지 못했기 때문입니다.

다음은 어떨까요?

class Dip2KDuck {
    alias: string = 'GISDEVELOPER'
    action() { console.log('인간은 행위로 정의된다.') }    
}

const Dip2KObj2: Dip2K = new Dip2KDuck()

가능할까요?

문제가 없습니다. 분명 서로 타입 이름이 다르므로 ‘타입검사’를 통과하지 못한 것으로 보이지만 ‘타입검사’를 통과합니다. 이유는 타입스크립트의 타입검사는 덕타이핑(Duck Typing) 방식이라는데 있습니다. 즉, 타입의 이름이 달라도 그 구성(필드, 매서드)가 같다면 동일 타입으로 본다는데 있습니다. 그럼 앞서 봤던 객체 리터럴은요? 객체 리터럴은 타입이 아닌 객체입니다. 그래서 ‘타입검사’를 통과하지 못하는 것입니다. 애매하죠? 사실 타입스크립트는 이해하기 매우 어려운 언어 중에 하나 입니다.

그럼 객체 리터럴에 대한 할당까지도 에러없이 처리하기 위해서는 어떻게 해야할까요? 다음처럼 인덱스 시그니쳐를 인터페이스의 선언에 추가하면 됩니다.

interface Dip2K {
    action(): void
    [prop:string]: any // 인덱스 시그니쳐
}

쌩뚱 맞은 인덱스 시그니쳐를 추가하는 것에 대해 불편함을 느낄 수 밖에 없는데요. 원래의 인터페이스에 인덱스 시그니쳐를 추가하지 않고도 좀더 나은 방식은 다음과 같습니다.

const Dip2KObj: Dip2K = <Dip2K>{
    alias: 'GISDEVELOPER',
    action() { console.log('인간은 행위로 정의된다.') }
}