[iOS-Swift] 함수와 클로저, 옵셔널

 

몰랐는데 Swift뜻이 칼새 라고한다.. 내가 아는 스위프트는 테일러 스위프트뿐이었는데

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) in return x + y }

이건 말로 하면:

“x랑 y를 받아서
x + y를 돌려주는 계산 방법”

👉 즉, 덧셈 함수 하나를 즉석에서 만든 것.

 

라고 하는데 대체 왜 이런 머리 아픈 방식을 쓰는 거지? 했는데 다음 보니까 이해 간다

.

.

.

4️⃣ 그림으로 비유하면 (진짜 쉬움)

함수는 이런 느낌이야 👇

 
operateOnNumbers( 숫자1, 숫자2, 계산기능 )

그리고 너는 계산 기능을 이렇게 넘긴 거야:

 
"더해줘"

다른 계산도 가능함 👇

// 빼기 
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