Conversation
Offroad-iOS/Offroad-iOS/Presentation/CourseQuest/Views/CourseQuestPlaceCell.swift
Outdated
Show resolved
Hide resolved
...oad-iOS/Offroad-iOS/Presentation/CourseQuest/ViewControllers/CourseQuestViewController.swift
Show resolved
Hide resolved
...oad-iOS/Offroad-iOS/Presentation/CourseQuest/ViewControllers/CourseQuestViewController.swift
Outdated
Show resolved
Hide resolved
| /// 이 함수는 CourseQuestViewController 전용 D-Day 계산 함수입니다. | ||
| /// CourseQuestCollectionViewCell에도 유사한 함수가 존재하지만, QuestListViewController에서 D-Day를 표시하기 위한 용도이며, | ||
| /// 책임이 분리되어야 한다고 판단하여 별도로 정의했습니다. |
There was a problem hiding this comment.
P4
d-day 계산 기능을 뷰컨트롤러와 셀 모두에서 구현하지 않고,
대신 '코스퀘스트' 타입의 기능으로 빼는 것은 어떨까요?
'해당 퀘스트가 얼마나 남았는지'는 퀘스트 자체의 특징이라고 충분히 생각할 수 있을 것 같은데,
이 경우, 코스퀘스트 타입의 메서드나 계산 속성 등으로 구현해도 좋을 것 같다고 생각합니다.
- 그리고 나서 뷰컨트롤러에서 코스퀘스트 타입 자체를 모델로 사용하면 훨씬 간편해질 것 같다는 생각이 들었습니다!
이 부분은 지금 당장 수정이 어렵다면 후순위로 미루어도 괜찮을 것 같습니다 :)
There was a problem hiding this comment.
좋은 의견 감사합니다!
말씀해주신 것처럼 D-Day 계산은 퀘스트 자체의 성격에 가까우니, CourseQuest 타입 내에서 메서드나 계산 속성으로 분리하는 방향이 좋을 것 같습니다.
다만 현재는 기능 개발 우선순위상 해당 부분은 조금 뒤로 미루고, qa 이후 적용하도록 하겠습니다!
...oad-iOS/Offroad-iOS/Presentation/CourseQuest/ViewControllers/CourseQuestViewController.swift
Outdated
Show resolved
Hide resolved
| private let courseQuestDetailService = CourseQuestDetailService() | ||
| private var quests: [CourseQuestDetailPlaceDTO] = [] | ||
| var questId: Int? | ||
| var deadline: String? |
There was a problem hiding this comment.
P3
지금 당장 바꿀 수 있는 부분은 아니라고 생각되는데,
데드라인의 경우 Date 타입으로 저장하는 게 더 적절할 것 같다는 의견 남깁니다.
추후 개선사항으로 남겨두면 좋을 것 같아요.
There was a problem hiding this comment.
알겠습니다! 추후 반영하도록 하겠습니다.
...oad-iOS/Offroad-iOS/Presentation/CourseQuest/ViewControllers/CourseQuestViewController.swift
Show resolved
Hide resolved
...oad-iOS/Offroad-iOS/Presentation/CourseQuest/ViewControllers/CourseQuestViewController.swift
Outdated
Show resolved
Hide resolved
...oad-iOS/Offroad-iOS/Presentation/CourseQuest/ViewControllers/CourseQuestViewController.swift
Outdated
Show resolved
Hide resolved
...oad-iOS/Offroad-iOS/Presentation/CourseQuest/ViewControllers/CourseQuestViewController.swift
Outdated
Show resolved
Hide resolved
Offroad-iOS/Offroad-iOS/Presentation/CourseQuest/Views/CourseQuestPlaceCell.swift
Show resolved
Hide resolved
Offroad-iOS/Offroad-iOS/Presentation/CourseQuest/Views/CourseQuestPlaceCell.swift
Outdated
Show resolved
Hide resolved
Offroad-iOS/Offroad-iOS/Presentation/CourseQuest/Views/CourseQuestPlaceCell.swift
Show resolved
Hide resolved
Offroad-iOS/Offroad-iOS/Presentation/CourseQuest/Views/CourseQuestPlaceCell.swift
Outdated
Show resolved
Hide resolved
Offroad-iOS/Offroad-iOS/Presentation/CourseQuest/Views/CourseQuestPlaceCell.swift
Outdated
Show resolved
Hide resolved
Offroad-iOS/Offroad-iOS/Presentation/CourseQuest/Views/CourseQuestView.swift
Outdated
Show resolved
Hide resolved
Offroad-iOS/Offroad-iOS/Presentation/CourseQuest/Views/CourseQuestView.swift
Show resolved
Hide resolved
Johyerin
left a comment
There was a problem hiding this comment.
민성님이 꼼꼼하게 리뷰 달아주셔서 코멘트가 많지는 않지만 읽어보시고 반영해주시면 좋을 것 같아요! 고생하셨습니다.
Offroad-iOS/Offroad-iOS/Presentation/CourseQuest/Views/CourseQuestMapView.swift
Show resolved
Hide resolved
Offroad-iOS/Offroad-iOS/Presentation/CourseQuest/Views/CourseQuestPlaceCell.swift
Show resolved
Hide resolved
Offroad-iOS/Offroad-iOS/Presentation/CourseQuest/Views/CourseQuestPlaceCell.swift
Show resolved
Hide resolved
| rootView.ongoingQuestSwitch.addTarget(self, action: #selector(ongoingQuestSwitchValueChanged(sender:)), for: .valueChanged) | ||
|
|
||
| #if DevTarget | ||
| rootView.questListCollectionView.onTapCourseQuestDetail = { [weak self] quest in |
There was a problem hiding this comment.
P4
ViewController가 아닌 QuestListCollectionView가 직접 모델을 가지고 있게 되면서 위와 같이 클로저를 통해 데이터를 전달해야하는 방식이 조금은 어색하다고 느껴집니다.(이전에 논의가 잠깐 됐었던 것 같기는 합니다...?!) 로직을 담당하는 ViewController에 비해 UI 요소들의 역할이 비대한 점을 고려하여 가능하다면 ViewController가 UICollectionView 프로토콜을 채택하여 관리하는 것이 구조적으로나 가독성 측면에서 이로울 것 같다는 생각이 드네요! 현재는 구조가 많이 복잡하게 얽혀있어서 추후 적절하게 반영해보셔도 좋을 것 같습니다.
There was a problem hiding this comment.
동의합니다!
뷰컨트롤러의 부담을 줄여보고자 컬렉션뷰에서 직접 모델을 가져보면 어떨까 하여 예전에 제가 구현할 때 이같이 코드를 짠 것으로 기억하는데요,
뷰컨트롤러에서 모델을 관리하거나, 혹은 DataSource 역할을 하는 별도 인스턴스를 구현하는 방법으로 개선 방향을 잡는 게 좋을 것 같습니다.
There was a problem hiding this comment.
초기에는 UI 구성과 기능 분리를 위해 뷰 쪽에서 처리하게 되었지만,
로직과 뷰의 책임이 뒤섞인 부분이 생기면서 오히려 복잡도가 커진 것 같습니다.
추후 리팩토링 시 모델 관리 책임을 ViewController로 옮기고 QuestListCollectionView는 UI 표현에만 집중하도록
구조를 재정리해보겠습니다!
🌴 작업한 브랜치
✅ 작업한 내용
CourseQuestMapView 생성 및 커스텀 마커 적용
코스 퀘스트의 지도 뷰는 별도 상호작용 없이 마커만 표시되면 되는 간단한 구조이므로 NMFNaverMapView를 직접 커스터마이징하여 CourseQuestMapView로 구현했습니다.
디자인 파트로부터 전달받은 카테고리별 색상 규칙에 따라, 각 장소에 맞는 마커가 지도 위에 표시됩니다.
코스퀘스트 상세 뷰의 장소 셀 CourseQuestPlaceCell

반복되는 장소 셀 레이아웃을 컴포넌트화하여 CourseQuestPlaceCell로 분리했습니다.
서버에서 장소 이미지를 SVG 형태로 전달받고 있어, 현재는 SVGKit을 사용하여 렌더링하고 있습니다.
추후 서버에서 실제 이미지 URL을 제공하면 Kingfisher로 교체할 예정입니다.
thumbnailImageView.fetchSvgURLToImageView(svgUrlString: model.categoryImage)코스퀘스트 목록 뷰(QuestListCollectionView) api 연결
기존 퀘스트 목록 API를 그대로 사용하되, 새로 추가된 isCourse 필드를 기반으로 코스 퀘스트를 필터링하여 courseQuests에 분리 저장합니다.
진행 중 탭에서는 currentCount > 0 조건을 만족하는 일반 퀘스트들을 activeQuestList로, 코스 퀘스트는 항상 상단에 고정되도록 조합해 quests를 구성합니다.
코스퀘스트 상세 뷰(CourseQuestViewController) api 연결
목록에서 선택된 퀘스트의 questId를 기반으로 코스 퀘스트 상세 장소 목록 API를 호출합니다.
각 장소 셀의 visitButton을 누르면 해당 장소의 placeId를 이용해 위치 인증을 수행하도록 구현하였습니다.
다만 현재 API에서 placeId 필드가 누락되어 있어 placeId를 0으로 설정하였고 위치 인증은 안되고 있으며, 서버 수정 이후에 정상 동작하도록 다음 PR에서 반영할 예정입니다.
위치 인증 성공 시, 남은 장소 수를 표시하는 Toast 메시지가 뜨고, 실패 시 Alert으로 안내됩니다.
❗️PR Point
디자인 파트에서 장소 목록을 위로 스크롤하면 목록이 모달처럼 올라가며 navigationBar 바로 아래에 붙는 형태로 구현해달라는 요청이 있었는데요, 해당 기능을 구현하려고 시도했으나 스크롤 동작에 문제가 발생해 우선순위를 뒤로 미뤘습니다. 현재 브랜치에서는 스크롤 시 어색한 부분이 있을 수 있지만, 해당 기능은 다음 PR에서 반영할 예정입니다.
UI도(토스트메세지, 마커 간 점선) 불완전한 부분이 있는데 이 부분은 빠른 시간 내에 반영하겠습니다.
📸 스크린샷
ScreenRecording_06-24-2025.23.mov
ScreenRecording_06-24-2025.18.mov