대메뉴 바로가기 본문 바로가기

데이터 기술 자료

데이터 기술 자료 상세보기
제목 스위프트 파헤치기 : 스위프트 고급 문법(Closures)
등록일 조회수 5028
첨부파일  

스위프트 파헤치기

스위프트 고급 문법(Closures)



스위프트 언어에서 새롭게 지원하기 시작한 클로저(Closures)에 대해서 알아보겠다. 클로저는 70년대 중반부터 LISP프로그램 등에서 이미 지원되고 있었다. 함수를 하나의 블록으로 만들어서 사용하는 함수형 언어에서의 사용 방식이 클로저다.



개념

클로저는 C/Objective-C 프로그램 등에서 사용하는 blocks 코드와 유사하고, 자바(Java) 언어 등에서 사용하는 람다(lambdas)와 같은 개념이다. 함수를 ‘{ }’ 기호 안에 넣어서 블록 형태로 만들어 사용하는 것이 스위프트 클로저가 가진 특징이다. 클로저는 어떠한 상수나 변수를 정의한 콘텍스트로부터 참조할 수 있도록 한다. 사용한 클로저 값은 메모리에서 관리해 앱이 종료할 때까지 참조할 수 있다.

클로저는 아래와 같은 방식으로 사용한다.
- 전역 함수는 이름을 갖고, 값을 갖지 않는 형태의 클로저다.
- 네스티드(중첩) 함수는 이름을 갖고 있고, 내부 함수로부터 값을 획득할 수 있는 형태의 클로저다.
- 클로저 표현식은 그 주위를 둘러싸고 있는 컨텍스트(Context)로부터 값을 획득할 수 있는 가벼운 문법의 형태로 작성된 클로저다.

스위프트의 클로저 표현식은 일반적으로 간결하고, 깔끔하며 최적화된 스타일로 제공된다. 이들은 다음과 같이 최적화 될 수 있는 특징을 포함하고 있다.

- 컨텍스트로부터 파라미터와 반환값의 형식을 추론할 수 있다.
- 싱글 표현식 클로저로부터 명확한 반환값을 가짐
- 단축 파라미터 이름
- 클로저 문법의 추적(Trailing closure)



클로저 표현식

네스티드(중첩) 함수는 큰 함수의 일부로서 동작하는 함수 자체에 대해서 코드 블록을 이름붙이거나 정의해 놓은 편리한 개념이다. 하지만, 간혹 함수의 전체 내용에 대한 정의와 이름 등이 없이 구조체와 같이 간략한 버전이 유용할 경우가 있다. 이것은 다른 함수들에 대해서 하나 이상의 파라미터로 받는 기능으로 만들 때 사용하면 된다.

여기서 소개하는 클로저 표현식들은 인라인 클로저(함수명을 생략하고 클로저 문장을 직접 함수의 파라미터로 기술하는 방식)를 간략하고, 집중된 문법으로 작성하는 방법에 관련된 것들이다. 클로저 표현식들은 여러 문법 항목들을 간결하고 명확하도록 하거나 최적화 되도록 한다.

sorted 함수
스위프트의 표준라이브는 제공된 정렬 클로저(sorting closure)의 결과값을 기본적으로 참조하여, 기존에 알려져 있는 값들의 배열을 정렬하는 sorted 라는 함수를 제공한다. 정렬 과정을 마치면 sorted 함수는 원래 제공된 값과 같은 동일한 형식과 크기를 갖게 되지만, 정렬된 상태의 새로운 배열값을 반환한다.

다음 예제 코드에서 표현된 sorted함수는 문자열 값의 배열을 정렬하는 경우다. 정렬 방법은 str1, str2 두 개의 문자열을 정의 하고 그 값을 비교해서 반환하는 방식이다. 알파벳을 체크해서 str1이 str2보다 앞쪽에 있을 경우 ‘true’를 반환하고, 그 이외의 경우에는 ‘false’를 반환하게 된다. 이 때 반환값을 배열 형태로 저장하게 될 sorted라는 함수는 스위프트 프레임워크에서 제공되는 함수고, 두 개의 파라미터를 가지게 된다.

배열에 다음과 같이 5명의 사람 이름을 정의했고, backwards라는 함수의 파라미터로 str1, str2를 정의했다.

let names = [“C 크리스”, “A 알렉스”, “E 이화”, “B 베리”, “D 다니엘”]

softed 함수를 사용해서 이름을 알파벳의 역순으로 표기했다. 첫 번째에는 정렬될 배열의 이름이 포함되고, 두 번째는 조건식에 따라서 반환하는 값을 표시한다. sorted 함수는 스위프트 프레임워크에 다음과 같이 정의돼 있다.

