티스토리 뷰

 

이전 포스트: 공공 데이터 포털에서 원하는 Open API 사용 신청하고 데이터 가져오는 방법

 

[Backend] 공공데이터 포털 OPEN API 크롤링 사용 방법 A to Z | 인증키 오류 해결 방법

안녕하세요 오늘은 공공데이터 포탈에서 데이터를 크롤링하는 방법을 공유해 보겠습니다. 저는 이번 겨울 방학에 2024 구글 솔루션 챌린지에 참가하기 위해 열심히 프로젝트를 진행하고 있는데

yuejeong.tistory.com

 


 

자 그럼 드디어 XML 형태로 포탈로부터 응답받은 데이터를 우리가 원하는 형태로 가공하는 방법을 확인해 보겠습니다.

이때, 포탈로부터 받을 수 있는 데이터는 XML 뿐만 아니라 JSON으로도 받을 수 있는데요, 하지만 XML만 지원하는 Open API 가 훨씬 많기 때문에 XML로만 받도록 통일시켜서 코드를 구현했습니다. 그래서 이번 포스트도 XML로 받은 데이터를 원하는 형태로 가공하는 방법을 보여드리겠습니다!

 

아래 코드는 컴버스 버스 예약하기 1단계로, 사용자의 위치 좌표를 기준으로 반경 범위 내의 정류소 목록을 넘겨주는 공공 데이터를 사용하는 기능입니다. 

 

ReservationApiController.java

@RestController
@RequiredArgsConstructor
@RequestMapping("/reservation")
public class ReservationApiController {//예약 기능에서 공공데이터 API다루는 경우를 모든 컨트롤러
    @Autowired
    ReservationApiService apiService;

    @Value("${serviceKey}")     // 보안을 위해 application.properties에 저장해둠
    String serviceKey;

    // 지정된 좌표와 반경 범위 내의 정류소 목록을 넘겨주는 공공 데이터 url
    String getStationByPos = "http://ws.bus.go.kr/api/rest/stationinfo/getStationByPos?";


    // 예약하기 1단계: 승객 위치 기반으로 주위 버스 정류장 리스트 반환
    @GetMapping("/startst")
    public ResponseEntity<List<StartBusStopDto>> StartBusStopList(
            @RequestParam(value = "gpsX") double gpsX, // 사용자 현재 위치 X 좌표
            @RequestParam(value = "gpsY") double gpsY  // 사용자 현재 위치 Y 좌표
    ) throws Exception {

        HttpURLConnection urlConnection = null;
        InputStream stream = null;
        String result = null;
        RestTemplate restTemplate = new RestTemplate();

        // 공공데이터 API 요청을 보낼 url 생성
        String urlStr = getStationByPos + "ServiceKey=" + serviceKey +
                "&tmX=" + gpsX + "&tmY=" + gpsY + "&radius=100";
        System.out.println(urlStr);

        URI uri = new URI(urlStr);
        String xmlData = restTemplate.getForObject(uri, String.class);

        List<StartBusStopDto> NearbyStopList = apiService.StartBusStopApiParseXml(xmlData);

        return new ResponseEntity<>(NearbyStopList, HttpStatus.OK);
    }
    
}

 

우선 컨트롤러에서는 RequestMapping, GetMapping을 해주고, 요청을 보낼 공공 데이터 포털 url을 지정해 줍니다. 

그리고 보안을 위해 API 서비스키는 코드에 지정해주지 않고, application.yml에 적어두고 노출시키지 않았습니다. 대신 @Value 애노테이션을 사용해서 해당 키를 가져올 수 있지요.

String 타입의 urlStr에 적절한 순서로 값을 이어 붙여서 데이터 요청을 보낼 url을 생성합니다.

 

제가 사용할 url의 경우에는 요청을 보낼 url, 다음에 서비스키, 프론트로부터 파라미터로 받은 X 좌표, 마찬가지로 파라미터로 받은 Y 좌표, 그리고 미리 지정했던 반경 100을 적어서 url을 생성하고 해당 URL로 요청을 보내서 xmlData로 결과를 받았습니다. 

 

urlStr 값:

http://ws.bus.go.kr/api/rest/stationinfo/getStationByPos?ServiceKey=${서비스키값}&tmX=127.025414191&tmY=37.637797794&radius=100

 

