CDN에서 받아온 js에 타입 넣기
문제 상황
네이버페이 결제 시스템을 구현을 하는데, 네이버페이 결제창을 호출하기 위해서는 자기들의 js 파일을 script
태그를 이용해서 불러와야 했다.
npm을 통한 설치 방식이나, js파일을 직접 다운받아서 사용하는 방식은 지양하라고 하였기에, 어쩔 수 없이 script
태그를 쓸 수 밖에 없었다.
React 환경에서 CDN의 script 잘 불러오기
React는 SPA 어플리케이션이기 때문에 index.html
에 <script src=
와 같은 방식으로 직접 박을 시, 결제창을 띄울 필요가 없는 (해당 라이브러리를 사용하지 않는) 페이지에서도 해당 js 파일이 다운로드가 될 것이기 때문에 사용자의 로딩 속도를 괜히 늦추는 요인이 될 것이라 생각했다. 이는 UX 측면에서도 감점 요소이기도 하고.
따라서 useScript
라는 훅을 사용하여 (유명한 분께서 만든 레퍼런스를 그대로 가져왔다) 동적으로 script
태그를 만들도록 하였다.
import { useScript } from './useScript';
function useNaverPay() {
const status = useScript('https://nsp.pay.naver.com/sdk/js/naverpay.min.js');
function createPayInstance() {
// Error!! Naver가 뭔지 알 수 없다
return Naver.Pay.create({
mode: 'development',
clientId: 'secret',
});
}
// ....
}
하지만 이렇게 불러온 js 파일은 Typescript 입장에서는 전혀 알 수 없는 친구이기 때문에, 정적 타이핑을 보장 받지 못함은 물론이고, 심지어는 에러까지 발생시킨다.
결국 저 ‘Naver’이라는 녀석을 쓰는 곳곳마다 @ts-expect-error
를 사용하며 덕지덕지 붙는 것을 눈뜨고 쳐다볼 수 밖에 없었다.
그런데, script 태그를 이용해서 얻어와진 변수들은 window.Naver
과 같은 모양으로 window 전역 변수에 저장이 된다는 사실을 알았다. 아하!
본격적으로 타입 붙여보기
export type Naver = {
Pay: {
create: (clientInfo: ClientInfo) => PayInstance;
getVersion: () => string;
};
};
type ClientInfo = {
mode: 'development' | 'production';
clientId: string;
chainId: string;
payType: 'normal' | 'recurrent';
openType?: 'page' | 'popup';
onAuthorize?: (oData: any) => void;
};
// ...
우선 네이버페이 개발자 문서를 보면서 타입을 만들어 준다.
내가 만들 도메인 로직에 필요한 타입만 작성해줄 수 있다. (결제 상품이 보험 및 위험 업종 등인 경우에만 필수 값인 변수 같은 것들은 내 서비스에는 필요 없기 때문에 굳이 작성하지 않고 생략해도 된다는 뜻!)
import { useScript } from './useScript';
function useNaverPay() {
const status = useScript('https://nsp.pay.naver.com/sdk/js/naverpay.min.js');
// @ts-expect-error
const Naver: Naver = window.Naver;
function createPayInstance() {
return Naver.Pay.create({
mode: 'development',
clientId: 'secret',
});
}
// ....
}
이런 식으로 window에게서 Naver를 꺼내온 후, 변수에 이 Naver의 참조를 저장 해준다.
거기에 내가 직접 만들어준 타입을 단언해서 붙여준다면 끝이다.
끝! 바로 바로 나와주는 자동완성.
이 맛에 어려워도 타입스크립트 쓰는 것이 아니겠는가?