아이폰 듀오에서 앱이 깨지는 건 화면 크기가 아니라 '화면'이라는 가정이에요

헤드라인

폴더블 대응이라고 하면 보통 "큰 화면 하나를 더 지원하면 되겠지"로 시작해요. 아이패드 대응을 한 번 해본 팀이라면 특히 그렇죠. 그런데 바이라인네트워크 김우용 기자가 정리한 애플의 아이폰 듀오 앱 최적화 안내를 읽어 보면, 애플이 내놓은 도구가 전부 한 방향을 가리키고 있다는 게 보입니다. 화면을 직접 보지 말라는 것. 크기 클래스를 보고, 안전 영역을 보고, 씬의 경계를 보라는 것. 그러니까 이건 화면이 하나 늘어난 문제가 아니라, 코드 곳곳에 박혀 있던 "화면은 하나이고 크기는 고정"이라는 가정을 지우는 문제예요.

기기 자체는 이렇습니다. 열면 19.3cm 로 아이패드 미니급, 닫으면 13.6cm 여권 크기. 펼쳤을 때와 접었을 때 화면 비율은 같고, 닫으면 콘텐츠가 조정되고 옆으로 돌리면 방향이 바뀌고 뒤집으면 다른 디스플레이로 넘어가요. 하나의 앱이 한 세션 안에서 이 전환을 전부 겪는다는 게 아이패드 대응과 결정적으로 다른 점이죠.

"SDK 로 빌드만 하면 된다"는 말의 정확한 범위

기사에는 세 단계가 나와요. iOS 27 SDK 로 빌드만 하면 기존 앱은 일단 돌아갑니다. 닫혀 있을 땐 상태 표시줄과 카메라 왼쪽 공간을 쓰고, 열면 아이패드에서 아이폰 앱을 띄운 것처럼 원래 크기와 비율을 유지해요. 화면 조정 설정을 켜면 열었을 때 상태표시줄 영역 왼쪽까지 확장되고, iOS 27.1 SDK 로 가야 상태표시줄 영역을 넘어 전체 화면을 씁니다.

저는 이 세 단계를 "호환성 레벨"이 아니라 "청구서가 도착하는 시점"으로 읽었어요. 첫 단계는 공짜예요. 아무것도 안 해도 앱이 뜨니까요. 대신 펼친 화면 한가운데 아이폰 크기 앱이 떠 있는 상태가 사용자에게 어떻게 보일지는 각자 상상해 보시면 됩니다. 세 번째 단계로 가는 순간부터 레이아웃이 실제로 늘어나고 회전하고, 그때 그동안 숨어 있던 고정 가정이 깨지기 시작해요. 디바이스 허브의 시뮬레이터로 열고 닫고 회전하는 동작을 테스트할 수 있다는 건, 뒤집어 말하면 그 조합을 전부 테스트해야 한다는 뜻이기도 하고요.

크기 클래스는 세 조합인데, 방향은 왜 믿지 말라는 걸까

가장 헷갈리기 쉬운 부분이 여기예요. 아이폰 듀오는 접었을 때 일반 아이폰과 똑같이 regular 세로 + compact 가로 크기 클래스를 씁니다. 접은 채로 90도 돌리면 compact 세로 + compact 가로. 펼치면 regular 가로 + regular 세로. 여기까지는 아이폰과 아이패드를 함께 지원해 본 팀이라면 익숙한 조합이죠.

아이폰의 디스플레이 크기 클래스

문제는 다 펼치지 않고 어느 정도만 열어둔 상태예요. 이때 내부 디스플레이의 인터페이스 방향이 달라지는데, 앱이 지원한다고 선언한 인터페이스 방향을 따르지 않습니다. 그래서 애플은 레이아웃을 결정할 때 방향을 확인하지 말고 크기 클래스를 쓰라고 해요. 저는 이 한 줄이 이번 안내에서 제일 중요한 문장이라고 봅니다. "가로면 사이드바, 세로면 스택"이라는 분기를 방향 값으로 짜 둔 앱은 반쯤 열린 기기에서 어떤 레이아웃이 나올지 아무도 모르게 되니까요.

아이폰듀오 내부 디스플레이를 꺾어 사용할 때의 크기 클래스

같은 맥락에서 메인 화면 참조도 하지 말라고 합니다. 두 개의 디스플레이가 있는 기기에서 코드가 "화면"을 가리키면 어느 화면인지부터 애매하고, 화면 참조 자체가 앞으로 점차 중단될 거라는 예고까지 붙어 있어요. 대신 환경, 트레이트 콜렉션, 씬의 경계 같은 국소적인 값을 쓰고, 꼭 필요하면 윈도우 씬에서 동적으로 접근하라는 거죠. SwiftUI 는 environment, UIKit 은 traitCollection 이고요. 그러니 기기가 무엇인지, 어느 방향인지, 화면이 몇 픽셀인지를 묻는 코드가 전부 빚이 됩니다. 그 코드가 지금까지 잘 돌아간 이유는 답이 하나뿐인 기기에서만 실행됐기 때문이에요.

