Android 17 화면 고정 예외 폐지 대비하기: Jetpack Compose의 mediaQuery·Grid·FlexBox·Navigation 3 적응형 설계 해부
Android 17의 화면 방향·종횡비 고정 우회 플래그 폐지에 대비해 Jetpack Compose의 차세대 적응형 UI API(mediaQuery, derivedMediaQuery, Grid, FlexBox, Navigation 3 SceneStrategy)를 정리한다. 레거시 XML 리소스 분기의 상태 유실 원인과 단일 컴포저블 기반 다중 폼팩터 대응 패턴을 살펴본다.
Jetpack Compose 적응형 아키텍처(UiMediaScope·Grid·FlexBox·Navigation 3) 및 Android 17 필수 대화면 정책 구조도
결론부터 말하면 Android 17부터 대화면 기기에서 화면 방향(Orientation)·종횡비(Aspect Ratio)·크기 조절(Resizability)을 강제로 고정하던 매니페스트 예외 플래그가 완전히 폐지되므로, 이제 안드로이드 앱의 적응형(Adaptive) 설계는 선택이 아니라 필수 생존 요건이다. Android Developers의 'Scale your app for every screen with Compose' 세션에서 구글 엔지니어링 팀은 레거시 XML 리소스 분기(layout-sw600dp, layout-land)가 유발하던 치명적인 상태 유실 문제를 짚어내고, 이를 근본적으로 해결하는 Jetpack Compose의 차세대 적응형 기술 스택(mediaQuery, Grid, FlexBox, Navigation 3 SceneStrategy)와 코딩 에이전트용 **Compose Adaptive Skill**을 공개했다.
단, 영상에서 소개된 에이전트 토큰 30% 이상 절감 수치나 약 20분(20만 토큰) 내 전체 앱 전환 사례는 Google의 내부 벤치마크 및 샘플 앱(Jetcaster) 기준 발표치이므로, 커스텀 뷰나 복잡한 레거시 상태 홀더가 섞인 프로덕션 환경에서는 모듈 단위로 단계적 검증을 거치는 편이 안전하다.
1. 왜 지금 적응형 UI가 강제되는가: Android 17 정책 변화와 XML의 구조적 한계
Android Developers — Scale your app for every screen with Compose 공식 세션
현재 활성화된 대화면 안드로이드 기기(폴더블, 태블릿, ChromeOS, 데스크톱 윈도우 환경, XR 및 차량용 디스플레이)는 5억 8,000만 대를 넘어섰다. Android 16(SDK 36)부터 플랫폼은 대화면에서 세로 고정(screenOrientation="portrait")이나 크기 조절 불가 설정을 무시하기 시작했으며, 유예 기간 동안만 매니페스트 opt-out 플래그를 허용했다. Android 17에서는 이 opt-out 플래그가 완전히 제거된다.
그동안 많은 앱이 세로 고정을 고집한 진짜 이유는 개발자의 게으름이 아니라 레거시 View/XML 시스템에서 화면 전환 시 상태를 온전히 보존하기가 구조적으로 너무 어렵기 때문이었다.
| 비교 항목 | 레거시 View + XML 리소스 분기 ( layout-sw600dp 등) | Jetpack Compose 선언형 적응형 UI ( UI = f(state)) |
| 레이아웃 분기 방식 | 폴더별 중복 XML 파일 유지 ( layout/, layout-land/, layout-sw600dp/) | 단일 Kotlin 컴포저블 트리 안에서 조건부 분기 및 적응형 레이아웃 조합 |
| 화면 회전·폴딩 시 동작 | 기존 View 트리를 **완전히 파괴(Destroy)**하고 새 XML을 인플레이트(Inflate) | 상태를 유지한 채 변경된 윈도우 조건에 맞춰 **리컴포지션(Recomposition)**만 수행 |
| 상태 복원 메커니즘 | onSaveInstanceState / onRestoreInstanceState 및 View ID 매칭에 의존 | rememberSaveable 및 ViewModel 상태 홀더를 통해 UI 계층과 독립적으로 유지 |
| 대표적인 장애 유형 | 1) XML 간 View ID 불일치 시 스크롤·입력값 조용한 유실(Silent State Loss) 2) onViewCreated 이후 상태 복원이 실행되며 발생하는 리스너 무한 루프3) 동일 ID에 다른 위젯( Spinner ↔ RadioGroup) 배치 시 ClassCastException 크래시 | 런타임 크래시 없이 단일 상태 소스(Single Source of Truth)가 새 레이아웃에 즉시 투영 |
| 코딩 에이전트 친화성 | 여러 XML 파일과 Activity/Fragment 코드를 동시에 읽어야 해 컨텍스트 소모 큼 | UI와 상태 로직이 한 곳에 응집되어 에이전트 토큰 사용량 30% 이상 절감 |
Compose에서는 UI가 상태의 순수 함수(UI = f(state))로 동작하므로, 기기를 펼치거나 데스크톱 창 크기를 드래그하는 행위가 아키텍처 차원의 파괴적 이벤트가 아니라 단순한 상태 변경 및 리컴포지션으로 처리된다.
2. 6개 API를 하나로 통합한 mediaQuery와 derivedMediaQuery
기존에 창 크기, 폴더블 힌지(Posture), 마우스/스타일러스 정밀도, 물리 키보드 연결 여부를 모두 확인하려면 5~6개의 서로 다른 플랫폼 API와 리스너를 조합해야 했다. 새롭게 도입된 mediaQuery(androidx.compose. adaptive / UiMediaScope)는 이 모든 환경 시그널을 단일 선언형 API로 통합한다.
UiMediaScope가 제공하는 핵심 프로퍼티
- 창 크기:
windowWidth,windowHeight(Dp단위 직접 비교 지원)
- 폴더블 힌지 상태(
windowPosture):Posture.Flat,Posture.Tabletop(반쯤 접어 바닥에 둔 상태),Posture.Book및hingeBounds영역 좌표
- 입력 정밀도(
pointerPrecision):Fine(마우스·트랙패드·스타일러스),Coarse(터치),Blunt(컨트롤러),None
- 키보드 종류(
keyboardKind):Physical(물리 키보드),Virtual(온스크린 키보드),None
- 하드웨어 및 시청 거리:
hasCamera,hasMicrophone,viewingDistance(Near,Medium,Far— TV/자동차/XR 대응)
실전 패턴: 불필요한 리컴포지션을 막는 derivedMediaQuery
데스크톱 창 모드에서 사용자가 창 모서리를 드래그하면 windowWidth가 프레임마다 수십 번씩 바뀐다. 이때 하위 트리 전체가 매 픽셀마다 리컴포지션되는 것을 막으려면 derivedStateOf와 동일한 원리로 동작하는 **derivedMediaQuery**를 사용해 브레이크포인트 교차 시점에만 상태를 갱신해야 한다.
또한 mediaQuery는 내부적으로 LocalUiMediaScope 컴포지션 로컬을 바라보므로, 에뮬레이터에서 폴더블 센서를 조작할 필요 없이 CompositionLocalProvider(LocalUiMediaScope provides TestMediaScope(...))만으로 프리뷰와 유닛 테스트에서 모든 폼팩터 조합을 즉시 검증할 수 있다.
3. 단일 트리로 폼팩터를 넘나드는 Grid, FlexBox, Navigation 3
화면 구조가 크게 바뀔 때 if (isTabletop) TabletopPlayer() else PhonePlayer()처럼 최상위 컴포저블을 통째로 갈아끼우면, 하위 동영상 플레이어나 웹뷰의 내부 상태가 초기화될 위험이 있다. 구글은 자식 컴포저블을 단 한 번만 선언하고 배치 규칙만 바꾸는 세 가지 핵심 레이아웃·내비게이션 도구를 안정화(Stable)했다.
1) 측정 단계에서 Named Area를 매핑하는 Grid
새롭게 안정화된 Compose Grid는 CSS Grid에서 영감을 받아 2차원 트랙(Fixed, Fraction, Percentage, Auto)과 Enum 기반 Named Area를 지원한다. 핵심은 config 블록이 컴포지션 단계가 아니라 측정(Measure) 단계에서 실행된다는 점이다.
2) 행·열 전환과 줄바꿈을 하나로 처리하는 FlexBox
기존 Row, Column, FlowRow, FlowColumn의 기능을 하나로 아우르는 **FlexBox**도 공식 가이드와 함께 도입되었다. FlexBox의 config 블록 역시 측정 단계에서 평가되므로, 화면 폭에 따라 direction(FlexDirection.Row)에서 direction(FlexDirection.Column)으로 바뀌거나 wrap(FlexWrap.Wrap)이 활성화되어도 자식 노드들을 재컴포지션하지 않고 레이아웃 패스 안에서 즉시 재배치한다.
3) 백스택을 직접 소유하고 멀티 페인을 자동화하는 Navigation 3
**Jetpack Navigation 3**는 기존 Navigation 2의 블랙박스 백스택 대신 개발자가 직접 SnapshotStateList<NavKey> 형태의 백스택을 소유하도록 재설계되었다. 여기에 SceneStrategy(ListDetailSceneStrategy, SupportingPaneSceneStrategy)와 **NavigationSuiteScaffold**를 결합하면 화면 크기에 따라 다음과 같이 자동 전환된다.
- 소형 화면(Compact): 하단
NavigationBar+ 단일 화면 전환
- 중·대형 화면(Medium/Expanded): 좌측
NavigationRail또는NavigationDrawer+ 리스트·디테일 2단 분할(ListDetailScene) 동시 렌더링
- 사용자 경험 개선 수치: 실제로 일정 관리 앱
Gratitude는 리스트-디테일 레이아웃 적용 후 대화면 일일 설치 수가 2배(100%) 증가했고, F1 팬 앱Box Box Club은 활성 사용자 7.6% 증가 및 체류 시간 54% 상승을 기록했다.
4. 코딩 에이전트를 위한 Compose Adaptive Skill과 실무 도입 체크리스트
구글은 기존 앱을 적응형으로 리팩터링할 때 코딩 에이전트(Gemini CLI, Claude Code, Android Studio 에이전트 등)에 바로 주입할 수 있는 공식 **Compose Adaptive Skill (SKILL.md)**을 대폭 업그레이드했다. 세션 데모에서는 Gemini 3 Flash 모델에 이 스킬을 장착해 Jetcaster 샘플 앱을 약 20분(약 20만 토큰) 만에 리팩터링하며 화면 회전 시 스크롤·재생 상태 유실 버그 수정, 폴더블 Tabletop 모드 지원, 데스크톱 키보드 단축키·마우스 호버·커스텀 커서·툴팁 적용까지 자동으로 완료하는 과정을 시연했다.
실무 프로젝트에서 Android 17 대화면 의무화에 대비하기 위해 당장 점검해야 할 핵심 항목은 다음과 같다.
- 매니페스트 고정 속성 제거 점검:
AndroidManifest.xml에 남아 있는android:screenOrientation="portrait",android:resizeableActivity="false",android:maxAspectRatio설정과 Android 16 임시 opt-out 플래그를 찾아 제거 계획을 세운다.
remember를rememberSaveable로 승격: 구성 변경이나 프로세스 재생성 시에도 검색어, 탭 선택, 스크롤 위치가 날아가지 않도록 UI 상태를rememberSaveable또는ViewModel로 이전한다.
- 최상위 분기 대신 레이아웃 레벨 적응화 적용: 화면 크기에 따라 화면 컴포저블 자체를 분기(
if (isExpanded) ExpandedScreen() else CompactScreen())하기보다,NavigationSuiteScaffold와Navigation 3 SceneStrategy,GridNamed Area를 활용해 하위 컴포저블 인스턴스를 유지한다.
- 데스크톱 입력 경험(
pointerPrecision,keyboardKind) 보강: 태블릿·크롬북·데스크톱 윈도우 사용자를 위해mediaQuery로 마우스 및 물리 키보드 연결을 감지하고, 호버(Hover) 피드백과 핵심 키보드 단축키(Space재생/일시정지 등)를 추가한다.
참고 자료
- Scale your app for every screen with Compose — Android Developers YouTube 공식 영상 (October 29, 2026 게시, November 2, 2026 확인)
- Build adaptive apps | Jetpack Compose 공식 문서 (November 2, 2026 확인)
- Query information for adaptive layouts with mediaQuery | Android Developers (November 2, 2026 확인)
- FlexBox | Adaptive Apps 공식 가이드 (November 2, 2026 확인)
- Jetpack Navigation 3 & Scenes 공식 가이드 (November 2, 2026 확인)
- A guide to making an app adaptive (Compose Adaptive Agent Skill) (November 2, 2026 확인)