###### tags: `프로젝트 매니저`
# 프로젝트 매니저
# 그라운드 룰
## 규칙
1. 생활 체조 원칙 숙지.
- 한 메서드에 오직 한 단계의 들여쓰기만 합니다
- else 표현을 사용하지 않습니다
- 모든 원시 값과 문자열을 포장합니다
- 한 줄에 점을 하나만 사용합니다
- 이름을 줄여 쓰지 않습니다(축약 금지).
- 모든 엔티티를 작게 유지합니다
- 3개 이상의 스위프트 기본 데이터타입(Int, String, Double 등) 프로퍼티를 가진 타입을 구현하지 않습니다
- 일급 콜렉션을 사용합니다
- getter/setter를 구현하지 않습니다
2. AM 10시에 회의 및 스크럼 / PM 6시에 일과 끝내기
## Github Labels

## Commit Convention
- feat = 주로 사용자에게 새로운 기능이 추가되는 경우
- fix = 사용자가 사용하는 부분에서 bug가 수정되는 경우
- docs = 문서에 변경 사항이 있는 경우
- style = 세미콜론을 까먹어서 추가하는 것 같이 형식적인 부분을 다루는 경우 (코드의 변화가 생산적인 것이 아닌 경우)
- refactor = production code를 수정하는 경우 (변수의 네이밍을 수정하는 경우)
- test = 테스트 코드를 수정하거나, 추가하는 경우 (코드의 변화가 생산적인 것이 아닌 경우)
- chore = 별로 중요하지 않은 일을 수정하는 경우 (코드의 변화가 생산적인 것이 아닌 경우
음 그러면 뷰를 아는건 뷰모델이라고 생각되는데...
사실 해당 의문점이 "기한만료를 판단하는 것이 비지니스 로직일까?"에서 시작했습니다...ㅋㅋ
서로 논의한 결과 따로 분리해낼 만한 비지니스 로직으로 보기에는 애매하고 판단했습니다.
뷰모델에서 사용할 DTO 객체를 인스턴스화 할 때 기한이 만료되었는지를 판단하여 프로퍼티로 가지고 있게 해주기로 했습니다.
그러면 이전에 만료 판단을 해서 만료시간이 되어도 View에서는 만료가 되지 않는다는 단점이 있습니다... 하지만 어떻게 문제해결에 접근해야할지 감이 안와서 조금 더 고민해봐야 할 것 같아요.
## 1 / 10 스크럼
### 오늘의 컨디션
- 아얀: soso한 컨디션
- 제이푸시: 컨디션 좋은 것 같습니다
### 오늘의 특이사항
- 아얀: 임재범 - 비상은 언제나 들어도 명곡이다.
- 제이푸시: 점심먹고 오다 지각함 ㅋ - **벌금 오만원**
### 오늘의 할 일
- [x] 스크럼 작성하기
- [x] 기술 스택 정하기
- [x] STEP1 PR 작성
## 1 / 11 스크럼
### 오늘의 컨디션
- 아얀: 낮잠자서 컨디션이 너무 좋다.
- 제이푸시: 낮잠자려다 한숨도 못잠. 피곤해유
### 오늘의 특이사항
- 아얀: 별다른 특이사항이 없네요.
- 제이푸시: 4시~5시쯤 잠시 30분 정도 시간이 필요할지도
### 오늘의 할 일
- [x] 스크럼 작성하기
- [x] 프로젝트 설계
## 1 / 12 스크럼
### 오늘의 컨디션
- 아얀: 음~ 머리가 아픈듯 안아픈듯함
- 제이푸시: 밥 안먹은지 20시간
### 오늘의 특이사항
- 아얀: 저녁에 마라탕 먹기 싫다...
- 제이푸시: 점심 맛있게 먹을 예정
### 오늘의 할 일
- [x] 스크럼 작성하기
- [x] UML 작성
## 1 / 13 스크럼
### 오늘의 컨디션
- 아얀: 아주 안좋음
- 제이푸시: 아주 안좋음
### 오늘의 특이사항
- 아얀: 할게 너무 많다... 누가 나좀 복제해줘
- 제이푸시: 요즘 머리가 굳는 것 같음을 느낌
### 오늘의 할 일
- [x] 스크럼 작성하기
- [x] step2 UML 작성
## 1 / 16 스크럼
### 오늘의 컨디션
- 아얀: 월요일인데 왜 졸리죠...
- 제이푸시: 한숨떄렸는데 왜 졸리죠...
### 오늘의 특이사항
- 아얀: 스유 진짜 하기 싫어요.
- 제이푸시: 야식 먹게될지도
### 오늘의 할 일
- [x] 스크럼 작성하기
- [x] 뷰 논의
- [x] 깃 허브 이슈 등록
?? 17일 안함
## 1 / 18 스크럼
### 오늘의 컨디션
- 아얀:
- 제이푸시: 좋지 못함
### 오늘의 특이사항
- 아얀:
- 제이푸시: 12시 반에 밥
### 오늘의 할 일
- [x] 스크럼 작성하기
- [x] ViewModel
미리 죄송합니다... 지성🙇🏻♂️
저희가 이번 프로젝트에서 바인딩을 쉽게하기 위해서 Rx를 사용해보았는데 너무 쉽게 생각했던 것 같습니다.
코멘트 주신 내용을 바탕으로 리액티브 프로그래밍 그리고 Rx를 어떻게 사용해야 좋을지 등등 많은 부분을 찾아봤습니다.
하지만 모르는 내용이 너무 많아지고 개념이 계속 깊어져만 가서 하루이틀 공부해서는 되는 일이 아니라는 것을 깨달았습니다..
저희의 이런 쓰레기같은 코드를 읽어주셔서 감사합니다.. 코멘트 까지 남겨주셨는데... 리팩토링을 지금 당장 진행하기에는 무리라고 판단되어 DM드립니다...
혹시 Rx에 대해 공부하면서 참고해보면 좋을만한 것들을 줍줍할수 있을까요??
또 저희가 나름 집중했던 부분인 클린 아키텍쳐와 MVVM은 구조적으로는 괜찮았을까? 궁금합니다
# Project Manager
Swift Style Guide
=================
Jpush와 Ayaan의 Swift 코드를 이해하기 쉽고 명확하게 작성하기 위한 스타일 가이드입니다. 구성원들의 의사결정에 따라 수시로 변경될 수 있습니다.
본 문서에 나와있지 않은 규칙은 아래 문서를 따릅니다.
- [Swift API Design Guidelines](https://swift.org/documentation/api-design-guidelines)
- [SE-0005](https://github.com/apple/swift-evolution/blob/master/proposals/0005-objective-c-name-translation.md)
## 목차
- [코드 레이아웃](#코드-레이아웃)
- [들여쓰기 및 띄어쓰기](#들여쓰기-및-띄어쓰기)
- [줄바꿈](#줄바꿈)
- [최대 줄 길이](#최대-줄-길이)
- [빈 줄](#빈-줄)
- [임포트](#임포트)
- [네이밍](#네이밍)
- [타입](#타입)
- [함수](#함수)
- [변수](#변수)
- [상수](#상수)
- [열거형](#열거형)
- [약어](#약어)
- [클로저](#클로저)
- [클래스와 구조체](#클래스와-구조체)
- [타입](#타입)
- [주석](#주석)
- [프로그래밍 권장사항](#프로그래밍-권장사항)
## 코드 레이아웃
### 들여쓰기 및 띄어쓰기
- 들여쓰기에는 탭(tab)을 사용합니다.
- 콜론(`:`)을 쓸 때에는 콜론의 오른쪽에만 공백을 둡니다.
```swift
let names: [String : String]?
```
### 줄바꿈
- 함수 정의가 최대 길이를 초과하는 경우에는 아래와 같이 줄바꿈합니다.
```swift
func collectionView(
_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath
) -> UICollectionViewCell {
// doSomething()
}
func animationController(
forPresented presented: UIViewController,
presenting: UIViewController,
source: UIViewController
) -> UIViewControllerAnimatedTransitioning? {
// doSomething()
}
```
- 함수를 호출하는 코드가 최대 길이를 초과하는 경우에는 파라미터 이름을 기준으로 줄바꿈합니다.
```swift
let actionSheet = UIActionSheet(
title: "정말 계정을 삭제하실 건가요?",
delegate: self,
cancelButtonTitle: "취소",
destructiveButtonTitle: "삭제해주세요"
)
```
단, 파라미터에 클로저가 2개 이상 존재하는 경우에는 무조건 내려쓰기합니다.
```swift
UIView.animate(
withDuration: 0.25,
animations: {
// doSomething()
},
completion: { finished in
// doSomething()
}
)
```
- `if let` 구문이 길 경우나, 여러 조건일 경우, 줄바꿈하고 `if` 다음 구문과 라인을 맞춥니다.
```swift
if let user = self.veryLongFunctionNameWhichReturnsOptionalUser(),
let name = user.veryLongFunctionNameWhichReturnsOptionalName(),
user.gender == .female {
// ...
}
```
- `guard let` 구문이 길 경우나, 여러 조건일 경우, 줄바꿈하고 한 칸 들여씁니다. `else`는 `guard`와 같은 들여쓰기를 적용합니다.
```swift
guard let user = self.veryLongFunctionNameWhichReturnsOptionalUser(),
let name = user.veryLongFunctionNameWhichReturnsOptionalName(),
user.gender == .female
else {
// dosomething()
return
}
```
- 만약, `guard` 다음 `else` 구문 내부에 코드가 `return` 만 존재하는 경우, 개행을 적용하지 않습니다.
```swift
guard let user = self.veryLongFunctionNameWhichReturnsOptionalUser() else { return }
```
### 최대 줄 길이
- 한 줄은 최대 100자를 넘지 않아야 합니다.
Xcode의 **Preferences → Text Editing → Display**의 'Page guide at column' 옵션을 활성화하고 99자로 설정하면 편리합니다.
### 빈 줄
- 빈 줄에는 공백이 포함되지 않도록 합니다.
- 모든 파일은 빈 줄로 끝나도록 합니다.
```swift
// MARK: Layout
override func layoutSubviews() {
// doSomething()
}
// MARK: Actions
override func menuButtonDidTap() {
// doSomething()
}
```
### 임포트
모듈 임포트는 알파벳 순으로 정렬합니다. 내장 프레임워크를 먼저 임포트하고, 빈 줄로 구분하여 서드파티 프레임워크를 임포트합니다.
```swift
import UIKit
import SwiftyColor
import SwiftyImage
import Then
import URLNavigator
```
### 어트리뷰트
어트리뷰트 (@가 붙은) 것들은 위로 올립니다.
```swift
@objc
func someThing() {}
@objc
var a = 1
```
## 네이밍
### 타입
- 타입 이름에는 UpperCamelCase를 사용합니다.
- 타입 이름에는 접두사<sup>Prefix</sup>를 붙이지 않습니다.
### 함수
- 함수 이름에는 lowerCamelCase를 사용합니다.
- 함수 이름 앞에는 되도록이면 `get`, `set`을 붙이지 않습니다.
**좋은 예:**
```swift
func name(for user: User) -> String?
```
**나쁜 예:**
```swift
func getName(for user: User) -> String?
```
- Action 함수의 네이밍은 '동사 + 목적어' 형태를 사용합니다.
**좋은 예:**
```swift
func tapBackButton() {
// ...
}
```
**나쁜 예:**
```swift
func backButtonTapped() {
// ...
}
```
### 변수
- 변수 이름에는 lowerCamelCase를 사용합니다.
### 상수
- 상수 이름에는 lowerCamelCase를 사용합니다.
**좋은 예:**
```swift
let maximumNumberOfLines = 3
```
**나쁜 예:**
```swift
let MaximumNumberOfLines = 3
let MAX_LINES = 3
```
### 열거형
- enum의 각 case에는 lowerCamelCase를 사용합니다.
**좋은 예:**
```swift
enum Result {
case .success
case .failure
}
```
**나쁜 예:**
```swift
enum Result {
case .Success
case .Failure
}
```
### 약어
- 약어로 시작하는 경우 약어 전체를 소문자로 표기하고, 그 외의 경우에는 항상 대문자로 표기합니다.
**좋은 예:**
<pre>
let user<strong>ID</strong>: Int?
let <strong>html</strong>: String?
let website<strong>URL</strong>: URL?
let <strong>url</strong>String: String?
</pre>
**나쁜 예:**
<pre>
let user<strong>Id</strong>: Int?
let <strong>HTML</strong>: String?
let website<strong>Url</strong>: NSURL?
let <strong>URL</strong>String: String?
</pre>
## 클로저
- 파라미터와 리턴 타입이 없는 Closure 정의시에는 `() -> Void`를 사용합니다.
**좋은 예:**
```swift
let completionBlock: (() -> Void)?
```
**나쁜 예:**
```swift
let completionBlock: (() -> ())?
let completionBlock: ((Void) -> (Void))?
```
- Closure 정의시 파라미터에는 괄호를 사용하지 않습니다.
**좋은 예:**
```swift
{ operation, responseObject in
// doSomething()
}
```
**나쁜 예:**
```swift
{ (operation, responseObject) in
// doSomething()
}
```
- Closure 정의시 가능한 경우 타입 정의를 생략합니다.
**좋은 예:**
```swift
...,
completion: { finished in
// doSomething()
}
```
**나쁜 예:**
```swift
...,
completion: { (finished: Bool) -> Void in
// doSomething()
}
```
- Closure 호출시 또다른 유일한 Closure를 마지막 파라미터로 받는 경우, 파라미터 이름을 생략합니다.
**좋은 예:**
```swift
UIView.animate(withDuration: 0.5) {
// doSomething()
}
```
**나쁜 예:**
```swift
UIView.animate(withDuration: 0.5, animations: { () -> Void in
// doSomething()
})
```
## 클래스와 구조체
- 클래스와 구조체 내부에서 지칭하는 값이 모호한 경우와 반드시 명시적으로 나타낼 필요가 있을 경우 `self`를 사용합니다.
- 구조체를 생성할 때에는 Swift 구조체 생성자를 사용합니다.
**좋은 예:**
```swift
let frame = CGRect(x: 0, y: 0, width: 100, height: 100)
```
**나쁜 예:**
```swift
let frame = CGRectMake(0, 0, 100, 100)
```
## 타입
- `Array<T>`와 `Dictionary<T: U>` 보다는 `[T]`, `[T: U]`를 사용합니다.
**좋은 예:**
```swift
var messages: [String]?
var names: [Int: String]?
```
**나쁜 예:**
```swift
var messages: Array<String>?
var names: Dictionary<Int, String>?
```
## 주석
- `// MARK:`를 사용해서 연관된 코드를 구분짓습니다.
Objective-C에서 제공하는 `#pragma mark`와 같은 기능으로, 연관된 코드와 그렇지 않은 코드를 구분할 때 사용합니다.
```swift
// MARK: Init
override init(frame: CGRect) {
// doSomething()
}
deinit {
// doSomething()
}
// MARK: Layout
override func layoutSubviews() {
// doSomething()
}
// MARK: Actions
override func tapMenuButton() {
// doSomething()
}
```
## 프로그래밍 권장사항
- 상수를 정의할 때에는 `enum`를 만들어 비슷한 상수끼리 모아둡니다. 재사용성과 유지보수 측면에서 큰 향상을 가져옵니다. `struct` 대신 `enum`을 사용하는 이유는, 생성자가 제공되지 않는 자료형을 사용하기 위해서입니다.
```swift
final class ProfileViewController: UIViewController {
private enum Metric {
static let profileImageViewLeft = 10
static let profileImageViewRight = 10
static let nameLabelTopBottom = 8
static let bioLabelTop = 6
}
private enum Font {
static let nameLabel = UIFont.boldSystemFont(ofSize: 14)
static let bioLabel = UIFont.boldSystemFont(ofSize: 12)
}
private enum Color {
static let nameLabelText = 0x000000.color
static let bioLabelText = 0x333333.color ~ 70%
}
}
```
이렇게 선언된 상수들은 다음과 같이 사용될 수 있습니다.
```swift
self.profileImageView.frame.origin.x = Metric.profileImageViewLeft
self.nameLabel.font = Font.nameLabel
self.nameLabel.textColor = Color.nameLabelText
```
- 더이상 상속이 발생하지 않는 클래스는 항상 `final` 키워드로 선언합니다.
- 프로토콜을 적용할 때에는 extension을 만들어서 관련된 메서드를 모아둡니다.
**좋은 예**:
```swift
final class MyViewController: UIViewController {
// ...
}
// MARK: - UITableViewDataSource
extension MyViewController: UITableViewDataSource {
// ...
}
// MARK: - UITableViewDelegate
extension MyViewController: UITableViewDelegate {
// ...
}
```
**나쁜 예**:
```swift
final class MyViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
// ...
}
```
## 라이센스
본 문서의 출처와 저작권은 [전수열](https://github.com/devxoul)과 [StyleShare](https://stylesha.re)에게 있습니다.
사용자의 상황에 맞게 몇몇 항목의 첨삭 및 수정이 이루어졌습니다.