Roen의 iOS 개발로그

초심으로 돌아가기) Swift API Design Guidelines 살펴보기

by Steady On

https://www.swift.org/documentation/api-design-guidelines/

 

Swift.org

Swift is a general-purpose programming language built using a modern approach to safety, performance, and software design patterns.

www.swift.org

Fundamentals; 기본

Clarity at the point of use is your most important goal. Entities such as methods and properties are declared only once but used repeatedly. Design APIs to make those uses clear and concise. When evaluating a design, reading a declaration is seldom sufficient; always examine a use case to make sure it looks clear in context.

사용하려는 순간의 명확성이 가장 중요한 목표입니다. 메서드와 프로퍼티 같은 entity는 딱 한번 선언해서 반복적으로 사용됩니다. 그래서 API를 설계할 때 명확하고 간결하게 사용할 수 있도록 해야합니다. 설계를 평가할 때, 선언문을 읽는 걸로는 충분하지 않습니다. 항상 사용 사례를 검토하여 문맥상으로 명료하게 보이는지 확인하세요.

 

Clarity is more important than brevity. Although Swift code can be compact, it is a non-goal to enable the smallest possible code with the fewest characters. Brevity in Swift code, where it occurs, is a side-effect of the strong type system and features that naturally reduce boilerplate.

명확함은 간결함보다 더 중요합니다. 최소한의 문자를 써서 가능한 짧은 코드를 쓰는 것은 목표가 아닙니다. 코드에서의 간결함은 강력한 타입 시스템의 부작용이며, 자연스럽게 보일러 플레이트를 줄이는 기능입니다.

 

Write a documentation comment for every declaration. Insights gained by writing documentation can have a profound impact on your design, so don’t put it off.

매 선언마다 문서주석을 작성하세요. 문서를 작성하면서 얻을 수 있는 통찰력은 설계에 큰 영향을 미칠 수 있으므로 미루지 마세요.

 

If you are having trouble describing your API’s functionality in simple terms, you may have designed the wrong API.
API의 기능을 간단한 용어로 설명하는데 문제가 있다면, 그 API 설계는 잘못되었을지도 모릅니다.

 

  • Use Swift’s dialect of Markdown. ; Swift의 마크다운 형식을 사용하세요.
  • Begin with a summary that describes the entity being declared. Often, an API can be completely understood from its declaration and its summary.
    선언할 entity를 설명하는 요약으로 시작하세요. API는 선언과 요약을 통해 완벽하게 이해될 수 있습니다.
    • Focus on the summary; it’s the most important part. Many excellent documentation comments consist of nothing more than a great summary.
      요약에 집중하세요. 이건 가장 중요한 부분입니다. 많은 훌륭한 문서 주석은 훌륭한 요약으로 구성되어 있습니다.
    • Use a single sentence fragment if possible, ending with a period. Do not use a complete sentence.
      가능하면 마침표로 끝나는 단일문장 조각을 사용하세요. 그리고 완전한 문장을 사용하지 마세요.
    • Describe what a function or method does and what it returns, omitting null effects and Void returns:
      null 효과 및 Void 반환은 생략하고, 함수나 메서드가 하는 일과 반환하는 것을 설명하세요. 
    • Describe what a subscript accesses:
      subscript가 접근하는 것에 대해 설명하세요.
    • Describe what an initializer creates:
      initializer가 생성하는 것에 대해 설명하세요.
    • For all other declarations, describe what the declared entity is.
      모든 다른 선언들에 대해, 선언된 entity가 무엇인지 설명하세요.
  • Optionally, continue with one or more paragraphs and bullet items. Paragraphs are separated by blank lines and use complete sentences.
    선택적으로, 하나 또는 그 이상의 단락과 글머리 기호를 지속합니다. 단락은 빈줄로 구분하고 완전한 문장을 사용합니다.
    • Use recognized symbol documentation markup elements to add information beyond the summary, whenever appropriate.
      공인된 심볼 문서 마크업요소를 사용하여 적합한 언제든 간에 요약 이외의 정보를 추가합니다. 
    • Know and use recognized bullet items with symbol command syntax. Popular development tools such as Xcode give special treatment to bullet items that start with the following keywords:
      기호 명령 구문과 함께 공인된 글머리 기호 항목을 알고 사용하세요. Xcode와 같은 인기있는 개발 도구는 다음에 따르는 키워드와 함께 시작하는 글머리 기호 항목을 특별하게 처리합니다. 
      Attention, Author, Authors, Bug, Complexity, Copyright, Date, Exriment, Important, Invariant, Note, Parameter, Parameters, Postcondition, Precondition, Remark, Requires, Returns, SeeAlso, Since, Throws, ToDo, Version, Warning

 

