프로젝트 목록

(주)사운드마인드

모하니 자녀 스마트폰 사용 관리

부모의 제어 명령이 푸시로 전달되는 서비스입니다. 푸시는 유실되고, 자녀는 권한을 끕니다. 그 두 상황에서도 통제가 유지되게 만드는 일이었습니다.

역할
서버 · 자녀앱 · 부모앱
기간
2025.07 ~ 현재
사용 기술
Spring BootReact NativeAndroidSamsung Knox SDKFCMRedis
앱 / 클라이언트네이티브서버워커저장소외부
제어 명령은 푸시로 가지만 푸시를 믿지 않습니다. 단말이 설정 버전을 폴링해 스스로 따라잡고, 접근성이 꺼지면 감시 서비스가 차단을 이어받습니다.블록에 마우스를 올리면 연결된 흐름만 강조됩니다. 드래그로 이동, 버튼으로 확대할 수 있습니다.

개선 결과

  • 버전 카운터

    푸시 유실 시 단말이 스스로 따라잡는 복구 장치

  • 이중화

    접근성 권한이 꺼져도 차단이 유지되는 구조

01문제

부모가 앱 차단이나 수면 시간을 설정하면 그 명령이 푸시로 자녀 단말에 갑니다. 그런데 푸시는 보장이 없습니다. 유실되면 부모 화면에는 차단이 걸려 있는데 단말에서는 아무 일도 일어나지 않은 상태가 됩니다. 다른 축의 문제도 있었습니다. 자녀가 접근성 권한을 꺼버리면 차단 자체가 무력화됩니다. 그리고 푸시가 사라지는 것은 우연이 아닙니다. 화면이 꺼진 단말은 절전 모드(도즈)로 내려가 푸시 전달을 늦추거나 묶어서 배달하고, 백그라운드 폴링도 같은 이유로 눌립니다. 명령이 도착한다는 보장이 없는 채널 위에서 통제를 설계해야 했습니다.

02결정

푸시를 신뢰하지 않는 쪽으로 설계를 바꿨습니다. 설정이 바뀔 때마다 자녀별 버전 번호를 DB에서 원자적으로 올리고, 단말은 주기적으로 그 번호를 비교해 뒤처졌으면 스스로 최신 설정을 받아옵니다. 푸시는 그 확인을 앞당기는 신호일 뿐입니다. 권한 문제는 별도 감시 서비스로 이중화해서, 접근성이 꺼지면 사용 기록을 주기적으로 확인해 비허용 앱을 계속 차단합니다.

  • 설정 버전 카운터를 원자적으로 증가시키고 단말이 비교·자가 복구
  • 도즈에서 폴링이 눌리는 것을 전제로, 화면 켜짐·앱 전환·부팅·푸시 수신 같은 깨어나는 순간마다 버전 확인과 정책 재평가를 얹어 상시 폴링 없이도 따라잡게 함
  • 접근성이 꺼지면 감시 서비스가 사용 기록 기반으로 차단을 지속
  • 구버전 번들에는 서버가 응답 헤더로 세대를 식별해 강제 업데이트 유도
  • 배포는 코드를 올리면 자동으로 트래픽 전환까지 이어지는 파이프라인으로 구성

03설치 첫날의 관문

