BIBI BLOG
📱 SwiftUI에서 메모리 사용 줄이기! ⚡ 본문
📱 SwiftUI에서 메모리 사용 줄이기! ⚡
SwiftUI로 앱을 만들다 보면, 어느 순간 앱이 점점 무거워지고 느려지는 걸 느낄 때가 있어요.
메모리를 많이 쓰면 기기 성능이 떨어지고, 심한 경우 앱이 강제 종료될 수도 있죠.
이런 문제를 방지하려면, SwiftUI의 동작 방식을 잘 이해하고 메모리를 효율적으로 관리하는 게 중요해요.
이번 글에서는 앱을 더 가볍고 빠르게 만들 수 있는 실전 최적화 방법을 정리해보았습니다.🚀
1️⃣ SwiftUI에서 메모리 사용 방식 이해하기
SwiftUI에서는 UI가 상태(State)에 따라 자동으로 변경돼요.
즉, 우리가 직접 화면을 업데이트할 필요 없이, 상태만 바꾸면 SwiftUI가 알아서 새로운 화면을 그려요.
하지만, 잘못된 상태(State) 사용법이나 큰 데이터를 잘못 다루면 메모리가 많이 사용될 수 있어요.
2️⃣ 올바른 State 관리 방법
SwiftUI에서는 데이터를 관리할 때 여러 가지 방법을 제공해요.
각 방법의 차이를 알면 불필요한 메모리 사용을 줄일 수 있어요!
속성 | 설명 | 메모리 관리 |
@State | 단순한 값 (숫자, 문자열 등) | 사용하기 쉬움, 작은 데이터에 적합 |
@Binding | 부모 뷰에서 전달받은 값 | 직접 데이터를 저장하지 않음 |
@ObservedObject | 외부에서 만든 객체를 관찰 | 메모리 관리 필요 |
@StateObject | SwiftUI가 객체를 직접 관리 | 뷰가 사라질 때 자동으로 해제됨 |
@EnvironmentObject | 여러 뷰에서 공유하는 객체 | 전역 데이터 관리 |
❎ 잘못된 사용법 예제 (메모리 과소비!)
struct ContentView: View {
@StateObject private var viewModel = LargeDataViewModel()
var body: some View {
Text(viewModel.data)
}
}
이렇게 하면 ContentView가 새로 생성될 때마다 viewModel이 계속 만들어져서 메모리를 낭비할 수 있어요! ❌
✅ 메모리를 아끼는 올바른 방법
struct ContentView: View {
@ObservedObject var viewModel: LargeDataViewModel
var body: some View {
Text(viewModel.data)
}
}
// 상위 뷰에서 ViewModel을 전달해주기
let sharedViewModel = LargeDataViewModel()
ContentView(viewModel: sharedViewModel)
✔️ 이렇게 하면 viewModel이 여러 뷰에서 재사용되면서 불필요한 메모리 낭비를 막을 수 있어요! 💡
3️⃣ 리스트 최적화 (LazyVStack 사용하기)
만약 리스트에서 수천 개의 데이터를 한 번에 로딩한다면 메모리가 폭발할 수도 있어요! 💥
SwiftUI에서는 이를 해결하기 위해 LazyVStack을 제공해요.
❎ 비효율적인 코드 (메모리 낭비!)
ScrollView {
VStack {
ForEach(largeDataSet) { item in
DataRowView(item: item)
}
}
}
이렇게 하면 리스트의 모든 아이템이 한 번에 메모리에 로드돼서 앱이 느려질 수 있어요.
✅ 최적화된 코드 (LazyVStack 활용)
ScrollView {
LazyVStack {
ForEach(largeDataSet) { item in
DataRowView(item: item)
}
}
}
✔️ LazyVStack은 화면에 보이는 데이터만 로드하기 때문에 메모리를 훨씬 절약할 수 있어요! 🎉
4️⃣ 이미지 로딩 최적화
큰 이미지를 많이 불러오면 메모리를 엄청 많이 사용할 수 있어요.
그래서 이미지 크기를 조절하거나 캐싱(임시 저장)하는 게 중요해요!
✅ AsyncImage를 활용한 최적화
struct ThumbnailView: View {
let imageURL: URL
var body: some View {
AsyncImage(url: imageURL) { phase in
if let image = phase.image {
image.resizable().scaledToFit()
} else if phase.error != nil {
Color.red // 에러 시 빨간 배경
} else {
ProgressView() // 로딩 중 표시
}
}
.frame(width: 100, height: 100)
}
}
✔️ AsyncImage를 사용하면 자동으로 이미지를 백그라운드에서 로딩하고, 캐싱할 수 있어요.
✔️ 또, ProgressView()를 추가하면 이미지가 로딩될 때 깔끔하게 표시돼요!
5️⃣ 무거운 연산을 백그라운드에서 실행하기
SwiftUI에서 무거운 연산을 직접 하면 화면이 멈추거나, 앱이 느려질 수 있어요.
이럴 때는 백그라운드에서 실행하고, 결과만 화면에 표시하면 돼요!
❎ 잘못된 코드 (앱이 멈출 수 있음)
struct ContentView: View {
var body: some View {
let processedData = heavyComputation()
Text("데이터: \(processedData)")
}
}
이렇게 하면 SwiftUI가 화면을 그릴 때마다 heavyComputation()을 실행해서 앱이 멈출 수 있어요.
✅ 백그라운드에서 실행하는 최적화 코드
struct ContentView: View {
@State private var processedData: String = ""
var body: some View {
Text("데이터: \(processedData)")
.onAppear {
DispatchQueue.global(qos: .userInitiated).async {
let data = heavyComputation()
DispatchQueue.main.async {
self.processedData = data
}
}
}
}
}
✔️ DispatchQueue.global을 사용하면 백그라운드에서 실행돼서 앱이 멈추지 않아요!
✔️ 결과는 DispatchQueue.main.async를 이용해 메인 화면에서 업데이트해 줘야 해요.
6️⃣ 메모리 누수 방지 (ARC와 Retain Cycle 해결)
Swift에서는 객체가 서로 강한 참조를 가지면 메모리가 해제되지 않는 문제(메모리 누수)가 생길 수 있어요.
이를 해결하려면 weak self를 사용해서 메모리 누수를 막아야 해요!
✅ 메모리 누수를 방지하는 코드
class ViewModel: ObservableObject {
@Published var data: String = ""
func fetchData() {
NetworkManager.fetch { [weak self] result in
DispatchQueue.main.async {
self?.data = result
}
}
}
}
✔️ [weak self]를 사용하면 ViewModel이 필요 없어질 때 자동으로 메모리에서 해제돼요! 🛠
🎯 정리
SwiftUI에서 메모리를 아끼는 가장 중요한 6가지 방법!
✅ @StateObject와 @ObservedObject를 적절히 사용하기
✅ LazyVStack을 사용해서 리스트 메모리 최적화
✅ AsyncImage를 활용한 이미지 로딩 최적화
✅ 백그라운드에서 무거운 작업 실행하기
✅ weak self를 사용해서 메모리 누수 방지하기
자료
'iOS > Swift' 카테고리의 다른 글
📌 [SwiftUI] Safari로 웹 링크 여는 방법 (0) | 2025.03.18 |
---|---|
📚[Swift] async let vs Task group 비교 (0) | 2025.03.16 |
Swift에서 자주 사용하는 모델 프로토콜 🎯 (0) | 2025.03.11 |
🚀 [SwiftUI] Async/await로 Debounce 적용하기 (0) | 2025.03.09 |
Swift Actors: 멀티스레드 버그 잘가~👋 (0) | 2025.03.05 |