빠르게 킥보드를 찾아타는 앱!
🎯Leader | 💻Development | 💻Development | 💻Development | 💻Development |
---|---|---|---|---|
황석현 | 장상경 | 전지혜 | 이명지 | 반성준 |
로그인 화면 | 킥보드 상세 페이지 | 킥보드 등록 페이지 | 킥보드 찾기 페이지 | 마이 페이지 |
회원가입 화면 | 메인 탭 바 | 코어 데이터 관리 |
- 필수 구현 사항을 모두 완료하기
- 각자 최소 1개 이상의 기능, UI 구현
- 협업을 위한 활발한 소통
- 마일스톤 준수하기
- 코드리뷰를 통한 협업 능력치 상승
- 협업을 통한 팀워크 향상
- 개인 역량 강화
- 데이터 구조 학습
- API 이해도 증가
- 아키텍처 패턴:
MVC
패턴 - 데이터 저장 방식:
UserDefaults
,CoreData
- 데이터 전달 방식:
Delegate
패턴
- 시작: 12.13(금) 17:00
- 종료: 12.20(금) 12:00 까지
- Sprint 1 : ~ 12.18(수요일 오전)
- Sprint 2 : ~ 12.19(목요일 오후)
- 오전 11시 튜터님과 스크럼: 마일스톤 진척도 확인, 피드백
- 오후 5시 팀원들과 스크럼: 금일 작업 내용, 진행도, 이슈 등 공유
커밋 유형 | 의미 |
---|---|
feat: | 새로운 기능 추가 |
add: | Feat 의외의 부수적인 코드 추가 |
file: | 새로운 파일 및 폴더 추가 |
fix: | 버그, 오류 해결 |
del: | 파일을 삭제하는 작업만 수행한 경우 |
mod: | UI 수정 |
comment: | 필요한 주석 추가 및 변경 |
rename: | 파일 또는 폴더 명을 수정 |
move: | 프로젝트 내 파일이나 코드의 이동 |
docs: | README나 WIKI 등의 문서 개정 |
proj: | 프로젝트 관련 설정 변경 |
-
main 브랜치에 프로젝트 기본 세팅
- README 작성
- .gitignore 파일 작성
- 프로젝트 파일 생성(Xcode)
- 코드베이스 기본 세팅(스토리보드 삭제, info 설정 등)
- 프로젝트 Asset 추가(이미지, 컬러세트 등)
- 프로젝트 디렉토리 분리(각 역할 별로 분리)
- 코어 데이터 추가
-
dev 브랜치 생성(main 브랜치를 기준으로)
- 메인 브랜치에 만들어진 내용을 복제
- 작업 브랜치(Default)를 dev 브랜치로 설정
-
팀원별 원격 브랜치 분리
- dev 브랜치를 기준으로 원격 브랜치를 분리
- 세부적인 작업 내용은 로컬 브랜치로 분리
-
PR-Merge 전략
- PR을 작성할 때는 신규 내용, 변경 내용, 문제점 등을 상세히 작성
- 팀원 모두는 PR에 대해 코멘트를 작성
- 2명의
approve
필요
-
모든 작업 완료 후 test 브랜치를 생성
- dev 브랜치의 내용을 test 브랜치로 복제
- test 브랜치에서 프로젝트 버그 확인 및 수정
- 필요에 따라 hotFix 브랜치를 생성하여 운영
-
완성된 프로젝트를 main에 전달
test
브랜치의 작업 내용을main
브랜치에 스쿼시 머지- 불필요한 브랜치 삭제
- README 수정
MyPageViewController
에서 Core Data Fetch 이후 데이터가 KickboardSectionView
와 HistorySectionView
에 표시되지 않는 문제 발생.
- Core Data Fetch 후 UI 갱신 로직이 누락됨.
- Fetch한 데이터를 뷰로 전달하지 않음.
Core Data에서 데이터를 Fetch한 후 configureSections()
메서드를 호출하여 데이터를 UI에 전달하고 갱신하도록 수정하였습니다.
private func loadKickboards() {
do {
kickboardData = try CoreDataManager.shared.context.fetch(Kickboard.fetchRequest())
configureSections() // Fetch 후 UI 갱신
} catch {
print("킥보드 데이터를 불러오는 데 실패했습니다: \(error)")
}
}
ProfileView
에서 사용자 정보가 항상 초기값("User1", "[email protected]")으로 표시되는 문제 발생.
UserDefaultsManager
의getUser()
메서드 호출 누락.- 사용자 정보 저장 로직이 제대로 작동하지 않음.
UserDefaultsManager
를 통해 사용자 정보를 가져오고 닉네임 변경 시 데이터를 저장하도록 수정하였습니다.
HistorySectionView
에 전달한 더미 데이터가 UI에 표시되지 않는 문제 발생.
HistorySectionView
의configure(with:)
메서드가 호출되지 않음.
configure(with:)
메서드를 호출하여 더미 데이터를 전달하고 UI를 업데이트하도록 수정하였습니다.
"내가 등록한 킥보드 >" 버튼 클릭 시 상세 화면으로 이동하지 않는 문제 발생.
KickboardSectionView
의configure
메서드에서onTap
클로저 설정이 누락됨.
클로저를 설정하여 버튼 클릭 시 상세 화면으로 이동하도록 구현하였습니다.
"내가 등록한 킥보드"에서 올바른 데이터가 표시되지 않는 문제 발생.
- Core Data Fetch Request에서
NSPredicate
설정이 누락됨.
isOccupied
필터를 추가하여 Fetch된 데이터가 올바르게 필터링되도록 수정하였습니다.
ModalViewDelegate
프로토콜의 필수 메서드가 구현되지 않아 컴파일 오류가 발생.
AddKickboardViewController
와MyKickboardDetailViewController
에서 필수 메서드가 누락됨.
필수 메서드를 구현하여 컴파일 오류를 해결하였습니다.
로그아웃 이후 로그인 화면으로 전환되지 않는 문제 발생.
- 로그아웃 후 새로운 Root View Controller 설정 누락.
LoginViewController
를 Root View Controller로 설정하여 로그아웃 이후 화면이 전환되도록 구현하였습니다.
HistorySectionView
와 KickboardSectionView
의 UI 간격 및 레이아웃이 어긋나는 문제 발생.
- AutoLayout 제약 조건에서 상하 간격 및 좌우 마진이 통일되지 않음.
AutoLayout 제약 조건을 조정하여 간격 및 마진을 통일하였습니다.
systemImage
로 구성한 UIButton
에 backgroundColor
를 지정했을 때 자동으로 Baseline offset
이 생겨 원치 않는 흰 테두리가 생기는 문제
Baseline offset
이 UIButton
의 default임
button.imageView?.contentMode = .center
버튼 내 이미지 뷰가 button
의 bounds
의 중앙에 있겠다는 명령으로, baseline offset
을 무시하게 됨.
주소가 길어질 경우 label.text
가 컨테이너 밖으로 벗어나는 문제
label.font
가 정적인 값을 가짐label
의leading
과trailing
에 대한 제약조건이 걸려있지 않음
label.adjustsFontSizeToFitWidth = true
addressLabel.snp.makeConstraints {
$0.leading.equalToSuperview().offset(8)
$0.trailing.equalToSuperview().offset(-8)
}
SearchLocationBarView
에서 searchResultsTableView
의 cell
을 터치해도 터치 이벤트 메서드가 호출되지 않는 문제.
- 터치 이벤트가
searchResultsTableView
의cell
로 전달되는 것이 아니라 다른 뷰로 전달되는 것.
- hitTest를 오버라이드 해 터치가
searchResultsTableView
의cell
영역 내에 발생하면 해당 뷰로 이벤트 전달하도록 수정.
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if !searchResultsTableView.isHidden {
let convertedPoint = convert(point, to: searchResultsTableView)
if searchResultsTableView.bounds.contains(convertedPoint) {
if let hitView = searchResultsTableView.hitTest(convertedPoint, with: event) {
print("TableView cell hit at point: \(point)")
return hitView
}
}
}
return super.hitTest(point, with: event)
}
- 상세페이지에서 수정을 위해 모달뷰를 열면 해당 킥보드에 대한 정보(닉네임, 타입 등)를 가져오는데, 데이터를 수정해도 '등록하기' 버튼이 비활성화 되는 버그
- 텍스트 필드(닉네임)를 지우는 행동을 하면 버튼이 활성화 되는 현상 확인
- 타입 버튼을 선택해도 버튼은 비활성화 상태
원인을 이해하려면 우선 '등록하기' 버튼의 활성화 조건을 이해해야 하는데, '등록하기' 버튼은 '닉네임' 텍스트 필드가 채워져 있고, '킥보드 타입'이 선택되어 있을 때 활성화 된다.
/// 등록하기 버튼을 활성화 하는 메소드
func activateButton() {
guard self.typeSelected, self.haveNickNameText else {
self.addButton.activateButton(false)
return
}
self.addButton.activateButton(true)
}
이 때, typeSelected
와 haveNickNameText
는 델리게이트가 가진 프로토콜이고, 닉네임 텍스트 필드의 텍스트 수가 0보다 크고 킥보드 타입이 선택 되어 있을 때 true
로 설정된다.
왜 활성화가 안될까 싶어서 브레이크 포인트를 걸고 출력을 해봤는데...
조건이 충족되지 않는 모습을 볼 수 있었다.
분명 메소드를 통해 Bool
값을 바꿔주도록 했는데 제대로 작동이 되질 않는다...
메소드를 수정해보고 값도 수정해 보고 여러 시도를 했지만 제대로 동작하지 않아서 결국 직접 값을 수정하도록 메소드를 수정하였다.
/// 내 킥보드 관리에서 cell을 탭했을 때 킥보드 유형과 별명을 전달 받는 메소드
func editKickboardData(_ type: Bool, _ text: String, _ id: NSManagedObjectID) {
self.kickboardID = id
self._sendNickName = text
self._kickboardType = type
self._haveNickNameText = true
self._typeSelected = true
self.typeButton.updateData(type)
self.textField.updateData(text)
self.addButton.modalMode = .edit
self.addButton.activateButton(true)
}
로그인 버튼은 UIButton
인데 체크박스는 UIImageView
이다.
따라서, 체크박스를 터치해도 아무 동작이 이루어지지 않는다.
하지만 사용자에게는 혼동을 줄 수 있는 부분이다.
그럼 왜 처음부터 이것을 UIButton
으로 만들지 않았는가? 라는 의문이 든다.
UIButton
의 setImage()
, setTitle()
를 사용해서 만들면 간단했겠지만
와이어프레임에 나와있는 UI에서는 버튼과 텍스트의 위치가 다르다.
따라서, 오토레이아웃을 통해서 위치를 배정하기 위해
UIButton
과 UIImageView
를 사용했었다.
UIView
를 생성하고 UILabel
, UIImageView
를 서브 뷰로 추가하고
UITapGestureRecognizer
를 통해서 사용자의 화면 터치를 감지한다.
이를 addTapGesture()
에 액션 함수로 추가하여서
해당 영역이 터치가 되면 버튼처럼 함수를 동작한다.
let autoLoginOptionTapGesture = UITapGestureRecognizer(target: self, action: #selector(autoLoginOptionTapped))
let rememberIDOptionTapGesture = UITapGestureRecognizer(target: self, action: #selector(rememberIDOptionTapped))
autoLoginOption.addGestureRecognizer(autoLoginOptionTapGesture)
rememberIDOption.addGestureRecognizer(rememberIDOptionTapGesture)