Skip to content

Conversation

@codeJiwon
Copy link
Member

🌴 작업한 브랜치

✅ 작업한 내용

1. 탐험 인증 버튼 동작 (onVisit) 구현

  • 유저 현재 위치(CLLocationManager) 기반 인증 시도
  • 개발자 설정값 bypassLocationAuthentication 여부(탐험 시 위치 인증 무시)에 따라 분기 처리

2. 탐험 성공 & 오늘 첫 방문 ((true, true))

  • 해당 장소의 isVisited 값을 true로 업데이트 UICollectionView.reloadItems(at:)로 셀만 부분 갱신
    ///CourseQuestViewController.swift
     case (true, true):
        // 탐험 성공
        self.courseQuestPlaces[indexPath.item] = CourseQuestDetailPlaceDTO(
           category: place.category,
           name: place.name,
           address: place.address,
           latitude: place.latitude,
           longitude: place.longitude,
           isVisited: true,
           categoryImage: place.categoryImage,
           description: place.description,
           placeId: place.placeId
        )
    
    ///CourseQuestPlaceCell.swift
    ///CourseQuestPlaceCell에서 isVisited를 기준으로 clearImageView를 보여줄 지를 결정하기 때문
    visitButton.isHidden = model.isVisited ?? false
    clearImageView.isHidden = !(model.isVisited ?? false)
  • 남은 탐험 장소 수 계산:
     currentCount += 1  
     remainCount = totalCount - currentCount

3. 팝업 메시지 분기 처리
남은 탐험 수가 0일 경우: "퀘스트 클리어! 보상을 받아보세요" → "보상" 강조
그 외: "방문 성공! 앞으로 N곳 남았어요" → "N곳" 강조

4. 위치 인증 실패
커스텀 ORBAlertController 표시

5. 커스텀 팝업 뷰 CourseQuestPopUp 사용

  • 메시지 내 특정 텍스트 강조 기능 (highlightText) 지원
  • 애니메이션과 함께 자동 사라짐 (toast 형태)
    ios_toast_popup-3

❗️PR Point

📸 스크린샷

설명 설명
IMG_8136 IMG_8135 IMG_8134

