[iOS] 내가 보려고 정리하는 Coding Convention (feat. Style Share)

2021. 8. 17. 21:52Swift, iOS Foundation

개발자로서 협업을 하게 되면, 같은 팀원들끼리 필수로 정해야 하는 규칙들이 있다.

개발을 할 때 지켜야 하는 Coding Convention,
깃을 사용하는 방법인 Git branch 전략,
모두가 알아볼 수 있도록 기록하는 Commit Convention,
프로젝트 내 파일을 관리하는 Foldering Convention 등이 그것들이다. (이 외에도 추가적인 규칙을 정하는 건 팀원들끼리의 자유이다.)


그중 오늘은 첫 번째로 Coding Convention에 대해 다루어 볼까 한다.

이미 솝트에서 경험한 세 번의 프로젝트 (합동 세미나, 솝커톤, 앱잼)에서 자의로든 타의로든 어느 정도 익숙해졌다 생각하지만, 그래도 아직 모르는 부분이 존재하고 이 부분은 iOS 개발자라면 꼭 짚고 넘어가야 하는 부분이기 때문에 정리를 해보려 한다.

오늘 포스팅은 솝커톤의 든든한 현규 선배께서 공부할 때 참고하라고 알려주신 주소,
아래 깃허브 링크에 담겨있는 Style Share를 참고해 작성했다. 
무조건적으로 이 규칙을 따를 필요가 있다는 것이 아니라, 아래 스타일 가이드에는 이런 식으로 컨벤션을 정하는구나. "아 우리도 이런 것들은 정해보고 협업을 해봐야겠다"라고 참고하는 수준으로 살펴보면 좋겠다.

 

GitHub - StyleShare/swift-style-guide: StyleShare에서 작성한 Swift 한국어 스타일 가이드

StyleShare에서 작성한 Swift 한국어 스타일 가이드. Contribute to StyleShare/swift-style-guide development by creating an account on GitHub.

github.com

 

1️⃣ 코드 레이아웃: 코드의 가독성을 높이기 위해 설정해 주는 규칙


들여쓰기 ✔️ 들여쓰기는 탭(tab) 대신 2개의 space를 사용한다.  
띄어쓰기 ✔️ 콜론을 쓸 때는 콜론 오른쪽 부분만 띄어서 쓴다. let names: [String: String]
줄바꿈 ✔️ 함수 정의/호출 코드가 최대 길이를 초과하는 경우에 파라미터 이름을 기준으로 줄바꿈한다.
✔️ 단, 파라미터에 클로저가 2개 이상 중첩될 경우에 무조건 줄을 바꿔서 사용한다.
✔️ if-let, guard-let 구문이 길 경우 줄바꿈하고 한 칸 들여쓴다.
UIView.animate(
  withDuration: 0.25,
  animations: {
    // 줄 바꿔서 코드 작성
  }
)
최대 줄 길이 ✔️ 한 줄은 최대 99자를 넘지 않아야 한다.  
빈 줄 ✔️ 빈 줄에는 공백이 포함되서는 안된다.
✔️ 모든 파일은 빈 줄로 끝나야 한다.
✔️ MARK 구문 위, 아래에는 공백을 준다.
// MARK: - View Life Cycle

override func viewDidLoad() { .. }

임포트 ✔️ 모듈의 임포트는 알파벳 순으로 정렬한다.
✔️ 단, 내장 프레임워크를 먼저 임포트하고, 외장 프레임워크는 빈 줄로 구분 후 임포트할 수 있도록 한다.
import UIKit

import SnapKit
import Then

 

2️⃣ 네이밍: 직관성을 위해 정하는 규칙


Swift 네이밍의 공통으로 적용되는 규칙은, Python에서 사용하는 snake_case 대신 CamelCase를 사용해서 네이밍을 한다는 것이다.
또한, 이름만 보았을 때에도 직관적으로 어떤 역할을 하는 인스턴스, 메서드인지 알아들을 수 있도록 하는 것이 핵심이다.

  • lowerCamelCase: 함수, 메소드, 변수, 상수 등에 사용
  • UpperCamelCase: 클래스, 구조체, 열거형, Extension 등에 사용
