Skip to content

Commit 52339a4

Browse files
authored
Feat: 추천 루틴 API 연동 (#T3-121)
* Style: 일부 파일들 import 컨벤션 맞추기 * Feat: RecommendedRoutineRepository 구현 (#T3-121) - RecommendedRoutineDTO, RecommendedRoutineDictionaryResponseDTO, RecommendedRoutineListResponseDTO 타입 정의 - RecommendedRoutineEndpoint 생성 - RecommendedRoutineRepositoryProtocol, RecommendedRoutineRepository 정의 및 구현 * Feat: RecommendedRoutineUseCase 구현 (#T3-121) - RecommendedRoutineUseCaseProtocol, RecommendedRoutineUseCase 정의 및 구현 - RecommendedRoutineEntity 수정 (카테고리, 난이도 값 추가) - RoutineCategoryType, RoutineLevelType 위치 변경 (기존 Presentation -> Domain) * Refactor: 추천 루틴 뷰 수정 (#T3-121) - 감정 등록 버튼이 추천 루틴 목록들보다 상단에 뜨도록 UI 수정 - RecommendedRoutineCardView에서 Label trailing Layout 추가 * Refactor: RoutineLevelType, RoutineCategoryType 모듈 위치 변경으로 인한 Presentation 모듈 수정 - Presentation 모듈에 있는 RoutineLevelType, RoutineCategoryType가 Domain을 extension해서 필요한 값을 추가하여 사용할 수 있도록 수정 - 그에 따른 import Domain 추가 * Feat: RecommendedRoutineView 및 RecommendedRoutineViewModel 로직 구현 (#T3-121) - 전체 추천 루틴 받아오는 로직 추가 + 더미 데이터 삭제 - 카테고리 및 난이도에 대해 받아온 루틴 필터링하는 로직 추가 - 의존성 조립 (DataSource, Domain, Presentation) * Feat: FloatingButton, FloatingMenuView UI 구현 * Feat: 추천 루틴 화면에 FloatingButton 추가 및 등록 화면 이동 (#T3-121) - RecommendedRoutineView에 FloatingButton, FloatingMenu, dimmedView 추가 - 루틴 등록 화면으로 이동해야 하므로 RoutineCreationViewModel 의존성 등록 * Style: 오타 수정 * Refactor: 맞춤 추천 카테고리인 경우 감정 등록 버튼 보여주기
1 parent 486323b commit 52339a4

35 files changed

Lines changed: 567 additions & 119 deletions

Projects/DataSource/Sources/Common/DataSourceDependencyAssembler.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,9 @@ public struct DataSourceDependencyAssembler: DependencyAssemblerProtocol {
2424
DIContainer.shared.register(type: UserDataRepositoryProtocol.self) { _ in
2525
return UserDataRepository()
2626
}
27+
28+
DIContainer.shared.register(type: RecommendedRoutineRepositoryProtocol.self) { _ in
29+
return RecommendedRoutineRepository()
30+
}
2731
}
2832
}

Projects/DataSource/Sources/DTO/RecommendedRoutineDTO.swift

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,42 +2,56 @@
22
// RecommendedRoutineDTO.swift
33
// DataSource
44
//
5-
// Created by 최정인 on 7/15/25.
5+
// Created by 최정인 on 7/27/25.
66
//
77

88
import Domain
99

10-
struct RecommendedRoutineListResponseDTO: Decodable {
11-
let recommendedRoutines: [RecommendedRoutineDTO]
12-
}
13-
1410
struct RecommendedRoutineDTO: Decodable {
1511
let id: Int
1612
let routineName: String
1713
let routineDescription: String
18-
let subRoutines: [SubRoutine]
14+
let routineLevel: String?
15+
let subRoutines: [SubRoutineDTO]
1916

2017
enum CodingKeys: String, CodingKey {
2118
case id = "recommendedRoutineId"
2219
case routineName = "recommendedRoutineName"
23-
case routineDescription
24-
case subRoutines = "recommendedSubRoutines"
20+
case routineDescription = "recommendedRoutineDescription"
21+
case routineLevel = "recommendedRoutineLevel"
22+
case subRoutines = "recommendedSubRoutineSearchResult"
2523
}
2624

27-
func toRecommendedRoutineEntity() -> RecommendedRoutineEntity {
25+
func toRecommendedRoutineEntity(category: String? = nil) -> RecommendedRoutineEntity {
26+
var routineCategory: RoutineCategoryType?
27+
if let category {
28+
routineCategory = RoutineCategoryType(rawValue: category)
29+
}
30+
31+
var level: RoutineLevelType?
32+
if let routineLevel {
33+
level = RoutineLevelType(rawValue: routineLevel)
34+
}
2835
return RecommendedRoutineEntity(
2936
id: id,
3037
title: routineName,
31-
description: routineDescription)
38+
description: routineDescription,
39+
category: routineCategory,
40+
level: level,
41+
subRoutines: subRoutines.compactMap({ $0.toSubRoutineEntity() }))
3242
}
3343
}
3444

35-
struct SubRoutine: Decodable {
45+
struct SubRoutineDTO: Decodable {
3646
let id: Int
3747
let routineName: String
3848

3949
enum CodingKeys: String, CodingKey {
4050
case id = "recommendedSubRoutineId"
4151
case routineName = "recommendedSubRoutineName"
4252
}
53+
54+
func toSubRoutineEntity() -> SubRoutineEntity {
55+
return SubRoutineEntity(id: id, title: routineName)
56+
}
4357
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//
2+
// RecommendedRoutineDictionaryResponseDTO.swift
3+
// DataSource
4+
//
5+
// Created by 최정인 on 7/27/25.
6+
//
7+
8+
struct RecommendedRoutineDictionaryResponseDTO: Decodable {
9+
let recommendedRoutines: [String: [RecommendedRoutineDTO]]
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//
2+
// RecommendedRoutineListResponseDTO.swift
3+
// DataSource
4+
//
5+
// Created by 최정인 on 7/15/25.
6+
//
7+
8+
struct RecommendedRoutineListResponseDTO: Decodable {
9+
let recommendedRoutines: [RecommendedRoutineDTO]
10+
}

Projects/DataSource/Sources/Endpoint/AuthEndpoint.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
// Created by 최정인 on 6/30/25.
66
//
77

8-
import Foundation
98
import Domain
109

1110
enum AuthEndpoint {

Projects/DataSource/Sources/Endpoint/OnboardingEndpoint.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
// Created by 최정인 on 7/15/25.
66
//
77

8-
import Foundation
9-
108
enum OnboardingEndpoint {
119
case registerOnboarding(choices: [String: String])
1210
case registerRecommendedRoutine(selectedRoutines: [Int])
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//
2+
// RecommendedRoutineEndpoint.swift
3+
// DataSource
4+
//
5+
// Created by 최정인 on 7/27/25.
6+
//
7+
8+
enum RecommendedRoutineEndpoint {
9+
case fetchRecommendedRoutines
10+
}
11+
12+
extension RecommendedRoutineEndpoint: Endpoint {
13+
var baseURL: String {
14+
return AppProperties.baseURL + "/api/v1/recommend-routines"
15+
}
16+
17+
var path: String {
18+
switch self {
19+
case .fetchRecommendedRoutines: baseURL
20+
}
21+
}
22+
23+
var method: HTTPMethod {
24+
return .get
25+
}
26+
27+
var headers: [String : String] {
28+
let headers: [String: String] = [
29+
"Content-Type": "application/json",
30+
"accept": "*/*"
31+
]
32+
return headers
33+
}
34+
35+
var queryParameters: [String : String] {
36+
return [:]
37+
}
38+
39+
var bodyParameters: [String : Any] {
40+
return [:]
41+
}
42+
43+
var isAuthorized: Bool {
44+
return true
45+
}
46+
}

Projects/DataSource/Sources/Persistence/KeychainStorage.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ final class KeychainStorage {
6161
}
6262

6363
private func baseQuery(for key: String) -> [String: Any] {
64-
var query: [String: Any] = [
64+
let query: [String: Any] = [
6565
kSecClass as String: kSecClassGenericPassword,
6666
kSecAttrService as String: service,
6767
kSecAttrAccount as String: key

Projects/DataSource/Sources/Repository/AuthRepository.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
// Created by 최정인 on 6/30/25.
66
//
77

8-
import Foundation
98
import Domain
10-
import Shared
11-
import KakaoSDKUser
9+
import Foundation
1210
import KakaoSDKAuth
11+
import KakaoSDKUser
12+
import Shared
1313

1414
final class AuthRepository: AuthRepositoryProtocol {
1515
private let networkService = NetworkService.shared
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//
2+
// RecommendedRoutineRepository.swift
3+
// DataSource
4+
//
5+
// Created by 최정인 on 7/27/25.
6+
//
7+
8+
import Domain
9+
10+
final class RecommendedRoutineRepository: RecommendedRoutineRepositoryProtocol {
11+
private let networkService = NetworkService.shared
12+
13+
func fetchRecommendedRoutines() async throws -> [RecommendedRoutineEntity] {
14+
let endpoint = RecommendedRoutineEndpoint.fetchRecommendedRoutines
15+
guard let response = try await networkService.request(endpoint: endpoint, type: RecommendedRoutineDictionaryResponseDTO.self)
16+
else { return [] }
17+
18+
var entities: [RecommendedRoutineEntity] = []
19+
for (category, recommendedRoutines) in response.recommendedRoutines {
20+
let recommendedRoutineEntity = recommendedRoutines.compactMap({ $0.toRecommendedRoutineEntity(category: category) })
21+
entities.append(contentsOf: recommendedRoutineEntity)
22+
}
23+
24+
return entities
25+
}
26+
}

0 commit comments

Comments
 (0)