관리자 글쓰기

본 내용은 혼자서 공부하기 위한 용도로 사용된 것으로 비효율적일 수 있으며 실용적이지 않을 수 있습니다.




본 개발을 위해선 https://www.data.go.kr/ 페이지에서 국토교통부 제공의 버스정류소정보조회서비스, 도착정보조회서비스, 버스위치정보조회서비스, 버스노선정보조회서비스 에 대한 운영 권한이 필요하다. 이 부분에 대해서는 해당 정보들을 운영할 자신의 페이지가 있어야 권한을 받아 낼 수 있다. 해당 부분은 각자 권한을 받아보아라.


해당 권한을 받았다고 생각하고 포스팅을 진행한다.


먼저 노선, 정류장들의 정보를 데이터베이스에 저장하기 위해 테이블부터 구성한다.


NODE_ROUTE
(
NODEID VARCHAR(30) NOT NULL,
NODENAME VARCHAR(30) NOT NULL,
ROUTEID VARCHAR(30) NOT NULL,
ROUTENO VARCHAR(30) NOT NULL,
PRIMARY KEY(NODEID,ROUTEID)
)
NODE_INFO
(
NODEID VARCHAR(30) PRIMARY KEY,
NODENAME VARCHAR(30) NOT NULL,
NODENO VARCHAR(10) NOT NULL,
GPSLATI DOUBLE NOT NULL,
GPSLONG DOUBLE NOT NULL
)
ROUTE_INFO
(
ROUTEID VARCHAR(30) PRIMARY KEY,
ROUTENO VARCHAR(30) NOT NULL,
ROUTETP VARCHAR(10),
STARTNODENM VARCHAR(30) NOT NULL,
ENDNODENM VARCHAR(30) NOT NULL,
STARTVEHICLETIME VARCHAR(4),
ENDVEHICLETIME VARCHAR(4),
INTERVALTIME VARCHAR(8),
INTERVALSATTIME VARCHAR(8),
INTERVALSUNTIME VARCHAR(8)
)
ROUTE_ORDER
(
ROUTEID VARCHAR(30) NOT NULL,
NODEORD INT(11) NOT NULL,
NODENAME VARCHAR(30) NOT NULL,
NODEID VARCHAR(30) NOT NULL,
PRIMARY KEY(ROUTEID,NODEORD)
)

앞으로 살펴볼 xml 구성으로부터 받아올 데이터들을 기반으로한 테이블들을 만들었다.



이제부터 각 정보들을 삽입하기 위한 기본 페이지부터 구성한다.


다음 코드들을 작성한다.

main/controller/GCBusController.java

@Controller
public class GCBusController {

//데이터베이스 데이터 삽입을 위한 컨트롤러
@RequestMapping(value="/regForm.do")
public ModelAndView regForm() throws Exception{
ModelAndView mv = new ModelAndView("setting/regForm");

return mv;
}
}

 

WEB-INF/jsp/setting/regForm.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"    pageEncoding="UTF-8"%>




<%@ include file="/WEB-INF/include/include-header.jspf" %>


해당 페이지는 관리자만 해당 기능을 사용할 수 있습니다.

관리자의 비밀번호:

정류장 정보 등록 정류장별 노선 정보 등록 노선 정보 등록 노선 경로 정보 등록
<%@ include file="/WEB-INF/include/include-body.jspf" %>

 

 

- 따로 특별한 부분은 없고 위에서 만든 버튼마다 각각 클릭 이벤트를 만들어줬다. MIN, MAX 값의 경우 현재 노선 코드 값의 마지막 4자리의 최소부터 최대값을 넣어둔 것이다. 굳이 변경할 필요는 없지만 변경할 경우를 위해 변경할 수 있게 만들어 놨다. 패스워드의 경우 인터셉터를 이용하여 설정해두었다.


common/interceptor/AdminInterceptor.java

public class AdminInterceptor extends HandlerInterceptorAdapter {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if(request.getParameter("adminPw").equals("1234")) {

return true;
}
response.sendRedirect(request.getContextPath()+"/regForm.do");
return false;
}
}

 

 

