WebClient - xml

Chooooo·2023년 8월 30일
0

TIL

목록 보기
4/22
post-thumbnail

😎 xml형태 open api 호출

data.go.kr에서 인증키 발급까지 끝마친 이후 과정

대부분 인코딩된 인증키가 사용됨 !

이제 인증키는 받았으니 데이터가 있는 xml 파일에 접속해야해

상세 사용 설명서에 나오는 내용을 토대로 활용해보자.

  • data.go.kr의 예제가 매우 불친절하다.
  • 그러기에 예제 코드를 통해 어떤 내용들을 필요로 하는지 확인하면서 !!!

나는 행정안전부_봉사참여정보서비스의 open api를 xml형태로 호출하고자 한다.

서비스URL:http://openapi.1365.go.kr/openapi/service/rest/VolunteerPartcptnService/getVltrSearchWordList

을 애초에 제공을 해줬다.

요청변수들 중에 어떤 값들을 활용하여 출력 값들을 뽑아낸 후 DB에 넣을 지 고민하다가 모집 종료일을 기준으로 데이터들을 뽑아내기로 했다.

우선 지금까지 WebClient 사용법은 다 알았으니

  • json이 아닌 xml형태로 호출하는 방법을 알아보자 !!

😀 open api xml 요청을 위한 WebClient 기본 세팅

/**
         * open api를 xml 형식으로 받아오기 위해 기본 세팅.
         */
        WebClient webClient = WebClient.builder()
                .baseUrl(BASE_URL_1365)
                .exchangeStrategies(ExchangeStrategies.builder()
                        .codecs(clientCodecConfigurer ->
                                clientCodecConfigurer.defaultCodecs()
                                        .jaxb2Encoder(new Jaxb2XmlEncoder())
                        ).codecs(clientCodecConfigurer ->
                                clientCodecConfigurer.defaultCodecs()
                                        .jaxb2Decoder(new Jaxb2XmlDecoder())
                        )
                        .build()
                )
                .build();

해당 코드는 open api를 xml 타입으로 호출받기 위한 기본세팅이라고 생각하면 된다. 여기에 .baseUrl()을 통해 서비스URL을 넣어놓으면 기본 세팅이 끝난다. (인코딩, 디코딩 세팅)

🎈 먼저 기본적으로 String으로 출력 후 객체를 만들 준비를 하자. (String으로 출력해서 어떤 형태로 받아올 DTO를 만들지를 정해야한다. 왜냐하면 공공기관 응답값이 매우 불친절…)

  • String으로 제대로 호출이 되는 것을 확인 후에 반환 DTO를 설정해서 매핑시키면 돼 !!!
  • 이런 방식으로 매번 진행할 예정 !!

😀 xml 타입으로 WebClient를 사용하여 요청 보내기 !

💨 위에서 WebClient 인스턴스는 생성했으니 이제 인스턴스 메서드 체인을 통해 요청 보내기

/**
         * 내가 원하는 형식으로 반환 성공 !
         */
        String res = webClient.get()
                .uri(uriBuilder -> uriBuilder
                        .path("/rest")
                        .path("/VolunteerPartcptnService")
                        .path("getVltrSearchWordList")
                        .queryParam("serviceKey", api_key_1365)
                        .queryParam("noticeEndde", num)   //모두 첫번째 파라미터에는 제공해주는 이름을 제대로 넣어줘야해 !! 
                        .build())
                .accept(MediaType.valueOf(MediaType.APPLICATION_XML_VALUE))
                .retrieve()
                .bodyToMono(String.class)
                .block();

위 코드는 json과 비슷하지만, .accept(MediaType.valueOf(MediaType.*APPLICATION_XML_VALUE*)) 를 통해 xml 형식으로 요청하는 코드이다.

.path()를 통해 요청하는 api의 서비스URL을 만들어주면 된다 !!

위 방식으로 마지막에 성공했지만, 안되서 아예 url을 만들고 적용했던 방식이 있다.

