# Convention ## Swift Code Convention ### Code Layout #### 들여쓰기 및 띄어쓰기 - 들여쓰기에는 tab 혹은 4칸의 space를 사용한다 - 삼항연산자를 제외하고 콜론(`:`)을 사용할 때에는 오른쪽에만 한 칸의 공백을 준다. ```swift let userAgeDictionary: [String: Int] ``` #### 줄바꿈 - 한 줄에 길이가 99자를 초과하는 경우, 줄바꿈한다. - Xcode에서 `Preferences` - `Text Editing` - `Display`의 `Page guide at column` 옵션을 체크하고 99자로 설정하면 편리하게 관리할 수 있다. - 함수 정의가 최대 길이(99자)를 초과하는 경우, 아래와 같이 줄바꿈한다. ```swift func collectionView ( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath ) -> UICollectionViewCell { // 119자 // doSomething() } func animationController( forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController ) -> UIViewControllerAnimatedTransitioning? { // doSomething() } ``` - 함수를 호출하는 코드가 최대 길이(99자)를 초과하는 경우, 파라미터 이름을 기준으로 줄바꿈한다. ```swift let actionSheet = UIActionSheet( title: "정말 계정을 삭제하실 건가요?", delegate: self, cancelButtonTitle: "취소", destructiveButtonTitle: "삭제해주세요" ) ``` - 파라미터에 클로저가 2개 이상 존재하는 경우, 무조건 내려쓰기한다. ```swift UIView.animate( withDuration: 0.25, animations: { // doSomething() } completion: { finished in // doSomething() } ) ``` #### 빈 줄 - 빈 줄에는 공백이 포함되지 않도록 한다. - 모든 파일은 빈 줄로 끝나도록 한다. - `// MARK: -` 구문 위와 아래에는 공백이 필요하다. ```swift // MARK: - Layout override func layoutSubview() { // doSomething } // MARK: - Actions override func menuButtonDidTap() { // doSomething } ``` #### import - 모듈 `import`는 알파벳 순으로 정렬한다. 내장 프레임워크를 먼저 import하고, 빈 줄로 구분하여 서드파티 프레임워크를 import한다. ```swift import Combine import Foundation import SwiftUI import UIKit import SwiftyColor import SwiftyImage import Then import URLNavigator ``` ### 네이밍 #### 클래스 - 클래스 이름에는 `PascalCase(UpperCamelCase)`를 사용한다. - 클래스 이름에는 접두사(prefix)를 붙이지 않는다. #### 함수 - 함수 이름에는 `camelCase(lowerCamelCase)`를 사용한다. - 함수 이름 앞에는 `get`, `set`을 사용하지 않는다. - Action 함수의 네이밍은 '주어 + 동사 + 목적어'의 형태를 사용한다. - `request`는 에러가 발생하거나, 실패할 수 있는 비동기 작업에 사용한다. - `fetch`는 요청이 실패하지 않고 결과를 바로 반환할 때 사용합니다. - Event-Handling 함수의 경우 (조동사 + 동사원형)으로 시작한다. 주어는 유추 가능하다면 생략 가능하다. - `will`은 특정 행위가 일어나기 직전을 의미한다. - `did`는 특정 행위가 일어난 직후를 의미한다. #### 변수 및 상수 - 변수 및 상수의 이름에는 `camelCase(lowerCamelCase)`를 사용한다. - 변수를 정의할 때, 변수의 타입이 명확하다면 생략한다. ✅ Preferred ```swift var number = 0 ``` ❌ Not Preferred ```swift var number: Int = 0 ``` - Sequential Type에는 각 Sequential Type에 맞는 접미어를 사용한다. ✅ Preferred ```swift var categoryArray: [String] var userDictionary: [String: Int] var person: Person var isShowing: Bool ``` ❌ Not Preferred ```swift var category: [String] var show: Bool ``` #### 열거형 - enum의 각 case에는 `camelCase(lowerCamelCase)`를 사용한다. ```swift enum Color { case Red case Green case Blue } ``` #### 약어 - 약어로 시작하는 경우 소문자로 표기하고, 그 외의 경우에는 항상 대문자로 표기한다. - 약어는 잘 알려진(well-known) 약어만 사용한다. - `arr`: Array - `dict`: Dictionary - `btn`: Button ✅ Preferred ```swift let userID: Int? let html: String? let websiteURL: URL? let urlString: String? ``` ❌ Not Preferred ```swift let userId: Int? let HTML: String? let websiteUrl: URL? let URLString: String? ``` #### 클로저 - 파라미터와 리턴 타입이 없는 Closure 정의시에는 `() -> Void` 사용한다. ✅ Preferred ```swift let completionBlock: (() -> Void)? ``` ❌ Not Preferred ```swift let completionBlock: (() -> ())? let completionBlock: ((Void) -> (Void))? ``` - Closure 정의시 파라미터에는 괄호를 사용하지 않는다. ✅ Preferred ```swift { operation, responseObject in // doSomething() } ``` ❌ Not Preferred ```swift { (operation, responseObject) in // doSomething() } ``` - Closure 정의시 가능한 경우 타입 정의를 생략한다. ✅ Preferred ```swift ..., completion: { finished in // doSomething() } ``` ❌ Not Preferred ```swift ..., completion: { (finished: Bool) -> Void in // doSomething() } ``` - Closure 호출시 또다른 유일한 Closure를 마지막 파라미터로 받는 경우, 파라미터 이름을 생략한다. ✅ Preferred ```swift Button("save") { saveData() } ``` ❌ Not Preferred ```swift Button(action: { saveData() }, label: { Text("save") }) ``` #### 클래스와 구조체 - 클래스와 구조체 내부에서는 `self`를 명시적으로 사용한다. - 구조체를 생성할 때에는 Swift 구조체 생성자를 사용한다. ✅ Preferred ```swift let frame = CGRect(x: 0, y: 0, width: 100, height: 100) ``` ❌ Not Preferred ```swift let frame = CGRectMake(0, 0, 100, 100) ``` #### 타입 - `Array<T>`와 `Dictionary<T: U>`보다는 `[T]`, `[T: U]`를 사용한다. ✅ Preferred ```swift var messages: [String]? var names: [Int: String]? ``` ❌ Not Preferred ```swift var messages: Array<String>? var names: Dictionary<Int: String>? ``` #### 주석 - 주석 내용을 입력할 때에는 `//` 뒤에 공백을 한 칸 입력한다. - `MARK: -`를 사용해서 연관된 코드를 구분짓고, `MARK: -` 구문 위와 아래에 공백을 넣는다. ```swift // MARK: - Init override init(frame: CGRect) { // doSomething() } deinit { // doSomething() } // MARK: - Layout override func layoutSubviews() { // doSomething() } // MARK: - Actions override func menuButtonDidTap() { // doSomething() } ``` ##### 문서화 주석 - 변수, 상수, 클래스, 메서드, 함수, 열거형 등을 설명할 경우 마크업 문법에 따라 주석을 작성하면 다른 프로그래머가 '퀵헬프'를 통해 해당 내용을 확인할 수 있다. (`command` + `option` + `/`) - 한 줄 문서화 주석은 `///`를 사용한다. ```swift /// 사용자 프로필을 그려주는 뷰 class ProfileView: UIView { /// 사용자 닉네임을 그려주는 라벨 var nameLabel: UILabel! } ``` - 여러 줄 문서화 주석은 아래와 같이 사용한다. ```swift import SwiftUI extension Image: ViewModifier { /** 이미지를 배경에 맞게 만들어 주는 Modifier 이미지에 사용하면 이미지를 SafeArea를 무시하고 배경에 꽉 차게 만들어 줍니다 **Example** ``` Image("imageName").backgroundImage() ``` */ func backgroundStyle() -> some View { self .resizable() .ignoresSafeArea() } } ![](https://i.imgur.com/9ZnIYov.png) ``` - 여러 줄 문서화 주석 사용시 아래와 같이 퀵헬프를 사용할 수 있다. #### 프로그래밍 권장사항 - 가능하다면 변수를 정의할 때 초기화하도록 한다. ✅ Preferred ```swift var number = 0 ``` ❌ Not Preferred ```swift var number: Int number = 0 ``` - 상수를 정의할 때에는 `enum`을 만들어 비슷한 상수끼리 모아둔다 - 재사용성 및 유지보수 측면에서 큰 향상 - `struct` 대신 `enum`을 사용하는 이유는, 생성자가 제공되지 않는 자료형을 사용하기 위함 ```swift final class ProfileViewController: UIViewController { private enum Font { static let nameLabel = UIFont.boldSystemFont(ofSize: 14) static let bioLabel = UIFont.boldSystemFont(ofSize: 12) } } ``` - 위와 같이 선언된 상수들은 아래와 같이 사용할 수 있다. ```swift self.nameLabel.font = Font.nameLabel ``` - 더이상 상속이 발생하지 않는 클래스는 항상 `final` 키워드로 선언한다. - 프로토콜을 적용할 때에는 extension을 만들어서 관련된 메서드를 모아둔다. ✅ Preferred ```swift final class MyViewController: UIViewController { // ... } // MARK: - UITableViewDataSource extension MyViewController: UITableViewDataSource { // ... } // MARK: - UITableViewDelegate extension MyViewController: UITableViewDelegate { // ... } ``` ❌ Not Preferred ```swift final class MyViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { // ... } ``` ### Git Convention ## Git Convention - feat: 새로운 기능 추가 - fix: 버그 수정 - docs: 문서 수정 - style: 포맷팅, 세미콜론 빠졌을 때, 코드 변경이 없는 수정 - refactor: 리팩토링 - test: 테스트 코드 추가, 테스트 코드 리팩토링 - chore: 빌드 관련 수정, 패키지 매니저 수정 - design: 사용자 ui 디자인 변경 - !BREAKING CHANGE: 커다란 API 변경의 경우 - !HOTFIX: 급하게 치명적인 버그를 고쳐야 하는 경우 - comment: 필요한 주석 추가 및 변경 - rename: 파일 혹은 폴더명을 수정하거나 옮기는 작업만인 경우 - remove: 파일을 삭제하는 작업만 수행한 경우 ## Issue Convention - [태그] 제목 - 이슈있는 사항을 한 줄 요약해서 제목으로 작성 - 내용에는 필요하다고 느끼는 내용, 문제가 생긴 것 같은 내용 남기기 - 혼낼 필요 없이 궁금한 것을 물어봐도 괜찮음 - ex) 메소드 활용 말고 계산 프로퍼티를 사용하는 건 어때요? - ex) 같은 프로퍼티 사용 말고 enum 문을 사용하는 건 어떤가요? ## Pull Request Convention - [태그] 제목 - 커밋 수 합쳐서 250줄 이내 - Issue assign하기 - 제목: 작성한 기능을 한 줄 요약해서 올리기 - 상세페이지: 번호를 매겨 추가된 기능별 설명하기 - 작업한 목록 - 해결한 이슈 - 고민한 부분 및 질문할 부분 - PR 요청하면 기다리지 말고 브랜치 추가해서 계속하기 -> checkout 말고 switch 사용 ### Approve convention - 최소 2 Approve를 받아야 머지 - 서로가 승인하지 않은 상태에서 함부로 머지하지 않기 - 멋진 코드 칭찬하기 - 궁금한 건 편하게 리뷰로도 물어보기 - 의견을 받아들일지는 코드를 작성한 사람이 결정하기 - 리뷰 의견 낼 때, 근거도 함께 쓰기 - 아침마다 머지, 밤에 리뷰 등등 추가 규칙 생길 수 있음 ## Optional - Xcode 프로젝트를 만들기 전에 미리 어떤 일들을 해야하는지 적어놓으면 좋음 - GitHub 레포지토리 내의 project에 있는 칸반 적극 활용해보기 (Tip: 'In Progress'를 최대한 잘게 쪼개기)