Jp swiftlint header image

SwiftLint로 보다 명확하고 깔끔한 코드를 만들어 보세요!

코드의 스타일은 크게 중요하지 않습니다. 그러므로 형식과 관련된 규칙 체크는 도구에 맡기고 개발자는 보다 중요한 코딩 측면에 집중해서 앱을 개발해야 합니다. Swift Language User Group 모임에서 JP Simard 는 항상 원하는 의미를 제공하면서 코드를 깔끔하게 유지할 수 있도록 돕는 SwiftLint를 프로젝트에 설정하는 방법을 알려줍니다.


소개 (0:00)

이 강연에서는 SwiftLint를 사용해서 보다 명확한 코드를 작성하는 법을 알려드립니다.

SwiftLint란? (0:36)

swiftlint-what-is-it

SwiftLint는 linter 입니다. 커뮤니티나 팀에서 정한 스타일 규칙을 따르지 않는 코드 부분을 식별하고 표시하는 것을 돕습니다.

사실 현재 나와 있는 Swift 코드 중엔 자연스럽지 않은 것이 많이 있습니다. 또는 다른 Swift 코드와 많이 다른 모습인 것도 있죠. SwiftLint는 이럴 때를 위한 도구로, 일관된 코드를 작성하는데 도움을 줍니다.

사용법은 다음과 같습니다. Xcode 프로젝트에 빌드 단계를 추가하거나 커맨드 라인으로 실행합니다. AppCode, Xcode, Vim, Sublime Text, Atom, Emacs 등을 위한 많은 SwiftLint 플러그인들이 SwiftLint 사용자에 의해 지난 몇 달 동안 선보여졌습니다. 실제로 많은 옵션을 가지고 사용할 수 있으며, 위의 그림처럼 코드의 어떤 부분이 스스로 정한 스타일에 맞지 않는지를 시각적으로 정확하게 보여줍니다.

SwiftLint는 Source Kit과 Swift Compiler와 같은 Apple이 Swift에 제공하는 도구 중 일부에 내장돼 있으므로, 이를 사용해서 Swift의 자체 버전을 파싱하지 않게 합니다. 잘 아시다시피 문법은 계속 변하고 있으므로 예를 들어 주석 처리한 부분이 실제로 코드인 경우와 같은 경우에도 경고를 보낼 수 있습니다. SwiftLint는 이처럼 구문을 똑똑하게 인식할 수 있습니다.

예를 들어 스트링에서 규칙 하나가 위반됐지만 큰 문제가 아니라면 간결하게 보여줍니다. SwiftLint를 비롯한 일반적인 linter에 대한 제 철학은 코드 스타일은 크게 중요하지 않다 는 것입니다. 좀 논쟁의 소지가 있긴 하죠? 실제로 linter를 제작하는 사람에게는 더욱 그럴 겁니다. 하지만 저는 보다 코드의 기능적인 측면에 집중해야 한다고 생각합니다. 컴파일되는지, 실행되는지, 모든 입출력에 대해 기대한 대로 작동하는지 말입니다.

스타일은 그 자체로 끝날 뿐인 단순한 매체입니다. 만약 세상에서 가장 깔끔하고 예쁜 코드를 만든다고 해도 아무 기능도 못하거나 잘못된 기능을 한다면 무슨 의미가 있을까요? 이런 시각에서 자동화를 도입하게 됐습니다. 바로 도구가 필요한 시점이죠. 우리에게 지워진 큰 짐을 대신해줄 기계가 있다면 프로그래머로서, 개발자로서, 코드 리뷰어로서 코드가 기대한대로 작동하는지, 혹은 버그가 있는지처럼 보다 중요한 것에 더 집중할 수 있을 겁니다.

개발 초기의 결정은 SwiftLint 자체에 스타일 가이드를 넣지 않는 것이었습니다. 즉, 다른 사람들이 스타일 가이드에 이미 설정한 스타일 규칙을 기반으로 도구를 만들기로 했습니다. Github의 Swift 스타일 가이드는 SwiftLint 개발 시점에 사람들이 주로 따르는 스타일이었습니다.

실제로 SwiftLint는 커뮤니티에 의해 결정된 스타일 가이드를 따르도록 하는 도구입니다. 그 결과 쉼표를 어디 찍어야 하는지, 세미콜론을 써야하는지 말아야하는지 와 같은 모든 논쟁을 아웃소싱하는 효과도 나타났죠. 이런 아이디어는 커뮤니티가 따르기로 결정하고 준수하는 스타일 컨벤션의 사용을 권장하기 위해 나왔습니다. 그럼으로써 새로운 규칙과 스타일이 발전됩니다.

왜 SwiftLint를 사용할까요? (5:37)

swiftlint-why-use-it