WEB-INF/config/action-servlet.xml


        
        
        

 

 

- 2장에서 주변 구성에서 코드들을 받았다면 이미 해당 부분은 있을 것이다. 그래도 다시 언급하여 이런 기능을 위해 추가 해두었다는 것을 알려준다. /reg/로 시작하는 페이지로 들어갈 시 항상 위의 인터셉터 클래스를 거쳐 비밀번호를 확인한 뒤 맞을 시 정보를 등록할 수 있다.




여기까지 따라왔다면 해당 페이지를 볼 수 있다. 이제 4가지 기능들의 컨트롤러, 서비스, DAO,XmlParser 를 작성해야하는데 거의 비슷비슷하며 먼저 한가지를 예시로 설명을 하고 나머지의 코드를 보자.


http://openapi.tago.go.kr/openapi/service/BusSttnInfoInqireService/getSttnNoList?ServiceKey=각자의키입력&cityCode=37030&numOfRows=1400


위의 주소로 들어가면 밑과 같이 XML이 어떻게 구성되어 있는지 볼 수 있다.

위와 같이 구성되어 있는 XML을 파싱하는 클래스를 작성하여야한다.


common/utils/setting/NodeInfo.java(정류장 정보)

@Component("nodeInfo")
public class NodeInfo {

 public final static String URL = "http://openapi.tago.go.kr/openapi/service/BusSttnInfoInqireService/getSttnNoList";
 public final static String KEY = "키 값";
 
    public List<Map<String,Object>> insertInfo(Map<String,Object> map) throws Exception {
     List<Map<String,Object>> list = new ArrayList<Map<String,Object>>();
 
        URL url = new URL(URL+"?ServiceKey="+KEY+"&cityCode=37030&numOfRows=1400");
     
        XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
        factory.setNamespaceAware(true);
        XmlPullParser xpp = factory.newPullParser();
        BufferedInputStream bis = new BufferedInputStream(url.openStream());
        xpp.setInput(bis, "utf-8");
       
        String tag = null;
        int event_type = xpp.getEventType();
       
        Map<String,Object> tempMap = null;
        while (event_type != XmlPullParser.END_DOCUMENT) {
            if (event_type == XmlPullParser.START_TAG) {
                tag = xpp.getName();
                if(tag.equals("item")) {
                 tempMap=new HashMap<String,Object>();
                }
            } else if (event_type == XmlPullParser.TEXT) {
                if(tag.equals("nodeid")){
                    tempMap.put("NODEID", xpp.getText());
                }
                else if(tag.equals("nodenm")) {
                 tempMap.put("NODENAME", xpp.getText());
                }
                else if(tag.equals("nodeno")) {
                 tempMap.put("NODENO", xpp.getText());
                }
                else if(tag.equals("gpslati")){
                 tempMap.put("LAT", Double.parseDouble(xpp.getText()));
                }
                else if(tag.equals("gpslong")) {
                 tempMap.put("LNG", Double.parseDouble(xpp.getText()));
                }
            } else if (event_type == XmlPullParser.END_TAG) {
                tag = xpp.getName();
                if (tag.equals("item")) {
                    list.add(tempMap);
                }
            }
            event_type = xpp.next();
        }
        bis.close();
        return list;
    }
}

- 해당 클래스는 도시 코드를 통해 해당 지역의 버스 정류장의 정보들을 보내주는 url에서 나타나는 xml 구성의 정보들을 태그별로 나누어 정보값을 받아오는 클래스이다.
- 4~10라인
버스 정류장의 정보를 가져올 수 있는 API URL로 cityCode와 numOfRows를 통해 지역 선택과 가져올 정류장 갯수를 설정해주었고, 해당 정보들을 저장할 List를 선언하였다.

-12~16라인

XML 구성의 페이지를 파싱하기 위해 필요한 변수를 선언하는 과정이다.

-18~51라인

해당 부분이 파싱하는 과정으로 event_type 변수를 통해 위 그림의 XML파일의 시작과 종료, 태그의 시작과 종료, 태그의 Text값을 구분하고 태그의 텍스트값을 가져와 비교하고 Map에 저장한 뒤 위에서 선언한 List에 저장하고 해당 XML을 모두 파싱한 뒤 정류장 정보값들이 들어있는 List를 리턴한다.(정류소ID, 이름, 번호, 경도, 위도 값)


