장단콩 보드게임 & 미니어처 게임 클럽

BGG 게임 검색 연동 설계

배경

일정 등록 폼(/write/)에서 게임 이름을 검색해 BoardGameGeek(BGG)의 게임 정보를 가져오고, 여러 개를 선택해 하나의 일정에 태그처럼 붙일 수 있게 한다. 상세 페이지(/post/)에서는 등록된 게임들을 썸네일 카드로 보여준다.

BGG API 조사 결과

이 프로젝트는 Firebase Functions(Node 20, v2)가 이미 배포되어 있으므로(functions/index.js, 텔레그램 알림 함수 참고) 같은 곳에 프록시 함수를 추가하는 것이 가장 자연스럽다.

아키텍처

write.js (검색 input, 디바운스)
   -> GET /searchBoardGame?q=카탄        (Firebase Function, HTTPS)
        -> BGG xmlapi2/search 호출 & XML -> JSON [{bggId, name, yearPublished}]
   -> 사용자가 후보 선택
   -> GET /getBoardGameDetail?id=13       (Firebase Function, HTTPS)
        -> BGG xmlapi2/thing 호출 & XML -> JSON {bggId, name, yearPublished, thumbnail}
   -> 선택된 게임을 태그 목록에 추가 (클라이언트 상태, 배열)
   -> 폼 제출 시 games 배열을 posts 문서에 저장

검색 단계는 썸네일을 조회하지 않는다(후보가 여러 개일 수 있어 비용 낭비). 사용자가 실제로 고른 게임에 대해서만 thing API로 썸네일을 1회 조회한다.

데이터 모델

posts 컬렉션 문서에 필드 추가:

games: [
  { bggId: "13", name: "Catan", yearPublished: 1995, thumbnail: "https://..." },
  ...
]

Firebase Functions 추가 (functions/index.js)

두 개의 onRequest HTTPS 함수:

  1. searchBoardGamequery 파라미터로 BGG search API 호출, XML 파싱해 후보 배열(JSON) 반환. 결과 없으면 빈 배열.
  2. getBoardGameDetailid 파라미터로 BGG thing API 호출, XML에서 이름/연도/썸네일 추출해 단일 객체(JSON) 반환.

두 함수 모두 BGG의 202(캐시 준비 중) 응답 시 짧게(예: 1초 후 1회) 재시도하고, 그래도 실패하면 빈 배열/null과 함께 200을 반환한다 — 게임 검색 실패가 폼 자체를 막지 않도록.

XML 파싱은 별도 npm 패키지를 추가하지 않고, Node 내장 기능만으로 충분한 단순한 정규식/문자열 기반 추출로 처리한다(필요한 필드가 id, name, yearPublished, thumbnail 4개뿐이라 풀 XML 파서는 과함).

write.js (일정 등록 폼) 변경

일정 필드 섹션에 “함께할 게임” 항목 추가:

post.js (상세 페이지) 변경

postData.games?.length가 있으면 일정 정보 아래에 “함께 할 게임” 섹션 렌더링:

에러 처리

테스트