결과로 받은 데이터(xmlData):

<ServiceResult>
<comMsgHeader/>
<msgHeader>
<headerCd>0</headerCd>
<headerMsg>정상적으로 처리되었습니다.</headerMsg>
<itemCount>0</itemCount>
</msgHeader>
<msgBody>
<itemList>
<arsId>09004</arsId>
<dist>24</dist>
<gpsX>127.0253079045</gpsX>
<gpsY>37.6375917165</gpsY>
<posX>202233.4630512789</posX>
<posY>459779.81076713046</posY>
<stationId>108000004</stationId>
<stationNm>수유역.강북구청</stationNm>
<stationTp>3</stationTp>
</itemList>
<itemList>
<arsId>09249</arsId>
<dist>33</dist>
<gpsX>127.0250607476</gpsX>
<gpsY>37.6376933332</gpsY>
<posX>202211.64804696792</posX>
<posY>459791.0821050885</posY>
<stationId>108000161</stationId>
<stationNm>수유역.강북구청</stationNm>
<stationTp>1</stationTp>
</itemList>
<itemList>
<arsId>09595</arsId>
<dist>35</dist>
<gpsX>127.0253881411</gpsX>
<gpsY>37.6374815091</gpsY>
<posX>202240.54736921377</posX>
<posY>459767.5821111342</posY>
<stationId>108900178</stationId>
<stationNm>수유역</stationNm>
<stationTp>1</stationTp>
</itemList>
<itemList>
<arsId>09567</arsId>
<dist>45</dist>
<gpsX>127.0249677991</gpsX>
<gpsY>37.6375906296</gpsY>
<posX>202203.4482373506</posX>
<posY>459779.6821037773</posY>
<stationId>108900177</stationId>
<stationNm>수유역</stationNm>
<stationTp>1</stationTp>
</itemList>
<itemList>
<arsId>09834</arsId>
<dist>48</dist>
<gpsX>127.0259230173</gpsX>
<gpsY>37.6376399831</gpsY>
<posX>202287.74626068593</posX>
<posY>459785.18210615637</posY>
<stationId>108900041</stationId>
<stationNm>수유역.강북구청</stationNm>
<stationTp>1</stationTp>
</itemList>
<itemList>
<arsId>09013</arsId>
<dist>67</dist>
<gpsX>127.0258703828</gpsX>
<gpsY>37.6382895216</gpsY>
<posX>202283.08130844196</posX>
<posY>459857.2651490504</posY>
<stationId>108000378</stationId>
<stationNm>수유(강북구청)역</stationNm>
<stationTp>3</stationTp>
</itemList>
<itemList>
<arsId>09272</arsId>
<dist>108</dist>
<gpsX>127.0260514034</gpsX>
<gpsY>37.6386311446</gpsY>
<posX>202299.0459896208</posX>
<posY>459895.1821264052</posY>
<stationId>108000184</stationId>
<stationNm>수유역.강북구청</stationNm>
<stationTp>1</stationTp>
</itemList>
</msgBody>
</ServiceResult>

 

자, 원하는 데이터를 받았지만 막막합니다. 여기서 내가 원하는 데이터만 가져와서 사용자에게 보내야 하는데 이거 어떻게 파싱 하지?

이걸 해결해 주는 코드는 서비스단에 구현했습니다. 

우선 내가 필요한 데이터만 담은 DTO를 만들어줍니다. 

 

StartBusStopDto.java

@Getter
@Setter
@Builder
public class StartBusStopDto {
    private String arsId; // 버스 정류장 고유 번호
    private String name; // 버스 정류장 이름
    private double gpsX; // 정류소 위치 x좌표 : 경도
    private double gpsY; // 정류소 위치  y좌표 : 위도

    public StartBusStopDto(String arsId, String name, double gpsX, double gpsY) {
        this.arsId = arsId;
        this.name = name;
        this.gpsX = gpsX;
        this.gpsY = gpsY;
    }
}

 

그리고 이제 쓸데없는 정보도 함께 담고 있는 XML 데이터에서 내가 원하는 정보의 틀인 DTO를 채우는 코드를 서비스에 구현합니다. 

 

ReservationApiService.java