강연에 앞서 오늘 저는 사람들에게 왜 SwiftLint를 사용하는지 물어봤습니다. 여러 이유가 있었는데요. 하나는 모든 파일 중 모든 Swift 코드가 일관성을 유지하도록 하기 위해서입니다. 이렇게 하면 새로운 개발자가 합류하거나 예전 코드를 읽을 때 도움이 됩니다. 모든 것이 일관적이면 어라, 이 코드는 파일의 다른 부분이랑 비교에서 문법이 좀 이상한걸? 하는 것에 신경이 분산되지 않고 중요한 것에 집중할 수 있습니다. 단일 프로젝트에서의 일관성 유지 는 상당히 중요합니다.

다른 이유는 프로젝트간 일관성 유지 입니다. 자신의 서브 프로젝트간이나 회사에서의 프로젝트간, 혹은 전체 Swift 커뮤니티간에도 일관성 유지가 필요합니다. 한 프로젝트에서 일반적으로 Swift가 어떤 모습인지 잘 알고 있고 커뮤니티가 제공한 가이드라인을 잘 따라왔다면, 다른 프로젝트를 봐도 효과적으로 어떤 것이 무슨 역할을 하는지 이해할 수 있고 이 언어에 익숙하다는 좋은 느낌을 가질 수 있습니다.

팀간 일관성 유지 도 역시 중요합니다. 사람들이 SwiftLint를 사용하게 된 이유는 팀에 새로 합류한 초보자나 초보 공헌자가 기존의 스타일을 따르도록 돕고, 나쁜 습관은 피하도록 하기 위해서 입니다.

가독성 측면도 있습니다. 앞서 언급한 것처럼 스타일에 대한 논장을 피하도록 할 수도 있습니다. 도구가 이런 깐깐한 평가를 자동으로 해준다면 핵심 기능에 보다 집중할 수 있게 됩니다.

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

마지막으로, 몇몇 사람들은 자신이 정말 지키고 싶어하는 특정한 코드 스타일 이 있어서 사용하기도 합니다. 예를 들어 자신의 모든 Swift 파일 내 배열의 맨 마지막 원소에 쉼표를 찍어서, 새로운 원소가 추가될 때 diff에 두 줄이 바뀌지 않도록 싶어하는 경우입니다.

일관성 은 사용자들이 SwiftLint나 일반적인 linter를 사용하는 가장 큰 요인으로 보입니다. 사람들이 “어떤 진정한 스타일”을 신경쓰는 것 같지는 않습니다. Swift 작성에서 정말 중요한 것은 모든 것이 일관적인지, 그리고 그 일관성으로 얻어지는 장점이 무엇인지입니다.

규칙 (8:54)

SwiftLint의 방법에 대해 더 알아보겠습니다. 오늘까지 SwiftLint에는 60가지 규칙이 있습니다. 이 중 16개가 opt-in 규칙으로 기본값에서는 꺼져 있습니다. 사용을 위해서는 개발자로서, SwiftLint 사용자로써 네, 기본값에서 왜 꺼져있는지 잘 이해했고, 이들을 제 코드베이스에서 실행할 겁니다. 라고 명시적으로 선언해야 합니다.

약 1/3 가량은 correctable 입니다. 즉, 프로젝트에서 SwiftLint가 자동으로 포맷을 만들고 스타일 위반을 고치도록 하는 것이죠. 이 기능은 앞서 말한대로 Source Kit과 Swift Compiler에 SwiftLint가 깊게 통합돼있기 때문에 가능합니다. 이 수정 기능이 안전하며 코드를 망치지 않을 것이라고 꽤 자신합니다.

규칙의 1/3 가량은 configurable 입니다. SwiftLint의 모든 규칙에 대해 설정에서 그 정도를 정할 수 있으며, 19개의 규칙은 사용자 지정 설정도 지원합니다. 예를 들어 맨 뒤에 쉼표를 찍어야 하는 규칙이 필수일지 생각해 볼까요? 앞서 말한대로 몇몇 이들은 배열나 딕셔너리 맨 뒤에 쉼표를 찍어서 다음 번에 원소를 추가했을 때 두 줄의 diff가 생기는 것을 피하고 싶어 합니다. 반면 어떤 사람들은 이렇게 추가적인 문자가 들어가는 것을 싫어하죠. 이처럼 프로젝트에 스타일과 관련된 선택을 하고, 설정에 포함하면, 프로젝트에서 지키기로 한 스타일이라는 메시지가 됩니다.

swiftlint-stylistic-rules

선호와 관련된 규칙으로 stylistic rules 라는 규칙도 있습니다. 이는 @objc이나 @testable과 같은 속성을 어디에 두는 것이 좋은지에 관한 것으로, 프로젝트에서 어떤 동작을 준수하도록 할지 지정할 수 있습니다. 클로저 매개변수를 어디에 둘지, 여는 괄호와 같은 줄에 둘지 다음 줄에 둘지 같은 것도 결정합니다. 선호와 간단한 배열 선언이나 전체 명시적 선언 등 간편 표기법(Syntactic sugar) 타입 사용을 선호하는지, 앞서 말한 맨 마지막 쉼표를 쓸 것인지, 매개변수 없는 클로저를 뒤에 쓸 것인지 말 것인지 등 스타일과 관련된 일관성을 유지하기 위해 이런 규칙을 많이 만들 수 있습니다.

