브레베 데이터베이스

발단 - 포켓몬 데이터베이스

몇 주 전에 타임라인에서 Notion Pokédex를 봤습니다. 노션 페이지에 포켓몬을 타입 별로 정보를 볼 수 있고 포켓몬 한 종류의 정보를 한 페이지에 나열해 볼 수도 있었습니다. 이 페이지의 중요한 특징은 정보를 페이지에 정적으로 입력하는 대신 내부에 데이터베이스를 두고 각 페이지에서는 이 정보를 조회한 결과만을 보여주고 있다는 점입니다. 이렇게 구성해두면 새로운 포켓몬이 추가될 때 편리하게 대응할 수 있고 일부 정보만 조회한 결과로 새로운 페이지를 만들 수도 있었습니다. 그렇잖아도 한국랜도너스 웹사이트에서 지난 시즌 브레베 정보가 종종 삭제되어 아쉽다고 생각하는 중이었고 페이지에 정적으로 입력된 일정과 이벤트 정보는 그 상태로만 조회할 수 있었습니다. 그래서 노션 포켓몬 데이터베이스와 비슷한 것을 흉내내보기로 했습니다.

환경

비슷한 것을 만들기로 했지만 노션을 사용하지는 않기로 했습니다. 이미 오랫동안 사용해 온 위키가 있었고 비슷한 역할을 하는 struct라는 플러그인이 있다는 것을 이미 알고 있었습니다. 이 플러그인은 SQLite 데이터베이스를 조회한 결과를 위키 페이지에 표시하는 기능을 합니다. 노션처럼 아름답고 우아한 모양은 결코 아닐테지만 기능상으로는 비슷할 거라고 예상했습니다. 하지만 플러그인과 SQLite 양쪽 모두 처음 사용해보는 입장이라 어디까지 가능하고 또 어디부터 가능하지 않을지 파악하지 못하는 상태로 시작했습니다.

테이블 구성

일단 테이블 구조를 생각해 두기로 했습니다. 브레베 이벤트는 각 지역 별로 서로 다른 코스에서 진행됩니다. 코스는 이 코스를 주행했음을 증명할 컨트롤 포인트(CP라고 부름)의 집합으로 구성되어 있고 각 컨트롤 포인트는 이곳에서 해야 할 행동이 있습니다. 대부분은 편의점에서 도장을 받지만 사진을 찍거나 그 위치에서만 알 수 있는 정보를 적어가야 할 때도 있습니다. 웹사이트에서는 이 코스를 여러 가지 데이터를 통해 제공합니다. GPX 파일과 RwGPS 주소, 구글맵 경로 주소 등을 매 코스마다 제공합니다. 또 코스 하나는 같은 지역의 서로 다른 날짜에 지정되어 서로 다른 이벤트에 사용됩니다.

몇 가지 생각할 점이 있었습니다. 일단 같은 코스가 여러 이벤트에 사용되는데 종종 이전에 사용된 코스를 다음 이벤트에 재사용하면서 테스트라이딩 결과에 따라 코스를 변경할 때가 있습니다. 그러면 이전에 이 코스를 사용하던 이벤트의 코스정보도 변경되곤 했습니다. 가령 3월과 8월에 같은 코스를 달리는 이벤트가 있는데 7월에 테스트라이딩을 해보니 공사구간이 많아 경로를 변경하고 이를 정적인 페이지에 반영해버리면 이 코스를 사용하는 3월 이벤트에도 변경된 코스가 나타나곤 했습니다. 이 상황을 없애고 싶었습니다. 또 컨트롤 포인트의 목록을 이미지 파일로 제공하는데 브레베 전에는 항상 각 컨트롤 포인트의 이름과 이들 사이의 거리, 예상 주행속도에 따른 목표 도착시각을 기록한 표를 만들었습니다. 시각정보를 제외하고서라도 컨트롤 포인트 목록을 이미지 대신 텍스트와 테이블로 전달하고 싶었습니다.

이런 생각을 좀 더 고려해서 테이블을 고쳤습니다. 여기까지 하고 보니 브레베 이벤트 데이터를 안전하게 보관할 수 있을 것 같아 보였지만 별도로 입력 페이지 코드를 작성하지는 않을 작정이라 입력이 편하지 않을 것 같다는 걱정을 하기 시작했습니다.

데이터베이스

제가 다뤄본 데이터베이스는 MySQL 딱 하나입니다. 회사에서는 항상 다른 데이터베이스를 사용했는데 그건 엔지니어들의 몫이었으므로 제가 그 데이터베이스를 직접 다룰 일은 없었습니다. 그런데 이번에 사용할 struct 플러그인은 SQLite를 지원하므로 이 데이터베이스를 사용할 수밖에 없었고 기존에 알고 있던 데이터베이스와 뭐가 다른지 알아둬야 했습니다. 일단 애초부터 다른 애플리케이션에 임베딩 할 용도이고 데이터타입 종류가 적은 점이 눈에 들어오는 특징이었습니다. 하다못해 enum 타입을 따로 정의할 수 없었습니다. 이를 엄격하게 적용한다면 enum에 해당하는 별도 테이블을 만들어야 했고 느슨하게 적용한다면 텍스트 타입으로 만든 다음 enum에 해당하는 값을 그냥 텍스트로 넣어버릴 수도 있어 보였습니다. 느슨한 정책을 사용하기로 했습니다.

struct 플러그인