Naming; 이름짓기

Promote Clear Usage; 명확한 사용을 촉진하세요.

  • Include all the words needed to avoid ambiguity for a person reading code where the name is used.
    코드를 읽는 사용자의 애매모호함을 방지하기 위해 필요한 모든 단어를 포함하세요. 
    예시) 매개변수로 index를 받아 컬렉션에서 제거하는 메서드 remove는 remove(_:)로만 지으면 인자로 들어가는 요소를 지운다고 생각할 수 있으므로 remove(at:)으로 지어서 인자에 있는 것을 제거한다는 것을 명확히 함
  • Omit needless words. Every word in a name should convey salient information at the use site.
    불필요한 단어는 생략하세요. 이름에 들어가는 모든 단어는 사용시에 중요한 정보를 전달해야 합니다.
  • Name variables, parameters, and associated types according to their roles, rather than their type constraints.
    변수, 매개변수, 연관된 타입에는 형식제약 조건이 아닌 역할에 따라 이름을 지으세요.
    예시) 변수이름을 단순하게 string, number 이런식으로 짓지 말고 numberOfSwitches 이런식으로 지어야 함
  • Compensate for weak type information to clarify a parameter’s role.
    취약한 형식정보를 보완하여 매개변수의 역할을 명확히 합니다.

Strive for Fluent Usage; 유창한 사용을 위해 노력하세요.

  • Prefer method and function names that make use sites form grammatical English phrases.
    메서드와 함수의 이름이 사용할 때 문법적인 영어 구문을 형성하도록 만들어보세요.
    예시) x.insert(y, at: z) → x, insert y at z
  • Begin names of factory methods with “make”, e.g. x.makeIterator().
    팩토리 메서드의 이름은 "make"로 시작하세요.
  • The first argument to initializer and factory methods calls should not form a phrase starting with the base name, e.g. x.makeWidget(cogCount: 47)
    initializer와 팩토리 메서드 호출에 대한 첫번째 인수는 기본이름으로 시작하는 구로 형성하면 안됩니다.
    ex) x.makeWidget(cogCoung:47)
  • Name functions and methods according to their side-effects
    함수와 메서드는 부가적인 효과에 따라 이름을 붙입니다.
    • Those without side-effects should read as noun phrases, e.g. x.distance(to: y), i.successor().
      부가적인 효과 없이는 명사구로 읽어야 합니다. ex) x.distance(to: y), i.successor().
    • Those with side-effects should read as imperative verb phrases, e.g., print(x), x.sort(), x.append(y).
      부가적인 효과를 붙이면 명령형 동사 구문으로 읽어야 합니다. ex) print(x), x.sort(), x.append(y).
    • Name Mutating/nonmutating method pairs consistently. A mutating method will often have a nonmutating variant with similar semantics, but that returns a new value rather than updating an instance in-place.
      파괴적/비파괴적* 메서드쌍은 연관되어지게 이름 붙이세요. 파괴적 메서드는 인스턴스를 현재 위치에서 업데이트하고, 비파괴적 메서드는 유사한 의미를 가지지만 새 값을 반환합니다.
      * 파괴적 : 원본의 데이터를 변형시키는
      * 비파괴적 : 원본을 데이터를 변형시키지 않는
      • When the operation is naturally described by a verb, use the verb’s imperative for the mutating method and apply the “ed” or “ing” suffix to name its nonmutating counterpart.
        연산이 동사로표현될 때는 파괴적 메서드에 동사의 명령형를 사용하고, 비파괴적 메서드에는 "ed"나 "ing" 접미사를 적용합니다.
        ex) x.sort() | z = x.sorted(), x.append(y) | z = x.appnding(y)
      • When the operation is naturally described by a noun, use the noun for the nonmutating method and apply the “form” prefix to name its mutating counterpart.
        연산이 명사로  표현될 때는 비파괴적 메서드에 명사형을 사용하고, 파괴적 메서드에는 "form" 접두사를 적용합니다.
        ex) x = y.union(z) | y.formUnion(z)
  • Uses of Boolean methods and properties should read as assertions about the receiver when the use is nonmutating, e.g. x.isEmpty, line1.intersects(line2).
    불리언 메서드와 프로퍼티의 사용은 리시버에 대한 주장으로써 해석되어야 합니다.
  • Protocols that describe what something is should read as nouns (e.g. Collection).
    무언가를 설명하는 프로토콜은 명사로 읽혀야 합니다.
  • The names of other types, properties, variables, and constants should read as nouns.
    타입, 프로퍼티, 변수와 상수의 이름은 명사로 읽혀야 합니다.

