티스토리 뷰

이번 개발 이야기는 저에게 많은 의미가 있어 따로 이야기 칼럼으로 기록을 해보고자 합니다.

 


개발 INFO

1. 곰두리봇 개발 프로젝트 소개

https://kbs4674.tistory.com/60

 

1. 프로그래밍 언어

1) API 서버로 쓰이는 홈페이지 (kakao-bot-api.herokuapp.com) : Ruby / Ruby on Rails(5.1.6)

2) 카카오톡 자동응답을 돕는 Messenger Bot : javascript

 

2. Github

https://github.com/kbs4674/kakao-bot_public

 


개발 계기

 

 

먼 옛날부터 춘천시 교통에 있어 강원대 학생들은 버스노선의 신설에 대해 민감하게 반응하는 택시업계와의 갈등으로 불만이 많았지만, 아무것도 할 수 없어서 어쩔 수 없이 현실에 순응하고 살아왔습니다.

학교에서 남춘천역을 다니는 버스는 강원대 후문에서 멈추는 버스 1대에 불가했고, 강원대 후문에서 버스를 타고 남춘천역으로 가나, 정문에서 걸어서 남춘천역을 가나 거의 비슷할정도로 학생에게 있어 춘천시의 교통은 너무나도 최악이었습니다. (+ 버스 배차간격은 15분~20분인건 덤..)

 

그리고 18년도 10월에 택시업계에서 사건이 터진게 하나있었는데(카카오T 콜 거부), 저는 해당 사건이 너무 부당하다고 생각해서 각종 언론사에 제보를 했습니다.

 

택시회사의 부당함을 각 언론사에 제보

 

사실 춘천시 교통은 학생들 뿐만 아니라 주민들에게도 큰 불만이었던게, 버스노선이 너무 한 곳(춘천 명동 사거리)에 집중적으로 거쳐가는 경향이 컸고, 버스 노선은 많은데 비해 이를 커버할 버스 대수가 적어서 버스 배차간격이 컸던것도 문제였습니다. 그렇다보니 춘천 내에서는 택시가 꽤 많은 이득을 볼 수 밖에 없는 구조였습니다.

하지만 이미 택시업계가 많은 유리한 조건을 가진 상황임에도 불구하고 버스노선 신설 방해, 택시비 증가, 카카오T 거부, 뭣만하면 단체 파업 등 행위를 하니 학생/시민들에게 있어선 별로 고운 시선은 아니었습니다.

 

춘천시 교통에 대해 관심을 가지기 시작한게 이 때 부터 인 것 같습니다.

이 사건을 제보한 이후로, 한 기자님과 인연이 이어져서 교내, 춘천시 교통이슈가 터질 때 마다 기자님께 소식을 전해오면서 학교생활을 보내왔습니다.

 

시간이 지나고, 19년도 초에 춘천시에서도 비효율적에 버스 노선에 관심을 가지기 시작했습니다.

 

 

비록 많은 갈등이 있었지만, 19년도 6월에 드디어 버스 노선을 55년만에 전면 개편한다고 밝혔고, 개편안에 있어서 강원대를 경유하는 버스노선이 새로 신설될거라는 발표가 났습니다.

실제로 이 작업을 위해 52대 We:higher 총학생회가 엄청 발벗고 나섰습니다.

 

총학생회가 이번 버스노선 신설을 위해 시민 공청회 참여, 춘천시/택시업계 노선 신설 회의 참여, 서명운동 등 많은 노력을 하며 뛰어줬고, 저도 이 상황을 몇 몇 언론사에 알리고 그랬습니다.

 

강원대학교를 통과하는 시내버스 (사진출처 : News1)

그리고 2019년 11월 15일, 바로 어제였죠..

어제 드디어 춘천시 내에서는 개정된 노선으로 버스들이 도로를 다니기 시작했고, 공식적으로 저희 학교에도 버스가 다니기 시작했습니다.

 

이번 개발을 위해 1년을 기다려왔고, 어제 노선이 신설되었다는 소식을 듣고 바로 개발에 착수했습니다.

 


개발 과정

개발 과정은 간단(?) 했습니다.

 

 

1) 버스요청에 대한 정보가 클라이언트(사용자)로 부터 요청이 올 경우, 카카오BOT은 요청에 맞게 API 서버에 명령을 내려 정보요청을 합니다.

