[TIL / 25.03.27] HarryPotterBooks 과제 2~4Lv 구현 및 회고

2025. 3. 27. 23:27·iOS/Swift

1. 구현한 것

2. 보완했던 사항들

기본 구현보단 부분 부분 고쳤던 사항들을 기록한다.

 

먼저, 에러 핸들링에 대해서 좀 더 alert창에 친근하게 접근할 수 있도록 구조를 바꿨다.

기존 loadBooks는 Result<[Book], Error> 타입을 반환했는데, 해당 방식으로 콜백을 하기보단 해당 throw 메서드로 변환하면서 [Book]으로 명시적으로 반환하도록 바꿨음.

class DataService {
    func loadBooks() throws -> [Book] {
        guard let path = Bundle.main.path(forResource: "data", ofType: "json") else {
            throw DataError.fileNotFound
        }
        
        do {
            let data = try Data(contentsOf: URL(fileURLWithPath: path))
            let bookResponse = try JSONDecoder().decode(BookResponse.self, from: data)
            let books = bookResponse.data.map { $0.attributes }
            return books
        } catch {
            print("🚨 JSON 파싱 에러 : \(error)")
            throw DataError.parsingFailed
        }
    }
}

해당 메서드를 실제로 써야할 파트에서

private func loadBook(index: Int) {
        do {
            try mainViewModel.loadBooks()
            guard let book = mainViewModel.book(index: index) else { return }
            self.myBook = book
        } catch let error as DataError {
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { // ViewController가 display 준비되기 전 메시지를 띄우지 않도록
                self.showMessage(title: nil, message: error.errorMessage)
            }
        } catch {
            DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
                self.showMessage(title: nil, message: "예기치 못한 오류가 발생했습니다.")
            }
        }
    }

이렇게 구현했다. viewModel 상에서는 해당 DataService의 메서드를 써서 books를 받게 되며 이를 한번 받고 그 중 책 한 권에 대해 리턴받도록 했는데, 이때 catch를 이용해 에러 핸들링을 할 수 있도록 했다. 

 

 

private func createLabelStackView(isAuthor: Bool, title: String, value: String) -> UIStackView {
        let stackView = UIStackView().then {
            $0.axis = .horizontal
            $0.spacing = 8
        }
        
        let titleLabel = UILabel().then {
            $0.font = UIFont.systemFont(ofSize: isAuthor ? 16 : 14, weight: .bold)
            $0.textColor = .black
            $0.text = title
        }
        
        let valueLabel = UILabel().then {
            $0.font = UIFont.systemFont(ofSize: isAuthor ? 18 : 14)
            $0.textColor = isAuthor ? .darkGray : .gray
            $0.text = value
        }
        
        [titleLabel, valueLabel].forEach { stackView.addArrangedSubview($0) }
        
        return stackView
    }

 

그리고 일부 타이틀과 그에 대응되는 값에 대한 라벨을 세 단 띄워야했을 때 전부 상단에 private let으로 선언하기엔 중복 프로퍼티도 많고 반복되는 코드가 너무 많아져서 해당 함수를 통해 커스텀할 수 있도록 했다. 다만, isAuthor를 통해 지금은 두 스타일을 구분하고 있는데 후에 이를 쓰는 케이스가 더 늘어나게 된다면 isAuthor를 빼고 좀 더 명시적으로 프로퍼티를 주입해줄 수 있어야 할 것 같다.

 

class ChapterView: UIView {
    
    private let titleLabel = UILabel().then {
        $0.font = UIFont.systemFont(ofSize: 18, weight: .bold)
        $0.textColor = .black
        $0.text = "Chapters"
    }
    
    private let contentStackView = UIStackView().then {
        $0.spacing = 8
        $0.axis = .vertical
        $0.alignment = .leading
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        
        setupUI()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented.")
    }
    
    private func setupUI() {
        setupHierarchy()
        setupConstraints()
    }
    
    private func setupHierarchy() {
        [titleLabel, contentStackView].forEach { addSubview($0) }
    }
    