/**
         * String format을 활용한 xml 활용

        String url = String.format("http://openapi.1365.go.kr/openapi/service/rest/VolunteerPartcptnService/getVltrSearchWordList?serviceKey=%s&noticeEndde=%d", api_key_1365, num);
        String res = webClient.get()
                .uri(url)
                .accept(MediaType.valueOf(MediaType.APPLICATION_XML_VALUE))
                .retrieve()
                .bodyToMono(String.class)
                .block();
         */

위 방식으로도 성공했다. 하지만 첫번째 방식을 통해 open api를 xml타입으로 요청하자 !!


😀 전체 코드

/**
         * open api를 xml 형식으로 받아오기 위해 기본 세팅.
         */
        WebClient webClient = WebClient.builder()
                .baseUrl(BASE_URL_1365)
                .exchangeStrategies(ExchangeStrategies.builder()
                        .codecs(clientCodecConfigurer ->
                                clientCodecConfigurer.defaultCodecs()
                                        .jaxb2Encoder(new Jaxb2XmlEncoder())
                        ).codecs(clientCodecConfigurer ->
                                clientCodecConfigurer.defaultCodecs()
                                        .jaxb2Decoder(new Jaxb2XmlDecoder())
                        )
                        .build()
                )
                .build();
        /**
         * 내가 원하는 형식으로 반환 성공 !
         */
        String res = webClient.get()
                .uri(uriBuilder -> uriBuilder
                        .path("/rest")
                        .path("/VolunteerPartcptnService")
                        .path("getVltrSearchWordList")
                        .queryParam("serviceKey", api_key_1365)
                        .queryParam("noticeEndde", num)
                        .build())
                .accept(MediaType.valueOf(MediaType.APPLICATION_XML_VALUE))
                .retrieve()
                .bodyToMono(String.class)
                .block();
        /**
         * 로그 확인
         */
        log.info("num : {}", num);
        log.info("response : {}", res);
        return res;

😀 Response Dto 반환

이제 xml 타입으로 요청해서 받아오는 것까지 했으니, DTO로 반환하는 거 진행 !!

  • 우선 받아온 정보를 매핑하는 것이 매우 힘들었다.