2) API서버는 공공데이터에 헤더와 함께 요청을 보내고, 반응값을 return 받습니다.

3) 반응값을 가진 API서버는 정보를 JSON 형식으로 재가공 후, 카카오봇에게 Return 합니다.

 

일단 여기서 제일 중요했던게 공공데이터 API 서버가 개선된 춘천 버스 노선에 맞게 데이터를 제공해주느냐였습니다.

하지만 다행히도 제 걱정은 기우였습니다.

카카오 지도에서는 이미 개선된 버스 노선에 맞게 버스정거장이 표시되어있는걸 보고, 혹시나 하고 공공데이터를 보니 개선된 노선에 맞게 데이터를 제공해주고 있었습니다.

 

 

1. 버스 데이터 수집 규칙

버스 데이터 정보를 알기 위해서는 공공데이터에서 제공되는 2개의 서비스를 이용해야 합니다 :

버스노선정보조회서비스, 도착정보조회서비스

 

  • 버스노선정보조회서비스 는 노선ID 및 정류장 ID를 알기 위해 필요합니다.

  • 도착정보조회서비스 는 실시간 버스 도착현황 데이터를 알기 위해 필요합니다.

 

버스 데이터 수집규칙은 다음과 같습니다.

 

1. [버스노선정보조회서비스 : 도시코드 목록 조회] 도시코드(cityCode) 확인

춘천시의 cityCode는 32010

 

2. [버스노선정보조회서비스 : 노선번호목록조회] 버스 노선 ID값(routeid) 확인

 

3. [버스노선정보조회서비스 : 노선별경유정류소목록조회] 해당 노선이 보유한 정류장 ID(nodeid)값 확인

 

4. [도착정보조회서비스 : 노선별경유정류소목록조회] 도시코드 및 정류장 ID 기반 정류장 도착시간 데이터 조회

 

이렇게 해서 공공데이터로 부터 실질적인 데이터 수집 기반이 마련되었습니다.

XML 규칙 설명

arriprevarrprevstationcnt : 남은 정거장

arrtime : 도착까지 남은 시간 (분 단위)

 

 

2. 나의 API 서버에서 공공데이터 수집 및 가공

처음에 카카오봇 작동 Process를 보고 이런 의문을 느끼신 분들이 있을겁니다.

' 카카오봇이 바로 다이렉트로 공공데이터 서버와 통신을 하면 되지 않나? '

저도 이렇게 로직이 구현되는게 큰 희망사항인데, 아쉽게도 카카오봇에서 제공하는 크롤링/API 통신 함수들이 전문적이지 않습니다. 그래가지고 중간에 제가 만든 홈페이지(내가만든 API 서버)를 경유하는게 있습니다.

 

내 입맛대로 재가공된 데이터

 

그래가지고 공공데이터로부터 수집된 데이터를 다시 중간 홈페이지(내가만든 API 서버) 에서 봇이 쉽게 데이터 파싱을 할 수 있도록 정보를 재가공을 해야할 필요가 있었습니다.

 

1. URL을 통해 데이터 파라미터를 받는다.

http://kakao-bot-api.herokuapp.com/transfers/chuncheon_bus.json/백록관

 

여기서 "백록관" 이라는 이름을 캐치해낸다.

URL 내에 있는 "백록관" 타이틀을 시간을 조회할 정류장으로서 역할이 될 것이다.

 

params[:station_name] = "백록관"

 

2. URL로 부터 받은 parameter 기준으로 사전에 초기화된 변수들이 정의된다.

## Ruby on Rails 5.1.6
## Transfers Controller

class TransfersController < ApplicationController

	def chuncheon_bus_json
		@stationName = params[:station_name]

		if @stationName == "정문"
			@busLineNumber = "300"
			@busDirect = "남춘천역 방향"
			@stationCode = "CCB250026907"
			@busOppositeDirect = "춘천역 방향"
			@stationOppositeCode = "CCB250026908"

		elsif @stationName == "백록관"
			@busLineNumber = "300"
			@busDirect = "남춘천역 방향"
			@stationCode = "CCB250026905"
			@busOppositeDirect = "춘천역 방향"
			@stationOppositeCode = "CCB250026906"

			... (이하 코드 생략) ...
		end
    end
end

 

3. 홈페이지(내가만든 API 서버)가 공공데이터와 통신

## 공공데이터로 부터 수집된 데이터들이 배열에 담길 것이다.
@busInfoArray = Array.new

