Skip to content

야곰 아카데미 iOS 일기장 프로젝트 저장소입니다

Notifications You must be signed in to change notification settings

kokkilE/ios-diary

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

50 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

📔 일기장

나의 일기를 등록, 수정, 삭제할 수 있는 앱

프로젝트 기간: 2023.04.24-2023.05.12

팀원

kokkilE 혜모리
Github Profile Github Profile

목차

  1. 타임라인
  2. 프로젝트 구조
  3. 실행 화면
  4. 트러블 슈팅
  5. 참고 링크
  6. 팀 회고

타임라인

날짜 내용
2023.04.24 - JSON Decode 모델인 Contents 타입 구현
- 일기 리스트 화면 구현
- custom TableviewCell 구현
- SwiftLint 적용
2023.04.25 - 날짜 지역화 구현
- 상세페이지 화면 구현
- KeyBoard에 따른 뷰 위치 변경 구현
2023.04.26 - DecodeManager 구현
- AlertManager 구현
- keyboardLayoutGuide 적용
- 프로젝트 Minimum DeployMents 변경 (14.0 → 15.0)
2023.04.28 - CoreDataManager - Create, Read 기능 구현
- Coredata의 Entity 구현
2023.05.01 - Core Data Update, Delete 구현을 위한 추가 학습
2023.05.02 - CoreDataManager - Update, Delete 기능 구현
- VC의 데이터 CRUD 기능 구현
2023.05.03 - 에러 Alert 기능 구현
- 데이터가 편집될 때 전체 데이터가 아닌 편집된 데이터만 reload 하도록 기능 수정
2023.05.04 - 에러 처리 위치 수정 (model → VC)
2023.05.05 - 프로젝트 회고 및 휴식
2023.05.08 - NetworkManager, EndPoint 구현
2023.05.09 - Core Location으로 사용자 위치정보 저장 구현
2023.05.10 - 화면에 날씨 아이콘을 보여주는 기능 구현
2023.05.11 - 코드 전체 리팩토링 (타입 분리, 컨벤션 정리)
2023.05.12 - 프로젝트 회고

프로젝트 구조

Class Diagram

클래스 다이어그램 보기 (클릭)

File Tree

파일 트리 보기 (클릭)
├── .swiftlint.yml
├── Common
│   ├── CoreData
│   │   ├── Diary.xcdatamodeld
│   │   │   ├── Diary v2.xcdatamodel
│   │   │   └── Diary.xcdatamodel
│   │   ├── ContentsEntity+CoreDataClass.swift
│   │   ├── ContentsEntity+CoreDataProperties.swift
│   │   └── CoreDataManager.swift
│   ├── Extension
│   │   ├── Date+.swift
│   │   └── NotificationName+.swift
│   ├── Network
│   │   ├── EndPoint.swift
│   │   └── NetworkManager.swift
│   ├── Error
│   │   ├── DiaryError.swift
│   │   └── NetworkError.swift
│   ├── Utility
│   │   ├── AlertManager.swift
│   │   ├── DecodeManager.swift
│   │   └── LocationManager.swift
│   └── Model
│       ├── ContentsDTO.swift
│       ├── WeatherDTO.swift
│       └── Coordinate.swift
├── Presentation
│   ├── DiaryList
│   │   ├── Protocol
│   │   │   ├── DiaryDetailViewControllerDelegate.swift
│   │   │   └── IdentifierType.swift
│   │   ├── ContentsTableViewCell.swift
│   │   └── DiaryListViewController.swift
│   └── DiaryDetail
│       ├── DiaryDetailViewController.swift
│       └── WeatherNetworkManager.swift
├── Resources
│   └── Info.plist
└── Application
    ├── AppDelegate.swift
    └── SceneDelegate.swift

실행 화면

초기 화면
일기 목록 화면
데이터 로드 실패 시
알림 표시
일기 목록 화면
스와이프로 공유 및 삭제
일기 상세 화면
새로운 데이터 저장
일기 상세 화면
데이터 편집 후 저장
일기 상세 화면
더보기 → 공유 및 삭제

트러블 슈팅

1️⃣ 키보드가 편집중인 텍스트를 가리지 않도록 처리

키보드가 나타날 때 편집중인 텍스트를 가리지 않도록 하기 위해 세 가지 방법을 찾아 고려하였습니다.

  • 키보드가 나타날 때 textView의 오토레이아웃을 조정하여 구현
  • 키보드가 나타날 때 textView.contentInset을 조정하여 구현
  • keyboardLayoutGuide의 제약으로 구현