NodeToRoute.java(정류장을 거쳐가는 노선)

@Component("nodeToRoute")
public class NodeToRoute{

public final static String ROUTE1_URL = "http://openapi.tago.go.kr/openapi/service/BusRouteInfoInqireService/getRouteNoList?serviceKey=키 값&cityCode=37030&numOfRows=500";
public final static String ROUTE2_URL = "http://openapi.tago.go.kr/openapi/service/BusRouteInfoInqireService/getRouteAcctoThrghSttnList?serviceKey=키 값&cityCode=37030&numOfRows=80&routeId=";

    public List> insertInfo() throws Exception {
    
    URL url = new URL(ROUTE1_URL);
     
        XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
        factory.setNamespaceAware(true);
        XmlPullParser xpp = factory.newPullParser();
        BufferedInputStream bis = new BufferedInputStream(url.openStream());
        xpp.setInput(bis, "utf-8");
        
        String tag = null;
        int event_type = xpp.getEventType();
        List> routeList = new ArrayList>();
        Map routeMap = null;
        while (event_type != XmlPullParser.END_DOCUMENT) {
            if (event_type == XmlPullParser.START_TAG) {
                tag = xpp.getName();
                if(tag.equals("item")) {
                routeMap = new HashMap();
                }
            } else if (event_type == XmlPullParser.TEXT) {
                if(tag.equals("routeid")){
                routeMap.put("routeid",xpp.getText());
                }else if(tag.equals("routeno")) {
                routeMap.put("routeno",xpp.getText());
                }
            } else if (event_type == XmlPullParser.END_TAG) {
                tag = xpp.getName();
                if(tag.equals("item")) {
                routeList.add(routeMap);
                }
            }
            event_type = xpp.next();
        }
        bis.close();
        
        Iterator> iterator = routeList.iterator();
        List> info = new ArrayList>();
        Map nodeMap = null;
        Map temp = null;
        while(iterator.hasNext()) {
        temp = iterator.next();
        url = new URL(getURLParam((String)temp.get("routeid")));
        bis = new BufferedInputStream(url.openStream());
            xpp.setInput(bis, "utf-8");
            tag = null;
            event_type = xpp.getEventType();
            
        while (event_type != XmlPullParser.END_DOCUMENT) {
        
                if (event_type == XmlPullParser.START_TAG) {
                    tag = xpp.getName();
                    if(tag.equals("item")) {
                    nodeMap= new HashMap();
                    nodeMap.put("ROUTENO", temp.get("routeno"));
                    }
                } else if (event_type == XmlPullParser.TEXT) {
                    if(tag.equals("nodeid")){
                    nodeMap.put("NODEID", xpp.getText());
                    }
                    else if(tag.equals("nodenm")){
                    nodeMap.put("NODENAME", xpp.getText());
                    }
                    else if(tag.equals("routeid")){
                    nodeMap.put("ROUTEID", xpp.getText());
                    }
                } else if (event_type == XmlPullParser.END_TAG) {
                    tag = xpp.getName();
                    if (tag.equals("item")) {
                        info.add(nodeMap);
                    }
                }
                event_type = xpp.next();
            }
        }
        bis.close();
        return info;
    }
    
private String getURLParam(String object) {
String url = ROUTE2_URL+object;
return url;
}

}

 

- 해당 부분은 정류장에 거쳐가는 노선값을 파싱하기 위한 클래스이다. 2번 파싱하는 이유는 해당 지역의 노선을 모두 검색 한뒤 정류장에 거쳐가는 노선들을 다시 검색하였기 때문이다.(정류장에 거쳐가는 노선은 도시코드로 검색이 불가능하여서) 위 정보를 통하여 정류



RouteInfo.java(노선 정보)

@Component("routeInfo")
public class RouteInfo {
 public final static String URL = "http://openapi.tago.go.kr/openapi/service/BusRouteInfoInqireService/getRouteInfoIem";
 public final static String KEY = "키 값";
 
