slick-address-kr

행정안전부 도로명주소 API 기반 팝업 없는 한국 주소 검색 컴포넌트 (자동완성 지원) - Korean address search with autocomplete using official road address API
4,212 1
Install
npm install slick-address-kr
Latest Version:2.0.1
Vue:3
License:MIT
Last Updated:Oct 6, 2026
Links: GitHub  ·  npm
Maintainer: devguru-j

Slick Address KR

행정안전부 도로명주소 API 기반 팝업 없는 한국 주소 검색 라이브러리

Korean address search library using official road address API - No popup required

npm version license

v1에서 올라오는 경우 docs/MIGRATION-v2.md를 먼저 읽으세요.

특징

  • ✨ 팝업 없는 인라인 UI: 다음 주소 API처럼 새 창이 열리지 않습니다
  • 🚀 가벼운 용량: 외부 의존성 없이 순수 TypeScript로 작성
  • 📱 반응형 디자인: 모바일부터 데스크톱까지 완벽 지원
  • 🎨 커스터마이징 가능: CSS 변수와 클래스로 쉽게 스타일 변경 가능
  • 🌗 다크모드 지원: --kaf-* CSS 변수와 prefers-color-scheme 대응
  • ⚡ 자동완성 모드: 타이핑하는 즉시 검색 결과 표시
  • ⌨️ 키보드 네비게이션: 화살표 키로 결과 선택, 엔터로 적용 - 마우스 없이 빠른 입력!
  • ♿ 스크린리더 지원: ARIA combobox 패턴 적용
  • 🔒 TypeScript 지원: 완전한 타입 정의 제공
  • 🌐 프레임워크 무관: Vanilla JS는 물론, React/Vue 전용 어댑터 제공
  • 🔌 자체 프록시 연동: endpoint/fetchFn 옵션으로 API 키를 서버에 숨길 수 있음
  • 📦 모든 패키지 매니저 지원: npm, yarn, pnpm, bun, deno 모두 호환

설치

npm

npm install slick-address-kr

yarn

yarn add slick-address-kr

pnpm

pnpm add slick-address-kr

bun

bun add slick-address-kr

deno

import { KoreanAddressFinder } from "npm:slick-address-kr@2";

TypeScript 사용자 참고: moduleResolution: "node"(node10) 설정은 지원하지 않습니다. "bundler", "node16", "nodenext" 중 하나를 쓰세요 — 이 패키지는 exports 필드 기반 타입 해석만 제공합니다.

빠른 시작

1. 기본 사용 (Vanilla JavaScript)

간단한 방법:

  1. 라이브러리를 빌드합니다 (examples/는 ../dist/index.js를 직접 참조하며, dist/는 git에 커밋되지 않습니다):
    npm run build
    
  2. 프로젝트 폴더의 examples/index.html 파일을 참조하세요
  3. 로컬 서버 실행:
    python -m http.server 8000
    
  4. 브라우저에서 열기:
    http://localhost:8000/examples/
    

코드 예제: examples/index.html

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <title>주소 검색</title>
  <link rel="stylesheet" href="node_modules/slick-address-kr/dist/styles.css">
</head>
<body>
  <div id="address-search"></div>

  <script src="node_modules/slick-address-kr/dist/browser.global.js"></script>
  <script>
    // 전역 이름은 window.KoreanAddressFinder다
    const finder = new KoreanAddressFinder({
      apiKey: 'YOUR_API_KEY',
      autocompleteMode: true,
      onSelect: (address, meta) => {
        console.log(meta.reason, address);
      },
    });

    finder.init(document.getElementById('address-search'));
  </script>
</body>
</html>

ESM 환경(모듈 번들러)에서는 다음처럼 씁니다.

import { KoreanAddressFinder } from 'slick-address-kr';
import 'slick-address-kr/styles.css';

const finder = new KoreanAddressFinder({
  apiKey: 'YOUR_API_KEY',
  onSelect: (address) => console.log(address),
});

finder.init(document.getElementById('address-search'));

CommonJS로 require하는 경우, require('slick-address-kr')는 이름공간 객체를 돌려주므로 .default나 이름이 붙은 export를 꺼내 써야 합니다.

const { KoreanAddressFinder } = require('slick-address-kr');
// 또는
const KoreanAddressFinder = require('slick-address-kr').default;

2. React에서 사용

전용 훅을 쓰는 것을 권장합니다.

import { useKoreanAddress } from 'slick-address-kr/react';
import 'slick-address-kr/styles.css';

