[개발 일지] 앱이 꺼져 있어도 놓치지 않게: 감지를 두 겹으로 만든 이유

새 파일을 놓치지 않기 위해 감지 방법을 두 가지로 병행한 결정을 나타낸 대표 이미지
하나만 쓰면 앱이 꺼져 있을 때 놓칩니다.

“새 파일이 생기면 자동으로 처리한다”는 말은 쉬운데, 구현하면 조건이 붙습니다. 앱이 켜져 있어야 하고, 화면이 떠 있어야 하고, 시스템이 앱을 재우지 않아야 합니다. 저는 이 조건들 때문에 감지 방법을 두 가지로 나눠 함께 쓰기로 했습니다. 이 글은 그 이유에 대한 기록입니다.

첫 번째 방법만으로는 부족했다

가장 단순한 방법은 앱이 켜져 있는 동안 변화를 지켜보는 것입니다. 구현도 쉽고 반응도 빠릅니다. 문제는 앱이 화면에 없을 때입니다.

요즘 운영체제는 배터리를 아끼려고 앱을 적극적으로 재웁니다. 그러면 지켜보던 것도 같이 멈춥니다. 사용자는 앱을 켜 뒀다고 생각하는데 실제로는 아무것도 안 보고 있는 상태가 됩니다. 가장 나쁜 형태의 실패입니다. 조용히 안 되기 때문입니다.

앱이 켜져 있을 때의 감지와 꺼져 있을 때를 대비한 예약 감지를 함께 쓰는 구조
두 경로가 서로의 빈 구간을 메웁니다.

두 번째 방법을 겹쳤다

그래서 시스템에 미리 맡겨 두는 방식을 함께 넣었습니다. “이 위치에 변화가 생기면 나를 깨워 달라”고 등록해 두는 것입니다. 앱이 꺼져 있어도 시스템이 대신 지켜보다가 깨웁니다.

여기에 하나 더 붙였습니다. 기기를 껐다 켜면 등록이 풀리므로, 부팅 후에 다시 등록하는 절차를 넣었습니다. 이걸 빠뜨리면 재부팅 이후로 조용히 동작이 멈춥니다.

과거 파일까지 처리하지 않도록

여기서 예상 못 한 문제가 하나 있었습니다. 처음 권한을 허용한 순간, 기기에 이미 쌓여 있던 과거 파일이 전부 새 파일로 보입니다. 그대로 두면 앱을 깔자마자 수백 건이 한꺼번에 처리됩니다.

그래서 권한을 허용한 시점을 기준선으로 저장하고, 그 이후에 생긴 것만 처리하도록 했습니다. 과거 파일은 사용자가 직접 고를 때만 처리합니다.

즉시 감지, 예약 감지, 부팅 후 재등록, 기준선 저장의 네 가지 요소
네 가지가 모두 있어야 “자동”이 실제로 자동이 됩니다.

정리하면

자동 감지는 “되게 만드는 것”보다 “안 될 때를 메우는 것”이 대부분의 일이었습니다. 앱이 꺼져도, 재부팅해도, 처음 켰을 때도 예상대로 동작해야 자동이라고 부를 수 있습니다.

실제 기기에서 장시간 두었을 때 얼마나 안정적으로 깨어나는지는 아직 측정하지 못했습니다. 이 부분은 미검증으로 남겨 두었습니다.

안드로이드 공식 문서로 보는 “꺼져도 이어지는 작업”

아래는 앱의 구현 플랫폼이 안드로이드라고 가정했을 때의 공식 설명입니다. 안드로이드 개발자 문서는 앱이 화면에서 사라진 뒤에도 이어져야 하는 작업에 WorkManager 라이브러리를 권장합니다. 문서에 따르면 WorkManager는 예약된 작업을 내부 SQLite 데이터베이스에 저장하고, 앱이 다시 시작되거나 기기가 재부팅되어도 작업이 유지되고 다시 예약되도록 처리합니다. 전원 절약 기능인 Doze 모드도 따릅니다. 이 설명은 위의 “부팅 후 다시 등록” 문제와 직접 이어집니다. 예약을 직접 만들었다면 재부팅 뒤 복구도 직접 책임져야 하고, 이런 라이브러리를 쓰면 그 부분을 맡길 수 있다는 뜻입니다.

같은 문서는 반대로 코루틴은 앱이 닫히면 곧바로 멈춘다고 설명합니다. 앱이 켜져 있는 동안만 지켜보는 첫 번째 방법이 왜 한계가 있는지를 보여 주는 대목입니다.