먼저 요청해서 응답 받아온 xml 데이터를 기반으로 매핑해보자.

  • xml 응답값
    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <response>
        <header>
            <resultCode>00</resultCode>
            <resultMsg>NORMAL SERVICE.</resultMsg>
        </header>
        <body>
            <items>
                <item>
                    <actBeginTm>10</actBeginTm>
                    <actEndTm>14</actEndTm>
                    <actPlace>아름다운가게 망원역점</actPlace>
                    <adultPosblAt>Y</adultPosblAt>
                    <gugunCd>3130000</gugunCd>
                    <nanmmbyNm>아름다운가게 망원역점</nanmmbyNm>
                    <noticeBgnde>20230704</noticeBgnde>
                    <noticeEndde>20230731</noticeEndde>
                    <progrmBgnde>20230710</progrmBgnde>
                    <progrmEndde>20230831</progrmEndde>
                    <progrmRegistNo>3004555</progrmRegistNo>
                    <progrmSj>[아름다운가게 망원역점] 7월~8월  매장지원 봉사 활동(8회 이상) (모집중)</progrmSj>
                    <progrmSttusSe>2</progrmSttusSe>
                    <sidoCd>6110000</sidoCd>
                    <srvcClCode>기타 &gt; 기타</srvcClCode>
                    <url>https://1365.go.kr/vols/P9210/partcptn/timeCptn.do?type=show&amp;progrmRegistNo=3004555</url>
                    <yngbgsPosblAt>N</yngbgsPosblAt>
                </item>
                <item>
                    <actBeginTm>18</actBeginTm>
                    <actEndTm>20</actEndTm>
                    <actPlace>시립마포청소년센터 지하1층 수영장 앞 게이트</actPlace>
                    <adultPosblAt>Y</adultPosblAt>
                    <gugunCd>3130000</gugunCd>
                    <nanmmbyNm>시립마포청소년센터</nanmmbyNm>
                    <noticeBgnde>20230718</noticeBgnde>
                    <noticeEndde>20230726</noticeEndde>
                    <progrmBgnde>20230724</progrmBgnde>
                    <progrmEndde>20230728</progrmEndde>
                    <progrmRegistNo>3010227</progrmRegistNo>
                    <progrmSj>시립마포청소년센터 지하1층 수영장 게이트 출입관리 근무</progrmSj>
                    <progrmSttusSe>2</progrmSttusSe>
                    <sidoCd>6110000</sidoCd>
                    <srvcClCode>기타 &gt; 기타</srvcClCode>
                    <url>https://1365.go.kr/vols/P9210/partcptn/timeCptn.do?type=show&amp;progrmRegistNo=3010227</url>
                    <yngbgsPosblAt>Y</yngbgsPosblAt>
                </item>
                <item>
                    <actBeginTm>9</actBeginTm>
                    <actEndTm>13</actEndTm>
                    <actPlace>마포구4호점 우리동네키움센터</actPlace>
                    <adultPosblAt>Y</adultPosblAt>
                    <gugunCd>3130000</gugunCd>
                    <nanmmbyNm>마포구4호점우리동네키움센터마을연계</nanmmbyNm>
                    <noticeBgnde>20230710</noticeBgnde>
                    <noticeEndde>20230825</noticeEndde>
                    <progrmBgnde>20230724</progrmBgnde>
                    <progrmEndde>20230825</progrmEndde>
                    <progrmRegistNo>3007159</progrmRegistNo>
                    <progrmSj>마포구4호점 우리동네키움센터 초등돌봄 및 놀이지도 (오전)</progrmSj>
                    <progrmSttusSe>2</progrmSttusSe>
                    <sidoCd>6110000</sidoCd>
                    <srvcClCode>기타 &gt; 기타</srvcClCode>
                    <url>https://1365.go.kr/vols/P9210/partcptn/timeCptn.do?type=show&amp;progrmRegistNo=3007159</url>
                    <yngbgsPosblAt>N</yngbgsPosblAt>
                </item>
                <item>
                    <actBeginTm>13</actBeginTm>
                    <actEndTm>18</actEndTm>
                    <actPlace>마포구4호점 우리동네키움센터</actPlace>
                    <adultPosblAt>Y</adultPosblAt>
                    <gugunCd>3130000</gugunCd>
                    <nanmmbyNm>마포구4호점우리동네키움센터마을연계</nanmmbyNm>
                    <noticeBgnde>20230710</noticeBgnde>
                    <noticeEndde>20230825</noticeEndde>
                    <progrmBgnde>20230724</progrmBgnde>
                    <progrmEndde>20230825</progrmEndde>
                    <progrmRegistNo>3007165</progrmRegistNo>
                    <progrmSj>마포구4호점 우리동네키움센터 초등돌봄 및 놀이지도 (오후)</progrmSj>
                    <progrmSttusSe>2</progrmSttusSe>
                    <sidoCd>6110000</sidoCd>
                    <srvcClCode>기타 &gt; 기타</srvcClCode>
                    <url>https://1365.go.kr/vols/P9210/partcptn/timeCptn.do?type=show&amp;progrmRegistNo=3007165</url>
                    <yngbgsPosblAt>N</yngbgsPosblAt>
                </item>
                <item>
                    <actBeginTm>13</actBeginTm>
                    <actEndTm>18</actEndTm>
                    <actPlace>서울시 강서구 등촌로5길79 2층 어린이도서관</actPlace>
                    <adultPosblAt>Y</adultPosblAt>
                    <gugunCd>3150000</gugunCd>
                    <nanmmbyNm>화곡지역아동센터</nanmmbyNm>
                    <noticeBgnde>20230630</noticeBgnde>
                    <noticeEndde>20230727</noticeEndde>
                    <progrmBgnde>20230703</progrmBgnde>
                    <progrmEndde>20230728</progrmEndde>
                    <progrmRegistNo>3003298</progrmRegistNo>
                    <progrmSj>(오후)2023년 7월 어린이도서관 정리 및 관리 (13시~18시)</progrmSj>
                    <progrmSttusSe>3</progrmSttusSe>
                    <sidoCd>6110000</sidoCd>
                    <srvcClCode>기타 &gt; 기타</srvcClCode>
                    <url>https://1365.go.kr/vols/P9210/partcptn/timeCptn.do?type=show&amp;progrmRegistNo=3003298</url>
                    <yngbgsPosblAt>Y</yngbgsPosblAt>
                </item>
                <item>
                    <actBeginTm>10</actBeginTm>
                    <actEndTm>14</actEndTm>
                    <actPlace>아름다운가게 목동점 </actPlace>
                    <adultPosblAt>Y</adultPosblAt>
                    <gugunCd>3140000</gugunCd>
                    <nanmmbyNm>아름다운가게목동점</nanmmbyNm>
                    <noticeBgnde>20230629</noticeBgnde>
                    <noticeEndde>20230728</noticeEndde>
                    <progrmBgnde>20230725</progrmBgnde>
                    <progrmEndde>20230829</progrmEndde>
                    <progrmRegistNo>3002609</progrmRegistNo>
                    <progrmSj>[아름다운가게 목동점] 8월 화요일 오전(10시~2시) 봉사자 모집 </progrmSj>
                    <progrmSttusSe>3</progrmSttusSe>
                    <sidoCd>6110000</sidoCd>
                    <srvcClCode>기타 &gt; 기타</srvcClCode>
                    <url>https://1365.go.kr/vols/P9210/partcptn/timeCptn.do?type=show&amp;progrmRegistNo=3002609</url>
                    <yngbgsPosblAt>N</yngbgsPosblAt>
                </item>
                <item>
                    <actBeginTm>14</actBeginTm>
                    <actEndTm>17</actEndTm>
                    <actPlace>서울 강서구 강서로 5길 50, 곰달래문화복지센터 4층</actPlace>
                    <adultPosblAt>Y</adultPosblAt>
                    <gugunCd>3150000</gugunCd>
                    <nanmmbyNm>강서구다문화가족지원센터</nanmmbyNm>
                    <noticeBgnde>20230314</noticeBgnde>
                    <noticeEndde>20230804</noticeEndde>
                    <progrmBgnde>20230506</progrmBgnde>
                    <progrmEndde>20230805</progrmEndde>
                    <progrmRegistNo>2955298</progrmRegistNo>
                    <progrmSj>[강서구가족센터]_아이돌봄 봉사자모집(독서자조모임)</progrmSj>
                    <progrmSttusSe>3</progrmSttusSe>
                    <sidoCd>6110000</sidoCd>
                    <srvcClCode>기타 &gt; 기타</srvcClCode>
                    <url>https://1365.go.kr/vols/P9210/partcptn/timeCptn.do?type=show&amp;progrmRegistNo=2955298</url>
                    <yngbgsPosblAt>N</yngbgsPosblAt>
                </item>
                <item>
                    <actBeginTm>13</actBeginTm>
                    <actEndTm>17</actEndTm>
                    <actPlace>서울 강서구 강서로 5길 50, 곰달래문화복지센터 4층</actPlace>
                    <adultPosblAt>Y</adultPosblAt>
                    <gugunCd>3150000</gugunCd>
                    <nanmmbyNm>강서구다문화가족지원센터</nanmmbyNm>
                    <noticeBgnde>20230314</noticeBgnde>
                    <noticeEndde>20230818</noticeEndde>
                    <progrmBgnde>20230520</progrmBgnde>
                    <progrmEndde>20230819</progrmEndde>
                    <progrmRegistNo>2955299</progrmRegistNo>
                    <progrmSj>[강서구가족센터]_아이돌봄 봉사자모집(무지개자조모임)</progrmSj>
                    <progrmSttusSe>3</progrmSttusSe>
                    <sidoCd>6110000</sidoCd>
                    <srvcClCode>기타 &gt; 기타</srvcClCode>
                    <url>https://1365.go.kr/vols/P9210/partcptn/timeCptn.do?type=show&amp;progrmRegistNo=2955299</url>
                    <yngbgsPosblAt>N</yngbgsPosblAt>
                </item>
                <item>
                    <actBeginTm>9</actBeginTm>
                    <actEndTm>17</actEndTm>
                    <actPlace>구립강서구직업재활센터 관내</actPlace>
                    <adultPosblAt>Y</adultPosblAt>
                    <gugunCd>3150000</gugunCd>
                    <nanmmbyNm>구립강서구직업재활센터</nanmmbyNm>
                    <noticeBgnde>20230522</noticeBgnde>
                    <noticeEndde>20230821</noticeEndde>
                    <progrmBgnde>20230522</progrmBgnde>
                    <progrmEndde>20230821</progrmEndde>
                    <progrmRegistNo>2983884</progrmRegistNo>
                    <progrmSj>장애인 직업재활시설 작업활동 봉사(정기/반복)</progrmSj>
                    <progrmSttusSe>2</progrmSttusSe>
                    <sidoCd>6110000</sidoCd>
                    <srvcClCode>기타 &gt; 기타</srvcClCode>
                    <url>https://1365.go.kr/vols/P9210/partcptn/timeCptn.do?type=show&amp;progrmRegistNo=2983884</url>
                    <yngbgsPosblAt>N</yngbgsPosblAt>
                </item>
                <item>
                    <actBeginTm>9</actBeginTm>
                    <actEndTm>13</actEndTm>
                    <actPlace>강서구립 우장산숲속도서관</actPlace>
                    <adultPosblAt>Y</adultPosblAt>
                    <gugunCd>3150000</gugunCd>
                    <nanmmbyNm>우장산숲속도서관</nanmmbyNm>
                    <noticeBgnde>20230519</noticeBgnde>
                    <noticeEndde>20230825</noticeEndde>
                    <progrmBgnde>20230601</progrmBgnde>
                    <progrmEndde>20230831</progrmEndde>
                    <progrmRegistNo>2984197</progrmRegistNo>
                    <progrmSj>우장산숲속도서관에서 고등학생이상 / 6월~8월 / 평일 오전 자원봉사자를 모집합니다.</progrmSj>
                    <progrmSttusSe>3</progrmSttusSe>
                    <sidoCd>6110000</sidoCd>
                    <srvcClCode>기타 &gt; 기타</srvcClCode>
                    <url>https://1365.go.kr/vols/P9210/partcptn/timeCptn.do?type=show&amp;progrmRegistNo=2984197</url>
                    <yngbgsPosblAt>Y</yngbgsPosblAt>
                </item>
            </items>
            <numOfRows>10</numOfRows>
            <pageNo>1</pageNo>
            <totalCount>4721</totalCount>
        </body>
    </response>