도쿠위키 페이지에 SQLite 데이터베이스를 조회한 결과를 표시하기 위해 struct 플러그인을 사용했습니다. 이 플러그인은 SQL과 비슷하지만 무척 제한적인 전용 문법을 통해 데이터베이스를 조회할 수 있습니다. 이 플러그인을 사용해 스키마를 입력하면 플러그인에 의존성을 가짐과 동시에 몇 가지 도움을 받을 수 있습니다. 위에서 이야기했던 enum 칼럼을 쉽게 만들 수 있습니다. 내부 타입은 그냥 텍스트이지만 이 플러그인이 중간에서 입력을 제한해 줍니다. 또 날짜시간 타입을 사용할 수 있고 URI 타입으로 칼럼을 생성하면 여기에 기입한 텍스트를 조회하면 자동으로 링크를 만들어줍니다. 게다가 미디어 타입은 미디어를 위키 시스템을 통해 업로드한 다음 이를 링크하는 구성이어서 굳이 BLOB 타입을 사용할 필요가 없을 뿐 아니라 최대한 위키에 호환성을 유지하는데도 도움이 될 것 같아 보였습니다.

문제

하지만 테스트 데이터 몇 개를 입력해놓고 출력 페이지를 만들기 시작한지 1분만에 문제가 생겼습니다. struct 플러그인은 데이터베이스 조회 결과를 항상 '테이블 모양' 또는 '리스트 모양'으로만 표시할 수 있었습니다. 아마도 개발자는 이거면 충분할 거라고 생각한 모양입니다. 실제로 저 두 가지 표현방법으로 데이터를 표현할 수 있었습니다. 하지만 정보를 전달하는 웹페이지를 만드는 것은 다른 이야기였고 저는 조회 결과를 '값 하나'로만 얻기를 원했습니다. 검색해보니 이미 저와 똑같은 의견을 가진 사람들이 'Struct data as variables'에 의견을 주고받고 있었을 뿐 아니라 'Value aggregation type'에 풀리퀘스트도 있었습니다. 다만 마지막 활동이 거의 1년 전이라 언제 적용될지 알 수 없었습니다. 급한 성격에 이 코드를 바로 적용했습니다. 이참에 미뤄뒀던 서버에 적용할 위키 코드를 깃헙 리파지토리로부터 배포할 수 있게 했습니다. 이제 원하는 대로 값 하나하나를 가져올 수 있게 됐고 무사히 브레베 정보 페이지를 만들게 됐습니다.

노가다

한국랜도너스 웹사이트로부터 작년 브레베 이벤트 정보를 가져오는건 꽤 귀찮았습니다. 이 정보는 여러 사람들이 긴 시간을 들여 직접 달려보고 작성한 거대한 노력의 결과물입니다. 라이딩을 통한 정보수집은 훌륭하지만 다들 정보시스템을 미리 정의된 규칙에 따라 운영하지는 않기 때문에 같은 장소를 의미하는 텍스트가 서로 다른 이름으로 사용되기도 하고 비슷한 거리를 어느 페이지에서는 파일 하나로 표현하는 반면 다른 페이지에서는 여러 파일로 나눠 표현하기도 했습니다. 또 위에서 이야기한 컨트롤 포인트 정보는 이미지만으로 제공되었습니다. 때문에 정보를 테이블에 입력하는건 그냥 노가다였습니다. 일의 종류가 이전에는 바로 앞에 있는 문제를 해결하는 것이었는데 순식간에 노가다로 바뀌면서 급속히 하기 싫어져 약간 곤란했습니다만 어쨌든 2019년도 정보를 모두 가져왔습니다.

결과

브레베 데이터베이스: https://neoocean.net/b/

작년도 브레베 이벤트의 일정과 지역 별 목록, 브레베 이벤트 하나하나의 정보를 테이블로부터 가져와 보여줍니다. 새로운 이벤트가 추가될 때 편리하게 대응할 수 있고 정적인 페이지일 때에 비해 다른 뷰를 만들어내기 쉬워졌습니다. 구조화된 데이터는 각각 eventcourseuri_cppageplace 테이블 페이지에 있습니다. 남은 목표는 두 가지입니다. 첫째, 한국랜도너스 웹사이트에서 사라진 2018년 및 그 이전 이벤트 정보를 찾아다가 입력하는 것. 둘째, 한국랜도너스 웹사이트보다 더 오래 이 정보를 유지하는 것입니다.

한계

struct 플러그인을 처음 볼 때는 SQL에 가까운 문법을 사용할 수 있을 줄 알았습니다. 그래서 조인이나 통계기능, 그룹핑을 통해 페이지를 쉽게 구성하고 컨트롤 포인트 별 분포나 사용 횟수 같은 정적인 정보로는 알아내는데 품이 많이 드는 정보를 바로 위키 페이지에 표시할 수 있기를 기대했습니다. 하지만 실제로는 기초적인 조회 쿼리밖에 사용할 수 없었습니다. 때문에 컨트롤 포인트 관련 통계 페이지를 만들어내는데는 실패했습니다. 위 결과에 place 테이블이 다른 정보 없이 컨트롤 포인트 이름만 나열한 채 끝난 이유가 이것입니다. 제한된 문법 대신 차라리 쿼리를 직접 날릴 수 있으면 좋지 않나 싶은데 뭔가 이렇게 만든 이유가 있을 거라고 생각합니다.

또 이렇게 데