
AI에게 AppDelegate 설명을 부탁했다.
Swift의 AppDelegate 코드 설명해줄게!
이 코드는 iOS 앱의 진입점 역할을 하는 AppDelegate 클래스를 정의한 거야. 주요 내용을 설명해 볼게:
1. 기본 구조
swift
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
// 메소드들...
}
- @main: 앱의 시작점을 지정하는 Swift 5.3부터 도입된 어트리뷰트. 이전에는 @UIApplicationMain을 사용했어.
- UIResponder 상속: 이벤트 응답 체인의 일부가 되게 해줌
- UIApplicationDelegate 프로토콜: 앱의 생명주기와 관련된 메소드 구현을 요구함
2. 앱 시작 메소드
swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// 앱 시작 시 커스터마이징할 수 있는 부분
return true
}
- 앱이 처음 실행될 때 호출됨
- 여기서 앱의 초기 설정을 수행할 수 있음 (데이터베이스 설정, 서비스 초기화 등)
- true 반환은 앱이 정상적으로 시작됐음을 의미
3. Scene 관련 메소드
swift
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
- iOS 13부터 도입된 멀티윈도우 지원을 위한 Scene 관련 메소드
- 새 Scene(화면)이 생성될 때 어떤 설정을 사용할지 지정
swift
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// 사용자가 Scene을 버렸을 때 호출됨
}
- 사용자가 멀티태스킹 UI에서 앱 화면을 닫았을 때 호출됨
- 해당 Scene과 관련된 리소스를 정리하는 코드를 여기에 작성하면 돼
MARK 주석
swift
// MARK: UISceneSession Lifecycle
- 코드 구조화를 위한 주석으로, Xcode의 점프 바에서 섹션으로 표시됨
- 코드 가독성을 높이는 Swift 개발 관행이야
SceneDelegate 코드도 질문해보았다.
SceneDelegate 코드도 설명해줄게!
이 파일은 iOS 13부터 도입된 멀티윈도우 지원을 위한 핵심 클래스야. AppDelegate와 함께 앱의 생명주기를 관리하는 중요한 역할을 해.
swift
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
// 메소드들...
}
주요 메소드들을 하나씩 살펴보자:
1. scene(_:willConnectTo:options:)
swift
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _ = (scene as? UIWindowScene) else { return }
}
- Scene이 처음 생성될 때 호출돼
- 여기서 window를 설정하고 앱의 UI를 초기화하는 코드를 작성해
- guard let 구문은 scene이 UIWindowScene 타입인지 확인하고, 아니면 함수를 종료시킴
2. sceneDidDisconnect(_:)
swift
func sceneDidDisconnect(_ scene: UIScene) {
// Scene이 시스템에 의해 해제될 때 호출됨
}
- Scene이 백그라운드로 이동하거나 세션이 버려질 때 호출됨
- 리소스 정리 작업을 여기서 수행하면 돼
3. sceneDidBecomeActive(_:)
swift
func sceneDidBecomeActive(_ scene: UIScene) {
// Scene이 비활성 상태에서 활성 상태로 전환될 때 호출
}
- 앱이 포그라운드에 오고 사용자와 상호작용할 준비가 됐을 때 호출
- 일시 중지됐던 작업들을 다시 시작하기 좋은 시점
4. sceneWillResignActive(_:)
swift
func sceneWillResignActive(_ scene: UIScene) {
// Scene이 활성 상태에서 비활성 상태로 전환될 때 호출
}
- 전화가 오거나 다른 앱으로 전환될 때 같은 일시적인 중단이 발생할 때 호출됨
- 중요 데이터를 저장하거나 UI 업데이트를 일시 중지하는 코드를 넣으면 좋아
5. sceneWillEnterForeground(_:)
swift
func sceneWillEnterForeground(_ scene: UIScene) {
// 백그라운드에서 포그라운드로 전환될 때 호출
}
- 앱이 다시 화면에 표시될 때 호출
- 백그라운드에서 변경된 설정을 되돌리는 코드를 넣기 좋은 곳
6. sceneDidEnterBackground(_:)
swift
func sceneDidEnterBackground(_ scene: UIScene) {
// 포그라운드에서 백그라운드로 전환될 때 호출
}
- 앱이 화면에서 사라질 때 호출
- 데이터 저장, 공유 리소스 해제 등의 작업을 수행하기 좋은 시점
iOS 13 이전에는 AppDelegate에서 모든 생명주기 관리를 했지만, 이제는 SceneDelegate가 UI와 관련된 생명주기를, AppDelegate는 앱 전체의 생명주기를 관리하는 구조로 나뉘어졌어. 멀티윈도우 지원이 필요한 아이패드 앱이나 Mac Catalyst 앱에서 특히 중요한 파일이지!
ViewController 코드도 설명해줄게!
swift
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
이건 iOS 앱의 화면을 담당하는 가장 기본적인 컨트롤러 클래스야. 엄청 심플하게 기본 구조만 갖춘 상태인데:
- UIViewController 클래스를 상속받아서 만들어진 커스텀 뷰 컨트롤러야.
- viewDidLoad() 메소드는 뷰가 메모리에 로드된 후에 호출되는 메소드로, 화면 초기화 작업을 여기서 많이 해.
- super.viewDidLoad()는 부모 클래스(UIViewController)의 viewDidLoad 메소드를 먼저 실행시켜주는 부분이야.
지금은 아무 기능도 없는 빈 화면 상태인데, 여기에 UI 요소들(버튼, 레이블 등)을 추가하고 액션을 연결해서 실제 기능을 구현하게 될 거야. 앱 개발 시작할 때 Xcode가 기본으로 생성해주는 템플릿 코드라고 보면 돼!
함수의 타입 (자료형,자료형,…) -> 리턴형 (Int, Int) -> Int 리턴형이 Void형이면 ()