@Service
@RequiredArgsConstructor
public class ReservationApiService {

    public List<StartBusStopDto> StartBusStopApiParseXml(String xmlData) throws Exception {
        List<StartBusStopDto> startBusStopList = new ArrayList<>();

        //Open API 에서 추출한 xml 데이터에서 원하는 정보 추출하기
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document document = builder.parse(new InputSource(new StringReader(xmlData)));

        NodeList itemListNodes = document.getElementsByTagName("itemList");
        System.out.println("itemlist개수: "+itemListNodes.getLength());


        for (int i = 0; i < itemListNodes.getLength(); i++) {
            Node itemListNode = itemListNodes.item(i);

            if (itemListNode.getNodeType() == Node.ELEMENT_NODE) {
                Element itemListElement = (Element) itemListNode;

                StartBusStopDto startBusStop;

                String arsId = getElementValue(itemListElement, "arsId");
                String name = getElementValue(itemListElement, "stationNm");
                double gpsX = Double.parseDouble(getElementValue(itemListElement, "gpsX"));
                double gpsY = Double.parseDouble(getElementValue(itemListElement, "gpsY"));

                startBusStop = new StartBusStopDto(arsId, name, gpsX, gpsY);

                startBusStopList.add(startBusStop);
            }
        }

        return startBusStopList;
    }
    
    private String getElementValue(Element element, String tagName) {
        NodeList nodeList = element.getElementsByTagName(tagName);
        if (nodeList.getLength() > 0) {
            Node node = nodeList.item(0);
            return node.getTextContent();
        }
        return null;
    }

}

 

XML 문서를 파싱 하기 위한 DocumentBuilderFactory, DocumentBuilder 인스턴스를 생성하고, 파싱 할 XML 데이터를 받아서 Document 객체로 파싱 합니다. 그리고 NodeList를 통해서 XML 문서에 itemList 태그를 가진 모든 노드를 가져오고, 노드를 반복하면서 각 노드의 정보를 추출하도록 코드를 구성했습니다. 

 

위 코드를 거치고 나면 아래와 같이 데이터가 깔끔하게 파싱 한 결과를 받을 수 있습니다.

[
    {
        "arsId": "09004",
        "name": "수유역.강북구청",
        "gpsX": 127.0253079045,
        "gpsY": 37.6375917165
    },
    {
        "arsId": "09249",
        "name": "수유역.강북구청",
        "gpsX": 127.0250607476,
        "gpsY": 37.6376933332
    },
    {
        "arsId": "09595",
        "name": "수유역",
        "gpsX": 127.0253881411,
        "gpsY": 37.6374815091
    },
    {
        "arsId": "09567",
        "name": "수유역",
        "gpsX": 127.0249677991,
        "gpsY": 37.6375906296
    },
    {
        "arsId": "09834",
        "name": "수유역.강북구청",
        "gpsX": 127.0259230173,
        "gpsY": 37.6376399831
    },
    {
        "arsId": "09013",
        "name": "수유(강북구청)역",
        "gpsX": 127.0258703828,
        "gpsY": 37.6382895216
    },
    {
        "arsId": "09272",
        "name": "수유역.강북구청",
        "gpsX": 127.0260514034,
        "gpsY": 37.6386311446
    }
]

 

위 코드는 단순히 XML 데이터에서 내가 원하는 데이터만 쏙쏙 뽑아오는 코드인데, 여기서 더 나아가서 Open API 원하는 데이터를 뽑고, 관련 데이터를 DB에서 가져와서 DB의 데이터와 API 데이터를 함께 출력하도록 구현하는 것도 가능합니다.


 

자 이번에는 한 단계 더 나아가봅시다. 

 

버스 기사의 홈화면에 필요한 데이터를 보내줄 건데, 버스 기사의 홈화면에는 버스 노선 즉, 해당 버스가 경유하는 모든 버스 정류장을 스크롤해서 모두 볼 수 있고, 거기에 승하차 예정 승객 명수와 휠체어 승객 승차하 여부 등의 데이터가 함께 표시되어야 합니다. 거기에다가 버스의 현재 위치 데이터까지 보내줘야 하죠.

 

