1243 words in content
6 minutes for read
사용자 상호작용을 위한 Native Home Widget 개발

들어가며…#

E&I 프로젝트를 진행하며 푸시 알림, 사용자 채팅이 만들어진 MVP에 GA를 적용한 이후 사용자들의 참여도가 떨어지는 것을 확인했다. 이 부분에 있어 팀원과의 회의와 베타 테스터 설문 조사를 통해 다음과 같은 문제점이 있는 것을 확인했다.

  1. 앱을 열지 않고도 사용자들이 참여할 수 있는 방법이 필요하다.
  2. 방해금지 기능, 알림 설정 등으로 인해 푸시 알림이 제대로 전달되지 않는 경우가 있다.

즉, 앱 내에서만 사용자와 상호작용이 이루어져, 재방문율과 참여도를 높이는 데 한계가 있다. 즉, 사용자와의 소통을 중시한다는 역할에서 제 역할을 하지 못하고 있었다.

Home Widget이란#

Home Widget은 사용자가 앱을 실행하지 않고도 주요 정보를 확인하거나 즉시 행동할 수 있게 하는 인터페이스 요소로 스마트폰의 홈 화면에 상주한다는 점에서, 앱의 진입 장벽을 낮추고 일일 사용자 점유율(DAU)을 높이는 핵심 수단으로 활용된다.

특히 최근에는 단순히 ‘정보 표시’ 수준을 넘어, 다음을 통해 사용자 경험(UX)을 확장하고 있다.

  • 앱 재방문 유도(리텐션 향상)
  • 즉각적인 상호작용(인터랙션 강화)
  • 개인화된 콘텐츠 노출(맞춤형 피드백 제공)

즉, 홈 위젯은 ‘보조 수단’이 아니라 앱의 일부 기능을 홈 화면으로 확장한 마이크로 인터페이스(Micro-Interface)로서, 앱과 사용자의 접점을 더 촘촘히 만들어 점유 시간을 늘리는 중요한 구성 요소이다.

Earth & I 에서는?#

게이미피케이션의 요소 중 “많이 보일수록 참여한다”라는 점에서 알림은 부족하다는 의견이 나왔고, 새롭게 할 수 있는 것이 무엇이 있을까 생각 중 홈의 캘린더 위젯이 눈에 보였다. 계속 보인다는 점에서 매력이 있었고, 앱을 보지 않더라도 캐릭터의 상태를 나타낼 수 있기에 Home Widget을 만들기로 결정했다.

Flutter에서의 Widget 개발#

이를 위해 Flutter의 Home Widget Library를 활용하기로 했다. 하지만, 기본적으로 Library를 사용하더라도 Home Widget은 Native Code로 작성해야 하기에 Flutter에서는 Method Channel을 통해 Native Code를 호출하였다.

abstract class WidgetUtil {
  static const String appGroupIdentifier = 'group.earthAndI.userInfoWidget';
  static const String iOSWidgetName = 'userInfoWidget';
  static const String androidWidgetName = 'UserInfoWidget';

  static Future<void> onInit() async {
    await HomeWidget.setAppGroupId(appGroupIdentifier);
  }

  static void setInformation({
    required double positiveDeltaCO2,
    required double negativeDeltaCO2,
    required bool isHealthCondition,
    required bool isMentalCondition,
    required bool isCashCondition,
  }) {
    // Saving Character Asset
    HomeWidget.saveWidgetData<String>(
      WidgetUtilExtension.characterAsset,
      _toCharacterAssetPath(
        positiveDeltaCO2: positiveDeltaCO2,
        negativeDeltaCO2: negativeDeltaCO2,
        isHealthCondition: isHealthCondition,
        isMentalCondition: isMentalCondition,
        isCashCondition: isCashCondition,
      ),
    );

    // Saving Positive Delta CO2
    // 안드로이드 아이폰 경우의 수 나누기, 안드로이드는 int 값으로, 아이폰은 double 값으로 저장
    if (foundation.defaultTargetPlatform == foundation.TargetPlatform.android) {
      HomeWidget.saveWidgetData<int>(WidgetUtilExtension.positiveDeltaCO2,
          (positiveDeltaCO2.abs() * 10000).round());
    } else {
      HomeWidget.saveWidgetData<double>(
          WidgetUtilExtension.positiveDeltaCO2, positiveDeltaCO2.abs());
    }

    // Saving Negative Delta CO2
    if (foundation.defaultTargetPlatform == foundation.TargetPlatform.android) {
      HomeWidget.saveWidgetData<int>(WidgetUtilExtension.negativeDeltaCO2,
          (negativeDeltaCO2 * 10000).round());
    } else {
      HomeWidget.saveWidgetData<double>(
          WidgetUtilExtension.negativeDeltaCO2, negativeDeltaCO2);
    }

    // Update Widget
    HomeWidget.updateWidget(
      iOSName: iOSWidgetName,
      androidName: androidWidgetName,
    );
  }

  static String _toCharacterAssetPath({
    required double positiveDeltaCO2,
    required double negativeDeltaCO2,
    required bool isHealthCondition,
    required bool isMentalCondition,
    required bool isCashCondition,
  }) {
    String eco = positiveDeltaCO2.abs() >= negativeDeltaCO2 ? '1' : '2';
    String health = isHealthCondition ? '1' : '2';
    String mental = isMentalCondition ? '1' : '2';
    String cash = isCashCondition ? '1' : '2';

    return '${eco}_${health}_${mental}_$cash';
  }
}

결과#

아래는 만들어진 Widget의 모습이다.

AndroidiOS
WidgetWidget

이후 개발된 홈 위젯을 테스트 사용자 그룹에 배포한 결과, 위젯이 들어간 버전을 Update한 이후 사용자들의 12% 높아진 것을 확인할 수 있었다.

나오며…#

이번 홈 위젯 개발은 단순한 기능 추가를 넘어, **‘앱 밖에서도 사용자와 소통할 수 있는 방법’**을 고민한 경험이었다. 특히 푸시 알림에만 의존하던 기존 방식에서 벗어나, 사용자가 직접 앱을 실행하지 않아도 참여할 수 있는 지속적인 접점을 만들었다는 점에서 의미가 있었다.

기술적으로는 Flutter 환경에서 Native 코드를 호출해야 하는 제약 속에서도, 플랫폼별 데이터 처리 차이(Android: int / iOS: double), AppGroup 설정 및 MethodChannel 통신 구조, 위젯 갱신 시점 관리(HomeWidget.updateWidget) 등을 직접 다루며 Flutter와 Native의 경계에 대한 이해를 넓힐 수 있었다.

무엇보다 이 경험을 통해 느낀 점은, “사용자의 행동을 유도하는 것은 기술이 아니라 노출 빈도와 맥락이다.” 즉, 사용자가 앱을 떠나 있는 순간에도 지속적으로 관계를 유지할 수 있는 UX 설계가 중요하다는 것이다.