Slug erica sadun cover

Swift 연산자 완벽 이해하기

Swift 연산자들은 유연하며 강력합니다. 이들은 수학에서 사용되는 일반적인 표기법을 따라 함수처럼 동작합니다. add(1, 2) 함수를 연산자를 이용해 1 + 2 로 표기할 수 있는 것처럼요. 하지만 연산자들을 다룰 때는 항상 주의를 기울여야 합니다.

Swift 언어 사용자 그룹 강연에서 Erica Sadun은 사용자가 직접 정의한 연산자를 신중하게 사용해야 하는 이유에 관해 이야기합니다. 강연에는 재미난 퀴즈도 함께 준비되어 있습니다. “Name That Operator!” 게임을 즐기면서 Swift 연산자 사용 사례들을 함께 살펴봅시다.


소개 (0:00)

저는 작가이자 블로거이며, 프로그래머이며 애플 생태계와 관련된 글을 주로 쓰고 있는 Erica Sadun입니다. 최근에 집필한 Swift Style이라는 책은 Pragmatic Press를 통해 베타 버전 이 공개되었고 조만간 출간될 예정입니다.

왜 스타일이 중요할까요? 여러분이 코드를 작성할 때 코딩 스타일을 리뷰하거나 향상하는데 관심을 가져야 하는 이유는 무엇일까요? 컴파일러는 여러분이 어떤 코딩 스타일을 가졌는지 신경 쓰지 않습니다. 스타일은 컴파일러에서 코드가 정확한지 확인하는 작업과 관련 있는 것이 아니라, 사람과 관련이 있습니다.

여러분의 코드는 컴파일러만을 위한 것이 아닙니다. 좋은 스타일은 함께 일하는 동료뿐만 아니라 코드를 작성하는 본인과 나중에 이 코드를 다시 볼 미래의 여러분과 관련 있습니다. 좋은 스타일을 배워서 코드의 가독성과 유지 보수성을 높여야 할 것입니다.

개발 환경에서 만날 수 있는 몇 가지 예시를 소개하고 코딩과 사람들 간의 관계에 관해서도 이야기해보도록 하겠습니다.

연산자는 멋집니다. (2:39)

연산자란 무엇이고 어떻게 동작할까요? 연산자는 함수처럼 동작하는 기호(symbol)라고 말할 수 있습니다. 일반적인 함수와는 다르게 연산자는 통합된 문법(syntax)을 가지고 있습니다. 연산자는 수학에서 사용되는 일반적인 표기법을 사용하여 값을 확인하거나, 변경, 결합할 수 있습니다.

1 + 2

addPair(1,2)

두 표현식 모두 두 개의 숫자를 더하는 일을 수행합니다. 연산자를 사용한 첫 번째 예제는 + 기호를 사용하였고, 두 개의 숫자 사이에 있습니다. 이러한 형태를 infix form 이라고 부르고 이러한 형태를 보이는 연산자를 중위 연산자라고 합니다. 읽기 쉽고 간단하지요? 수학에서의 표기법과도 일치합니다. 방정식을 작성하던 방식으로 코드를 작성하면 됩니다.

두 번째 예제인 addPair는 함수 버전입니다. 이 버전에는 addPair 라는 이름이 붙어 있네요. 괄호 안에는 인자들을 나열했는데 첫 번째 예제에 비해 장황해 보이네요. 이런 표현 방식은 좀 더 구조적으로 보이기는 하지만 연산자를 이용한 방식에 비해 자연스러워 보이지는 않는군요.

이런 개발 뉴스를 더 만나보세요

여러분이 처음 이 예제를 보았을 때 연산자를 사용한 방식이 더 읽기도 쉽고 이해하기도 쉬울 거로 생각합니다. 왜냐하면, 여러분이 손으로 필기해가면서 수학을 배웠던 방식과 같은 형태이기 때문이지요.

일반적으로 연산자는 멋집니다. 연산자는 가독성이 높고, 여러분이 이미 알고 있는 개념을 사용하고 있으며, 코드의 의도를 명확하게 해줍니다.

그렇다면 연산자를 사용하는 것이 항상 옳은 것일까요? 다른 언어들처럼 연산자도 장단점이 있습니다. 연산자를 사용하는 것이 적절할 때가 있고 그렇지 않은 경우도 있습니다.

중요한 것은 연산자를 언제, 어떻게 사용하는 것이 적절한지 아는 것입니다.

