이번 글에서는 iOS의 ActivityKit (Live Activity)을 학습하면서 정리한 내용을 하나의 흐름으로 정리해보려고 한다.
단순히 API 설명이 아니라 실제 프로젝트에 적용한 구조를 기준으로 정리했다.
글의 구조는 다음 5단계로 구성되어 있다.
- 이론 설명
- 내 프로젝트 기준 해당 작업
- 실제 프로젝트 작업 위치
- 알게 된 것
- 학습 핵심 요약
이 글 하나로 ActivityKit의 전체 구조를 잡는 것이 목표다.
1️⃣ ActivityKit 이론 설명
Live Activity란 무엇인가
Live Activity는 앱의 진행 상태를 잠금화면(Lock Screen) 또는 Dynamic Island에 실시간으로 보여주는 기능이다.
대표적인 예시는 다음과 같다.
- 배달 상태 추적
- 택시 이동 상황
- 스포츠 경기 스코어
- 운동 기록
- 타이머 진행 상태
즉 현재 진행 중인 이벤트 상태를 계속 업데이트하면서 보여주는 UI라고 볼 수 있다.
Apple에서는 Live Activity를 다음과 같이 설명한다.
Live Activities는 앱의 작업 진행 상황을 잠금화면이나 Dynamic Island에서 한눈에 확인할 수 있도록 하는 기능이다.
일반 위젯과 차이도 중요하다.
구분특징
| WidgetKit | 일정 주기로 업데이트 |
| Live Activity | 상태 변화가 즉시 반영 |
즉 Live Activity는 “현재 진행 상태”에 특화된 UI다.
2️⃣ ActivityKit 핵심 개념
Live Activity는 크게 3가지 개념만 이해하면 된다.
1. ActivityAttributes
Activity의 **고정 정보(static data)**를 정의한다.
예시
- 주문 번호
- 매장 이름
- 사용자 이름
즉 활동 동안 변하지 않는 데이터다.
예시 코드
struct DeliveryAttributes: ActivityAttributes {
public struct ContentState: Codable, Hashable {
var status: DeliveryStatus
var eta: Date
}
var orderId: String
}
2. ContentState
Activity의 **변하는 상태(dynamic data)**를 정의한다.
예시
- preparing
- pickedUp
- nearby
- delivered
즉 시간에 따라 계속 바뀌는 데이터다.
정리하면
ActivityAttributes → static data
ContentState → dynamic data
이 구조가 Live Activity의 핵심이다.
3. Lifecycle
Live Activity는 다음 흐름으로 동작한다.
start → update → end
Activity 시작
Activity.request(...)
상태 업데이트
activity.update(...)
Activity 종료
activity.end(...)
즉 구조는 매우 단순하다.
모델 정의
→ Activity 시작
→ 상태 업데이트
→ Activity 종료
3️⃣ 내 프로젝트 기준 ActivityKit 적용
이번 학습에서는 DeliveryTracker 시나리오로 ActivityKit을 구현했다.
프로젝트 구조는 크게 두 부분으로 나뉜다.
App
Widget Extension
App
Activity 상태를 제어하는 역할
예시
start
update
end
Widget Extension
Activity 상태를 받아 UI를 렌더링
예시
Lock Screen
Dynamic Island
이렇게 분리하는 이유는 간단하다.
👉 상태 제어와 UI 렌더링의 책임을 분리하기 위해서
실무에서도 동일한 구조를 사용한다.
4️⃣ 실제 프로젝트에서 수행한 작업
ActivityKit을 학습하면서 다음 작업을 진행했다.
Live Activity 모델 정의
DeliveryAttributes
DeliveryStatus
Debug 패널 구현
앱에서 다음을 테스트할 수 있게 구현
start
update
end
Activity Manager 구현
Activity lifecycle 호출을 Manager로 통합
DeliveryActivityManager
Lock Screen UI 구현
Live Activity 기본 화면 구성
Dynamic Island UI 구현
3가지 레이아웃 구현
Compact
Minimal
Expanded
Activity 모니터
현재 활성 Activity 추적
ActivityMonitor
로그 분리
AppActivityLogger
WidgetActivityLogger
5️⃣ 실제 프로젝트 코드 위치
프로젝트 구조는 다음과 같다.
[스크린샷]