다음으로는 hygienic rules 가 있습니다. 프로젝트가 과중한 범위를 가지고 있거나 단일 파일에 알려진 것보다 더 많은 책임이 지워져서 과부하가 걸리지는 않은지 일반적으로 확인하는 규칙입니다. 파일이나 타입 바디가 얼마나 오랫동안 유지돼야 하는지도 결정할 수 있습니다. 단일 타입 내에 여러 책임 영역을 유지하려는 경우 익스텐션을 사용하기를 권장합니다. 줄 수나 함수를 얼마나 길게 허용할지와 같은 많은 것에 관련한 규칙들도 모두 구성할 수 있습니다. 어떤 레벨로 설정할지, 규칙을 켤지 끌지 등 다양한 것을 프로젝트에 맞게 설정할 수 있습니다. 개발자의 본능을 믿고 직접 결정하기를 원한다면 그것도 가능합니다.

convention rules 라는 규칙은 Swift 커뮤니티나 Apple에서 선택한 규칙입니다. 예를 들어 최근 Apple은 공식 Swift 문서에서 매개 변수가 없는 클로저를 나타내기 위해 void->가 아니라 ()->를 사용한다고 했습니다. 한편 반환값이 없다는 것을 나타낼 때는 ->() 보다 -> Void를 사용한다고 했습니다.


Empty Parameters: Prefer () -> over Void ->

Void Return: Prefer -> Void over -> ()

모든 구문이 정확하고 컴파일되며 유효한 Swift 코드를 이루더라도, Swift 커뮤니티와 Apple이 결정한 컨벤션은 엄연히 존재합니다. CGRectInfinite 변수 대신 CGRect.infinite 상수를 쓰는 것처럼 기존 지오메트리나 상수 규칙을 사용하는 것도 마찬가지입니다.

code smells 도 있습니다. 물론 코드가 좀 지저분해도 돌아가긴 하겠지만 force try, force unwrap, force cast 등이 코드 내에서 많이 사용된다면 API에 대해 다시 생각해봐야 한다는 증거일지 모릅니다. 어떻게 이들이 노출될지 다시 생각해보고 실패할 때를 대비해서 지역화하거나, 더 많은 함수에 throw를 표시해서 구현부가 아니라 호출부에서 에러를 처리하는 방법을 지정할 수 있습니다.

일반적으로 이 규칙은 적당히 사용하기에는 좋지만 많이 사용하는 것은 나쁜 징조일 수 있습니다. 함수 안의 매개 변수 숫자도 유사합니다. 20개 이상의 매개변수를 가진 함수가 있다면 이들을 구성할 객체나 구조체를 사용해서 타입을 안전하게 재현하고 단일 블록에다 건네도록 만드는 것이 좋습니다.

Cyclomatic complexity 는 특정 코드 블록이 통과할 수 있는 코드 경로의 수를 측정합니다. cyclomatic complexity 값이 10 이상, 20, 30으로 아주 높다면 이해하기 어려울 수밖에 없습니다. 다시 말하지만, 이들이 있다고 코드가 멈추는 것은 아니며 이렇게 사용해도 무방합니다. 특히 많은 멤버를 지닌 enum인 경우 그렇죠. 100개가 넘는 멤버를 가진 enum이 있고, 각각이 다른 코드 경로를 지나가도 이해될 수 있긴 하겠지만, 여기에 여러분의 판단이 필요합니다.

Nesting: 복잡한 중첩 타입이나 선언문을 피하세요. if, if, if, if… 나, 엄청난 피라미드 구조를 가졌다면 코드가 지저분해집니다. 꼭 필요한 경우에는 어쩔 수 없지만 대부분의 경우 재고할 필요가 있습니다.

다른 카테고리의 규칙은 SwiftLint의 규칙들 중 제가 가장 좋아하는 카테고리입니다. 스타일에 관련된 것은 아니며, 자신을 자기 자신으로부터 보호하는 규칙으로, 항상 유효한 Swift를 생성하는 코드입니다. 컴파일되는 코드라도 성능이 좋지 않거나 정의되지 않은 동작이 될 수도 있습니다.

예를 들어 최근 dynamic inline 규칙이 추가됐는데 @dynamic 속성을 @inline(__always)와 함께 사용하지 못하도록 하는 규칙입니다. 만약 Swift 컴파일러가 이 코드를 유효하다고 판정하더라도 컴파일된 코드에서는 의도하지 않은 동작의 원인이 되며 파악하기도 쉽지 않습니다.