function AddressForm() {
  const { containerRef, address } = useKoreanAddress({
    apiKey: import.meta.env.VITE_JUSO_API_KEY,
    autocompleteMode: true,
  });

  return (
    <>
      <div ref={containerRef} />
      {address && <p>{address.zipCode} {address.roadAddress}</p>}
    </>
  );
}

훅 없이 클래스를 직접 다루고 싶다면 examples/react-example.jsx를 참고하세요.

3. Vue에서 사용

<script setup>
import { useKoreanAddress } from 'slick-address-kr/vue';
import 'slick-address-kr/styles.css';

const { containerRef, address } = useKoreanAddress({
  apiKey: import.meta.env.VITE_JUSO_API_KEY,
  autocompleteMode: true,
});
</script>

<template>
  <div ref="containerRef"></div>
  <p v-if="address">{{ address.zipCode }} {{ address.roadAddress }}</p>
</template>

참고: slick-address-kr/vue의 컴포저블은 setup() 시점의 옵션을 한 번만 캡처합니다. React 훅과 달리 이후 onSelect 등을 바꿔도 반영되지 않습니다. 콜백을 동적으로 바꿔야 한다면 반환된 finder 인스턴스를 직접 조작하세요.

API 문서

Options

interface AddressFinderOptions {
  /** 행정안전부 API 키. endpoint를 자체 프록시로 지정한 경우 생략 가능 */
  apiKey?: string;

  /** 한 페이지에 표시할 결과 수 (기본값: 10) */
  countPerPage?: number;

  /** 현재 페이지 번호 (기본값: 1) */
  currentPage?: number;

  /** 검색 결과를 표시할 컨테이너 ID. init()에 엘리먼트를 직접 넘기면 무시됨 */
  containerId?: string;

  /** 주소 선택 시, 그리고 상세주소가 바뀔 때마다 호출된다 */
  onSelect?: (address: AddressResult, meta: AddressSelectMeta) => void;

  /** 상세주소 입력 필드 표시 여부 (기본값: true) */
  showDetailInput?: boolean;

  /** 커스텀 스타일 클래스 */
  customClass?: string;

  /** 자동완성 모드 사용 여부 (기본값: false) */
  autocompleteMode?: boolean;

  /** 호출할 주소. 자체 프록시 서버를 쓸 때 지정 */
  endpoint?: string;

  /** fetch 대체 구현. 인증 헤더 주입이나 테스트에 사용 */
  fetchFn?: typeof fetch;
}

AddressResult

interface AddressResult {
  /** 도로명 주소 */
  roadAddress: string;

  /** 지번 주소 */
  jibunAddress: string;

  /** 우편번호 */
  zipCode: string;

  /** 건물명 */
  buildingName?: string;

  /** 상세 주소 */
  detailAddress?: string;

  /** 참고 항목 */
  extraAddress?: string;

  /** 시도명 */
  sido?: string;

  /** 시군구명 */
  sigungu?: string;

  /** 읍면동명 */
  bname?: string;

  /** 상세주소·좌표 API 연동에 필요한 원본 코드 (행정구역코드, 도로명코드 등) */
  codes?: AddressCodes;
}

AddressSelectMeta

interface AddressSelectMeta {
  /**
   * 'select'        — 사용자가 검색 결과에서 주소를 고른 시점
   * 'detail-change' — 상세주소 입력값이 바뀐 시점
   */
  reason: 'select' | 'detail-change';
}

Methods

init(targetElement?: HTMLElement): void

주소 검색 UI를 초기화합니다.

const finder = new KoreanAddressFinder(options);
finder.init(); // containerId 사용
// 또는
finder.init(document.getElementById('my-container')); // 직접 엘리먼트 전달
destroy(): void

컴포넌트를 제거하고 이벤트 리스너를 정리합니다. 여러 번 호출해도 안전합니다.

finder.destroy();
getSelectedAddress(): AddressResult | null

현재 선택된 주소 정보를 반환합니다. 선택 전에는 null입니다.

const selected = finder.getSelectedAddress();
console.log(selected); // AddressResult | null
search(): Promise<void>

검색 입력란의 현재 값으로 주소를 검색합니다. 검색 버튼 클릭이나 Enter와 같은 동작을 코드에서 그대로 재현할 때 씁니다. 진행 중이던 이전 검색은 자동으로 취소됩니다.

await finder.search();

에러 처리

API 호출이 실패하면 JusoApiError가 던져집니다 (v1처럼 조용히 빈 결과로 처리되지 않습니다). AddressApiClient를 직접 쓰는 경우:

import { AddressApiClient, JusoApiError } from 'slick-address-kr';