그럼 벌써 필요한 공공데이터 API만 해도 두 개이고, 추가로 DB에 현재 저장된 유효한 예약 내역도 조회해야 합니다. 

무척 복잡해 보이지만 이것도 한번 시도해 봅시다!

 

 

BusRouteController.java

@RestController
@RequiredArgsConstructor
@RequestMapping("/drivers")
public class BusRouteController {

    private final BusRouteService busRouteService;
    private final BusMatchRepository busMatchRepository;
    private final ReservationService reservationService;

    @Value("${serviceKey}")
    String serviceKey;

    // 버스 노선 ID 사용해서 정류장 정보 가져오기
    String getRouteInfoURL = "http://ws.bus.go.kr/api/rest/busRouteInfo/getStaionByRoute?";

    // 버스 실시간 위치 정보 가져오기
    String getBusPosURL= "http://ws.bus.go.kr/api/rest/buspos/getBusPosByVehId?";

    @GetMapping("/home/{driverId}")
    public ResponseEntity<ResponseData<DriverHomeResponseDto>> getBusRoutesByDriverId(
            @PathVariable("driverId") Long driverId
            ) throws Exception {

        System.out.println("현재 로그인한 버스 기사: "+ driverId);

        //현재 로그인한 버스기사가 운전하는 버스의 vehID 가져오기
        Optional<BusMatch> busMatchOptional = busMatchRepository.findBusMatchByDriverId(driverId);

        if (busMatchOptional.isPresent()) {
            BusMatch busMatch = busMatchOptional.get();

            String busRouteId = busMatch.getBus().getRouteId();
            String busRouteName = busMatch.getBus().getRouteName();
            Long vehId = busMatch.getBus().getVehId();

            // busRouteID를 사용해서 해당 버스의 노선 리턴       
            String url = getRouteInfoURL + "ServiceKey=" + serviceKey + "&busRouteId=" + busRouteId;
            System.out.println(url);

            URI uri = new URI(url);
            RestTemplate restTemplate = new RestTemplate();
            String xmlData = restTemplate.getForObject(uri, String.class);

            // 버스 노선 정류소 정보 가져오기
            List<DriverHomeBusStopDto> busStopList = busRouteService.getDriverRouteInfo(xmlData, vehId);

            int total_reserved = 0;     // 총 예약 건수: Total boarding passengers count
            int total_drop = 0;         // 총 하차 건수: Total drop off passengers count

            for(DriverHomeBusStopDto busStopDto : busStopList){ // 총 예약 건수 구하기: Total reservation count
                total_reserved += busStopDto.getReserved_cnt();
                total_drop += busStopDto.getDrop_cnt();
            }

            BusPosDto busPos = GetBusPosDto(vehId);

            DriverHomeResponseDto driverHomeResponseDto =
                    new DriverHomeResponseDto(vehId, busRouteName, total_reserved, total_drop, busPos, busStopList);

            return ResponseData.toResponseEntity(ResponseCode.DRIVER_HOME_SUCCESS, driverHomeResponseDto);

        } else {
            return ResponseData.toResponseEntity(ResponseCode.DRIVER_HOME_FAILED,null);
        }

    }
    
    
    public BusPosDto GetBusPosDto(Long vehId) throws Exception {
        String url = getBusPosURL + "ServiceKey=" + serviceKey + "&vehId=" + vehId.toString();
        System.out.println(url);

        URI uri = new URI(url);
        RestTemplate restTemplate = new RestTemplate();
        String xmlData = restTemplate.getForObject(uri, String.class);

        BusPosDto busPosDto = busRouteService.getBusPosParseXml(xmlData);

        return busPosDto;
    }
}

 

마찬가지로 @value 애노테이션을 사용해서 application.yml에 저장해 둔 서비스키를 가져오고 있습니다. 사용하고 있는 두 가지 OpenAPI url을 각각 적절한 이름의 스트링 타입 변수에 저장해 두고, 올바른 순서와 값으로 데이터 요청을 보낼 urlStr을 생성해서 요청을 보냅니다. 

 

 

버스 노선 데이터 요청 보낸 URL

http://ws.bus.go.kr/api/rest/busRouteInfo/getStaionByRoute?ServiceKey=${서비스 키값}&busRouteId=100100019

 

버스 노선 데이터 응답