이 제품의 통제는 앱을 깐다고 시작되지 않습니다. 자녀가 단말을 받고 온보딩을 마쳐야 정책이 걸리는데, 그 사이가 가장 헐겁습니다. 권한을 하나라도 건너뛰면 차단이 반쪽이 되고, 제조사 관리 라이선스가 활성화되지 않으면 방화벽이나 앱 비활성화 같은 강한 수단이 통째로 빠집니다. 그래서 온보딩을 안내가 아니라 통과하지 못하면 단말을 쓸 수 없는 게이트로 만들었습니다.

  • 권한 요청을 단계로 나누고, 각 단계에서 실제로 부여됐는지 시스템에 다시 확인한 뒤에만 다음으로 넘어가게 했습니다. 화면을 넘겼다는 사실과 권한이 있다는 사실은 다릅니다
  • 온보딩이 끝났는데 관리 라이선스가 비활성이면 화면 전체를 덮는 오버레이로 단말을 봉쇄하고 재활성화를 유도합니다. 상시 감시 대신 화면 켜짐·앱 전환·부팅·라이선스 결과 같은 깨어나는 순간에만 다시 판정합니다
  • 다만 그 커버가 전화와 문자를 막으면 안 됩니다. 통화 앱이 앞에 있거나 벨이 울리는 동안에는 커버를 즉시 내려 위급 통신을 보장합니다
  • 제조사 정책상 별도 허용이 필요한 기능이 있어, 제조사에 허용 요청을 넣고 승인받는 절차까지 도입 과정에 포함시켰습니다

04설치 후에 열리는 우회 루트들

운영에 들어가면 아이들은 빠르게 빈틈을 찾습니다. 차단을 정면으로 뚫기보다 차단이 서 있는 발판을 치우는 쪽인데, 그 발판이 전부 안드로이드의 권한 모델과 제조사 정책 위에 있습니다. 그래서 우회가 보고될 때마다 그것이 안드로이드의 어떤 동작을 이용한 것인지부터 확인하고, 같은 계층에서 막을 수단을 찾았습니다. 응용 계층에서 임시로 덮으면 다음 우회가 바로 나옵니다.

우회 시도와 대응 계층

우회가 들어온 계층과 같은 곳에서 막습니다

  1. 접근성 권한 끄기

    차단 판정이 접근성 이벤트에 의존하니 권한을 끄면 통째로 무력화됩니다. 별도 포그라운드 서비스를 데드맨 스위치로 두어, 접근성이 꺼지면 유예 후 사용 기록을 1초 주기로 훑어 비허용 앱을 계속 정지시킵니다. 화면 분할·팝업·PIP처럼 앞에 있는 것이 하나가 아닌 경우까지 포함합니다

  2. 브라우저의 암호화 DNS

    도메인 차단이 DNS 조회에 기대는데, 크로미엄 계열 브라우저가 암호화된 경로로 조회해 차단을 지나가는 것을 실측했습니다. 개별 도메인 목록과 분리된 전역 차단 목록을 서버에서 내려받도록 하고, 브라우저 앱 자체를 통제 대상에 올렸습니다

  3. 인앱 브라우저

    차단된 사이트를 다른 앱 안의 웹뷰로 여는 경로가 있어, 앱 단위가 아니라 화면에 떠 있는 내용 기준으로도 판정하도록 했습니다

  4. 시계 되돌리기

    수면 시간 정책이 단말 시각을 기준으로 하니 시각을 바꾸면 풀립니다. 제조사 정책으로 시각 변경 자체를 잠갔습니다

  5. 설정 앱 우회

    권한과 앱 관리 화면으로 들어가는 경로를 제조사 정책의 설정 봉쇄로 막았습니다

  6. 앱 강제 종료

    죽였다가 다시 켜지는 사이를 노립니다. 로그인 토큰을 네이티브 저장소에 이중으로 두어 재기동 직후에도 정책을 즉시 복원하고, 중요한 원격 명령은 앱 화면 코드를 거치지 않고 네이티브에서 바로 처리해 앱이 죽어 있어도 도착합니다

  7. 패키지 위장과 재설치

    이름만 다른 같은 앱들을 하나의 보호 묶음으로 묶어, 개별 대응이 아니라 묶음 단위로 판정합니다

05결과

푸시가 유실돼도 단말이 스스로 따라잡기 때문에 부모 화면과 단말 상태가 어긋난 채 남는 문제가 사라졌습니다. 권한을 꺼서 차단을 피하는 경로도 막혔고, 서버 상태와 로그는 모니터링으로 한곳에서 확인합니다.