해당 응답값을 보고 이제 @XmlRootElement, @XmlElement 어노테이션을 사용해서 적절히 매핑했어야 했다.

먼저 나는 body태그 안쪽만 해주면 되는줄 알았는데 그게 아니었다.

가장 바깥쪽 태그인 response 부터 그 안쪽 header, body 모두 해줬어야 했다. 사실 이유는 잘 모름…

매핑해서 만든 Dto 객체를 보면서 다시 말해자면…

😀 Volunteer


	@Setter
    @ToString
    @NoArgsConstructor
    @AllArgsConstructor
    @XmlRootElement(name = "response")
    public class Volunteer {
    
        @XmlElement(name = "header")
        private Header header;
    
        @XmlElement(name = "body")
        private Body body;
    
        @Setter
        @ToString
        @NoArgsConstructor
        @AllArgsConstructor
        @XmlRootElement(name = "header")
        private static class Header {
            @XmlElement(name = "resultCode")
            private String resultCode;
            @XmlElement(name = "resultMsg")
            private String resultMsg;
        }
    
        @Setter
        @ToString
        @NoArgsConstructor
        @AllArgsConstructor
        @XmlRootElement(name = "body")
        private static class Body {
            //    @XmlElementWrapper(name = "items")    //items로 감싸겠다는 뜻 !!
            @XmlElement(name = "items")
            private Items items;
        }
    
        @Setter
        @ToString
        @NoArgsConstructor
        @AllArgsConstructor
        @XmlRootElement(name = "items")
        private static class Items {
            @XmlElement(name = "item")
            private List<Item> itemList;
        }
    
        @ToString
        @NoArgsConstructor
        @AllArgsConstructor
        @XmlRootElement(name = "item")
        private static class Item {
            @XmlElement(name = "actBeginTm")
            private String actBeginTm;
            @XmlElement(name = "actEndTm")
            private String actEndTm;
            @XmlElement(name = "actPlace")
            private String actPlace;
            @XmlElement(name = "adultPosblAt")
            private String adultPosblAt;
            @XmlElement(name = "gugunCd")
            private String gugunCd;
            @XmlElement(name = "nanmmbyNm")
            private String nanmmbyNm;
            @XmlElement(name = "noticeBgnde")
            private String noticeBgnde;
            @XmlElement(name = "noticeEndde")
            private String noticeEndde;
            @XmlElement(name = "progrmBgnde")
            private String progrmBgnde;
            @XmlElement(name = "progrmEndde")
            private String progrmEndde;
            @XmlElement(name = "progrmRegistNo")
            private String progrmRegistNo;
            @XmlElement(name = "progrmSj")
            private String progrmSj;
            @XmlElement(name = "progrmSttusSe")
            private String progrmSttusSe;
            @XmlElement(name = "sidoCd")
            private String sidoCd;
            @XmlElement(name = "srvcClCode")
            private String srvcClCode;
            @XmlElement(name = "url")
            private String url;
            @XmlElement(name = "yngbgsPosblAt")
            private String yngbgsPosblAt;
        }
    
    }