Use Terminology Well; 용어 사용 잘하기

Term of Art noun - a word or phrase that has a precise, specialized meaning within a particular field or profession.
명사 - 특정 분야나 직업 내에서 정확하고 특별한 의미를 가진 단어나 구
  • Avoid obscure terms if a more common word conveys meaning just as well. Don’t say “epidermis” if “skin” will serve your purpose. Terms of art are an essential communication tool, but should only be used to capture crucial meaning that would otherwise be lost.
    더 일반적인 단어가 의미를 잘 전달한다면, 애매한 용어의 사용을 피하세요. "피부"가 목적에 부합한다면 굳이 "표피"라고 말하지는 마세요. 용어는 필수적인 의사소통 도구이지만, 굳이 꼭 그 단어여야만 표현할 수 있는 중요한 의미를 나타낼 때만 사용되어야 합니다.
  • Stick to the established meaning if you do use a term of art.
    용어를 사용한다면 정해진 의미를 고수하세요.

    The only reason to use a technical term rather than a more common word is that it precisely expresses something that would otherwise be ambiguous or unclear. Therefore, an API should use the term strictly in accordance with its accepted meaning.
    기술적인 용어를 사용하는 이유는 단지 그게 더 일반적인 단어보다 모호하거나 불분명한 것을 정확하게 표현하기 때문입니다. 그러므로 API는 해당 용어의 허용된 의미에 따라 용어를 엄격하게 사용해야 합니다. 
    • Don’t surprise an expert: anyone already familiar with the term will be surprised and probably angered if we appear to have invented a new meaning for it.
      전문가를 놀라게 하지 마세요: 이미 사용되고 있는 용어에 새로운 의미를 발명한 것처럼 나타내면, 이미 그 단어를 친숙하게 쓰던 사람들은 놀라거나 화가날 수도 있습니다.
    • Don’t confuse a beginner: anyone trying to learn the term is likely to do a web search and find its traditional meaning.
      초보자를 혼란시키지 마세요: 용어를 배우는 사람들은 누구나 웹검색을 하고 전통적인 의미를 찾을 가능성이 높습니다.
  • Avoid abbreviations. Abbreviations, especially non-standard ones, are effectively terms-of-art, because understanding depends on correctly translating them into their non-abbreviated forms.
    약어 사용을 피하세요. 약어, 특히 비표준 약어는 효과적인 전문용어 입니다. 왜냐하면 약어를 이해하는 것은 약어가 아닌 형태로 올바르게 번역하는 것에 달려있기 때문입니다.

    The intended meaning for any abbreviation you use should be easily found by a web search.
    사용되는 약어의 의도된 의미는 웹 검색으로 쉽게 찾을 수 있어야 합니다.
  • Embrace precedent. Don’t optimize terms for the total beginner at the expense of conformance to existing culture.
    선례를 받아 들이세요. 기존 문화에 대한 순응을 비용으로 하면서 모든 초보자를 위한 용어 최적화를 하지 마세요.

    It is better to name a contiguous data structure Array than to use a simplified term such as List, even though a beginner might grasp the meaning of List more easily. Arrays are fundamental in modern computing, so every programmer knows-or will soon learn-what an array is. Use a term that most programmers are familiar with, and their web searches and questions will be rewarded.
    연속적인 데이터 구조의 이름을 List로 단순화된 용어를 사용하면 초보자가 더 쉽게 이해할 수 있다 하더라도 Array로 지정하는 것이 좋습니다. Array는 현대 컴퓨팅에서 기본적인 요소이므로 모든 프로그래머들은 알고 있을 뿐더러 그게 뭔지 곧 배울겁니다. 대부분의 프로그래머들에게 친숙한 용어를 사용하세요. 그리고 웹 검색과 질문에서 배우게 하세요.

    Within a particular programming domain, such as mathematics, a widely precedented term such as sin(x) is preferable to an explanatory phrase such as verticalPositionOnUnitCircleAtOriginOfEndOfRadiusWithAngle(x). Note that in this case, precedent outweighs the guideline to avoid abbreviations: although the complete word is sine, “sin(x)” has been in common use among programmers for decades, and among mathematicians for centuries.
    수학과 같은 특정 프로그래밍 도메인에서는 verticalPositionOnUnitCircleAtOriginOfEndOfRadiusWithAngle(x) 같은 설명 문구보단 이미 광범위하게 사용되고 있는 sin(x)과 같은 용어가 더 선호됩니다. 이 경우 선례는 줄임말을 피하라는 지침을 능가합니다. 완전한 단어는 sine이지만, "sin(x)"는 수십년동안 프로그래머들과 수학자들 사이에서 공통적으로 사용되어 왔습니다.