Swift 연산자들

간단하고 보편적인 연산들을 처리하려면 연산자가 필요합니다. Swift는 기본적인 빌트인 연산자들을 제공하고 있습니다. 예를 들어, Boolean 논리 연산을 위해 논리 연산자 && 가 제공됩니다. 우리에게 익숙한 형태이며 사용하기 편리합니다.

시프트 연산자 << 같은 비트 연산자도 제공합니다. 강력한 Swift 연산자 중의 하나이지요. 또한, 1부터 5까지와 같은 범위를 표현하기 위해 ... 와 같은 연산자도 제공하고 있습니다. 이 연산자들은 Swift 언어의 강력한 표현 요소입니다.

사용자 정의 연산자

빌트인 연산자들뿐만 아니라 Swift 에서는 1개 또는 2개의 인자로 구성된 사용자 정의 연산자도 사용할 수 있습니다. 사용자 정의 연산자들을 이용하면 표현할 수 있는 영역이 풍부해지는 효과가 있습니다.

예를 들면, ** 과 같은 사용자 정의 연산자를 만들 수 있습니다. 저는 지수(exponentiation) 연산을 표현하기 위해 이 연산자를 정의하였습니다. 이 연산자에 대한 구현은 정말 간단하며, 파이썬과 같은 다른 프로그래밍 언어에서도 이 연산자를 지수 연산에 사용하고 있습니다. 이처럼, 사용자 정의 연산자를 통해 기존의 표기법을 Swift 생태계에서도 그대로 사용할 수 있습니다.

사용자 정의 연산자의 다양한 활용

사용자 정의 연산자는 수학 표현식뿐만 아니라 다른 영역에서도 활용할 수 있습니다. 예를 들어, --> 와 같이 안전한 비트 캐스트(bitcast) 연산을 수행하는 사용자 정의 연산자를 만들 수 있습니다. 이 경우에는, destination 타입의 사이즈가 캐스트하려고 하는 아이템의 메모리 구조와 일치할 필요가 있겠지요. Swift 빌트인 연산자에는 없는 이러한 추가 검증 기능은 Swift 언어의 기능과 가치를 향상시킵니다.

작년에 Swift에서 사라졌던 ++, -- 를 사용자 정의 연산자로 만들 수도 있겠네요. 이들 연산자를 제거하는 결정은 Swift가 오픈 소스가 되기 전에 이루어진 것입니다. 이들 연산자에 대한 사용자 정의에 관심이 있으신 분들은 저의 코드를 참고하시길 바랍니다.

사용자 연산자 예시

사용자 정의 연산자들은 인쇄 기술에 사용하는 기호들을 사용하는 경우가 많습니다. 예를 들어, 유니코드 기호 를 이용해 Swift 표준 라이브러리가 제공하는 union 연산자를 대신하는 사용자 정의 연산자를 만들 수 있습니다.

사용자 정의 연산자를 사용하여, union 함수를 사용하지 않고도 함수를 적용(apply)할 수 있는 강력한 방법을 얻게 되었습니다.

let set1: Set<Int> = [1, 5]
let set2: Set<Int> = [2, 5]
let set1: set1  set2
print(set3) // [1, 2, 5]

예제의 세 번째 라인을 보면 사용자 정의 연산자가 사용되고 있습니다.

부분 집합 연산자 역시 이와 유사한 방법으로 만들 수 있습니다. 수학에서 부분 집합을 표기하기 위해 사용되는 기호인 를 Swift에서도 그대로 사용할 수 있습니다. 하지만, 문제는 이 기호들을 입력하기 어렵다는 것입니다.

사용자 정의 연산자를 입력하는 방법

맥 OS 사용자라면 특수 문자표에서 검색을 이용해 원하는 유니코드를 찾을 수 있을 것입니다.

키보드 매크로를 만들어서 연산자 입력에 활용할 수도 있지만, 안타깝게도 시스템 설정에서 만든 단축키는 Xcode 에서는 동작하지 않습니다. 키보드와 관련된 해결책을 원한다면 Keyboard maestro 와 같은 서드 파티 솔루션을 찾아보시길 바랍니다. 이들은 사용자 정의 연산자를 쉽게 입력할 수 있는 환경을 제공해 줄 것입니다.

연산자 입력은 쉬워야 한다

