Final (23)
리뷰 상세 페이지 이미지 확대 기능 추가
우선 UIImageView의 extension을 만들어 주었다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import UIKit
import Kingfisher
extension UIImageView {
private struct AssociatedKeys {
static var urlKey = "urlKey"
}
Add commentMore actions
var imageURL: URL? {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.urlKey) as? URL
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.urlKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
이 코드는 UIImageView에 직접 저장할 수 없는 값을 동적으로 추가하기 위해 런타임 속성(Associated Object) 을 사용하는 확장이다. imageURL이라는 커스텀 속성을 UIImageView에 추가하고, 이미지에 연결된 URL 정보를 저장할 수 있게 만든다. 이건 GPT의 도움을 받아서 만들었다.
DetailedReviewViewController 변경사항 요약
1. 사용자 정보 관련 기능 추가
기존에는 리뷰 데이터를 보여줄 뿐, 해당 리뷰가 누가 작성했는지에 대한 시각적 정보(닉네임, 프로필 이미지)는 없었음.
이를 위해 아래와 같은 프로퍼티가 추가됨:
1
2
3
var userInfo: UserModel?
let userProfileImage = UIImageView()
let userNicknameLabel = UILabel()
사용자 정보를 받아 뷰에 반영하는 전용 메서드도 생성:
1
2
3
4
5
6
7
8
func setUserData(info: UserModel) {
userInfo = info
userNicknameLabel.text = info.nickName
if let url = URL(string: info.profileImageUrl) {
userProfileImage.kf.setImage(with: url)
}
}
이를 통해 리뷰 작성자의 신원 정보를 UI에 노출함으로써 리뷰 신뢰도와 개인화 요소가 강화됨.
2. 이미지 클릭 시 전체 화면 보기 기능 추가
기존에는 이미지가 화면에 뜨기만 할 뿐, 확대 보기나 상세 보기 기능이 없음.
이를 위해 이미지 뷰에 다음과 같은 설정이 추가됨:
1
2
imageView.isUserInteractionEnabled = true
imageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(imageTapped(_:))))
제스처가 인식되면 다음 메서드를 통해 전체화면 이미지 뷰어로 전환됨:
1
2
3
4
5
6
@objc func imageTapped(_ sender: UITapGestureRecognizer) {
guard let image = imageView.image else { return }
let fullscreenVC = FullscreenPageViewController(images: [image])
fullscreenVC.modalPresentationStyle = .fullScreen
present(fullscreenVC, animated: true)
}
이를 통해 사용자는 리뷰 이미지를 상세하게 확대해서 볼 수 있는 경험을 하게 된다.
3. 리뷰 생성일 표시 기능 추가
리뷰에 언제 작성되었는지에 대한 정보가 빠져 있어, 시점에 대한 신뢰도가 낮았음.
Firebase Timestamp를 문자열로 포맷팅하는 유틸 메서드를 구현:
1
2
3
4
5
func timestampToString(value: Timestamp) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy.MM.dd"
return dateFormatter.string(from: value.dateValue())
}
이렇게 변환된 날짜는 UILabel을 통해 다음과 같이 UI에 표시됨:
1
2
let createdAtLabel = UILabel()
createdAtLabel.text = timestampToString(value: review.createdAt)
이를 통해 사용자는 리뷰가 언제 작성되었는지를 명확하게 확인할 수 있다.
4. 전반적인 UI 개선
기존의 뷰 구조는 텍스트 간격이 좁고 정렬이 어색해 가독성이 떨어졌음.
“리뷰 전체보기” 안내 문구 추가:
1
2
introLabel.text = "리뷰 전체보기"
introLabel.font = UIFont.systemFont(ofSize: 14, weight: .medium)
상호명(Label)과 제목(Label)에 대해 Bold 및 정렬 처리:
1
2
3
storeNameLabel.font = UIFont.boldSystemFont(ofSize: 16)
reviewTitleLabel.numberOfLines = 0
reviewTitleLabel.textAlignment = .left
프로필 이미지 뷰에 대한 오토레이아웃도 재정비:
1
2
3
4
5
6
userProfileImage.snp.makeConstraints { make in
make.leading.equalToSuperview().offset(16)
make.top.equalToSuperview().offset(16)
make.width.height.equalTo(40)
make.bottom.lessThanOrEqualToSuperview().offset(-16)
}
이러한 변경을 통해 화면 구성의 정렬, 여백, 시각적 계층 구조가 명확히 개선되었음.
5. 코드 리팩토링 및 안정성 개선
뷰에 바인딩하는 코드를 별도 메서드로 분리:
func setUserData(info: UserModel) { ...
뷰 컨트롤러가 해제될 때 이미지 다운로드를 취소하여 메모리 누수 방지:
1
2
3
4
deinit {
userProfileImage.kf.cancelDownloadTask()
imageView.kf.cancelDownloadTask()
}
또한, nil 체크에 대해 guard 구문을 적극 사용하여 안전한 실행 흐름을 유도:
guard let user = userInfo else { return }
Kingfisher로 이미지를 비동기 로딩하는 경우에도 retain cycle을 피하기 위해 [weak self]
를 활용함.
결론
이번 DetailedReviewViewController의 개선 사항은 다음의 목적을 충실히 달성했다:
- 사람 중심 UI 구성: 작성자의 정체성과 후기 신뢰도 강화
- 전체화면 이미지 뷰어 제공: 생생한 후기 경험 제공
- 날짜 표시 도입: 시점 기반의 정보 신뢰도 확보
- 전반적인 레이아웃 개선: 텍스트와 이미지의 시각적 구조 향상
- 리팩토링을 통한 코드 안정화: 유지보수와 성능 모두 고려
기능적으로나 시각적으로나 이 뷰는 단순 리뷰 조회를 넘어,
사용자 경험 중심의 리뷰 읽기 뷰로 진화한 것이라 할 수 있다.