func sorted< C : SequenceType>(source: C, isOrderedBefore: (C.Generator.Element, C.Generator.Element) -> Bool) -> [C.Generator.Element]

이것을 활용해서 reversed라는 변수에 backwards라는 함수를 사용해서 정렬된 값을 정렬된 형태로 넣을 수 있다. 이 때 backwards라는 함수가 sorted라는 함수의 클로저에 해당되는 것이다.



<그림 1> 클로저 함수의 표현(sorted)



클로저 함수의 문법

다음과 같이 중괄호({ }) 안에 파라미터를 써주고 반환하는 값의 형식을 써주고 ‘in’기호 다음에 문법에 맞도록 문장을 기술한다.

{ (파라미터들) -> 반환값 in
//스위프트 문법에 맞는 코드 작성
}

스위프트 표현식은 상수/변수/입력(input) 파라미터를 모두 적용해서 표현할 수 있다. 기본 값은 사용되지 않을 수 있다. 위의 예제에서 살펴본 backwards함수에 대해서 클로저 함수의 문법을 적용해 작성해 보겠다. 클로저 함수는 항상 ‘{ }’ 기호를 사용해서 함수의 내용을 감싸는 것이 특징이다. 첫 번째 예제와 결과값이 같은 것을 볼 수 있다.



<그림 2> 클로저 함수의 결과값 반환예



컨텍스트로부터 반환값의 형식 가정

예제에서 살펴본 바와 같이 클로저를 사용해서 sorted함수의 두 번째 파라미터에 직접 클로저로 변환한 backwards함수의 코드를 넣고, 변수 ‘reversed’에 값을 할당하는 것이 다소 복잡해 보인다고 생각할 수 있다.

값을 정렬하는 클로저는 sorted함수의 두 번째 인자로 들어가게 되는데, 이것은 인자의 형식으로 Bool형을 정의했기 때문에(‘(str1:String, str2:String)->Bool’) 반환되는 값도 Bool 형식이라고 추정할 수 있을 것이다. 이런 경우에 클로저의 파라미터 형식을 정의한 ‘String’과 같이 자료의 형식을 나타내는 코드들도 생략할 수 있다.

이 경우의 코드는 <그림 3>과 같이 간단하게 작성할 수 있다.



<그림 3> 컨텍스 값의 반환



단일 표현식 클로저

클로저라는 이름으로 함수의 파라미터를 기술할 때 우리가 생각하는 기대되는 값들을 표기하기 위해서 사용하는 키워드 등은 모두 생략이 가능하다(이 예제에서는 return키워드). 따라서 reversed함수는 <그림 4>와 ‘in’ 키워드와 변수들로만 구성하는 것이 가능하다.



<그림 4> 단일 표현식



단축 인자 이름($0, $1...)으로 표기하기

클로저를 표현함에 있어서 더욱 단순하게 표시할 수 있는데 <그림 5>와 같이 매개변수 등을 표시할 때 사용하는 형식인 ‘$0, $1...’ 등으로 나타낼 수도 있다. 클로저로 모든 의미가 함축적으로 표현될 수 있으므로 <그림 5>의 예제에서는 ‘in’ 키워드도 생략해 표현해 봤다.



<그림 5> 단축 인자 이름 표현식



연산자를 활용한 클로저 표현

클로저의 다른 한가지 표기 방식으로 모든 변수나 상수들을 코드에 포함시키지 않고 단지 연산자로만 표현하는 방법이 있다. <그림 6>의 예제에서는 5개의 이름을 정렬(sorted)로 표현하기 위해서 ‘>’ 기호만 사용했다.



<그림 6> 연산자 기호의 사용



트래일링 클로저(Trailing Closures)

클로저 표현식을 쓸 때 함수의 마지막 인자로 전달할 경우에 너무 길 경우에는 트래일링 클로저라는 이름으로 함수의 ( ) 이후에 { }로 표시할 수 있다. 이렇게 함수의 바깥에 사용할 경우에 클로저라는 것을 확실하게 보여줄 수 있어서 유용하다.

<그림 7>의 예는 트래일링 클로저를 함수를 정의할 때 기술하는 방법을 보여준다. ‘1.함수 바디’는 일반적으로 스위프트에서 함수를 정의할 때 문법 규칙이다. ‘2.클로저 함수 바디’ 선언은 클로저를 인자로 사용해서 함수를 정의한 경우다. ‘3.트래일링 클로저 함수 바디’ 부분은 트래일링 클로저를 사용할 때 함수의 ( ) 기호 다음에 클로저의 표현식인 { }를 적용한 예다.



