2026년 10월 31일 토요일

[안드로이드] PC의 사진을 케이블 한 번으로 액자에 보내기 — 동기화 도구를 바이브코딩으로 만들기 (액자 시리즈 3편)


[안드로이드] PC의 사진을 케이블 한 번으로 액자에 보내기 — 동기화 도구를 바이브코딩으로 만들기

서두

지난 글에서 액자 앱까지 완성했다. 그런데 앱은 어디까지나 기기에 저장된 사진을 보여줄 뿐이다. 새 사진은 어떻게 들어가나?

선택지를 정리하면 이렇다:

방법자동화계정문제
구글포토 연동완전 자동필요2025년 4월 구글이 개인 앱의 라이브러리 읽기 API를 차단 — 연동 앱들이 대량 사망
Syncthing류 폴더 동기화완전 자동불필요폰에 앱 설치·페어링 필요, 폰의 로컬 폴더만 가능
탐색기 드래그앤드롭수동불필요매번 어떤 파일이 새 건지 사람이 기억해야 함
케이블 + 자작 도구반자동불필요이 글의 주인공

나는 PC의 특정 폴더에 사진을 넣고, 케이블 연결 후 아이콘 더블클릭 한 번이면 끝나는 방식을 택했다. 구글포토에서 내려받은 사진, 폰에서 옮긴 사진, 카메라 사진 — 어디서 온 것이든 결국 PC 폴더에 모이니까, 그 폴더만 액자로 흘려보내면 된다.

요구사항은 네 가지:

  1. 증분 동기화 — 이미 보낸 사진은 건너뛰고 새 것만 (매번 300장을 다시 보내면 몇 분 걸린다)
  2. 한글 파일명 생존 — 우리나라 가족 사진은 이름에 한글이 있다
  3. 즉시 반영 — 복사 후 액자에 바로 나타나야 한다
  4. 안전장치 — "전체 삭제" 같은 위험한 동작엔 확인 절차

STEP 1. 기본 동기화 스크립트

프롬프트 1

파워셸 스크립트를 만들어줘. 기능:
- C:\액자사진 폴더의 이미지(jpg/jpeg/png/webp, 하위폴더 포함)를
  adb push로 태블릿 /sdcard/PhotoFrame 폴더로 복사
- adb는 C:\android-sdk\platform-tools\adb.exe
- 시작 시 adb devices로 태블릿 연결을 확인하고,
  연결 안 됐으면 "케이블 연결 + USB 디버깅 허용" 안내 후 종료
- 진행 상황을 콘솔에 한국어로 출력

바이브코딩으로 만든 도구는 30줄이면 충분하다. 그런데 이대로는 두 가지가 부족하다 — 새 파일 판별과, 복사 후 액자가 눈치채는 것.

STEP 2. 증분 동기화 — "이미 보낸 것"을 기억하기

프롬프트 2

증분 동기화를 추가해줘:
- .pushed.tsv 파일에 (보낸 파일명, 크기)를 탭으로 기록
- 실행 시 소스 폴더의 파일을 (파일명, 크기)로 대조해서
  이미 보낸 것은 건너뛰고 새 것만 push
- 소스의 하위폴더는 평평하게(flat) 태블릿으로 보내고,
  같은 이름인데 크기가 다른 파일은 이름 뒤에 _2, _3을 붙여 충돌 회피
- 보낼 게 없으면 "새 사진이 없습니다"라고 알리고 종료

매니페스트 방식의 장점은 기기 쪽 목록을 읽을 필요가 없다는 것(빠르다). 단점은 나중에 나온다 — 기억해두자.

STEP 3. 즉시 반영 — 여기서 하루를 태운 함정

파일을 복사만 하면 액자는 모른다. 액자 앱은 "사진 목록을 다시 읽어라"는 신호가 필요하다. PC에서는 이렇게 보내면 된다:

adb shell am broadcast -a com.handy.photoframe.REFRESH

그런데 이 명령이 안드로이드 8에서는 아무리 날려도 안 먹는다. 8.0부터 암시적 브로드캐스트가 차단됐기 때문이다. 매니페스트에 receiver를 등록해도, 기기가 잠자는 동안에는 전달이 안 된다. 나는 이거 하나 때문에 "복사는 됐는데 액자에는 사진이 안 떠"의 상태로 꽤 오래 헤맸다.