### 남춘천역 방향
chunCheonBus = "http://openapi.tago.go.kr/openapi/service/ArvlInfoInqireService/getSttnAcctoArvlPrearngeInfoList?serviceKey=#{ENV['GO_DATA_API']}&cityCode=32010&nodeId=#{@stationCode}"
@chunCheonBusParams = RestClient::Request.execute :method => 'GET', :url => chunCheonBus
@chunCheonBusResult = @chunCheonBusParams.body
@chunCheonBusXml = Nokogiri::XML(@chunCheonBusResult)
@busItem = @chunCheonBusXml.xpath("//item")
      
if @busItem.empty?

	@busInfoArray.push([@busLineNumber, @busDirect, "정보 없음.", "-"])
	@busInfoArray.push([@busLineNumber, @busOppositeDirect, "정보 없음.", "-"])  

else
      
	for i in 0..@busItem.length-1
		if ((@busItem[i].xpath("//routeid")[i].text == "CCB250080000") == true) && (@busItem[i].xpath("//arrtime")[i].text != nil || @busItem[i].xpath("//arrtime")[i].text != "")
			@busLineNumber = "300"
			## 춘천역 방향 : 도착 예정시간
			@busArrival = "약 #{@busItem[i].xpath("//arrtime")[i].text.to_i/60}분"
			## 춘천역 방향 : 정거장 도착까지 남은 정거장
			@busPoint = @busItem[i].xpath("//arrprevstationcnt")[i].text
			break
		else
			@busArrival = "정보 없음."
			@busPoint = "-"
		end
	end

end
        
@busInfoArray.push([@busLineNumber, @busDirect, @busArrival, @busPoint])
        
### 춘천역 방향
# 춘천역 과정과 알고리즘 동일, 정류장 ID가 담긴 변수만 다름

## 배열 to Json
@tree = { :station_name => @stationName, :ChunCheonBusInfo => [{ :bus_line_number => @busInfoArray[0][0], :direction => @busInfoArray[0][1], :arrival_time => @busInfoArray[0][2], :bus_point => @busInfoArray[0][3] }, { :bus_line_number => @busInfoArray[1][0], :direction => @busInfoArray[1][1], :arrival_time => @busInfoArray[1][2], :bus_point => @busInfoArray[1][3] } ] }
@dataResult = @tree.to_json

render :json => @dataResult

 

 

3. 카카오봇에서 단어 규칙 및 응답메세지 꾸밈

이제 모든 준비는 끝났습니다.

마지막으로 카카오봇에서 규칙만 지정해주면 끝납니다.

 

카카오봇은 제가 만든 것은 아니며, 구글 플레이 스토어Messenger Bot이라는 어플이 존재하는데 해당 어플이 카카오톡의 메세지를 캐치해내서 사용자로 부터 받은 카톡 메세지에 따라 응답을 하도록 도와주는 어플입니다.

메세지에 따른 응답은 javascript로 자유롭게 코딩을 함으로서 내 입맛대로 꾸밀 수 있습니다.

 참고 1  플레이 스토어 Messenger Bot 어플 [클릭]

 참고 2  곰두리BOT 개발 이야기 [클릭]

 

심플하게, 사용자로부터 '안녕하세요' 라는 메세지를 받으면 그에 맞게 응답하는 코드는 다음과 같습니다.

/*
Messenger Bot 어플
javascript
*/

function response(room, msg, sender, isGroupChat, replier)
{
	/* 사용자가 "안녕하세요" 라고 톡을 보낸 경우
	?? : 안녕하세요
	=> 방가워요! 히히 */
	if (msg == "안녕하세요") { replier.reply("방가워요! 히히") }
    
	/* 사용자가 대화내용 중 "사랑" 이란 단어가 있을 경우
	?? : 철수야 사랑해, 오래살자!
	=> 저도 사랑해요! */
	if (msg.indexOf("사랑") != -1) { replier.reply("저도 사랑해요!") }
}

 

이제 이를 이용해서 사용자가 BOT으로 부터 버스 정보를 알아내고자 메세지를 보내면 자동으로 응답하는 코드는 대략적으로 공개하자면 다음과 같습니다.

