본문으로 건너뛰기
5분 AI.
TRENDSHIFT WEEKLY #24 딥다이브 · 2026-10-23 분석

free-my-tv 딥다이브
— TV의 첫 화면을 그리는 법, 다시 돌려놓는 법

TV 앱

빠른 판단

무엇
스트리밍 앱·작품 추천·날씨·시세를 배치하고 원래 홈 화면 복구를 다루는 TV 런처
이럴 때
Google TV의 시작 화면을 직접 구성할 때
광고 없는 TV 홈 화면을 따라가며 Java·Canvas 렌더링, 비동기 데이터 처리, 장애 복구를 함께 배웁니다. Android에서는 기본 홈 화면을 바꾸고, LG·Samsung에서는 같은 디자인의 웹 앱으로 동작합니다. Java 31개 파일 약 11,131줄 안에 앱 실행부터 직접 구현한 ADB 통신까지 담았습니다. 저장소: simbasjungles/free-my-tv · 스타 3,629 · 포크 519 · MIT · 2026-10-23 GitHub 기준. 코드 설명은 커밋 4d73951 기준입니다.
목차
  1. 프로젝트 한줄 요약
  2. 왜 주목받는가
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 분석
  5. 디렉토리 구조 해부
  6. 학습 포인트 (기술별)
  7. 하드웨어 / 시스템 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한줄 요약

TV를 켰을 때 만나는 첫 화면을 내가 통제한다

free-my-tv는 즐겨 쓰는 스트리밍 앱, 작품 추천, 날씨와 시세를 배치하는 TV용 홈 화면입니다. 이 저장소의 학습 가치는 화면을 빠르게 그리는 코드와, 그 화면이 실패해도 TV를 계속 쓸 수 있게 하는 코드가 함께 있다는 데 있습니다.

홈 화면에서 Netflix 같은 앱을 고르면 해당 앱을 실행합니다. 영화 자체를 전송하거나 재생하는 서비스는 아닙니다. 작품 정보는 TMDB에서 받고, Android에서는 선택적으로 Watchmode의 링크를 이용해 작품 페이지를 엽니다. 시청에는 각 스트리밍 앱과 해당 서비스의 이용 조건이 그대로 적용됩니다.

용어
런처 (launcher)
다른 앱을 찾아 실행하는 출발점입니다. 스마트폰의 바탕 화면을 떠올리면 됩니다. Android에서 HOME으로 등록되는 화면과, 앱 목록에 나타나는 실행 아이콘은 서로 다른 진입점일 수 있습니다.
한 문장 비유

방송국을 새로 짓는 대신, 거실의 리모컨 보관함을 다시 만드는 일입니다

Netflix와 YouTube는 각각 방송국에 가깝습니다. free-my-tv는 그곳으로 가는 버튼을 보기 좋게 정리하는 보관함입니다. 보관함이 고장 났다고 TV의 원래 조작 방법까지 사라져서는 안 됩니다.

그래서 이 프로젝트에는 보기 좋은 선반만큼이나 기존 화면을 기억하고 복원하는 장치가 중요합니다. UI 개발과 시스템 운영이 한 제품 안에서 만나는 지점입니다.

1-1. 같은 이름, 서로 다른 설치 방식

대상실행 형태기존 홈 화면과의 관계
Google TV / Android TVJava 기반 Android 앱, FreeMyTV.apkFree My TV 모드에서 HOME 역할을 바꿉니다. 일부 앱은 삭제 대신 비활성화합니다.
LG webOSweb/를 묶은 .ipk 웹 앱기본 홈은 유지합니다. README는 숫자 키 길게 누르기로 빠른 실행을 안내합니다.
Samsung Tizen같은 웹 화면을 묶은 서명된 .wgt기본 홈은 유지합니다. README는 마지막 앱 자동 실행 설정을 안내합니다.

Android 앱은 app/AndroidManifest.xml에서 HomeActivity를 HOME으로, SettingsActivity를 일반 앱 목록의 진입점으로 선언합니다. LG·Samsung의 앱 실행 기능은 web/tv.js가 각 운영체제의 API에 연결합니다.

1-2. 처음 읽을 때 잡아 둘 질문 세 가지

  1. 무엇을 그릴까? HomeView는 현재 선택 위치, 작품 목록, 날씨를 화면으로 바꿉니다.
  2. 언제 다시 그릴까? 데이터가 바뀌거나 애니메이션이 움직일 때 필요한 레이어를 갱신합니다.
  3. 실패하면 어디로 갈까? HomeMode와 Watchdog가 원래 화면으로 가는 경로를 관리합니다.
읽기 목표
기능 목록보다 데이터와 제어의 흐름을 따라가세요

“날씨 기능이 있다”에서 멈추지 말고, 요청 → 캐시 → 알림 → 다시 그리기의 순서를 설명해 보세요. 이 흐름을 이해하면 뉴스 위젯이나 환율 카드 같은 다른 기능에도 같은 사고방식을 적용할 수 있습니다.

2왜 주목받는가

눈에 보이는 문제, 짧은 개발 주기, 직접 읽을 수 있는 구현

2-1. 사용자가 바로 이해할 수 있는 문제를 푼다

README는 광고성 추천 행, 광고 배너, 자동 재생 홍보가 없는 홈 화면을 약속합니다. 앱 선반을 직접 편집하고, 추천은 설치된 스트리밍 앱에 맞추며, 낮과 밤에 따라 색을 바꿉니다. 기능 설명을 읽기 전에 “TV 첫 화면을 내 취향으로 정리한다”는 목적을 이해할 수 있습니다.

다만 “광고 없음”의 범위는 이 홈 화면입니다. 실행한 외부 앱의 광고나 TV 펌웨어 전체의 통신을 없애는 기능으로 확대 해석하면 안 됩니다. 소스의 HomeMode.DEBLOAT도 알려진 패키지 목록과 보호 조건을 이용합니다.

2-2. 만들어진 지 나흘 만에 수천 개의 별

관찰2026-10-23 GitHub 기준학습자가 살펴볼 점
관심스타 3,629 / 포크 519관심과 변형 수요를 보여 줍니다. 안정성이나 실제 설치 대수의 증명은 아닙니다.
개발 시점2026-10-19 생성 / 2026-10-23 최근 push아주 초기 프로젝트입니다. 오래 축적된 운영 경험과 구분해야 합니다.
배포v1.1.0 / 2026-10-22릴리스와 분석 대상 커밋의 관계를 확인하며 읽어야 합니다.
선정TrendShift Weekly #24분석일 2026-10-23의 선정 근거입니다.

저장소 소개와 README는 Claude Code로 만들었다고 밝힙니다. AGENTS.md와 CLAUDE.md에는 설치 안내, 기기별 예외, 복구 원칙이 적혀 있습니다. AI 도구로 만든 결과물을 실제 하드웨어 제약과 연결한 사례라는 점이 관심을 끌 만합니다. 다만 이것은 제품과 소스에서 읽어 낸 해석이며, 스타 증가 원인을 검증한 조사 결과는 아닙니다.

2-3. Projectivy와 비교하면 장점의 위치가 보인다

