본문으로 건너뛰기
← 블로그로 돌아가기
개발일지 2026년 7월 8일

Flutter 플러그인 0개로 게임을 출시했다 — Tidy Trove 출시기

Windows 개발자 모드가 막혀서 pub get이 플러그인을 설치할 수 없었다. 저장·오디오·광고를 전부 MethodChannel로 직접 구현하고 Google Play에 출시하기까지.

Tidy Trove Flutter Flame MethodChannel

Tidy Trove는 안개 덮인 9×9 보드에 블록을 배치해 칸을 발굴하고 너구리를 구출하는 캐주얼 퍼즐이다. Google Play에 정식 출시했고, 지금도 라이브다. 이 글은 그 과정에서 겪은 제일 이상한 제약 — Flutter 플러그인을 하나도 못 쓰는 환경 — 을 어떻게 뚫었는지에 대한 기록이다.

pub get이 안 된다고?

시작은 황당했다. shared_preferences를 추가하고 flutter pub get을 돌렸는데 실패. 원인을 파보니 개발 머신의 Windows 개발자 모드가 꺼져 있었고, 이 상태에선 pub get이 플러그인 패키지 설치에 필요로 하는 NTFS 심볼릭 링크를 만들 수 없다. 개발자 모드를 켜면 끝나는 문제지만, 이 머신에선 정책상 켤 수 없었다.

선택지는 둘이었다. 환경을 바꾸거나, 플러그인 없이 가거나. 환경을 바꿀 수 없으니 답은 정해져 있었다 — 네이티브 기능을 전부 직접 구현한다.

MethodChannel로 다 만들기

Flutter의 MethodChannel은 Dart와 네이티브(Kotlin) 사이의 얇은 다리다. 플러그인들이 내부적으로 쓰는 그 통로를 직접 쓰면, 플러그인 없이도 네이티브 기능에 닿을 수 있다.

필요한 것보통 쓰는 플러그인직접 만든 것
게임 저장shared_preferencestidytrove/paths 채널 → Android filesDir에 파일 저장
BGM/SFXjust_audiotidytrove/audio 채널 → SoundPool(효과음) + MediaPlayer(배경음)
광고google_mobile_adstidytrove/ads 채널 → AdMob 전면/리워드 SDK 직접 연결

전부 MainActivity.kt 안에 있다. 각 채널은 메서드 이름과 인자를 받아 네이티브 API를 호출하고 결과를 돌려주는 단순한 구조다. 플러그인이 해주던 일을 손으로 쓰는 것뿐인데, 막상 해보니 생각보다 코드가 적었다 — 필요한 기능만 만들면 되니까.

잃은 것과 얻은 것

잃은 것: iOS 대응. 플러그인은 양쪽 네이티브 코드를 다 들고 오지만, 직접 구현은 Android만 썼다. iOS를 하려면 Swift로 같은 채널을 한 번 더 써야 한다. Android 우선 출시라 감수했다.

얻은 것: 의존성 0의 홀가분함. 플러그인 버전 충돌, 메이저 업데이트 마이그레이션, “이 플러그인이 내부에서 뭘 하는지 모르겠다” 같은 문제가 아예 없다. 앱 크기도 줄었고, 광고 SDK 초기화 타이밍 같은 민감한 부분을 직접 제어할 수 있었다.

게임 자체는 Flame으로

게임 루프와 렌더링은 Flame 엔진에 맡겼다. 9×9 보드, 안개 발굴, 블록 드래그 — 전부 Flame의 컴포넌트 시스템 위에서 돈다. Flutter는 메뉴·도감·설정 같은 UI 셸을 담당한다. 이 분업은 만족스러웠다. UI는 위젯으로, 게임은 게임 엔진으로.

100개 스테이지는 손으로 만들고 stage_data.dart에 상수로 박았다. 스테이지 생성기와 밸런스 시뮬레이터를 Dart CLI 툴로 만들어서 난이도 곡선을 검증했다.

배운 것

  • 제약이 설계를 단순하게 만든다. 플러그인을 못 쓰니 “정말 필요한 네이티브 기능이 뭔가”를 먼저 묻게 됐고, 답은 셋뿐이었다(저장·소리·광고).
  • MethodChannel은 무섭지 않다. 플러그인 소스를 한 번 열어보면 안다 — 그들도 결국 같은 통로를 쓴다.
  • 출시가 스펙을 이긴다. iOS 포기, 플러그인 포기 같은 결정 덕에 게임이 스토어에 올라갔다. 완벽한 크로스플랫폼 구조를 고민만 했다면 아직 출시 못 했을 거다.

Tidy Trove는 Google Play에서 무료로 받을 수 있다.