Swift API Guideline - (상편) #Naming

J.Noma·2021년 10월 7일
0

Swift : 문법

목록 보기
9/11
post-thumbnail

이번 포스팅에서는 Swift 공식문서에서 지정한 함수명, 변수명 convention에 대해 정리해보겠습니다

출처 : Swift API Design Guideline

🐵 이론


✅ 사용시점(use case)에서 명료해야 합니다

변수든 함수든 간에 호출, 할당하는 부분의 코드를 읽을 때 명료함이 온전히 드러나야 합니다

Why?

선언은 한번이지만 사용은 반복적이므로 사용시점(use case)에 명료하게 읽히는지가 기준이 되어야 합니다

✅ 길더라도 명확하게 짓는게 낫습니다

물론 가장 best는 간결하면서도 그 의미가 온전히 드러나는 것이다

하지만, 길이를 줄이다가 명료함을 해친다면 차라리 길게 만드는 편이 훨씬 낫다

비록 Swift 코드는 굉장히 간결하게 만들어져 있지만서도 smallest code를 짜는게 Goal이 아님을 인지하자

// TODO: 무슨 뜻인지 파악하고 다시 정리
Swift 코드의 이 간결함은 strong type system과 features that naturally reduce boilerplate의 부작용이다


✅ 모든 선언부에 documentation 주석을 답니다

설계자의 심오한 의도는 주석을 통해서만 독자에게 전달할 수 있습니다.
미루지말고 꼭 답시다

hoxy... 주석달기가 어려운가요?

그건 당신이 API를 잘못 설계했다는 증거입니다

documentation 작성에 대해서 convention이 있는데 이건 나중에 따로 다루도록 하겠습니다

🐱 구체적인 Naming 방법


예제코드들을 통해 감을 익혀봅시다

✔ 애매할 바에, 필요한 모든 단어를 넣습니다

예제

💢 Bad

employees.remove(x)

👍 Good

employees.remove(at: x)

✔ 필요없는 단어는 생략합니다

예제

💢 Bad

allViews.removeElement(cancelButton)

👍 Good

allViews.remove(cancelButton) // clearer

✔ 이름은 역할(role)을 토대로 짓습니다

이름은 타입의 제약사항 같은 것들보다 역할을 토대로 짓는게 좋습니다

예제

💢 Bad

var string = "Hello"
protocol ViewController {
  associatedtype ViewType : View
}
class ProductionLine {
  func restock(from widgetFactory: WidgetFactory)
}

👍 Good

var greeting = "Hello"
protocol ViewController {
  associatedtype ContentView : View
}
class ProductionLine {
  func restock(from supplier: WidgetFactory)
}

생각

widgetFactory이라는 파라미터 이름은 물론 나쁘지 않은 선택지입니다. 이름으로 파라미터 타입 정보를 같이 알려주죠.

그런데 파라미터 타입을 꼭 이름에 넣어야 할까요?

파라미터 타입은 따로 알리지 않더라도 쉽게 파악되지 않을까하는 생각이 듭니다
함수호출부 근처에 파라미터 선언부가 있을 것이기 때문이죠

그리고 widgetFactory라는 이름은 restock()로 전달되어 어떤 용도로 쓰일지(어떤 role을 가질지) 드러나지 않는다는 문제가 있습니다

결론적으로, 파라미터의 역할이 포함된 widgetFactoryForSupplier 혹은 supplier가 더 좋은 파라미터명으로 생각됩니다

//TODO: 프로토콜 공부 후 재방문
예외 케이스
associatedtype은 프로토콜의 이름 자체가 role을 나타내므로, 우리가 규칙대로면 타입명을 이름으로 정하는게 맞다
하지만, 프로토콜명과 타입명이 같으면 충돌이 나므로 이런 경우엔 타입+Protocol로 쓰자

protocol Sequence {
  associatedtype Iterator : IteratorProtocol
}
protocol IteratorProtocol { ... }

✔ 타입만으로 정보가 부족하면 이름을 구체적으로 짓습니다

우리는 Argument가 함수에서 어떻게 쓰일지 이름타입으로 유추합니다

그런데, Argument 타입Int, String 같은 기본 타입이면(특히 Any) 용도가 광범위합니다
따라서, 타입이 줄 수 있는 정보가 적으므로 역할(role)을 유추하기 어렵습니다