대체 런처 자체는 새로운 발명이 아닙니다. Projectivy도 광고 없는 화면, 카테고리·채널 구성, 배경화면과 플러그인, 기본 런처 대체 기능을 소개합니다. 비교 기준은 Projectivy 공식 README입니다.

비교 축free-my-tvProjectivy 공식 소개
공통 목적광고 없는 홈 화면과 앱 실행광고 없는 Android TV 런처와 개인화
눈에 띄는 구성앱 선반, 시간대별 작품 선택, 날씨·시세, 일몰 테마카테고리·채널, 아이콘·바로가기, 배경화면 플러그인
공부하기 좋은 점이 저장소에서 Java UI·ADB·복원 경로를 함께 읽을 수 있음공식 저장소는 기기 설정과 이슈 추적 용도도 겸한다고 설명함
플랫폼 접근Android 네이티브 + LG·Samsung 공유 웹 구현공식 소개의 중심은 Android TV와 프로젝터·셋톱박스
선택의 기준구현을 직접 바꾸며 시스템 설계까지 배우고 싶을 때풍부한 화면 구성 옵션을 먼저 살펴보고 싶을 때

free-my-tv의 차별점은 광고 없는 화면이라는 약속에 더해, 그 화면과 전환·복구 과정을 소스 수준에서 함께 공부할 수 있다는 점입니다. 이 비교표는 각 앱의 소개와 구현을 바탕으로 하며, 같은 TV에서 성능이나 호환성의 우열을 검증한 결과는 아닙니다.

2-4. 성능 수치는 조건까지 읽는다

README는 Android 14를 쓰는 98인치 TCL TV에서 홈 화면 메모리 96 MB 대 242 MB, 약 60% 감소와 60 fps를 제시합니다. 이는 README의 특정 기기 측정 주장입니다. 모든 TV의 실측 결과나 최소 메모리 요구량으로 사용할 수 없습니다.

소스에는 그 주장을 뒷받침할 설계 흔적이 있습니다. HomeView.doFrame()은 바뀐 레이어만 갱신하고, HomeMode.measureMemory()와 measureHomeTime()은 기기에서 측정하는 경로를 갖습니다. 최적화 코드가 있다는 사실과 특정 숫자를 재현했다는 사실은 구분해서 읽으세요.

3기술 스택 전체 지도

서버 프레임워크보다 운영체제 API와 빌드 도구가 중심인 앱

이 저장소에는 package.json, build.gradle, Cargo.toml, pyproject.toml, go.mod, Dockerfile이 없습니다. 웹 서비스의 전형적인 프론트엔드·백엔드 구성을 억지로 대입하기보다, 화면·데이터 처리·패키징으로 나눠야 정확합니다.

3-1. 프론트엔드: Android 직접 그리기와 ES5 웹 화면

층실제 기술·버전확인할 파일
Android 언어Java 8 문법·바이트코드 수준, 실행할 컴파일러는 JDK 17 이상build.sh의 -source 8 -target 8, CI의 Java 17
화면Android ViewGroup, Canvas, Paint, ChoreographerHomeView.java, DreamView.java
그래픽 호환Android 10 이상 RenderNode, 이전 버전 Picture; AGSL은 Android 13 이상 경로Recording.java, Scene.java, Shaders.java
LG·Samsung 화면ES5 JavaScript, HTML, CSS. React·Vue·번들러 없음web/home.js, web/home.css
표시 언어한국어 포함 JSON 카탈로그 25개, Android ICU 복수형 처리app/assets/i18n/, Ui.java, web/strings.js
폰트·기호Inter 가변 폰트, Material Symbols 자산. 패키지 버전 번호는 명시되지 않음app/assets/fonts/, app/assets/symbols/

위 표의 Java 파일은 모두 app/src/io/freemytv/ 아래입니다. JDK 버전, Java 언어 수준, Android 최소 실행 버전은 서로 다른 숫자이며 각각 다른 문제를 결정합니다.

용어
SDK와 API 수준
SDK는 개발 도구와 운영체제 기능 목록입니다. minSdk 26은 설치 가능한 최소 OS, targetSdk 33은 앱이 대상으로 삼는 Android 동작 규칙입니다. 빌드에 사용하는 android-34는 컴파일할 때 참고하는 기능 목록입니다.

3-2. 백엔드에 해당하는 역할: 앱 내부 데이터 계층

자체 운영 API 서버나 SQL 데이터베이스는 없습니다. HomeData와 Recs가 TV 안에서 외부 API에 직접 요청하고, SharedPreferences와 파일에 결과를 보관합니다. Android JSON 파서는 SDK의 org.json, HTTP 클라이언트는 HttpURLConnection입니다.

데이터출처와 조건실제 동작
날씨·일출·일몰Open-Meteo. 좌표 사용, API 키 없음HomeData.refreshIfStale(), 30분 기준. 도시 검색은 별도 지오코딩 API
암호화폐 시세Binance 공개 API. 시세 표시가 켜져 있고 선택한 코인이 있을 때5분 기준. 지원하지 않는 통화의 HTTP 400에는 달러 기준으로 재시도
작품 추천TMDB API v3. 사용자 TMDB 키 필요Recs.fetchList(), 국가·설치 앱·서비스 목록을 함께 사용
작품 바로가기Watchmode v1. 선택적 키 필요Recs.findLink(). 링크를 못 찾거나 앱이 처리하지 못하면 앱 실행으로 이어짐
설정컴파일 시 기본값 + TV에서 바꾼 값Config 기본값보다 Prefs 저장값 우선

“계정·분석 추적 없음”은 README의 개인정보 설명입니다. 네트워크를 전혀 사용하지 않는다는 뜻은 아닙니다. 선택한 기능에 따라 위치 좌표, 작품 정보 요청, 시세 요청이 각 외부 서비스에 전달됩니다.

3-3. 인프라·빌드: Gradle 없이 APK가 만들어지는 순서

config.properties │ build.sh가 기본 설정을 Java 상수로 변환 ▼ out/gen/io/freemytv/Config.java + app/src/ + app/res/ + app/assets/ │ ├─ aapt2 compile / link ── 리소스·Manifest·자산 패키징 ├─ javac ──────────────── Java → .class ├─ d8 ─────────────────── .class → classes.dex ├─ zip / jar ──────────── APK 안에 DEX 추가 ├─ zipalign ───────────── 내부 데이터 정렬 └─ apksigner ──────────── 서명 → FreeMyTV.apk

build.sh는 SDK 폴더에서 설치된 가장 높은 build-tools와 android.jar를 고릅니다. 반면 CI는 별도 SDK 경로에 지정 버전만 설치합니다. 로컬에서 최신 API를 잘못 호출하면 통과하고 CI에서는 실패할 수 있습니다.

