다음 우편번호 API(주소 검색) 사용하기 - 1편

다음 우편번호 API(주소 검색) 사용하기 - 2편

daum_postcode_search | Flutter Package
flutter_inappwebview | Flutter Package

이번 글과 다음글에서는 다음 주소 검색/찾기에 대해서 알아보도록 하겠다.

다음 주소 검색은 운영하고 있는 커머스, 여행 등 다양한 앱에서 사용하고 있는 주소 검색이다. 현재는 다음에서 서버를 지원하고 있지 않고, html 웹 코드만 지원하고 있어서 직접 서버에 올려서 사용을 해야하거나, 로컬 서버를 구동해서 사용해야 한다.

다음 주소 검색을 사용하게 되면 UI 커스텀이 불가능하고 웹뷰를 사용해야 한다는 단점이 있지만 로컬 서버로 구동하면 무료로 사용이 가능하고, 이러한 방법이 별로라면 카카오 주소 API, 공공 API의 주소 검색 등을 활용할 수 도 있다. 하지만 트래픽 제한이 있어서 무료로는 사용이 어렵다.

Flutter에서는 다음 주소 검색과 관련된 라이브러리로 두 개정도가 올라와 있는 상태인데, 이번 글에서는 그 중 하나인 daum_postcode_search 라이브러리로 구현하는 방법에 대해서 알아보고, 다음 글에서는 직접 로컬 서버를 구동해서 사용하는 방법에 대해서 소개해 보려고 한다.

Flutter

다음 주소 검색에 사용할 라이브러리는 daum_postcode_search 라이브러리이고, 해당 라이브러리에 사용되는 웹뷰로 flutter_inappwebview를 사용하고 있어서 옵션 파라미터를 사용하고 싶다면 inappwebview도 함께 사용하여야 한다.

먼저 해당 라이브러리는 정말 간편하게 다음 주소를 사용할 수 있다는 장점은 있지만, 로컬 서버 구동시에 대한 에러 핸들링이 되어있지 않아 로컬 서버 구동에 실패하거나, 다운됬을 때 무한 로딩으로만 노출되게 된다는 단점이 있다.

dependencies

dependencies:
	daum_postcode_search: ^0.0.2
    flutter_inappwebview: ^5.7.2+3

AndroidManifest

project > android > app > src > main > AndroidManifest.xml

 <application
        android:label="flutter_velog_sample"
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher"
        android:usesCleartextTraffic="true"> // Add
        <activity
            android:name=".MainActivity"
		...>
		</activity>
</application>

UI

다음 주소 검색을 통해서 검색된 주소 관련 정보를 노출시키는 페이지를 만들었다.

먼저 상단에 DataModel 객체를 Nullable 타입으로 선언하자. DataModel은 daum_postcode_search 라이브러리에서 제공되는 주소 관련된 객체이다.

DataModel? _dataModel;

검색된 주소 정보를 노출시키는 부분이다. 주로 사용되는 데이터는 아래 정도의 데이터가 사용될 것 같아서 해당 정보만 노출시켜 봤다. 이 외에도 객체 안에는 추가적인 정보가 더 있어서 필요한 정보를 가져다 사용하면 된다.

Expanded(
      child: ListView(
          children: [
                if (_dataModel != null) ...[
                  _text("Address", _dataModel!.address),
                  _text("Road Address", _dataModel!.roadAddress),
                  _text("Jibun Address", _dataModel!.jibunAddress),
                  _text("Sido", _dataModel!.sido),
                  _text("Sigungu", _dataModel!.sigungu),
                  _text("B Name", _dataModel!.bname),
                  _text("Road Name", _dataModel!.roadname),
                  _text("Building Name", _dataModel!.buildingName),
                  _text("Address(EN)", _dataModel!.addressEnglish),
                  _text("Road Address(EN)", _dataModel!.roadAddressEnglish),
                  _text("Jibun Address(EN)", _dataModel!.jibunAddressEnglish),
                  _text("Sido(EN)", _dataModel!.sidoEnglish),
                  _text("Sigungu(EN)", _dataModel!.sigunguEnglish),
                  _text("B Name(EN)", _dataModel!.bnameEnglish),
                  _text("Road Name(EN)", _dataModel!.roadnameEnglish),
                  _text("Zonecode", _dataModel!.zonecode),
                  _text("Sigungu Code", _dataModel!.sigunguCode),
                  _text("B Code", _dataModel!.bcode),
                  _text("Building Code", _dataModel!.buildingCode),
                  _text("Roadname Code", _dataModel!.roadnameCode),
                  _text("Address Type", _dataModel!.addressType),
                  _text("Apertment", _dataModel!.apartment),
                  _text("User Language Type", _dataModel!.userLanguageType),
                  _text("User Selected Type", _dataModel!.userSelectedType),
                ],
              ],
            ),
          ),