함수명 (외부매개변수명: 외부매개변수명: ...)
BMI 계산기
import Foundation
//let weight = 60.0
//let height = 170.0
func calcBMI(weight: Double, height: Double) -> String{
let bmi = weight / (height*height*0.0001) // kg/m*m
let shortendBmi = String(format: "%.1f", bmi)
var body = ""
if bmi >= 40 {
body = "3단계 비만"
} else if bmi >= 30 && bmi < 40 {
body = "2단계 비만"
} else if bmi >= 25 && bmi < 30 {
body = "1단계 비만"
} else if bmi >= 18.5 && bmi < 25 {
body = "정상"
} else {
body = "저체중"
}
return "BMI:\(bmi), 판정:\(body)"
}
print(calcBMI(weight: 60.0, height:170.0))
switch case 문으로도 변환해보기
import Foundation
func calcBMI(weight : Double, height : Double) -> String {
let bmi = weight / (height*height*0.0001) // kg/m*m
let shortenedBmi = String(format: "%.1f", bmi)
var body = ""
switch bmi {
case 0.0..<18.5:
body = "저체중"
case 18.5..<25.0:
body = "정상"
case 25.0..<30.0:
body = "1단계 비만"
case 30.0..<40.0 :
body = "2단계 비만"
default :
body = "3단계 비만"
}
return "BMI:\(shortenedBmi), 판정:\(body)"
}
print(calcBMI(weight:69.5, height: 183.0))
1급 객체와 1급 시민

toUp의 자료형

swift는 변수나 상수에 함수를 집어넣을 수 있다.
// 정수를 받아서 1을 더한 값을 반환하는 함수 선언
func up(num: Int) -> Int {
return num + 1
}
// 정수를 받아서 1을 뺀 값을 반환하는 함수 선언
func down(num: Int) -> Int {
return num - 1
}
// 함수를 상수에 할당 - 이게 일급 객체의 핵심 특징!
// 함수가 값처럼 다뤄질 수 있어서 변수나 상수에 저장 가능
let toUp = up
// 원래 함수를 직접 호출 (argument label 'num:' 사용)
print(up(num:10)) // 결과: 11
// 함수를 저장한 상수를 통해 함수 호출 (argument label 생략)
// 일급 객체로서 함수를 변수에 저장하면 호출 시 파라미터 레이블을 생략해야 함
print(toUp(10)) // 결과: 11
// 또 다른 함수를 상수에 할당 (일급 객체 활용)
let toDown = down
// 원래 함수 호출 (실행만 하고 결과는 출력 안 함)
down(num:10) // 결과 값 9는 사용되지 않음
// 원래 함수 호출 결과 출력
print(down(num:10)) // 결과: 9
// 함수를 저장한 상수를 통해 호출 (argument label 생략)
print(toDown(8)) // 결과: 7
func upDown(Fun: (Int) -> Int, value: Int) {
let result = Fun(value)
print("결과 = \(result)")
}
print(type(of: upDown)) // 자료형 : ((Int) -> Int, Int) -> ()
upDown(Fun:toUp, value: 3)
upDown(Fun:toUp, value: 10) //toUp(10)
upDown(Fun:toDown, value: 10) //toDown(10)
((Int) -> Int, Int) -> () AI에게 물어보니,
- (Int) -> Int: 이건 정수를 받아서 정수를 반환하는 함수 타입이야. 예를 들면 아까 봤던 up이나 down 함수 같은 거지!
- Int: 그냥 정수 하나
- ((Int) -> Int, Int): 이 부분은 매개변수 목록을 나타내는데, 두 개의 매개변수를 받는다는 뜻이야.
- 첫 번째 매개변수: (Int) -> Int 타입의 함수
- 두 번째 매개변수: Int 타입의 정수
- -> (): 이건 반환 타입이 Void라는 뜻으로, 아무것도 반환하지 않는다는 의미야.