위의 첫 번째, 두 번째 방법은 Notification을 활용하여 키보드 이벤트를 수신하는 방법으로 구현이 가능했습니다. 다만 첫 번째 방법인 오토레이아웃을 변경하는 방법으로 구현할 경우, 다음과 같이 딜레이 시간동안 키보드가 내려감에도 채워지지 않는 레이아웃이 어색하게 느껴졌습니다.



두 번째 방법과 세 번째 방법은 동작상의 문제는 없다고 생각했습니다. 세 번째 방법은 keyboardLayoutGuide의 제약조건으로 비교적 간단히 구현이 가능해 세 번째 방법을 채택하였습니다. 다만 keyboardLayoutGuide을 사용하기 위해 Minimum Deployments를 iOS 15.0으로 올려야 했습니다.

📄 코드 참조

키보드가 나타날 때 textView의 오토레이아웃을 조정하여 구현

@objc func keyboardWillShow(notification: Notification) {
    guard let userInfo = notification.userInfo,
          let keyboardFrameValue = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else {
        return
    }
    let keyboardHeight = keyboardFrameValue.cgRectValue.height
    
    textViewBottomAnchor.isActive = false
    
    // 키보드가 나타날 경우 textView의 bottomAnchor 조정
    textViewBottomAnchor = textView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -keyboardHeight)

    textViewBottomAnchor.isActive = true
    }

키보드가 나타날 때 textView.contentInset을 조정하여 구현

@objc private func keyboardWillShow(notification: Notification) {
    guard let userInfo = notification.userInfo,
          let keyboardFrameValue = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else {
        return
    }
    let keyboardHeight = keyboardFrameValue.cgRectValue.height
        
    textView.contentInset.bottom = keyboardHeight
    textView.verticalScrollIndicatorInsets.bottom = keyboardHeight
    }

✅ keyboardLayoutGuide의 제약으로 구현

private func configureLayout() {
    //...    
    view.keyboardLayoutGuide.followsUndockedKeyboard = true
        
    NSLayoutConstraint.activate([
        view.keyboardLayoutGuide.topAnchor.constraint(equalTo: textView.bottomAnchor),
        
        //...           
            
        textView.bottomAnchor.constraint(equalTo: view.keyboardLayoutGuide.topAnchor)
        ])
    }

2️⃣ 일기장 내용이 전부 보이도록 수정

🔍 문제점

가장 긴 글의 수정 페이지로 이동했을 때 위로 스크롤하지 않으면 제목 부분이 잘려보이는 현상이 확인됐습니다.

⚒️ 해결방안

contentSize가 텍스트 내용보다 작아 발생한 현상으로, 하이어라키를 설정할 때 뷰의 offset의 크기를 초기화시켜주니 정상적으로 표시되었습니다.

private func configureLayout() {
    view.addSubview(textView)
    textView.contentOffset = .zero // 추가
    
    // 이하 layout 설정
}

3️⃣ 예외 처리 정리

🔍 문제점

기능 구현 중 스텝 요구사항 외에 어색하다고 생각되는 로직이 여러가지 있었습니다. 이 내용들은 예외적으로 처리가 필요하다고 생각하여 관련 정책을 자체적으로 협의하여 결정했습니다.

⚒️ 결과

  1. 새 글을 create할 때 아무 내용도 입력하지 않은 경우 → 저장되지 않습니다.
  2. 새 글을 create하고 + 자동 저장된 후 글 내용을 전부 삭제한 경우 → 비어있는 내용이 저장됩니다.
  3. 글을 한 줄만 등록한 경우 (title만) → title은 입력한 내용이 저장되고 body는 빈 문자열로 저장됩니다.
  4. 이미 등록된 글의 내용을 전부 지운 경우 update 여부 → 비어있는 내용이 저장됩니다.
  5. 새 글을 create할 때 더보기 버튼(right bar button) 노출 여부 → 입력 후 내용을 바로 공유하거나, 자동저장된 내용을 바로 삭제시킬 수 있다고 생각하기 때문에 새 글을 저장할 때도 버튼이 노출됩니다.

4️⃣ 목록 화면과 상세 화면간 데이터 동기화

상세 화면에서 편집된 데이터를 목록 화면에도 반영하기 위한 로직을 구현하였습니다. 개선 전에는 목록 화면으로 돌아올 때 전체 데이터를 다시 읽고, 목록을 reload하는 방법으로 구현하였습니다. 개선 후에는 편집된 데이터만 갱신되도록 구현하였습니다.

🔍 문제점

