From 5abab3c426aa78237de3e2752d8669dc5a5de4df Mon Sep 17 00:00:00 2001 From: opficdev Date: Thu, 19 Feb 2026 10:30:45 +0900 Subject: [PATCH 1/6] =?UTF-8?q?chore:=20AuthenticationDataResponse=20?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=96=B4=20=EA=B0=84=20=EC=9C=84=EC=B9=98=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DTO/AuthenticationDataResponse.swift} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename DevLog/{Data/DTO/AuthenticationData+.swift => Infra/DTO/AuthenticationDataResponse.swift} (95%) diff --git a/DevLog/Data/DTO/AuthenticationData+.swift b/DevLog/Infra/DTO/AuthenticationDataResponse.swift similarity index 95% rename from DevLog/Data/DTO/AuthenticationData+.swift rename to DevLog/Infra/DTO/AuthenticationDataResponse.swift index 6576b1b5..fb4f837b 100644 --- a/DevLog/Data/DTO/AuthenticationData+.swift +++ b/DevLog/Infra/DTO/AuthenticationDataResponse.swift @@ -1,5 +1,5 @@ // -// AuthenticationData+.swift +// AuthenticationDataResponse.swift // DevLog // // Created by 최윤진 on 11/2/25. From 1272f3ea1f396c719222e883ef0ee960c383a4d6 Mon Sep 17 00:00:00 2001 From: opficdev Date: Thu, 19 Feb 2026 10:39:59 +0900 Subject: [PATCH 2/6] =?UTF-8?q?style:=20Data=20=EB=A0=88=EC=9D=B4=EC=96=B4?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=A0=9C=EA=B1=B0=EB=90=98=EC=97=88?= =?UTF-8?q?=EA=B8=B0=EC=97=90=20toResponse=EB=A1=9C=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B0=8D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DevLog/Infra/Extension/FirebaseAuthUser.swift | 2 +- .../Infra/Service/SocialLogin/AppleAuthenticationService.swift | 2 +- .../Infra/Service/SocialLogin/GithubAuthenticationService.swift | 2 +- .../Infra/Service/SocialLogin/GoogleAuthenticationService.swift | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DevLog/Infra/Extension/FirebaseAuthUser.swift b/DevLog/Infra/Extension/FirebaseAuthUser.swift index e2ae7662..7cc98691 100644 --- a/DevLog/Infra/Extension/FirebaseAuthUser.swift +++ b/DevLog/Infra/Extension/FirebaseAuthUser.swift @@ -9,7 +9,7 @@ import Foundation import FirebaseAuth extension FirebaseAuth.User { - func toData( + func toResponse( providerID: AuthProviderID, fcmToken: String, accessToken: String? = nil diff --git a/DevLog/Infra/Service/SocialLogin/AppleAuthenticationService.swift b/DevLog/Infra/Service/SocialLogin/AppleAuthenticationService.swift index 21848331..2a2f84ad 100644 --- a/DevLog/Infra/Service/SocialLogin/AppleAuthenticationService.swift +++ b/DevLog/Infra/Service/SocialLogin/AppleAuthenticationService.swift @@ -81,7 +81,7 @@ final class AppleAuthenticationService: AuthenticationService { let fcmToken = try await messaging.token() logger.info("Successfully signed in with Apple") - return result.user.toData(providerID: .apple, fcmToken: fcmToken) + return result.user.toResponse(providerID: .apple, fcmToken: fcmToken) } catch { logger.error("Failed to sign in with Apple", error: error) throw error diff --git a/DevLog/Infra/Service/SocialLogin/GithubAuthenticationService.swift b/DevLog/Infra/Service/SocialLogin/GithubAuthenticationService.swift index 22e18dd2..52c5f41b 100644 --- a/DevLog/Infra/Service/SocialLogin/GithubAuthenticationService.swift +++ b/DevLog/Infra/Service/SocialLogin/GithubAuthenticationService.swift @@ -56,7 +56,7 @@ final class GithubAuthenticationService: NSObject, AuthenticationService { let fcmToken = try await messaging.token() logger.info("Successfully signed in with GitHub") - return result.user.toData( + return result.user.toResponse( providerID: .gitHub, fcmToken: fcmToken, accessToken: accessToken diff --git a/DevLog/Infra/Service/SocialLogin/GoogleAuthenticationService.swift b/DevLog/Infra/Service/SocialLogin/GoogleAuthenticationService.swift index 55c4b1f4..13709cc5 100644 --- a/DevLog/Infra/Service/SocialLogin/GoogleAuthenticationService.swift +++ b/DevLog/Infra/Service/SocialLogin/GoogleAuthenticationService.swift @@ -54,7 +54,7 @@ final class GoogleAuthenticationService: AuthenticationService { let fcmToken = try await messaging.token() logger.info("Successfully signed in with Google") - return result.user.toData(providerID: .google, fcmToken: fcmToken) + return result.user.toResponse(providerID: .google, fcmToken: fcmToken) } catch { logger.error("Failed to sign in with Google", error: error) throw error From 889ec8eed19d3ef2d24ba9bef4a1a609d4d51426 Mon Sep 17 00:00:00 2001 From: opficdev Date: Thu, 19 Feb 2026 10:42:10 +0900 Subject: [PATCH 3/6] chore: AuthenticationDataResponse -> AuthDataResponse --- DevLog/Data/Protocol/AuthenticationService.swift | 2 +- DevLog/Data/Repository/AuthenticationRepositoryImpl.swift | 2 +- ...uthenticationDataResponse.swift => AuthDataResponse.swift} | 4 ++-- DevLog/Infra/Extension/FirebaseAuthUser.swift | 4 ++-- .../Service/SocialLogin/AppleAuthenticationService.swift | 2 +- .../Service/SocialLogin/GithubAuthenticationService.swift | 2 +- .../Service/SocialLogin/GoogleAuthenticationService.swift | 2 +- DevLog/Infra/Service/UserService.swift | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) rename DevLog/Infra/DTO/{AuthenticationDataResponse.swift => AuthDataResponse.swift} (90%) diff --git a/DevLog/Data/Protocol/AuthenticationService.swift b/DevLog/Data/Protocol/AuthenticationService.swift index aa106235..51359a50 100644 --- a/DevLog/Data/Protocol/AuthenticationService.swift +++ b/DevLog/Data/Protocol/AuthenticationService.swift @@ -8,7 +8,7 @@ import Foundation protocol AuthenticationService { - func signIn() async throws -> AuthenticationDataResponse + func signIn() async throws -> AuthDataResponse func signOut(_ uid: String) async throws func deleteAuth(_ uid: String) async throws func link(uid: String, email: String) async throws diff --git a/DevLog/Data/Repository/AuthenticationRepositoryImpl.swift b/DevLog/Data/Repository/AuthenticationRepositoryImpl.swift index 3b2e01e3..54fc5681 100644 --- a/DevLog/Data/Repository/AuthenticationRepositoryImpl.swift +++ b/DevLog/Data/Repository/AuthenticationRepositoryImpl.swift @@ -27,7 +27,7 @@ final class AuthenticationRepositoryImpl: AuthenticationRepository { } func signIn(_ provider: AuthProvider) async throws { - let response: AuthenticationDataResponse + let response: AuthDataResponse switch provider { case .apple: response = try await appleAuthService.signIn() diff --git a/DevLog/Infra/DTO/AuthenticationDataResponse.swift b/DevLog/Infra/DTO/AuthDataResponse.swift similarity index 90% rename from DevLog/Infra/DTO/AuthenticationDataResponse.swift rename to DevLog/Infra/DTO/AuthDataResponse.swift index fb4f837b..043b635d 100644 --- a/DevLog/Infra/DTO/AuthenticationDataResponse.swift +++ b/DevLog/Infra/DTO/AuthDataResponse.swift @@ -1,5 +1,5 @@ // -// AuthenticationDataResponse.swift +// AuthDataResponse.swift // DevLog // // Created by 최윤진 on 11/2/25. @@ -7,7 +7,7 @@ import Foundation -struct AuthenticationDataResponse { +struct AuthDataResponse { let uid: String let displayName: String? let email: String? diff --git a/DevLog/Infra/Extension/FirebaseAuthUser.swift b/DevLog/Infra/Extension/FirebaseAuthUser.swift index 7cc98691..a2b3e2d2 100644 --- a/DevLog/Infra/Extension/FirebaseAuthUser.swift +++ b/DevLog/Infra/Extension/FirebaseAuthUser.swift @@ -13,8 +13,8 @@ extension FirebaseAuth.User { providerID: AuthProviderID, fcmToken: String, accessToken: String? = nil - ) -> AuthenticationDataResponse { - return AuthenticationDataResponse( + ) -> AuthDataResponse { + return AuthDataResponse( uid: self.uid, displayName: self.displayName, email: self.email, diff --git a/DevLog/Infra/Service/SocialLogin/AppleAuthenticationService.swift b/DevLog/Infra/Service/SocialLogin/AppleAuthenticationService.swift index 2a2f84ad..c8b9df69 100644 --- a/DevLog/Infra/Service/SocialLogin/AppleAuthenticationService.swift +++ b/DevLog/Infra/Service/SocialLogin/AppleAuthenticationService.swift @@ -22,7 +22,7 @@ final class AppleAuthenticationService: AuthenticationService { private let providerID = AuthProviderID.apple private let logger = Logger(category: "AppleAuthService") - func signIn() async throws -> AuthenticationDataResponse { + func signIn() async throws -> AuthDataResponse { logger.info("Starting Apple sign in") do { diff --git a/DevLog/Infra/Service/SocialLogin/GithubAuthenticationService.swift b/DevLog/Infra/Service/SocialLogin/GithubAuthenticationService.swift index 52c5f41b..dfc55e09 100644 --- a/DevLog/Infra/Service/SocialLogin/GithubAuthenticationService.swift +++ b/DevLog/Infra/Service/SocialLogin/GithubAuthenticationService.swift @@ -21,7 +21,7 @@ final class GithubAuthenticationService: NSObject, AuthenticationService { private let provider = TopViewControllerProvider() private let logger = Logger(category: "GithubAuthService") - func signIn() async throws -> AuthenticationDataResponse { + func signIn() async throws -> AuthDataResponse { logger.info("Starting GitHub sign in") do { diff --git a/DevLog/Infra/Service/SocialLogin/GoogleAuthenticationService.swift b/DevLog/Infra/Service/SocialLogin/GoogleAuthenticationService.swift index 13709cc5..e297eff6 100644 --- a/DevLog/Infra/Service/SocialLogin/GoogleAuthenticationService.swift +++ b/DevLog/Infra/Service/SocialLogin/GoogleAuthenticationService.swift @@ -21,7 +21,7 @@ final class GoogleAuthenticationService: AuthenticationService { private let logger = Logger(category: "GoogleAuthService") @MainActor - func signIn() async throws -> AuthenticationDataResponse { + func signIn() async throws -> AuthDataResponse { logger.info("Starting Google sign in") guard let topViewController = provider.topViewController() else { diff --git a/DevLog/Infra/Service/UserService.swift b/DevLog/Infra/Service/UserService.swift index b7116f70..63250d16 100644 --- a/DevLog/Infra/Service/UserService.swift +++ b/DevLog/Infra/Service/UserService.swift @@ -15,7 +15,7 @@ final class UserService { private let logger = Logger(category: "UserService") // 유저를 Firestore에 저장 및 업데이트 - func upsertUser(_ response: AuthenticationDataResponse) async throws { + func upsertUser(_ response: AuthDataResponse) async throws { logger.info("Upserting user with provider: \(response.providerID)") guard let user = Auth.auth().currentUser else { From ea0b8bef0ae36b72b80458c9960233c3f371e776 Mon Sep 17 00:00:00 2001 From: opficdev Date: Thu, 19 Feb 2026 11:13:26 +0900 Subject: [PATCH 4/6] =?UTF-8?q?feat:=20TodoDTO=EC=99=80=20=EB=A7=A4?= =?UTF-8?q?=ED=8D=BC=EB=A5=BC=20=EB=B6=84=EB=A6=AC=20=EB=B0=8F=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DevLog/Data/Common/Error+.swift | 21 ++++++++ DevLog/Data/Mapper/TodoMapping.swift | 51 ++++++++++++++++++ .../Data/Repository/TodoRepositoryImpl.swift | 10 ++-- .../Todo+.swift => Infra/DTO/TodoDTO.swift} | 52 +++++-------------- 4 files changed, 92 insertions(+), 42 deletions(-) create mode 100644 DevLog/Data/Mapper/TodoMapping.swift rename DevLog/{Data/DTO/Todo+.swift => Infra/DTO/TodoDTO.swift} (55%) diff --git a/DevLog/Data/Common/Error+.swift b/DevLog/Data/Common/Error+.swift index 33a4d0a6..cda9fc17 100644 --- a/DevLog/Data/Common/Error+.swift +++ b/DevLog/Data/Common/Error+.swift @@ -27,3 +27,24 @@ enum FirestoreError: Error, LocalizedError { enum UIError: Error { case notFoundTopViewController } + +enum DataError: Error { + case invalidData(context: String) + + private static let logger = Logger(category: "DataError") + + static func invalidData( + _ context: String, + file: String = #file, + function: String = #function, + line: Int = #line + ) -> DataError { + logger.error( + "Invalid data: \(context)", + file: file, + function: function, + line: line + ) + return .invalidData(context: context) + } +} diff --git a/DevLog/Data/Mapper/TodoMapping.swift b/DevLog/Data/Mapper/TodoMapping.swift new file mode 100644 index 00000000..5fd5f876 --- /dev/null +++ b/DevLog/Data/Mapper/TodoMapping.swift @@ -0,0 +1,51 @@ +// +// TodoMapping.swift +// DevLog +// +// Created by 최윤진 on 2/19/26. +// + +import Foundation + +extension TodoRequest { + static func fromDomain(_ entity: Todo) -> Self { + TodoRequest( + id: entity.id, + isPinned: entity.isPinned, + isCompleted: entity.isCompleted, + isChecked: entity.isChecked, + title: entity.title, + content: entity.content, + createdAt: entity.createdAt, + updatedAt: entity.updatedAt, + dueDate: entity.dueDate, + tags: entity.tags, + kind: entity.kind + ) + } +} + +extension TodoResponse { + func toDomain() throws -> Todo { + guard let id = self.id else { + throw DataError.invalidData("TodoResponse.id is nil") + } + guard let kind = TodoKind(rawValue: self.kind) else { + throw DataError.invalidData("TodoResponse.kind is invalid: \(self.kind)") + } + + return Todo( + id: id, + isPinned: self.isPinned, + isCompleted: self.isCompleted, + isChecked: self.isChecked, + title: self.title, + content: self.content, + createdAt: self.createdAt, + updatedAt: self.updatedAt, + dueDate: self.dueDate, + tags: self.tags, + kind: kind + ) + } +} diff --git a/DevLog/Data/Repository/TodoRepositoryImpl.swift b/DevLog/Data/Repository/TodoRepositoryImpl.swift index 0cd3c374..a7aed2a5 100644 --- a/DevLog/Data/Repository/TodoRepositoryImpl.swift +++ b/DevLog/Data/Repository/TodoRepositoryImpl.swift @@ -16,17 +16,21 @@ final class TodoRepositoryImpl: TodoRepository { func fetchTodos(_ kind: TodoKind) async throws -> [Todo] { let response = try await todoService.fetchTodos(kind: kind) - return response.map { $0.toDomain() } + return try response.map { item in + try item.toDomain() + } } func fetchPinnedTodos() async throws -> [Todo] { let response = try await todoService.fetchPinnedTodos() - return response.map { $0.toDomain() } + return try response.map { item in + try item.toDomain() + } } func fetchTodo(_ todoID: String) async throws -> Todo { let response = try await todoService.fetchTodo(todoID: todoID) - return response.toDomain() + return try response.toDomain() } func upsertTodo(_ todo: Todo) async throws { diff --git a/DevLog/Data/DTO/Todo+.swift b/DevLog/Infra/DTO/TodoDTO.swift similarity index 55% rename from DevLog/Data/DTO/Todo+.swift rename to DevLog/Infra/DTO/TodoDTO.swift index d289057b..f45b0fc0 100644 --- a/DevLog/Data/DTO/Todo+.swift +++ b/DevLog/Infra/DTO/TodoDTO.swift @@ -1,5 +1,5 @@ // -// Todo+.swift +// TodoDTO.swift // DevLog // // Created by 최윤진 on 12/14/25. @@ -21,21 +21,6 @@ struct TodoRequest: Dictionaryable { let tags: [String] let kind: TodoKind - static func fromDomain(_ entity: Todo) -> Self { - TodoRequest( - id: entity.id, - isPinned: entity.isPinned, - isCompleted: entity.isCompleted, - isChecked: entity.isChecked, - title: entity.title, - content: entity.content, - createdAt: entity.createdAt, - updatedAt: entity.updatedAt, - dueDate: entity.dueDate, - tags: entity.tags, - kind: entity.kind - ) - } } struct TodoResponse: Decodable { @@ -45,9 +30,9 @@ struct TodoResponse: Decodable { let isChecked: Bool let title: String let content: String - let createdAt: Timestamp - let updatedAt: Timestamp - let dueDate: Timestamp? + let createdAt: Date + let updatedAt: Date + let dueDate: Date? let tags: [String] let kind: String @@ -68,8 +53,8 @@ struct TodoResponse: Decodable { let isChecked = data["isChecked"] as? Bool, let title = data["title"] as? String, let content = data["content"] as? String, - let createdAt = data["createdAt"] as? Timestamp, - let updatedAt = data["updatedAt"] as? Timestamp, + let createdAtTimestamp = data["createdAt"] as? Timestamp, + let updatedAtTimestamp = data["updatedAt"] as? Timestamp, let tags = data["tags"] as? [String], let kind = data["kind"] as? String else { return nil @@ -80,26 +65,15 @@ struct TodoResponse: Decodable { self.isChecked = isChecked self.title = title self.content = content - self.createdAt = createdAt - self.updatedAt = updatedAt - self.dueDate = data["dueDate"] as? Timestamp + self.createdAt = createdAtTimestamp.dateValue() + self.updatedAt = updatedAtTimestamp.dateValue() + if let dueDateTimestamp = data["dueDate"] as? Timestamp { + self.dueDate = dueDateTimestamp.dateValue() + } else { + self.dueDate = nil + } self.tags = tags self.kind = kind } - func toDomain() -> Todo { - Todo( - id: self.id == nil ? UUID().uuidString : self.id!, - isPinned: self.isPinned, - isCompleted: self.isCompleted, - isChecked: self.isChecked, - title: self.title, - content: self.content, - createdAt: self.createdAt.dateValue(), - updatedAt: self.updatedAt.dateValue(), - dueDate: self.dueDate?.dateValue(), - tags: self.tags, - kind: TodoKind(rawValue: self.kind) ?? .etc - ) - } } From 7a73c5cef49be499fee5ea538827f3ea572f5391 Mon Sep 17 00:00:00 2001 From: opficdev Date: Thu, 19 Feb 2026 11:21:18 +0900 Subject: [PATCH 5/6] =?UTF-8?q?refactor:=20UserProfileResponse=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DevLog/Data/DTO/UserProfile+.swift | 24 --------------------- DevLog/Data/Mapper/TodoMapping.swift | 2 -- DevLog/Data/Mapper/UserProfileMapping.swift | 17 +++++++++++++++ DevLog/Infra/DTO/UserProfileResponse.swift | 15 +++++++++++++ 4 files changed, 32 insertions(+), 26 deletions(-) delete mode 100644 DevLog/Data/DTO/UserProfile+.swift create mode 100644 DevLog/Data/Mapper/UserProfileMapping.swift create mode 100644 DevLog/Infra/DTO/UserProfileResponse.swift diff --git a/DevLog/Data/DTO/UserProfile+.swift b/DevLog/Data/DTO/UserProfile+.swift deleted file mode 100644 index ec58ed84..00000000 --- a/DevLog/Data/DTO/UserProfile+.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// UserProfile+.swift -// DevLog -// -// Created by 최윤진 on 1/10/26. -// - -import Foundation - -struct UserProfileResponse: Decodable { - let name: String - let email: String - let statusMessage: String - let avatarURL: URL? - - func toDomain() -> UserProfile { - return UserProfile( - name: self.name, - email: self.email, - statusMessage: self.statusMessage, - avatarURL: self.avatarURL - ) - } -} diff --git a/DevLog/Data/Mapper/TodoMapping.swift b/DevLog/Data/Mapper/TodoMapping.swift index 5fd5f876..8032c532 100644 --- a/DevLog/Data/Mapper/TodoMapping.swift +++ b/DevLog/Data/Mapper/TodoMapping.swift @@ -5,8 +5,6 @@ // Created by 최윤진 on 2/19/26. // -import Foundation - extension TodoRequest { static func fromDomain(_ entity: Todo) -> Self { TodoRequest( diff --git a/DevLog/Data/Mapper/UserProfileMapping.swift b/DevLog/Data/Mapper/UserProfileMapping.swift new file mode 100644 index 00000000..b8259bf7 --- /dev/null +++ b/DevLog/Data/Mapper/UserProfileMapping.swift @@ -0,0 +1,17 @@ +// +// UserProfileMapping.swift +// DevLog +// +// Created by 최윤진 on 2/19/26. +// + +extension UserProfileResponse { + func toDomain() -> UserProfile { + UserProfile( + name: self.name, + email: self.email, + statusMessage: self.statusMessage, + avatarURL: self.avatarURL + ) + } +} diff --git a/DevLog/Infra/DTO/UserProfileResponse.swift b/DevLog/Infra/DTO/UserProfileResponse.swift new file mode 100644 index 00000000..0983db6d --- /dev/null +++ b/DevLog/Infra/DTO/UserProfileResponse.swift @@ -0,0 +1,15 @@ +// +// UserProfileResponse.swift +// DevLog +// +// Created by 최윤진 on 1/10/26. +// + +import Foundation + +struct UserProfileResponse: Decodable { + let name: String + let email: String + let statusMessage: String + let avatarURL: URL? +} From 793c58e7a828a9190ebe97f29118f75b8c36fa1b Mon Sep 17 00:00:00 2001 From: opficdev Date: Thu, 19 Feb 2026 11:59:01 +0900 Subject: [PATCH 6/6] =?UTF-8?q?refactor:=20WebPageMetadata=EC=9D=84=20WebP?= =?UTF-8?q?age=EB=A1=9C=20=EB=8C=80=EC=B2=B4=20=EB=B0=8F=20=EB=A7=B5?= =?UTF-8?q?=ED=8D=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WebPageMapping.swift} | 13 +++---------- .../Data/Repository/WebPageRepositoryImpl.swift | 15 ++++++++------- DevLog/Domain/Protocol/WebPageRepository.swift | 4 ++-- .../WebPage/Fetch/FetchWebPagesUseCaseImpl.swift | 2 +- .../WebPage/Upsert/AddWebPageUseCaseImpl.swift | 2 +- DevLog/Infra/DTO/WebPageResponse.swift | 7 ++++++- DevLog/Infra/DTO/WebPageURLResponse.swift | 10 ++++++++++ DevLog/Infra/Service/WebPageMetadataService.swift | 4 ++-- DevLog/Infra/Service/WebPageService.swift | 4 ++-- 9 files changed, 35 insertions(+), 26 deletions(-) rename DevLog/Data/{DTO/WebPageMetadata.swift => Mapper/WebPageMapping.swift} (50%) create mode 100644 DevLog/Infra/DTO/WebPageURLResponse.swift diff --git a/DevLog/Data/DTO/WebPageMetadata.swift b/DevLog/Data/Mapper/WebPageMapping.swift similarity index 50% rename from DevLog/Data/DTO/WebPageMetadata.swift rename to DevLog/Data/Mapper/WebPageMapping.swift index 06c43f78..1c3e03f4 100644 --- a/DevLog/Data/DTO/WebPageMetadata.swift +++ b/DevLog/Data/Mapper/WebPageMapping.swift @@ -1,18 +1,11 @@ // -// WebPageMetadata.swift +// WebPageMapping.swift // DevLog // -// Created by 최윤진 on 2/9/26. +// Created by 최윤진 on 2/19/26. // -import Foundation - -struct WebPageMetadata: Hashable { - let title: String? - let url: URL - let displayURL: URL - let imageURL: URL? - +extension WebPageResponse { func toDomain() -> WebPage { WebPage( title: title, diff --git a/DevLog/Data/Repository/WebPageRepositoryImpl.swift b/DevLog/Data/Repository/WebPageRepositoryImpl.swift index f480cb98..fdeb5d08 100644 --- a/DevLog/Data/Repository/WebPageRepositoryImpl.swift +++ b/DevLog/Data/Repository/WebPageRepositoryImpl.swift @@ -17,11 +17,11 @@ final class WebPageRepositoryImpl: WebPageRepository { self.metadataService = metadataService } - func fetch() async throws -> [WebPageMetadata] { + func fetch() async throws -> [WebPage] { let responses = try await webPageService.fetchWebPages() let indexedResponses = responses.enumerated().map { ($0.offset, $0.element) } - return try await withThrowingTaskGroup(of: (Int, WebPageMetadata?).self) { group in + return try await withThrowingTaskGroup(of: (Int, WebPageResponse?).self) { group in for (index, response) in indexedResponses { group.addTask { let metadata = try? await self.metadataService.fetchMetadata(from: response) @@ -29,19 +29,20 @@ final class WebPageRepositoryImpl: WebPageRepository { } } - var results: [WebPageMetadata?] = Array(repeating: nil, count: responses.count) + var results: [WebPageResponse?] = Array(repeating: nil, count: responses.count) for try await (index, metadata) in group { results[index] = metadata } - return results.compactMap { $0 } + return results.compactMap { $0?.toDomain() } } } - func upsert(_ urlString: String) async throws -> WebPageMetadata { + func upsert(_ urlString: String) async throws -> WebPage { try await webPageService.upsertWebPage(urlString) - let response = WebPageResponse(urlString: urlString) - return try await metadataService.fetchMetadata(from: response) + let response = WebPageURLResponse(urlString: urlString) + let metadata = try await metadataService.fetchMetadata(from: response) + return metadata.toDomain() } func delete(_ urlString: String) async throws { diff --git a/DevLog/Domain/Protocol/WebPageRepository.swift b/DevLog/Domain/Protocol/WebPageRepository.swift index bd9dea72..721aee98 100644 --- a/DevLog/Domain/Protocol/WebPageRepository.swift +++ b/DevLog/Domain/Protocol/WebPageRepository.swift @@ -6,7 +6,7 @@ // protocol WebPageRepository { - func fetch() async throws -> [WebPageMetadata] - func upsert(_ urlString: String) async throws -> WebPageMetadata + func fetch() async throws -> [WebPage] + func upsert(_ urlString: String) async throws -> WebPage func delete(_ urlString: String) async throws } diff --git a/DevLog/Domain/UseCase/WebPage/Fetch/FetchWebPagesUseCaseImpl.swift b/DevLog/Domain/UseCase/WebPage/Fetch/FetchWebPagesUseCaseImpl.swift index 8ac986c9..d06c7a4c 100644 --- a/DevLog/Domain/UseCase/WebPage/Fetch/FetchWebPagesUseCaseImpl.swift +++ b/DevLog/Domain/UseCase/WebPage/Fetch/FetchWebPagesUseCaseImpl.swift @@ -13,6 +13,6 @@ final class FetchWebPagesUseCaseImpl: FetchWebPagesUseCase { } func execute() async throws -> [WebPage] { - return try await repository.fetch().map { $0.toDomain() } + try await repository.fetch() } } diff --git a/DevLog/Domain/UseCase/WebPage/Upsert/AddWebPageUseCaseImpl.swift b/DevLog/Domain/UseCase/WebPage/Upsert/AddWebPageUseCaseImpl.swift index d4f5fff7..9aa3404e 100644 --- a/DevLog/Domain/UseCase/WebPage/Upsert/AddWebPageUseCaseImpl.swift +++ b/DevLog/Domain/UseCase/WebPage/Upsert/AddWebPageUseCaseImpl.swift @@ -13,6 +13,6 @@ final class AddWebPageUseCaseImpl: AddWebPageUseCase { } func execute(_ urlString: String) async throws -> WebPage { - try await repository.upsert(urlString).toDomain() + try await repository.upsert(urlString) } } diff --git a/DevLog/Infra/DTO/WebPageResponse.swift b/DevLog/Infra/DTO/WebPageResponse.swift index c940437b..5bdbd044 100644 --- a/DevLog/Infra/DTO/WebPageResponse.swift +++ b/DevLog/Infra/DTO/WebPageResponse.swift @@ -5,6 +5,11 @@ // Created by 최윤진 on 2/9/26. // +import Foundation + struct WebPageResponse { - let urlString: String + let title: String? + let url: URL + let displayURL: URL + let imageURL: URL? } diff --git a/DevLog/Infra/DTO/WebPageURLResponse.swift b/DevLog/Infra/DTO/WebPageURLResponse.swift new file mode 100644 index 00000000..0716964e --- /dev/null +++ b/DevLog/Infra/DTO/WebPageURLResponse.swift @@ -0,0 +1,10 @@ +// +// WebPageURLResponse.swift +// DevLog +// +// Created by 최윤진 on 2/9/26. +// + +struct WebPageURLResponse { + let urlString: String +} diff --git a/DevLog/Infra/Service/WebPageMetadataService.swift b/DevLog/Infra/Service/WebPageMetadataService.swift index 9f08b975..74947676 100644 --- a/DevLog/Infra/Service/WebPageMetadataService.swift +++ b/DevLog/Infra/Service/WebPageMetadataService.swift @@ -10,7 +10,7 @@ import LinkPresentation final class WebPageMetadataService { private let logger = Logger(category: "WebPageMetadataService") - func fetchMetadata(from response: WebPageResponse) async throws -> WebPageMetadata { + func fetchMetadata(from response: WebPageURLResponse) async throws -> WebPageResponse { logger.info("Fetching metadata for URL: \(response.urlString)") guard let url = URL(string: response.urlString) else { @@ -26,7 +26,7 @@ final class WebPageMetadataService { let imageURL = try await extractImageURL(from: metadata.imageProvider, url: url) logger.info("Successfully fetched metadata for: \(metadata.title ?? "Unknown")") - return WebPageMetadata( + return WebPageResponse( title: metadata.title, url: url, displayURL: metadata.url ?? url, diff --git a/DevLog/Infra/Service/WebPageService.swift b/DevLog/Infra/Service/WebPageService.swift index ab6d7347..763bda8f 100644 --- a/DevLog/Infra/Service/WebPageService.swift +++ b/DevLog/Infra/Service/WebPageService.swift @@ -13,7 +13,7 @@ final class WebPageService { private let logger = Logger(category: "WebPageService") /// 저장한 웹페이지를 모두 불러옴 - func fetchWebPages() async throws -> [WebPageResponse] { + func fetchWebPages() async throws -> [WebPageURLResponse] { logger.info("Fetching web pages") guard let uid = Auth.auth().currentUser?.uid else { @@ -28,7 +28,7 @@ final class WebPageService { if doc.exists, let data = doc.data() { if let webPageInfos = data["webPageInfos"] as? [String] { logger.info("Successfully fetched \(webPageInfos.count) web pages") - return webPageInfos.map { WebPageResponse(urlString: $0) } + return webPageInfos.map { WebPageURLResponse(urlString: $0) } } } logger.error("Web page data not found or invalid")