    public List<Map<String,Object>> insertInfo(Map<String,Object> map) throws Exception {
     List<Map<String,Object>> list = new ArrayList<Map<String,Object>>();
     
     for(int i = Integer.parseInt((String) map.get("MIN"));i<= Integer.parseInt((String)map.get("MAX"));i++) {
         URL url = new URL(URL+"?ServiceKey="+KEY+"&cityCode=37030&routeId=GCB4720"+i);
      
         XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
         factory.setNamespaceAware(true);
         XmlPullParser xpp = factory.newPullParser();
         BufferedInputStream bis = new BufferedInputStream(url.openStream());
         xpp.setInput(bis, "utf-8");
        
         String tag = null;
         int event_type = xpp.getEventType();
        
         Map<String,Object> tempMap = null;
         while (event_type != XmlPullParser.END_DOCUMENT) {
             if (event_type == XmlPullParser.START_TAG) {
                 tag = xpp.getName();
                 if(tag.equals("item")) {
                  tempMap=new HashMap<String,Object>();
                 }
             } else if (event_type == XmlPullParser.TEXT) {
                 if(tag.equals("routeid")){
                     tempMap.put("ROUTEID", xpp.getText());
                 }
                 else if(tag.equals("routeno")) {
                  tempMap.put("ROUTENO", xpp.getText());
                 }
                 else if(tag.equals("startnodenm")) {
                  tempMap.put("STARTNODENM", xpp.getText());
                 }
                 else if(tag.equals("endnodenm")){
                  tempMap.put("ENDNODENM", xpp.getText());
                 }
                 else if(tag.equals("startvehicletime")) {
                  tempMap.put("STARTVEHICLETIME", xpp.getText());
                 }
                 else if(tag.equals("endvehicletime")) {
                  tempMap.put("ENDVEHICLETIME", xpp.getText());
                 }
             } else if (event_type == XmlPullParser.END_TAG) {
                 tag = xpp.getName();
                 if (tag.equals("item")) {
                     list.add(tempMap);
                 }
             }
             event_type = xpp.next();
         }
         bis.close();
     }
        return list;
    }
}

- 노선들의 정보를 파싱하는 클래스이다.


routeToOrder.java(노선들의 정류장 순서)

@Component("routeToOrder")
public class RouteToOrder {
 public final static String URL = "http://openapi.tago.go.kr/openapi/service/BusRouteInfoInqireService/getRouteAcctoThrghSttnList";
 public final static String KEY = "키 값";
 
    public List<Map<String,Object>> insertInfo(Map<String,Object> map) throws Exception {
     List<Map<String,Object>> list = new ArrayList<Map<String,Object>>();
     
     for(int i = Integer.parseInt((String) map.get("MIN"));i<= Integer.parseInt((String)map.get("MAX"));i++) {
         URL url = new URL(URL+"?ServiceKey="+KEY+"&cityCode=37030&numOfRows=100&routeId=GCB4720"+i);
      
         XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
         factory.setNamespaceAware(true);
         XmlPullParser xpp = factory.newPullParser();
         BufferedInputStream bis = new BufferedInputStream(url.openStream());
         xpp.setInput(bis, "utf-8");
        
         String tag = null;
         int event_type = xpp.getEventType();
        
         Map<String,Object> tempMap = null;
         while (event_type != XmlPullParser.END_DOCUMENT) {
             if (event_type == XmlPullParser.START_TAG) {
                 tag = xpp.getName();
                 if(tag.equals("item")) {
                  tempMap=new HashMap<String,Object>();
                 }
             } else if (event_type == XmlPullParser.TEXT) {
                 if(tag.equals("routeid")){
                     tempMap.put("ROUTEID", xpp.getText());
                 }else if(tag.equals("nodeord")) {
                  tempMap.put("NODEORD", xpp.getText());
                 }
                 else if(tag.equals("nodeid")){
                  tempMap.put("NODEID", xpp.getText());
                 }
                 else if(tag.equals("nodenm")) {
                  tempMap.put("NODENAME", xpp.getText());
                 }
                
             } else if (event_type == XmlPullParser.END_TAG) {
                 tag = xpp.getName();
                 if (tag.equals("item")) {
                     list.add(tempMap);
                 }
             }
             event_type = xpp.next();
         }
         bis.close();
     }
        return list;
    }
 
}

