라티의 작은 일기장

[11일차] 클로저 본문

Swift

[11일차] 클로저

코드라티 2023. 3. 2. 23:48

오늘은 내가 4학년으로 올라가는... 개강일이었다. 시장통을 방불케하는 시끌벅적한 캠퍼스가 적응이 되지 않았지만... ㅠㅠ

이러나 저러나 오늘도 Swift 공부는 미룰 수 없다!

 

오늘은 클로저(Closure)에 대해 공부해보려고 한다.

클로저?

클로저를 처음 들어보는 사람도 있겠지만, JS(JavaScript)를 공부해 본 사람들은 이미 알고 있는 개념일 것이다.

JS에서는 익명 함수 등으로 소개되어 있을 수도 있다.

음... 일단, 클로저는 코드에서 전달하거나 사용할 수 있는 독립 기능 블록이며, 일급 객체의 역할을 수행한다.

여기서 일급 객체는 파라미터로 보낼 수도 있고, 변수 / 상수의 값으로 저장하거나 함수의 반환 값이 될 수 있는 객체이다.

그렇다고 이게 JS의 기능인가? 그렇지는 않다. 클로저의 개념은 원래 존재했고, JS에 기능으로 구현한 것이기 때문이다.

그래서 Swift에서 클로저는 JS의 클로저와 기능이 같다.

Swift에서 클로저 표현식을 살펴보자.

{ (매개변수) -> 리턴 타입 in
	실행 구문
}

여기서 (매개변수) -> 리턴 타입을 클로저 헤드, 실행 구문을 클로저 바디라고 한다. 이 둘을 구분 짓는 키워드가 in인 것이다.

 

클로저의 기본 예시를 코드로 살펴보자.

let hello = { () -> () in
    print("Hello!")
}

hello()

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

hello2("Rati")

hello에 할당된 클로저는 파라미터와 리턴값이 없는 클로저, hello2에 할당된 클로저는 파라미터와 리턴값이 있는 클로저이다.

실행 결과를 보면 다음과 같다.

여기서 주의 깊게 볼 부분은, 클로저에 파라미터를 넘길 때 레이블을 사용하지 않는다는 것이다. 사용하면 에러가 난다.

 

또한 클로저는 일급 객체로서, 파라미터로 전달되거나 반환값으로 반환 될 수도 있다고 했는데, 그에 대한 예시를 살펴보자.

func doSomething(closure: () -> ()) {
    closure()
}

doSomething(closure: { () -> () in
    print("Hello~")
})

doSomething 함수는 클로저를 전달받아 클로저를 호출하는 함수이다.

함수를 선언하고 Hello~ 문자열을 출력하는 print문을 반환하는 클로저를 파라미터로 전달하였다.

실행 결과는 다음과 같다.

클로저를 반환하는 코드의 예시도 살펴보자.

func doSomething2() -> () -> () {
    return { () -> () in
        print("Hello#")
    }
}

doSomething2()()

 

 

천천히 살펴보자. doSomething2 함수는 일단 클로저를 반환하는 클로저를 반환 타입으로 갖고 있고,

반환하는 클로저가 반환하는 클로저는 아무런 파라미터를 받지 않으면서 Hello# 문자열을 출력하는 print문을 실행한다.

말이 좀 어렵긴 한데.. 긴 영어 문장 나눠서 해석하는 것 처럼 호출 및 반환 관계를 생각하면서 보면 된다. 사실 나도 잘 이해한건지 모르겠다.

그래서 일단 클로저가 두 개니까 소괄호 쌍도 두 개로 적고, 실행 결과 print문이 잘 실행되는 것으로 이해했다.

 

다음은 클로저의 가독성을 향상시키기 위한, 후행 클로저를 사용하는 방법에 대해 알아보자.

func doSomething(closure: () -> ()) {
    closure()
}

doSomething(closure: { () -> () in
    print("Hello~")
})

doSomething() {
    print("Hello@")
}

클로저를 받아서 클로저를 호출해주는 doSomething 함수와, 클로저를 넘겨주는 코드를 가져와서 간소화를 해보겠다.

우선 클로저가 파라미터도 받지 않고 리턴도 하지 않는데 실행 구문이 있으면 저렇게 죄다 생략을 할 수 있다.

확인해보니 똑같은 기능을 수행하는데 아랫쪽 클로저가 훨씬 간단하다.

 

클로저가 여러개인 경우도 간소화가 가능하다.

func doSomething2(success: () -> (), fail: () -> ()) {
    
}

doSomething2 {
    <#code#>
} fail: {
    <#code#>
}

또 이런 식의 간소화도 가능하다.

func doSomething3(closure: (Int, Int, Int) -> Int) {
    closure(1,2,3)
}

doSomething3(closure: {(a, b, c) in
    return a+b+c
})

doSomething3(closure: {
    return $0+$1+$2
})

doSomething3(closure: {
    $0+$1+$2
})

doSomething3() {
    $0+$1+$2
}

doSomething3 {
    $0+$1+$2
}

doSomething3 호출문이라고 작성한 것들은 모두 같은 기능을 수행하는 코드이다 놀랍게도...

단, 여기서 return 키워드를 생략하는 단계가 있는데, return문 이외에 다른 실행문이 있으면 return 키워드를 생략할 수 없다.

 

Swift에서 클로저에 대해 알아보았다. JS도 어려웠지만 Swift도 만만치 않다.. 잘 써먹을 수 있길!!

 

 

 

패스트캠퍼스 바로가기 : http://bit.ly/3Y34pE0

 

패스트캠퍼스 [직장인 실무교육]

프로그래밍, 영상편집, UX/UI, 마케팅, 데이터 분석, 엑셀강의, The RED, 국비지원, 기업교육, 서비스 제공.

fastcampus.co.kr

> 본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성되었습니다.