근거버전·설정값배울 점
build.sh--min-sdk-version 26 --target-sdk-version 33, d8 --min-api 26리소스 패키징과 DEX 변환의 최소 버전을 함께 맞춥니다.
.github/workflows/check.ymlTemurin JDK 17, build-tools 34.0.0, platform android-34검증 환경의 변동을 줄입니다.
같은 CI 파일actions/checkout@v7, actions/setup-java@v5앱 의존성과 CI 액션 버전을 구별합니다.
tools/check.shacorn@8.14.0 --ecma5런타임 라이브러리가 아니라 오래된 TV용 구문 검사 도구입니다.
웹 패키지Python 3, 정확한 부 버전 고정 없음. Tizen 최소 3.0표준 라이브러리로 .ipk·.wgt를 구성합니다.
pages.ymlconfigure-pages@v6, upload-pages-artifact@v5, deploy-pages@v5docs/ 정적 사이트의 배포이며 TV 앱 배포와는 별도입니다.
비유

Gradle은 여러 공정을 묶어 주는 생산 관리 시스템에 가깝습니다. 여기서는 build.sh가 각 공정의 기계를 직접 부릅니다. 공정이 눈에 잘 보이는 대신, 도구 탐색·Windows 경로·서명 키 관리도 스크립트가 직접 책임집니다.

Android 런타임에는 별도 서드파티 라이브러리를 연결하지 않습니다. 그렇다고 프로젝트 전체에 외부 의존성이 없는 것은 아닙니다. Android SDK, 외부 API, CI 도구, Samsung 서명 도구, 폰트와 아이콘 자산에는 각각 별도의 수명과 이용 조건이 있습니다.

4아키텍처 심화 분석

핵심 패턴 네 가지를 실제 호출 경로로 읽는다
[Android TV: io.freemytv] ┌─ :home 프로세스 ─────────────────────────────────────┐ │ 리모컨 ──▶ HomeActivity.dispatchKeyEvent() │ │ │ │ │ ▼ │ │ HomeView: 포커스·레이어·프레임 계산 │ │ ├─ HomeApps ──▶ PackageManager / 다른 앱 실행 │ │ ├─ HomeData ──▶ Open-Meteo / Binance │ │ └─ Recs ──────▶ TMDB / 선택적 Watchmode │ │ SettingsActivity ──▶ HomeMode.request() │ │ 파일·설정 캐시 / Watchdog의 충돌 기록 │ └──────────────────────┬───────────────────────────────┘ │ JobScheduler ▼ ┌─ 기본 프로세스 ──────────────────────────────────────┐ │ ModeJob ──▶ HomeMode ──▶ Adb ──▶ 127.0.0.1:5555 │ │ 전환·복원·부팅 후 확인 │ TV의 adbd │ │ home-state.json ▼ │ │ HOME 역할 / 앱 활성 상태 │ └──────────────────────────────────────────────────────┘ :keys 프로세스: RemoteKeyService ──▶ RemoteKeys remote-keys.txt에서 버튼 설정 읽기 [LG / Samsung] web/home.js ──▶ web/data.js ──▶ 외부 API / localStorage └──────▶ web/tv.js ─────▶ LG Luna / Samsung Tizen API

이 도식에서 프로세스 경계는 중요합니다. Prefs의 static 필드는 프로세스마다 따로 존재합니다. HomeData.get() 같은 싱글턴도 기기 전체에 하나라는 뜻이 아니라, 해당 프로세스 안에서 하나입니다.

4-1. 패턴 ① 상태와 렌더링을 나누고, 바뀐 레이어만 갱신한다

app/src/io/freemytv/HomeView.java는 ViewGroup을 상속하며, 내부 Layer extends View가 하늘·작품·버튼·선반·서랍 등을 나눠 그립니다. “Canvas 하나로 모든 화면을 그린다”는 요약보다 여러 사용자 정의 레이어를 직접 그리는 구조가 정확합니다.

onKey()는 방향키를 move()로 보냅니다. 상태가 바뀌면 스프링의 목표값을 조정하고, doFrame()이 위치·속도를 진행시킵니다. Layer.onDraw()는 레이어 종류에 맞는 그리기 함수로 연결합니다.

// HomeView.java — doFrame()의 일부, 원문 발췌
float dt = lastNanos == 0 ? 1 / 60f
    : Math.min(0.05f, (frameTimeNanos - lastNanos) / 1e9f);
lastNanos = frameTimeNanos;

if (tilesMoving || tilesDirty) tiles.invalidate();

// busy는 움직임과 열린 패널 등 현재 상태를 합친 값
if (busy) Choreographer.getInstance().postFrameCallback(this);
else Choreographer.getInstance().postFrameCallbackDelayed(this, 1000);

활동 중에는 프레임 콜백을 이어 받고, 유휴 상태에는 1초 뒤에 다시 확인합니다. “가만히 있으면 아무 코드도 실행하지 않는다”는 뜻은 아닙니다. 시계 같은 변화도 확인해야 하기 때문입니다. Choreographer 공식 문서에서 프레임 타이밍과 콜백의 관계를 더 살펴볼 수 있습니다.

Spring.step()은 한 번의 시간 간격을 최대 약 4 ms 단위로 나눠 적분합니다. 프레임이 늦어졌을 때 물리 계산이 크게 튀는 일을 줄이려는 장치입니다. HomeView의 서랍 스프링은 new Spring(0.46f, 0.92f, 0)입니다.

// Spring.java — 핵심 계산, 원문 발췌
float omega = (float) (2 * Math.PI / response);
float stiffness = omega * omega, friction = 2 * damping * omega;
int steps = Math.max(1, (int) Math.ceil(dt / 0.004f));
float h = dt / steps;
for (int i = 0; i < steps; i++) {
    float accel = -stiffness * (value - target) - friction * velocity;
    velocity += accel * h;
    value += velocity * h;
}
용어
더티 플래그 (dirty flag)
“이 부분은 내용이 바뀌었으니 다시 그려야 한다”는 표시입니다. tilesDirty가 그 예입니다. 매번 모든 것을 계산하지 않고, 이전 결과를 재사용할 수 있는지 판단합니다.

updateMotion()은 서랍을 여는 동안 이미 그린 레이어의 위치와 투명도를 바꿉니다. 비싼 유리 효과를 매 프레임 다시 만드는 비용을 줄입니다. Recording.create()는 API 29 이상이면 RenderNode, 이전이면 Picture를 선택합니다.

4-2. 패턴 ② 캐시를 먼저 읽고, 비동기로 갱신한 뒤 알린다

HomeData 생성자는 저장된 날씨·시세를 먼저 복원합니다. refreshIfStale()은 유효 기간이 지난 데이터만 단일 작업 스레드에 맡깁니다. notifyListeners()는 메인 스레드로 돌아와 화면의 onData()를 호출합니다.

화면 등장: HomeView.resume() ├─ Listener 등록 ├─ 저장된 값으로 표시 └─ refreshIfStale() │ 오래되었고, 요청 중이 아니며, 실패 대기 중도 아님 ▼ io.execute(...) ──▶ HTTP 요청 / JSON 해석 / 캐시 저장 │ ▼ main.post(...) ───▶ Listener.onData() ──▶ 필요한 레이어 갱신 화면 퇴장: HomeView.pause() ──▶ Listener·프레임 콜백 제거
방어 장치실제 소스막으려는 문제
중복 요청 차단fetchingPrices, fetchingWeather동일한 새로고침이 연달아 요청되는 상황
실패 뒤 대기failedRecently(), 2분네트워크 장애 중 API를 계속 두드리는 상황
시간 역행 처리now < pricesAt, now < weatherAtTV 시계가 뒤로 바뀌어 캐시가 계속 새것으로 보이는 상황
도시별 캐시 식별weatherPlace()와 저장된 좌표 비교도시를 바꾼 뒤 이전 도시의 날씨를 그대로 쓰는 상황
요청 시간 제한HomeData.get(String url)의 연결·읽기 각각 6,000 ms응답하지 않는 서버를 무한히 기다리는 상황