- 버스 노선들의 거치는 정류장들을 순서대로 나타내는 정보들을 파싱하는 클래스이다.



이후 Controller - > Service - > DAO 순으로 작성하겠다. 이 부분의 경우도 4개의 부분이 비슷하거나 동일하다.


GCBusController.java

@Resource(name="busStopParser")
 private BusStopParser busStopParser;
 
 
 //데이터베이스 데이터 삽입을 위한 컨트롤러
 @RequestMapping(value="/regForm.do")
 public ModelAndView regForm() throws Exception{
  ModelAndView mv = new ModelAndView("setting/regForm");
  
  return mv;
 }
 
 @RequestMapping(value="/reg/regNodeInfo.do")
 public ModelAndView regNodeInfo(CommandMap commandMap) throws Exception{
  
  ModelAndView mv = new ModelAndView("setting/regSuc");
  mv.addObject("size", gcBusServ.regNodeInfo(commandMap.getMap()));
  
  return mv;
 }
 
 @RequestMapping(value="/reg/regNodeToRoute.do")
 public ModelAndView regNodeToRoute() throws Exception{
  ModelAndView mv = new ModelAndView("setting/regSuc");
  mv.addObject("size", gcBusServ.regNodeToRoute());
  
  return mv;
 }
 
 @RequestMapping(value="/reg/regRouteToOrder.do")
 public ModelAndView regRouteToOrder(CommandMap commandMap) throws Exception{
  
  ModelAndView mv = new ModelAndView("setting/regSuc");
  mv.addObject("size", gcBusServ.regRouteToOrder(commandMap.getMap()));
  
  return mv;
 }
 
 @RequestMapping(value="/reg/regRouteInfo.do")
 public ModelAndView regRouteInfo(CommandMap commandMap) throws Exception{
  
  ModelAndView mv = new ModelAndView("setting/regSuc");
  mv.addObject("size", gcBusServ.regRouteInfo(commandMap.getMap()));
  
  return mv;
 }

- 특별한 것은 없는 일반적인 Conroller 부분으로 4개의 메소드가 Service 부분을 호출하는 부분이 같고 size의 경우 몇 개의 정보가 파싱 되었는 지를 확인하기 위함이다. regSuc.jsp 페이지는 나중에 만들 것이다.


main/Service/GCBusService.interface

public interface GCBusService {
 Integer regNodeToRoute() throws Exception;
 Integer regRouteToOrder(Map<String,Object> map) throws Exception;
 Integer regRouteInfo(Map<String,Object> map) throws Exception;
 Integer regNodeInfo(Map<String, Object> map) throws Exception;
}

main/Service/GCBusServiceImpl.java
@Service("gcBusServ")
public class GCBusServiceImpl implements GCBusService {

@Resource(name="gcBusDAO")
private GCBusDAO gcBusDAO;

@Resource(name="nodeToRoute")
private NodeToRoute nodeToRoute;

@Resource(name="routeToOrder")
private RouteToOrder routeToOrder;

@Resource(name="routeInfo")
private RouteInfo routeInfo;

@Resource(name="nodeInfo")
private NodeInfo nodeInfo;

@Override
public Integer regNodeToRoute() throws Exception {
List> list = nodeToRoute.insertInfo();
Iterator> iterator = list.iterator();
Map map = null;
    while(iterator.hasNext()) {
        map=iterator.next();
        gcBusDAO.insertNodeRouteInfo(map);
    }
    
    return Integer.valueOf(list.size());
}

@Override
public Integer regRouteToOrder(Map map) throws Exception {
List> list = routeToOrder.insertInfo(map);
Iterator> iterator = list.iterator();
Map temp = null;
while(iterator.hasNext()) {
temp=iterator.next();
gcBusDAO.insertRouteOrder(temp);
}

return Integer.valueOf(list.size());
}

@Override
public Integer regRouteInfo(Map map) throws Exception {
List> list = routeInfo.insertInfo(map);
Iterator> iterator = list.iterator();
Map temp = null;
while(iterator.hasNext()) {
temp=iterator.next();
gcBusDAO.insertRouteInfo(temp);
}
return Integer.valueOf(list.size());
}
}

 

