iOS
고친 사람 github-actions[bot]
iOS 는 iPhone 에서 여러 앱이 안전하게 돌도록 관리해 주는 운영체제입니다. Apple 이 iPhone 과 함께 직접 만듭니다. 앱은 카메라나 화면을 직접 만지지 않고 iOS 에 부탁해서 씁니다.
쉽고 빠른 이해
iOS 는 iPhone 속 앱들을 관리하는 운영체제입니다. 사진 앱이 카메라를 쓰려면 iOS 를 거쳐 사용자의 허락을 받아야 합니다.
이게 없으면 앱마다 휴대폰 부품을 다루는 코드를 따로 짜야 합니다. 앱 하나가 다른 앱의 파일을 마음대로 읽을 수도 있습니다.
돌아가는 방식은 이렇습니다.
- 개발자가 앱을 휴대폰이 바로 읽는 기계어로 빌드합니다. 누가 만들었는지 확인하는 서명도 붙입니다
- Apple 의 심사를 통과하면 앱 스토어에 올라가 사용자에게 설치됩니다
- 설치된 앱은 칸막이 친 방에서 돕니다. 화면에서 사라지면 곧 멈춥니다
대가가 있습니다. 앱은 뒤에서 오래 돌지 못합니다. 그러니 서버가 보내는 알림은 Apple 의 알림 서버를 거쳐야 합니다. 버그를 고친 새 버전도 심사를 다시 받아야 해서 사용자에게 닿기까지 시간이 걸립니다.
상세
iOS 는 Apple 이 iPhone 에 올리는 운영체제입니다. 운영체제는 메모리와 저장 공간, 화면 같은 하드웨어를 여러 프로그램에게 나눠 주는 프로그램입니다. 프로그램끼리 서로 망가뜨리지 못하게 막는 일도 합니다.
이름 끝의 OS 는 Operating System, 곧 운영체제의 줄임말입니다. 처음 이름은 iPhone OS 였습니다. 뒤에 iOS 로 바뀌었습니다. iPad 용은 나중에 iPadOS 라는 이름으로 갈라져 나왔습니다.
아래 소절은 이런 차례로 이어집니다. 기기와 운영체제를 한 회사가 만드는 방식, 커널부터 쌓인 계층, 앱이 기계어로 빌드되는 과정, 메모리를 치우는 방식, 샌드박스, 코드 서명, 서버 연결 규칙, 앱의 실행 상태, 푸시 알림, App Store 심사입니다. 이 가운데 서버 연결 규칙부터 뒤쪽은 앱과 통신하는 백엔드에도 닿는 이야기입니다.
기기와 함께 만드는 운영체제
iOS 는 Apple 이 만든 기기에서만 돕니다. Android 처럼 여러 휴대폰 제조사가 받아 자기 기기에 올리는 운영체제가 아닙니다. 운영체제와 기기를 한 회사가 함께 설계합니다.
이 방식은 기기 종류를 적게 만듭니다. 화면 크기와 부품의 조합이 몇 가지로 정해져 있습니다. 앱을 시험해 볼 기기도 그만큼 적습니다.
새 버전도 Apple 이 지원하는 모든 기기에 직접 내려보냅니다. 제조사나 통신사를 거치지 않으므로 사용자가 새 버전으로 빨리 옮겨 갑니다. 버전이 여럿으로 흩어진 상태를 파편화라고 부릅니다. iOS 는 Android 보다 파편화가 덜합니다.
대가는 고를 수 있는 것이 적다는 것입니다. 오래된 기기는 어느 시점부터 새 버전을 받지 못합니다. 사용자와 제조사가 운영체제를 입맛대로 고쳐 쓸 수도 없습니다.
Darwin 위의 계층 구조
iOS 는 여러 겹의 계층으로 되어 있습니다. 맨 밑 계층의 바탕은 Darwin 입니다. 그 위로 세 겹이 더 쌓입니다.
Darwin 은 Apple 이 소스 코드를 공개한 운영체제 바탕입니다. macOS 도 같은 Darwin 위에 섭니다. iOS 전체는 공개되어 있지 않지만 이 바닥은 누구나 읽을 수 있습니다.
아래 그림은 계층이 쌓인 순서입니다. 위에 놓인 계층일수록 앱에 가깝습니다. 맨 아래에 하드웨어가 있습니다.
flowchart TD
A["앱"] --- T["Cocoa Touch · 화면과 터치"]
T --- M["Media · 그림 · 소리 · 영상"]
M --- S["Core Services · 네트워크 · 데이터 저장"]
S --- O["Core OS · Darwin"]
O --- H["하드웨어"]
앱은 대개 맨 위의 Cocoa Touch 계층만 부릅니다. Cocoa Touch 는 화면을 그리고 손가락 터치를 받는 기능을 앱에 묶어 주는 계층입니다.
계층 안의 기능은 프레임워크 단위로 나뉩니다. 프레임워크는 관련된 기능과 화면 부품을 한데 묶은 라이브러리 꾸러미입니다.
Cocoa Touch 의 대표 프레임워크는 UIKit 입니다. UIKit 은 버튼과 목록 같은 화면 부품을 만들어 줍니다. 손가락 터치를 받아 앱 코드에 넘기는 일도 UIKit 이 합니다.
그 아래 Media 계층은 그림과 소리, 영상을 다룹니다. Core Services 계층은 네트워크 통신과 데이터 저장처럼 화면과 상관없는 기본 기능을 모읍니다. 맨 밑 Core OS 계층에 Darwin 이 있습니다.
Darwin 의 한가운데에는 커널이 있습니다. 커널은 운영체제 한가운데에서 메모리와 실행 순서, 장치를 직접 다루는 부분입니다. iOS 의 커널 이름은 XNU(X is Not Unix)입니다.
XNU 는 두 뿌리를 합친 커널입니다. 한 뿌리는 Mach 라는 커널입니다. 스레드 관리와 메시지 전달처럼 꼭 필요한 일만 맡도록 작게 만든 커널입니다.
다른 뿌리는 BSD(Berkeley Software Distribution)입니다. BSD 는 버클리 대학에서 나온 유닉스 계열 운영체제입니다. 파일 시스템과 네트워크, 프로세스 권한 같은 부분이 이쪽에서 왔습니다.
BSD 쪽 코드는 많은 부분을 FreeBSD 에서 가져왔습니다. FreeBSD 는 BSD 를 이어 지금도 개발되는 공개 운영체제입니다.
Swift 와 기계어 빌드
iOS 앱은 주로 Swift 로 짭니다. Swift 는 Apple 이 자기 운영체제의 앱을 짜라고 만든 언어입니다. 그 전에는 Objective-C 를 썼습니다. 지금도 오래된 코드와 라이브러리에 Objective-C 가 남아 있습니다.
빌드는 Xcode 에서 합니다. Xcode 는 Apple 이 내놓는 개발 도구 모음입니다. 코드 편집기와 컴파일러, 앱에 서명하는 도구가 다 들어 있습니다.
Xcode 는 macOS 에서만 돕니다. 그래서 iOS 앱을 빌드하려면 Mac 이 있어야 합니다. 빌드를 자동으로 돌리는 지속적 통합(Continuous Integration, CI) 서버도 macOS 기계로 둡니다.
Swift 코드는 컴파일러가 휴대폰 프로세서의 기계어로 바로 옮깁니다. 기계어는 프로세서가 받아서 곧장 실행하는 명령어입니다. Java 처럼 중간 단계의 바이트코드를 만들어 가상 머신 위에서 돌리는 방식이 아닙니다. Android 앱과 가장 크게 갈리는 대목이 이것입니다.
이렇게 실행하기 전에 기계어로 다 옮겨 두는 것을 AOT 컴파일(Ahead-Of-Time compilation, 미리 하는 컴파일)이라고 부릅니다. 실행 중에 번역하는 비용이 들지 않습니다. 빌드한 기계어는 이미지와 화면 파일과 함께 설치 파일 하나로 묶입니다.
메모리를 치우는 ARC
백엔드에서 Java 나 Go 를 쓰면 안 쓰는 객체를 가비지 컬렉션이 치웁니다. 가비지 컬렉션은 런타임이 틈틈이 살아 있는 객체를 찾아보고 나머지를 지우는 방식입니다. 치우는 동안 프로그램을 잠깐 세우기도 합니다. 이 소절은 iOS 앱이 이 일을 어떻게 다르게 하는지를 짧은 코드 하나로 봅니다.
iOS 앱은 가비지 컬렉션을 쓰지 않습니다. 대신 ARC(Automatic Reference Counting, 자동 참조 카운팅)를 씁니다. 객체마다 자기를 가리키는 참조가 몇 개인지 세다가, 그 수가 0 이 되는 순간 바로 지웁니다.
참조 수를 늘리고 줄이는 코드는 컴파일러가 빌드할 때 끼워 넣습니다. 개발자가 직접 적지 않아서 「자동」이라는 이름이 붙었습니다. 아래 코드는 줄마다 참조 수가 어떻게 바뀌는지를 오른쪽 주석으로 보입니다.
class Order {}
var a: Order? = Order() // 참조 1
var b = a // 참조 2
a = nil // 참조 1
b = nil // 참조 0 → 해제
객체는 마지막 줄에서 곧바로 사라집니다. 가비지 컬렉션이 언젠가 치워 주는 것이 아니라, 지워지는 때가 코드로 정해집니다. 그래서 가비지 컬렉션 때문에 앱이 잠깐 서는 일이 없습니다.
대가는 순환 참조입니다. 두 객체가 서로를 가리키면 둘 다 참조 수가 1 아래로 내려가지 않습니다. 아무도 안 쓰는데 지워지지도 않는 메모리 누수가 됩니다.
이 고리를 끊으려고 한쪽 참조를 약한 참조로 둡니다. 약한 참조는 가리키기만 하고 참조 수를 늘리지 않는 참조입니다. Swift 에서는 weak 라는 낱말을 붙여 표시합니다.
샌드박스와 권한
iOS 는 앱마다 전용 폴더를 하나씩 줍니다. 앱은 자기 폴더 밖의 파일을 읽지 못합니다. 다른 앱의 폴더도 마찬가지로 닫혀 있습니다.
이렇게 앱을 하나씩 가두는 것을 샌드박스라고 부릅니다. 앱 하나에 버그나 악성 코드가 있어도 옆 앱의 데이터에 손대지 못합니다. 대가는 앱끼리 무언가를 나누려면 iOS 가 열어 둔 방법만 써야 한다는 것입니다.
폴더 밖의 것을 쓰려면 사용자의 허락이 있어야 합니다. 카메라, 위치, 연락처, 사진이 그런 것입니다. 앱이 처음 쓰려는 순간 iOS 가 창을 띄워 사용자에게 묻습니다. 사용자가 거절하면 앱은 그 기능 없이 돌아야 합니다.
그 창에 뜰 설명 문구는 앱이 미리 적어 둬야 합니다. 적는 곳은 Info.plist 라는 설정 파일입니다. 앱의 이름과 버전, 필요한 권한 같은 정보를 키와 값의 짝으로 적는 파일입니다.
<key>NSCameraUsageDescription</key>
<string>영수증을 찍으려고 카메라를 씁니다</string>
방금 본 두 줄은 카메라를 쓰는 까닭을 적은 것입니다. 사용자에게 묻는 창에 이 문구가 뜹니다. 문구를 적지 않고 카메라를 쓰려 하면 iOS 는 앱을 바로 끝냅니다.
코드 서명
iOS 는 코드 서명이 없는 코드를 실행하지 않습니다. 코드 서명은 누가 이 코드를 만들었고 그 뒤로 바뀌지 않았는지를 인증서로 보증하는 일입니다. iOS 앱에는 Apple 이 발급한 인증서로 만든 서명이 붙습니다.
iOS 는 앱을 띄울 때 이 서명을 확인합니다. 서명이 없거나 설치 뒤에 코드가 바뀌었으면 실행을 거부합니다. 누군가 앱 파일을 몰래 고쳐 퍼뜨려도 휴대폰에서 돌지 않는 까닭입니다.
같은 까닭으로 보통의 앱은 실행 중에 기계어를 새로 만들어 돌릴 수 없습니다. 새로 만든 기계어에는 서명이 없기 때문입니다. 서버의 Java 가상 머신이 쓰는 JIT 컴파일(Just-In-Time compilation, 실행 중에 하는 컴파일)을 iOS 앱은 쓰지 못한다는 뜻입니다.
HTTPS 만 받는 서버 연결
iOS 앱이 서버에 연결할 때는 기본으로 HTTPS(Hypertext Transfer Protocol Secure)만 허용됩니다. HTTPS 는 암호화한 HTTP 입니다. 앱이 암호화하지 않은 HTTP 로 요청하면 iOS 가 연결을 막습니다.
이 규칙의 이름은 App Transport Security 입니다. Info.plist 에 예외 도메인을 적어 풀 수는 있습니다. 앱이 부르는 백엔드는 믿을 만한 인증서를 단 HTTPS 로 열어 두는 것이 전제가 됩니다.
앱의 실행 상태
휴대폰은 메모리가 적고 배터리로 돕니다. 그래서 iOS 는 화면에 보이지 않는 앱을 오래 돌게 두지 않습니다. 이 소절은 앱이 거치는 다섯 상태를 그림으로 봅니다.
stateDiagram-v2
state "실행 안 됨" as N
state "비활성" as I
state "활성" as A
state "백그라운드" as B
state "일시 정지" as S
[*] --> N
N --> I: 앱을 띄움
I --> A: 입력을 받기 시작
A --> I: 전화가 옴
I --> B: 홈 화면으로 나감
B --> I: 다시 앞으로 옴
B --> S: 짧은 시간이 끝남
S --> B: 다시 깨움
S --> N: 메모리가 모자람 · 알림 없음
활성은 앱이 화면 맨 앞에서 손가락 입력을 받는 상태입니다. 비활성은 화면에는 있지만 입력을 못 받는 잠깐의 상태입니다. 전화가 오거나 다른 앱으로 넘어갈 때 이 상태를 거칩니다. 실행 안 됨은 앱이 아직 뜨지 않았거나 이미 끝난 상태입니다.
사용자가 홈 화면으로 나가면 앱은 백그라운드로 내려갑니다. 백그라운드에서는 하던 일을 마무리할 짧은 시간만 받습니다. 그 시간이 지나면 iOS 가 앱을 일시 정지 상태로 만듭니다.
일시 정지한 앱은 메모리에 남아 있지만 코드는 한 줄도 돌지 않습니다. 메모리가 모자라면 iOS 는 이런 앱부터 알림 없이 끝냅니다. 그림의 마지막 화살표가 이 경우입니다. 사용자가 다시 부르면 일시 정지한 앱은 백그라운드를 거쳐 앞으로 돌아옵니다.
음악 재생이나 길 안내처럼 뒤에서 계속 돌아야 하는 앱은 미리 신고한 경우에만 백그라운드에 오래 남습니다. 이 신고도 Info.plist 에 적습니다.
백엔드 쪽에서 보면, 앱이 보낸 요청은 응답이 오기 전에 끊길 수 있습니다. 그사이 앱이 멈추기 때문입니다. 그러면 앱이 다음에 깨어났을 때 같은 요청을 다시 보내는 일이 생깁니다. 서버가 같은 요청을 두 번 받아도 결과가 한 번과 같게 짜는 멱등성이 이때 쓰입니다.
큰 파일을 주고받을 때는 사정이 다릅니다. 앱이 멈춘 동안에도 전송을 이어 가게 할 수 있습니다.
iOS 앱은 서버 요청을 보통 URLSession 으로 보냅니다. URLSession 은 iOS 가 앱에 주는 HTTP 통신 도구입니다. 이 도구의 백그라운드 세션에 전송을 맡기면 앱이 멈춰도 iOS 가 전송을 대신 이어 줍니다. 앱이 멈췄다고 큰 파일을 처음부터 다시 보낼 일이 그만큼 줄어듭니다.
푸시 알림과 APNs
일시 정지한 앱은 코드가 돌지 않습니다. 그래서 서버에 새 소식이 있는지 스스로 물어볼 수 없습니다. 이 소절은 서버가 앱에게 먼저 알리는 길을 백엔드 쪽에서 봅니다.
그 길이 APNs(Apple Push Notification service)입니다. APNs 는 Apple 이 운영하는 알림 중계 서버입니다. 모든 앱의 푸시 알림이 이 서버를 거쳐 기기로 갑니다.
중계 서버를 하나로 모은 까닭은 배터리입니다. 앱마다 제 서버와 연결을 붙잡고 있으면 배터리가 빨리 닳습니다. 기기는 APNs 와 연결 하나만 유지합니다. 모든 앱의 알림을 그 연결로 받습니다.
sequenceDiagram
participant 앱
participant APNs
participant 백엔드
앱->>APNs: 알림을 받겠다고 등록
APNs-->>앱: 디바이스 토큰
앱->>백엔드: 디바이스 토큰을 보냄
백엔드->>APNs: 토큰과 알림 내용
APNs-->>앱: 알림 전달
그림은 알림 하나가 오가는 차례입니다. 앱이 등록하면 APNs 가 디바이스 토큰을 돌려줍니다. 디바이스 토큰은 「이 기기의 이 앱」을 가리키는 주소 같은 값입니다. 앱은 이 토큰을 자기 백엔드에 보내 저장해 둡니다.
알림을 보낼 때 백엔드는 기기가 아니라 APNs 에 요청합니다. 요청에는 저장해 둔 토큰과 알림 내용을 싣습니다. APNs 는 백엔드가 그 앱의 주인인지 확인하려고 인증을 요구합니다. Apple 에서 받은 키로 서명한 JWT(JSON Web Token)를 싣거나, 인증서로 연결하는 두 방법이 있습니다.
토큰은 바뀔 수 있습니다. 앱을 지웠다가 다시 설치하면 새 토큰이 나옵니다. 그래서 백엔드는 앱이 토큰을 보내올 때마다 저장한 값을 새것으로 바꿉니다. APNs 가 더는 쓰지 않는 토큰이라고 답하면 그 토큰을 지웁니다.
App Store 심사와 배포
iOS 앱은 대개 App Store 를 거쳐 설치됩니다. App Store 는 Apple 이 운영하는 앱 마켓입니다. 올라가는 앱은 새 버전을 낼 때마다 Apple 의 앱 심사를 받습니다. 규칙에 어긋나면 반려됩니다. 그때는 고쳐서 다시 올려야 합니다.
심사에는 시간이 걸립니다. 그래서 버그를 고친 새 버전도 사용자에게 닿기까지 늦어질 수 있습니다. 사용자가 앱을 바로 업데이트하지 않는 경우도 많습니다. 결국 서버에는 여러 버전의 앱이 동시에 요청을 보냅니다.
백엔드가 API(Application Programming Interface)를 바꿀 때는 옛 앱의 요청도 받도록 하위 호환을 지킵니다. API 는 여기서 앱이 서버에 보내는 요청의 형식을 말합니다. 앱을 다시 내지 않고 동작을 바꾸려고 서버에서 켜고 끄는 기능 플래그를 두기도 합니다.
정식 출시 전에는 TestFlight 로 시험판을 나눠 줍니다. TestFlight 는 초대받은 사람이 출시 전 빌드를 받아 써 보게 하는 Apple 의 배포 도구입니다.
앱 안에서 디지털 상품을 팔 때는 Apple 의 인앱 결제를 거치는 것이 기본 규칙입니다. 결제가 끝나면 백엔드는 그 거래가 진짜인지 Apple 서버에 물어 확인합니다. 앱이 보내온 결제 결과만 믿으면 조작된 요청에 속을 수 있기 때문입니다.
관련 항목
iOS 가 속하는 운영체제 갈래
운영체제 · 모바일 운영체제 · 유닉스 · Darwin · 임베디드 시스템
iOS 와 같은 일을 두고 겨루는 모바일 운영체제
Android · HarmonyOS · Windows Phone · BlackBerry OS
iOS 와 Darwin 을 나눠 쓰는 Apple 운영체제
macOS · iPadOS · watchOS · tvOS · visionOS
iOS 를 이루는 계층과 커널
커널 · XNU · Mach · BSD · FreeBSD · 마이크로커널 · Cocoa Touch · Core Services · Core OS
iOS 앱을 짓는 언어와 도구
Swift · Objective-C · Xcode · UIKit · SwiftUI · LLVM · AOT 컴파일 · 기계어 · 시뮬레이터 · 지속적 통합
iOS 앱이 메모리를 치우는 방식
ARC · 참조 카운팅 · 약한 참조 · 순환 참조 · 메모리 누수 · 가비지 컬렉션
iOS 가 앱을 가두는 보안 장치
샌드박스 · 권한 · Info.plist · 코드 서명 · 인증서 · JIT 컴파일 · App Transport Security · HTTPS · TLS · 키체인
iOS 앱이 거치는 실행 상태와 제약
생명주기 · 백그라운드 실행 · URLSession · 프로세스 · 파편화 · 멱등성
iOS 앱이 사용자에게 가는 배포 경로
앱 스토어 · 앱 심사 · TestFlight · App Store Connect · 단계적 출시 · 인앱 결제
iOS 앱과 서버가 만나는 통로
APNs · 푸시 알림 · 디바이스 토큰 · JWT · API · 하위 호환 · API 버전 관리 · 기능 플래그 · 모바일 개발
iOS 를 만들고 싣는 회사와 기기
Apple · iPhone · iPad · iPod touch
다른 이름: 아이오에스 · ios · iPhone OS