const client = new AddressApiClient({ apiKey: 'YOUR_API_KEY' });

try {
  const { results, totalCount } = await client.search('판교역로 235', {
    currentPage: 1,
    countPerPage: 10,
  });
} catch (error) {
  if (error instanceof JusoApiError) {
    // error.code: 행안부 API 원본 에러 코드
    // error.userMessage: 화면에 바로 보여줄 수 있는 한국어 안내
    console.error(error.code, error.userMessage);
  }
}

error.userMessage는 실제 API로 확인된 코드에만 따로 쓴 안내를 담습니다.

  • 승인키 문제(E0001): 키·도메인 확인 안내와 발급 링크
  • 검색어 문제(E0005 빈 검색어, E0006 시도명만 입력, E0008 한 글자, E0009·E0012 숫자·특수문자만, E0013 금지 특수문자): 코드 번호 없는 부드러운 안내. error.isInputError가 true이고, 컴포넌트는 빨간 오류 대신 결과 머리줄 한 줄로 보여줍니다.

그 외 코드는 API가 준 원본 메시지를 그대로 담습니다.

자동완성 모드는 검색어가 두 글자가 될 때까지 검색하지 않습니다.

사용 예제

자동완성 모드

입력하는 즉시 검색 결과를 표시합니다.

const finder = new KoreanAddressFinder({
  apiKey: 'YOUR_API_KEY',
  autocompleteMode: true,
  onSelect: (address) => {
    console.log(address);
  }
});

finder.init();

키보드 네비게이션

마우스 없이 키보드만으로 주소를 빠르게 입력할 수 있습니다!

사용 방법:

  • ⬇️ 화살표 아래 / ⬆️ 화살표 위: 검색 결과 사이를 순환 이동
  • Enter: 활성화된 결과를 선택 (없으면 검색 실행)
  • ESC: 검색 결과 닫기
// 검색 결과가 열려 있으면 키보드 네비게이션이 자동으로 활성화됩니다
const finder = new KoreanAddressFinder({
  autocompleteMode: true,  // 타이핑 시 자동 검색
  onSelect: (address, meta) => {
    console.log('선택된 주소:', address, meta.reason);
    // 폼에 자동으로 입력됩니다
  }
});

finder.init();

데모: examples/proxy-test.html에서 키보드 네비게이션을 체험할 수 있습니다.

상세주소 입력 없이 사용

const finder = new KoreanAddressFinder({
  apiKey: 'YOUR_API_KEY',
  showDetailInput: false,
  onSelect: (address) => {
    console.log(address);
  }
});

finder.init();

자체 프록시 서버로 승인키 숨기기

API 키를 클라이언트 번들에 노출하고 싶지 않다면, 자체 서버가 승인키를 붙여 행안부 API를 대신 호출하게 하고 endpoint만 지정하세요.

new KoreanAddressFinder({
  endpoint: 'https://your-server.example/api/address',
  // apiKey는 서버가 붙이므로 전달하지 않습니다
});

동작하는 예제 서버는 examples/backend-proxy/에 있습니다.

테마 커스터마이징

색상은 CSS 변수로 바꿀 수 있습니다.

.kaf-wrapper {
  --kaf-accent: #ff6b35;
  --kaf-border: #ddd;
}

다크모드는 prefers-color-scheme: dark를 자동으로 따릅니다. 클래스 단위 커스터마이징이 필요하면 customClass 옵션을 쓰세요.

const finder = new KoreanAddressFinder({
  apiKey: 'YOUR_API_KEY',
  customClass: 'my-custom-class',
  onSelect: (address) => {
    console.log(address);
  }
});

finder.init();
.my-custom-class .kaf-search-btn {
  background-color: #ff6b6b;
}

.my-custom-class .kaf-search-input:focus {
  border-color: #ff6b6b;
}

API 키 발급 및 관리

이 라이브러리는 행정안전부 주소기반산업지원서비스 API를 사용합니다.

1. API 키 발급

  1. 주소기반산업지원서비스 검색 API 신청 접속
  2. 승인키 신청
  3. ⚠️ 중요: "검색 API" 선택 (팝업 API가 아님!)
  4. 사용할 도메인을 등록하세요. 발급 시 등록한 도메인에서만 호출이 허용됩니다. localhost 개발에는 도메인에 localhost를 넣은 별도의 개발용 키가 필요합니다.

2. API 키 안전하게 사용하기

⚠️ 중요: API 키를 코드에 직접 넣으면 누구나 볼 수 있습니다!

프로젝트 규모에 맞는 방법을 선택하세요:

방법 A: 설정 파일 사용 (간단한 프로젝트)
// config.js (Git에 올리지 않음)
export const config = {
  jusoApiKey: 'your_actual_api_key_here'
};

// main.js
import { config } from './config.js';

const finder = new KoreanAddressFinder({
  apiKey: config.jusoApiKey,
  onSelect: (address) => console.log(address)
});
방법 B: 환경 변수 사용 (React, Vue 등)
# .env
VITE_JUSO_API_KEY=your_key_here
const finder = new KoreanAddressFinder({
  apiKey: import.meta.env.VITE_JUSO_API_KEY,
  onSelect: (address) => console.log(address)
});
방법 C: 백엔드 프록시 서버 (상용 서비스 ⭐ 추천)

API 키를 서버에만 저장하고 클라이언트는 자체 서버를 통해 API 호출:

// 클라이언트에서는 apiKey 불필요
const finder = new KoreanAddressFinder({
  endpoint: 'https://your-server.example/api/address',
  onSelect: (address) => console.log(address)
});

자세한 내용은 docs/API-KEY-GUIDE.md 및 examples/backend-proxy/ 참조

다음 주소 API와의 비교

기능 Slick Address KR 다음 주소 API
팝업 ❌ 없음 ✅ 있음 (새 창)
인라인 UI ✅ 지원 ❌ 미지원
커스터마이징 ✅ 자유롭게 가능 ⚠️ 제한적
자동완성 ✅ 지원 ✅ 지원
데이터 소스 행정안전부 공공 API 다음 자체 DB
의존성 ✅ 독립적 ⚠️ 다음 의존
모바일 최적화 ✅ 완벽 지원 ✅ 지원

브라우저 지원

  • Chrome (최신 2개 버전)
  • Firefox (최신 2개 버전)
  • Safari (최신 2개 버전)
  • Edge (최신 2개 버전)
  • iOS Safari (12+)
  • Android Chrome (최신)

더 읽어보기

라이선스

MIT License - 자유롭게 사용하세요!

기여하기

이슈와 PR은 언제나 환영합니다! 자세한 개발 절차는 CONTRIBUTING.md를 참고하세요.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

문제 해결

"승인되지 않은 KEY 입니다" 오류가 나요

이 라이브러리는 API 키가 필수입니다. 키 없이 사용하면 동작하지 않습니다.

  1. 행정안전부 도로명주소 OpenAPI에서 "검색 API" 키를 직접 발급받으세요. (팝업 API 아님!)
  2. 발급받은 키를 apiKey 옵션으로 전달하세요:
    new KoreanAddressFinder({ apiKey: '발급받은_본인_키', /* ... */ });
    
  3. 행정안전부 개발용(dev) 키는 발급 후 일정 기간이 지나면 만료됩니다. 만료 시 새로 발급받으세요.

CORS 에러가 발생해요

행정안전부 API는 CORS를 지원합니다. 만약 에러가 발생한다면:

  1. API 키가 올바른지 확인
  2. ⚠️ 중요: API 발급 시 "검색 API"로 발급받았는지 확인 (팝업 API가 아님!)
  3. 키 발급 시 등록한 도메인에서만 호출이 허용됩니다. 사용할 도메인(예: localhost, 실제 서비스 도메인)을 juso.go.kr에 등록했는지 확인하세요. localhost 개발에는 별도의 개발용 키가 필요합니다.
  4. HTTPS 사용 권장

검색 결과가 나오지 않아요

  1. 인터넷 연결 확인
  2. API 키가 올바른지 확인
  3. ⚠️ API 발급 시 "검색 API"로 선택했는지 확인
  4. 콘솔에서 에러 메시지 확인 (v2부터는 JusoApiError가 던져지므로 원인이 명확히 보입니다)

지원


Project tango down by Devguru-J

Related Packages

@krds.ui/vue

대한민국 정부 디자인 시스템(KRDS) Vue 3 컴포넌트 라이브러리 - KRDS 원본의 마크업...

2,755 1
@trevoreyre/autocomplete

Simple autocomplete component in vanilla JS and Vue

62,090 437
@trevoreyre/autocomplete-vue

Simple autocomplete component in vanilla JS and Vue

388,169 437
@trevoreyre/autocomplete-js

Simple autocomplete component in vanilla JS and Vue

224,366 437
vue-a11y-directives

A comprehensive set of Vue 3 directives for building accessible web applications...

20,510

Version History

Version Released Vue License
2.0.1 >=3 MIT
2.0.0 >=3 MIT
1.0.4 MIT
1.0.3 MIT
1.0.2 MIT