if(msg.indexOf(".버스 ") != -1 || msg.indexOf(".ㅂㅅ ") != -1)
{
	station = msg.substring(4,50);
      
	replier.reply("["+ station +"] 정거장 도착 예정시간을 조회중입니다..");
    
	/* 내 홈페이지와 통신 시도 및 JSON Parsing */
	crawl = org.jsoup.Jsoup.connect("http://kakao-bot-api.herokuapp.com/transfers/chuncheon_bus.json/"+ station).ignoreContentType(true).get().text();
	crawlResult = JSON.parse(crawl);
            
	var arr = [];
	for (var i = 0 ; i < crawlResult.ChunCheonBusInfo.length ; i++)
	{
		arr.push([]);
		arr[i][0] = crawlResult.ChunCheonBusInfo[i].bus_line_number;
		arr[i][1] = crawlResult.ChunCheonBusInfo[i].direction;
		arr[i][2] = crawlResult.ChunCheonBusInfo[i].bus_point;
		arr[i][3] = crawlResult.ChunCheonBusInfo[i].arrival_time;
	}
      
	var busResult = "";
	for (var i = 0 ; i < crawlResult.ChunCheonBusInfo.length ; i++) { busResult += "["+ arr[i][0] +" "+ arr[i][1] + "]\n" + arr[i][2] + " 정거장 전 (" + arr[i][3] + " 후 도착 예정)\n\n"; }

	if (arr[0][0] == null) { replier.reply("["+ station +"] 정거장 검색결과가 없습니다.\n올바른 명령어 규칙을 적용해주세요.\n\n* 규칙 설명 ⇒ .버스\n* 이전 메뉴 ⇒ .교통"); }
	else { replier.reply(station +" 정거장 조회결과 :\n"+ busResult); }
}

 

이제 카카오톡으로 메세지를 날리면, Messenger Bot에 짜여진 코드에 맞게 응답을 합니다 :D

 


마무리

진짜 이 서비스를 만들기 위해 1년이란 시간이 걸릴줄은 몰랐네요..

하지만 그래도 이런 우여곡절 끝에 춘천시 교통이 재정비가 되었고, 춘천시민이나 대학생들에게 윈윈이 되는 사업이었습니다.

 

더군다나 저도 이번 교통 재정비 사업 덕분에 새로운 컨텐츠를 만들 수 있고, 누군가에게 도움을 줄 수 있는게 정말 기뻤습니다!

 

 

이 글을 쓰는 와중에 오늘 오전 10시에 제가 작성했던 봇 명령어 추가 소식이 인기 게시글로 올라서 저희학교 소속 에브리타임 유저들에게 전체적으로 제 글이 푸쉬알람이 울렸었는데, 소식이 울린지 2시간만에 이번 달 곰두리봇 전체명령어 중 2순위권(명령어 사용자 90명)으로 올랐네요..ㄷ

 

정말 어메이징 합니다..

 

아무튼 긴 개발썰 글을 봐주셔서 감사합니다 :D

 

 

더불어 춘천시 교통의 변화에 기여를 해주신 많은 언론사의 기자님들께 감사합니다.

댓글
  • 프로필사진 비밀댓글입니다 2019.12.05 22:09
  • 프로필사진 마음 따뜻한 개발자, 나른한 하루 앗 안녕하세요 ㅎㅎ 늦게 답변드려서 죄송해요 ㅠㅠ
    일단 컴공 기준으로 활발히 이루어지는 전국 단위 대외활동은 Develup(이번에 새로 신설된 대외활동으로서, 온라인 위주 기반, 3-4번 정도 오프라인 모임이 있습니다), 멋쟁이 사자처럼 이 대표적으로 있습니다 :)

    그 외에 그냥 교내에 있는 동아리로서는.. 저도 '이런거 하는 동아리가 있다' 라는 정도만 알고있지, 실제로 이름이 뭔지는 모르겠네요..ㅠ (중앙동아리 1개, 컴퓨터과학전공 과 기준 2-3개정도 있는걸로 압니다.)

    더불어 ICPC는 주로 연구실 내 학생들이 자체적으로 팀원을 모아서 대회에 참가하는 것 같아요 :)

    제 글이 많이 도움되었길 빌게요!^^
    더 궁금하신게 있으시면 https://open.kakao.com/o/gyTsXdNb 톡방에 놀러와서 질문주시면 됩니다!

    감사합니다.
    2019.12.08 22:52 신고
댓글쓰기 폼
공지사항
Total
37,626
Today
18
Yesterday
231
링크
«   2020/08   »
            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 31          
글 보관함