다른 예는 empty count 규칙입니다. 크기가 큰 컬렉션을 count를 계산하는 것은 비용이 비쌀 수 있습니다. 많은 Swift 표준 라이브러리 컬렉션이 isEmpty 멤버로 확인할 수 있도록 제공하는데 컬렉션에 원소가 있는지 확인하고 싶을 때 보다 최적화된 코드입니다. 일반적으로 많이 사용되고, 구현에 따라 상당히 빨라질 수 있습니다.

최근 추가된 다른 규칙을 더 소개하겠습니다. Xcode나 Storyboard에서 지원하지 않는 IBInspectable을 사용할 때가 있습니다. 유닛 테스트나 IB 아울렛을 private로 만드는 것과 같은데요. 컴파일이 되긴 하지만 다루기가 힘들죠. 이런 규칙 카테고리에서 Swift가 어떻게 작동하는지, 무엇이 작동하지 않는지, 컴파일러의 확장인지 등을 배울 수 있습니다. 이들은 이상적으로 컴파일러가 이미 제공할 수 있는 종류의 규칙입니다.

멋진 규칙들 (18:55)

제가 좋아하는 규칙들에 대해 말씀드리겠습니다. 아래 empty parameters 규칙을 참조한 예제를 보여드릴텐데요.


// Triggers
let abc: Void -> Void = {}
func foo(completion: Void -> Void)
func foo(completion: Void throws -> Void)
let foo: Void -> () throws -> Void)

// Corrections
let abc: () -> Void = {}
func foo(completion: () -> Void)
func foo(completion: () throws -> Void)
let foo: () -> () throws -> Void)

예제 코드를 두 개의 블록으로 나눴습니다. 위 코드는 규칙을 트리거하는 코드이며, 아래 코드는 규칙을 따르려고 할 때 작성해야 하는 코드입니다. “SwiftLint autocorrect”를 실행하면 이런 위반 사항을 자동으로 발견하고 아래 코드처럼 공식적이고 스타일에 맞는 형식으로 변환해 줍니다.

하지만 SwiftLint autocorrect를 사용할 때라도 SwiftLint 규칙 커맨드를 참조해야 하는 경우가 있습니다. 여러 가지 트리거 예제와 트리거되지 않는 예제를 볼 수 있는데요. 규칙을 제대로 이해하지 못한다면 명령어를 실행해서 예제를 비교해보면 됩니다.

반면 void return 도 있습니다. 위와 같은 코드를 작성하고 SwiftLint autocorrect를 돌리면 스타일에 맞게 수정해줍니다.


// Triggers
let abc: () -> () = {}
func foo(completion: () -> ())
func foo(completion: () -> ( ))
let foo: (ConfigurationTests) -> () throws -> ())

// Corrections
let abc: () -> Void = {}
func foo(completion: () -> Void)
func foo(completion: () -> Void)
let foo: (ConfigurationTests) -> () throws -> Void)

legacy CGGeometry 규칙도 살펴볼까요?


// Triggers
CGRectIsInfinite( rect )
CGRectStandardize( rect)
CGRectIntegral(rect )
CGRectInset(rect, 5.0, -7.0)
CGRectOffset(rect, -2, 8.3)
CGRectUnion(rect1, rect2)
CGRectIntersection( rect1 ,rect2)

// Corrections
rect.isInfinite
rect.standardized
rect.integral
rect.insetBy(dx: 5.0, dy: -7.0)
rect.offsetBy(dx: -2, dy: 8.3)
rect1.union(rect2)
rect1.intersect(rect2)

앞서 Swift 커뮤니티가 기존의 CGGeometry와 같은 C 스타일의 함수 대신 Swift에 맞는 타입, 상수 및 방법을 사용해서 표준화됐다고 말씀드렸는데요. 예를 들어 위와 같은 코드는 아래와 같은 코드로 자동 변환됩니다.

이 기능의 멋진 점은 단순히 스트링을 변환한 것이 아니란 겁니다. 숫자 리터럴이 있음을 확인할 수 있는데요. CGRectInset 예제를 보면, 3개의 매개변수가 있고 호출된 함수에 맞게 재배치되며, 추가 공백을 더해서 Swift 커뮤니티가 지정한 스타일에 맞게 변환됩니다.

간단한 다른 예제도 보여드리겠습니다. 간격이 일정하지 않거나 불필요한 공백이 있는 경우는 어떨까요?


// Triggers
[].map( { } )

// Corrections
[].map({ })

이런 규칙으로 opt-in 설정했다면 아래 코드처럼 수정됩니다.

그다지 훌륭하지 않은 규칙들 (21:57)

여러 이유때문에 그렇게 훌륭하지만은 않은 규칙들도 있습니다. 이런 규칙들을 opt-in 이라고 명명했으며, 기본값으로는 꺼놨기 때문에 SwiftLint 사용자가 명시적으로 SwiftLint 설정에서 opt-in을 켜야 합니다.