먼저 클래스에만 붙여야 하는 @XmlRootElement 를 바깥쪽 태그부터 하나씩 붙여주면 된다.

즉 Volunteer, Header, Body, Items, Item 만들 때마다 @XmlRootElement 를 붙여주자 !!

그리고 내부 요소들에는 @XmlElement 를 붙여서 요소임을 나타내주면 된다 !! 나는 혹시 몰라서 하나하나 매핑될 정보들의 이름을 다 써줬다! (name = “” 안에 제대로 똑같이 써줘야 매핑됨!)

이런 식으로 클래스들을 하나씩 작성해주니.. 드디어 매핑이 되어 제대로 출력됐다 !!!

😀 Controller에서 xml로 반환

최상단에 @RestController 를 써놨기에 알아서 반환될 줄 알았는데.. 그게 안된다. 이 부분은 다시 공부해서 더 채워넣을 것이다.

@GetMapping(value = "/test5", produces = MediaType.APPLICATION_XML_VALUE)
    public Volunteer getData(@RequestParam("noticeEndde") Integer num) {
        Volunteer res = service.returnData(num);
        return res;
    }

그렇기에 xml로 반환해주기 위해서는 (@RestController 반환 형식이 기본이 json이라 로그로는 찍히는데 반환값이 아예 안나옴) @GetMapping(value = "/test5", produces = MediaType.APPLICATION_XML_VALUE) 이 부분처럼 반환 양식을 xml로 해주면 제대로 출력된다 !!

정리

이로써, WebClient로 open api를 json으로 받아오는 방법과, xml로 받아오는 방법 모두를 공부해 보았다. xml 부분은 과연 사용할 일이 있겠느냐마는… 공공기관 데이터는 아직까지도 open api가 xml만 있는 것이 있다. 그렇기에 이렇게 한번 정리해 놓으면 나중에 분명 쉬울 것!!

  • 그리고 xml로 반환하는 것이 아니라 요청해서 받아온걸 다시 json으로 어떻게 반환하는지 알아봐야겠다…
profile
back-end, 지속 성장 가능한 개발자를 향하여

0개의 댓글