잠금화면과 다이나믹 아일랜드에 표시되는 위젯을 볼 수 있다
App (Activity 제어)
Debug Panel
DeliveryActivityDebugPanel.swift
Activity Manager
DeliveryActivityManager.swift
에러 처리
ActivityError.swift
Activity 상태 모니터
ActivityMonitor.swift
App Logger
AppActivityLogger.swift
Widget Extension (UI 렌더)
Live Activity Entry
DeliveryLiveActivity.swift
Lock Screen View
LockScreenView.swift
Dynamic Island Layout
DeliveryCompactLayout.swift
DeliveryExpandedLayout.swift
Widget Logger
WidgetActivityLogger.swift
Shared Model
앱과 위젯이 같은 모델을 사용하도록 공유
DeliveryAttributes.swift
DeliveryStatus.swift
이 부분이 상당히 중요하다.
6️⃣ 구현하면서 알게 된 것
ActivityKit을 직접 구현하면서 얻은 인사이트도 꽤 있었다.
1. 앱 / 위젯 분리는 필수
처음에는 하나의 구조에서 처리하려 했지만
상태 관리와 UI가 섞이면 구조가 복잡해진다.
분리 이후
- 코드 가독성
- 디버깅 속도
- 유지보수
모두 개선되었다.
2. 모델 중복은 반드시 문제를 만든다
앱과 위젯에서
enum
struct
이 조금이라도 다르면 Activity 업데이트가 꼬일 수 있다.
그래서
Shared Model
구조가 필요하다.
3. 로컬 테스트와 실제 서비스는 다르다
학습 단계
앱 버튼으로 start / update / end 테스트
실제 서비스
서버 → push → live activity update
이 두 가지는 완전히 다른 시나리오다.
4. 로그는 앱 / 위젯 둘 다 필요하다
ActivityKit은 프로세스가 분리되어 동작한다.
그래서
- 앱 로그
- 위젯 로그
두 가지를 모두 확인해야 전체 흐름을 이해할 수 있다.
5. Live Activity UI는 단순해야 한다
Live Activity는 표시 공간이 매우 제한적이다.
특히 Dynamic Island는
- compact
- minimal
- expanded
세 가지 레이아웃을 모두 고려해야 한다.
따라서 핵심 정보 중심 UI 설계가 중요하다.
7️⃣ 학습 핵심 요약
ActivityKit을 한 줄로 요약하면 다음과 같다.
ActivityKit은 상태 모델 + lifecycle + UI 렌더 분리 구조만 이해하면 된다.
핵심 포인트는 다음 5가지다.
1. ActivityAttributes / ContentState 구분
static(바뀌지 않는 정적) / dynamic(가변적) 데이터 분리
2. Activity lifecycle 통합
start / update / end는 Manager에서 관리
3. App / Widget 역할 분리
App → 상태 제어
Widget → UI 렌더
4. Shared 모델 사용
앱과 위젯 상태 계약 유지
5. 로컬 테스트 / 실제 서비스 구조 분리
로컬 테스트 → Debug Panel
실제 서비스 → Push 기반 업데이트
마무리
ActivityKit은 처음 보면 복잡해 보이지만 실제로는 다음 구조만 이해하면 된다.
ActivityAttributes
ContentState
Lifecycle
App / Widget 역할 분리
이 구조만 잡히면 Live Activity 구현 자체는 생각보다 단순하다.
'Tech Log > iOS' 카테고리의 다른 글
| SwiftUI의 some View / any View / AnyView 차이 정리 (0) | 2026.03.12 |
|---|---|
| AppEntity 에러로 이해한 Swift Concurrency: nonisolated vs MainActor.run (0) | 2026.03.10 |
| [WidgetKit] Weather Widget 학습 정리: TimelineEntry부터 AppIntent 인터랙션까지 (0) | 2026.02.26 |
| Github Issue 생성부터 PR 머지까지 프로세스 (0) | 2026.02.02 |
| 🦀 바위게캘린더 배포하다! (1) | 2025.10.14 |