서드 파티 솔루션을 이용해 입력 문제를 해결했다고 하더라도, 상당한 양의 유니코드 연산자를 입력해야 할 것입니다. 한 번 생각해보세요. 이 소스코드를 오픈 소스로 만들 건가요? 서드 파티에서 사용하는 라이브러리로 만들 계획인가요? 사용자들도 당신처럼 연산자 입력을 도와주는 매크로 도구들을 사용해야 하나요?

여러분의 사무실에서는 쉽게 입력할 수 있는 것일지라도 라이브러리 배포를 고려하는 경우라면 연산자 입력이 쉽지 않을 수도 있습니다. 따라서, 연산자 클럽(Operator Club)의 첫 번째 규칙은 입력하기 어려우면 사용하기 어렵다 입니다.

여러분이 작성한 연산자가 수학적 표기법과 일치하며 쉽게 알 수 있는 기호를 사용하였더라도, 코드를 작성할 때 쉽게 입력할 수 없다면 미적인 요소를 위해 유용성을 포기한 것이므로 좋은 선택이라고 할 수 없습니다.

연산자를 작성할 때는 연산자와 관련하여 얼마나 많은 수고가 필요한지를 고민한 뒤 연산자를 선택해야 하며, 연산자로 사용해도 되는지는 수고로움의 강도를 기준으로 판단할 수 있습니다.

연산자 클럽의 또 다른 규칙은 모두가 US 키보드를 사용하지 않는다 는 점입니다. US 키보드에서는 입력하기 쉬운 기호일지라도 다른 나라 키보드에서는 입력이 어려울 수 있습니다. 이러한 문제를 피하려면, ASCII 코드를 기반으로 연산자를 작성하시길 바랍니다. 모두가 같은 키보드 레이아웃을 가지고 있을 것이라고 가정하지 마세요.

연산자를 사용해야 할까요? (13:58)

지금까지 연산자를 작성하는 방법을 살펴보았는데, 다시 강연 시작부분에 언급했던 질문으로 돌아가 보죠. 사용자 정의 연산자가 정말 필요한가요?

연산자는 인간의 인지 능력 중 크게 두 가지 요소와 관련이 있습니다. 인지 능력이란 사람들의 사고방식, 정보를 처리하는 방법을 말합니다. 우리의 인체와 정신세계가 어떻게 프로그래밍과 상호작용하는지 이해하면, 좋은 코딩 습관과 스타일을 갖는 데 도움이 될 것으로 생각합니다. 연산자와 관련된 인간의 인지 능력은 인지(recognition)와 기억(recall)입니다.

인지는 익숙함(familiarity)과 관련되어 있습니다. 코드에서 연산자를 발견했을 때 그것이 무엇을 의미하는지 알 수 있나요? 아래의 예제 코드를 보세요.

let set3 = set1  set2

많은 분들이 이 연산자가 무엇인지 아시겠죠. 쉽게 알게 된 이유는 수학의 표준 set 연산자와 닮아있기 때문이기도 하고, union이라는 이름과 유사한 기호가 사용되었기 때문이기도 합니다. 또한, 제가 좀 전에 이것을 union 연산자라고 설명했기 때문이기도 하죠. 이 연산자를 보고 그것이 맞는 기호인지 틀린 기호인지 확인하기만 하면 됩니다.

기억은 인지보다는 어렵습니다. 기억은 관련 개념들을 담고 있는 정보들과 연계하여 기억저장 공간에서 정보를 검색합니다.

부분 집합 연산자가 어떻게 생겼었는지 기억나세요? 오목한 부분이 오른쪽인지 왼쪽인지 기억나세요? 머릿속으로 연산자를 시각적으로 상상해보세요. 어떻게 생겼나요? 아래의 코드 예제를 보세요.

let isSubset = set1  set3

연산자를 보면 그 즉시 인지(recognition)가 발생합니다. 문맥 정보와 코드는 set1 이 set3 의 부분 집합임을 나타내는 연산자라는 사실을 명확하게 해줍니다. 인지는 항상 기억보다 인지의 부담이 적습니다. 특정한 사실이 옳은 것인지 인지(recognition)하는 것이 문제에 대한 답을 찾는 기억(recall)보다 쉬운 것이지요.