규칙을 opt-in으로 설정한 이유는 다음과 같습니다. 먼저 이 규칙들이 때로 오판할 수 있기 때문입니다. empty count rule의 경우가 그렇습니다. 어떤 컬렉션 타입에서는 이것이 객체가 비었는지 파악하기 위해 count를 부르고 0인지 비교하는 것보다 아주 효율적이라고 말씀드렸었는데요. 사실 isEmpty 멤버가 없어도 되는 컬렉션이 존재합니다.

예를 들어 표준 라이브러리가 아닌 컬렉션이나 서드파티 프레임워크에서 제공되는 컬렉션 중 일부가 그렇습니다. 이 경우 기본값으로 이 규칙을 켜서 코드 상에서는 바꿀 수 없는 에러를 잔뜩 만들어내기보다는, 기본값으로 꺼두고 이 룰을 사용하는 것의 트레이드오프를 잘 이해하는 경우 유용할 수 있다 고 해두는 편이 낫습니다. 사용하기를 원한다면 직접 opt-in 설정을 하시면 됩니다. 이는 SwiftLint 자체가 스스로를 보완하도록 만든 규칙입니다.

다른 이유는 이 규칙들이 느리기 때문입니다. 때때로 많은 처리를 해야할 때가 생기고 규칙이 트리거되는지 여부를 알기 위해 많은 사이클이 소모되므로, 기본으로 이들 규칙을 꺼둠으로써 SwiftLint가 몹시 느리다고 사용자가 불평하는 일이 없도록 했습니다. 이들 규칙을 활성화시키려면 역시 직접 opt-in 설정을 하시면 됩니다.

마지막으로, 논쟁의 여지가 있거나 공감대를 얻지 못하거나 특정 라이브러리를 사용하는 경우에만 유용할 수 있는 규칙의 경우, SwiftLint 전체를 느리게 하지 않도록 규칙을 적용할 수 있게 했습니다. 만약 Nimble을 사용하지 않는다면 함수 대신 writeNimble 연산자를 사용하세요.

이같은 이유로 저희는 16개의 opt-in 규칙을 만들었고, 이는 전체 규칙의 1/3이 안됩니다.

SwiftLint는 현재 60개의 규칙을 가지고 있지만 확장성이 높습니다. 사용자 지정 규칙과 SwiftLint 사용자들이 직접 작성한 실세계와 실사용에 맞는 사용자 지정 규칙을 따르고 있습니다. 이런 규칙들은 사용자 정의 규칙을 구성하는 .swiftLint.yml 파일을 가진 Github 프로젝트를 검색해서 발견됩니다.


comments_space: # From https://github.com/brandenr/swiftlintconfig
	name: "Space After Comment"
	regex: "(^ *//\w+)"
	message: "There should be a space after //"
	severity: error

force_https: # From https://github.com/Twigz/Game
	name: "Force HTTPS over HTTP"
	regex: "((?i)http(?!s))"
	match_kinds: string
	message: "HTTPS should be favored over HTTP"
	severity: warning

double_space: # From https://github.com/IBM-Swift/Package-Builder
	include: "*.swift"
	name: "Double space"
	regex: "([a-z,A-Z] \s+)"
	message: "Double space between keywords"
	match_kinds: keyword
	severity: warning

사용자들이 만든 멋진 규칙들이 많습니다. 예를 들어 커멘트를 적을 때 슬래쉬 뒤에 빈칸을 하나만 넣어서 전체 코드의 모든 커멘트가 일관성을 갖게 하는 규칙도 있습니다.

감명깊었던 다른 규칙은 “force_https”으로, https가 아니라 http를 적게 되면 경고가 뜨게 됩니다. 여기서 알 수 있는 점은 스스로 매치 종류를 지정할 수 있다는 점으로, SwiftLint와 Source Kit의 통합된 덕분에 가능한 기능입니다. 변수나 커멘트가 http://를 가지고 있다면 트리거되지 않고, 스트링으로 지정할 경우에만 트리거됩니다.

또한 IBM에서 사용하는 것과 같은 규칙이 있습니다. 사용자 지정 공백 두개 규칙은 매치 종류가 키워드 인 경우에만 발동됩니다. 즉, 키워드 사이에 한 개 이상의 공백이 있는 경우 트리거됩니다.

말씀드린 것은 방대한 규칙 중 작은 예일 뿐으로, SwiftLint 사용자가 소스를 공개하지 않는 경우에는 사용자 지정 규칙 숫자에 포함되지 않습니다.

.swiftlint.yml 파일로 SwiftLint를 설정하면 프로젝트 전체에서 사용하지 않는 규칙을 비활성화하는 것과 같은 작업을 자유롭게 할 수 있습니다.


disabled_rules: # rule identifiers to exclude from running
	- colon
	- comma
	- control_statement

opt_in_rules: # some rules are only opt-in
	- empty_count
	- missing_docs
	# Find all the available rules by running:
	# swiftlint rules