클래스와 구조체 ✔️ UpperCamelCase를 사용하며, 클래스 이름은 접두사를 붙이지 않는다. class SomeClass { ... }
함수 ✔️ LowerCamelCase를 사용한다.
✔️ 함수 이름 앞에는 되도록 get을 붙이지 않도록 한다.
✔️ Action 함수의 네이밍은 "주어 + 동사 + 목적어" 형태를 사용한다.
    1) Tab은 .touchUpInside에 대응, Press는 .touchDown Events에 대응한다.
    2) will~은 특정 행위가 일어나기 "직전"을 의미
    3) did~는 특정 행위가 일어난 "직후"를 의미
    4) should~는 대개 Bool을 반환하는 함수에 사용
func backButtonDidTap() {
  ...
}
변수/상수 ✔️ LowerCamelCase를 사용한다. var someVar = 0
let someVar = 0
열거형 ✔️ enum의 이름은 UpperCamelCase를 사용한다.
✔️ enum의 각 case는 lowerCamelCase를 사용한다.
enum Result {
  case .success
  case .failure
}
프로토콜 ✔️ UpperCamelCase를 사용한다.
✔️ 프로토콜을 채택할 때는 콜론과 빈칸을 넣어 구분한다. (띄어쓰기 규칙 동일)
extension VC: SomeProtocol { ... }
약어 ✔️ 약어로 시작하는 경우에 소문자로 표기하고, 그 외에 경우에는 항상 대문자로 표기한다. let userID: Int
var urlString: String

 

3️⃣ 클로저


✔️ 파라미터와 리턴 타입이 없는 클로저는 () -> Void를 사용한다. let someClosure: (() -> Void)?
✔️ 클로저 정의 시 파라미터에는 괄호를 사용하지 않는다. { make in ... } (O)   { (make) in ... } (X)
✔️ 클로저 정의 시 가능한 경우 타입 정의를 생략한다. completion: { (make: Bool) -> Void in
  ..
}  // 이렇게 사용하지 말라는 뜻!
✔️ 클로저 호출 시 또다른 유일한 클로저를 마지막 파라미터로 받는 경우, 파라미터 이름을 생략한다. 후행 클로저 적용하라는 뜻!

 

4️⃣ 클래스와 구조체


✔️ 클래스와 구조체 내부에는 self를 명시적으로 사용한다.  
✔️ 구조체를 생성할 때는 Swift 구조체 생성자를 사용한다. let frame = CGRect(x: ..., y: ...)

 

5️⃣ 타입


Array ✔️ Array 타입은 Array<T>보다 [T]를 사용하도록 한다. let names: [String]
Dictionary ✔️ Dictionary 타입은 Dictionary<T: U>보다 [T: U]를 사용하도록 한다. let names: [Int: String]

 

6️⃣ 주석: 내 코드를 다른 협업하는 개발자들에게도 잘 설명할 수 있는 방법!


✔️ 주석 표시 이후에는 한 칸 띄어쓰기 후, 내용을 작성한다.  
✔️ // MARK: - 를 사용하여 연관된 코드와 연관되지 않은 코드를 구분하도록 한다.  // MARK: - View Life Cycle
✔️ /// 를 사용해서 퀵헬프 주석을 남긴다.  

 

7️⃣ 프로그래밍시 추가 권장사항: 권장사항이긴 하지만 어지간하면 지키는 것이 좋다!


변수 초기화 ✔️ 변수를 정의할 때 함께 초기화하도록 한다. let label = UILabel.then {
  $0.textColor = ... 
}
enum 활용 ✔️ 상수를 정의할 때, struct 대신 enum을 사용해서 모아둔다.
✔️ 재사용성과 유지보수 측면, 그리고 생성자가 제공되지 않는 자료형을 사용하기 위해서이다.
private enum Color {
  static let mainColor = ...
}
final 키워드 ✔️ 더 이상 상속이 발생하지 않는 클래스는 항상 final 키워드로 선언한다. final class ViewController { ... }
프로토콜 extension ✔️ 프로토콜을 적용했을 때 관련 메서드는 extension으로 모아둔다. extension VC: UITableViewDelegate { ... }
생명주기 ✔️ 생명 주기에는 최대한 코드를 간결하게 작성한다.