일반적으로, 함수 이름을 인지하는 것이 연산자를 기억하기보다 쉽습니다. + 기호처럼 그동안 자주 사용했고, 광범위하게 이해하고 있는 연산자의 경우를 제외하고는 말이죠. 기억하는 일은 어려운 것이고 연산자를 기억하는 것은 특히 더 어렵습니다. 얼마나 멋진 연산자를 만들었느냐와 상관없이 새로 만든 연산자는 기억하는 작업에 부담을 더하기만 할 뿐입니다.

문맥 정보가 부족한 연산자

연산자, 특히 수학에서 차용한 것이 아니라 코딩 과정에서의 필요성 때문에 만들어진 사용자 정의 연산자의 경우, 관련 정보와 기능을 코드에서 본 것과 연관시킬 수 있는 문맥 정보와 단서가 태생적으로 부족합니다.

코드 리뷰는 오프라인 또는 인쇄해서 진행되는 경우가 빈번한데, 이런 환경에서는 Xcode 또는 도움말 시스템을 활용할 수 없습니다. 또한, Xcode 의 도움말 기능이 제공되지 않는 개발 플랫폼 또는 리눅스 환경에서 Swift 언어를 사용하는 개발자가 늘고 있다는 점도 간과해서는 안 됩니다. Xcode 는 연산자에 대한 정보를 빠른 도움말 또는 다른 도구들과 연동하여 관련 정보를 찾을 수 있도록 돕고 있지만, MacOS 가 아닌 다른 플랫폼에서는 Xcode 를 활용할 수 없습니다.

연계(Association)

연계성은 용어들을 아이디어와 연관 지어 개념을 좁히는 데 도움을 줍니다. 연계성은 기억 작업을 도와줍니다. Xcode 에서는 연관된 단어를 자동 완성시스템에 넣을 수 있습니다. 표준 set 연산자 중의 하나인 부분 집합(subset)을 수행할 방법이 기억나지 않을 때, 부분 집합과 연관된 용어인 “subset”이라고 입력하면 부분 집합 연산자를 쉽게 찾을 수 있을 것입니다. 이 방법은 연계시스템에서 사용할 수 없는 기호를 기억해내거나 입력하기보다 훨씬 쉽습니다.

부자연스러운 연산자(Unnatural operators)

부자연스러운 연산자는 태생적으로 잘 인식되지 않습니다. 여러분 자신을 이 코드를 읽는 사람, 이 연산자를 만들지 않은 사람이라고 생각해보세요. 이 연산자를 처음 본 사람에게는 수학이나 과학의 표기법에서 가져오지 않은 연산자에 대한 암시적이거나 예상되는 인식이 전혀 없습니다.

누군가는 여러분의 코드를 읽을 것입니다.

당신의 코드를 읽는 것은 컴파일러뿐만이 아닙니다. 여러분은 코드를 작성한 뒤 자신의 코드를 읽어볼 것이며, 한 달 또는 일 년 뒤에 그 코드를 다시 읽을 것입니다. 미래의 당신은 당신이 예전에 작성한 코드를 유지 보수해야 할 것입니다. 여러분의 조직에 있는 사람들이나 당신이 만든 API 사용자들 역시 당신이 작성한 코드를 읽을 것입니다. 도움말 문서를 사용할 수 없는 환경에서는 임의로 만든 연산자는 사용자들에게 제대로 된 의미를 전달할 수 없습니다.

강화(Reinforcement) (29:48)

강화란, 반복과 적절한 사용에 대한 보상을 통해 개념을 여러분의 접근 가능한 기억 속으로 통합하는 것을 의미합니다. 연산자를 적용하여 코드가 동작하고 매번 그것을 사용하면 긍정적인 강화가 발생합니다.

사람들은 기존에 알고 있는 기호들을 대해서는 어려움을 느끼지 않습니다. + 기호를 계속해서 사용해왔기 때문에 + 기호가 더하기를 의미한다는 것을 기억할 필요가 없듯이요. 여러분이 어렸을 때 더하기 기호를 배웠고 모든 계산기에도 이 기호가 표시되어 있습니다. union 연산자나 subset 연산자보다 오래 배우지 않았지만 + 연산자가 더 많이 강화됐습니다.

기억(recall)을 돕기 위해 사용자 정의 연산자는 강화 작업이 필요합니다. 연산자를 정기적으로, 의미 있게 사용하여 여러분의 인지체계를 강화해야 합니다. 많이 사용하지 않을 것 같은 연산자를 만들지 마세요. 연산자를 많이 사용할수록, 기억체계에 더 유지할 수 있습니다.