Conventions; 관례

General Conventions; 일반적인 컨벤션들

  • Document the complexity of any computed property that is not O(1). People often assume that property access involves no significant computation, because they have stored properties as a mental model. Be sure to alert them when that assumption may be violated.
    O(1)이 아닌 어떤 계산된 프로퍼티의 복잡성을 문서화 합니다. 사람들은 종종 멘탈모델로 프로퍼티를 저장했기 때문에 프로퍼티의 접근이 중요한 계산과 연관되지 않는다고 가정합니다. 그 가정이 위반될 수도 있으므로 경고해야 합니다.
  • Prefer methods and properties to free functions. Free functions are used only in special cases:
    자유 함수보다 메서드와 프로퍼티를 선호합시다. 자유 함수는 특별한 경우에만 사용됩니다.
    1. 명백한 self가 없을 때: ex) min(x, y, z)
    2. 함수가 제한되지 않은 제너릭인 경우: ex) print(x)
    3. 함수 구분이 설정된 도메인 표기법의 일부인 경우: ex) sin(x)
  • Follow case conventions. Names of types and protocols are UpperCamelCase. Everything else is lowerCamelCase.
    case 컨벤션을 따릅시다. 타입과 프토토콜은 대문자 카멜케이스로 이름 짓고, 그외 나머지는 소문자 카멜케이스 입니다.
  • Acronyms and initialisms that commonly appear as all upper case in American English should be uniformly up- or down-cased according to case conventions:
    미국 영어에서 일반적으로 모두 대문자로 나타내는 두문자어와 이니셜은 케이스 컨벤션에 따라 대/소문자로 표시되어야 합니다.
    예를 들어 UTF8은 모두 대문자로 나타내야 함. 하지만, 변수이름 등에 쓰일때는 utf8Bytes처럼 모두 소문자로 표시하거나 encodedUTF8처럼 모두 대문자로 나타내야 함. 단어 자체를 하나의 문자라고 취급하면 됨
  • Methods can share a base name when they share the same basic meaning or when they operate in distinct domains.
    메서드는 기본적인 의미가 동일하거나 서로 다른 도메인에서 작동할 때 기본 이름을 공유할 수 있습니다. 
    컬렉션에도 contains가 있고, Shape에도 contains가 있는 것처럼!

