
TIL 4일차 - Swift 함수와 클로저, 옵셔널
1. 함수 (Function)
함수 쓰는 이유?
: 코드의 재사용성과 가독성을 높임
기본적인 함수 쓰는 법
func 함수이름(매개변수: 타입) -> 반환타입{
실행할 코드
return 반환값
}
// 기본적인 함수의 예제
func add(a: Int, b: Int) -> Int {
return a + b
}
let result = add(a: 3, b: 5)
print(result)
// 8 출력
// 입력값과 반환값이 없는 함수
func sayHello() {
print("안녕하세요")
}
sayHello()
// 안녕하세요 출력
// 매개변수가 없는 함수
func getCurrentYear() -> Int {
return 2025
}
let year = getCurrentYear()
print(year)
// 2025 출력
func greet(_name: String) {
print("안녕하세요, \(name)님!")
}
/*함수 정의 시 매개변수 이름 앞에 언더바(_)를 붙이면,
이 함수를 호출할 때는 귀찮게 이름표를 붙이지 않아도 된다는 말임*/
greet("철수")
// 안녕하세요, 철수님! 출력됨
// 중첩 함수
func outerFunction() {
func innerFunction() {
print("이것은 내부 함수입니다.")
}
innerFunction()
}
outerFunction()
// 이것은 내부 함수입니다. 출력 됨
2. 클로저
클로저란? 이름이 없는 함수(익명 함수) 임
데이터 타입으로 사용할 수 있어 변수에 할당해 재사용할 수도 있고, 함수의 파라미터로도 전달할 수 있다.
클로저는 코드의 재사용성을 높이고, 비동기 처리, 컬렉션 연산 등의 장점이 있음
(1) 기본적인 클로저 사용법
{(매개변수) -> 반환타입 in
실행할 코드
}
let multiply = { (a:Int, b:Int) -> Int in
return a*b
}
let result = multiply(3,4)
print(result)
//12 출력
파라미터의 이름은 생략 가능하며. n번째 파라미터는 $n으로 작성할 수 있음
파라미터의 이름을 생략할땐 in을 생략해야함
(2) 클로저를 타입으로 사용하는 방법
// 타입으로 사용할 때 표현 방법
(파라미터 데이터 타입) -> (리턴 타입)
- 파라미터의 타입은 없을 때 비워두고, 리턴 타입이 없는 경우엔 Void라고 명시함.
- 데이터 타입으로 사용할 수 있기 때문에 Optional로 사용할 수 있음 (이 경우엔 전체를 ()로 감싼 후 ?를 입력해야 함)
let closure: (Int, String) -> Void
// 파라미터는 튜플(Int, String)타입이고 반환타입은 없음
let closure2: (Int) -> Void
// 파라미터는 1개로 Int이며 반환타입은 없음
let closure3: () -> Void
// 파라미터는 없고 반환타입도 없음, 파라미터는 소괄호로 감싸고있어 Void 안써도 됨
let optionalClosure: ((Int) -> Int)?
// 옵셔널 타입임
let closure: (Int, String) -> Void = { intValue, stringValue in
print(intValue)
print(stringValue)
}
// 상수인 closure의 타입은 클로저로 Int와 String 타입의 파라미터가 있고 반환값은 없음
// 타입이 클로저인 closure에게 앞에는 파라미터 이름이 intValue, stringValue이고 print문을 실행하는 클로저를 대입하였다.
let closure2: (Int) -> Void = {
print($0)
}
// 파라미터의 이름이 생략되어있으므로 in도 생략하여 쓸 수 있다.
// 파라미터의 이름이 생략되었기때문에 $0을 사용하여 첫번째 파라미터를 쓸 수 있음
let closure3: () -> Void = {
print("Hello")
}
let closure4: (Int) -> Int = { value in
return value * 2
} // 파라미터의 이름을 value라 하고 Int값을 return한다.
let closure5: (String, Int) -> Int = {
return $0.count + $1 // 파라미터의 이름도 없고 in도 없음
} // 즉 첫번째 파라미터의 개수 + 두번째 파라미터를 더한 Int값을 출력함
let optionalClosure: ((Int) -> Int)? = { value in
return value * 2
}
print (optionalClosure?(1)) // optional(2) 출력
print (optionalClosure!(1)) // 강제 언래핑으로 2 출력
(3) 클로저를 호출(사용)하는 방법
- 클로저를 변수나 상수에 저장했다면 함수처럼 이름뒤에 괄호를 사용하면 됨
- 옵셔널 변수에 저장한 클로저는 변수이름?() 형식으로 사용하면 됨
- 구현하고 즉시 호출한 결과값을 저장하고 싶으면 {} 뒤에 괄호 ()를 사용하면 됨
// 상수에 저장한 클로저 호출하기
let closure: (Int, String) -> Void = { intValue, stringValue in
print(intValue)
print(stringValue)
}
closure(100,"백") // 함수처럼 출력하면 됨
// 100 '백' 출력
// 옵셔널 변수에 저장한 클로저 호출하기
let optionalClosure: ((Int) -> (Int))? = {
return $0 * 2
}
print(optionalClosure?(100))
// 상수의 타입이 옵셔널이므로 결과값은 Optional(200)으로 출력
// 구현하고 바로 호출하기
// 클로저를 구현하고 바로 실행해 결과값을 helloClosure 상수에 할당하기
let helloClosure = {
return "구현하고 바로 할당하는법은 중괄호 다음에 소괄호를 넣어주면 된다"
}()
print(helloClosure) // '구현하고 바로 할당하는법은 중괄호 다음에 소괄호를 넣어주면 된다' 출력 됨
(4) 트레일링 클로저(trailing closure)
쉽게 말해 클로저의 경량화 방법이다.
언제쓰냐면 함수를 호출할 때 마지막 파라미터가 클로저일때 괄호를 닫은 뒤에 작성할 수 있다.
장점: 클로저를 함수 호출 외부에 작성해서 클로저의 구현부가 길어져도 가독성이 좋아짐
func trailingTest(key: String, closure: () -> Void) {
print(key)
closure()
}
trailingTest(key: "key name", closure: {
print("Hello") // 이게 원래 방식
})
trailingTest(key: "Hello") {
print("World")
} // 마지막 파라미터가 클로저이면 함수 호출할때 일단 닫고 중괄호 내에 써도 된다.
// 이게 트레일링 클로저다.
(5) 클로저를 함수의 매개변수로 사용하기
// 클로저를 함수의 매개변수로 사용
func operateOnNumbers(a: Int, b: Int, operation: (Int,Int) -> Int) -> Int {
return operation(a,b)
}
let sum = operateOnNumbers(a:5, b:10) { (x,y) in
return x+y
}
print(sum)
// 15출력
여기부터 갑자기 어려워진다.. 뇌정지 올뻔함