<ServiceResult>
<comMsgHeader/>
<msgHeader>
<headerCd>0</headerCd>
<headerMsg>정상적으로 처리되었습니다.</headerMsg>
<itemCount>0</itemCount>
</msgHeader>
<msgBody>
<itemList>
<arsId>10340</arsId>
<beginTm>14:53</beginTm>
<busRouteAbrv>140</busRouteAbrv>
<busRouteId>100100019</busRouteId>
<busRouteNm>140</busRouteNm>
<direction>AT센터</direction>
<gpsX>127.0443522431</gpsX>
<gpsY>37.6899483575</gpsY>
<lastTm>22:49</lastTm>
<posX>203911.40811268348</posX>
<posY>465590.88289417233</posY>
<routeType>3</routeType>
<sectSpd>0</sectSpd>
<section>0</section>
<seq>1</seq>
<station>109000406</station>
<stationNm>도봉산역광역환승센터</stationNm>
<stationNo>10340</stationNo>
<transYn>N</transYn>
<fullSectDist>0</fullSectDist>
<trnstnid>121001340</trnstnid>
</itemList>
<itemList>
<arsId>10001</arsId>
<beginTm>03:58</beginTm>
<busRouteAbrv>140</busRouteAbrv>
<busRouteId>100100019</busRouteId>
<busRouteNm>140</busRouteNm>
<direction>AT센터</direction>
<gpsX>127.0455053816</gpsX>
<gpsY>37.6890359607</gpsY>
<lastTm>22:48</lastTm>
<posX>204013.15214933947</posX>
<posY>465489.6750102709</posY>
<routeType>3</routeType>
<sectSpd>16</sectSpd>
<section>109602290</section>
<seq>2</seq>
<station>109000001</station>
<stationNm>도봉산역</stationNm>
<stationNo>10001</stationNo>
<transYn>N</transYn>
<fullSectDist>248</fullSectDist>
<trnstnid>121001340</trnstnid>
</itemList>
<itemList>
<arsId>10003</arsId>
<beginTm>04:00</beginTm>
<busRouteAbrv>140</busRouteAbrv>
<busRouteId>100100019</busRouteId>
<busRouteNm>140</busRouteNm>
<direction>AT센터</direction>
<gpsX>127.04598371</gpsX>
<gpsY>37.6839230725</gpsY>
<lastTm>22:49</lastTm>
<posX>204055.61468182542</posX>
<posY>464922.27404683083</posY>
<routeType>3</routeType>
<sectSpd>43</sectSpd>
<section>109600312</section>
<seq>3</seq>
<station>109000003</station>
<stationNm>도봉한신아파트</stationNm>
<stationNo>10003</stationNo>
<transYn>N</transYn>
<fullSectDist>574</fullSectDist>
<trnstnid>121001340</trnstnid>
</itemList>
<itemList>
<arsId>10005</arsId>
<beginTm>04:01</beginTm>
<busRouteAbrv>140</busRouteAbrv>
<busRouteId>100100019</busRouteId>
<busRouteNm>140</busRouteNm>
<direction>AT센터</direction>
<gpsX>127.0448851161</gpsX>
<gpsY>37.6776824784</gpsY>
<lastTm>22:51</lastTm>
<posX>203959.05389440668</posX>
<posY>464229.6548868981</posY>
<routeType>3</routeType>
<sectSpd>18</sectSpd>
<section>109602294</section>
<seq>4</seq>
<station>109000005</station>
<stationNm>서울북부지방법원.검찰청.도봉역성황당</stationNm>
<stationNo>10005</stationNo>
<transYn>N</transYn>
<fullSectDist>701</fullSectDist>
<trnstnid>121001340</trnstnid>
</itemList>
<itemList>
<arsId>10007</arsId>
<beginTm>04:04</beginTm>
<busRouteAbrv>140</busRouteAbrv>
<busRouteId>100100019</busRouteId>
<busRouteNm>140</busRouteNm>
<direction>AT센터</direction>
<gpsX>127.0441629656</gpsX>
<gpsY>37.6733126379</gpsY>
<lastTm>22:54</lastTm>
<posX>203895.5856814154</posX>
<posY>463744.6664328077</posY>
<routeType>3</routeType>
<sectSpd>63</sectSpd>
<section>109602296</section>
<seq>5</seq>
<station>109000007</station>
<stationNm>신도봉사거리</stationNm>
<stationNo>10007</stationNo>
<transYn>N</transYn>
<fullSectDist>490</fullSectDist>
<trnstnid>121001340</trnstnid>
</itemList>
<itemList>
<arsId>10019</arsId>
<beginTm>04:05</beginTm>
<busRouteAbrv>140</busRouteAbrv>
<busRouteId>100100019</busRouteId>
<busRouteNm>140</busRouteNm>
<direction>AT센터</direction>
<gpsX>127.0436250299</gpsX>
<gpsY>37.6699703757</gpsY>
<lastTm>22:55</lastTm>
<posX>203848.3073295818</posX>
<posY>463373.7253236752</posY>
<routeType>3</routeType>
<sectSpd>46</sectSpd>
<section>109600317</section>
<seq>6</seq>
<station>109000019</station>
<stationNm>신도봉시장.도봉구청.방학역북부</stationNm>
<stationNo>10019</stationNo>
<transYn>N</transYn>
<fullSectDist>374</fullSectDist>
<trnstnid>121001340</trnstnid>
</itemList>

