TypeScript의 interface 보강
Typescript에서 인터페이스는 "보강" 기법을 적용할 수 있습니다.
interface Namnamman {
eat: () => void
}
interface Namnamman {
coding: () => void
}
const person:Namnamman = {
eat: () => {},
coding: () => {}
}
주로 보강기법은 이런식으로 표현되곤 합니다.
동일한 이름의 인터페이스를 같은 네임스페이스에서 여러번 선언하게되면
프로퍼티들이 말그대로 "보강" 되는 형식인것입니다.
이러한 보강기법은 Global 하게 적용해줄 수도 있습니다. 바로 declare 키워드를 이용하는 것인데요
index.d.ts
export declare global {
interface Window {
flutter_inappwebview: {
callHandler: (
prop:string
option: string
) => void;
};
}
}
예컨대 이러한 형식으로 사용한다. 라고 생각하면 되겠습니다.
필요한 이유
연픽은 웹앱 중심으로 애플리케이션이 구성되어 있으며 웹에서 수행할 수 없는 동작들은
flutter에게 위임하는 형태로 동작하게 됩니다.
따라서 다양한 요구사항으로 인하여
web application과 native app 사이에 소통이 필요한 경우가 있습니다.
flutter와 javascript 사이에 통신을 주고 받기 위한 방법으로는
flutter_inappwebview라는 오픈소스 패키지가 많이 이용되곤 하는데요
연픽의 경우에도 flutter_inappwebview 를 통하여 네이티브 기능에 접근하고 있습니다.
그러나 이러한 flutter_inappwebview가 런타임에 window 객체의 프로퍼티로 존재할 것이
거의 확정적이라는 것을 알고있음에도 typescript는 그 사실을 모르기 때문에 이에 대해 알려주어야하는데요
이렇게 전역객체를 전역적으로 확장할 필요성이 있을 때에 보강기법을 고려할 수 있습니다.
시행착오
연픽의 서비스에서 flutter_inappwebview를 사용하는 코드는 기존에 이러한 형태로 작성되어있었습니다.
이해를 돕기 위해 간단한 예제로 대체하였습니다.
const windowObj = window as any
windowObj.flutter_inappwebview.callhandler('dosomething','testing')
이러한 작성방식에는 많은 문제점이 있습니다.
우선 any 타입을 사용한 것으로 인하여 windowObj의 타입은 컴파일 시점에 체크되지 않습니다.
두번째 문제점은 그로 인하여 타입스크립트가 제공하는 타입추론, 자동완성을 이용할 수 없습니다.
까다롭고 긴 프로퍼티명과 옵션들을 사람이 직접 작성하는 것은 휴먼에러를 불러오기 쉽습니다.
또한 개발속도에도 직접적인 영향을 미치는 부분입니다.
따라서 이를 개선할 필요성이 있었습니다.
우선 일반적으로 declare global과 같이 declare를 전역적으로 수행할때에는
주로 .d.ts 파일에서 정의하게 됩니다.
마침 next로 작성된 프로젝트는 next-env.d.ts를 제공합니다.
next-env.d.ts에 작성하면 되겠다. 라고 판단하고 작성했지만 이내 이상함을 느꼈습니다.
next-env.d.ts
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
export declare global {
interface Window {
flutter_inappwebview: {
callHandler: (
prop:string
option: string
) => void;
};
}
}
이런 식으로 기존 코드에 커스텀 타입을 추가하는 형태로 작성하였는데
자꾸 개발을 하다보면 아래 global 선언이 사라지는 것이었습니다.
사실 원인은 매우 간단합니다.
위 코드의 주석에서도 예측할 수 있는 것인데요
NOTE: This file should not be edited
라는 경고가 주석으로 달려있는것을 확인할 수 있습니다.
직접 이 파일을 수정하는 게 권장되지 않는 이유는
next.js가 핫리로드를 수행할때마다 next-env.d.ts 파일이 삭제되고 재생성되기 때문입니다.
그렇기 때문에 next-env.d.ts 파일에 아무리 열심히 타입정의를 써봐야
저장 한두번하면 다시 초기상태로 돌아가는 것이지요
그럼 어떻게 해야할까요?
이것을 해결하기 위해서는 custom.d.ts 파일과 같이 사용자정의 .d.ts 파일을 생성하고
그 파일 내부에서 타입 정의를 수행한 뒤 typescript에게
내가 작성한 커스텀 파일의 존재를 알리면 됩니다.
저는 프로젝트의 루트 폴더에 index.d.ts파일을 생성했습니다.
index.d.ts
export declare global {
interface Window {
flutter_inappwebview: {
callHandler: (
prop:string
option: string
) => void;
};
}
}
이후 타입스크립트에게 index.d.ts 파일의 존재를 알리기 위해
tsconfig.json 의 include option을 수정해주겠습니다.
{
"compilerOptions": {
...
}
},
"include": [
"next-env.d.ts",
"index.d.ts", << add!
"**/*.ts",
"**/*.tsx",
],
"exclude": ["node_modules"]
}
이제 타입스크립트 서버를 재 실행하고 테스트해보면
적절하게 윈도우 객체에서도 추가한 타입이 동작하는 것을 확인할 수 있습니다.
마치며
전역객체 보강과 같은 부분은 이런게 있다 정도로만 알고 넘어간 상태였는데
직접 프로덕트에 적용하는 것이 좋은 경험이었던 것 같습니다.
'프로젝트 진행기' 카테고리의 다른 글
react circle progressbar 원형 차트 구현하기 (2) | 2023.10.17 |
---|---|
[연픽] SSG 되지 않아도 되는 컴포넌트 격리하기 (1) | 2023.09.27 |
[연픽] 성공적인 프론트엔드 리팩토링을 위한 사전 준비 (3) | 2023.09.24 |
[Plip] 이메일 요청은 되도록 한번만 보내주세요 (0) | 2023.07.24 |
[PliP] 로그인의 restful 한 설계와 토큰 관리 전략 (1) | 2023.07.19 |