작업 유형공식 문서의 설명
즉시(Immediate)바로 시작해 곧 끝나야 하는 작업. 빠르게 처리해야 하면 expedited로 지정할 수 있음
오래 걸리는 작업(Long Running)10분을 넘길 수 있는 작업. 작업 안에서 setForeground()를 호출해 알림을 처리함
미룰 수 있는 작업(Deferrable)나중에 시작하고 주기적으로 반복할 수 있는 작업. PeriodicWorkRequest 사용

조건을 걸면 실행 시점도 달라진다

작업 정의 문서에는 실행 조건(Constraints)이 소개됩니다. 네트워크 종류, 배터리 부족 아님, 충전 중, 기기 유휴 상태, 저장 공간 부족 아님의 다섯 가지이며, 여러 조건을 걸면 모두 충족될 때만 작업이 실행됩니다. 실행 중에 조건이 깨지면 WorkManager가 작업을 멈추고, 조건이 다시 맞을 때 재시도합니다. 주기 작업은 조건 때문에 특정 회차가 늦춰지거나 건너뛰어질 수 있다고도 적혀 있습니다. 첫 실행을 늦추는 setInitialDelay도 소개하며, 정확한 실행 시각은 조건과 시스템 최적화에 따라 달라진다고 덧붙입니다.

이 설명은 글 끝의 “장시간 두었을 때 얼마나 안정적으로 깨어나는지는 측정하지 못했다”는 한계와 연결됩니다. 시스템이 실행 시점을 조정하는 것이 정상 동작이므로, “정확히 몇 초 안에 처리된다”를 약속하는 설계는 맞지 않습니다. “언젠가 빠짐없이 처리된다”를 목표로 두는 편이 공식 설명과 맞습니다.

다만 WorkManager의 시작 안내 문서와 작업 정의 문서에는 새 파일이 생겼을 때 작업을 깨우는 콘텐츠 변경 감지(content URI 트리거) 방법이 나와 있지 않았습니다. 그래서 이 글은 그 부분을 공식 문서 기준으로 설명하지 않습니다. 사용하는 플랫폼에서 지원하는 변경 감지 방식은 해당 플랫폼의 공식 문서에서 따로 확인해야 합니다.

시스템이 앱을 재우는 방식: Doze

안드로이드 오픈소스 프로젝트(AOSP) 문서는 Doze를 기기를 오래 쓰지 않을 때 앱의 백그라운드 CPU와 네트워크 활동을 미루는 기능으로 설명합니다. 기기가 가만히 놓인 채 화면이 꺼진 시간이 이어져야 들어가고, 충전 중에는 작동하지 않습니다. 절전 상태에서는 네트워크 접근이 막히고, 알람은 미뤄지며, JobScheduler 작업은 다음 ‘유지보수 구간’까지 기다립니다. 유지보수 구간은 주기적으로 열려 앱이 밀린 일을 처리하게 해 주고, 절전이 반복될수록 구간 사이의 간격은 길어져 최대 몇 시간까지 늘어납니다. 사용자가 기기를 만지거나 움직이거나 화면을 켜면 빠져나옵니다.

이 설명은 앞에서 말한 “앱을 켜 뒀다고 생각하는데 실제로는 아무것도 안 보고 있는 상태”가 왜 생기는지 알려 줍니다. 앱이 잘못된 것이 아니라 시스템이 의도한 대로 일을 미룬 결과일 수 있습니다. 그래서 감지가 늦어지는 경우와 아예 놓치는 경우를 구분해서 기록해야 합니다.

두 방법을 함께 쓸 때의 점검표

상황첫 번째 방법(앱이 켜져 있을 때)두 번째 방법(시스템에 맡김)점검할 것
앱이 화면에 있음즉시 감지대기같은 파일이 두 번 처리되지 않는가
앱이 꺼져 있음동작하지 않음시스템이 깨움깨어난 뒤 놓친 파일을 따라잡는가
기기를 재부팅함동작하지 않음다시 등록이 필요한 방식인가 확인재등록이 실제로 일어나는가
처음 권한을 허용함이미 있던 파일이 전부 새 파일로 보임같음허용 시점의 기준선이 저장되는가

두 경로가 같은 파일을 모두 잡을 수 있으므로, 처리한 파일의 목록을 남겨 중복을 걸러 내는 단계도 필요합니다. 이 점은 위 표의 첫 줄처럼 “겹쳐서 쓰면 중복이 생긴다”는 구조적 대가입니다. 놓치는 것보다 겹치는 쪽이 낫지만, 겹침을 정리하는 규칙이 없으면 같은 파일이 두 번 처리됩니다.

확인 자료