Recs도 Listener와 별도 실행기를 사용합니다. README의 “매시간 새 추천 세 개”는 매시간 전체 목록을 다운로드한다는 뜻이 아닙니다. now(String kind)는 시간 값을 이용해 세 작품을 고르고, refreshIfStale()은 6시간 경과, 오전·오후·밤의 전환, 언어·키·설치 앱 변화 등으로 목록 재수집을 판단합니다.

fetchList()는 지역의 제공 사업자와 설치된 앱을 맞추고, 주간 인기 작품과 최근 공개 작품을 섞습니다. 설치된 앱을 안다고 유료 구독 여부까지 아는 것은 아닙니다. 기본 탑재 앱을 구독하지 않는 사용자는 hide_services로 추천 대상을 줄일 수 있습니다.

4-3. 패턴 ③ 변경 기록을 남기는 전환과 장애 격리

HomeMode.request()는 ModeJob.schedule()로 작업을 넘깁니다. ModeJob.onStartJob()은 작업 스레드에서 HomeMode.applyNow() 등을 실행하고, 기본 프로세스에서 전환을 진행합니다. 화면 프로세스가 죽어도 별도의 복구 경로를 둘 수 있습니다.

  1. applyFree()가 원래 홈·화면보호기를 조사해 home-state.json에 기록합니다.
  2. wifiOwners()로 Wi-Fi를 저장한 앱을 확인합니다. 끄려는 앱과 겹치거나 확인이 불가능하면 조건에 따라 중단합니다.
  3. 새 HOME과 화면보호기를 지정한 뒤, 보호 목록·TV 입력 기능·기존 비활성 상태를 고려해 끌 앱을 고릅니다.
  4. pm disable-user --user 0 성공을 확인할 때마다 disabled 목록을 저장합니다.
  5. applyOriginal()은 이 기록과 keptOff를 이용해 원복을 시도합니다.

save()는 임시 파일에 쓰고 FileDescriptor.sync() 후 이름을 바꿉니다. 잘리다 만 JSON을 읽을 가능성을 줄이는 방식입니다. 다만 여러 시스템 명령 전체가 하나의 DB 트랜잭션처럼 원자적으로 처리되는 구조는 아닙니다.

되돌리기 버튼은 반대 명령 하나로 완성되지 않습니다. 변경 전 상태, 실제 성공한 변경, 복원 순서를 함께 기억해야 합니다.

소스에서 읽어야 할 한계
자동 복구와 Original은 복원 범위가 다릅니다

Watchdog는 5분 안에 충돌이 3번 기록되면 requestRevertHome()을 요청합니다. revertHomeNow()은 홈과 화면보호기 중심으로 복구하고, 나머지 비활성 앱은 목록에 남깁니다. 전체 Original 전환과 같은 동작이 아닙니다.

둘 다 TV 내부 ADB 연결이 가능해야 합니다. 또한 applyFree()의 예외 처리는 오류를 알리며 끝나므로, 모든 중간 실패가 즉시 자동 원복된다고 읽으면 안 됩니다.

targets()는 Google TV 초기 설정 앱을 원래 런처보다 앞에 둡니다. 복원 시에는 원래 런처를 먼저 살립니다. 코드 주석은 설정 앱의 복구 화면이 HOME을 가져가는 기기 문제를 설명합니다. Build.DEVICE가 R3_GTV이면 전환을 거부하는 방어 조건도 있습니다.

4-4. 패턴 ④ 공통 화면과 플랫폼 어댑터를 분리한다

web/tv.js는 TV.apps(), launch(), inputs(), settings(), back() 같은 공통 인터페이스를 제공합니다. LG에서는 Luna 서비스, Samsung에서는 tizen.application 등을 호출합니다. web/home.js는 운영체제별 세부 API를 매번 알 필요가 줄어듭니다.

// web/tv.js — launch() 내부 분기, 원문 발췌
if (samsung) {
    if (id.indexOf('source:') === 0) samsungSource(id, done);
    else samsungLaunch(id, done);
    return;
}
if (!webos) {
    if (done) setTimeout(function () { done(true); }, 0);
    return;
}
luna('luna://com.webos.applicationManager/launch',
    params ? {id: id, params: params} : {id: id}, function (res) {
        if (done) done(!!res);
    });

일반 브라우저 분기는 실제 TV 앱을 실행하는 대신 성공 콜백을 돌려주는 모의 동작입니다. 웹 화면이 브라우저에 잘 보인다는 사실만으로 HDMI 전환이나 TV 앱 실행까지 검증되는 것은 아닙니다.

비유

호텔 객실의 “조명 켜기” 버튼은 같아도 내부 배선은 건물마다 다릅니다. 화면이 버튼의 의미를 정하고, 어댑터가 해당 TV의 배선으로 연결합니다. 새 플랫폼을 붙일 때 바뀌는 부분을 한곳에 모으는 것이 핵심입니다.

공유 범위에도 경계가 있습니다. Android의 HomeView.java와 웹의 home.js는 별도 구현입니다. Python의 tools/webapp.py가 공유하는 것은 설정·번역·폰트·기호와 LG·Samsung 웹 파일입니다. 화면 로직까지 한 번 작성해 세 플랫폼에서 그대로 실행하는 구조는 아닙니다.

5디렉토리 구조 해부

작은 부품에서 시작해 큰 흐름으로 넓혀 읽기

저장소는 Android 앱, 공유 웹 앱, 플랫폼별 포장 도구, 소개 사이트로 나뉩니다. 같은 이름의 build.py가 두 개 있지만, 만드는 패키지 형식과 서명 절차가 다릅니다. 루트 기준 주요 구조는 다음과 같습니다.