Parameters; 매개변수

  • Choose parameter names to serve documentation. Even though parameter names do not appear at a function or method’s point of use, they play an important explanatory role.
    문서에 제공할 매개변수명을 고르세요. 매개변수 명이 함수나 메서드가 사용될때는 나타나지 않더라도 중요한 설명 역할을 합니다.
  • Take advantage of defaulted parameters when it simplifies common uses. Any parameter with a single commonly-used value is a candidate for a default.
    일반적인 사용을 단순화할 때 매개변수에 기본값을 줍시다. 그러면 일반적으로 사용되는 값이 하나인 매개변수는 기본값의 후보가 되는 이점을 얻을 수 있습니다.

    Every member of a method family needs to be separately documented and understood by users. To decide among them, a user needs to understand all of them, and occasional surprising relationships—for example, foo(bar: nil) and foo() aren’t always synonyms—make this a tedious process of ferreting out minor differences in mostly identical documentation. Using a single method with defaults provides a vastly superior programmer experience.
    메서드 패밀리의 모든 구성원은 별도로 문서화 되어지고 사용자로 하여금 이해될 필요가 있습니다. 사용자가 뭘 사용할 지 결정하기 위해서 그걸 전부 이해할 필요가 있고 때로는 거의 동일한 문서에서 사소한 차이를 찾아내는 지루한 과정이 되기 때문입니다. 매개변수에 기본값을 제공하면서 단일 메서드를 사용하면 훨씬 우수한 프로그래서 경험을 제공합니다.
  • Prefer to locate parameters with defaults toward the end of the parameter list. Parameters without defaults are usually more essential to the semantics of a method, and provide a stable initial pattern of use where methods are invoked.
    기본값이 설정된 매개변수는 매개변수 목록에서 가장 끝에 위치시키세요. 기본값이 없는 매개 변수는 일반적으로 메서드의 의미론에 더 중요하며 메서드가 호출되는 안정적인 초기 사용 패턴을 제공합니다.
  • If your API will run in production, prefer #fileID over alternatives. #fileID saves space and protects developers’ privacy. Use #filePath in APIs that are never run by end users (such as test helpers and scripts) if the full path will simplify development workflows or be used for file I/O. Use #file to preserve source compatibility with Swift 5.2 or earlier.
    API가 운영 환경에서 실행될 경우 대안보다 "#fileID"를 선호합니다. #fileID는 공간을 절약하고 개발자의 개인 정보를 보호합니다. 전체 경로가 개발 워크플로우를 단순화하거나 파일 I/O에 사용되는 경우 최종 사용자(예: 테스트 도우미 및 스크립트)가 실행하지 않는 API에서 #filePath를 사용합니다. #file를 사용하여 Swift 5.2 이전 버전과의 소스 호환성을 유지합니다.