... <이하 생략> ...

 

 

버스 위치 데이터 요청 보낸  url

http://ws.bus.go.kr/api/rest/buspos/getBusPosByVehId?ServiceKey=${서비스 키값}&vehId=123060440

 

버스 위치 데이터 응답값

<ServiceResult>
<comMsgHeader/>
<msgHeader>
<headerCd>0</headerCd>
<headerMsg>정상적으로 처리되었습니다.</headerMsg>
<itemCount>0</itemCount>
</msgHeader>
<msgBody>
<itemList>
<busType>1</busType>
<congetion>3</congetion>
<dataTm>20240228145004</dataTm>
<isFullFlag>0</isFullFlag>
<lastStnId>121000016</lastStnId>
<plainNo>서울74사3097</plainNo>
<posX>201820.57001721935</posX>
<posY>445964.2663145787</posY>
<stId>121000016</stId>
<stOrd>54</stOrd>
<stopFlag>1</stopFlag>
<tmX>127.020595</tmX>
<tmY>37.513102</tmY>
<vehId>123060440</vehId>
</itemList>
</msgBody>
</ServiceResult>

 

이렇게 두 가지 API에 요청을 보내서 XML 데이터를 응답받았습니다. 이걸 서비스 단에 가지고 가서 원하는 틀에 담는 과정이 필요하겠죠?

필요한 틀은 다음과 같습니다.

 

첫 번째 틀: DrvierHomeResponseDto.java

@Getter
@Setter
@Builder
@AllArgsConstructor
@ToString
public class DriverHomeResponseDto {

    private Long vehId;           // 버스 고유 ID
    private String busRouteName;    // 버스 노선 번호

    private int totalReserved;      // 총 승차 예약 인원수
    private int totalDrop;          // 총 하차 예정 인원수

    private BusPosDto busPosDto;

    private List<DriverHomeBusStopDto> BusStopList; // 각 정거장 정보
    
}

 

여기서 각 정거장 정보를 담는 DTO는 따로 생성해 줍니다. 

 

DriverHomeBusStopDto.java

@Getter
@Setter
@Builder
@AllArgsConstructor
public class DriverHomeBusStopDto {

    private String arsId;       // 버스 정류장 고유 번호
    private String name;       // 버스 정류장 이름
    private double gpsX;       // 정류소 위치 x좌표 : 경도
    private double gpsY;       // 정류소 위치  y좌표 : 위도
    private int seq;           // 정류장 순서

    private int reserved_cnt;  // 승차 예약 인원수
    private int drop_cnt;      // 하차 예정 인원수

    private boolean wheelchair; // 휠체어 사용객 탑승 여부
    private boolean blind;      // 시각 장애인 승객 탑승 여부
    
}

 

자 그럼 틀에 데이터를 담으러 가봅시다!

 

BusRouteService.java

@Service
@RequiredArgsConstructor
public class BusRouteService {

    @Autowired
    ReservationService reservationService;

    @Autowired
    BusStopService busStopService;

