[TIL] Closure

박주하·2025년 5월 26일

1. 클로저(Closure)


클로저는 변수처럼 저장할 수 있는 코드 덩어리(함수)다. 함수와 비슷한 구조지만 이름이 없어도 사용 가능하고, 함수보다 더 유연하게 동작한다.

// 기본 구조
{ (매개변수) -> 반환타입 in
    실행할 코드
}

// 예
let greeting = { (name: String) -> String in
    return "Hello, \(name)"
}

print(greeting("Luna")) // 출력: Hello, Luna

📌 함수의 인자로 클로저 전달 가능

func operateOnNumbers(_ a: Int, _ b: Int, using operation: (Int, Int) -> Int) {
    let result = operation(a, b)
    print("결과: \(result)")
}

// 클로저를 넘김
operateOnNumbers(3, 5, using: { (x, y) in
    return x + y
})

📌 간단하게 축약 가능

// 1. 전체 코드
{ (x: Int, y: Int) -> Int in
    return x + y
}

// 2. 타입 추론 생략
{ x, y in
    x + y
}

// 3. 단축 인자 이름 사용
{ $0 + $1 }

📌 트레일링 클로저(Trailing Closure)

클로저가 함수의 마지막 인자일 때는 괄호 밖으로 꺼낼 수 있다.

UIView.animate(withDuration: 0.5, animations: {
    view.alpha = 0
})

// 트레일링 클로저 방식
UIView.animate(withDuration: 0.5) {
    view.alpha = 0
}

📌 클로저에서 외부 변수 사용(캡처)

변수 캡처: 자신이 정의된 스코프의 외부 변수를 기억해서 나중에도 사용 가능

func makeCounter() -> () -> Int {
    var count = 0  // count는 함수 안에서 선언됐지만, 클로저가 기억하고 있음.
    return {
        count += 1
        return count
    }
}

let counter = makeCounter()
print(counter()) // 1
print(counter()) // 2

2. 장단점 및 자주 쓰이는 상황


✅ 장점

  • 간결하고 유연한 코드 작성

    • 코드 블록을 전달하거나 임시로 정의할 때 매우 간편
    • 함수형 스타일의 프로그래밍 가능(예: map, filter, reduce)
  • 코드 응집력 향상

    • 코드 흐름과 연관된 동작을 인라인으로 작성할 수 있어 가독성 향상
  • 비동기 작업에서 콜백 처리에 최적

    • 네트워크 응답, 사용자 이벤트, 애니메이션 완료 등에서 콜백 처리를 명확하게 구현
  • 외부 변수 캡처 가능

    • 클로저는 자신이 선언된 스코프의 변수를 기억하므로 상태를 유지하는 동작 구현에 유리
  • 재사용성과 추상화

    • 클로저를 통해 다양한 전략이나 로직을 매개변수화할 수 있어 전략 패턴처럼 활용 가능

🚨 주의점

  • 캡처에 의한 강한 참조(메모리 누수 가능성)

    • 클로저가 self를 강하게 참조하면 retain cycle이 발생할 수 있다
    • [weak self], [unowned self] 사용하여 해결
  • 디버깅 어려움

    • 클로저 내부에서 발생한 에러는 디버깅이 어려운 경우가 많다(특히 비동기 클로저)
  • 복잡한 중첩 구조

    • 클로저 안에 클로저가 반복되면 가독성 저하

📌 자주 쓰이는 상황

상황사용 방식
콜백 처리비동기 작업 결과 처리
UI 애니메이션UIView.animate
컬렉션 처리map, filter, reduce
이벤트 핸들링버튼 클릭 시 액션
타이머/딜레이DispatchQueue.main.asyncAfter

📌 예제

// 1. 애니메이션
UIView.animate(withDuration: 1.0) {
    someView.alpha = 0
}

// 2. 버튼 이벤트 핸들러
button.addAction(UIAction { _ in
    print("버튼 클릭됨!")
}, for: .touchUpInside)

// 3. 배열 고차 함수
let numbers = [1, 2, 3, 4, 5]
let doubled = numbers.map { $0 * 2 }
print(doubled) // [2, 4, 6, 8, 10]

0개의 댓글