included: # paths to include during linting. `--path` is ignored if present.
	- Source

excluded: # paths to ignore during linting. Takes precedence over `included`.
	- Carthage
	- Pods
	- Source/ExcludedFolder
	- Source/ExcludedFile.swift

기본적으로 사용하지 않도록 설정한 규칙을 opt-in 해서 사용할 수 있습니다. 어느 경로에서 linter를 사용할지 지정할 수도 있죠. CocoaPods나 Carthage를 사용하는 분 중에 모든 서드파티 코드에서 linter를 사용하고 싶지 않다면 이 기능을 적용할 수 있습니다.

소스 디렉토리나 소스 및 테스트 디렉토리를 포함 할 수도 있습니다. Carthage나 CocoaPods를 제외 할 수도 있습니다. 설정 옵션에 대한 부분은 Github의 SwiftLint 홈페이지에서 확인하세요.

Commands. SwiftLint를 Swift 코드 내에서 제어할 수도 있습니다.


/**** Regions ****/
// swiftlint:disable colon
let noWarning :String = ""
// swiftlint:enable colon
let hasWarning :String = ""
/**** Local ****/
// swiftlint:disable:next force_cast
let noWarning = NSNumber() as! Int
let hasWarning = NSNumber() as! Int
let noWarning2 = NSNumber() as! Int // swiftlint:disable:this force_cast
let noWarning3 = NSNumber() as! Int
// swiftlint:disable:previous force_cast

규칙을 로컬에서 사용하지 않으려는 경우 유용합니다. 전체 파일에서 비활성하고 싶을 때 swiftlint:disable colon 커멘트를 추가하면 콜론 규칙이 전체 파일에서 비활성화됩니다.

파일 전체를 대상으로 하거나 파일의 끝에 한 줄 이상의 새 줄이 필요한 경우라면 이런 것이 유용합니다. 코드 영역에서만 사용하고 싶은 경우, swiftlint:disable colon과 같은 커멘트를 적고, 규칙을 적용하고 싶지 않은 구간이 끝나면 다시 swiftlint:enable colon과 같은 커멘트로 동일한 규칙을 다시 활성화 시킬 수도 있습니다.

이렇게 사용하면 코드 일부를 나머지 프로젝트와 조금 다른 설정으로 쉽게 적용할 수 있습니다. 대체로 규칙은 전체 프로젝트 내에서 한 줄 혹은 한 개의 위반 사항에 대해서만 트리거되는 경우가 많습니다. 이 경우 해당 명령어가 적용될 범위를 줄일 수 있죠.

예를 들어 예제 코드에서는 다음 줄을 비활성화하거나 현재, 혹은 이전 줄을 비활성화했습니다. 이렇게 사용하면 하나의 경고를 무시하기 위해 커멘트로 활성화와 비활성화로 정확한 줄을 감싸지 않아도 되니 편합니다.

SwiftLint를 어디서 사용하게 될까요? 사용 방법은 무궁무진합니다. Xcode 빌드 페이즈에 넣어서 Xcode에서 매번 프로젝트를 빌드할 때 시각적인 피드백을 얻을 수 있습니다. 사용하는 에디터나 IDE가 Xcode가 아니더라도, 커뮤니티에서 많은 버전을 만들어줬기 때문에 많은 옵션을 가지고 사용할 수 있습니다.

혹시 지속적인 통합을 위해 Travis를 사용하고, Mac OS X의 모든 이미지와 최근 출시된 Xcode 8.21은 SwiftLint가 활성화되어 제공되므로 편하게 사용할 수 있습니다. 최신 버전의 SwiftLint가 포함돼 있으며 최신 규칙과 기능이 모두 포함됩니다.

Jenkins에서 사용하고 싶다면 Xcode 형식이나 JSON, HTML, XML 체크스타일 등의 출력을 특정할 수 있는 SwiftLint 설정이나 커맨드라인에서 사용 가능한 다양한 리포터가 있습니다. 대부분의 Jenkins 플러그인을 지원하며, 직접 작성할 수도 있습니다. 형식을 직접 지정하고 싶으면 HTML 리포터를 사용하고 X3에 이를 넣은 후 GitHub 주석에서 링크하면 필요한 형식의 리포터를 얻을 수 있습니다.

ThoughtBot이 제공하는 서비스인 Hound CI가 제공하는 옵션도 있습니다. Github 통합으로 PR에서 SwiftLint 위반사항을 모두 커멘트해줍니다. 로컬에서 실행할 때 auto-correct와 auto-format 명령어를 사용해서 스타일을 적용할 수도 있습니다.