    public List<DriverHomeBusStopDto> getDriverRouteInfo(String xml, Long vehId) throws Exception {
        List<DriverHomeBusStopDto> driverHomeBusStopDtoList = new ArrayList<>();

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document document = builder.parse(new InputSource(new StringReader(xml)));

        NodeList nodeList = document.getElementsByTagName("itemList");
        System.out.println("itemlist개수: "+ nodeList.getLength());


        for (int i = 0; i < nodeList.getLength(); i++) {
            Node itemListNode = nodeList.item(i);

            if (itemListNode.getNodeType() == Node.ELEMENT_NODE) {
                Element itemListElement = (Element) itemListNode;

                String arsId =getElementValue(itemListElement, "arsId");
                if(arsId.equals(" ")) continue;
                String name = getElementValue(itemListElement, "stationNm");
                double gpsX = Double.parseDouble(getElementValue(itemListElement, "gpsX"));
                double gpsY = Double.parseDouble(getElementValue(itemListElement, "gpsY"));
                int seq = Integer.parseInt(getElementValue(itemListElement, "seq"));


                // 휠체어 탑승객, 시각 장애인 탑승 여부
                boolean wheelchair = false;
                boolean blind = false;

                // 예약 내역 DB에서 정류장 승차 예약 내역 가져오기
                List<Reservation> boardingReservations = reservationService.findDetailOfBoardingStop(arsId);
                int reserved_cnt = boardingReservations.size(); // 예약 건수
                System.out.println("승차 예정 건수: "+reserved_cnt);


                for(Reservation reservation : boardingReservations){
                    if(reservation.getUser().isWheelchair()) {
                        wheelchair = true;
                        break;
                    }
                    if(reservation.getUser().isBlindness()){
                        blind = true;
                        break;
                    }
                }

                // 예약 내역 DB에서 정류장 하차 예약 내역 가져오기
                List<Reservation> dropReservations = reservationService.findDetailOfDropStop(arsId);
                int drop_cnt = dropReservations.size(); // 하차 예정 건수
                System.out.println("하차 예정 건수: "+drop_cnt);

                for(Reservation reservation : dropReservations){
                    if(reservation.getUser().isWheelchair()) {
                        wheelchair = true;
                        break;
                    }
                }

                DriverHomeBusStopDto driverHomeBusStopDto =
                        new DriverHomeBusStopDto(arsId, name, gpsX, gpsY, seq, reserved_cnt, drop_cnt, wheelchair, blind);
                driverHomeBusStopDtoList.add(driverHomeBusStopDto);
                System.out.println(driverHomeBusStopDto.toString());

            }
        }
        return driverHomeBusStopDtoList;
    }

    public BusPosDto getBusPosParseXml(String xmlData) throws Exception {

        //Open API 에서 추출한 xml 데이터에서 원하는 정보 추출하기
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document document = builder.parse(new InputSource(new StringReader(xmlData)));

        NodeList itemListNodes = document.getElementsByTagName("itemList");
        System.out.println("itemlist개수: "+itemListNodes.getLength());


        Node itemListNode = itemListNodes.item(0);

        if (itemListNode.getNodeType() == Node.ELEMENT_NODE) {
            Element itemListElement = (Element) itemListNode;

            BusPosDto busPosDto;

            long stId = Long.parseLong(getElementValue(itemListElement, "stId"));
            int stSeq = Integer.parseInt(getElementValue(itemListElement, "stOrd"));
            Boolean stopFlag = Boolean.parseBoolean(getElementValue(itemListElement, "stopFlag"));

            BusStop busStop = busStopService.findByNodeId(stId);

            busPosDto = new BusPosDto(busStop.getArsId(),stSeq,stopFlag);
            return busPosDto;
        }
        else return null;
    }
    
    
    private String getElementValue(Element element, String tagName) {
        NodeList nodeList = element.getElementsByTagName(tagName).item(0).getChildNodes();
        Node node = nodeList.item(0);
        return node.getNodeValue();
    }

}

 

이번에는 단순히  XML 데이터에서 원하는 값을 골라오는 것에서 그치지 않고, Reservation 테이블에서 현재 유효한 예약 내역이 있는지 확인하고, 유효한 데이터를 가져와서 개수를 세는 과정이 필요합니다. 뿐만 아니라 해당 승객이 휠체어 탑승객인지, 시각 장애인인지도 확인해서 해당 데이터를 담아서 보내줍니다. 이러한 과정을 거친 데이터는 아래와 같습니다.

 

