내가 어떻게 누아르 테마 퍼즐 플랫폼을 365일 동안 독특한 콘텐츠와 함께 설계했는지

발행: (2025년 12월 8일 오전 02:33 GMT+9)
8 min read
원문: Dev.to

Source: Dev.to

OnlinePuzzle.net

OnlinePuzzle.net을 만들면서 가벼운 웹 앱도 무거운 엔지니어링이 필요할 수 있다는 것을 배웠습니다—특히 콘텐츠, 세계관 구축, 사용자 경험이 모두 맞물려야 할 때 말이죠.

사이트를 만들기 시작했을 때, 목표는 단순히 또 다른 퍼즐 게임을 만드는 것이 아니었습니다. 1940년대 사건 파일에 들어가는 듯한 완전하게 설계된 탐정 세계를 만들고 싶었고, 브라우저에서 부드럽게 실행되며 즉시 로드되고, onboarding이 전혀 필요 없는 경험을 제공하고 싶었습니다.

이를 위해 설계해야 했던 것들은:

  • 네 개의 독립적인 퍼즐 엔진
  • 중복 없는 365일 콘텐츠 시스템
  • 검증 가능한 데이터 파이프라인
  • 일관된 Noir 사용자 경험
  • 백엔드에 의존하지 않는 가벼운 아키텍처

01 — 왜 아키텍처가 게임플레이보다 더 중요한가

각 퍼즐 유형은 겉보기엔 단순합니다:

  • Daily 5 – Wordle‑스타일 추리
  • Scramble – 애너그램 풀이기
  • Word Search – 테마가 있는 8단어 그리드
  • Memory Clues – 스토리 조각 매칭

하지만 비전은 다음을 요구했습니다:

  • 365개의 고유 일일 사건
  • 2700개 이상의 수작업 단서와 단어
  • 내부적으로 일관된 세계관
  • 퍼즐 유형 간의 우연한 재사용이나 교차 누수 금지

진짜 도전은 콘텐츠 엔지니어링이었지, JavaScript나 UI가 아니었습니다. 따라서 아키텍처는 콘텐츠를 느슨한 텍스트 파일이 아니라 코드처럼 다룹니다.

02 — 콘텐츠 백본으로서의 TypeScript

텍스트를 JSON, Google Sheets, Markdown 등에 저장하는 대신, 모든 게임 콘텐츠를 타입이 지정된 객체로 구조화했습니다:

export interface DailyClue {
  word: string
  hintPrimary: string
  hintSecondary: string
}

export interface WordSearchPack {
  id: number
  theme: string
  words: string[]
}

export interface MemoryPair {
  clueA: string
  clueB: string
  category: 'evidence' | 'person' | 'location' | 'action'
}

왜?

  • 컴파일 시점 검증
  • 스크립트 기반 일관성 검사
  • 런타임 서프라이즈 제로
  • 향후 확장 용이

자동화 스크립트가 검증한 내용:

  • 2700개 항목 전체에 중복 없음
  • 숨겨진 충돌 없음(예: 유사 어근)
  • 모든 단어가 Noir 스타일 가이드라인 준수
  • 테마와 카테고리 일관성 유지

콘텐츠는 장식이 아니라 일급 엔지니어링이 되었습니다.

03 — 네 개의 퍼즐 엔진, 하나의 재사용 가능한 프레임워크

게임플레이 엔진은 UI와 콘텐츠에서 완전히 분리됩니다. 각 엔진이 공유하는 요소:

  • 통합 인터페이스
  • 타이밍 & 스트릭 모듈
  • 애니메이션 훅
  • 오류 처리
  • 접근성 헬퍼

예시: Daily 5 체크러는 순수 함수입니다:

export function evaluateGuess(guess: string, answer: string) {
  return guess.split('').map((char, i) => {
    if (char === answer[i]) return 'correct'
    if (answer.includes(char)) return 'present'
    return 'absent'
  })
}

같은 순수성 원칙이 적용되는 곳:

  • Scramble 섞기
  • Word Search 그리드 생성
  • Memory Clues 페어링 로직

엔진은 작고, 테스트 가능하며, 교체하기 쉽습니다.

04 — Noir UX 레이어: 가볍지만 일관된

무거운 에셋 없이 미학을 구현하기 위해 다음을 활용했습니다:

  • Tailwind CSS – 타이포그래피와 종이 같은 질감
  • Framer Motion – 스탬프, 카드 플립, 씬 전환
  • Web Audio API – 타자기 클릭음 및 배경 소음
  • 컴포넌트 수준 변형을 통한 탐정 스타일 카드, 폴더, 사건 파일

전형적인 Noir 카드 컴포넌트 예시:

{/* Noir card component */}
{ /* component markup goes here */ }
{text}

모든 스타일은 이미지가 아닌 구성(composition)으로 구현됩니다. 덕분에 앱은:

  • 빠름
  • 반응형
  • PWA 친화적
  • 테마 변경이 쉬움

Noir 경험은 디자인 시스템을 통해 만들어지며, 무거운 그래픽이 필요하지 않습니다.

05 — PWA + Offline = 가벼운 “일일 습관” 앱

전체 플랫폼은 Service Worker를 통해 오프라인에서도 동작합니다:

  • 자산 캐시
  • 게임플레이 로직 자체 포함
  • 사용자 진행 상황은 localStorage에 저장

이점

  • 번개 같은 로드 속도
  • 높은 일일 유지율
  • 전 세계 퍼즐 플레이어에게 글로벌 접근성 제공

퍼즐 게임이 백엔드 없이도 동작해야 하므로, 백엔드를 두지 않았습니다.

06 — 탐정 프로필 시스템 (백엔드 없이)

사용자 데이터—스트릭, XP, 업적—는 로컬에 저장됩니다:

interface DetectiveProfile {
  streak: number
  bestStreak: number
  xp: number
  achievements: string[]
}

왜 서버와 동기화하지 않을까?

  • 프라이버시
  • 즉시 쓰기
  • 오프라인 모드
  • 복잡도 감소
  • 로그인 마찰 최소화

이는 최대 몰입, 최소 마찰이라는 철학과 일치합니다.

07 — 배운 점

  • 콘텐츠도 백엔드 코드만큼 엄격히 다루어야 한다.
    구조화되지 않은 텍스트는 규모가 커질수록 위험 요소가 된다.

  • 미적 일관성은 스킨이 아니라 시스템이다.
    Noir 경험 = 타이포그래피 + 모션 + 사운드 + 내러티브 톤.

  • 퍼즐 엔진은 순수 함수여야 한다.
    테스트 가능성과 이식성을 보장한다.

  • PWA는 앱을 매일 방문할 때 빛난다.
    일일 퍼즐 + 오프라인 기능이 완벽한 조합.

  • 가벼운 도구도 깊은 경험을 전달할 수 있다.
    사람들은 AAA 그래픽이 아니라 일관성, 완성도, 감성적 텍스처를 원한다.

08 — 플랫폼 체험하기

다음에 흥미가 있다면:

  • 내러티브 중심 시스템 설계
  • 가벼운 웹 앱 구축
  • 대규모 구조화된 콘텐츠 엔지니어링

OnlinePuzzle.net이 영감을 줄 수 있을 것입니다.

Back to Blog

관련 글

더 보기 »