함수가 함수를 리턴하는 경우가 나오게 된다.
AI의 설명
// 불리언 값에 따라 다른 함수를 반환하는 고차 함수
func decideFun(x: Bool) -> (Int) -> Int {
// 매개변수는 Bool, 반환 타입은 '정수를 받아 정수를 반환하는 함수'
if x {
return toUp // true면 toUp 함수를 반환 (숫자를 1 증가시키는 함수)
} else {
return toDown // false면 toDown 함수를 반환 (숫자를 1 감소시키는 함수)
}
}
// decideFun 함수를 호출하고 결과 함수를 r에 저장
// x에 false를 전달했으므로 toDown 함수가 반환됨
let r = decideFun(x: false) // 실질적으로 let r = toDown과 같음
// r의 타입 출력 - 함수 타입인 (Int) -> Int 출력됨
print(type(of: r)) // "(Int) -> Int" 출력
// r 함수 실행 (실제로는 toDown 함수 실행)
// 10에서 1을 뺀 9가 출력됨
print(r(10)) // toDown(10)과 같으므로 9 출력
🔹 고차함수 활용도가 높은 순
- Haskell
- 순수 함수형 언어 → 모든 함수가 1급 객체, 고차 함수 사용이 기본
- map, filter, fold 등 고차 함수가 필수적임
- Scala
- 함수형 + 객체지향 혼합 언어
- 컬렉션 처리, 익명 함수(람다), 고차 함수 활용 빈도가 매우 높음
- Clojure
- Lisp 계열 함수형 언어
- 함수가 1급 객체이고 컬렉션 처리 시 고차 함수를 적극적으로 사용
- F#
- .NET 기반 함수형 언어
- 고차 함수와 패턴 매칭을 중심으로 프로그래밍
- JavaScript (ES6 이상)
- 함수가 1급 객체
- map, filter, reduce, 클로저 등에서 고차 함수 활용 빈도 높음
- 특히 프론트엔드에서 콜백, 이벤트 처리 등에서도 자주 사용
- Swift
- 함수가 1급 객체, map, filter, reduce 등의 고차 함수 제공
- UI나 데이터 처리에서 람다/클로저 활용 많음
- Python
- map, filter, reduce, sorted 등 고차 함수 제공
- 전통적 Python 스타일에서는 반복문(for) 사용 비중이 커서, 순수 FP보다는 보조적 사용
- Java (8 이상)
- 람다와 Stream API 도입 이후 고차 함수 활용 가능
- 하지만 기존 Java 코드 스타일에서 반복문 위주라, 고차 함수 사용 빈도는 상대적으로 낮음
- C++
- C++11 이상에서 람다 도입, std::function, 알고리즘(std::transform) 지원
- 하지만 여전히 전통적 반복문 위주로 작성되는 경우가 많음
🔹 요약
- FP 중심 언어 → 고차 함수 필수적 (Haskell, Scala, Clojure, F#)
- 멀티 패러다임 언어 → 필요에 따라 자주 사용 (JavaScript, Swift, Python)
- 주로 절차/객체지향 언어 → 고차 함수 사용 빈도 낮음 (Java, C++)
클로저
특정 작업(함수)과 그 작업이 일어난 곳(환경 또는 상태)을 모두 기억하고 있는 도구
Swift에서는 일반적으로 클로저 표현식을 클로저로 표현
익명함수 또는 독립적인 코드 블록으로 클로저의 하위 개념
AI 답변
클로저는 그냥 '이름 없는 함수'라고 생각하면 돼. 코드 블록을 변수처럼 저장하고 전달할 수 있게 해주는 거야. 아까 우리가 본 일급 객체 개념이랑 연결되는 부분이지!
기본 문법은 이렇게 생겼어:
swift
{ (매개변수) -> 반환타입 in
실행 코드
}
실제 예시로 보면 더 쉽게 이해될 거야:
swift
// 일반 함수
func add(a: Int, b: Int) -> Int {
return a + b
}
// 같은 기능의 클로저
let addClosure = { (a: Int, b: Int) -> Int in
return a + b
}
print(addClosure(5, 3)) // 8
근데 Swift는 클로저 문법을 엄청 간결하게 만들 수 있어서 실제로는 이렇게 많이 써:
swift
// 타입 추론으로 더 간결하게
let addClosure = { (a, b) in a + b }
// 배열에서 사용하는 예시
let numbers = [1, 2, 3, 4, 5]
let doubled = numbers.map { $0 * 2 } // [2, 4, 6, 8, 10]
클로저의 진짜 장점은 주변 변수를 "캡처"해서 나중에도 사용할 수 있다는 건데, 이게 바로 "클로저(closure)"라는 이름의 유래야. 주변 환경을 "닫아서(close)" 가지고 다닌다는 의미지!

func add(x: Int, y: Int) -> Int {
return x+y
}
//print(add(x:10, y:20))
let add1 = { (x: Int, y: Int) -> Int in
return x+y
}
print(add1(10,15)) //25
바꾸는 방법 내 말로 정리
// 1. 함수 이름, 선언구 다 지우고
// 2. 중괄호 맨 앞으로 옮기고
// 3. 자료형 뒤에 in이라고 해서 클로저인거 알려주고
// 4. 상수 하나 만들어서 그 안에 집어넣어주기


클래스에서 만들어진 실체를 객체라 하고, 객체들 중 실제로 사용중인 객체를 인스턴스라고 한다.
영어권에서는 객체들을 모두 인스턴스라고만 하지 object라는 단어를 사용하진 않는다.


Swift에서는 클래스랑 구조체가 얼핏 보면 비슷해 보이는데, 결정적인 차이점이 딱 있어! 바로 **값 타입(Value Type)**이냐 **참조 타입(Reference Type)**이냐의 차이야. 이게 사용하는 방식에 엄청난 영향을 줘.
쉽게 비유하자면,
- 구조체 (Struct) = 복사본 (값 타입)
- 카드를 복사해서 주면, 내 카드랑 네 카드랑 따로따로 관리돼. 네 카드를 막 칠해도 내 카드는 멀쩡!
- 클래스 (Class) = 원본 리모컨 (참조 타입)
- TV 리모컨을 주면, 네가 그 리모컨으로 채널을 바꾸면 내 TV 채널도 바뀌어! 둘 다 같은 TV를 조작하는 거지.
그럼 이제 이걸 iOS 앱 개발에서 어떻게 쓰는지 알려줄게!
1. 클래스 (Class) 활용
언제 쓰냐?
- 상속이 필요할 때: 어떤 객체가 다른 객체의 특성을 물려받아야 할 때. UIViewController나 UIView처럼 애플 프레임워크의 기본 구성 요소들은 대부분 클래스로 되어있고, 이걸 상속받아서 내 화면이나 UI 요소를 만드는 경우가 많아.
- class MyViewController: UIViewController { ... }
- 하나의 인스턴스를 여러 곳에서 공유하고 싶을 때: 앱 전체에서 설정이나 데이터를 공유하는 싱글톤 객체(예: UserDefaults.standard)나, 공통된 네트워크 매니저 같은 객체를 만들 때 사용해.
- 예: 앱 전체의 사용자 정보를 담는 UserManager 클래스
- 인스턴스의 생명 주기가 길고 복잡할 때: 메모리에 오랫동안 유지되어야 하고, 다양한 상태 변화를 가지는 객체들 (예: 앱 델리게이트, 씬 델리게이트)
- Objective-C와의 상호 운용성: Objective-C 코드를 써야 한다면 클래스를 써야 해.
장점: 상속을 통한 확장성, 타입 캐스팅(어떤 타입인지 확인하고 변환), ARC(자동 참조 카운팅)를 통한 메모리 관리.
2. 구조체 (Struct) 활용
언제 쓰냐?
- 간단한 데이터 묶음: 좌표(CGPoint), 크기(CGSize), 색상(UIColor도 내부적으로는 구조체 기반), 간단한 사용자 정보 (User 모델) 등 '값'을 표현할 때 주로 써.
- struct User { var name: String; var age: Int }
- 복사 시 독립적인 객체가 필요할 때: 어떤 데이터를 복사해서 넘겨줬을 때, 원본 데이터에 영향을 주지 않고 복사본만 따로 수정하고 싶을 때. Swift의 기본 타입들(Int, String, Array, Dictionary 등)은 모두 구조체라서 값 타입으로 동작해!
- 생명 주기가 짧고 가벼운 객체: 함수 내에서 잠깐 사용하고 버려지는 객체들. 스택 영역에 할당돼서 메모리 관리가 클래스보다 효율적이야.
- Codable 프로토콜을 이용한 데이터 모델링: 서버에서 JSON 데이터를 받아올 때, Swift에서 이 JSON을 파싱해서 Swift 객체로 만들 때 구조체를 엄청 많이 써.
- struct Product: Codable { var id: String; var name: String; var price: Int }
장점: 값 타입이라 데이터 변경 시 안전함(예상치 못한 사이드 이펙트 방지), 메모리 효율성, 스레드 안전성(멀티스레딩 환경에서 공유 문제 발생 X).
⭐️ iOS 개발 팁 (언제 뭘 쓸지 고민될 때)
Swift는 기본적으로 구조체를 더 선호하는 경향이 있어! 애플 공식 문서에서도 대부분의 경우 구조체를 사용하는 것을 권장하거든.
- 일단 구조체를 먼저 고려해 봐.
- 클래스가 꼭 필요한 경우에만 클래스를 써. (상속, 참조 의미가 중요한 경우, Objective-C 호환 등)
- 클래스가 필요한 조건:
- 다른 클래스의 기능을 물려받아야 할 때 (상속).
- 특정 타입의 객체를 여러 곳에서 하나의 인스턴스로 공유하며 상태를 변경해야 할 때.
- ARC가 관리하는 메모리 힙에 저장되어야 할 때.
- 클래스가 필요한 조건:

class Man {
var age : Int
var weight : Double
}

이 코드를 바로 Xcode에 넣으면 에러가 날 거야. 왜냐면 Swift에서는 클래스의 모든 저장 프로퍼티(age, weight)는 초기값이 있거나, 아니면 **초기화 구문(init())**을 통해 반드시 초기화되어야 하거든. 😬
지금처럼 그냥 선언만 해두면 Man 클래스로 객체(인스턴스)를 만들 때 age랑 weight에 뭐가 들어갈지 모르니까 컴파일러가 "야 이거 초기화 안 됐잖아!" 하고 에러를 뿜는 거지.
오류 해결 방법:
- 초기값 지정: //stored property는 초기값이 있어야 함 선언과 동시에 초기값을 줘버리는 방법.이러면 인스턴스 만들 때 let someMan = Man()처럼 기본값으로 바로 만들 수 있어.
- class Man { var age: Int = 0 // 초기값 0 var weight: Double = 0.0 // 초기값 0.0 }
- 초기화 구문(init()) 추가: 인스턴스 생성 시점에 값을 받아서 초기화하는 방법. 이 방식이 훨씬 일반적이야.이렇게 해두면 let 늠재 = Man(age: 23, weight: 65.5) 이런 식으로 인스턴스를 만들 수 있게 돼!
- class Man { var age: Int var weight: Double // 초기화 구문(initializer) 추가 init(age: Int, weight: Double) { self.age = age // 전달받은 age 값으로 프로퍼티 초기화 self.weight = weight // 전달받은 weight 값으로 프로퍼티 초기화 } }
그래서 이 Man 클래스는 뭐냐?
Man이라는 '설계도' 또는 '청사진'을 만든 거야. 이 설계도를 가지고 실제 사람(Man의 인스턴스) 객체들을 만들 수 있어. 각 객체는 고유한 age와 weight 값을 가질 수 있겠지!
nil 이용하기
class Man {
var age : Int?
var weight : Double?
}

초기값을 주어도 되고 initializer를 주어도 되는데 둘 다 해도 안될 건 없지만 굳이 둘 다 안해도 됨
메소드

class Man{
var age : Int = 1
var weight : Double = 3.5
func display(){ //인스턴스 메서드
print("나이=\(age), 몸무게=\(weight)")
}
}
var x : Int = 10
var kim : Man = Man() // 자동적으로 이니셜라이저가 눈에 안보이게 생성된다.
// 이니셜라이저에 한해서 .Init()을 생략할 수 있다. 자바에서 호출할 때도 같은 개념이다.
print(kim.age)
print(kim.weight)
kim.display()
최종적으로 클래스 호출할 땐 var kim = Man()으로 줄여쓸 수 있다.