한 줄 요약부터
👉 이 코드는 “숫자 두 개랑 계산 방법을 받아서 계산해 주는 함수”야.
그 계산 방법을 함수 대신 ‘클로저(코드 덩어리)’로 넘긴 것뿐이야.
1️⃣ 제일 바깥 함수부터 보자 (이게 핵심)
func operateOnNumbers(
a: Int,
b: Int,
operation: (Int, Int) -> Int
) -> Int {
return operation(a, b)
}
이걸 한국어로 번역하면 👇
“정수 a, 정수 b를 받고
정수 2개 받아서 정수 1개 돌려주는 ‘계산 방법’도 하나 받아서
그 계산 방법으로 a랑 b를 계산해 줄게”
operation: (Int, Int) -> Int
이 말의 뜻은:
- Int, Int → 숫자 2개를 받아서
- -> Int → 숫자 1개를 돌려주는
- 함수(또는 클로저)를 받겠다
👉 즉, 함수를 매개변수로 받는다는 뜻.
2️⃣ 그다음 이 줄이 뭐냐면
let sum = operateOnNumbers(a: 5, b: 10) { (x, y) in
return x + y
}
여기서 멘붕 오는 거 이해함 😭
하나씩 자르자.
① operateOnNumbers(a: 5, b: 10)
- a = 5
- b = 10
까지는 평범함.
② 뒤에 붙은 { (x, y) in... }
이게 바로 operation 자리에 들어가는 클로저야.
이건 말로 하면:
“x랑 y를 받아서
x + y를 돌려주는 계산 방법”
👉 즉, 덧셈 함수 하나를 즉석에서 만든 것.
라고 하는데 대체 왜 이런 머리 아픈 방식을 쓰는 거지? 했는데 다음 보니까 이해 간다
.
.
.
4️⃣ 그림으로 비유하면 (진짜 쉬움)
함수는 이런 느낌이야 👇
그리고 너는 계산 기능을 이렇게 넘긴 거야:
다른 계산도 가능함 👇
// 빼기
operateOnNumbers(a: 5, b: 10) { x, y in x - y }
// 곱하기
operateOnNumbers(a: 5, b: 10) { x, y in x * y }
👉 함수는 그대로인데, 계산 방법만 바꿔 끼우는 구조
5️⃣ 왜 이런 걸 쓰냐면 (중요)
❌ 안 쓰면
- 함수 여러 개 만들어야 함
- add, subtract, multiply 전부 따로
⭕ 쓰면
- 함수 하나
- 계산 방식만 갈아 끼움
👉 유연한 코드@!@!!!!!!!
// 정수 배열에서 짝수만 필터링해 배열로 리턴하는 함수를 작성하라 (filter 고차 함수를 사용)
func filterEvenNumbers(_ numbers: [Int]) -> [Int] {
return numbers.filter { $0 % 2 == 0}
// $0의 정체는 배열에서 전달된 첫번째 값임
}
let numList = [1,2,3,4,5,6,7,8]
let evenNumbers = filterEvenNumbers(numList)
print(evenNumbers)
공부를 하다 보면 지금 이 클로저를 배우는 순간처럼 도무지 이해되지 않는 시간이 반복해서 찾아오지만, 그럴 때마다 ‘지금은 낯설 뿐’이라는 생각을 하려고 한다. 어떤 개념이든 손에 익고 눈에 익는 과정을 거치면 결국 어렵지 않게 다뤄질 수 있다는 걸 3년의 수험생활과 전공수업 등등 여러 순간순간에 경험해 왔기 때문이다. 덕분에 예전보다 좌절하는 시간은 줄고, 버티는 힘은 조금씩 길러지고 있는 것 같다. 하좌좧!!!!
바로 다음 옵셔널을 공부하자.~
3. 옵셔널
옵셔널을 쓰는 이유?
:안전하게 값을 다룰 수 있음
->무슨 말이냐?
Swift에서는 값이 존재하지 않을 수 있는 변수를 처리해야 하는 경우가 많음 (ex. 회원가입 안된 사용자, 연산 결과 없음, 서버에서 데이터 받아올 때 값이 아예 존재 X)
이런 경우를 nil 이라고 한다.
기본적인 옵셔널 문법
var 변수이름: 타입? = 값
// 옵셔널 예제
var name: String? = "JH"
print(name)
name = nil
print(name)
// Optional("JH")
// nil 출력
(1) 옵셔널 바인딩 (if let)
옵셔널을 언박싱하려면 if let을 써서 nil이 튀어나오지 않도록 해야 한다.
확인 안 하고 막 언박싱했는데 nil이면 대형사고가 난다. 상자가 폭발함.
// 옵셔널 변수의 값을 쓰려면 if let을 사용해 nil의 여부를 확인해야 함
var name: String? = "Alice"
if let unwrappedName = name {
print("이름:\(unwrappedName)")
} else {
print("이름이 없습니다.")
}
// 출력 결과: 이름: Alice
(2) 옵셔널 바인딩 (guard let)
func printName(_name: String?) {
guard let unwrappedName = name else {
print("이름이 없습니다.")
return
//아예 함수 자체에서 나와서 밑에있는 print문 자체를 실행 안함
}
print("이름:\(unwrappedName)")
}
printName(nil)
printName("Bob")
또 뇌정지 단계에 부딪혔다 아니 그냥 if let 쓰면 되지 뭐 하러 가드 렛을 쓰나 물어봤더니
- Optional 안전하게 처리하려고
- 조건 안 맞으면 빨리 탈출하려고
- 코드가 읽기 쉬워짐
그래서 실무 iOS 코드에 guard let 엄청 많이 나와.
라고 한다. 그래도 이해 안 가서 또 물어봄 if let보다 나은 게 대체 뭐냐!!!
func printName(_ name: String?) {
if let unwrappedName = name {
print("이름: \(unwrappedName)")
} else {
print("이름이 없습니다.")
}
}
이유 1. 뷰컨트롤러는 조건 실패가 많다
id, token, url이 일치할 때만 실행하는 건데 불일치하는 경우가 실제로 더 많다고 한다. 그래서 guard let 안 쓰면
if let이 덕지덕지 if let의 지옥에 빠질 수도 있음......
이유 2. "이 함수는 이 조건을 만족해야만 진행된다"라는 뜻을 명확하게 전달할 수 있음
(3) 옵셔널 강제 해제
// 옵셔널을 강제 언박싱하고싶으면 ! 를 써주면 된다.
var name: String? = "Bob"
print(name!)
라는데 또 의문생김 아니 내가 url 끌어올 때 분명 !로 옵셔널 강제 해제는 안된댔는데 뭘까 싶었는데
URL은
“!로 꺼내면 앱이 죽을 가능성이 있어서
Apple이 애초에 if let / guard let 쓰라고 만든 타입”이야.
그래서 Apple이 권장하는 방식이 if let / guard let
if let url = URL(string: urlString) {
webView.load(URLRequest(url: url))
}
이걸 꼭 써야 한단다.. 아무래도 앱은 웹에 비해 훨씬 철저히 잡아야 별점테러에 사용자 이탈을 막을 수 있으니..
4. nil 병합 연산자 (??)
let userName: String? = nil
let displayName = userName ?? "Guest"
print(displayName)
// Guest 출력
할 게 너무 많다.. 밀린 강의도 빨리 듣고 정리해야 하고 Git 특강 들은것도 다 정리해야하고 ㅠㅠ
그리고 감기 걸려서 감기약 먹었더니 졸려서 죽어버릴 뻔했음.. 하지만 시간은 금이니 1분 1초가 소중하게!! 파이팅하자 나 자신
'iOS > Swift ' 카테고리의 다른 글
| [iOS-Swift] 조건문 (0) | 2026.01.06 |
|---|---|
| [iOS-Swift] 기본 데이터 타입 (0) | 2026.01.06 |
| [iOS-Swift] 변수와 상수 (0) | 2026.01.06 |
| [iOS] 앱 아키텍쳐 기초 (MVC,MVVM) (0) | 2026.01.05 |
| [iOS-Swift] 클래스와 구조체 (0) | 2026.01.01 |