본문 바로가기
PROGRAM/JAVA / JSP

도로명 주소 API 연동방식, 외부 API 연동 시 유의사항

by ojava 2020. 12. 14.
반응형

처음에 도로명 주소 쓰라고 했을 때는 이걸 어떻게 쓰나 싶었다.

뭐든 간에 익숙한 것을 새로운 것으로 바꾸는 데는 큰 저항이 있는 법이다. 그게 오랜 시간 사용했을 수록 더더욱.

지번으로 구성되던 기존의 주소 체계를 도로명으로 변경할 때는 외우기도 힘들었고 찾아가기도 힘들다고 생각했는데, 글쓰면서 생각해보니 이제 내가 살고 있는 집 주소를 도로명 주소로만 알고 있어서 원래 지번이 뭔지를 모른다.

아, 길찾기의 경우는 할 말이 없는게 원래 길을 잘 못 찾는 관계로 예나 지금이나 못 찾는건 마찬가지다.

 

도로명 주소를 언제부터 썼나 싶어서 도로명주소법 시행령을 찾아보니, 시행일이 2017. 7. 26 이다. 생각보다 얼마 안됐는데 너무 익숙해진 도로명 주소. 

 

오늘 이 글을 쓰게 된 이유는 이렇게 익숙해진 도로명 주소를 검색하기 위해서 주소 검색 API를 사용하게 되는데 도로명주소의 경우 행정안전부에서 제공하는 도로명주소 사이트가 있다.

오른쪽 상단에 보면 개발자센터가 있는데 여기를 통해 API 연계 및 주소 DB 연계 등을 진행할 수 있다.

 

www.juso.go.kr/addrlink/main.do

 

도로명주소 개발자센터

 

www.juso.go.kr

 

API를 제공하고 이와 관련해서 기본 정보 및 요청변수, 출력결과를 보여주고 구현 예제도 제공한다.

갑작스럽게 검색이 안되는 일이 있어서 도움센터로 문의해서 개발자와 직접 연락하는 일도 몇 번 있었는데 빠르게 대응해주셔서 좋은 기억이 남아있다.

 

이미 대부분의 사이트가 잘 구현되어 있어서 큰 문제는 없을 거고 신규로 구현한다고 하더라도 API에서 팝업 API도 제공하고 간단하게 검색 API도 잘 제공하고 있어서 구현에 어려움이 있지는 않겠지만, 이와 같은 사항이 없도록 주의가 필요하겠다 싶어 정리한다.

 

 


 

도로명 주소 API 연동방식, 외부 API 연동 시 유의사항

 

API 연동방식은 검색형 API를 이용해서 이미 구성된 화면에서 도로명 주소 API를 이용해 검색어를 전달하고 해당 결과를 받아와 구현된 화면에 보여주는 방식으로 진행할 예정이다.

화면은 각자 너무 다양한 방식으로 구성되어 있을터라 java단 호출 로직과 구현 시 유의사항만 간략하게 작성하도록 한다.

 

@Controller
public class AddressController {
	private static final String API_URL = "https://www.juso.go.kr/addrlink/addrLinkApi.do";
    
    @GetMapping("address")
    public HashMap searchAddress (String keyword, Integer page, 
    								@RequestParam(defaultValue = "10") Integer length) throw IOException {
     
        URI uri = UriComponentsBuilder.fromUriString(API_URL)
                                      .queryString("currentPage", page)
                                      .queryString("countPerPage", length)
                                      .queryString("resultType", "json") // xml로 변경 가능
                                      .queryString("confmKey", "발급받은 키 값")
                                      .build()
                                      .encode("utf-8")
                                      .toUri();

        RestTemplate restTemplate = new RestTemplate();
        // URI로 호출한 값을 string형태로 읽어들임
        String responseText = restTemplate.getForObject(uri, String.class);
        ObjectMapper mapper = new ObjectMapper();

        // 결과값을 읽어들일 형태 지정. Address라는 class에 지정함
        ObjectReader reader = mapper.readerFor(Address.class).withRootName("results");
        Address addr = reader.readValue(responseText);
	
        Map<String, Object> result = new HashMap<String, Object>();
        result.put("header", addr.getHeader());
        result.put("addresses", addr.getAddresses());
        
        return result;        
    }
}

외부 API를 호출해서 불러들이는 기본적인 구조다.

