Back to Career

Soundmind · MX Team Lead · 2025.02 - Present

Mohani (Parental Control)

자녀 단말 통제에서 "차단이 안 되는 버그"를 추적하다가 외부 MDM SDK 통합 시점에 생긴 CPU 과부하를 발견, 무거운 Native 작업을 백그라운드 스레드로 분리해 ANR을 해소하고 OS 레벨 MDM + AccessibilityService 다층 방어로 자녀 단말 제어를 안정화했습니다.

PARENTSERVERCHILD DEVICE (OS-LEVEL MDM)모하니 부모앱React Native정책 설정 · 자녀 상태 조회설정 변경 · 푸시 명령모하니 서버Spring Boot정책 저장 · 자녀 단말로 푸시차단 푸시 명령PIN 검증 · Native 직접 HTTP(RN JS 미경유 — 우회 차단)모하니 자녀앱RN + Native · OS-level MDM 라이선스 단말다층 차단 트리거 (5중망)1앱 전환 감지포그라운드 윈도우 변경 즉시2시간 초과 검사주기 폴링 + 슬립타임 사전 경고3백그라운드 앱 감지PIP·음악·녹음 (포그라운드 서비스 카운트)4정책 변경 즉시 반영부모가 설정 바꾸면 자녀 단에서 재검사5원격 차단 명령푸시 단발 명령복합 실행 layer외부 MDM SDK (OS 레벨)+ AccessibilityService (시스템 레벨 강제 종료)Native PIN Gateway접근성 / 기기관리자 / VPN 설정 차단→ 통과 시 10분 graceANR 진단 흐름 (운영 중 발견)① 사용자: "차단이 안 된다" 컴플레인② Logcat 따라가니 백그라운드 서비스가 죽은 게 아니라 main thread 잡힘③ 결정적 단서: 외부 MDM SDK 통합 이후부터 발생 (시간 흐름)④ SDK 호출을 비동기 실행자로 직렬화 + 큐 적체 방지⑤ ANR 제거 + 차단 안정 동작 회복큐 한도 초과 시 호출 스레드에서 직접 실행 (backlog 폭증 방지)정상 명령 — 부모 설정 / 푸시 차단 명령보안 경로 — Native에서 서버 직접 호출 (PIN 검증)★ 다층 트리거 — 단일 트리거로는 우회되는 도메인★ MDM SDK + Accessibility 복합 실행

개요

부모 앱이 자녀 단말의 앱 차단·시간 제한·콘텐츠 차단을 원격 제어하는 제품입니다. 사업부 요구는 들어왔지만 일반 앱 권한으로 가능한지 검증되지 않은 상태였기에, AccessibilityService와 OS 레벨 MDM SDK 조합으로 시스템 레벨 차단이 가능하다는 기술 검증 보고서를 직접 작성해 출시 일정 근거를 만들었습니다. 이후 팀장으로서 자녀앱(RN+Native)·부모앱(RN)·서버(Spring Boot) 세 컴포넌트의 아키텍처를 주도해 설계·구현했습니다. 자녀 단말의 네트워크 단 도메인 차단은 네트워크 레벨 MDM SDK 기능을 별도 레이어로 함께 적용해 앱 단위 차단(AccessibilityService)과 OS 단위 제어 위에 보조 안전망을 두었습니다.

마주한 문제

문제는 두 갈래로 들어왔습니다. 첫째는 ANR이었습니다. "차단이 안 된다"는 컴플레인을 추적하다 main thread가 잡혀 차단 로직 자체가 돌지 못하고 있음을 확인했고, 원인 추적 기준점은 시점 정보였습니다. 원래 없던 증상이 외부 MDM SDK 통합 이후부터 발생했기 때문입니다. 둘째는 우회 경로였습니다. PIP·음악·녹음처럼 화면 없이 도는 백그라운드 앱은 화면 전환 이벤트가 발화하지 않아 차단 자체가 작동하지 않았습니다.

  • 컴플레인 패턴: "차단이 안 된다" — 백그라운드 서비스 사망이 아니라 main thread pin
  • 결정적 단서: 외부 MDM SDK 통합 이후부터 증상 발생 (변경 이력 기반 시점 단서)
  • 우회 경로: PIP·음악 재생·녹음 앱처럼 화면 없는 백그라운드 앱은 화면 전환 이벤트가 발화하지 않음

