실무

PLEY — 아동 행동패턴 연구 앱 재설계

Backend · Android Engineer · 2024 · 3 min read 읽기

클라이언트(앱)에 종속돼 실패하던 웨어러블 토큰 갱신 처리를 서버사이드 스케줄러로 이관해, 오프라인·저전력 환경에서 무너지던 데이터 수집 성공률을 0%에서 90%이상으로 끌어올려 연구 실험 자체를 성립시켰습니다

Overview

아동의 올바른 스마트폰 사용을 유도하기 위한 행동패턴 연구용 모바일 앱 PLEY의 백엔드·안드로이드 개선을 담당했습니다. 국내 대학 소속 아동 연구팀과 협업해 진행한 연구용 앱으로, 전임 담당자가 개인 사정으로 중도 하차하며 인수인계 없이 넘겨받았습니다. 코드베이스 구조 분석부터 시작해 겉으로는 유지보수처럼 보였지만, 실제로는 '특정 기능을 어느 계층에 배치했는가'라는 시스템 아키텍처 자체의 문제를 안고 있었습니다.

System Architecture

PLEY 시스템 구성도 — Fitbit 연동, Android/Node.js 서버, Room·PostgreSQL 데이터 흐름과 개선 전후(AS-IS/TO-BE) 비교
PLEY 시스템 구성도 — Fitbit 연동, Android/Node.js 서버, Room·PostgreSQL 데이터 흐름과 개선 전후(AS-IS/TO-BE) 비교

Constraints

  • 진행 중인 연구의 기존 데이터 스키마와 토큰 관리 RDB(PostgreSQL) 구조를 유지해야 함
  • 인수인계 없이 넘겨받은 레거시 코드베이스 — 구조 분석부터 선행 필요
  • 아동 단말은 저전력 정책·간헐적 네트워크 등 통제 불가능한 실사용 환경
  • On-premise 백엔드 인프라 위에서 운영 (외부 클라우드 미사용)
  • 다음 연구 코호트 일정에 맞춘 개선 납기

Tech Stack

  • Node.js
  • PostgreSQL
  • Crontab (On-premise Scheduler)
  • Android (Native)
  • Wearable Vendor API (걸음 수 연동)
  • OAuth Token (access/refresh)

Learnings

  • 특정 기능을 클라이언트/서버 어느 쪽에 두든, 그 기능의 제약조건과 경우의 수를 구체적으로 이해한 뒤 배치를 결정해야 한다
  • '앱이 활성일 때만 동작'처럼 암묵적 전제를 잡으면 정상처럼 보이지만, 실사용 패턴(저전력 종료·간헐 연결)이라는 경우의 수를 넣는 순간 무너진다
  • 인수인계 없이 넘겨받은 레거시는 증상(데이터 유실)이 아니라 아키텍처의 기능 배치에서 근본 원인을 찾아야 한다
  • 실험 설계(실험군/대조군)를 성립시키려면 UI 스레드 이슈 같은 클라이언트 디테일까지 연구 조건의 일부로 다뤄야 한다

Problem

아동 참여자의 웨어러블 기기(걸음 수)를 앱 사용 시간과 연동하는 실험이었습니다. 걸음 수에 비례해 앱 사용 시간이 적립되고, 실험군은 잔여 시간을 UI에서 볼 수 없는 반면 대조군은 Progressbar로 즉시 확인할 수 있게 해, 현황 인지 여부가 행동에 미치는 영향을 비교하는 설계였습니다. 핵심 장애는 웨어러블 벤더 API 호출에 필요한 access token의 갱신을 클라이언트(앱)에서 처리하도록 되어 있던 점입니다. 앱이 활성 상태일 때만 갱신이 돌기 때문에, 아동 단말이 게임 등 고부하 앱 실행 시 저전력 정책으로 백그라운드에서 종료되거나 장시간 미사용·간헐적 Wi-Fi 연결에 머무는 실제 사용 패턴에서는 토큰 갱신이 누락됐습니다. 그 결과 건강 데이터 취득과 적재가 연쇄적으로 실패해 데이터 수집 성공률이 사실상 0%였습니다.

Approach

토큰 갱신을 '앱이 활성일 때만 동작'하는 클라이언트 로직에서 분리해, 단말 상태와 무관하게 항상 도는 서버사이드 스케줄러로 이관했습니다. On-premise 백엔드 API 서버에서 Crontab으로 DB에 저장된 사용자별 토큰을 정기적으로 유효성 검사·갱신하도록 재배치했습니다. 병행해 안드로이드 앱의 대조군 Progressbar가 간헐적으로 표시되지 않던 문제를 UI Thread와 로직 Thread 분리 및 GC 처리 개선으로 해결했습니다.

Key Decisions

웨어러블 토큰 갱신을 클라이언트(앱)에서 서버사이드 백엔드로 이관

Reasoning:

토큰 정보는 RDB에서 관리됐지만 실제 갱신 처리는 앱에서 수행돼, 앱이 활성 상태여야만 갱신이 돌았습니다. 아동 단말의 저전력 앱 종료·간헐적 연결이라는 실사용 경우의 수에서 이 전제가 무너져 데이터 수집이 실패했습니다. 사용자 단말 상태에 종속되지 않는 On-premise 백엔드에서 Crontab으로 정기 갱신하도록 옮겨, 단말이 오프라인이어도 서버가 토큰 유효성을 확보하게 했습니다.

Alternatives considered:
  • 클라이언트 갱신 유지 (단말 배터리·서버 부하는 아끼지만 가용성 희생)
  • 앱 백그라운드 실행 정책 우회 등 클라이언트 측 보완

토큰(access/refresh)의 사용자별 관리는 기존 RDB(PostgreSQL) 유지

Reasoning:

사용자별 토큰 상태의 정합성을 보장하고, 서버 스케줄러가 단일 소스에서 조회·갱신하도록 하기 위함입니다. 갱신 '처리'만 서버로 옮기고 '저장소'는 그대로 두어 마이그레이션 비용 없이 이관했습니다.

Alternatives considered:
  • 토큰 저장소를 별도 캐시/스토어로 재설계
  • 사용자 단위 분산 저장
  • 사용자별 토큰 상태의 정합성을 보장하고, 서버 스케줄러가 단일 소스에서 조회 및 갱신하도록 하기 위해서 였으며, 연구용 앱이라 참가자 수가 소규모라 애초에 샤딩에 필요한 트래픽/데이터 규모가 아니었다.

대조군 Progressbar 누락 → 안드로이드 UI/로직 Thread 분리로 해결

Reasoning:

대조군에서 시각적으로 노출돼야 할 잔여 시간 Progressbar가 간헐적으로 표시되지 않았습니다. UI Thread와 로직 실행 Thread 간 처리 충돌이 원인이라, 스레드 처리를 분리하고 GC 처리를 개선해 실험 조건(대조군의 현황 인지)이 안정적으로 성립하도록 했습니다.

Alternatives considered:
  • UI 갱신 주기 단순 조정 등 표면적 대응

Result & Impact

  • 0% → 90% (실험 성립)
    데이터 수집 성공률
  • 최대 30회 → 3회 미만 (약 90%↓)
    토큰 조회 이슈
  • 기능 100% / 성능 약 80~90%↑
    기능 개선 / 성능

클라이언트에 종속돼 사실상 데이터가 쌓이지 않던 실험이, 이관 이후 참여 아동 대부분에서 정상적으로 데이터가 수집되며 연구 실험 자체가 성립됐습니다. 단순 유지보수가 아니라 기능 배치를 바로잡아 실패하던 실험을 살린 사례입니다.