게임 봇의 조회 기능을 공통 API로 분리
게임 아이템과 시세를 찾기 위해 처음에는 봇이 Mobilife API를 직접 호출하도록 했습니다. 검색어를 받아 결과 몇 개를 짧게 보여주는 구성이었습니다.
룬과 시세 이력, 계산 기능까지 추가하면서 데이터를 봇 안에서 계속 가공하기는 어려워졌습니다. 다른 화면에서도 같은 설명과 계산 기준을 사용할 수 있도록 ErinDex API로 분리했습니다.
봇 명령과 조회 기능 분리
초기 시세·아이템 명령에는 검색어와 결과 수, 요청 제한 시간을 뒀습니다. 결과가 없거나 요청 제한에 걸리면 봇에서 안내했습니다.
명령 몇 개로 시작하기에는 충분했지만, 데이터 설명과 계산 규칙이 메시지 출력 가까이에 있으면 다른 클라이언트에서 쓰기 어렵습니다. 조회와 계산은 API가 맡고, 봇은 결과를 메시지로 구성하는 방향으로 나눴습니다.
출처별 원문 보관
공식 가이드와 공지, 아이템·시세 API 자료를 모으면서 출처와 확인 시각을 남겼습니다. 일회성 캡처로 정리한 자료는 정기 수집 자료와 구분했습니다.
룬 설명은 원문과 계산용 효과를 함께 유지했습니다. 자동 해석 결과를 사람이 확인한 입력과 같은 상태로 취급하지 않았습니다. 계산이 이상할 때 해석 결과뿐 아니라 원문으로 돌아갈 수 있어야 했기 때문입니다.
| 데이터 | 구분한 내용 |
|---|---|
| 룬 효과 | 원문·자동 해석·확인 상태 |
| 시세 | 최신값·조회 시각별 이력 |
| 조건부 효과 | 기본 조건과 발동 조건 |
| 계산 결과 | 입력 스탯과 가정 |
실시간 조회와 캐시
검색어별 짧은 캐시와 동시 갱신 요청을 합치는 처리를 넣었습니다. 외부 조회에 실패하면 마지막 값을 반환할 수 있지만, 그 값을 새로 가져온 자료처럼 시각을 바꾸지는 않았습니다.
메시지 에이전트는 입출력, 봇은 방 정책과 명령, ErinDex는 데이터와 계산을 맡습니다. 구조화된 API 결과가 있으면 봇 외의 화면에서도 같은 자료를 사용할 수 있습니다.
API로 조회하는 룬과 시세
ErinDex는 읽기 전용 HTTP API에서 구조화된 JSON을 반환합니다. 실행 중인 서비스의 /docs와 /openapi.json에서 경로와 응답을 확인할 수 있습니다. 다음은 로컬 개발 주소로 바꾼 실제 조회 경로 예시입니다.
curl --fail-with-body 'http://127.0.0.1:4310/v1/health'
curl --fail-with-body 'http://127.0.0.1:4310/v1/status'
curl --fail-with-body --get 'http://127.0.0.1:4310/v1/runes' \
--data-urlencode 'q=뼈' --data 'season=2'
curl --fail-with-body --get 'http://127.0.0.1:4310/v1/market/live' \
--data-urlencode 'q=해연' --data-urlencode 'category=무기'인증을 설정한 환경에서는 문서·health 등 예외 경로를 제외한 요청에 별도 인증이 필요합니다. 위 명령은 로컬 API와 자료를 준비한 뒤의 조회 예시이며 키나 저장 데이터를 포함하지 않습니다. 한국어 검색어는 URL 인코딩해 전달합니다.
룬 응답의 description은 원문이고 normalized_effects는 계산용으로 해석한 값입니다. 자동 해석은 confidence: parsed로 구분합니다. 같은 설명을 다시 분석할 수 있어도 원문을 버리면 파서의 해석이 잘못됐을 때 무엇과 비교해야 하는지 알 수 없습니다.
동시 조회와 마지막 정상값
시세 조회에서는 같은 검색어의 동시 갱신을 합칩니다. 예를 들어 같은 순간에 세 명이 같은 아이템을 요청했다면 세 요청이 각각 외부 API를 호출하는 대신 진행 중인 갱신 결과를 공유할 수 있습니다.
검색어 A 요청 ─┐
검색어 A 요청 ─┼→ 같은 갱신 요청 → 원천 API → 캐시·응답
검색어 A 요청 ─┘
검색어 B 요청 ───→ 별도 키의 갱신 요청현재 설정에서 실시간 시세의 기본 캐시 시간은 5분입니다. 새 조회에 실패하면 마지막 시세를 stale로 반환할 수 있지만 원래 조회 시각은 유지합니다. 실패한 시각을 조회 시각으로 저장하면 오래된 값이 방금 확인한 값처럼 보이게 됩니다.
최신값 저장과 이력도 목적이 다릅니다. market_prices에는 현재 조회할 값을, market_price_history에는 조회 시각별 스냅샷을 쌓습니다. 이력을 만든 시점에 가지고 있던 최신값도 첫 스냅샷으로 보존했지만, 그 이전의 알 수 없는 가격까지 복원한 것은 아닙니다.
코드와 데이터의 재현 범위도 구분해야 했습니다. 일부 룬 상세는 일회성 수집으로 준비돼 저장소 복제만으로 다시 만들어지지 않습니다. API를 실행하는 명령과 자료를 확보하는 절차가 별도로 있어야 다른 장비에서 같은 상태를 만들 수 있습니다.
조건에 따른 계산 결과
보유 룬 최적화에서는 조건이 발동하지 않는 조합과 최대 발동을 가정한 조합을 나눴습니다. 조건부 효과가 포함되면 하나의 결과를 모든 전투 상황의 순위로 사용할 수 없기 때문입니다.
저장소 코드와 보관 데이터의 범위도 구분했습니다. 일부 자료는 일회성 수집으로 준비한 것이어서 코드만으로 모두 복원되는 구성은 아니었습니다. 수집 방법과 남아 있는 자료를 함께 문서에 정리했습니다.