- 위에서 만든 XML 파서들을 각각 선언하였고 DAO도 선언하였다. 각각 메서드는 파서들로부터 파싱되어온 정보들을 List에 저장하고 반복자를 통해 gcBusDAO를 호출한다. 그리고 리스트의 크기를 리턴한다.


main/dao/GCBusDAO.java

@Repository("gcBusDAO")
public class GCBusDAO extends AbstractDAO {

public void insertNodeRouteInfo(Map map) throws Exception{
insert("common.insertNodeSetting",map);
}

public void insertRouteOrder(Map map) throws Exception{
insert("common.insertRouteOrderSetting",map);
}

public void insertRouteInfo(Map map) throws Exception{
insert("common.insertRouteInfoSetting",map);
}

public void insertNodeInfo(Map map) throws Exception{
insert("common.insertNodeInfoSetting",map);
}

}

- 4개의 메서드 모두 각자 DB에 정보 삽입을 시도한다.


resources/mapper/common_SQL.xml
<insert id="insertNodeSetting" parameterType="HashMap">
  <![CDATA[
   INSERT INTO NODE_ROUTE(
    NODEID,
    NODENAME,
    ROUTEID,
    ROUTENO
   )
    VALUES(
    #{NODEID},
    #{NODENAME},
    #{ROUTEID},
    #{ROUTENO}
   )
  ]]>
 </insert>
 <insert id="insertRouteOrderSetting" parameterType="HashMap">
  <![CDATA[
   INSERT INTO ROUTE_ORDER(
    ROUTEID,
    NODEORD,
    NODENAME,
    NODEID
   )
    VALUES(
    #{ROUTEID},
    #{NODEORD},
    #{NODENAME},
    #{NODEID}
   )
  ]]>
 </insert>
 <insert id="insertRouteInfoSetting" parameterType="HashMap">
  <![CDATA[
   INSERT INTO ROUTE_INFO(
    ROUTEID,
    ROUTENO,
    STARTNODENM,
    ENDNODENM,
    STARTVEHICLETIME,
    ENDVEHICLETIME
   )
    VALUES(
    #{ROUTEID},
    #{ROUTENO},
    #{STARTNODENM},
    #{ENDNODENM},
    #{STARTVEHICLETIME},
    #{ENDVEHICLETIME}
   )
  ]]>
 </insert>
 <insert id="insertNodeInfoSetting" parameterType="HashMap">
  <![CDATA[
   INSERT INTO NODE_INFO(
    NODEID,
    NODENO,
    NODENM,
    GPSLATI,
    GPSLONG
   )
    VALUES(
    #{NODEID},
    #{NODENO},
    #{NODENAME},
    #{LAT},
    #{LNG}
   )
  ]]>
 </insert>
 

- 2번 포스팅에서 첨부했던 파일 중 common_SQL.xml 에 해당 코드를 추가한다. 각각 해당 정보들을 가져와 삽입하는 부분이다. 


WEB-INF/jsp/setting/regSuc.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE>
<html>
<head>
<title>데이터 추가</title>
</head>
<body>
 ${size }개의 데이터 추가에 성공하였습니다.
</body>
</html>

- 해당 jsp 작성을 통해 제대로 데이터 삽입이 되었는 지 확인이 가능하다.


- 해당 과정을 모두 따라온 뒤 만약 정류장 정보 등록을 할 경우는 

해당 화면에서 등록을 누를 시


위와 같은 화면을 결과로 문제 없이 지금 과정을 잘 따라왔다고 알 수 있다.



해당 과정을 통해 공공 API로부터 정보를 받아와 파싱하고 분류하여 DB에 정보를 삽입하는 과정을 살펴보았다. 다음 포스팅에서 해당 정보들을 통해 다음 지도 API에 나타낼 것이다.