Realm의 한 프로젝트인 Realm Tasks 에서 이를 적용하고 있는데, to-do 리스트 앱과 Realm의 플랫폼이 실시간으로 동기화되는 것을 보여주는 데모 앱입니다. 여기서는 SwiftLint auto-correct를 저장소 루트에서 실행하고 있고, 1초도 안되는 시간에 실행돼서 diff를 볼 수 있습니다. 특히 void return 규칙을 SwiftLint에 넣자마자 이 곳에서 실행했기 때문에 스타일에 맞지 않는 모든 부분을 자동으로 조정할 수 있었습니다. 그 다음 kaleidoscope을 사용해서 diff를 보면서 실제로 적절한 부분만 고쳐졌는지 확인했죠.

다양한 SwiftLint 사용 방법 (32:10)

강연의 마지막 주제로 SwiftLint를 사용하는 다양한 방법에 대해 말씀드리고자 합니다. 사용 방법에 옳고 그름이 있지는 않지만, 몇몇은 좀 더 추천할만한 방법이 있습니다. 일년 반 정도 사용자들과 소통하고 사용 행태를 보면서 사용자 유형을 여섯 가지로 분류해 봤습니다.

첫 번째 유형은 점진적 코드 스타일리스트 입니다. 이 사용 패턴은 매우 큰 프로젝트에서 SwiftLint를 비활성화하면서 시작합니다. 그 다음 스타일 lint의 사용 필요성에 따라 SwiftLint 통합을 시작하면서 일단 모든 규칙을 비활성화해둡니다. 이후 가장 중요하다고 생각하는 규칙부터 활성화합니다. 해당 규칙을 실행하고 이슈를 수정해서 커밋해가면서 느리지만 확실하게 방대한 수십만 줄의 Swift 프로젝트에 필요한 규칙을 찬찬히 적용시켜 나갑니다.

파일 내에 여기저기 세미콜론이 붙어있어도 상관없을 수 있겠지만, @dynamic@inline always를 함께 사용하는 경우는 주의해야 합니다. 이런 것을 모두 취향에 맞게 설정할 수 있고, SwiftLint의 멋진 새 규칙을 사용하지 않는다고 비난할 사람도 없습니다. 카페테리아 컨셉과 같은데, 사용자가 들어와서 원하는 것을 고르고 사용하는 구조입니다. 제가 관찰한 사용법 중 훌륭한 방법으로, 특히 조직 규모가 크거나 프로젝트가 방대한 경우 단지 linter에 맞추려고 전체 코드 베이스를 리팩터링할 수 없을 때 필요합니다.

다른 사용 유형은 회의론자 라고 이름지었습니다. 자신에게 linter가 필요하지 않다고 여기는 사람으로, 이런 경우 SwiftLint가 제공하는 규칙 중 작은 부분만을 사용하는 것도 괜찮습니다. 가장 중요하다고 생각하는 규칙 몇 개를 고르고 프로젝트에 그 가치만을 더한 상태로 놔둬도 괜찮습니다. 다시 말하지만, 여러분의 프로젝트에 SwiftLint 버전을 계속 업데이트하지 않고 새로 추가되는 규칙들을 따르지 않는다고 해서 비난할 사람은 없습니다.

흥미로운 완성주의자 유형도 있습니다. 어떤 사용자들은 SwiftLint는 정말 멋지고, SwiftLint가 말하는게 진리야. 규칙이 만들어졌으면 만든 이유도 있을테니 모든 규칙을 다 켜고 내 프로젝트의 모든 파일을 다 linter에 맞춰야지. 난 하루 종일이라도 매일 매일 linter에 맞출거고, 누구든지 linter에 꼭 맞춰야 해!

사실 그렇지 않습니다. SwiftLint는 기계에 의해 돌아가는 도구로, 기계는 멍청하고 사람은 똑똑하죠. 지나치게 일반화한 감이 있지만, 제가 조언하는 바는 이렇습니다. SwiftLint의 모든 규칙은 적용할 영역과 하지 말아야 할 영역이 있으며, 무시하거나 비활성화해야 할 영역 등이 있습니다. 규칙이 있다고 해서 꼭 쓸 필요는 없습니다.

최고 사령관 유형도 흥미롭습니다. 주로 제가 SwiftLint를 사용하는 방법이기도 합니다. 기본적으로는 사용 방식에 따라 프로젝트의 각기 다른 부분을 조정하는 방식입니다. 예를 들어 모든 force cast를 통합하려는 파일이 있다거나, 반대로 force-casting을 파일에서 비활성화한 후 여기서 이런 작업을 해도 안전하다는 이유를 커멘트로 다는 상황을 생각해 보죠. 이 상황에서는 이런 커멘트 명령어를 자유롭게 사용할 수 있습니다. 전체 코드 베이스를 이런 SwiftLint 명령어로 묶는 것을 선호하지 않는 상황이니, linter가 제공하는 실제 가치 이상으로 사용하고 싶지도 않을 겁니다. 자유롭게 그러나 현명하게 사용하세요.

이런 것을 약간만 적용하는 것은 괜찮습니다. 다만 어떤 상황에서 어떤 규칙이 프로젝트의 각 부분에 적용될지 지속적으로 변화시킨다면 앞서 말한대로 사용자들이 linter를 사용하는 효과인 일관성을 얻기는 어렵습니다.

