이번 학기부터 BCSD의 작업방식이 변경됐다.
동아리에서 운영중인 서비스인 KOIN
은 기존에 백엔드
, 프론트엔드
, 안드로이드
, IOS
, UI
이렇게 트랙별로 나뉘어서 운영되고 있었다. 이렇게 나눠서 진행을 하니 서로가 어떤 작업을 맡고 있는지 확인하기가 어려웠고, 트랙장들끼리만 소통을 하다보니 다른 트랙원들은 내가 무슨일을 하고 있는지, 또 왜 해야하는지 알기 어려웠다고 한다.
그래서 트랙체제를 벗어나 기능별로 팀을 나누어 앞으로의 작업을 진행하기로 했다. 나는 위의 3개의 팀 중 Campus Team
에 들어가게 되었다. 졸업작품이 셔틀버스 관련이기도 하고 사람들이 많이 사용하는 기능을 작업해보고 싶어서 선택하게 되었다.
팀 체제로 변경된 후 가장 먼저 한 작업은 식단 관련 신규 기능인데 이에 관련해서는 나중에 따로 글을 써봐야겠다. 이번 글은 애증의 버스..
버스 관련해서 내가 한 일은 크게 3가지로 나눌 수 있다.
순서대로 시외버스
, 시간표
, 오류 수정
인데 이 순서로 설명해보겟다.
성빈이랑 시내버스
도 하긴 했는데 거의 옆에서 구경한 수준이라 빼고 말할 예정
시외버스
랑 로직은 비슷하다!
이거 때문에 3일 밤샜다. 구조 파악하느라 진짜 힘들었던 기억이 난다.
현재 KOIN에서는셔틀
, 대성고속(시외)
, 시내
이렇게 3개의 버스들의 남은 시간을 보여준다. 그 중 나는 대성고속(시외)
의 남은시간을 보여주는 작업을 맡게 되었다.
어떤식으로 로직이 동작되는지 살펴보자
@RequestParam(value = "bus_type") BusType busType,
@RequestParam BusStation depart,
@RequestParam BusStation arrival
우선 인자는 이렇게 3개가 들어온다. BusType
에는 말 그대로 어떤 버스인지 위의 3종류의 버스 중 하나가 들어오고, 출발지와 도착지를 인자로 받아온다.
인자를 받아서 Service
에서 처리를 해주는데 들어온 BusType
에 따라서 해당 버스에 맞는 로직을 실행해준다.
공공데이터포털에서 버스 api를 신청해서 사용하는데, 계정당 하루 100000회로 제한이 되어 있다. 현재 외부 api를 사용하는게 대성고속(시외)
, 시내버스
이렇게 2개가 있는데 프로덕션
과 스테이지
에서 사용해서 남은시간 로직을 호출할때마다 외부 api를 부르게 된다.
위의 사진에 있는 저 남은시간은 코인 홈페이지에서 메인화면에 있기 때문에 접속만 해도 외부 api를 계속해서 호출하게 된다.
이렇게 되면 하루 최대 api 호출 수를 넘길 수도 있기 때문에 레디스에 캐싱
을 하기로 했다. 시내버스
는 1분마다 캐싱을 하고 시외버스
는 1시간 주기로 캐싱을 한다.
시내버스는 실시간으로 오는 버스의 시간이 필요하기 때문에 1분마다 캐싱을 하여 레디스에 저장하고, 시외버스는 시간표가 정해져 있고 변경의 가능성이 적기 때문에 1시간에 1번씩만 캐싱을 하기로 했다.
캐싱을 언제 할 지 판단하는 방법은 version
테이블을 이용하기로 했다.
version 테이블의 updated_at 확인
-> 현재시간과 updated_at을 비교하여 시내버스는 1분, 시외버스는 1시간이상 차이나면 오픈 api를 호출하여 캐시에 저장
결국 레디스에 저장이 된 것을 가져와서 남은 시간을 알려주는 방식이다.
Koin
의 시외버스에서 보여주는 정류장은 한기대
, 야우리
이렇게 2개를 보여줬다.
그렇게 되면
이렇게 2가지 경우가 나왔다.
공공데이터포털의 오픈 api에서 해당 정류장을 넣었을 때 값이 제대로 나오는 지 확인을 해보기 위해서 다양한 경우의 수로 실험을 해봤는데 한기대
→ 터미널
에 해당하는 시외버스의 api가 동작을 하지 않았다.
2시간 정도 보다가 혹시나 해서 공공데이터포털
의 이전 문의내역을 보니 1년전에 동일한 내용으로 문의한 것을 확인할 수 있었다.
이에 대한 국토교통부의 답변도 있었는데
1년이 지났는데도 불구하고 아무런 조치가 안되어 있어서 다시 문의를 남겼다.
약 2주 뒤에 답변이 왔는데 차량운행은 하지만 스케줄 정보는 제공해주지 않는다는 답변이었다.
결국 Redis
에 캐싱을 해두고 유효시간을 없애서 캐싱해 둔 데이터를 통해 시외버스의 남은 시간을 제공하기로 했다.
이런식으로 진행이 된다.
이거 하느라 3일 연속으로 밤샜던것 같다...
구조 파악을 제대로 하고 코드를 짜야겠다고 느꼈다.
이런 구조로 코드를 짜고 머지를 시키고 한숨 돌렸는데, 남은 시간이 제대로 나오지 않는 오류가 발생했다..!
오랜 시간을 찾아본 결과 저 오류는 Redis
에 넣어둔 데이터가 날아가서 생기는 오류였다. 분명히 Redis
캐시의 만료기간이 없도록 설정을 해놓았는데도 불구하고 오픈 API를 받아와 데이터를 덮어 씌워서 캐시가 없어졌다..
급한 문제이긴 했지만 아예 없어지는 문제는 아니었기 때문에 일단 데이터가 없어질 때마다 Redis
에 넣어주며 급한 다음 작업을 진행했다.
다음 작업은 버스 시간표를 가져오는 작업이었다. 오픈 API를 통해서 제공되는 버스 시간표만 받아오면 될 줄 알았다.... 근데 전혀 아니었다.
위에서 말했다시피 KOIN
에서 제공하는 버스는 셔틀
, 대성고속(시외)
, 시내
이렇게 3가지다. 이 중 시내는 KOIN
에서는 제공하지 않기 때문에 셔틀
과 대성고속(시외)
의 시간표만 제공하면 됐다.
셔틀버스는 그냥 받아오기만 하면 돼서 어렵지 않았다. 문제는 또 대성고속(시외)
였다..
시외버스를 설명하기 전에 일단 대략적인 구조를 설명해야겠다.
후.. 이 사진보면 이해가 한번에 됐으면 좋겠다..
그 동안 다른 코드들을 보면서
이런 아무것도 없는 클래스는 왜 있을까 궁금했는데 이번에 알게 되었다.
시내버스
, 셔틀버스
, 대성고속(시외)
를 하나의 상위타입으로 관리하기 위해 BusTimetable
이라는 클래스를 상속받아 구현을 해줬다.
이렇게 하지 않으면 각 버스타입마다 메서드를 생성해주어야해서 코드 중복이 발생하게 된다.
아무튼 위에 말한 것처럼 시내버스
는 교통상황에 따라서 도착/출발 시간이 유동적이기 때문에 제공하지 않기로 했다.
셔틀버스
는 Mongo
에 저장되어 있는 셔틀버스
시간표를 그대로 가져와서 반환하기만 하면 된다.
문제는 또 시외버스
였다..
이것도 결국 공공데이터포털
의 api를 사용해야 하는데 현재의 문제는 해당 기관에서 시간표를 제공하지 않는다는 문제가 있었다. 그래서 우리는 레디스에 캐싱을 해놓고 사용하기로 했다.
구조를 다 그려놓으니까 쓸게 없네.. 저 구조를 생각하는게 어려웠다는걸 알아주세요...
어찌저찌 코드를 짜고 머지를 했는데 계속 시외버스 시간표가 Redis
를 탈출하는 이슈가 발생했다. 그럴 때마다 임의로 넣어주었는데 근본적인 이유를 찾아야했다.
그렇게 어디서 탈출하는지 한참 찾다가 의심되는곳을 발견했다!
예전의 내가 잘 써놔서 편하군
간단요약을 하자면 캐시 만료로직의 수정이 필요하다는 내용이다. 기존의 캐시 만료로직은 외부 api가 정상적으로 돌아갈 경우 제대로 동작하는 로직인데 현재는 외부 api 문제로 인해 제대로 동작하지 못하는데, 이 때 캐시가 만료되었다고 판단을 하고 외부 api를 호출해서 Redis
에 덮어 씌워서 계속 집을 나갔던 것이다.
그래서 캐시만료 로직을 Redis에 접근해서 해당하는 캐시 ID가 존재하는지 확인하는 방식으로 바꿨다.
이렇게 하니 더 이상 집을 나가지 않고 잘 작동하게 되었다. 휴~
HSET expressBus:koreatech:terminal busInfos.[0].depart "08:35" busInfos.[0].arrival "08:55" busInfos.[0].charge "1900" busInfos.[1].depart "10:35" busInfos.[1].arrival "10:55" busInfos.[1].charge "1900" busInfos.[2].depart "11:35" busInfos.[2].arrival "11:55" busInfos.[2].charge "1900" busInfos.[3].depart "13:35" busInfos.[3].arrival "13:55" busInfos.[3].charge "1900" busInfos.[4].depart "14:35" busInfos.[4].arrival "14:55" busInfos.[4].charge "1900" busInfos.[5].depart "16:35" busInfos.[5].arrival "16:55" busInfos.[5].charge "1900" busInfos.[6].depart "17:35" busInfos.[6].arrival "17:55" busInfos.[6].charge "1900" busInfos.[7].depart "19:35" busInfos.[7].arrival "19:55" busInfos.[7].charge "1900" busInfos.[8].depart "22:05" busInfos.[8].arrival "22:25" busInfos.[8].charge "1900" _class "in.koreatech.koin.domain.bus.model.express.ExpressBusCache" id "koreatech:terminal"
HSET expressBus:terminal:koreatech busInfos.[0].depart "07:00" busInfos.[0].arrival "07:33" busInfos.[0].charge "1900" busInfos.[1].depart "09:00" busInfos.[1].arrival "09:33" busInfos.[1].charge "1900" busInfos.[2].depart "10:00" busInfos.[2].arrival "10:33" busInfos.[2].charge "1900" busInfos.[3].depart "12:00" busInfos.[3].arrival "12:33" busInfos.[3].charge "1900" busInfos.[4].depart "13:00" busInfos.[4].arrival "13:33" busInfos.[4].charge "1900" busInfos.[5].depart "15:00" busInfos.[5].arrival "15:33" busInfos.[5].charge "1900" busInfos.[6].depart "16:00" busInfos.[6].arrival "16:33" busInfos.[6].charge "1900" busInfos.[7].depart "18:00" busInfos.[7].arrival "18:33" busInfos.[7].charge "1900" busInfos.[8].depart "20:30" busInfos.[8].arrival "21:03" busInfos.[8].charge "1900" _class "in.koreatech.koin.domain.bus.model.express.ExpressBusCache" id "terminal:koreatech"
이건 레디스에 시간표를 직접 때려넣는 코드다
사용할 일 있으면 나중에 사용하세요
사실 지금도 간헐적으로 외부 api로 인한 오류가 생기는데 이를 어떻게 처리할 지 좀 더 고민해봐야겠다.
그리고 T머니에서 제공하는 버스 api를 사용해서 다중화하는 방법도 현식이형이랑 해 볼 예정이다.
다중화도 되면 정말 멋지겠네요 ㅋㅋㅋㅋ 👍