<그림 7> 트래일링 클로저 선언

만약 클로저 표현식으로 제공되는 함수의 인자가 1개뿐일 경우에는 함수를 호출할 때 사용하는 함수의 괄호 ( ) 기호를 생략할 수 있다. <그림 7>의 예제의 reversed 변수에서 사용한 클로저 표현식의 경우 <그림 8>과 같이 reversed01, reversed02처럼 표현될 수 있을 것이다. 두 변수는 같은 결과를 보여준다. reversed02가 트래일링 클로저 형식이다.



<그림 8> 트래일링 클로저 선언 표현식

트래일링 클로저는 표현식이 한 라인으로 표현할 수 없을 때처럼 매우 길어질 경우에 유용하게 사용할 수 있다. 함수의 인자 형태로 클로저 표현식이 사용될 경우에 함수의 바깥에 클로저를 기술하게 되면 독립된 형태로 보여지는 효과가 있어 좋다. 스위프트의 배열 형식은 클로저의 표현식을 하나의 인자로 받아서 처리하는 map(_:) 메소드를 제공한다. 클로저는 각각의 배열 아이템에서 한번씩만 불려지게 되고, 선택적으로 map(_:) 메소드를 사용해 값을 반환한다. 맵핑된 값과 반환값의 형식은 클로저에 의해 결정된다. map(_:) 메소드는 스위프트 프레임워크에 다음과 같이 정의돼 있다.

func map< U>(transform: (T) -> U) -> [U]

트래일링 클로저를 사용해서 map(_:) 메소드를 어떻게 사용할 수 있는지 확인해 보자. <그림 9>의 예제는 정수형의 배열을 문자열 값들의 배열로 변환하는 과정을 보여주는 코드다. 상수 numbers는 숫자의 조합에 의해서 [“OneZero”,“TwoNine”,“SevenOne”]:이라는 딕셔너리 문자열 배열로 다시 생성된다.



<그림 9> map 메소드의 사용

이제 numbers 배열을 문자열 값의 배열로 만들기 위해 사용할 수 있다. map메소드가 하나의 인자만을 받게 되므로 <그림 10>의 예제에서 클로저 형식으로 표시한다. 여기서 주의해서 볼 것은 numbers.map은 호출될 때 하나의 인자만으로 처리가 가능하므로 트래일링 클로저를 사용할 수 있다.



<그림 10> 문자열 변환된 정수의 표현

<그림 10>의 예제에서 numbers.map메소드에 인자가 하나만 존재한다. 이것을 기술할 때 클로저 표현식에 의해서 호출될 때마다 output 변수에 변경된 배열 형식인 문자열을 할당하게 된다. 이 때 할당되는 문자열은 number를 10으로 나눈 나머지 값으로 연산되는데, ‘number /= 10’이 0이 될 때까지 반복한다. 할당되는 값을 output이라는 문자열 형식의 변수로 반환해 준다.



값의 캡처(Capturing Values)

클로저를 사용할 때 주변에서 설정된 상수나 변수를 가져올 수 있는데 이것을 캡처(Capture)라고 한다. 클로저는 정의된 함수 내부에서 사용된 상수나 변수의 값을 수정하거나 참조할 수 있다. 심지어 최초로 사용되었던 값들이 더 이상 존재하지 않게 되었을 때도 캡처된 값을 그대로 유지할 수 있다. 즉, 클로저에서 한번 캡처한 값들은 앱이 종료될 때 까지 의미있는 값으로 사용할 수 있다.

클로저의 캡처 사용법을 가장 잘 설명할 수 있는 것은 외부 함수에서 정의된 값을 가져와서 사용할 수 있는 네스티드(중첩) 함수다. 이것은 외부 함수에서 사용된 인자와 상수, 변수를 캡처(capture)해서 사용할 수 있다.



마치며

스위프트의 함수를 블록 형태(‘{ }’)로 표현해 직관적으로 사용할 수 있는 클로저를 알아보았다. 클로저는 함수와 반환값 등을 코드상에서 표기할 때 좀 더 간결하고 직접적으로 사용하기 위해 도입됐다. 클로저를 사용해서 반환되는 함수의 리턴값이나 조건은 정해진 클로저 표현식을 활용해 생략하거나 줄여서 사용할 수 있다.



출처 : 마이크로소프트웨어 6월호

제공 : 데이터 전문가 지식포털 DBguide.net