방랑자 유형을 볼까요? 회의론자와 비슷한 유형인데요, 모든 종류의 SwiftLint 명령어로 전체 프로젝트 코드에 linter를 적용시킬 필요는 없습니다. 주기적으로 상태가 온전한지만 체크해서 코드에 뭔가 문제가 있는지만 확인하면 되죠. 코드 베이스에서 일시적으로 다시 실행해봐도 좋습니다. 코드에서 스타일을 끊임없이 신경써야 하는 번거로움과 부담을 덜고 싶을 경우에 유용합니다. 프로토타이핑 중이고 일정이 촉박하다면 풀 리퀘스트를 푸시하기 전 단계로만 linter를 사용하는 것이 좋습니다.

SwiftLint 전체 규칙을 사용해도 좋고, 특정 규칙들만 사용하거나 프로젝트 내의 특정 파일에만 적용해도 괜찮습니다. 전적으로 여러분의 결정에 달렸죠. 이 사용 예에서 추천하는 사용 패턴은 한달에 한 번쯤 돌려보고 싶은 여러 유용한 규칙들이 있는 경우, 그 때만 상태 확인을 위해 실행하는 방법입니다. 예를 들어 empty count 규칙의 경우 위반이 되더라도 CI 전체를 중단하지는 않겠지만 가끔은 조사해볼 가치가 있죠. 이 경우 SwiftLint에서 사용할 설정 파일을 지정할 수 있기 때문에 하드 모드로 SwiftLint를 실행할 때 사용할 두 번째 SwiftLint 설정 파일을 추가해서 평소에는 사용하지 않을 여러 다른 규칙을 넣고 필요한 ad-hoc에서 실행하면 됩니다.

마지막 유형은 저격수 입니다. @dynamic@inline always 때문에 고통받은 적이 있고 이런 버그를 영영 프로젝트에서 없애 버리고 싶다면 이렇게 사용하세요. SwiftLint는 여러분이 원하는 대로 사용할 수 있습니다.

팁과 결론 (39:07)

마무리하며 드는 생각과 팁을 공유하며 마치겠습니다. 기계에게 의지를 굽히지 마세요. 여러분은 사람이고, 두뇌가 있지만 기계는 그렇지 않습니다. SwiftLint는 완벽하지 않고 버그가 없는 코드 자체가 아닙니다. 스스로의 판단하세요.


// Persist your data easily
let realm = try! Realm()
try! realm.write {
  realm.add(mydog)
}

SwiftLint가 언제 잘못됐는지, 혹은 언제 이를 무시해야하는지 판단하는 것은 스스로의 생각에 달렸습니다. SwiftLint가 틀릴 수도 있습니다. 항상 옳다면 그건 컴파일러의 영역일테고, 이미 그 안에 편입됐겠죠. 절대 컴파일되면 안되는 코드인데 된다면 컴파일러의 버그입니다. linter의 역할은 보다 선택적인 것으로, 유효한 Swift 코드 두 개를 놓고 다른 쪽보다 아마 이 쪽이 좋을 겁니다 라면서 제시하는 것이죠.

다시 말하지만, 기계에게 의지를 굽히지 마세요. 자유롭게 규칙을 비활성화하고, 프로젝트의 특정 부분에서 규칙을 비활성화하기 위해 중첩 설정을 구성하세요. 필요한 경우 프로젝트의 서브 디렉토리에 .swiftlint.yml를 넣을 수 있고, 이 경우 하위 디렉토리에 적용됩니다. 소스 코드에서 규칙을 비활성화하는 것도 겁내지 마세요. 저희가 의도한 사용 방법입니다.

마지막으로, SwiftLint의 모든 공헌자들에게 감사하고 싶습니다. 거의 백 명의 공헌자들이 지난 18개월 동안 참여해 주셨죠. Apple이 도구를 개발하기를 기다리지 않고, 커뮤니티가 주도해서 발전한 큰 증거라고 생각합니다. 커뮤니티는 합심해서 직접 만들 수 있는 힘이 있습니다. 공헌해준 모든 분께 다시 감사드리며, SwiftLint에 적용하고 싶은 규칙이 있는 분은 꼭 참여해주시길 부탁드립니다.

다음: Realm Obj-C와 Realm Swift의 새로운 기능을 소개합니다.

General link arrow white

컨텐츠에 대하여

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

JP Simard

JP works at Realm on the Objective-C & Swift bindings, creator of jazzy (the documentation tool Apple forgot to release) and enjoys hacking on Swift tooling. JP는 Realm에서 Objective-C와 Swift 바인딩 부문을 개발하고 있습니다. 다큐멘테이션 도구인 jazzy의 개발자이며, Swift 도구 개발을 즐깁니다.

4 design patterns for a RESTless mobile integration »

close