    private func setupConstraints() {
        titleLabel.snp.makeConstraints {
            $0.directionalHorizontalEdges.equalToSuperview().inset(20)
            $0.top.equalToSuperview().offset(24)
        }
        
        contentStackView.snp.makeConstraints {
            $0.top.equalTo(titleLabel.snp.bottom).offset(8)
            $0.directionalHorizontalEdges.equalTo(titleLabel)
        }
        
        self.snp.makeConstraints {
            $0.bottom.equalTo(contentStackView.snp.bottom)
        }
    }
    
    private func setupContentStackView(with titles: [Title]) {
        for title in titles {
            let label = UILabel().then {
                $0.font = UIFont.systemFont(ofSize: 14)
                $0.textColor = .darkGray
                $0.text = title.title
            }
            
            contentStackView.addArrangedSubview(label)
        }
    }
    
    func configure(with titles: [Title]) {
        setupContentStackView(with: titles)
    }
    
}

 

이번 과제에선 StackView의 활용을 좀 많이 하는 편인데 기존에 StackView를 자주 사용하지 않고 오히려 좀 피하는 경향이 있었는데 이번 기회를 통해 좀 더 잘 활용할 수 있게 된 것 같다. 

 

그리고 과제 코드리뷰 중 parameter의 네이밍 중 전치사를 잘 써보라고 하셔서 그렇게 해보려고 노력중인데, 확실히 이 방향으로 잡으니깐 후에 가독성도 좋아질 것 같다. 

 

struct Book: Decodable {
    let title: String
    let author: String
    let pages: Int
    let releaseDate: String
    let dedication: String
    let summary: String
    let wiki: String
    let chapters: [Title]
    
    enum CodingKeys: String, CodingKey {
        case title
        case author
        case pages
        case releaseDate = "release_date"
        case dedication
        case summary
        case wiki
        case chapters
    }
}

 

코딩키를 기존에 써본 적이 없는데, data.json에선 release_date로 나오는데 swift에선 snake case대신 camel case를 쓰는게 컨벤션이기에 codingKey를 써보게 되었다.

 


이번 주는 플젝주라 과제 레벨 별 구현하고 저녁에 코드리뷰하고 그에 맞춰 반영하는 과정의 반복인데, 얻는 게 너무 많다. 너무 재밌음

'iOS > Swift' 카테고리의 다른 글

[TIL / 25.03.31] 앨범에서 사진 고르고 잘라서 쓸 수 있도록! (feat. PHPickerViewController, TOCropViewController)  (1) 2025.03.31
[TIL / 25.03.28] 과제 5, 스택뷰가 보이지 않는 문제 해결  (2) 2025.03.28
HarryPotterBooks 과제 1레벨 기록  (0) 2025.03.25
[TIL / 25.03.24] UIKit No Storyboard 초기 세팅  (0) 2025.03.24
[TIL / 25.03.20] 최적화(OptimizationTips) 2  (0) 2025.03.20
'iOS/Swift' 카테고리의 다른 글
  • [TIL / 25.03.31] 앨범에서 사진 고르고 잘라서 쓸 수 있도록! (feat. PHPickerViewController, TOCropViewController)
  • [TIL / 25.03.28] 과제 5, 스택뷰가 보이지 않는 문제 해결
  • HarryPotterBooks 과제 1레벨 기록
  • [TIL / 25.03.24] UIKit No Storyboard 초기 세팅
subkyu-ios
subkyu-ios
subkyu-ios 님의 블로그 입니다.
  • subkyu-ios
    subkyu-ios 님의 블로그
    subkyu-ios
  • 전체
    오늘
    어제
    • 분류 전체보기 (56)
      • iOS (38)
        • Swift (38)
      • 내일배움캠프 (7)
      • Git, Github (3)
      • Algorithm (6)
      • 회고 (1)
      • 면접 질문 정리 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    내일배움캠프
    사전캠프
    algorithm
    ios
    stackview
    UIKit
    TableView
    RxSwift
    알고리즘
    tabman
    Wil
    Swift
    의존성 주입
    트러블슈팅
    회고
    프로그래머스
    github
    til
    본캠프
    KPT
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
subkyu-ios
[TIL / 25.03.27] HarryPotterBooks 과제 2~4Lv 구현 및 회고
상단으로

티스토리툴바