Argument Labels

  • Omit all labels when arguments can’t be usefully distinguished, e.g. min(number1, number2), zip(sequence1, sequence2).
    인수를 유용하게 구분할 수 없는 경우(예: min(1번, 2번), zip(sequence1, sequence2) 등) 모든 레이블을 생략합니다.
  • In initializers that perform value preserving type conversions, omit the first argument label, e.g. Int64(someUInt32)
    값 보존 형식 변환을 수행하는 이니셜라이저에서 첫 번째 인수 레이블(예: Int64)을 생략합니다.(일부)Unt32)
  • When the first argument forms part of a prepositional phrase, give it an argument label. The argument label should normally begin at the preposition, e.g. x.removeBoxes(havingLength: 12).
    첫 번째 인수가 전치사 구문의 일부를 구성하는 경우 인수 레이블을 지정합니다. 인수 레이블은 일반적으로 x.removeBoxes(havingLength: 12)와 같은 전치사에서 시작해야 합니다.
  • Otherwise, if the first argument forms part of a grammatical phrase, omit its label, appending any preceding words to the base name, e.g. x.addSubview(y)
    반면에, 첫 번째 인수가 문법적 구문의 일부를 구성하는 경우 해당 레이블을 생략하고 기본 이름에 선행 단어를 추가합니다(예: x.addSubview(y))
  • Label all other arguments.
    다른 모든 인수에 레이블을 지정합니다.

Special Instructions

  • Label tuple members and name closure parameters where they appear in your API.
    tuple 멤버에 레이블을 지정하고 API에 나타나는 곳에서 closure 매개 변수의 이름을 지어줍니다.
  • Take extra care with unconstrained polymorphism (e.g. Any, AnyObject, and unconstrained generic parameters) to avoid ambiguities in overload sets.
    오버로드 세트의 모호성을 방지하기 위해 제한되지 않은 다형성(예: Any, AnyObject 및 제한되지 않은 일반 매개 변수)에 각별히 주의합니다.

요약

  1. 명확성 최고주의!! 명확성은 간결함보다 무조건 중요하다!
  2. API를 설계할 때 명확함을 항상 염두에 두고, 마크업 문서 주석을 함께 정리하는 것을 생활화하자!
  3. 이름을 대충 짓지 말자! 10년 뒤, 20년 뒤의 내가 봐도 이해할 수 있게 짓자
  4. 이름을 지을 땐 선례를 따르고, 최대한 영어 구문으로 사용되도록 하고, 오해할 여지를 남기지 않도록 단어선정에 유의하자.
  5. 컨벤션을 잘 따르자! 특히 카멜 케이스!
  6. 매개 변수에 따라 똑같은 메서드를 여러개 만들지 말고 기본값을 잘 활용해서 꼭 받아야 하는 인자와 그렇지 않는걸 잘 구분해서 만들자!
  7. 인수 레이블, tuple 멤버 레이블, closure의 매개 변수 이름도 귀찮다고 빼먹지 말것! 물론 필요한 생략은 과하게 적는 것 보다 훨씬 낫다!

마무리

사실 나는 클린 코드...를 짜는 사람은 아니었다. 프로그래머스 문제 풀어놓은 것만 봐도 메서드 떡칠에 숏코딩을 즐기는 사람이었다. 좋은 코드, 클린 코드... 뭐 알아먹게 쓰면되는거 아닌가...라고 막연하게 생각했었다. 사람들과 협업을 하고 또 앱을 만들면서 클린 코드가 얼마나 중요한지 깨달았는데, 여전히 클린 코드의 정의가 모호했다.

그러다가 이걸 공부하면서 어디서 본 건데 "코드를 읽을 때, 문서처럼 술술 읽히는 코드가 좋은 코드다."라는 말을 봤다. 이 말을 본 뒤로 나는 더이상 클린 코드에 대한 정의가 모호하지 않아졌다. 연습코드 하나 짤때도, 단순한 코드 하나 적을 때도 허투루 쓰지 말자. 평소의 내 코드가 쌓이고 쌓여서 실전에서 발휘될 것이다.

블로그의 정보

Roen의 iOS 개발로그

Steady On

활동하기