클로저가 헷갈려요... (클로저 표현 간소화)

김하민·2024년 10월 25일
3

[Swift] 문법 배우기

목록 보기
1/2

Swift를 공부하는 과정 중 많은 고비가 있습니다.

그중 하나는 단연 클로저라 할 수 있죠.

클로저가 뭐냐고요?

함수입니다.

그럼

.

.

.

.

.

...사실 이 정도 설명으로 끝날 주제였다면 시작도 하지 않았겠죠.

설명할 게 좀 됩니다.

그러니 질문을 하나 던져봅시다.

절대 설명하기 귀찮아서가 아니라,

모르는 것을 아는 것이 배움의 시작 이어서입니다.

(누군가 했던 말 같은데 누군지는 기억이 안 나네요 아시는 분은 댓글부탁드려요)

그럼,

클로저가 왜 어려울까요?

가장 큰 이유는 $0, $1 가 갑툭튀하고,

기존에 쓰지 않던 문법들이 등장해서...라고 저는 생각합니다.

마치 숫자만 있던 초등학교 시간의 아름다운 수학시간이 지나고,

중, 고등학교에서 점점 문자와 기호만 그득한

수학(숫자 안 나옴)을 보는 것과 같은 느낌이랄까요?

그니까,

func deepThought() {
	print("42")
}

이런 걸 보다가 갑자기,

이런 걸 봐버리니 정신이나가버릴것같은것이죠.

하지만 두려워 마십시오.

Swift 뉴비인 저도 저렇게 어렵게 보이게 쓰는 게 가능하니

여러분도 조만간 어렵잖게 저렇게 어렵게 보이게 쓸 수 있습니다.

Swift를 공부하며 클로저까지 왔을 시점이면,
타입 선언(Type Annotation)과 타입 추론(Type Inference),
그리고 함수 파라미터에 대해서는 알고 계시리라 간주하고 설명하겠습니다.
*모르시는 분들을 위한 설명은 추후에 따로 올려보겠습니다.

함수는 클로저다

먼저 함수는 클로저입니다.

정확히는 우리들이 헷갈려하는 클로저는 "익명 클로저"라는 이름이 없는 함수이죠.

이름 있는 클로저인 함수는 잘 써보셨을 겁니다.

아니라고요...? 그거는... 예... 추후 함수에 대해 글을 써보겠습니다...!

여튼 지금 이 글에서 말하려는 헷갈리는 클로저는 익명 클로저입니다.

아래 코드에서 함수와 익명 클로저를 함께 보시죠:

5번 줄과 7번 줄에서 볼 수 있는 나는상수다에 할당된 { } <- 요 녀석이 클로저입니다.

위에 있는 "나는함수다" 함수와 비슷한 기능을 하죠.

함수와 익명 클로저의 공통점이 좀 보이시나요?

함수는 함수명 옆에 리턴 타입과 파라미터들이 적혀있는 반면,

익명인 클로저는 { 중괄호 } 안에 리턴 타입과 파라미터들을 작성하게 됩니다. 실행문과는 in 키워드로 분리가 되고요!

그러니까 정리하자면 이렇습니다:

파라미터가 있는 다른 예시로도 한 번 볼까요?

둘이 똑같은 기능을 하는 것을 볼 수 있습니다.

모양은 다르지만요.


그래서 결론은?

{ }가 클로저다.

이게 내 결론이다.


근데 여기서 의문을 가지실 수 있습니다.

"아니 내가 많이 봐온 클로저랑은 좀 다르게 생겼는데?"

맞습니다.

클로저가 헷갈리는 이유가 여기서 발생하죠.

풀 버전에서 생략되는 게 많습니다.

마치 마블 영화 요약 설명을 *튜브에서 찾아보면,

뭔가 (중략) 되는 부분이 많은 것처럼요.

다른 예시로 설명해 보겠습니다:

이번엔 클로저와 상수를 같이 적어보았습니다.

같이 적힌 상수 선언과 클로저의 공통점이 보이시나요?

공통점이 잘 안 보이는군요. 표시를 해보겠습니다.

좀 감이 오시나요? 오지 않아도 괜찮습니다.

제가 친절하게 설명해 드립니다.

위의 사진에서 표시된 부분은 타입 선언입니다.

각각의 영역이 알려주는 내용을 문자로 풀어서 써보겠습니다:

  1. is42는 - "Int를 받아 Bool을 리턴하는 클로저"가 할당된 상수이다

  2. fourtyTwo는 - "Int 타입"의 상수이다

Swift Playground에서 변수 좀 할당해서 놀아보신 분들이라면,

어렵잖게 이해가 가능하실 겁니다.

생략의 시작

근데 아시다시피, Swift에는 타입 추론이라는 기능이 있습니다.

주어진 값에 따라 데이터 타입을 Swift가 추론할 수 있는 기능이죠.

그래서 변수 또는 상수를 생성할 때 타입 선언을 생략하는 게 가능합니다.

(단, 주어지는 값에서 추론이 가능해야 합니다.)

어? 근데 보면, is42는 뒤에 (num: Int) -> Bool이라고 함수 인풋값과 리턴값이 명시되어 있네요?

그리고 fourtyTwo는 정수 42가 할당되어 있구요~!