@codeJiwon codeJiwon self-assigned this Jul 2, 2025
@codeJiwon codeJiwon linked an issue Jul 2, 2025 that may be closed by this pull request
@codeJiwon codeJiwon added Style 🎨 UI 작업 Network 🌏 서버 통신 labels Jul 2, 2025
Comment on lines 152 to 247
cell.onVisit = { [weak self] in
self?.courseQuestPlaces[indexPath.item] = CourseQuestDetailPlaceDTO(
category: quest.category,
name: quest.name,
address: quest.address,
latitude: quest.latitude,
longitude: quest.longitude,
isVisited: true,
categoryImage: quest.categoryImage,
description: quest.description,
placeId: quest.placeId
)
collectionView.reloadItems(at: [indexPath])
self?.showToast("방문 성공! 앞으로 N곳 남았어요")
guard let self else { return }
let place = quest

guard let currentCoordinate = locationManager.location?.coordinate else { return }

// 변수 먼저 선언
var requestDTO: AdventuresPlaceAuthenticationRequestDTO

// 개발자 모드의 설정값 중 '탐험 시 위치 인증 무시' 값에 따라 분기 처리
let locationAuthenticationBypassing = UserDefaults.standard.bool(forKey: "bypassLocationAuthentication")
if locationAuthenticationBypassing {
requestDTO = AdventuresPlaceAuthenticationRequestDTO(
placeId: place.placeId,
latitude: place.latitude,
longitude: place.longitude
)
} else {
requestDTO = AdventuresPlaceAuthenticationRequestDTO(
placeId: place.placeId,
latitude: currentCoordinate.latitude,
longitude: currentCoordinate.longitude
)
}

Task {
do {
let result = try await AdventureService().authenticateAdventurePlace(adventureAuthDTO: requestDTO)

switch (result.isValidPosition, result.isFirstVisitToday) {
case (true, true):
// 탐험 성공
self.courseQuestPlaces[indexPath.item] = CourseQuestDetailPlaceDTO(
category: place.category,
name: place.name,
address: place.address,
latitude: place.latitude,
longitude: place.longitude,
isVisited: true,
categoryImage: place.categoryImage,
description: place.description,
placeId: place.placeId
)
self.currentCount += 1
//남은 탐험 수
let remainCount = max(self.totalCount - self.currentCount, 0)
collectionView.reloadItems(at: [indexPath])
await MainActor.run {
if remainCount == 0 {
CourseQuestPopUp.showPopUp(
on: self.view,
message: "퀘스트 클리어! 보상을 받아보세요"
) {
$0.highlightText(targetText: "보상", font: .offroad(style: .iosTextBold))
}
} else {
CourseQuestPopUp.showPopUp(
on: self.view,
message: "방문 성공! 앞으로 \(remainCount)곳 남았어요"
) {
$0.highlightText(targetText: "\(remainCount)", font: .offroad(style: .iosTextBold))
}
}
}

default:
// 위치 인증 실패 → ORBAlertController 사용
let message = AlertMessage.courseQuestFailureLocationMessage
let buttonTitle = "확인"

await MainActor.run {
let alertController = ORBAlertController(
title: AlertMessage.courseQuestFailureLocationTitle,
message: message,
type: .normal
)
alertController.configureMessageLabel {
$0.highlightText(targetText: "위치", font: .offroad(style: .iosTextBold))
$0.highlightText(targetText: "내일 다시", font: .offroad(style: .iosTextBold))
}
alertController.xButton.isHidden = true
let action = ORBAlertAction(title: buttonTitle, style: .default) { _ in
// 위치 인증 실패 시 후처리
}
alertController.addAction(action)
self.present(alertController, animated: true)
}
}

} catch {
await MainActor.run {
print("탐험 인증에 실패했어요. 다시 시도해주세요.")
}
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P4
cell 안의 onVisit 변수에 거의 100줄 가까이 되는 클로저를 할당하고 있는 것 같은데,
이 정도의 길이면 별도 함수로 빼서 구현하는 게 좋을 것 같다는 생각이 듭니다..!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

말씀해주신 대로 onVisit 클로저의 길이가 길어 가독성과 유지보수 측면에서 부담이 될 수 있을 것 같습니다.
해당 로직은 별도 함수로 분리해 추후 리팩토링하겠습니다. 감사합니다!

Copy link
Contributor

@nolanMinsung nolanMinsung left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수고하셨습니다!
시간이 얼마 없어서,
지금은 P1 코멘트만 먼저 반영해 주시고, 다른 코멘트는 나중에 반영해도 무방합니다.

Copy link
Collaborator

@Johyerin Johyerin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

변경내용 확인했습니다아 고생하셨습니다!!

Comment on lines +30 to +32
private let typeLabelView = UIView().then {
$0.roundCorners(cornerRadius: 10)
$0.backgroundColor = .neutral(.nametagInactive)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2

지금 보니 일부 셀에 한해서 방문 버튼을 누르기 전과 후의 셀 높이 차이가 있는 경우가 확인됩니다! QA 수정 때 같이 확인해주셔야 될 것 같아용

Copy link
Member Author

@codeJiwon codeJiwon Jul 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

꼼꼼하게 확인해주셔서 감사합니다. 해당 부분은 수정하려 했으나 정확한 원인을 파악하지 못해 QA 수정 시 함께 반영할 예정입니다.

Copy link
Collaborator

@Johyerin Johyerin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

몇가지 오류 더 발견되어서 추가로 올립니당

++

요렇게 요약된 퀘스트 수와 실제 퀘스트 수가 다르게 나타나는 경우도 있는데(확인해보니 서버에서 다르게 내려오긴 하네요!) QA 때 참고하시면 좋을 것 같아서 첨부합니당

clearImageView.snp.makeConstraints {
$0.trailing.equalToSuperview().inset(6)
$0.centerY.equalToSuperview()
$0.size.equalTo(75)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ScreenRecording_07-05-2025.02-03-06_1.MP4

해당 코드로는 문제 수정이 안된 것 같아서 스크린샷 같이 첨부해드리겠습니다!

Copy link
Contributor

@nolanMinsung nolanMinsung left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수고하셨습니다!
남은 이슈들과 추가된 QA 수정사항은 새 브랜치에서 구현하는 게 좋을 것 같습니다!

Copy link
Collaborator

@Johyerin Johyerin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨습니다!!

@codeJiwon codeJiwon merged commit 8b04d1f into main Jul 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] 코스퀘스트 탐험 인증 및 스크롤 구현

4 participants