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 |