블로그로 돌아가기
회고

10일 만에 Limbus Company 팬 아카이브를 만든 이야기

2026.03.12·3분 소요·5회 조회
Next.jsTypeScriptSupabaseGSAPTailwind CSS

프로젝트 소개

SAIGO NO DANTE(최애의 관리자)는 Limbus Company 팬 아카이브 웹앱이다. 수감자 도감, 덱 빌더, 덱 공유, 거울 던전 EGO 기프트, 메타 분석, 추출 시뮬레이터 등을 한 곳에 모아놓은 올인원 툴이다.

saigo-no-dante.com에서 직접 확인할 수 있다.

왜 만들었나

기존에 흩어져있던 림버스 컴퍼니 정보들 — 나무위키, 디스코드, 개인 스프레드시트 — 을 하나로 모으고 싶었다. 특히 덱 빌더와 EGO 기프트 조합은 기존 툴이 없거나 불편했다.

기술 스택

tsxtsx
const stack = {
  framework: "Next.js 16 (App Router)",
  language: "TypeScript",
  styling: "Tailwind CSS 4 + GSAP",
  database: "Supabase (PostgreSQL)",
  auth: "NextAuth.js (Google OAuth)",
  testing: "Vitest + Playwright",
  deploy: "Vercel",
};

10일간의 개발 타임라인

Day 1-2: 초기 구축 (2/27 ~ 3/1)

Create Next App에서 시작해 전체 사이트의 기본 구조를 잡았다. Supabase 스키마 설계, 수감자 데이터 모델링, 기본 UI 레이아웃을 만들었다.

  • 전체 사이트 구현 + 모바일 최적화 + SEO
  • Google Search Console 인증
  • 덱 상세 모바일 레이아웃

Day 3-4: 덱 빌더 핵심 기능 (3/1 ~ 3/2)

덱 빌더가 이 프로젝트의 핵심이다. 12인격 필수 검증, 커뮤니티 공유, 북마크, 댓글 시스템을 만들었다.

typescripttypescript
const validateDeck = (identities: Identity[]) => {
  const sinners = new Set(identities.map(i => i.sinner_id));
  if (sinners.size !== 12) {
    throw new Error("12명의 수감자를 모두 편성해야 합니다");
  }
};
  • 덱 북마크, 댓글, OG 공유 이미지 생성
  • 패시브 죄악 카운트 덱 전체 합산
  • 메타 분석 페이지 리뉴얼
  • 한글 IME 조합 중 Tab 자동완성 글자 중복 방지 — 이거 잡는데 꽤 걸렸다

Day 5-6: 데이터 파이프라인 (3/3 ~ 3/4)

나무위키에서 스킬/패시브 데이터를 자동 동기화하는 스크래핑 스크립트를 만들었다. 408종의 EGO 기프트 데이터도 구조화했다.

  • 나무위키 통합 동기화 스크립트
  • 거울 던전 EGO 기프트 페이지 (408종)
  • min_identitiesactivation_condition JSONB 리팩토링
  • 이미지 최적화 + ISR 온디맨드 revalidation

Day 7-8: UX 고도화 (3/5 ~ 3/7)

전역 검색(Ctrl+K)을 만들었다. 인격, EGO, 수감자, 키워드, EGO 기프트를 한 곳에서 검색할 수 있다.

typescripttypescript
const getSuggestions = (query: string, items: Item[]) => {
  return items
    .map(item => ({
      item,
      score: levenshteinDistance(query, item.name)
    }))
    .filter(({ score }) => score <= 3)
    .sort((a, b) => a.score - b.score)
    .slice(0, 5);
};
  • 키워드 DB 마이그레이션 (하드코딩 완전 삭제)
  • 인격 리뷰 시스템 (개추/비추 + 한줄평)
  • PWA 지원 — manifest, 서비스 워커, 앱 아이콘
  • Vitest 세팅 + 유틸 함수 단위 테스트 37개

Day 9-10: 안정화 & 디자인 (3/8 ~ 3/11)

버그 수정과 UI 폴리싱에 집중했다. GSAP 애니메이션 추가, 텍스트 가독성 개선, px→rem 변환 등.

  • 전체 UI 텍스트 가독성 개선
  • 전체 페이지 GSAP 애니메이션 추가
  • 관리자 점검 모드 기능 추가

기술적 챌린지

1. Supabase 1000행 제한

Supabase의 기본 select()는 최대 1000행만 반환한다. 스킬/패시브 데이터가 이를 넘어서 페이지네이션을 직접 구현해야 했다.

typescripttypescript
async function getAllRows(table: string) {
  const rows = [];
  let from = 0;
  const PAGE = 1000;
  while (true) {
    const { data } = await supabase
      .from(table)
      .select("*")
      .range(from, from + PAGE - 1);
    if (!data || data.length === 0) break;
    rows.push(...data);
    if (data.length < PAGE) break;
    from += PAGE;
  }
  return rows;
}

2. 한글 IME 자동완성 버그

운영법 에디터에서 한글 입력 중 Tab으로 자동완성하면 글자가 중복되는 문제가 있었다. compositionend 이벤트를 추적해서 IME 조합 상태를 관리하는 것으로 해결했다.

3. px → rem 전환

전체 UI를 px에서 rem으로 전환했다. 접근성과 반응형 측면에서 rem이 훨씬 낫다. 1rem = 16px 기준으로 모든 수치를 변환하되, border-width 같은 곳은 px를 유지했다.

배운 것들

  1. ISR + force-dynamic 전략 — 정적 페이지는 ISR로, API는 force-dynamic으로 분리하니 성능과 실시간성 둘 다 잡을 수 있었다
  2. 나무위키 스크래핑 — 구조가 일정하지 않아서 leaf div 패턴 탐지 같은 휴리스틱이 필요했다
  3. Supabase RLS — Row Level Security를 잘 설계하면 API 보안 로직을 대폭 줄일 수 있다
  4. GSAP + Next.jsuseGSAP 훅으로 cleanup을 자동 관리하는 게 핵심

소스코드는 GitHub에서 볼 수 있다.

다음 글

React에서 Apple Liquid Glass 효과를 구현하고 최적화한 과정