Padding _text(String title, String expain) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 6),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          SizedBox(
              width: MediaQuery.of(context).size.width * 0.31,
              child: Center(
                  child: Text(
                title,
                style: const TextStyle(
                    fontSize: 12, color: Color.fromRGBO(195, 195, 195, 1)),
              ))),
          Flexible(
            child: Text(
              expain,
              style: const TextStyle(fontSize: 13, fontWeight: FontWeight.bold),
            ),
          ),
        ],
      ),
    );
  }

주소 검색 페이지로 라우팅되는 버튼 UI 부분이다.

여기서 중요한 점은 네비게이터로 넘기고 해당 페이지가 닫힐 때 callback을 보내주는데, 여기에서 리턴되는 데이터가 바로 DataModel 객체이다.

Padding(
            padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 20),
            child: GestureDetector(
              onTap: () {
                HapticFeedback.mediumImpact();
                Navigator.of(context)
                    .push(MaterialPageRoute(builder: (context) {
                  return const LibraryDaumPostcodeScreen();
                })).then((value) {
                  if (value != null) {
                    setState(() {
                      _dataModel = value;
                    });
                  }
                });
              },
              child: Container(
                width: MediaQuery.of(context).size.width,
                decoration: BoxDecoration(
                  color: Colors.amber,
                  borderRadius: BorderRadius.circular(12),
                ),
                child: const Padding(
                  padding: EdgeInsets.symmetric(vertical: 12),
                  child: Center(
                    child: Text(
                      "Daum 주소 검색",
                      style: TextStyle(
                          color: Color.fromRGBO(41, 41, 41, 1),
                          fontSize: 18,
                          fontWeight: FontWeight.bold),
                    ),
                  ),
                ),
              ),
            ),
          ),

DaumPostcodeSearch

다음 주소 검색 웹뷰 부분을 작성해보자.

webPageTitle을 넣을 수 있고, onConsolMessage를 통해서 웹뷰에서 오는 리턴 값등을 받아볼 수도 있다. 이외에도 권한 설정과 관련된 안드로이드 퍼미션을 넣을 수 있는데, 다음 주소 웹뷰 사용시 필수 권한은 없어서 사용하지 않아도 된다. 그리고 에러관련된 핸들링도 가능하다.
하지만 위에서도 언급했듯이 daum_postcode_search 라이브러리는 패키지 안에 다음 주소 검색 웹뷰의 html코드를 flutter_inappwebview 라이브러리에서 제공되는 localhost를 구동시켜 로컬 서버로 띄우는 구조인데, 로컬 서버가 작동을 하지 않을 때는 에러 처리가 불가능하다.

Scaffold(
      backgroundColor: AppTheme.darkTheme.backgroundColor,
      appBar: appBar(title: "다음 주소 검색"),
      body: DaumPostcodeSearch(
        webPageTitle: "다음 주소 검색",
        initialOption: InAppWebViewGroupOptions(),
        onConsoleMessage: ((controller, consoleMessage) {}),
        onLoadError: ((controller, url, code, message) {}),
        onLoadHttpError: (controller, url, statusCode, description) {},
        onProgressChanged: (controller, progress) {},
        androidOnPermissionRequest: (controller, origin, resources) async {
          return PermissionRequestResponse(
              resources: resources,
              action: PermissionRequestResponseAction.GRANT);
        },
      ),
    );

Result

Git

https://github.com/boglbbogl/flutter_velog_sample/tree/main/lib/library/daum_postcode

마무리

다음 주소 검색을 daum_postcode_search 라이브러리를 통해 간단하게 개발을 해봤다.
다음 시간에는 라이브러리를 사용하지 않고 html파일을 추가하여 로컬 서버를 직접 구동시켜 해당 기능을 사용해 보자.

profile
Flutter Developer

0개의 댓글