안전 영역이 비대칭이 되는 순간

아이패드 대응에서 안전 영역은 대체로 대칭이었잖아요. 위아래 여백만 챙기면 됐고요. 아이폰 듀오에서는 스플릿뷰 멀티태스킹 상황에서 세로 방향 버튼이 왼쪽에 나타날 수 있어요. 그러면 왼쪽 여백과 오른쪽 여백이 다릅니다. 반대쪽 여백이 같다고 가정하지 말고 각 측면을 독립적으로 처리하라고 기사가 못 박는 이유가 이거예요. 레이아웃 여백도 비대칭이라서, 전경 콘텐츠가 한쪽에서는 세로 버튼과 상태 표시줄에 더 가까이 붙을 수 있고요.

아이폰듀오 사파리 스플릿뷰

탭 바는 안전 영역 바깥에 놓이고 상태 표시줄이나 카메라 같은 하드웨어를 알아서 피합니다. 가로 막대는 상단·하단 여백을, 세로 막대는 앞쪽·뒤쪽 여백을 만들어요. 데이비드 잭슨 애플 UI 프레임워크 엔지니어링 매니저의 말을 그대로 옮기면 "앱에서 안전 영역을 가장 효과적으로 적용하는 방법은 UI를 배치할 때 상호작용 가능한 컨트롤 같은 전경 요소를 안전 영역 내에 배치하는 것"입니다. SwiftUI 는 기본으로 그렇게 되고, UIKit 에서 뷰를 손으로 배치하는 코드는 안전 영역 여백을 참조하거나 오토 레이아웃 가이드에 맞춰야 하고요. 배경 아트워크처럼 툴바 뒤까지 채우고 싶은 요소만 ignoresSafeArea 나 뷰의 bounds 로 빼는 거죠.

iOS 27.1 에 들어온 ReserveRegion 과 UIViewReservedRegion 은 그 사이를 메우는 도구예요. 안전 영역 밖에 사용자 정의 바를 두고 싶은데 시스템 UI 와 충돌하면 안 되는 경우. 저는 이 API 가 "커스텀 하단 바를 직접 그린 팀"을 위한 구제책이라고 읽었는데, 그 팀은 애초에 이번 대응 비용이 제일 클 겁니다.

표준 내비게이션에 맡긴 팀이 공짜로 얻는 것

NavigationSplitView 와 UISplitViewController 는 닫혀 있을 때 열이 단일 스택으로 접히고, 열려 있을 때 타일이나 오버레이로 펼쳐집니다. TabView 와 UITabBarController 는 탭을 내부·외부 디스플레이에 맞춰 보여주고 상황에 따라 수직으로 세워요. 펼쳤을 때 더 풍부한 탐색을 원하면 기본 탭 바 위치를 사이드바로 바꾸는 설정 하나로 끝나고요. 시트는 외부 디스플레이에서 버튼이 수직으로, 내부 디스플레이에서는 중앙 정렬로 알아서 조정됩니다.

아이폰듀오 내비게이션 패턴

이 목록이 길다는 것 자체가 메시지예요. 표준 컴포넌트를 쓴 팀은 이번에 거의 아무것도 안 해도 되고, 브랜드 때문에 내비게이션과 탭 바를 직접 그린 팀은 이 목록을 전부 손으로 다시 만들어야 합니다. 디자인 시스템을 운영해 본 입장에서 이건 익숙한 청구서예요. 커스텀의 비용은 만들 때가 아니라 플랫폼이 움직일 때 옵니다. X코드 27.1 의 App Resizability 가 UIKit 에서 SwiftUI 와 듀오까지 확장된 것도 같은 결의 도구고요. 기존 앱을 현대화하는 쉬운 방법이라고 소개되는데, 커스텀이 많을수록 쉽지 않을 겁니다.

월요일에 할 일은 새 API 붙이기가 아니에요

이 안내의 도구는 전부 "화면을 가정하지 말라"의 변주예요. 그래서 대응의 첫 단계는 iOS 27.1 SDK 를 받는 게 아니라, 코드베이스에서 화면·기기 모델·인터페이스 방향으로 분기하는 곳을 찾아내는 일이라고 봅니다. 그 목록의 길이가 곧 이번 대응의 견적이에요. 디자인 쪽도 마찬가지고요. 기기별 시안 대신 compact/regular 조합 세 가지로 시안을 그리면, 반쯤 열린 상태 같은 애매한 경우가 자연스럽게 그중 하나로 떨어집니다.

조건을 붙이면 이래요. 아이패드 대응을 크기 클래스로 제대로 해 둔 팀은 이번이 거의 공짜에 가깝고, 아이폰과 아이패드를 두 개의 고정 브레이크포인트로 하드코딩해 둔 팀은 이번에 그 빚을 갚게 됩니다. 폴더블이 어려운 게 아니라, 그동안 미뤄 둔 유연한 레이아웃 원칙이 드디어 강제되는 거예요.