프롬프트 3

해결책을 적용해줘:
1. 앱 쪽: RefreshReceiver는 매니페스트 등록을 유지하되,
   브로드캐스트를 받으면 즉시 재스캔 + 현재 사진 새로 표시
2. 도구 쪽: push가 끝나면 브로드캐스트를 명시적 컴포넌트로 발송:
   adb shell am broadcast -a com.handy.photoframe.REFRESH
       -n com.handy.photoframe/.RefreshReceiver
(안드로이드 8+에서는 -n으로 수신자를 정확히 지정해야 전달된다)

이제 복사 끝나자마자 액자 화면에 새 사진이 뜬다. 밤 11시~아침 7시 야간 모드라면 아침 7시부터 나타난다(액자 앱이 밤에는 렌더링을 안 하기 때문).

STEP 4. 사용자 경험 — 아이콘 하나로

.bat 파일로 감싸 더블클릭 실행 가능하게 하고, 바로가기를 작업표시줄에 고정한다.

프롬프트 4

실행 마무리를 다듬어줘:
- 끝나면 콘솔이 닫히지 않게 "종료하려면 Enter" 대기
- 연결 실패 시 원인별로 다른 안내: 케이블 뽑힘 / unauthorized(태블릿에서 허용 필요)
- 사용법.txt 파일도 만들어줘: 폴더에 사진 넣기 → 케이블 연결 → 아이콘 더블클릭

STEP 5. 액자 비우기 — 위험한 기능은 더 조심히

사진을 통째로 갈아끼우고 싶을 때가 있다. 이때 필요한 건 "전부 지우기"인데, 실수로 눌렀다가 가족 사진이 통째로 날아가면 재앙이다.

프롬프트 5

'액자 비우기' 스크립트를 만들어줘. 안전 요건:
1. 태블릿의 /sdcard/PhotoFrame 안의 이미지 개수를 세서 보여주고
2. "지우려면 Y를 직접 입력" 확인을 받는다 (그 외 입력은 취소)
3. 지우는 대상은 /sdcard/PhotoFrame 폴더뿐 — DCIM 같은
   보관용 폴더는 절대 손대지 않는다 (경로를 하드코딩하지 말고
   PhotoFrame 폴더 안에서만 작업)
4. 삭제 후 동기화 기록(.pushed.tsv)도 함께 초기화한다
   (안 그러면 같은 이름·크기의 사진을 나중에 다시 넣을 때
   "이미 보냈음"으로 스킵돼서 액자에 영원히 안 뜬다!)
5. 삭제 완료 후 REFRESH 신호 전송

4번이 은근한 함정이다. 매니페스트를 같이 안 지우면, "비우고 → 같은 사진 다시 넣기"가 조용히 실패한다.

실전에서 배운 것들

  1. adb pull은 한글을 깨뜨리고, adb push는 의외로 잘 간다 — 반대 방향으로 갈 때 문제가 생긴다면 전송 경로(명령줄 인수)에 한글을 직접 쓰지 말았는지 보자. 스크립트 파일 안에 넣어두면 인코딩 문제가 사라진다.
  2. 안드로이드 8+의 암시적 브로드캐스트 차단 — 도구와 앱이 "묵시적으로 통신"하려 하면 반드시 명시적 컴포넌트(-n)로.
  3. 매니페스트(증분 기록)는 상태다 — 이를 지우는 연산(비우기)이 있으면 매니페스트도 같이 지워야 일관성이 유지된다.
  4. 위험한 동작에는 타이핑 확인 — 버튼 한 번보다 "Y 입력"이 실수를 확실히 막는다.
  5. 동기화 방향은 한쪽으로 — PC → 태블릿 일방향(추가만)이면 실수가 적다. 양방향 동기화는 "PC에서 지운 사진이 액자에서도 사라지는" 의미까지 생각해야 한다. 나는 추가만 하는 방식을 택했다.

마무리 — 시리즈 완결

이로써 웅진북클럽 태블릿이 완전히 재탄생했다:

  1. 오딘으로 순정 펌웨어 부활 (1편)
  2. 바이브코딩으로 액자 앱 개발 (2편)
  3. PC 동기화 도구로 사진 공급선 확보 (3편, 이 글)

폰도, 구글 계정도, 구독료도 없이. 있던 것은 케이블과 무료 도구뿐이다.