왜 나는 앱 개발을 멈추고 Flutter로 엔진을 만들기 시작했는가

발행: (2026년 3월 12일 오후 09:46 GMT+9)
7 분 소요
원문: Dev.to

I’m happy to translate the article for you, but I need the actual text of the post. Could you please paste the content you’d like translated (excluding any code blocks or URLs you want to keep unchanged)? Once I have the text, I’ll provide a Korean translation while preserving the original formatting and the source link at the top.

소개

인디 개발자로서 가장 큰 병목 현상은 실력이 아니라 시간입니다. 여러 앱을 유지보수하면 생산성이 급격히 떨어질 수 있습니다.

Source:

문제

몇 달 전, 나는 PyMaster라는 게임화된 앱을 만들기 시작했습니다. 이 앱은 사람들에게 휴대폰으로 파이썬을 배우도록 돕는 것이 목표였습니다. 첫 번째 버전을 출시한 직후, 나는 바로 SQL, JavaScript, 심지어 Rust까지 추가하고 싶었습니다. 가장 명백한 방법은 코드베이스를 복제하고, 로고를 바꾸고, 콘텐츠를 수정한 뒤 새로운 앱을 배포하는 것이었습니다.

시도해 보았습니다: 레포지토리를 복제하고, 몇 가지 이름을 바꾸었으며, 10분도 안 되어 내가 보고 있는 것이 싫어졌습니다.

  • 세 개의 별도 코드베이스
  • 세 개의 별도 버그 집합
  • 스트릭 로직을 고치기 위해 세 번이나 푸시해야 함

솔로 창업자로서는 이것이 로드맵이 아니라, 느린 죽음입니다.

Source:

솔루션: 화이트‑라벨 엔진

다른 앱을 하나 더 만드는 대신, 나는 엔진을 만들었다—무한히 많은 앱으로 컴파일할 수 있는 하나의 코드베이스. Flutter + Riverpod 로 구동되는 화이트‑라벨 교육 플랫폼이다.

UI와 콘텐츠 분리

일반 앱에서는 UI가 너무 많은 것을 알고 있다:

Text("Welcome to Python")

또한 특정 언어만 이해하는 강하게 결합된 파서가 포함되어 있다. 이를 화이트‑라벨화하려면 UI가 완전히 무디(무지)해져야 한다—주어진 것을 그대로 렌더링하기만 하면 된다.

핵심 계약: CourseBlueprint

// 모든 앱 플레버가 충족해야 하는 마스터 계약
abstract class CourseBlueprint {
  String get appTitle;
  String get virtualCurrencyName;

  // Theme Engine
  Color get brandPrimaryColor;
  Color get brandSecondaryColor;

  // Logic Injection
  CodeParserStrategy get codeParser;

  // Per‑app 3rd Party Config
  String get analyticsId;
  String get aiSystemPrompt;
}

각 플레버(Python, SQL, JavaScript, …)는 이 인터페이스를 구현한다. UI는 구체적인 파서를 직접 import 하지 않고, 설정을 통해 제공된 파서를 요청하기만 한다.

예시: Python 플레버

// Python‑전용 구현
class PythonCourseConfig implements CourseBlueprint {
  @override
  String get appTitle => "PyMaster";

  @override
  String get virtualCurrencyName => "Tokens";

  @override
  Color get brandPrimaryColor => const Color(0xFFFFD43B); // Python Yellow

  @override
  Color get brandSecondaryColor => const Color(0xFF306998); // Python Blue

  @override
  CodeParserStrategy get codeParser => PythonCodeParser(); // Handles indentation

  @override
  String get analyticsId => "UA-XXXXX-PYTHON";

  @override
  String get aiSystemPrompt =>
      "You are an expert Python tutor. Explain the error in simple terms.";
}

새로운 플레버(예: SQL)를 출시하려면 SqlCourseConfig 를 만들고, 테마 색상을 바꾸고, SqlKeywordParser 를 주입하고, AI 프롬프트만 업데이트하면 된다.

올바른 설정 선택

컴파일 시 Flutter의 --dart-define 플래그를 사용한다. 작은 팩토리가 플레버를 읽어 적절한 설정을 반환한다:

class BlueprintFactory {
  static CourseBlueprint getForFlavor(String flavor) {
    switch (flavor.toLowerCase()) {
      case 'python':
        return PythonCourseConfig();
      case 'sql':
        return SqlCourseConfig();
      default:
        return PythonCourseConfig(); // Safe fallback
    }
  }
}

흔히 겪는 실수: 빌드 플래그(--dart-define=APP_FLAVOR=sql)를 전달하지 않아 앱이 기본 Python 테마로 돌아가는 경우.

Riverpod 로 전역 접근

Riverpod 은 prop‑drilling 을 없애준다:

// Global provider — overridden at startup
final blueprintProvider = Provider((ref) {
  throw UnimplementedError("Must be overridden in main.dart");
});

void main() {
  const flavor = String.fromEnvironment('APP_FLAVOR', defaultValue: 'python');
  final selectedConfig = BlueprintFactory.getForFlavor(flavor);

  runApp(
    ProviderScope(
      overrides: [
        blueprintProvider.overrideWithValue(selectedConfig),
      ],
      child: const MyEduApp(),
    ),
  );
}

이제 어떤 위젯도 설정을 읽을 수 있다:

Widget build(BuildContext context, WidgetRef ref) {
  final config = ref.watch(blueprintProvider);

  return Text(
    "Welcome to ${config.appTitle}!",
    style: TextStyle(color: config.brandPrimaryColor),
  );
}

UI는 도메인에 전혀 알지 못한다; 단지 전달받은 것을 그대로 렌더링한다.

혜택

  • 단일 진실 원천: 핵심 게이미피케이션 엔진(연속성, XP, 오프라인 진행)에서 버그를 한 번 수정하면, 그 수정이 모든 변형에 전파됩니다.
  • 빠른 반복: 새로운 앱을 시작하는 데 몇 주가 아니라 몇 시간이 걸립니다.
  • 집중된 노력: 이제 내 시간 대부분을 인프라가 아니라 콘텐츠에 할애합니다.

결과

첫 번째 플러버인 PyMaster를 출시했으며, 현재 Google Play 스토어에 라이브 중입니다. 주요 기능은 다음과 같습니다:

  • 게임화된 오프라인 진행
  • 동적 테마 엔진
  • 내장 AI 튜터

토론

비슷한 구조를 설계해 본 적이 있나요? 다음 중 어떤 것을 선택했나요:

  • 앱 플레버
  • 모노레포
  • 플러그인 아키텍처

당신이 겪은 트레이드‑오프가 궁금합니다. 아래 댓글에 의견을 남겨 주세요.

0 조회
Back to Blog

관련 글

더 보기 »