풀이 가설

"변경 이력에서 가장 가까운 원인부터 의심한다"는 원칙으로 외부 MDM SDK 통합 시점을 강한 후보로 잡았고, 그 SDK 호출이 main thread를 잡고 있을 가능성을 가설로 설정했습니다.

검토한 대안

systrace, Perfetto, Crashlytics ANR thread dump는 정확도는 높지만 셋업과 학습 곡선 부담이 컸습니다. 시점 단서가 명확했기 때문에 Android Studio Logcat과 AI 코드 분석 보조를 조합해 더 가볍게 접근했습니다.

진단 도구정확도비용
systrace / Perfetto높음 — main thread block 시점 정확히 포착셋업 시간·학습 곡선 부담 (시점 단서가 명확해 불필요)
Firebase Crashlytics ANR thread dump높음 — 사용자 단말의 실제 ANR stack 확보사전 셋업 필요 — 사고 시점에는 미구비 (회고 항목)
★ Android Studio Logcat + AI 코드 분석 보조시점 단서가 명확할 때 충분낮음 — 기존 도구로 즉시 시작 가능

선택과 근거

무거운 Native 작업을 백그라운드 스레드로 분리했습니다. 외부 MDM SDK 호출을 비동기 실행자로 옮겨, main thread를 잡고 있던 경로를 끊었습니다. 차단 자체도 단일 트리거로는 우회되는 도메인이라 다층 방어를 함께 짰습니다.

구현과 시행착오

먼저 외부 MDM SDK 호출을 비동기 실행자로 옮겨 메인 스레드를 잡고 있던 경로를 끊었고, 그 과정에서 호출이 폭주하면 큐가 무한히 쌓이는 후속 문제가 확인돼 큐 적체 방지 장치를 함께 넣었습니다. 우회 차단은 처음에는 화면 전환 이벤트 트리거만으로 처리했지만 PIP·음악·녹음 같이 화면 없는 앱은 그 이벤트가 발화하지 않아, 포그라운드 서비스 시작·종료 카운트를 차단 판단에 더했습니다. 시간 안전망은 메인 폴링과 보조 폴링 이중화, 부모 정책 변경은 푸시 알림 즉시 발사와 보조 동기화로 단일 실패 지점이 없도록 묶었습니다.

  • ① 앱 전환 감지 — Accessibility 이벤트 기반 (포그라운드 앱 전환 캡처)
  • ② 시간 초과 검사 — 주기 폴링 (사용 시간 누적 만료 감지)
  • ③ 백그라운드 앱 감지 — 포그라운드 서비스 시작·종료 카운트 추적 (PIP·음악·녹음 등 화면 없는 앱 우회 차단)
  • ④ 정책 변경 즉시 반영 — 로컬 설정 변경 리스너 (부모 정책 변경이 자녀 단말에 즉시 적용)
  • ⑤ 원격 차단 명령 — 푸시 알림 기반 (부모가 즉시 발사하는 원격 차단)
  • ★ 외부 MDM SDK 호출은 비동기 실행자로 직렬화 + 큐 적체 방지로 main thread pin 차단

성과

반복 발생하던 ANR이 제거되고 다층 방어가 정상 운영되면서, 단일 트리거로는 잡히지 않던 화면 없는 앱(PIP·음악·녹음)도 함께 차단되게 됐습니다. 사용자 측에서는 "차단이 안 된다"는 컴플레인이 사라졌고, 운영 측에서는 외부 SDK 큰 변경 후에도 회귀가 잡히는 구조가 만들어졌습니다.

다층 방어
하나가 우회당해도 나머지가 잡는 구조 — 앱 전환 감지·주기 폴링·화면 없는 앱 감지·정책 변경 즉시 반영·원격 차단 명령
이중 안전망
차단 누락 지연 — 메인 폴링이 돌고 실패해도 보조 폴링이 한 번 더 잡고, 부모 정책 변경은 푸시 즉시 + 보조 동기화로 이중화
PIP·음악·녹음
화면 없는 앱도 차단 — 포그라운드 서비스 시작·종료 카운트 추적으로 단일 트리거가 못 잡던 우회 경로 봉쇄