코드네임 :
🍏 on시리즈 모디피어 본문
2주차인데 왜 3주차에 하시는 걸까용~~?ㅎㅎ
ㄴ ㅈㅅ
뷰의 생명 주기 및 이벤트 감지와 관련된 수정자(modifier)
- 사용자 인터렉션이나 데이터 변경을 효과적으로 처리하는 데 중요한 역할
onAppear
: 뷰가 화면에 나타날 때 실행되는 메서드
뷰의 초기 데이터를 로드하거나 특정 작업을 시작할 때 사용
< 기본 사용법 >
struct ContentView: View {
var body: some View {
Text("Hello, SwiftUI!")
.onAppear {
print("뷰가 나타났습니다!")
}
}
}
< 네트워크 호출 시 사용법 >
struct ContentView: View {
@State private var data: String = "로딩 중..."
var body: some View {
Text(data)
.onAppear {
fetchData()
}
}
func fetchData() {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
data = "데이터 로드 완료"
}
}
}
List나 NavigationView 내에서 onAppear를 사용힐 경우, 해당 뷰가 다시 로드될 때마다 실행될 수 있으므로 불필요한 API 호출을 방지하기 위해 상태 관리가 필요
onAppear의 아주 아주 치명적인 단점
- 비동기 코드(async/await)을 다루기 불편
- 작업 취소가 불가능한 점(이 부분이 제일 중요합니다) -> 메모리에 미치는 영향이 있기때문
- onAppear 내부에서 실행된 작업은 뷰가 사라져도 취소되지 않습니다.
- 불필요한 네트워크 요청이나 비동기 작업이 계속 실행될 수 있습니다.
>> 메모리에 미치는 영향이 뭐냐??
- 불필요한 네트워크 요청 지속 문제
- 사용자가 뷰를 빠르게 이동할 경우, 여러 개의 네트워크 요청이 동시에 실행
- 응답을 받을 필요가 없는 요청이 계속 남아있으면 CPU 사용량 증가
- 메모리 누수
- onAppear에서 실행된 비동기 작업이 뷰의 @State 변수를 캡처하면, 해당 뷰가 사라져도 메모리에 남아 있을 가능성
- 네트워크 요청이 완료될 때까지 뷰가 해제되지 않으면 메모리 누수가 발생
- 백그라운드 불필요한 작업 실행
- 뷰가 화면에서 사라졌음에도 불구하고, 실행중인 Task가 백그라운드에서 계속 실행
- 즉, 뷰가 존재하지 않아도 작업이 끝날 때까지 CPU 지속적으로 사용
⬇️ 해결 위해
task 수정자를 사용
: 뷰가 나타나기 전에 수행할 비동기 작업을 추가합니다.
- 비동기 작업을 뷰의 생명주기와 연동하여 자동으로 취소할 수 있습니다. 즉, 뷰가 사라질 때 작업이 자동 취소됩니다.
<기본사용법>
struct ContentView: View {
var body: some View {
Text("Hello, SwiftUI!")
.task {
print("뷰가 나타났습니다!")
}
}
}
<네트워크 호출 시 사용법>
struct ContentView: View {
@State private var data: String = "로딩 중..."
var body: some View {
Text(data)
.task {
fetchData()
}
}
func fetchData() {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
data = "데이터 로드 완료"
}
}
}
onDisappear
: 뷰가 화면에서 사라질 때 실행되는 메서드
네트워크 요청을 취소할 때 리소스를 해제하는 용도로 사용됩니다.
<기본사용법>
struct ContentView: View {
var body: some View {
Text("Hello, SwiftUI!")
.onDisappear {
print("뷰가 사라졌습니다!")
}
}
}
<네트워크 취소 시 사용법>
struct ContentView: View {
@State private var task: Task<(), Never>? = nil
var body: some View {
Text("데이터 로딩 중...")
.onAppear {
task = Task {
await networkData()
}
}
.onDisappear {
task?.cancel()
}
}
func networkData() async {
do {
try await Task.sleep(nanoseconds: 5_000_000_000)
print("데이터 로드 완료")
} catch {
print("작업이 취소되었습니다.")
}
}
}
<리소스 정리 (데이터 저장, 로그아웃 등)>
- 사용자가 앱을 종료하거나 다른 화면으로 이동할 때 리소스를 정리할 수 있습니다
struct LogoutView: View {
var body: some View {
Text("로그아웃 화면")
.onDisappear {
saveUserData()
}
}
func saveUserData() {
print("사용자 데이터 저장 완료")
}
}
onDisappear 주의점
- onDisappear는 단순히 코드가 실행되는 트리거 역할만 합니다.
- 실행 중인 Task는 자동으로 취소되지 않으며, 직접 취소해야 합니다.
⬇️
>>위에서 학습한 .task를 사용하면 뷰가 사라질 때 자동으로 취소됩니다.
>>즉, onDisappear에서 수동으로 취소할 필요가 없습니다.
struct ContentView: View {
var body: some View {
Text("데이터 로딩 중...")
.task {
await fetchData()
}
}
func fetchData() async {
do {
try await Task.sleep(nanoseconds: 5_000_000_000) // 5초 대기
print("데이터 로드 완료")
} catch {
print("작업이 취소되었습니다.") // 뷰가 사라지면 자동 취소됨
}
}
}
OnChange
: 제스처의 값이 변경될 때 수행할 동작을 추가
- 특정 값이 변경될 때 실행되는 메서드
- @State, @Binding, @ObservedObject, @EnvironmentObject 등의 값이 변경될 때 이를 감지하여 실행할 수 있습니다.
- 값이 변경될 때만 동작하며, 초기에 실행되지 않습니다.
struct ContentView: View {
@State private var text = ""
var body: some View {
TextField("입력하세요", text: $text)
.padding()
.onChange(of: text) { oldValue, newValue in
print("텍스트 변경됨: \(newValue)")
}
}
}
<onChange를 이용한 API 호출>
struct SearchView: View {
@State private var query = ""
@State private var results: [String] = []
var body: some View {
VStack {
TextField("검색어 입력", text: $query)
.onChange(of: query) { oldQuery, newQuery in
fetchResults(for: newQuery)
}
List(results, id: \.self) { result in
Text(result)
}
}
}
func fetchResults(for query: String) {
guard !query.isEmpty else { return }
print("'\(query)'에 대한 검색 실행")
}
}
onReceive
: 퍼블리셔가 내보낸 데이터를 감지할 때 수행할 작업을 추가
(SwiftUI에서 onReceive는 Combine 프레임워크의 Publisher를 감지하여 특정 작업을 실행할 때 사용됩니다. Combine 프레임워크에 대한 간단한 학습은 9주차에서 비동기 관련 처리할 때 배우게 될 겁니다. 그러니 여기서의 설명은 생략하도록 하겠습니다.)
onSubmit
: 사용자가 보기에 값을 제출할 때 수행할 작업을 추가
- onSubmit은 사용자가 입력을 완료하고 Enter(리턴)키를 눌렀을 때 실행되는 메서드
- SwiftUI의 TextField 또는 SecureField에서 입력 완료 이벤트를 감지하여 실행할 수 있습니다.
<기본 사용법>
struct ContentView: View {
@State private var text = ""
var body: some View {
TextField("입력하세요", text: $text)
.textFieldStyle(.roundedBorder)
.onSubmit {
print("사용자가 입력 완료: \(text)")
}
}
}
<입력 완료 후 키보드 닫기>
struct ContentView: View {
@State private var text = ""
var body: some View {
TextField("이름을 입력하세요", text: $text)
.textFieldStyle(.roundedBorder)
.onSubmit {
hideKeyboard()
}
}
func hideKeyboard() {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
}
<검색 기능 구현>
struct SearchView: View {
@State private var query = ""
var body: some View {
TextField("검색어 입력", text: $query)
.textFieldStyle(.roundedBorder)
.onSubmit {
search()
}
}
func search() {
guard !query.isEmpty else { return }
print("검색 실행: \(query)")
}
}
<로그인 입력 완료 후 검증> - 오 이걸로 과제 수정하면 되겟긔
struct LoginView: View {
@State private var username = ""
@State private var password = ""
var body: some View {
VStack {
TextField("아이디", text: $username)
.textFieldStyle(.roundedBorder)
SecureField("비밀번호", text: $password)
.textFieldStyle(.roundedBorder)
.onSubmit {
login()
}
Button("로그인", action: login)
}
.padding()
}
func login() {
guard !username.isEmpty, !password.isEmpty else {
print("아이디와 비밀번호를 입력하세요.")
return
}
print("로그인 시도: \(username), \(password)")
}
}
onTabGesture
: 탭 제스처를 인식할 때 수행할 동작을 추가
- onTapGesture는 SwiftUI에서 사용자가 화면을 탭했을 때 특정 동작을 수행하도록 하는 제스처 모디파이어
<기본 사용법> - 음! 이거 지구본 모양 눌렀을 때 안녕하세요! 뜸

<onTabGesture 탭 횟수 설정>
- 기본적으로 onTapGesture는 한 번 탭하면 동작하지만, count 매개변수를 사용하면 특정 횟수만큼 탭해야 동작하도록 설정할 수 있음
(위랑 거의 똑같은 코드라 실행 화면 안넣음)
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
.onTapGesture(count: 2) {
print("안녕하세요!!")
}
Text("Hello, world!")
}
.padding()
}
}
#Preview {
ContentView()
}
'👥Club > 🍀UMC🍀' 카테고리의 다른 글
🍎 4주차 문법 - 함수와 클로저, 클래스, 구조체 (0) | 2025.04.06 |
---|---|
🍏 이미지 관리 (0) | 2025.04.02 |
🍎 3주차 실습 (0) | 2025.04.01 |
🍎 3주차 워크북 - List와 Navigation (0) | 2025.03.31 |
🍎 3주차 문법 - 딕셔너리와 세트(Set), 열거형(enum) (0) | 2025.03.26 |