Meerkit
Meerkit
HomeDocsComponentsAPI
GitHub

Getting Started

5
IntroductionInstallationProject StructureThemingDeployment

CLI

6
CLI Overviewinit & setupstart, stop & openstatus, check, env & logsdbadd, test & update

Core Concepts

5
AuthenticationRoles & PermissionsData ModelLogin LockoutSecurity

Guides

3
Protect a pageAdd a roleCall your own API

Components

24

사용자 화면12

Login FormSignup FormEmail VerificationTerms AgreementGoogle LoginPassword ResetProfile & PasswordNicknameAuth DialogProfile ImageUser AvatarUser Menu

관리자 화면6

Member TableMember DirectoryMember AI UsageMember AI Usage HistoryRole ManagerRole Select

공통5

Address FieldFile DropzoneLoadingDateRoute Guards

AI Components

4
AI Prompt InputThinking IndicatorAPI Key InputVoice

API

6
OverviewHealthAuthUsers · 내 정보Users · 관리자LLM

Reference

2
Hooks & StateFAQ

Address Field

공통 컴포넌트사용자 화면과 관리자 화면 모두에서 쓰입니다.

주소 검색 버튼으로 우편번호와 주소를 함께 입력하는 두 가지 주소 입력 방식입니다.

shadcn/ui · Inputshadcn/ui · Dialogshadcn/ui · Button

[주소 검색] 버튼으로 검색해서 주소를 고르면 우편번호와 주소가 함께 채워지고, 상세 주소만 직접 입력하는 컴포넌트입니다. 서비스에 맞게 입력 방식 2가지와 검색 방식 2가지를 고를 수 있습니다.

우편번호·주소 칸은 직접 입력하지 못하고 검색 결과로만 채워집니다. 입력칸을 눌러도 검색이 실행되지 않으며, 검색은 항상 주소 검색 버튼으로 시작합니다.

Input types

타입구성이럴 때
"basic"주소 · 상세 주소우편번호를 따로 저장하지 않아도 되는 서비스
"postcode"우편번호 · 주소 · 상세 주소배송, 결제처럼 우편번호가 필요한 서비스

Type A — basic

주소 입력칸 옆의 주소 검색을 눌러 검색합니다. 아래에 상세 주소칸이 이어집니다.

현재 값: {"postcode":"","address":"","address_detail":""}

tsx
<AddressField
  variant="basic"
  searchMode="dialog"
  value={value}
  onChange={setValue}
/>

Type B — postcode

우편번호칸과 검색 버튼이 위에, 그 아래 주소칸, 상세 주소칸 순서입니다. 검색 결과를 고르면 우편번호와 주소가 한 번에 들어갑니다.

현재 값: {"postcode":"","address":"","address_detail":""}

tsx
<AddressField
  variant="postcode"
  searchMode="dialog"
  value={value}
  onChange={setValue}
/>
미리보기는 테헤란로, 봉은사로 같은 도로명이나 미어캣타워 같은 건물명으로 검색하면 샘플 주소 150건 중에서 찾아 줍니다. 아래 "검색 창" 섹션에서 직접 해 보세요.

Search modes

주소를 어디서 어떻게 검색할지 두 가지 중에 고릅니다. signup.addressSearch 설정 또는 searchMode prop으로 정합니다.

searchMode무엇이 열리나서버결과가 많을 때
"popup" (기본)카카오(다음) 우편번호 팝업필요 없음팝업이 알아서 목록을 나눠 보여 줍니다.
"dialog"우리 검색 창 (검색어 입력 → 결과 목록)필요함 (GET /address/search)10건씩 보여 주고 [더 보기]로 이어서 불러옵니다.
meerkit.config.ts
export default defineMeerkitConfig({
  signup: {
    optionalFields: ["address"],
    addressType: "postcode",   // 입력 방식
    addressSearch: "dialog",   // 검색 방식: "popup" | "dialog"
  },
});

Search dialog