이런 경우에는 이름에 그 정보를 담아 보완해야 합니다

예제

💢 Bad

func add(_ observer: NSObject, for keyPath: String) {
    ...
}
grid.add(self, for: graphics) // vague

👍 Good

func addObserver(_ observer: NSObject, forKeyPath path: String) {
    ...
}
grid.addObserver(self, forKeyPath: graphics) // clear

생각

우선, 코드를 읽는 사람 입장에서 함수선언부와 호출부 간 차이가 있습니다

함수선언부는 Argument Label + Argument이름 두 가지 모두가 한눈에 보이므로 줄 수 있는 정보가 많습니다
반면, 호출부(use case)에선 Argument이름 하나만 보이므로 여기에 모든걸 담아야 합니다

Bad case에서 for라는 Argument이름을 사용했는데 이게 명확한지/아닌지는 graphics의 타입에 달렸습니다

graphics의 타입이 KeyPath라던지 사용자 지정 타입이었다면 for로도 충분합니다.
타입 자체가 주는 정보가 많아 함수 내 역할을 어느정도 유추할 수 있습니다

반면, 지금처럼 String같은 기본타입이면 forString만으로는 역할을 유추할 수 없어 개선이 필요합니다
Good case에서 제시된 forKeyPath가 좋은 예시입니다


✔ 영어 문장을 읽는 것처럼 짓습니다

함수/메소드 이름과 파라미터는 호출부를 읽었을때,
문법상 영어 문장을 읽는 것처럼 느껴지도록 짓습니다

예제

💢 Bad

x.insert(y, position: z)
x.subViews(color: y)
x.nounCapitalize()

👍 Good

x.insert(y, at: z)          “x, insert y at z”
x.subViews(havingColor: y)  “x's subviews having color y”
x.capitalizingNouns()       “x, capitalizing nouns”

✔ Factory Method는 "make"로 시작하게 짓습니다

예시로는 x.makeIterator() 정도가 되겠습니다

Factory Method

인스턴스를 생성하는 메소드를 말합니다
자세한건 다른 포스팅에서 따로 다루겠습니다


✔ Initializer와 Factory Method는 영어 문장처럼 짓지 않습니다

💢 Bad
일반적인 함수에서는 분사를 붙혀 영어 문장을 읽듯이 변형시켰습니다

let foreground = Color(havingRGBValuesRed: 32, green: 64, andBlue: 128)
let newPart = factory.makeWidget(havingGearCount: 42, andSpindleCount: 14)
let ref = Link(to: destination)

👍 Good #1
하지만, Initializer와 Factory Method는 분사없이 그대로 짓습니다

let foreground = Color(red: 32, green: 64, blue: 128)
let newPart = factory.makeWidget(gears: 42, spindles: 14)
let ref = Link(target: destination)

👍 Good #2
실제로 많이 볼 수 있는 예시로, String을 Int로 value preserving type conversion하는 경우가 있겠습니다
원래는 from이라던지 영어 어순처럼 지어야겠지만 Initializer이므로 _description으로 지었습니다

init(_ description: String)

let userNumber = Int("123")

✔ side-effect 여부에 따라 함수명의 품사를 달리 합니다

//TODO: side-effect이란게 구체적으로 어떤 경우일까. 예시가 필요하다
함수에 side-effect이 있을 수 있다고 판단되면 명령형 동사로 짓고, 아니라면 명사로 짓습니다

//side-effect X
x.distance(to: y)
i.successor()

//side-effect O
print(x)
x.sort()
x.append(y).

어떤 메소드가 mutating/non-mutating pair로 존재한다면 품사를 달리 지어줍니다

기능이 비슷한 두 메소드가 있다고 가정해봅시다

1) 하나는 메소드 외부에서 선언된 어떤 값을 변경시키고
2) 다른 하나는 직접 변경하지 않고 변경될 값을 return합니다

기능이 비슷하니 이름도 비슷해야할 것 같은데 어떻게 구분시킬까요?

예제

구체적으로 어떤 경우인지 떠올리기 어려울 수 있지만 이미 우리가 익히 알고 있는 메소드가 있습니다

  • x.sort(): x를 직접적으로 정렬
  • x.sorted(): x를 직접 변경하지 않고 정렬된 것을 return