UriComponentBuilder를 통해 호출한 내용을 불러들여서 원하는 형태로 가공하고 있는데, 해당 내용을 잘 보면 String 형태로 읽어들인 responseText 값을 Address 라는 class의 형태에 맞춰서 읽어들인다.

 

 

아래 내용이 외부 API를 쓸 때 유의해야 하는 부분이다.

API에서 json type으로 결과값을 받아왔고, 해당 내용을 String으로 return 받아서 아래의 Address라는 클래스에서 Lombok을 이용하여 읽어들이며 자동으로 getter/setter가 제공된다.

도로명 주소 API를 참고하여 출력결과를 보면 아시겠지만 common, juso 이렇게 두 묶음으로 값이 나오는데 각각을 내가 원하는 형태로 받아오기 위해서 JsonProperty를 지정해줬다.

// Lombok을 이용하여 데이터 받아오는 클래스
// 하기 변수들의 return type을 주목하자.
@Data
public class Address () {
    @JsonProperty("common")
    HashMap<String, Object> header;
    // Header header; // 사용금지 잘못된 예시
	
    @JsonProperty("juso")
    List<HashMap<String, Object>> addresses;
    // List<AddressDetail> addresses; // 사용금지 잘못된 예시 
}

 

현재는 HashMap 형태로 받아오지만, 기존에는  Header와 AddressDetail이라는 별도의 Class에 담도록 되어있었는데 이 형태는 외부 API를 이용할 때 문제가 될 수 있다.

특히 이렇게 공공기관에서 제공하는 API일수록 가져다가 사용해야 하는 사람이 아쉬운 쪽이므로 변경되면 얼른 받아다가 수정해서 사용해야 하는 입장이므로, 변경되는 사항을 알기도 어렵지만 변경되면 영향도가 크다.

 

 

기존에 정의되어 있던 클래스를 보자.

// 하기 내용은 잘못된 내용 참고입니다. 사용하지 마세요.
@Data
public class Header () {
    String totalCount;
    int currentPage;
    int countPerPage;
    String errorCode;
    String errorMessage;
}

// 하기 내용은 잘못된 내용 참고입니다. 사용하지 마세요.
@Data
public class AddressDetail () {
    String roadAddr;
    String roadAddrPart1;
    String roadAddrPart2;
    .
    .
    .
    String lnbrMnnm;
    String lnbrSlno;
    String emdNo;    
}

 

API의 출력 결과를 보고 각각의 필드를 선언해두었다.

이 형태는 출력 결과가 변경되지 않는다면 큰 문제가 되지 않겠지만, 변경되는 경우 치명적인 문제가 있다.
신규 필드가 추가되면 해당 내용을 같이 갱신해줘야 한다.

API에서는 신규 필드가 추가된 형태로 결과값을 전달해주는데 전달받는 쪽에서는 해당 필드가 없다면, 값을 읽어들여서 해당 클래스에 setter로 입력하는 과정에서 정의되지 않은 필드를 찾는 오류가 발생하면서 결과값을 사용할 수가 없게 된다.

org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "~~~~"

이를 해결하기 위해서 각각의 필드값을 선언하고 불러들이던 Header, AddressDetail 클래스 맵핑 방식이 아닌 HashMap 형태로 값을 받아오도록 변경하였다.

필드가 추가되더라도 java단에서는 소스코드 변경없이 읽어들일 수 있고 화면단에서만 신규 필드에 대한 처리를 진행하면 된다. 

 

다른 해결 방법으로 기존의 AddressDetail 클래스에 Annotation을 추가하면 새로운 필드에 대해 무시할 수 있다.

@JsonIgnoreProperties(ignoreUnknown = true)

단, 이 경우에는 새롭게 추가되는 필드가 선택적으로 사용해야 하는 경우나 추가되는 내용이 필요 없을 경우에만 의미 있는 방법이고 신규 추가된 필드를 사용해야 하면 결국은 필드값을 새로 정의해줘야 하는 문제가 있어서 원론적인 해결은 불가능하겠다.

 

 

외부 API를 사용해야 하는 경우, 고정된 형태로 값을 받아오지 않도록 주의가 필요하다.

언제든 변경될 수 있음을 가정하고 가변적으로 유연하게 대처가 가능한 형태로 구성해야 서비스 연속성에 영향을 주지 않을 수 있다.

반응형