Swift가 타입을 추론할 수 있으니,

저기 위에 표시한 영역을 썰어서 날려버려도 된다는 말이죠.

금방 다듬어서 갖고 올 테니 기다려주셔요.

(서걱서걱)

여기 나왔습니다:

하지만 여기서 끝이 아닙니다.

클로저 구문 내에서 선언된 타입도 생략이 가능합니다.

왜냐?

뒤에 있는 return num == 42에서 타입 유추가 가능하기 때문이죠.

네? 무슨 소리냐고요?

return num == 42는 짧아 보이지만 자그마치 3개의 형식이 합쳐진 콤비네이션,
Int값 42, Boolean 연산자 ==, 그리고 Int 인풋값 num.

Swift는 여기서 이것들을 보고는,

"음! Int값인 42와 Comparable**(==)하려면,
또 다른 Int인 num을 받아야겠고,
비교한 결과는 Bool이니, 리턴 타입은 Bool이겠구만!"

이라고 생각*하며 센스 있게 받아들이기 때문이죠.

* 실제로 스위프트가 이런 말투로 생각하거나 작동하는지는 모릅니다. 글쓴놈의 상상일 뿐입니다.
** 여기서 Comparable은 Swift의 프로토콜 중 Comparable을 일컫는 것입니다. 관련해선 나중에 더 자세히 써보도록 하겠습니다.

네 여튼 Swift가 알아서 타입 유추를 할 수 있으니 생략 가능하다는 말입니다.

더 썰어오도록 하겠습니다

(서걱서걱)

근데 사실 더 줄일 수 있습니다.

만약 클로저가 반환 값을 갖는 클로저이고,
클로저 내부의 실행문이 단 한 줄이라면,
암시적으로 그 실행문을 반환 값으로 사용할 수 있습니다.
스위프트 프로그래밍 3판에서 발췌

예시와 같이 실행문이 한 줄이고, 그게 리턴값이면

return을 날려버려도 된다는 소리입니다.

(서걱)

근데 사실 더 줄일 수 있습니다. (2트)

바로 짧은 인수 이름(Shorthand Argument Name)이라는 것을 사용한다면 말이죠.

거 왜 맨날 map, filter, reduce, sorted 같은 메서드 사용하다 보면 항상 보이는 거 있잖아요.

0달라 1달라 2달라 3달라...

가 아니고...

$0, $1, $2... 이거요...


이 짧은 인수 이름을 사용하면,

클로저의 머리와 몸통을 분리하고 있던 목뼈 개념의 in 키워드도 생략이 가능합니다.

왜?라고 물어보시면... Swift가 그렇게 태어났어요...
(BGM: Born This Way - Lady Gaga)

그야말로 몸뚱이만 남기고 다 없애버린 것이지요.

관공 어찌하여 목만 오셨소... 가 아니라,

클공 어찌하여 몸통만 오셨소...

가 되겠네요.

이렇게 말이죠.

축약하는 과정을 정리해 보자면 아래와 같습니다:

// 기본 문법
let is42: (Int) -> Bool = { (num: Int) -> Bool in
    return num == 42
}

// 상수의 타입 선언을 생략하기 (타입 추론)
let is42 = { (num: Int) -> Bool in
    return num == 42
}

// 클로저의 타입 선언 생략하기 (입력, 반환되는 값에서 추론)
let is42 = { num in
    return num == 42
}

// return문 생략
let is42 = { num in
    num == 42
}

// Shorthand Argument를 사용하여 in 구문 생략하기
let is42 = { $0 == 42 }

어느 정도... 이해가 되셨나요?

다음 시간에는 자주 쓰이는 메서드들로 예시를 작성해 보도록 하겠습니다.

그럼

이 포스트는 아래 자료에 기반하여 작성되었습니다:
Swift 한국어 - 클로저 (Closures) [링크]
스위프트 프로그래밍 3판 - 야곰

8개의 댓글

comment-user-thumbnail
2024년 10월 25일

모야 글 왜케 잘 써요,,, 재밌어서 훅훅 읽히네 클로저 저도 어려워서 맨날 검색하는데ㅜ 잘 보고 갑니둥

1개의 답글
comment-user-thumbnail
2024년 10월 25일

도대체 0달러가 어떻게 42라는 건가요? 0달러는 0 아닌가요? 서걱서걱 깎으면 단가요?

1개의 답글
comment-user-thumbnail
2024년 10월 25일

클로저가 어떻게 저렇게 짧아질 수 있을까 했는데, Swift가 타입 추론을 할 줄 알아서 그렇다는 게 아주 유용한 지식이었습니다. 이번 포스팅을 통해, Swift의 기능과 말투까지 배워갑니다.

1개의 답글
comment-user-thumbnail
2024년 10월 28일

"모르는 것을 아는 것이 배움의 시작"
워낙 좋은 말이라 여러 사람들이 했을 것 같긴 한데, 찾아보니 소크라테스의 명언인 것 같네요
"무지를 아는 것이 곧 앎의 시작이다" - 소크라테스

글 정독하고나니까 클로저가 덜 무서워졌어요! 재밌고 유익한 글 고맙읍니다!!

1개의 답글