free-my-tv/ ├── AGENTS.md AI 도구용 안내의 진입점 ├── CLAUDE.md 기기 예외·설치·복구 원칙 ├── README.md 사용자용 기능·설치 설명 ├── LICENSE MIT 라이선스 ├── config.example.properties 기본 설정의 예시 ├── build.sh Android APK 생성 ├── app/ │ ├── AndroidManifest.xml Activity·서비스·프로세스·권한 │ ├── src/io/freemytv/ Java 31개 파일 │ │ ├── HomeActivity.java 홈 화면 생명주기 │ │ ├── HomeView.java 화면 레이어·입력·애니메이션 │ │ ├── HomeApps.java 설치 앱·TV 입력·앱 실행 │ │ ├── HomeData.java 날씨·시세·날짜와 숫자 표시 │ │ ├── Recs.java 작품 목록·이미지·링크 │ │ ├── HomeMode.java 모드 전환·원복·부팅 후 확인 │ │ ├── ModeJob.java 백그라운드 작업 진입점 │ │ ├── Adb.java TV 내부 ADB 클라이언트 │ │ ├── Watchdog.java 충돌 기록과 복구 요청 │ │ ├── Prefs.java 사용자 설정·버튼 설정 파일 │ │ ├── Spring.java 작은 스프링 계산기 │ │ ├── Recording.java OS 버전별 그리기 추상화 │ │ └── Spoken.java 사용자 정의 화면의 접근성 │ ├── assets/i18n/ 25개 언어 JSON │ ├── assets/fonts/ Inter와 라이선스 │ ├── assets/symbols/ 기호 이미지와 라이선스 │ └── res/ 아이콘·테마·버전별 리소스 ├── web/ │ ├── index.html / home.css TV 웹 화면의 골격·스타일 │ ├── home.js 포커스·설정·화면 동작 │ ├── data.js 데이터·설정·로컬 캐시 │ ├── tv.js LG·Samsung 플랫폼 연결 │ ├── strings.js / zones.js 번역·시간대별 기본 정보 │ └── icons/ 플랫폼 보완용 앱 로고 ├── webos/ LG 패키징·설치·appinfo.json ├── tizen/ Samsung 패키징·설치·config.xml ├── tools/ 공유 웹 자산·번역·호환성 검사 ├── docs/ 제품 소개용 정적 사이트 └── .github/workflows/ 앱 검사와 사이트 배포

5-1. 줄 수가 알려 주는 무게 중심

부분물리적 줄 수읽을 때의 의미
Java 전체31개 / 11,131줄Android 네이티브 앱이 중심입니다.
HomeView.java3,051줄여러 UI 책임이 모여 있어 처음부터 끝까지 읽기 어렵습니다.
HomeMode.java961줄전환·복구의 예외 처리가 상당한 비중을 차지합니다.
TV 웹 JavaScript5개 / 2,220줄짧게 압축된 zones.js도 포함합니다.
사이트 JavaScript1개 / 281줄TV 앱과 별도인 소개 사이트 코드입니다.
Python 전체9개 / 1,239줄빌드·설치·자산 생성·번역 도구입니다.

공백과 주석을 포함한 텍스트 줄 수입니다. 생성 결과와 Git 내부 데이터는 제외했습니다. 한 줄에 많은 데이터가 든 파일도 있어 줄 수를 기능 복잡도나 개발 시간으로 그대로 환산할 수는 없습니다.

5-2. 초보자에게 권하는 읽기 순서

  1. Spring.java 41줄: 입력·상태·계산·종료 조건을 한 번에 이해합니다.
  2. HomeActivity.java 80줄: 화면이 언제 생기고 멈추는지 봅니다.
  3. HomeData.java 452줄: HTTP 요청과 캐시·Listener를 연결합니다.
  4. HomeView.onData() → doFrame() → Layer.onDraw(): 큰 파일을 호출 경로로 나눕니다.
  5. ModeJob.java 58줄 → Watchdog.java 84줄 → HomeMode: 복구 흐름을 따라갑니다.
  6. web/tv.js → tools/webapp.py: 운영체제 연결과 패키징의 차이를 봅니다.
큰 파일 읽기
3천 줄짜리 파일도 질문 하나로 좁힐 수 있습니다

“OK 버튼을 놓았을 때 무엇이 실행될까?”를 정하고 onKey()에서 activate()까지 추적하세요. 이후에 포커스 이동, 서랍, 앱 재정렬처럼 질문을 한 개씩 늘리는 편이 구조를 이해하기 쉽습니다.

6학습 포인트 (기술별)

한 기능에서 다른 프로젝트로 가져갈 수 있는 원리를 찾는다

각 기술을 따로 외우기보다 사용자 입력이 상태를 바꾸고, 상태 변화가 저장·통신·화면 갱신으로 이어지는 연결을 설명해 보세요.

6-1. Java: 작은 상태 객체로 동작을 모델링하기

Spring은 value, velocity, target 세 상태를 가집니다. snap()은 즉시 이동하면서 속도를 0으로 만들고, step()은 시간에 따라 조금씩 이동합니다. settled()는 거리와 속도가 충분히 작을 때 종료를 결정합니다.

미니 실습: 종이에 초기값 0, 목표값 1을 놓고 response와 damping이 각각 어떤 경험을 바꿀지 예상해 보세요. 목표만 바꾸는 것과 snap()의 차이를 표로 적으면 좋습니다.

6-2. Android: 생명주기와 작업의 수명을 맞추기

HomeActivity.onResume()은 home.resume()으로 연결되고, onPause()는 home.pause()로 연결됩니다. 화면이 사라질 때 Listener·콜백·TV 입력 콜백을 해제하는 코드를 짝으로 찾으세요.

미니 실습: resume()의 등록 작업과 pause()의 해제 작업을 두 열에 적습니다. 해제가 없을 때 오래된 화면을 붙잡거나 불필요한 알림을 받을 수 있는 부분을 설명해 보세요. 이미 시작된 네트워크 작업을 모두 취소하는 것과 Listener 제거도 서로 다른 동작입니다.

6-3. 그래픽: 픽셀을 다시 계산하는 비용과 합성 비용 구별하기

Layer.onDraw()는 직접 그리기를 맡고, updateMotion()은 레이어의 위치·투명도 등을 바꿉니다. Scene은 배경을 축소해 그리는 경로를 두고, Frost는 48×27 픽셀의 작은 이미지를 만듭니다. 작은 이미지를 확대·보간해 흐린 유리 배경에 활용하는 방식입니다.

미니 실습: 서랍을 10픽셀 옮길 때 글자와 배경을 다시 그려야 하는지, 이미 그린 결과를 이동해도 되는지 나눠 보세요. Android 직접 그리기 문서의 Canvas와 Paint 역할을 이 코드에 대응시키면 좋습니다.

6-4. 동시성: 백그라운드 작업과 UI 통지 구분하기

HomeData는 단일 ExecutorService로 I/O를 처리하고, Handler(Looper.getMainLooper())로 UI에 결과를 알립니다. volatile은 스레드 사이에서 값의 변경을 보이게 하는 도구지만, 여러 필드의 변경 전체를 원자적으로 묶어 주지는 않습니다.

용어
옵서버 (observer)
데이터를 가진 쪽이 “바뀌었다”고 알리고, 구독한 화면이 반응하는 구조입니다. 여기서는 Listener 인터페이스와 addListener()·removeListener()가 그 역할을 합니다.

미니 실습: 시세와 날씨 요청이 같은 단일 실행기를 쓰면 한쪽의 지연이 다른 쪽에 어떤 영향을 주는지 타임라인을 그려 보세요. 무조건 스레드를 늘리기 전에 순서·중복·서버 부하를 함께 따져 봅니다.

6-5. 국제화: 문장을 이어 붙이지 않고 패턴을 번역하기

Ui.tr()는 영어 키에 대응하는 번역을 찾고, trf()는 이름 있는 자리표시자를 채웁니다. Ui.plural()은 ICU가 고른 복수형 범주를 이용합니다. 한국어에는 하나로 보이는 형태가 다른 언어에서는 수에 따라 여러 개 필요할 수 있습니다.

