포켓몬 GO 일정과 실제 등장 정보의 차이
포켓몬 GO 일정과 도감, 레이드 정보를 웹과 봇, 아침 알림에서 함께 사용하려고 PogoAtlas API를 만들었습니다. 처음에는 기사와 HTML 파싱도 검토했지만 원본을 조사하며 정형 JSON을 사용할 수 있는 부분을 찾았습니다.
자료를 한 API로 모아도 각각의 의미는 달랐습니다. 특히 지금 확인된 레이드 목록과 시작·종료 시각이 있는 일정을 같은 정보로 취급하면 오늘과 내일의 안내가 잘못될 수 있었습니다.
수집 성공과 보스 목록 갱신
9월에는 동기화가 정상 종료했는데 현재 레이드에 이전 보스가 남았습니다. 여러 API를 비교했지만 같은 자료를 가져오는 곳은 모두 갱신이 늦었습니다. 반면 일정 피드에는 새 보스가 있었습니다.
기존 구현은 현재 목록에 날짜를 붙이는 방식이었습니다. 일정 시작 시각이 됐다고 새 보스로 교체되는 구조는 아니었습니다. 이를 바꿔 확인된 일정에서 오늘의 보스를 구성하도록 했습니다.
| 정보 | 의미 |
|---|---|
| current 목록 | 원천이 현재 목록으로 제공한 정보 |
| 확인된 일정 | 시작·종료 시각이 있는 예정 정보 |
| 동기화 시각 | 자료를 가져온 시각 |
| 자료 변경 시각 | 원천 내용이 바뀐 시각 |
동기화 시각만 새로워져도 실제 내용은 이전과 같을 수 있습니다. 수집 작업이 정상으로 끝났다는 로그와 원본이 최신이라는 판단을 나눠야 했습니다.
일정과 현재 등장 정보 구분
내일 조회에는 한국 날짜를 기준으로 겹치는 일정을 사용했습니다. 날짜를 알 수 없는 current 보스를 내일도 등장할 것이라고 복사하지 않았습니다.
API와 웹, 봇, 브리핑은 예정·진행 중·종료를 같은 기준으로 판단하도록 맞췄습니다. 일정에 연결됐다는 사실과 현재 진행 중이라는 상태도 구분했습니다. 일정이 없는 날과 조회에 필요한 저장 자료가 아직 준비되지 않은 상태 역시 다른 응답으로 다뤘습니다.
날짜 구간으로 일정을 고르는 예시
한국의 하루를 조회한다면 그날 0시 이상, 다음 날 0시 미만의 구간과 겹치는 행사를 찾을 수 있습니다. 행사 끝을 포함하지 않는 방식으로 비교하면 한 행사가 끝나는 순간 다음 행사가 시작해도 이전 행사가 계속 남지 않습니다.
from datetime import datetime
def overlaps(start, end, day_start, day_end):
return start < day_end and end > day_start
day_start = datetime.fromisoformat("2026-10-18T00:00:00+09:00")
day_end = datetime.fromisoformat("2026-10-19T00:00:00+09:00")
assert overlaps(
datetime.fromisoformat("2026-10-17T10:00:00+09:00"),
datetime.fromisoformat("2026-10-18T10:00:00+09:00"),
day_start, day_end,
)
assert not overlaps(
datetime.fromisoformat("2026-10-17T10:00:00+09:00"),
day_start, day_start, day_end,
)날짜 비교를 설명하는 가상 일정입니다. 실제 src/calendar.ts는 제공처별 UTC·로컬 시간 기준을 구분하고 월간 조회의 시작·종료 시각을 계산합니다. 실제 API에는 종료 시각이 없는 자료를 처리하는 조건도 있어 위 함수보다 복잡합니다. 제공처마다 시간대가 다르면 날짜 문자열을 같은 시간대 기준으로 맞추는 작업도 필요합니다.
오늘과 내일 조회는 같은 목록을 복사해 날짜만 바꾸지 않습니다. 오늘 자료에서 이름을 찾았다고 다음 날도 유효한 종료 시각이 있는 것은 아니기 때문입니다. 날짜가 있는 일정과 날짜를 확인할 수 없는 current 목록을 구분해 저장한 이유입니다.
API 실행과 조회 예시
현재 API는 Node.js 24 이상과 내장 SQLite를 사용합니다. 소스를 준비한 개발 환경에서는 다음 순서로 기본 자료와 이벤트를 동기화하고 실행할 수 있습니다.
npm ci
npm run sync:go-data
npm run sync:events
npm run apicurl --fail-with-body 'http://localhost:8001/v1/status'
curl --fail-with-body 'http://localhost:8001/v1/raids/current?day=tomorrow'
curl --fail-with-body 'http://localhost:8001/v1/events/calendar?month=2026-10'
curl --fail-with-body 'http://localhost:8001/v1/pokemon/445'동기화 명령은 외부 자료를 가져오는 단계이며, 조회 요청은 저장한 자료를 읽는 단계입니다. 순위·보스 공략 등 별도 자료에는 각각의 동기화 명령이 있으므로 위 두 명령만으로 모든 데이터가 채워졌다고 볼 수는 없습니다. 실행 중인 API의 /docs와 /openapi.json에서 경로와 실제 응답을 확인합니다.
최신 자료를 읽지 못해 이전 성공값을 유지했다면 그 상태와 확인 시각도 응답에 남아야 합니다. 조회 속도를 위한 캐시가 원본의 갱신 상태까지 새것으로 바꾸지는 않도록 했습니다. 같은 결과를 웹·봇·알림에서 써도 날짜와 자료의 의미가 유지되게 만드는 것이 API 쪽 역할입니다.
도감과 순위 데이터 연결
한국어 이름은 매번 모델로 번역하는 대신 고정 데이터 매핑을 사용했습니다. 일반·그림자·메가와 진화체의 활용은 따로 연결했습니다.
레이드 점수와 PvP 점수는 용도가 달라 하나로 합치지 않았습니다. 도감에 존재하는 폼도 실제 게임에 출시됐거나 지금 등장한다는 의미는 아닙니다. 이름과 폼, 활용 점수, 등장 상태를 각각의 자료에 맞게 연결해야 했습니다.
수집 작업과 조회 API 분리
일정과 보스 공략, 맥스 자료는 SQLite에 저장하고 조회 요청은 저장된 자료를 사용하도록 정리했습니다. 화면을 열 때마다 외부 사이트 수집부터 시작하지 않는 구성입니다.
수집 과정에서는 필수 값과 중복, 비정상적인 자료 감소를 확인했습니다. 새 자료를 정상적으로 읽지 못하면 이전 성공값을 유지했습니다. 검색과 파싱, 순위에는 캐시를 두고 같은 DB 연결의 쓰기와 다른 연결의 변경을 구분해 갱신했습니다.
공개 캐시는 일부 정상 조회에만 짧게 적용하고 오류 응답에는 적용하지 않았습니다. 데이터가 준비되지 않았다는 오류까지 캐시에 남으면 복구 후에도 이전 실패가 보일 수 있기 때문입니다.
API에 자료를 모으는 것만으로 오늘의 레이드와 내일의 일정을 같은 방식으로 안내할 수는 없었습니다. 현재 목록과 예정 정보, 자료 변경 시각을 구분하고 웹·봇·브리핑이 같은 시각 기준을 사용하도록 맞췄습니다. 저장 자료가 아직 준비되지 않은 경우도 일정이 없는 날과 다른 응답으로 구분했습니다.
이 구성은 이전 작업을 누적해 운영하고 나중에 Git에 정리한 것입니다. 웹 전환과 Android 앱은 같은 API를 사용하지만 각각 별도 글로 다룹니다.