상세 화면에서 데이터가 편집된 후 다시 목록 화면으로 돌아올 때, 편집된 데이터를 목록 화면에 반영하기 위해 DiaryDetailViewController에서는 데이터가 편집될 때 CoreData 저장소의 데이터를 변경해주었습니다. 이후 목록 화면이 다시 나타날 때 아래와 같은 코드로 데이터를 갱신하였습니다.

DiaryListViewController

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    
    fetchContents()
    tableView.reloadData()
}

위 코드는 동작에 문제는 없었지만, 하나의 데이터만 편집되는 상황에서 전체 데이터를 다시 읽어오고, reload하기 때문에 오버헤드가 발생합니다. 데이터가 많아질수록 오버헤드는 커지기 때문에 개선할 필요가 있다고 생각하였습니다.

⚒️ 해결방안

상세 화면에서 편집된 데이터만 목록 화면에서 갱신할 수 있도록 delegate 패턴을 적용하여 다음과 같이 코드를 개선하였습니다.

DiaryListViewController

// MARK: - DiaryDetailViewController Delegate
extension DiaryListViewController: DiaryDetailViewControllerDelegate {
    func createCell(contents: Contents) {
        // ...
        contentsList?.append(contents)
        tableView.insertRows(at: [selectedCellIndex], with: .automatic)
    }
    
    func updateCell(contents: Contents) {
        // ...
        contentsList?[selectedCellIndex.row] = contents
        tableView.reloadRows(at: [selectedCellIndex], with: .automatic)
    }
    
    func deleteCell() {
        // ...
        contentsList?.remove(at: selectedCellIndex.row)
        tableView.deleteRows(at: [selectedCellIndex], with: .fade)
    }
}

DiaryDetailViewController

	// ...
	weak var delegate: DiaryDetailViewControllerDelegate?
	// ...
	private func updateContents() {
	    // ...
	    delegate?.updateCell(contents: contents)
	    // ...
	}
	    
	private func createContents() {
	    // ...
	    delegate?.createCell(contents: contents)
	    // ...
	}
	        
	private func deleteContents() {
	    // ...
	    delegate?.deleteCell()
	    // ...
	}

5️⃣ 날씨 표시 기능 구현 전에 저장된 데이터를 보존하기 위한 처리

🔍 문제점

날씨 정보를 표시하는 기능을 추가하면서 Core Data의 Entity 모델이 변경되었습니다.

⚒️ 해결방안

그에 따라 기존에 Core Data에 저장되어있던 모델과 Migration하였습니다.

이 과정에서 기존에 날씨 정보가 없던 데이터는 날씨 정보가 없는 채로 받아오기 위해, ContentsDTO에 다음과 같이 weather를 옵셔널로 구현하였습니다.

/*  ContentsDTO은 다음의 용도로 사용됩니다.
    1. JSON 데이터 Decode를 위한 모델
    2. VC에서 사용되는 모델
    3. VC에서 CoreData와 데이터를 주고받는 모델 */

struct ContentsDTO: Codable {
    var title: String
    var body: String
    let date: Double
    let identifier: UUID?
    var weather: Weather?
    ...
}

날짜 정보가 없으면 없는 대로, 있으면 있는 대로 앱에 표시됩니다.

참고 링크

블로그

공식 문서

API

팀 회고

팀 회고 보기 (클릭)

우리 팀이 잘한 점

  • 시간 약속을 하루도 빠짐없이 잘 지켰습니다.
  • 서로 원하는 점을 솔직하게 이야기하고 잘 협의하여 원하는 결과를 도출했습니다.
  • 적용할 기술을 충분히 이해하면서 프로젝트를 진행하였습니다.

서로 칭찬할 점

  • 코낄이 -> 혜모리
    • 팀원의 의견을 잘 들어주고, 본인의 의견도 적극적으로 표현하였습니다. 항상 의견에 근거가 있었기 때문에 협의가 원만했습니다.
    • 대화를 잘 이끌어줘서 즐겁게 협업할 수 있었습니다.
  • 혜모리 -> 코낄이
    • 코낄이는 정말 꼼꼼하셔서 오류가 발생할 때도 금방 찾을 수 있었고, 제 얘기를 충분히 잘 들어주시고 적절한 대안을 주셔서 정말 많이 배울 수 있는 기회였습니다.
    • 어려운 개념을 잘 이해하시고 잘 설명해 주셔서 듣는 저도 이해하기가 무척 쉬웠습니다.

About

야곰 아카데미 iOS 일기장 프로젝트 저장소입니다

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Swift 100.0%