운영체제 판올림 파손
고친 사람 github-actions[bot]
운영체제 판올림 파손은 운영체제가 새 판으로 올라간 뒤 멀쩡하던 앱이 깨지는 일입니다. 앱 코드는 한 줄도 바뀌지 않았습니다. 운영체제가 앱이 기대던 동작을 바꾸거나 막아서 생깁니다. 휴대폰을 업데이트한 다음 날부터 어떤 앱이 안 되는 일이 대표적입니다.
쉽고 빠른 이해
운영체제 판올림 파손은 운영체제를 업데이트했더니 앱이 깨지는 일입니다. 달리기 기록 앱이 어제까지는 화면을 꺼도 경로를 기록했습니다. 업데이트 뒤로는 화면을 끄면 기록이 멈춥니다.
왜 생기나 — 운영체제는 판을 올리면서 배터리와 사생활을 지키려고 앱이 할 수 있는 일을 조금씩 좁힙니다. 앱이 그 동작에 기대고 있었다면 앱은 그대로여도 결과가 바뀝니다.
어떻게 터지나:
- 앱이 운영체제의 어떤 동작에 기댑니다
- 새 판이 그 동작을 바꾸거나 막습니다
- 사용자가 기기를 새 판으로 올리면 앱이 그 변경을 맞습니다
셋 중 하나만 빠져도 앱은 깨지지 않습니다. 기기를 옛 판에 둔 사용자에게는 아무 일도 없습니다.
무엇이 나빠지나 — 개발자가 아무것도 안 했는데 장애가 납니다. 터지는 때는 사용자가 업데이트를 누르는 때라서 개발자가 고를 수 없습니다. 고친 앱을 내도 스토어 심사와 사용자의 앱 업데이트를 거쳐야 퍼집니다.
상세
운영체제는 앱에게 화면, 저장 공간, 네트워크, 배터리를 나눠 주는 소프트웨어입니다. 휴대폰에서는 Android와 iOS가 이 일을 맡습니다. 앱은 이 자원을 직접 만지지 않습니다. 운영체제에 부탁해서 씁니다.
운영체제도 다른 소프트웨어처럼 새 판이 나옵니다. 판을 새로 올리는 일을 판올림이라 부릅니다. 휴대폰 운영체제는 대개 해마다 큰 판을 하나씩 냅니다. 사용자는 업데이트 알림을 받고 기기를 새 판으로 올립니다.
운영체제 판올림 파손은 이 판올림이 앱을 깨는 일입니다. 앱 개발자는 아무것도 바꾸지 않았습니다. 바뀐 것은 앱 아래에 깔린 운영체제뿐입니다.
이 절은 화면을 꺼도 달리기 경로를 기록하는 앱 하나로 이 문제를 따라갑니다. 앱이 운영체제의 무엇에 기대는지부터 봅니다.
앱이 기대는 운영체제의 동작
앱이 운영체제에 일을 부탁하는 통로를 API(Application Programming Interface, 응용 프로그램 인터페이스)라 합니다. API 는 「이 함수를 이렇게 부르면 이 일을 해 준다」는 약속의 묶음입니다. 위치를 달라는 함수, 파일을 열어 달라는 함수, 알림을 띄워 달라는 함수가 모두 API 입니다.
앱은 문서에 적힌 약속에만 기대지 않습니다. 문서에는 없지만 늘 그렇게 돌던 동작에도 기댑니다. 「화면을 꺼도 앱이 계속 돈다」는 누가 약속한 적이 없습니다. 그래도 오래 그랬다면 앱은 그것을 믿고 짜입니다.
판올림은 이 두 가지를 다 바꿀 수 있습니다. 문서에 적힌 약속을 고치기도 합니다. 문서에 없던 동작을 바꾸기도 합니다. 어느 쪽이든 그 동작에 기대던 앱은 깨집니다.
달리기 기록 앱 하나
달리기 기록 앱은 사용자가 시작을 누르면 몇 초마다 위치를 받아 경로를 그립니다. 사용자는 대개 화면을 끄고 휴대폰을 주머니에 넣은 채 달립니다. 그러니 앱은 화면이 꺼진 뒤에도 위치를 받아야 합니다.
화면에 보이지 않는 채로 앱이 도는 상태를 백그라운드 실행이라 합니다. 옛 판 운영체제는 백그라운드에서도 앱에 위치를 계속 주었습니다. 앱은 이 동작을 믿고 짜였습니다.
이제 새 판 운영체제가 백그라운드 실행을 좁혔다고 합시다. 백그라운드에서 위치를 받으려면 사용자에게 따로 허락을 받아야 합니다. 앱에는 이 허락을 청하는 코드가 없습니다.
sequenceDiagram
participant 사용자
participant 앱
participant 운영체제 as 새 판 운영체제
사용자->>앱: 기록을 시작한다
앱->>운영체제: 위치를 계속 달라고 한다
운영체제-->>앱: 위치를 준다
사용자->>운영체제: 화면을 끈다
Note over 운영체제: 백그라운드 위치 허락이 없다
운영체제->>앱: 위치 전달을 멈춘다
Note over 앱: 오류 없이 기록이 끊긴다
달리기를 마친 사용자는 경로가 출발 직후에서 끊긴 것을 봅니다. 앱은 죽지도 않았고 오류도 내지 않았습니다. 운영체제가 앱이 기대던 동작을 바꾼 것입니다.
운영체제가 동작을 바꾸는 까닭
운영체제는 앱을 깨려고 동작을 바꾸지 않습니다. 대개 사용자를 지키려고 앱이 할 수 있는 일을 좁힙니다. 좁히는 방향은 몇 가지로 모입니다.
| 바뀌는 동작 | 운영체제가 바꾸는 까닭 | 기대던 앱이 겪는 일 |
|---|---|---|
| 백그라운드 실행 | 보이지 않는 앱이 배터리와 메모리를 쓴다 | 화면을 끄면 작업이 멈춘다 |
| 권한 | 위치, 연락처, 사진은 사생활이다 | 허락을 못 받아 기능이 빈다 |
| 저장소 접근 | 한 앱이 다른 앱의 파일을 못 읽게 한다 | 공용 폴더에서 파일을 못 찾는다 |
| 네트워크 | 암호화하지 않은 통신은 엿보인다 | 서버로 가는 요청이 막힌다 |
| 오래된 함수 | 안 쓰는 기능을 계속 지고 가면 짐이다 | 부르던 함수가 없어 앱이 죽는다 |
| 화면 | 카메라 구멍이 난 기기와 어두운 바탕 설정을 받아들인다 | 화면 일부가 가려지거나 글자가 배경에 묻힌다 |
표의 위 다섯 줄은 한 방향을 가리킵니다. 앱이 하던 일을 운영체제가 더 좁게 허락합니다. 할 수 있는 일을 넓혀 주는 변경은 앱을 거의 깨지 않습니다. 좁히는 변경이 앱을 깹니다.
마지막 화면 줄은 결이 다릅니다. 새 판은 앱의 일을 좁히지 않습니다. 대신 앱이 그려질 화면의 꼴을 늘립니다. 화면 위쪽에 카메라 구멍이 뚫린 기기나 어두운 바탕 설정이 그런 예입니다. 앱이 네모난 화면과 밝은 바탕만 가정했다면 그 가정이 깨집니다.
권한은 앱이 위치나 사진처럼 민감한 것에 손대도 된다는 사용자의 허락입니다. 사용자가 모르는 사이에 앱이 사생활을 들여다보지 못하게 하려고 둡니다.
Android 는 설치할 때 한꺼번에 받던 허락을 기능을 쓰는 순간에 따로 묻도록 바꿨습니다. 이렇게 실행 중에 묻는 허락을 런타임 권한이라 합니다. 사용자는 그 기능이 필요해진 때에 허락할지를 고릅니다.
이 변화를 모르는 앱은 허락을 청하는 코드가 없습니다. 그러니 허락을 끝내 못 받습니다. 위치를 부르면 빈 값이 오거나 앱이 죽습니다.
옛 앱을 지키는 호환 장치
옛 앱이 새 판에서도 계속 돌게 지켜 주는 성질을 하위 호환성이라 합니다. 운영체제도 판올림이 앱을 깬다는 것을 압니다. 그래서 많은 변경을 모든 앱에 한꺼번에 걸지 않습니다. 앱이 어느 판을 겨냥해 만들어졌는지를 보고 가립니다.
Android 앱은 자기가 겨냥한 운영체제 판을 설정 파일에 적습니다. 이 값을 타깃 API 수준이라 부릅니다. 운영체제는 옛 판을 겨냥한 앱에는 옛 동작을 계속 내줍니다. 새 판을 겨냥하도록 올린 앱에만 새 동작을 겁니다.
SDK(Software Development Kit, 소프트웨어 개발 키트)는 앱을 만드는 데 쓰는 운영체제 쪽 도구와 라이브러리 묶음입니다. 새 판의 SDK 로 바꿔야 새 판이 내준 API 를 쓸 수 있습니다.
iOS 는 앱을 어느 판의 SDK 로 빌드했는지를 보고 일부 동작을 가립니다. 옛 SDK 로 빌드한 앱은 새 기기에서도 옛 동작을 받습니다. 그러니 iOS 에서는 새 SDK 로 빌드하는 것이 새 판을 겨냥하는 것에 해당합니다.
모든 변경이 겨냥한 판을 따지지는 않습니다. 사생활이나 보안에 걸린 변경은 모든 앱에 한꺼번에 걸기도 합니다. 이런 변경은 사용자가 기기를 올리는 날 바로 앱을 깹니다.
flowchart TD
A{"새 판의 변경이 모든 앱에 걸리나"} -->|걸린다| X["사용자가 기기를 올리는 날 깨진다"]
A -->|겨냥한 판에 따라 걸린다| B{"앱이 새 판을 겨냥했나"}
B -->|아니다| Y["옛 동작이 이어진다"]
B -->|그렇다| Z["새 판을 겨냥한 앱을 내는 날 깨진다"]
그림의 두 갈래에서 파손이 오는 때가 다릅니다. 모든 앱에 걸리는 변경은 사용자가 기기를 올리는 날 옵니다. 겨냥한 판에 따라 걸리는 변경은 개발자가 겨냥할 판을 올린 앱을 내는 날 옵니다.
그렇다고 옛 판만 겨냥하고 버틸 수는 없습니다. 앱 스토어는 너무 낡은 판을 겨냥한 앱을 새로 받아 주지 않습니다. 이 장치는 파손을 없애지 않습니다. 파손이 오는 때를 개발자가 고를 수 있게 미뤄 줄 뿐입니다.
재현되는 조건
운영체제 판올림 파손은 세 조건이 함께 설 때 생깁니다.
- 앱이 운영체제의 어떤 동작에 기댑니다. 문서에 적힌 동작이든 아니든 상관없습니다
- 새 판이 그 동작을 바꾸거나 막습니다
- 그 변경이 이 앱에 걸립니다. 모든 앱에 걸리는 변경이거나, 앱이 새 판을 겨냥했습니다
하나만 빠져도 앱은 깨지지 않습니다. 달리기 앱에 대 보면 이렇습니다. 앱은 백그라운드에서 위치를 계속 받는 동작에 기댔습니다. 새 판이 그 동작에 허락을 요구했습니다. 그 변경이 이 앱에 걸렸습니다.
둘째 조건 때문에 같은 앱이 어떤 기기에서는 되고 어떤 기기에서는 안 됩니다. 옛 판 기기에는 바뀐 동작이 아예 없습니다. 개발자 손에 있는 테스트 기기가 옛 판이면 개발자는 문제를 못 봅니다.
서버와 다른 점
서버에도 운영체제 판올림이 있습니다. 서버의 리눅스 판을 올리면 그 위에서 돌던 프로그램이 깨질 수 있습니다. 다만 모바일 앱에서 이 문제가 더 아픈 까닭은 판올림을 누가 언제 하느냐가 다르기 때문입니다.
| 서버 | 모바일 앱 | |
|---|---|---|
| 판올림을 정하는 사람 | 서버를 운영하는 개발자 | 기기를 쥔 사용자 |
| 판올림 시점 | 시험을 마친 뒤 고른다 | 사용자가 업데이트를 누를 때 |
| 한꺼번에 도는 판의 수 | 대개 하나로 맞춘다 | 여러 판이 섞여 돈다 |
| 문제가 났을 때 | 옛 판으로 되돌린다 | 사용자의 운영체제를 개발자가 못 되돌린다 |
| 고친 코드가 닿는 때 | 배포하면 바로 | 스토어 심사와 사용자의 앱 업데이트를 거친다 |
서버 개발자는 판올림 날짜를 자기가 고릅니다. 문제가 나면 되돌립니다. 모바일 개발자는 날짜도 되돌림도 손에 없습니다. 고친 앱을 내도 사용자가 앱을 업데이트할 때까지 깨진 앱이 남습니다.
시장에는 여러 운영체제 판과 여러 기기가 섞여 있습니다. 이 상태를 파편화라 부릅니다. 앱 하나가 옛 판 기기와 새 판 기기에서 함께 돌아야 합니다.
무엇이 나빠지나
가장 늦게 드러나는 경우는 앱이 죽지 않고 기능만 조용히 비는 것입니다. 앱이 실행 도중 갑자기 죽어 꺼지는 일을 크래시라 합니다. 크래시를 개발자가 바로 알 수 있도록, 많은 앱은 그 기록을 모아 개발자에게 보내 주는 크래시 보고 도구를 답니다.
달리기 앱은 죽지 않았으니 크래시 보고에 아무것도 남지 않습니다. 사용자 리뷰가 쌓이고 나서야 개발자가 알게 됩니다.
부르던 함수가 사라진 경우는 반대로 요란합니다. 앱이 켜자마자 크래시가 납니다. 새 판이 나온 날부터 업데이트한 사용자 수만큼 크래시가 한꺼번에 늘어납니다.
판올림이 해마다 오니 대응도 해마다 해야 합니다. 앱이 오래될수록 판마다 갈라 둔 코드가 쌓입니다. 그만큼 코드가 읽기 어려워지고 시험할 경우의 수도 늘어납니다.
미리 잡는 수단
운영체제를 만드는 쪽은 새 판을 정식으로 내기 전에 개발자용 미리보기 판을 먼저 냅니다. 바뀌는 동작을 적은 문서도 함께 냅니다. 개발자는 그 기간에 앱을 새 판에서 돌려 보고 깨지는 곳을 고칩니다.
앱 하나가 옛 판과 새 판에서 함께 돌아야 합니다. 그래서 코드 안에서 지금 기기의 판을 보고 갈라 가는 일이 흔합니다.
아래는 허락을 묻는 방식이 판마다 다를 때의 꼴을 특정 언어에 묶지 않고 적은 의사코드입니다. osVersion 은 지금 기기의 운영체제 판입니다. NEW_VERSION 은 허락 방식이 바뀐 판입니다.
if (osVersion >= NEW_VERSION) {
askFirst(); // 새 판: 묻고 쓴다
} else {
useNow(); // 옛 판: 바로 쓴다
}
새 판 기기에서는 쓰기 전에 사용자에게 허락을 묻습니다. 옛 판 기기에서는 묻지 않고 바로 씁니다. 앞 절에서 말한 「판마다 갈라 둔 코드」가 이런 조건문입니다.
판마다 기기를 다 갖출 필요는 없습니다. 에뮬레이터는 컴퓨터 안에서 휴대폰과 그 운영체제를 흉내 내는 프로그램입니다. 판마다 에뮬레이터를 띄우면 기기를 사지 않고도 여러 판에서 앱을 돌려 볼 수 있습니다.
이렇게 판마다 앱을 돌려 보는 시험이 회귀 테스트입니다. 회귀 테스트는 앞서 되던 기능이 여전히 되는지를 거듭 확인하는 시험입니다. 판올림 파손은 되던 것이 안 되는 일이라 이 시험에 걸립니다.
고친 앱은 단계적 출시로 냅니다. 단계적 출시는 새 앱을 사용자 일부에게 먼저 내보내는 방식입니다. 고친 앱에 새 문제가 있어도 일부 사용자만 겪습니다. 문제가 없으면 받는 사용자를 늘립니다.
관련 항목
판올림 파손을 키우는 모바일 환경
모바일 개발 · Android · iOS · 파편화 · 앱 스토어 · 앱 스토어 심사 · Google Play
판올림이 좁히는 운영체제 기능
백그라운드 실행 제한 · 권한 · 런타임 권한 · Doze · 포그라운드 서비스 · 범위 지정 저장소 · 위치 권한 · 다크 모드
운영체제가 옛 앱을 지키는 호환 장치
하위 호환성 · 타깃 API 수준 · SDK · 호환성 모드 · 지원 중단
판올림이 깰 수 있는 앱과 운영체제 사이의 접점
API · ABI · 시스템 콜 · 라이브러리 · 운영체제
판올림 파손을 미리 잡는 수단
베타 테스트 · 에뮬레이터 · 회귀 테스트 · 충돌 보고 · 단계적 출시 · 기능 감지 · 기능 플래그
판올림 파손과 원리가 같은 호환성 문제
하위 비호환 변경 · 하이럼의 법칙 · 시맨틱 버저닝 · 의존성 지옥 · 회귀
판올림 파손과 견주는 다른 앱 실패
앱 크래시 · ANR · 권한 거부 처리 누락 · 메모리 부족 킬러 · 앱 크기 비대
같은 문제를 겪는 다른 운영체제
다른 이름: OS 판올림 파손 · 운영체제 업데이트 파손 · OS 업데이트 파손 · OS update breakage · OS upgrade breakage