// Ui.java의 사용 규칙을 보여 주는 호출 예시
Ui.trf("{n} min ago", "n", 12);
Ui.plural("plural|one:{n} season|other:{n} seasons", 3);

// 번역은 문장 순서를 바꿀 수 있어도 {n}은 보존해야 합니다.
// 언어 변경 시 Ui.generation이 증가하여 화면의 글자 캐시를 갱신합니다.

미니 실습: app/assets/i18n/ko.json과 en.json에서 같은 키를 찾아 비교합니다. tools/translate.py의 PLACEHOLDER와 check()가 왜 필요한지 설명해 보세요. 공유 웹 앱은 tools/webapp.py가 실제 사용하는 번역 키만 묶습니다.

6-6. 시스템 프로그래밍: 소켓 위의 메시지를 이해하기

Adb.connect()는 TV 자신의 127.0.0.1:5555에 접속합니다. CNXN·AUTH로 연결과 인증을 진행하고, shell()은 OPEN·WRTE·OKAY·CLSE 메시지를 처리합니다.

미니 실습: Adb.read()가 읽는 24바이트 헤더를 필드별로 분해해 적습니다. “문자열 명령을 보낸다” 아래에 길이·바이트 순서·인증·종료 신호가 있다는 점을 찾는 것이 목표입니다. 명령을 실제 TV로 보내지 않아도 프로토콜 구조는 공부할 수 있습니다.

6-7. 접근성과 구형 웹: 편리한 기본 기능이 없는 환경 다루기

Canvas로 그린 버튼은 화면 낭독기가 자동으로 의미를 알기 어렵습니다. Spoken extends AccessibilityNodeProvider는 가상 항목의 설명·경계·동작을 제공합니다. 리모컨 버튼을 가로채는 RemoteKeyService와는 목적이 다른 코드입니다.

웹 쪽은 오래된 TV를 위해 ES5 문법과 콜백·XMLHttpRequest를 사용합니다. tools/check.sh는 구문 검사 외에 fetch, Promise, Array.from 등 구형 환경에 없는 호출도 찾습니다. 구문이 해석된다고 API가 존재하는 것은 아닙니다.

미니 실습: 화려한 새 문법 하나를 도입할 때 “파싱 가능 여부”와 “런타임 기능 존재 여부”를 별도로 적습니다. 사용자 정의 버튼에는 시각적 포커스뿐 아니라 낭독 이름과 클릭 동작도 함께 정의해 보세요.

7하드웨어 / 시스템 요구사항

설치 가능한 OS와 쾌적하게 동작하는 기기는 같은 개념이 아니다
용도요구 조건근거와 주의점
Android 실행Android 8 / API 26 이상 TV·셋톱박스build.sh의 minSdk. 기기 펌웨어별 HOME 전환 호환성은 별개입니다.
Android 개발JDK 17 이상, Android SDK, adb, Bash 환경CI 기준 build-tools 34.0.0, platform android-34. Windows는 README의 Git Bash 또는 WSL 경로
LG 실행webOS 3 이상, Developer Mode최소 버전은 README 기준. 기존 LG 홈은 유지합니다.
LG 개발·설치Python 3, OpenSSH 도구, 같은 네트워크의 컴퓨터webos/build.py·install.py. 개발자 모드의 세션 유지 조건도 확인해야 합니다.
Samsung 실행2017년 이후 TV, Tizen 3 이상README와 tizen/config.xml의 required_version="3.0"
Samsung 개발Python 3, 개발자 모드, 설치할 기기에 맞는 서명자체 서명 빌드는 Tizen 도구 필요. 2023년 이후 기기 인증서 조건은 README·제조사 문서 확인
온라인 기능인터넷, 선택 기능별 API 접근TMDB 키 없이도 기본 홈은 동작하지만 작품 추천은 비어 있습니다.
RAM·CPU·저장공간정량적 최소 사양 명시 없음96 MB는 README의 앱 측정치이며 TV 전체 RAM 요구량이 아닙니다.

최소 Android 버전을 충족해도 GPU, 메모리, 제조사 서비스, 디버깅 방식에 따라 동작과 복구 가능성이 달라집니다.

7-1. OS 버전별로 같은 효과를 다른 방식으로 그린다

성능을 생각할 때는 패널의 “4K” 표기와 실제 UI 렌더링 해상도를 구분하세요. HomeView.onMeasure()는 너비를 1920 기준으로 환산하며, Backdrop는 UI 크기를 고려해 이미지 크기를 결정합니다.

메모리 감각

1920×1080 RGBA 이미지는 픽셀당 4바이트로 계산하면 약 7.9 MiB입니다. 3840×2160이면 약 31.6 MiB로 네 배가 됩니다. 이는 압축 파일 크기가 아니라 픽셀 버퍼의 단순 계산입니다. 원본·흐림·낮·밤용 이미지를 각각 보관하면 작은 TV에서 메모리 사용량이 빠르게 늘어납니다.

7-2. Android 전환은 일반 앱 권한만으로 이루어지지 않는다

Adb.java는 디버깅이 허용된 TV의 로컬 ADB를 이용합니다. 연결 시 사용자 키 승인이 필요할 수 있고, 무선 디버깅의 TLS 요청을 받으면 USB 디버깅 방식을 안내하며 중단합니다. “루트 불필요”와 “권한 승인 불필요”를 같은 뜻으로 읽으면 안 됩니다.

실기기에서의 경계
복구 경로까지 포함해 호환성을 판단하세요

Wi-Fi를 저장한 시스템 앱을 끄면 연결 정보가 사라질 수 있어 wifiOwners()가 이를 방어합니다. 일부 TCL 펌웨어는 백그라운드 작업을 제한하고, R3_GTV는 홈 전환 자체가 차단돼 있습니다.

README도 Free My TV 모드에서 곧바로 제거하면 비활성 앱이 그대로 남는다고 설명합니다. 제거 전 Original 복원과 단순 APK 삭제는 서로 다른 작업입니다.

제조사 개발자 모드 조건은 변할 수 있습니다. 기기 설치를 계획할 때는 LG 공식 개발자 모드 문서와 Samsung 공식 TV 연결 문서를 확인하세요.

8직접 해볼 수 있는 실습 과제

읽기 과제에서 시작해 작은 개선 설계로 확장하는 다섯 단계

다음 과제는 기기 변경 없이 시작할 수 있습니다. 첫 두 과제는 텍스트만 읽어도 완성할 수 있고, 이후 과제는 별도 학습용 사본에서 구현하는 확장 과제입니다. 과제를 끝냈다는 기준을 “코드를 바꿨다”가 아니라 “입력·실패 조건·예상 결과를 설명할 수 있다”로 정하세요.

과제 1 · 코드 읽기

달러 카드는 왜 사라져야 할까? 입문

출발점: HomeData.active(), quote(), pricesPath().

할 일: currency=USD, BRL, 지원되지 않는 통화에서의 달러 대체 상황을 표로 적습니다. 네 코인이 모두 선택됐다고 가정하고 요청할 거래쌍을 써 보세요.