dialog 방식은 검색어를 입력하면 결과 목록이 나옵니다. 아래에서 직접 검색해 보세요. (서울처럼 넓게 검색하면 150건이 나와 더 보기를 쓸 수 있고, 오류를 검색하면 오류 화면을, 없는 단어를 검색하면 결과 없음 화면을 볼 수 있습니다.)

tsx
<AddressField
  variant="postcode"
  searchMode="dialog"
  searchProvider={mySearch}   // 생략하면 서비스 서버의 GET /address/search
  value={value}
  onChange={setValue}
/>

화면 동작

상황화면
처음 열었을 때"주소를 검색해 보세요" 안내와 검색 예시
검색 중"검색 중…" 표시 (버튼 잠김)
결과 있음"검색 결과 N건"과 목록. 각 줄에 도로명 주소, 우편번호, 지번 주소(건물명)
결과가 많음10건씩 보여 주고 [더 보기]로 이어 붙임. "N건 표시 중"으로 진행 상황 표시. 30건이 넘으면 번지나 건물명을 더해 보라는 안내
모두 불러옴"모든 결과를 불러왔습니다."
결과 없음"…와 맞는 주소가 없습니다"와 다시 검색 안내
서버 오류오류 문구와 [다시 시도] 버튼. 더 보기에서 실패하면 이미 불러온 목록은 그대로 두고 오류만 표시
주소를 고름우편번호와 주소가 입력칸에 채워지고 창이 닫힘. 이어서 상세 주소 입력

Many results

검색 결과가 수백 건이면 한 번에 모두 보여 줄 수 없으니, 한 쪽씩 불러오고 검색어를 좁히게 안내하는 방식을 씁니다.

  • 한 번에 10건만 요청하고, [더 보기]를 누르면 다음 쪽을 이어서 붙입니다. 화면이 길어지지 않도록 목록 영역은 높이가 제한되고 안에서 스크롤됩니다.
  • 입력한 글자마다 요청하지 않고, 검색 버튼이나 Enter를 눌렀을 때만 요청합니다.
  • 결과가 아주 많으면 "번지나 건물명을 더하면 더 빨리 찾을 수 있어요" 안내를 보여 줍니다. 카카오 로컬 API처럼 가져올 수 있는 개수·쪽수에 상한이 있는 서비스에서는 전부 볼 수 없으므로 검색어를 좁히는 것이 가장 좋은 해결책입니다.
  • popup 방식(카카오 우편번호 팝업)은 이 모든 것을 팝업이 알아서 처리하므로 서버 작업이 필요 없습니다.

Kakao address API (server)

dialog 방식은 서비스 서버의 GET /address/search를 호출합니다. 서버가 카카오 로컬 API(주소 검색)를 대신 불러서 아래 모양으로 돌려주면 됩니다. 카카오 REST API 키가 브라우저에 노출되지 않도록 반드시 서버를 거치세요.

GET /address/search
GET /address/search?q=테헤란로&page=1&size=10

// response 200
{
  "items": [
    {
      "postcode": "06236",
      "address": "서울특별시 강남구 테헤란로 152",
      "jibun_address": "서울특별시 강남구 역삼동 737",
      "building_name": "강남파이낸스센터"
    }
  ],
  "total": 87,
  "is_end": false
}
server example (FastAPI)
import httpx
from fastapi import APIRouter

router = APIRouter()

@router.get("/address/search")
async def search_address(q: str, page: int = 1, size: int = 10):
    async with httpx.AsyncClient() as client:
        res = await client.get(
            "https://dapi.kakao.com/v2/local/search/address.json",
            params={"query": q, "page": page, "size": size},
            headers={"Authorization": f"KakaoAK {settings.kakao_rest_api_key}"},
        )
    data = res.json()
    items = []
    for d in data["documents"]:
        road = d.get("road_address") or {}
        jibun = d.get("address") or {}
        items.append({
            "postcode": road.get("zone_no", ""),
            "address": road.get("address_name") or d["address_name"],
            "jibun_address": jibun.get("address_name"),
            "building_name": road.get("building_name") or None,
        })
    meta = data["meta"]
    return {"items": items, "total": meta["pageable_count"], "is_end": meta["is_end"]}
  • 우편번호(zone_no)는 도로명 주소가 있을 때만 옵니다. 지번 주소만 있는 결과는 postcode가 빈 문자열이 될 수 있습니다.
  • 카카오 API의 사용 한도와 약관, size·page의 허용 범위는 카카오 개발자 문서에서 확인하세요.
  • 다른 주소 API(행정안전부 도로명주소 등)를 써도 같은 응답 모양으로 맞추면 화면은 바꿀 필요가 없습니다.
