Lazy Property는 Swift에서 지연 초기화 메커니즘으로, 속성이 객체 생성 시가 아니라 첫 번째 액세스 시점에 계산됩니다. 일반 저장 속성과 달리, lazy 변수는 리소스 집약적 작업의 시작 시간과 메모리 소비를 크게 줄일 수 있습니다. Apple Developer Documentation (2026)에 따르면, lazy 속성은 한 번만 초기화되도록 보장되며 첫 번째 액세스 시 스레드 안전합니다.
핵심 사항
Lazy Property는 첫 번째 액세스까지 초기화가 지연되는 저장 속성입니다. var 선언 앞에 lazy 키워드를 사용하면 객체 생성 시 값을 계산하지 않도록 컴파일러에 지시합니다.
표준적인 경우, 모든 저장 속성은 클래스나 구조체의 인스턴스가 생성될 때 초기화됩니다. 속성이 복잡한 계산, 네트워크 로딩 또는 파일 시스템 작업을 필요로 하는 경우, lazy를 사용하면 데이터가 실제로 필요할 때까지 이 작업을 지연시킬 수 있습니다.
Swift by Sundell (2025)에 따르면, lazy 속성은 iOS 애플리케이션에서 가장 효과적인 성능 최적화 도구 중 하나입니다. 그래픽 편집기나 분석 대시보드와 같이 초기 설정이 무거운 앱에서는 시작 시간을 20~40% 줄일 수 있습니다.
lazy 속성을 선언하려면 var 앞에 lazy 키워드가 필요합니다. 값은 클로저나 함수 호출을 통해 설정됩니다.
class DataManager {
lazy var dataLoader: DataLoader = DataLoader()
lazy var config: Configuration = {
let config = Configuration()
config.loadDefaults()
return config
}()
}
lazy 속성의 클로저는 한 번만 계산되며 이후 액세스에서는 저장된 값을 반환합니다. 이는 여러 단계가 있는 복잡한 구성에 특히 편리합니다.
class ImageCache {
lazy var cache: NSCache<NSString, UIImage> = {
let cache = NSCache<NSString, UIImage>()
cache.countLimit = 100
cache.totalCostLimit = 50 * 1024 * 1024
return cache
}()
}
여러 lazy 속성은 서로 독립적으로 초기화됩니다. 두 lazy 속성이 클로저를 통해 서로를 참조하면 순환 의존성이 발생하며, Swift가 자동으로 해결할 수 없습니다.
초기화 시점이 lazy와 일반 저장 속성의 주요 차이점입니다. 초기화는 속성의 첫 번째 읽기 또는 쓰기 시에 발생하며, 그 이전에는 발생하지 않습니다.
초기화는 속성에 대한 첫 번째 액세스(읽기 또는 쓰기) 시 트리거됩니다. 그 시점까지는 속성에 메모리가 할당되지 않으며 — “초기화되지 않음” 플래그만 저장됩니다.
class ReportGenerator {
lazy var report: String = {
print("보고서 생성 중...")
return "Annual report data"
}()
}
let generator = ReportGenerator()
// 이 시점에서 보고서가 아직 생성되지 않음
print(generator.report) // "보고서 생성 중..."
print(generator.report) // 이후 액세스에서 재생성되지 않음
lazy 속성은 하위 클래스에서 재정의할 수 없습니다. 클래스가 lazy 속성을 선언하면, 하위 클래스는 이를 다른 동작을 가진 계산 속성이나 저장 속성으로 재정의할 수 없습니다.
Apple Swift Blog (2025)에 따르면, 이 제한은 lazy가 재정의 수정자가 아니라 저장 구현 세부 사항이기 때문입니다. 하위 클래스는 lazy 속성이 클로저 내에서 호출하는 메서드만 재정의할 수 있습니다.
Swift에서 lazy 속성의 초기화는 스레드 안전합니다. 두 스레드가 동시에 lazy 속성에 액세스하면 초기화는 한 번만 수행되고 두 번째 스레드는 완료될 때까지 차단됩니다.
그러나 초기화 후에는 동기화가 해제됩니다. 즉, 여러 스레드에서 동시에 읽는 것은 안전하지만, 한 스레드가 lazy var에 새 값을 쓰는 동안 다른 스레드가 읽으면 데이터 경합이 발생합니다.
Swift Evolution 제안 SE-0254 (2025)에 따르면, lazy 속성의 동기화 메커니즘은 낮은 수준에서 objc_sync_enter/exit을 통해 구현됩니다. 이는 원자적 초기화를 보장하지만 이후의 변경은 보장하지 않습니다. 변경 가능한 lazy 속성으로 스레드 안전하게 작업하려면 별도의 동기화 메커니즘을 사용하세요.
lazy 속성은 Swift 프로젝트에서 성능을 최적화하고 코드 아키텍처를 개선하는 데 사용됩니다.
이미지 로딩, JSON 파싱, 데이터베이스 작업 — 이러한 모든 작업은 사용자가 실제로 데이터를 필요로 할 때까지 lazy를 사용하여 지연시킬 수 있습니다.
class ProfileViewController {
lazy var avatarImageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
return imageView
}()
}
앱 실행 시 즉시 필요하지 않은 서비스와 관리자는 효과적으로 lazy로 선언됩니다. 이는 시작 시간과 메모리 소비를 줄입니다.
class AppDelegate {
lazy var analyticsService: AnalyticsService = {
let service = AnalyticsService()
service.configure()
return service
}()
lazy var notificationManager: NotificationManager = {
NotificationManager()
}()
}
Storyboard 없이 프로그래밍 방식으로 인터페이스를 구축할 때, lazy 속성을 사용하면 init을 복잡하게 만들지 않고 UI 요소 초기화를 구성할 수 있습니다.
첫 번째 제한: lazy는 let과 함께 사용할 수 없습니다. 상수는 init 완료 전에 초기화되어야 하며, 이는 지연 초기화 개념과 모순됩니다.
두 번째 제한: lazy는 계산 속성에서 사용할 수 없습니다. 계산 속성은 값을 저장하지 않는 반면, lazy는 지연 초기화가 있는 저장 속성입니다.
세 번째 제한: 구조체의 lazy 속성은 변경 문제를 일으킬 수 있습니다. lazy 속성이 있는 구조체가 let으로 선언된 경우, 초기화에 구조체 변경이 필요하므로 속성에 액세스할 수 없습니다.
네 번째: lazy 속성은 초기화 중에 willSet/didSet을 트리거하지 않습니다. 속성이 var로 선언된 경우 이후 변경 시에만 트리거됩니다.
Stack Overflow (2025)에 따르면, lazy에 대한 질문의 약 15%는 let 컨텍스트에서 lazy를 사용하려는 시도나 한 번만 초기화된다는 점에 대한 오해와 관련됩니다. 클래스를 설계할 때 이러한 제한을 고려하는 것이 중요합니다.
자주 묻는 질문
아니요, lazy는 var에서만 사용할 수 있습니다. let으로 선언된 상수는 init 완료 전에 초기화되어야 하며, 이는 지연 초기화와 호환되지 않습니다.
정확히 한 번입니다. 첫 번째 액세스 시 속성이 초기화된 후, 다시 계산하지 않고 저장된 값을 반환합니다.
초기화는 스레드 안전합니다 — 두 스레드가 속성을 두 번 초기화하지 않습니다. 그러나 이후의 변경은 자동으로 동기화되지 않습니다.
아니요, lazy 속성은 재정의할 수 없습니다. 하위 클래스는 클로저 내부의 로직만 변경할 수 있으며, lazy 초기화 자체는 변경할 수 없습니다.
Lazy는 한 번 계산되고 값을 저장하는 저장 속성입니다. 계산 속성은 액세스할 때마다 계산되며 값을 저장하지 않습니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.