Swift 함수와 클로저 | 함수 정의, 클로저, 고차 함수

Swift 함수와 클로저 | 함수 정의, 클로저, 고차 함수

이 글의 핵심

Swift 함수와 클로저에 대한 실전 가이드입니다. 함수 정의, 클로저, 고차 함수 등을 예제와 함께 상세히 설명합니다.

들어가며

함수는 일급 값으로 다루어지며, 클로저 문법으로 짧은 콜백을 쓰기 좋습니다. @escaping 여부로 수명을 컴파일러와 맞출 수 있습니다.


1. 함수 정의

기본 함수

func greet(name: String) {
    print("안녕하세요, \(name)님!")
}

greet(name: "홍길동")  // 안녕하세요, 홍길동님!

// 반환값이 있는 함수
func add(a: Int, b: Int) -> Int {
    return a + b
}

let result = add(a: 3, b: 5)
print(result)  // 8

// 반환값이 한 줄이면 return 생략 가능
func multiply(a: Int, b: Int) -> Int {
    a * b
}

매개변수 레이블

// 외부 레이블과 내부 레이블
func greet(to name: String, from sender: String) {
    print("\(sender)\(name)에게 인사합니다")
}

greet(to: "홍길동", from: "김철수")

// 외부 레이블 생략
func add(_ a: Int, _ b: Int) -> Int {
    return a + b
}

let sum = add(3, 5)  // 레이블 없이 호출

기본 매개변수

func greet(name: String = "Guest", greeting: String = "안녕하세요") {
    print("\(greeting), \(name)님!")
}

greet()  // 안녕하세요, Guest님!
greet(name: "홍길동")  // 안녕하세요, 홍길동님!
greet(name: "홍길동", greeting: "반갑습니다")  // 반갑습니다, 홍길동님!

가변 매개변수

func sum(_ numbers: Int...) -> Int {
    var total = 0
    for number in numbers {
        total += number
    }
    return total
}

print(sum(1, 2, 3))  // 6
print(sum(1, 2, 3, 4, 5))  // 15

inout 매개변수

func swap(_ a: inout Int, _ b: inout Int) {
    let temp = a
    a = b
    b = temp
}

var x = 10
var y = 20
swap(&x, &y)
print("x: \(x), y: \(y)")  // x: 20, y: 10

2. 클로저 (Closures)

기본 클로저

let add = { (a: Int, b: Int) -> Int in
    return a + b
}

let result = add(3, 5)
print(result)  // 8

// 타입 추론
let multiply: (Int, Int) -> Int = { a, b in
    a * b
}

// 단축 인자 이름
let subtract: (Int, Int) -> Int = { $0 - $1 }

후행 클로저 (Trailing Closure)

let numbers = [1, 2, 3, 4, 5]

// 일반 문법
let doubled = numbers.map({ $0 * 2 })

// 후행 클로저
let tripled = numbers.map { $0 * 3 }

// 여러 줄
let filtered = numbers.filter { number in
    number % 2 == 0
}

3. 고차 함수

map, filter, reduce

Swift의 고차 함수를 사용한 배열 처리입니다:

let numbers = [1, 2, 3, 4, 5]

// 1. map: 각 요소를 변환
let doubled = numbers.map { $0 * 2 }
// { $0 * 2 }: 클로저 (후행 클로저 문법)
// $0: 첫 번째 인자 (현재 요소)
// 각 요소에 2를 곱함
// [1, 2, 3, 4, 5] → [2, 4, 6, 8, 10]
print(doubled)  // [2, 4, 6, 8, 10]

// map 실전 예시: 객체 배열 변환
struct User {
    let name: String
    let age: Int
}
let users = [User(name: "홍길동", age: 25), User(name: "김철수", age: 30)]
let names = users.map { $0.name }
print(names)  // ["홍길동", "김철수"]

// 2. filter: 조건을 만족하는 요소만 선택
let evens = numbers.filter { $0 % 2 == 0 }
// { $0 % 2 == 0 }: 짝수 판별 클로저
// true를 반환하는 요소만 포함
// [1, 2, 3, 4, 5] → [2, 4]
print(evens)  // [2, 4]

// filter 실전 예시: 성인만 필터링
let adults = users.filter { $0.age >= 18 }

// 3. reduce: 배열을 하나의 값으로 축약
let sum = numbers.reduce(0) { $0 + $1 }
// reduce(초기값, 클로저)
// $0: 누적값 (accumulator), 처음엔 0
// $1: 현재 요소
// 
// 동작 과정:
// $0=0, $1=1 → 0+1=1
// $0=1, $1=2 → 1+2=3
// $0=3, $1=3 → 3+3=6
// $0=6, $1=4 → 6+4=10
// $0=10, $1=5 → 10+5=15
print(sum)  // 15

// reduce 간단 문법 (연산자 전달)
let sum2 = numbers.reduce(0, +)
// +: 함수처럼 전달 가능
print(sum2)  // 15

// 4. 체이닝: 여러 고차 함수 연결
let result = numbers
    .filter { $0 % 2 == 0 }  // 짝수만: [2, 4]
    .map { $0 * 2 }          // 2배: [4, 8]
    .reduce(0, +)            // 합계: 12
print(result)  // 12 (4+8)

// 실전 예시: 평균 계산
let average = numbers.reduce(0, +) / numbers.count
print(average)  // 3

고차 함수의 장점:

  1. 가독성: 의도가 명확함
  2. 간결성: 반복문보다 짧음
  3. 불변성: 원본 배열 변경 없음
  4. 체이닝: 여러 연산 연결 가능

compactMap, flatMap

let strings = ["1", "2", "three", "4"]

// compactMap: nil 제거
let numbers = strings.compactMap { Int($0) }
print(numbers)  // [1, 2, 4]

// flatMap: 중첩 배열 평탄화
let nested = [[1, 2], [3, 4], [5]]
let flattened = nested.flatMap { $0 }
print(flattened)  // [1, 2, 3, 4, 5]

4. 캡처 (Capture)

func makeIncrementer(increment: Int) -> () -> Int {
    var total = 0
    
    let incrementer = {
        total += increment
        return total
    }
    
    return incrementer
}

let incrementByTwo = makeIncrementer(increment: 2)
print(incrementByTwo())  // 2
print(incrementByTwo())  // 4
print(incrementByTwo())  // 6

5. 실전 예제

예제: 사용자 필터링

struct User {
    let name: String
    let age: Int
    let isActive: Bool
}

let users = [
    User(name: "홍길동", age: 25, isActive: true),
    User(name: "김철수", age: 17, isActive: false),
    User(name: "이영희", age: 30, isActive: true),
]

// 활성 성인 사용자
let activeAdults = users
    .filter { $0.isActive && $0.age >= 18 }
    .map { $0.name }

print(activeAdults)  // ["홍길동", "이영희"]

정리

핵심 요약

  1. 함수: func, 매개변수 레이블, 기본값
  2. 클로저: 익명 함수, 단축 문법
  3. 고차 함수: map, filter, reduce
  4. 캡처: 외부 변수 참조
  5. @escaping: 비동기 클로저

다음 단계

  • Swift 클래스와 구조체
  • Swift 프로토콜
  • Swift 제네릭

관련 글

  • JavaScript 함수 | 함수 선언, 화살표 함수, 콜백, 클로저 완벽 정리
  • Kotlin 함수 | 함수 정의, 람다, 고차 함수
  • C++ 기본 인자 |
  • C++ 람다 캡처 에러 |
  • C++ 이름 은닉 |