교훈 (32:06)

첫 번째

연산자는 귀중하며 비쌉니다. 연산자와 관련된 코드를 작성하고, 읽고, 유지 보수하는 작업에는 상당한 정신적 비용이 따라옵니다. 최소한의 연산자만 사용하세요.

두 번째

수학처럼 기본 개념을 이미 인식하고 있는 것을 기반으로 작성된 연산자는 중요한 문맥정보와 관련 단서를 지닙니다. 이러한 단서들은 훈련 비용을 줄여주며, 정신적 유지 기간(mental retention)을 강화해줍니다. 항상 수학 기호와 같은 자연스러운 연산자를 사용하세요.

세 번째

연산자를 사용하기로 했다면 가능한 큰 영향력을 가진 기능을 선택하세요. 제가 관리하는 프로젝트에서는 많은 연산자를 사용하고 있지는 않지만, 이들은 안정성, 효율성, 간결성을 효과적으로 향상하는데 도움을 주고 있습니다. 중요한 기능을 소화하는 연산자를 선택하세요.

네 번째

좋은 연산자일지라도 입력하기 어렵다면 사용하지 않습니다. 간단하고 입력하기 쉬운 연산자를 선택하세요.

다섯 번째

글로벌 환경을 생각하세요. US 키보드에서처럼 독일어 키보드에서도 쉽게 입력할 수 있는 기호를 선택하세요. 또한, 라이브러리를 외부에 배포할 때 항상 사용자에게도 의미 있는 연산자인지를 생각하며 개발하세요.

여섯 번째

연산자를 사용하기로 했다면 꾸준히 사용하세요. 함수만큼 해당 연산자를 자주 사용하지 않는다면 그 연산자를 계속 유지할 것인지에 대한 고민해보고 함수로 대체해야 할 것입니다. 창피한 일이 아닙니다. 이렇게 함으로써 훈련 및 유지 보수 비용을 줄일 수 있습니다.

일곱 번째

가끔 실용성보다는 연산자를 만들어야겠다는 생각 자체에 집중하기도 합니다. Swift 코드가 수학 논문처럼 보인다면 잘못된 길로 가고 있는 것입니다. 예쁜 코드보다는 실용적인 코드를 만드세요.

여덟 번째

강연에서 인지, 기억, 유지, 연계, 문맥 정보 등의 개념에 관해 이야기했습니다. 이러한 개념을 연산자를 평가하는 데 있어 매우 유용한 것들일 뿐만 아니라 여러분의 코드에 적용할 수 있는 인간의 인지능력과 관련된 요소들입니다.

이번 강연에 스타일에 관한 모든 것이 다 들어있습니다. 여러분은 컴파일러뿐만 아니라 코드를 읽을 사람에게 미칠 영향에 대해 생각하며 코드를 작성해야 한다는 것을 배웠습니다. 다른 사람들이 당신의 코드를 유지 보수하고, 확장하고 사용하게 될 것입니다. 프로그래밍 스타일의 핵심적인 내용은 인간이 생각하는 방법과 정보를 처리하는 방법을 이해하는 것입니다. 이것은 모든 스타일 가이드에서 공통으로 이야기하는 사항이기도 합니다.

좋은 코딩, 좋은 스타일은 컴파일러뿐만 아니라 인간도 생각하는 것입니다. 좋은 스타일이란 사람과 협업하는 모든 것입니다.

추가 정보 (37:41)

Swift 코드 작성과 관련한 추가적인 설명을 원한다면, 저의 책을 참고하세요. 개인 블로그에 올라오는 포스팅도 많은 관심 부탁드려요.

다음: Swift 시작부터 RxSwift까지 #4: Swift의 클로저 및 고차 함수 이해하기

General link arrow white

컨텐츠에 대하여

이 컨텐츠는 저자의 허가 하에 이곳에서 공유합니다.

Erica Sadun

Erica Sadun은 많은 책의 저술자이자, 세계 지배 기술을 연마하고 있는 괴짜 자녀를 둔 부모입니다. 그녀는 기술 탐구를 좋아하고 컴퓨팅 및 디지털 미디어에 대한 수십 권의 책을 저술했고 공동 저술이나 저작 기여 경험도 많습니다. Swift에 대해서는 코어 팀 사람들을 포함한 그 누구보다 못지않게 크게 기여했습니다.

4 design patterns for a RESTless mobile integration »

close