Project Soljik DevLog
  • Project Soljik 소개 문서
  • 프로젝트 소개
    • 솔직한 온라인 쇼핑, Soljik
    • 기술 스택
    • 컨벤션
  • 최적화
    • 이미지 최적화
    • Lazy Loading / Code Splitting
    • 렌더링 최적화
  • 트러블 슈팅
    • BaaS(Firebase) 환경에서의 세션 관리
    • Firebase 저장소의 CORS 에러 해결
    • 다음 우편번호 API 적용하기
Powered by GitBook
On this page
  • 문제점
  • 해결 방법
  • 결과
  1. 트러블 슈팅

다음 우편번호 API 적용하기

다음에서 제공하는 우편번호 API 를 React + TypeScript 프로젝트에 적용할 때 발생할 수 있는 문제점들의 해결 방법을 정리한 문서입니다.

PreviousFirebase 저장소의 CORS 에러 해결

Last updated 2 months ago

다음에서 제공하는 우편번호 서비스의 를 보면, 기본적인 사용법이 다음과 같이 명시되어 있습니다.

<script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
<script>
    new daum.Postcode({
        oncomplete: function(data) {
            // 팝업에서 검색결과 항목을 클릭했을때 실행할 코드를 작성하는 부분입니다.
            // 예제를 참고하여 다양한 활용법을 확인해 보세요.
        }
    }).open();
</script>

스크립트가 로딩되면 글로벌 객체의 daum 에 접근하여 Postcode 생성자를 사용하는 방식입니다.

React 프로젝트에서 이 기능을 사용하기 위해서 가장 간단한 방식은 바로 프로젝트의 index.html 파일에 직접 스크립트 내용을 추가하는 방식입니다.


문제점

index.html 문서에 스크립트 내용을 포함하는 것은 분명 간단한 방식이지만, 다음과 같은 문제점들을 고려해야 합니다.

  1. 해당 기능을 사용하지 않는 경우에도 무조건 스크립트를 불러와야 한다.

  2. 스크립트를 불러오는 과정에서 에러가 발생하는 경우에는 사용할 수 없다.

  3. 타입스크립트를 사용하는 경우 글로벌 객체(window.daum )가 인식되지 않는다.


해결 방법

1

스크립트를 필요한 시점에 부르기

document.createElement 메서드를 활용해 필요한 시점에 특정 스크립트 태그를 만들어서 불러옵니다. 또한 다른 외부 api 스크립트들도 똑같은 방식으로 불러올 수 있도록 util 함수로 분리하여 재사용성을 확보합니다.

utils.ts
// util 로 분리한 스크립트 로딩 메서드
export const loadScript: LoadScript = (src, id) => {
  return new Promise((resolve, reject) => {
    if (document.getElementById('KakaoPostcodeApi')) resolve();
    else {
      const script = document.createElement('script');
      script.id = id;
      script.src = src;
      script.async = true;
      script.onload = () => {
        resolve();
      };
      script.onerror = () => {
        reject();
      };
      document.body.appendChild(script);
    }
  });
};
2

Promise 를 활용해 후처리 구현하기

스크립트 태그의 onload, onerror 핸들러와 Promise 객체를 사용해서 성공, 실패 여부에 따라 동기적인 후처리를 할 수 있도록 합니다.

다음 우편번호 API 의 로딩과 검색메서드를 제공하는 커스텀 훅

usePostcode.ts
import { useEffect, useState } from 'react';
import { loadScript } from '@/utils/utils';
import { toast } from 'sonner';
import { DaumPostcodeResult } from '@/types/DaumPostcodeType';

type OpenPostcodeSearch = (
  onSelect: (data: DaumPostcodeResult) => void,
) => void;

const usePostcode = () => {
  const [scriptLoaded, setScriptLoaded] = useState(false);
  useEffect(() => {
    toast.promise(
      loadScript(
        'https://t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js',
        'KakaoPostcodeApi',
      ),
      {
        loading: 'Daum 우편번호 기능을 불러오는 중입니다.',
        success: () => {
          setScriptLoaded(true);
          return 'Daum 우편번호 기능을 불러왔습니다. "우편번호 찾기" 버튼을 클릭해 주소를 검색할 수 있습니다.';
        },
        error:
          'Daum 우편번호 기능을 불러오는데 실패했습니다. 직접 입력하시거나 "새로고침"을 통해 다시 페이지를 로딩해주세요.',
      },
    );
  }, []);

  const openPostcodeSearch: OpenPostcodeSearch = (onSelect = () => {}) => {
    if (!scriptLoaded) {
      toast.error('아직 Daum 우편번호 기능을 불러오지 못했습니다.');
    }

    new window.daum.Postcode({
      oncomplete: (data) => {
        // 불러온 주소 데이터를 사용할 callback 함수
        onSelect(data);
      },
    }).open();
  };

  return { openPostcodeSearch, scriptLoaded };
};

export default usePostcode;
3

Type 지정 파일 생성하기

우편번호 API 의 타입 선언 파일을 만들고 global 객체 및 우편번호 api 메서드의 타입을 지정합니다.

DaumPostcodeType.ts
declare global {
  interface Window {
    daum: IDaum;
  }
}

interface IDaum {
  Postcode: new (options: {
    oncomplete: (data: DaumPostcodeResult) => void;
  }) => { open: () => void };
}

export type DaumPostcodeResult = {
  postcode: string;
  postcode1: string;
  postcode2: string;
  postcodeSeq: string;
  zonecode: string;
  address: string;
  addressEnglish: string;
  addressType: 'R' | 'J';
  apartment: 'Y' | 'N';
  bcode: string;
  bname: string;
  bnameEnglish: string;
  bname1: string;
  bname1English: string;
  bname2: string;
  bname2English: string;
  sido: string;
  sidoEnglish: string;
  sigungu: string;
  sigunguEnglish: string;
  sigunguCode: string;
  query: string;
  buildingName: string;
  buildingCode: string;
  jibunAddress: string;
  jibunAddressEnglish: string;
  roadAddress: string;
  roadAddressEnglish: string;
  autoRoadAddress: string;
  autoRoadAddressEnglish: string;
  autoJibunAddress: string;
  autoJibunAddressEnglish: string;
  userLanguageType: 'K' | 'E';
  userSelectedType: 'R' | 'J';
  noSelected: 'Y' | 'N';
  hname: string;
  roadnameCode: string;
  roadname: string;
  roadnameEnglish: string;
};

사용 예시 코드는 다음과 같습니다.

const { openPostcodeSearch } = usePostcode();

const handleClickPostcode: MouseEventHandler<HTMLButtonElement> = () => {
  openPostcodeSearch((data) => {
    // 검색된 data 처리 로직
  });
};

커스텀 훅에서 제공하는 openPostcodeSearch 메서드를 실행할 때, 검색 결과 데이터를 사용할 콜백 함수만 지정해 사용할 수 있습니다.


결과

  • 사용자 관점에서 우편번호 기능을 이용할 때 문제 발생 여부를 확인하고, 안내 메세지를 받을 수 있습니다.

  • 개발자 관점에서 명확한 타입 정의로 안정성을 높이고, 향후 유지보수 및 확장성이 용이해졌습니다.

사용자 가이드 문서