백엔드는 아직 구현 전이라 이 엔드포인트는 설계 기준(예정)입니다. 서버 없이 쓰려면 popup 방식을 쓰세요.

Popup mode

기본값입니다. 주소 검색을 누르면 카카오(다음) 우편번호 팝업이 열리고, 주소를 고르면 우편번호와 주소가 함께 채워집니다. 서버나 API 키가 필요 없습니다. 스크립트를 불러오지 못하면 직접 입력으로 자동 전환되어 가입이 막히지 않습니다.

이 문서의 미리보기는 실제 카카오 팝업 대신 위의 검색 창을 사용합니다. 팝업 방식은 배포한 서비스에서 동작합니다.

Use it in signup & profile

가입·내 정보 수정 폼에서는 설정만 바꾸면 됩니다.

addressType서버로 가는 필드
basicaddress, address_detail
postcodepostcode, address, address_detail
POST /auth/signup
{
  "user_id": "newbie",
  "postcode": "06236",
  "address": "서울특별시 강남구 테헤란로 152",
  "address_detail": "12층 1201호",
  ...
}
주소를 받으려면 백엔드 users 테이블에 postcode(basic은 불필요), address, address_detail 컬럼이 있어야 합니다 (Data Model 참고).

Custom search

두 방식 모두 직접 만든 검색으로 바꿀 수 있습니다.

tsx — dialog: 내 검색 함수
const mySearch: AddressSearchProvider = async (query, page, size) => {
  const res = await fetch(`/my-api/address?q=${query}&page=${page}&size=${size}`);
  const data = await res.json();
  return { items: data.list, total: data.count, isEnd: data.last };
};

<AddressField searchMode="dialog" searchProvider={mySearch} ... />
tsx — popup: 내 팝업
async function mySearch() {
  const picked = await openMyAddressPopup();
  return picked ? { postcode: picked.zip, address: picked.road } : null; // 닫으면 null
}

<AddressField searchMode="popup" onSearch={mySearch} ... />

Props

PropType설명
value필수{ postcode, address, address_detail }현재 값. 세 칸 모두 문자열입니다.
onChange필수(value) => void검색하거나 상세 주소를 입력하면 바뀐 전체 값을 전달합니다.
variant"basic" | "postcode"입력 방식. 기본 basic.
searchMode"popup" | "dialog"검색 방식. 기본 popup.
searchProvider(query, page, size) => Promise<{ items, total, isEnd }>dialog 방식의 검색 방법. 없으면 서비스 서버의 /address/search.
onSearch() => Promise<{ postcode, address } | null>popup 방식의 검색 방법. 없으면 카카오 우편번호 팝업.
idPrefixstring입력창 id 접두사. 한 화면에 둘 이상 둘 때 겹치지 않게 지정합니다.
disabledboolean모두 잠급니다.

Accessibility

  • 각 입력에 aria-label(우편번호, 주소, 상세 주소)이 있고, 검색창 입력에도 aria-label이 있습니다. 검색 중 상태는 role="status", 오류는 role="alert"로 읽힙니다.
  • 검색 창은 Radix Dialog 기반이라 포커스가 갇히고 Esc로 닫을 수 있으며, 열리면 검색어 입력칸에 바로 포커스가 갑니다.
  • 검색 결과의 각 줄은 버튼이라 키보드(Tab, Enter)로 고를 수 있습니다.
Role SelectFile Dropzone
© Meerkit. All rights reserved.GitHub