완료 기준: USD에서는 BTCUSDT·ETHUSDT·SOLUSDT만, BRL에서는 USDTBRL까지 포함되는 이유를 설명합니다. USDTUSDT라는 잘못된 쌍이 전체 요청을 실패시킬 수 있다는 점을 연결하세요.

더 생각하기: 선택 상태뿐 아니라 Prefs.version과 inDollars가 activeCoins 캐시의 유효성에 필요한 이유는 무엇일까요?

과제 2 · 경계 조건

새로고침 판단표 만들기 입문

출발점: HomeData.refreshIfStale(), failedRecently().

할 일: 시세가 4분 전·6분 전일 때, 요청 중일 때, 30초 전에 실패했을 때, TV 시계가 뒤로 이동했을 때를 나눠 요청 여부를 판단합니다.

완료 기준: “오래됨”, “중복 요청 방지”, “실패 대기”가 각각 별도 조건임을 설명하고, 각 사례에서 어떤 조건이 요청을 막는지 표시합니다. 실제 API 키나 통신은 필요하지 않습니다.

과제 3 · UI 상태 설계

시세가 오래됐다는 안내를 설계하기 중급

출발점: HomeData.staleness(), HomeView.onData(), Ui.trf(), app/assets/i18n/ko.json.

할 일: 마지막 성공 시각과 마지막 실패 시각의 역할을 구분한 뒤, 데이터 없음·정상·오래된 캐시 세 상태의 안내 문구와 표시 위치를 설계합니다. 확장 구현에서는 기존 값은 유지하고 안내만 바꾸는 방법을 찾아봅니다.

완료 기준: 네트워크 장애가 가격 0원으로 표시되지 않고, 오래된 값을 최신값처럼 오해하지 않으며, 문구가 번역 키와 자리표시자 규칙을 지킵니다. Double.NaN의 의미도 적으세요.

과제 4 · 플랫폼 분리

TV 없이 확인할 모의 어댑터 설계 중급

출발점: web/tv.js의 일반 브라우저 분기와 launch(id, params, done).

할 일: 앱 세 개와 HDMI 입력 두 개를 반환하는 모의 데이터를 정의하고, 실행 성공·실패·지연 시 화면이 어떻게 반응할지 설계합니다. 실제 TV 주소나 서비스 호출은 사용하지 않습니다.

완료 기준: 공통 화면 코드를 운영체제 조건으로 채우지 않고도 실패를 표현할 수 있으며, done이 한 요청에서 두 번 호출되지 않는지 확인할 사례를 제시합니다.

과제 5 · 복구 설계

가짜 ADB로 중간 실패 시나리오 만들기 고급

출발점: HomeMode.applyFree(), applyOriginal(), revertHomeNow(), save().

할 일: 실제 명령을 보내는 대신 “명령 → 예정된 문자열 결과”를 반환하는 가짜 실행기를 설계합니다. HOME 지정 성공 후 앱 비활성화 실패, 상태 저장 실패, ADB 연결 실패를 각각 주입한다고 가정하세요.

완료 기준: 성공한 변경만 복원 목록에 남는지, 원래 꺼 둔 앱은 보존되는지, 초기 설정 앱과 원래 런처의 복원 순서가 맞는지 검사 항목을 적습니다. 기존 코드의 RUN 잠금이 제공하는 범위와 영속 기록의 역할도 구별하세요.

9관련 기술 심화 학습 로드맵

주 4~6시간을 가정한 여섯 주의 학습 순서

처음부터 ADB 인증이나 셰이더 수식을 전부 이해할 필요는 없습니다. 값 하나가 화면으로 가는 경로를 익힌 뒤, 수명·성능·실패·플랫폼 차이를 차례로 추가합니다. 일정은 학습 계획의 예시이며 공식 교육 과정이나 숙련 시간을 보장하는 기준은 아닙니다.

주차주제·읽을 소스직접 남길 결과물
1주차Java 객체·인터페이스·상태. Spring, HomeData.Listener상태 변수 뜻과 snap()·step() 차이, 시세 거래쌍 표
2주차Android Activity·Manifest·생명주기. HomeActivity, HomeView.resume()·pause()진입점과 프로세스 표, 등록·해제 작업의 대응표
3주차HTTP·JSON·캐시·스레드. HomeData, Recs.refreshIfStale()성공·실패·시간 역행을 포함한 새로고침 판단표
4주차Canvas·프레임·이미지 메모리. Layer.onDraw(), doFrame(), Recording한 번의 방향키 입력에서 재계산할 레이어 목록, 이미지 메모리 계산
5주차영속 상태·작업·복구. ModeJob, HomeMode, Watchdog, Adb전환 중 실패 위치별 복구 시나리오와 가짜 명령 실행기 설계
6주차플랫폼 어댑터·국제화·배포. web/tv.js, Ui, tools/webapp.py, CI모의 TV API 명세, 한국어 문구 설계, 빌드 단계 지도

로드맵의 마지막 목표는 기능을 하나 더 넣는 것보다, 그 기능이 느려지거나 실패했을 때의 동작까지 설계하는 것입니다.

9-1. 이해가 막히면 내려갈 기초

9-2. 이미 경험이 있다면 깊게 볼 갈림길

그래픽 경로

Backdrop의 이미지 크기·흐림 결과 재사용 → Scene의 배경 기록 → Shaders의 AGSL 수식 순서로 들어갑니다. “GPU를 쓴다”가 아니라 어떤 계산을 언제 반복하는지 설명하는 것이 목표입니다.

시스템 경로

HomeMode의 보호 조건 → 상태 파일 갱신 → verifyNow()의 부팅 후 재확인 → Adb 인증 순서로 들어갑니다. 프로세스 분리가 제공하는 격리와, 동일한 기기의 ADB에 의존한다는 한계를 함께 평가합니다.

최종 점검
다섯 문장을 스스로 완성해 보세요

“캐시는 언제 버리는가”, “화면을 떠나면 무엇을 해제하는가”, “구형 OS에서는 무엇으로 대체하는가”, “중간 실패 후 무엇이 남는가”, “웹과 Android가 실제로 공유하는 것은 무엇인가”를 소스 이름과 함께 답할 수 있으면 이 프로젝트의 큰 흐름을 이해한 것입니다.

10핵심 키워드 사전