규칙

위 예시에서 느껴지겠지만, 이렇게 pair로 존재하는 메소드의 이름은 mutating: 동사 / non-mutating: 동사+ed/ing로 지정합니다

(기본적으로 ~ed를 붙이되 문법적으로 틀리면 ~ing를 붙입니다)

MutatingNon-mutating
x.sort()x.sorted()
x.append(y)x.appending(y)

또 다른 경우

함수의 기능이 동사보다는 명사로 표현하는 것이 더 자연스러운 경우가 있습니다
이 경우엔 mutating 메소드에 form이라는 prefix를 붙혀 구분합니다

MutatingNon-mutating
y.formUnion(z)x = y.union(z)
c.formSuccessor(&i)j = c.successor(i)


✔ Boolean 함수는 어떤 명제를 주장하는 것처럼 지어야 합니다

예제로 이해해봅시다

  • x.isEmpty
    x는 비어있습니다 -> 참/거짓
  • line1.intersects(line2)
    line1은 line2와 교차합니다 -> 참/거짓


✔ 이게 무엇인지?를 나타내는 프로토콜은 명사로 읽어야 한다

//TODO: 이게 무슨말일까



✔ 무언가를 할 수 있음(능력)을 설명하는 프로토콜은 suffix를 붙인다

~able / ~ible / ~ing같은 suffix를 붙인다

예시

  • Equatable
    : 공평하게 쓰일 수 있는 프로토콜이다
  • ProgressReporting
    : 진행상황을 report하는 능력을 가진 프로토콜이다


✔ 타입, 프로퍼티, 상.변수는 전부 명사로 읽습니다

문서에서 "should read"라는 표현이 자주 사용되었습니다
확실친 않습니다만, 그 뜻은 명사로 읽기위해 명사로 Naming하라는 의미로 생각됩니다



🐷 전문용어 사용 시 유의사항


✔ 일반적인 용어로도 설명이 되면 굳이 사용하지 않습니다

쓰지 않으면 그 의미를 온전히 전달할 수 없는 경우에만 전문용어를 사용합니다



✔ 전문용어를 쓸거면 그 의미를 정확히 알고 사용해야 합니다

굳이 전문용어를 쓰는 이유는, 안 쓰고는 도저히 그 심오하고 깊은 의미를 정확히 전달하기 어려운 경우입니다

그러므로 그 심오함과 깊이를 이해하여 정확하게 사용해야 문제가 없습니다

  • For 전문가
    전문가가 보기에 원래 의미가 다르게 사용(오역)한 것으로 보이면 안됩니다
  • For 초보자
    용어를 모르는 초보자를 오해시키면 안됩니다. 웹 서치를 통해 찾은 용어의 전통적인 의미와 비교하며 혼란에 빠질 수 있습니다


✔ 약어를 피해야 합니다

물론 사람들에게 원래 용어가 쉽게 떠오르는 경우는 약어를 사용하는 것이 매우 효과적일 수 있습니다

그 기준을 제시하자면, 웹 서치로 쉽게 찾아지는 경우에만 약어를 사용해야 합니다



✔ 널리 알려진 표현이 있다면 원리/원칙을 따르는 것보다 더 우선시합니다

초보자에게 쉽게 설명하기 위해 용어를 최적화하다보면 기존의 문화를 배제시키는 경우가 발생할 수도 있는데 그러지 말아야 합니다

예시

  • Array vs List
    프로그래머 모두가 연속된 자료 구조를 Array라 하는 것을 알고 있는데 초보자에게 설명하려고 List로 표현하는 것은 좋지 않습니다
    대중적인 표현을 사용하는 것이 초보자의 웹 서치에도 도움이 됩니다
  • sin(x)
    sin처럼 특정 분야에서 널리 통용되는 용어가 있다면 사용하는 것이 좋습니다

    sin이 유명하지 않았다면 약어를 피해야 한다는 원칙에 따라 verticalPositionOnUnitCircleAtOriginOfEndOfRadiusWithAngle(x)라는 설명 구절로 표현해야 할 것입니다
profile
노션으로 이사갑니다 https://tungsten-run-778.notion.site/Study-Archive-98e51c3793684d428070695d5722d1fe

0개의 댓글