-
Notifications
You must be signed in to change notification settings - Fork 1
[Feat-T3-127] 감정 구슬 API 연동 및 추천 루틴 결과 화면 재사용 #29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
95a855e
03a1d35
571b51a
42f0a09
460f07a
77a9622
bcb616e
d70170d
51d2da4
6532e55
7e53f73
93e9a1b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| // | ||
| // EmotionResponseDTO.swift | ||
| // DataSource | ||
| // | ||
| // Created by 최정인 on 7/29/25. | ||
| // | ||
|
|
||
| import Domain | ||
| import Foundation | ||
|
|
||
| struct EmotionResponseDTO: Decodable { | ||
| let type: String | ||
| let name: String | ||
| let imageUrl: String | ||
|
|
||
| enum CodingKeys: String, CodingKey { | ||
| case type = "emotionMarbleType" | ||
| case name = "emotionMarbleName" | ||
| case imageUrl | ||
| } | ||
| } | ||
|
|
||
| extension EmotionResponseDTO { | ||
| func toEmotionEntity() -> EmotionEntity { | ||
| return EmotionEntity( | ||
| emotionType: type, | ||
| emotionName: name, | ||
| emotionImageUrl: URL(string: imageUrl)) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| // | ||
| // EmotionEndpoint.swift | ||
| // DataSource | ||
| // | ||
| // Created by 최정인 on 7/28/25. | ||
| // | ||
|
|
||
| enum EmotionEndpoint { | ||
| case fetchEmotions | ||
| case registerEmotion(emotion: String) | ||
| } | ||
|
|
||
| extension EmotionEndpoint: Endpoint { | ||
| var baseURL: String { | ||
| return AppProperties.baseURL + "/api/v1/emotion-marbles" | ||
| } | ||
|
|
||
| var path: String { | ||
| return baseURL | ||
| } | ||
|
|
||
| var method: HTTPMethod { | ||
| switch self { | ||
| case .fetchEmotions: .get | ||
| case .registerEmotion: .post | ||
| } | ||
| } | ||
|
|
||
| var headers: [String : String] { | ||
| let headers: [String: String] = [ | ||
| "Content-Type": "application/json", | ||
| "accept": "*/*" | ||
| ] | ||
| return headers | ||
| } | ||
|
|
||
| var queryParameters: [String : String] { | ||
| return [:] | ||
| } | ||
|
|
||
| var bodyParameters: [String : Any] { | ||
| switch self { | ||
| case .fetchEmotions: | ||
| return [:] | ||
| case .registerEmotion(let emotion): | ||
| return ["emotionMarbleType": emotion] | ||
| } | ||
| } | ||
|
|
||
| var isAuthorized: Bool { | ||
| return true | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| // | ||
| // EmotionRepository.swift | ||
| // DataSource | ||
| // | ||
| // Created by 최정인 on 7/28/25. | ||
| // | ||
|
|
||
| import Domain | ||
|
|
||
| final class EmotionRepository: EmotionRepositoryProtocol { | ||
| private let networkService = NetworkService.shared | ||
|
|
||
| func fetchEmotions() async throws -> [EmotionEntity] { | ||
| let endpoint = EmotionEndpoint.fetchEmotions | ||
| guard let response = try await networkService.request(endpoint: endpoint, type: [EmotionResponseDTO].self) | ||
| else { return [] } | ||
|
|
||
| let emotionEntities = response.compactMap({ $0.toEmotionEntity() }) | ||
| return emotionEntities | ||
| } | ||
|
Comment on lines
+14
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 에러 처리 개선 필요 두 메서드 모두 네트워크 요청 실패 시 빈 배열을 반환하고 있습니다. 이는 실제로 빈 결과와 에러를 구분하기 어렵게 만듭니다. 호출하는 측에서 에러를 적절히 처리할 수 있도록 에러를 throw하는 것이 좋습니다: -guard let response = try await networkService.request(endpoint: endpoint, type: [EmotionResponseDTO].self)
-else { return [] }
+let response = try await networkService.request(endpoint: endpoint, type: [EmotionResponseDTO].self)Also applies to: 22-29 🤖 Prompt for AI Agents |
||
|
|
||
| func registerEmotion(emotion: String) async throws -> [RecommendedRoutineEntity] { | ||
| let endpoint = EmotionEndpoint.registerEmotion(emotion: emotion) | ||
| guard let response = try await networkService.request(endpoint: endpoint, type: RecommendedRoutineListResponseDTO.self) | ||
| else { return [] } | ||
|
|
||
| let recommendedRoutineEntity = response.recommendedRoutines.compactMap({ $0.toRecommendedRoutineEntity() }) | ||
| return recommendedRoutineEntity | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| // | ||
| // EmotionEntity.swift | ||
| // Domain | ||
| // | ||
| // Created by 최정인 on 7/29/25. | ||
| // | ||
|
|
||
| import Foundation | ||
|
|
||
| public struct EmotionEntity { | ||
| public let emotionType: String | ||
| public let emotionName: String | ||
| public let emotionImageUrl: URL? | ||
|
|
||
| public init( | ||
| emotionType: String, | ||
| emotionName: String, | ||
| emotionImageUrl: URL? | ||
| ) { | ||
| self.emotionType = emotionType | ||
| self.emotionName = emotionName | ||
| self.emotionImageUrl = emotionImageUrl | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| // | ||
| // EmotionRepositoryProtocol.swift | ||
| // Domain | ||
| // | ||
| // Created by 최정인 on 7/28/25. | ||
| // | ||
|
|
||
| /// 감정 구슬에 대한 로직을 처리하는 Repository | ||
| public protocol EmotionRepositoryProtocol { | ||
| /// 감정 구슬 목록을 불러옵니다. | ||
| /// - Returns: 조회된 감정 구슬 목록 | ||
| func fetchEmotions() async throws -> [EmotionEntity] | ||
|
|
||
| /// 감정 구슬을 등록합니다. | ||
| /// - Parameter emotion: 감정 구슬 String 값 | ||
| /// - Returns: 등록한 감정 구슬에 따른 추천 루틴 리스트 | ||
| func registerEmotion(emotion: String) async throws -> [RecommendedRoutineEntity] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| // | ||
| // EmotionUseCaseProtocol.swift | ||
| // Domain | ||
| // | ||
| // Created by 최정인 on 7/28/25. | ||
| // | ||
|
|
||
| public protocol EmotionUseCaseProtocol { | ||
| /// 감정 구슬 목록을 불러옵니다. | ||
| /// - Returns: 조회된 감정 구슬 목록 | ||
| func fetchEmotions() async throws -> [EmotionEntity] | ||
| } |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| // | ||
| // ResultRecommendedRoutineUseCaseProtocol.swift | ||
| // Domain | ||
| // | ||
| // Created by 최정인 on 7/29/25. | ||
| // | ||
|
|
||
| import Foundation | ||
|
|
||
| public protocol ResultRecommendedRoutineUseCaseProtocol { | ||
| /// 선택한 온보딩 결과를 저장하고, 추천 루틴을 받습니다. | ||
| /// - Parameter onboardingChoices: 선택한 온보딩 항목 list | ||
| /// - Returns: 온보딩 결과를 바탕으로 받은 추천루틴 목록 | ||
| func fetchResultRecommendedRoutines(onboardingChoices: [OnboardingChoiceType]) async throws -> [RecommendedRoutineEntity] | ||
|
|
||
| /// 감정 구슬을 등록하고 그에 따른 추천 루틴 리스트를 받습니다. | ||
| /// - Parameter emotion: 감정 구슬 타입 String | ||
| /// - Returns: 등록한 감정 구슬에 따른 추천 루틴 리스트 | ||
| func fetchResultRecommendedRoutines(emotion: String) async throws -> [RecommendedRoutineEntity] | ||
|
|
||
| /// 선택한 추천 루틴을 등록합니다. | ||
| /// - Parameter selectedRoutines: 선택한 추천 루틴 ID 목록 | ||
| func registerRecommendedRoutines(selectedRoutines: [Int]) async throws | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| // | ||
| // EmotionUseCase.swift | ||
| // Domain | ||
| // | ||
| // Created by 최정인 on 7/28/25. | ||
| // | ||
|
|
||
| public final class EmotionUseCase: EmotionUseCaseProtocol { | ||
| private let emotionRepository: EmotionRepositoryProtocol | ||
|
|
||
| public init(emotionRepository: EmotionRepositoryProtocol) { | ||
| self.emotionRepository = emotionRepository | ||
| } | ||
|
|
||
| public func fetchEmotions() async throws -> [EmotionEntity] { | ||
| let emotions = try await emotionRepository.fetchEmotions() | ||
| return emotions | ||
| } | ||
| } |
This file was deleted.
This file was deleted.
This file was deleted.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
URL 생성 시 안전성 확보 필요
URL(string:)은 유효하지 않은 URL 문자열의 경우 nil을 반환할 수 있습니다. API 응답의 imageUrl이 항상 유효한 URL 형식임을 보장할 수 없다면, 안전한 처리가 필요합니다.EmotionEntity의emotionImageUrl이 옵셔널 타입이라면 현재 코드가 적절합니다. 그렇지 않다면 기본값 처리나 에러 처리를 고려해주세요.🤖 Prompt for AI Agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
맞는 말씀이지만, 서버님들이 유효한 URL을 내려주실거라 믿어요~
어차피 유효한 URL이 아니면 기본 이미지를 표시하겟죠???????
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
마주아요 !!! 유효한 URL을 내려주실거지만 ~ 없다면 default 구슬 (empty 구슬을 보여드리겠슴니다 !!!)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@taipaise 네, 말씀하신 대로
EmotionEntity의emotionImageUrl이 옵셔널 타입(URL?)이라서 현재 구현이 안전합니다!그리고 Kingfisher를 사용하고 계시니 nil이나 유효하지 않은 URL의 경우 자동으로 placeholder나 기본 이미지를 표시해줄 거예요. 서버 팀을 믿고 가시는 것도 합리적인 접근 방식이네요 👍
✏️ Learnings added
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.