{
    "timestamp": "2024-02-28T05:26:54.380425049",
    "code": "DRIVER_HOME_SUCCESS",
    "status": "OK",
    "detail": "버스기사 홈화면을 성공적으로 불러왔습니다",
    "data": {
        "vehId": 123060440,
        "busRouteName": "140",
        "totalReserved": 5,
        "totalDrop": 4,
        "busPosDto": {
            "arsId": "22293",
            "stSeq": 45,
            "stopFlag": false
        },
        "busStopList": [
            {
                "arsId": "10340",
                "name": "도봉산역광역환승센터",
                "gpsX": 127.0443522431,
                "gpsY": 37.6899483575,
                "seq": 1,
                "reserved_cnt": 0,
                "drop_cnt": 0,
                "wheelchair": false,
                "blind": false
            },
            {
                "arsId": "10001",
                "name": "도봉산역",
                "gpsX": 127.0455053816,
                "gpsY": 37.6890359607,
                "seq": 2,
                "reserved_cnt": 0,
                "drop_cnt": 0,
                "wheelchair": false,
                "blind": false
            },
            {
                "arsId": "10003",
                "name": "도봉한신아파트",
                "gpsX": 127.04598371,
                "gpsY": 37.6839230725,
                "seq": 3,
                "reserved_cnt": 0,
                "drop_cnt": 0,
                "wheelchair": false,
                "blind": false
            },
            {
                "arsId": "10005",
                "name": "서울북부지방법원.검찰청.도봉역성황당",
                "gpsX": 127.0448851161,
                "gpsY": 37.6776824784,
                "seq": 4,
                "reserved_cnt": 0,
                "drop_cnt": 0,
                "wheelchair": false,
                "blind": false
            },
            {
                "arsId": "10007",
                "name": "신도봉사거리",
                "gpsX": 127.0441629656,
                "gpsY": 37.6733126379,
                "seq": 5,
                "reserved_cnt": 0,
                "drop_cnt": 0,
                "wheelchair": false,
                "blind": false
            },
            {
                "arsId": "10019",
                "name": "신도봉시장.도봉구청.방학역북부",
                "gpsX": 127.0436250299,
                "gpsY": 37.6699703757,
                "seq": 6,
                "reserved_cnt": 0,
                "drop_cnt": 0,
                "wheelchair": false,
                "blind": false
            },
            {
                "arsId": "10009",
                "name": "도봉소방서.방학역남부",
                "gpsX": 127.0429312049,
                "gpsY": 37.6661739233,
                "seq": 7,
                "reserved_cnt": 0,
                "drop_cnt": 0,
                "wheelchair": false,
                "blind": false
            },
            {
                "arsId": "10011",
                "name": "도봉보건소",
                "gpsX": 127.0407623758,
                "gpsY": 37.6589364195,
                "seq": 8,
                "reserved_cnt": 0,
                "drop_cnt": 0,
                "wheelchair": false,
                "blind": false
            },
            {
                "arsId": "10013",
                "name": "도봉구민회관.도봉문화원",
                "gpsX": 127.0381736668,
                "gpsY": 37.6545598452,
                "seq": 9,
                "reserved_cnt": 0,
                "drop_cnt": 0,
                "wheelchair": false,
                "blind": false
            },
            {
                "arsId": "10015",
                "name": "쌍문역",
                "gpsX": 127.0347686574,
                "gpsY": 37.6488174173,
                "seq": 10,
                "reserved_cnt": 0,
                "drop_cnt": 0,
                "wheelchair": false,
                "blind": false
            },
            
            ..{이하 생략}..
            
    	]
    }
]

 

 

 

 

모든 예제의 전체 코드는 아래 깃허브에서 확인할 수 있습니다.

 

GitHub - GDSC-COMBUS/Combus-Backend: 🚌 Combus Backend- Google Solution Challenge 2024

🚌 Combus Backend- Google Solution Challenge 2024. Contribute to GDSC-COMBUS/Combus-Backend development by creating an account on GitHub.

github.com

 

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함