타입스크립트로 프로젝트를 작업을 하면서 d.ts
문서들을 많이 들여다보면서 작업을 하였습니다. 해당 문서에서 자주 보이는 declare
의 사용 방법과 사용하는 이유를 올바르게 알고 실제 개발에서 사용을 하면 좋을 것 같아 찾아보았습니다.
declare
는 직역을 하면 "선언을 하다"라는 뜻을 가지고 있습니다. 그럼 이 선언을 언제? 또 왜? 해야하는 것인지 예제를 통해서 알아보겠습니다.
Google Maps 플랫폼의 JS-SDK를 가져올 때 스크립트 태그로 다른 JS-SDK를 가져올 수 있습니다.
<script src="https://maps.googleapis.com/maps/api/js?key=Appkey..." defer/>
해당 코드를 불러온 이후에 SDK를 통해 불러온 API를 호출해줍니다.
Google Maps 개발 문서에 따라 SDK에서 제공하는 API를 호출하였지만 TypeScript 컴파일러는 위 코드에 해당하는 오류 메시지를 표시합니다. 이는 TypeScript 컴파일러가 전역 변수를 인식하지 못하기 때문입니다.
그렇다면 이를 해결할 수 있는 방법은 없을까요?
대답은 declare
키워드를 사용하여 google
을 전역변수로 설정하는 것입니다.
declare
를 통해서 선언을 한 전역변수에 대해서는 찾을수 없다는 에러가 발생하지 않습니다.
이는 declare
를 통해 컴파일러에게 나는 이미 정의되어 있는 타입이야. 나는 바로 사용할 수 있어 와 같이 친절하게 미리 알려주는 것입니다.
이를 위해서 좀 더 타입스크립트 코드를 뜯어보겠습니다.
// typescript/lib/lib.es5.d.ts
declare var NaN: number;
declare var Infinity: number;
declare var JSON: JSON;
저희가 이전에 사용하고 있던 NaN
, Infinity
, JSON
도 이미 선언이 되어있었습니다. 이처럼 실제 전역 변수 선언 이외에도 선언 키워드를 사용하여 전역 함수, 전역 클래스등을 선언할 수도 있습니다.
// typescript/lib/lib.es5.d.ts
declare function eval(x: string): any;
declare function isNaN(number: number): boolean;
declare function encodeURI(uri: string): string;
declare function parseInt(string: string, radix?: number): number;
이 말은 맞는 말이기도하지만 더 좋은 방법이 있습니다. 이미 만들어져있는 Typed 프로젝트의 d.ts
파일을 찾는다던지 혹은 해당 라이브러리의 d.ts
파일을 받아오는 것입니다.
d.ts
란 declare typescript의 약자로 타입 선언 파일 뒤에 붙이는 확장자명입니다.
해당 Google Maps의 경우 해당 라이브러리의 문서를 읽을 경우 사용 가이드를 찾을 수 잇습니다. 그런 다음 npm을 사용하여 설치할 수 있습니다.
npm i -D @types/ggole .maps
-D
로 설치하는 이유는 클라이언트에서는 해당 타입검사가 필요가 없기 때문입니다.
declare의 다른 용도들에 대해서도 알아보겠습니다. Vite 프로젝트를 사용한다면 해당 폴더를 열면 다음과 같이 clident.d.ts
모듈을 선언하는 코드들이 많이 보입니다.
왜 모듈을 선언해야 할까요? 모듈을 선언하지 않으면 안되나요? 선언하지 않으면 TypeScript 컴파일러가 이러한 모듈을 인식하지 못하고 해당 오류 메시지가 표시되기 때문입니다.
모듈을 선언할 때는 각 리소스를 해당 모듈과 함께 선언하는 것을 피하기 위해 TypeScript 2.0에서부터 와일드카드(*) 형식을 지원하기 시작하였습니다.
또한 declare
를 이용하여 기존 모듈에 정의된 유형을 확장할 수 있습니다.
import { AxiosInstance } from "axios";
declare module "@vue/runtime-core" {
interface ComponentCustomProperties {
$axios: AxiosInstance;
}
}
그런 다음 globalProperties
구성 개체의 속성을 사용하여 $axios
각 구성 요소에 속성을 추가 및 사용할 수 있습니다.
import { createApp } from "vue";
import axios from "axios";
import App from "./App.vue";
const app = createApp(App);
app.config.globalProperties.$axios = axios;
app.mount("#app");
마지막으로 구성 요소에서 구성 요소 내부 인스턴스 proxy!.$axios
속성을 통해 axios 객체에 접근할 수 있습니다.
import { getCurrentInstance , ComponentInternalInstance} from "vue"
const { proxy } = getCurrentInstance() as ComponentInternalInstance
proxy!.$axios
.get("https://jsonplaceholder.typicode.com/todos/1")
.then((response) => response.data)
.then((json) => console.log(json));
svg
파일을 컴포넌트로 만들어서 사용하기
declare
를 통해서 사용할 수 있는 가장 방법들은 위에있는 것들 말고도 여러가지 방법들이 있습니다 예를 들어서 다음과 같이 .svg
파일을 ReactComponent
로 사용하는 방법도 있습니다.
declare module '*.svg' {
import React = require('react');
export const ReactComponent: React.FC<React.SVGProps<SVGSVGElement>>;
const src: string;
export default src;
}
import { ReactComponent as DeleteIcon } from '@assets/svg/deleteIcon.svg';
const DeleteIconComponent = () => {
return <DeleteIcon/>
}
.env
파일 정의를 할 때 사용하기env
파일을 읽어올 수 없습니다.만약 vite
를 사용한다면 ImportMeta
를 정의를 해줘야합니다. 만약 여기서 자동완성까지 가능하게 한다면 개발 생산성을 향상시킬 수 있습니다.
VITE_NAVER_CLIENT_KEY: string;
VITE_GOOGLE_CLIENT_ID: string;
}
}
interface ImportMeta { import 'vite/client';
declare global {
interface ImportMetaEnv {
VITE_BASE_URL: string;
VITE_BACK_URL: string;
VITE_KAKAO_KEY: string;
VITE_KAKAO_REST_API: string;
VITE_NAVER_CLIENT_ID: string;
readonly env : ImportMetaEnv
}
위 코드에서는 ImportMetaEnv
를 정의해줌으로서 자동완성이 가능하게 변경을 해주었습니다.
vite
를 사용하지 않고 CRA
환경에서 개발한다면 다음과 같이 설정할 수 있습니다.
declare namespace NodeJS {
export interface ProcessEnv {
BASE_KEY: string;
API_KEY: string;
}
}
-------
const App = () => {
return <div>{process.env.API_KEY}</div>
}
처음으로 하는 타입스크립트 프로젝트가 마무리단계에 오면서 프로젝트를 되돌아보면 declare
와 같이 개발 향상성에 도움이 되는 다양한 기능등이 있지만 타입스크립트를 온전히 활요하지 못한거 같았습니다. 앞으로는 declare
를 활용하는 개발자가 되기를..😂