처음 만나는 용어를 소스의 구체적인 역할과 연결한다
용어쉬운 설명
런처 / HOME 역할다른 앱을 실행하는 출발 화면과 그 화면을 담당하는 시스템 역할. HomeActivity가 맡습니다.
ActivityAndroid의 화면 진입 단위. 이 앱은 홈과 설정에 서로 다른 Activity를 둡니다.
Intent“이 앱을 열어라”처럼 운영체제에 행동을 요청하는 메시지입니다.
Manifest화면·서비스·권한·프로세스를 운영체제에 알리는 앱의 구성표입니다.
Canvas / Paint도형·글자·이미지를 그리는 도구와 그릴 때의 색·두께·폰트 설정입니다.
레이어 / 합성화면을 여러 조각으로 그려 두고 위치·투명도와 함께 최종 화면으로 합치는 방식입니다.
더티 플래그그린 결과를 다시 만들어야 한다는 표시. tilesDirty처럼 쓰입니다.
Choreographer디스플레이의 프레임 타이밍에 맞춰 계산할 기회를 주는 Android 도구입니다.
감쇠 스프링목표를 향한 힘과 움직임을 줄이는 저항으로 부드러운 변화를 만드는 모델입니다.
RenderNode / Picture그리기 명령을 기록하고 재사용하는 도구. Recording이 OS별 차이를 감쌉니다.
AGSL / 셰이더픽셀의 색을 계산하는 프로그램과 그 언어. 여기서는 하늘·유리 효과를 만듭니다.
ExecutorService작업을 맡길 스레드를 관리하는 도구. HTTP와 파싱을 UI 작업과 분리합니다.
Handler / Looper지정한 스레드의 작업 순서에 함수를 넣는 통로와 작업 처리 루프입니다.
Listener / 옵서버데이터 변경을 구독하고 통지를 받는 객체입니다.
캐시 / 유효 기간이전 응답을 재사용하고 언제 다시 가져올지 판단하는 규칙입니다.
SharedPreferences설정처럼 작은 키·값을 기기에 저장하는 Android 기능. 프로세스 간 최신값 공유를 자동 보장하지 않습니다.
프로세스 격리화면과 작업을 별도 실행 공간에 두는 방식. 화면 충돌이 복구 작업까지 바로 죽이지 않게 합니다.
JobScheduler앱의 백그라운드 작업을 운영체제에 예약하는 도구. ModeJob의 실행 기반입니다.
워치독문제를 감지하고 복구를 요청하는 감시 장치. 여기서는 짧은 시간의 반복 충돌을 셉니다.
ADB / adbdAndroid 디버깅 통신 도구와 기기 쪽 서비스. 이 앱은 자체 ADB 클라이언트를 구현합니다.
루프백 주소127.0.0.1처럼 자기 기기를 가리키는 주소. TV가 자기 TV의 ADB에 접속합니다.
little-endian여러 바이트 숫자의 낮은 자리 바이트를 먼저 저장하는 순서. ADB 헤더 해석에 사용합니다.
원자적 교체임시 파일을 완성한 뒤 이름을 바꿔 기존 파일을 대체하는 방식. 시스템 변경 전체의 원자성과는 다릅니다.
딥 링크앱의 첫 화면 대신 특정 작품 같은 내부 목적지로 가는 주소입니다.
플랫폼 어댑터공통 요청을 운영체제마다 다른 API로 바꾸는 연결부. web/tv.js가 예입니다.
ES5오래된 JavaScript 표준. 구형 TV가 이해할 문법과 사용할 API 범위를 제한하는 기준입니다.
국제화 / 자리표시자언어별 표현을 준비하는 구조와 문장에 넣을 값의 이름. {n} 같은 표시를 보존합니다.
가상 접근성 노드직접 그린 버튼을 낭독기가 탐색하도록 제공하는 논리적 항목. Spoken이 만듭니다.
APK / DEXAndroid 설치 패키지와 그 안에 들어가는 실행 코드 형식입니다.
IPK / WGT이 프로젝트가 LG와 Samsung 앱을 각각 포장하는 파일 형식입니다.
코드 서명배포자의 키로 패키지를 서명하는 절차. 업데이트할 때 같은 서명 키를 유지해야 합니다.
CI코드 변경 때 자동으로 검사하는 절차. 여기서는 빌드·번역·웹 호환성 등을 확인합니다.

11참고 링크

변하는 저장소 정보와 고정된 소스 기준을 나누어 확인한다

11-1. 저장소와 분석 기준

11-2. 관련 기술의 공식 문서

11-3. 현재 상태 (2026-10-23 확인)

항목상태
프로젝트simbasjungles/free-my-tv — 광고 없는 Google TV 홈 화면, Claude Code로 제작했다고 소개
선정 근거TrendShift Weekly #24 · 분석일 2026-10-23
스타 / 포크3,629 / 519 — 2026-10-23 GitHub 기준
생성일2026-10-19
최근 push2026-10-23 — 저장소 메타데이터의 갱신일
최신 릴리스v1.1.0 · 2026-10-22
분석 대상 최근 커밋4d73951 · 2026-10-22 (UTC−03:00) · PR #188 병합: 달러 기준에서의 달러 카드 관련 수정
버전 파일Android versionName="1.1", versionCode="2". webOS·Tizen은 1.1.0. 릴리스 이름과 앱별 버전 표기는 다릅니다.
주 언어·핵심 규모Java 31개 / 11,131줄. JavaScript 6개 / 2,501줄. Python 9개 / 1,239줄
기타 텍스트 규모CSS 2개 / 1,903줄, HTML 3개 / 708줄, Shell 2개 / 174줄, XML 13개 / 274줄, JSON 26개 / 4,865줄, YAML 2개 / 75줄
집계 범위커밋 기준 추적 파일 172개. 위 줄 수는 공백·주석을 포함하며 Git 내부 데이터·빌드 결과·이미지·폰트는 제외
번역app/assets/i18n/의 JSON 25개, 한국어 포함
라이선스앱 코드 MIT. Inter는 SIL OFL 1.1, Material Symbols는 Apache 2.0 등 자산별 표기 별도 확인

스타·포크·생성일·push·릴리스는 2026-10-23 GitHub 기준이며, 파일·줄 수·함수 동작은 커밋 4d73951 기준입니다. 이후 변경 사항과 전체 커밋 수를 이 수치에서 추정하지 않습니다.

사용하기 전에 알아둘 한계
소스의 의도와 모든 TV에서의 동작 보장은 다릅니다

성능 수치는 README의 특정 TCL 기기 측정입니다. 모든 기기에서의 60 fps·메모리 절감, 모든 제조사 펌웨어의 전환·복원 성공을 보장하지 않습니다. CI의 빌드·번역·ES5 검사는 실기기 검증을 대신하지 않습니다.

자동 충돌 복구는 홈·화면보호기를 우선 복원하고, Original은 더 넓은 원복을 시도합니다. 비활성화로 인한 모든 부작용까지 복원하는 만능 백업은 아니며, 복구 자체도 로컬 ADB가 동작해야 합니다.

설치된 앱을 기준으로 추천하므로 구독권·지역별 재생 가능 여부를 모두 판별하지는 못합니다. 외부 앱의 광고도 제거하지 않습니다. 외부 API 장애·요금제·지역 제한은 각 서비스 조건에 따릅니다.

config.properties의 키는 Android의 생성된 상수나 웹의 config.js에 들어갑니다. Git에서 제외하는 것과 배포 파일에서 비밀로 숨기는 것은 다릅니다. 개인 키를 넣은 패키지를 공개 배포하지 않도록 관리해야 합니다.

LG·Samsung은 기본 홈 교체가 아닌 별도 앱입니다. 또한 Android UI와 웹 UI는 별도 구현이라 기능·디자인 변경 시 양쪽을 함께 살펴야 합니다. MIT 코드와 별개로 폰트·아이콘·API 데이터의 조건도 확인하세요.