diff --git a/Projects/App/Sources/Application/NeedleGenerated.swift b/Projects/App/Sources/Application/NeedleGenerated.swift index 2716594e..04504c8d 100644 --- a/Projects/App/Sources/Application/NeedleGenerated.swift +++ b/Projects/App/Sources/Application/NeedleGenerated.swift @@ -92,6 +92,9 @@ private class GSMAuthenticationDependencydf257bda3051cc91534fProvider: GSMAuthen var authenticationDomainBuildable: any AuthenticationDomainBuildable { return appComponent.authenticationDomainBuildable } + var fileDomainBuildable: any FileDomainBuildable { + return appComponent.fileDomainBuildable + } private let appComponent: AppComponent init(appComponent: AppComponent) { self.appComponent = appComponent @@ -471,33 +474,33 @@ extension JwtStoreComponent: Registration { extension AppComponent: Registration { public func registerItems() { - localTable["rootComponent-RootComponent"] = { [unowned self] in self.rootComponent as Any } - localTable["signinBuildable-any SigninBuildable"] = { [unowned self] in self.signinBuildable as Any } - localTable["inputInformationBuildable-any InputInformationBuildable"] = { [unowned self] in self.inputInformationBuildable as Any } - localTable["inputProfileInfoBuildable-any InputProfileInfoBuildable"] = { [unowned self] in self.inputProfileInfoBuildable as Any } - localTable["inputSchoolLifeInfoBuildable-any InputSchoolLifeInfoBuildable"] = { [unowned self] in self.inputSchoolLifeInfoBuildable as Any } - localTable["inputWorkInfoBuildable-any InputWorkInfoBuildable"] = { [unowned self] in self.inputWorkInfoBuildable as Any } - localTable["inputMilitaryInfoBuildable-any InputMilitaryInfoBuildable"] = { [unowned self] in self.inputMilitaryInfoBuildable as Any } - localTable["inputCertificateInfoBuildable-any InputCertificateInfoBuildable"] = { [unowned self] in self.inputCertificateInfoBuildable as Any } - localTable["inputLanguageInfoBuildable-any InputLanguageInfoBuildable"] = { [unowned self] in self.inputLanguageInfoBuildable as Any } - localTable["inputPrizeInfoBuildable-any InputPrizeInfoBuildable"] = { [unowned self] in self.inputPrizeInfoBuildable as Any } - localTable["inputProjectInfoBuildable-any InputProjectInfoBuildable"] = { [unowned self] in self.inputProjectInfoBuildable as Any } - localTable["mainBuildable-any MainBuildable"] = { [unowned self] in self.mainBuildable as Any } - localTable["myPageBuildable-any MyPageBuildable"] = { [unowned self] in self.myPageBuildable as Any } - localTable["techStackAppendBuildable-any TechStackAppendBuildable"] = { [unowned self] in self.techStackAppendBuildable as Any } - localTable["studentDetailBuildable-any StudentDetailBuildable"] = { [unowned self] in self.studentDetailBuildable as Any } - localTable["filterBuildable-any FilterBuildable"] = { [unowned self] in self.filterBuildable as Any } - localTable["splashBuildable-any SplashBuildable"] = { [unowned self] in self.splashBuildable as Any } - localTable["gsmAuthenticationBuildable-any GSMAuthenticationBuildable"] = { [unowned self] in self.gsmAuthenticationBuildable as Any } - localTable["authDomainBuildable-any AuthDomainBuildable"] = { [unowned self] in self.authDomainBuildable as Any } - localTable["studentDomainBuildable-any StudentDomainBuildable"] = { [unowned self] in self.studentDomainBuildable as Any } - localTable["majorDomainBuildable-any MajorDomainBuildable"] = { [unowned self] in self.majorDomainBuildable as Any } - localTable["fileDomainBuildable-any FileDomainBuildable"] = { [unowned self] in self.fileDomainBuildable as Any } - localTable["userDomainBuildable-any UserDomainBuildable"] = { [unowned self] in self.userDomainBuildable as Any } - localTable["techStackDomainBuildable-any TechStackDomainBuildable"] = { [unowned self] in self.techStackDomainBuildable as Any } - localTable["jwtStoreBuildable-any JwtStoreBuildable"] = { [unowned self] in self.jwtStoreBuildable as Any } - localTable["keychainBuildable-any KeychainBuildable"] = { [unowned self] in self.keychainBuildable as Any } - localTable["authenticationDomainBuildable-any AuthenticationDomainBuildable"] = { [unowned self] in self.authenticationDomainBuildable as Any } + localTable["rootComponent-RootComponent"] = { self.rootComponent as Any } + localTable["signinBuildable-any SigninBuildable"] = { self.signinBuildable as Any } + localTable["inputInformationBuildable-any InputInformationBuildable"] = { self.inputInformationBuildable as Any } + localTable["inputProfileInfoBuildable-any InputProfileInfoBuildable"] = { self.inputProfileInfoBuildable as Any } + localTable["inputSchoolLifeInfoBuildable-any InputSchoolLifeInfoBuildable"] = { self.inputSchoolLifeInfoBuildable as Any } + localTable["inputWorkInfoBuildable-any InputWorkInfoBuildable"] = { self.inputWorkInfoBuildable as Any } + localTable["inputMilitaryInfoBuildable-any InputMilitaryInfoBuildable"] = { self.inputMilitaryInfoBuildable as Any } + localTable["inputCertificateInfoBuildable-any InputCertificateInfoBuildable"] = { self.inputCertificateInfoBuildable as Any } + localTable["inputLanguageInfoBuildable-any InputLanguageInfoBuildable"] = { self.inputLanguageInfoBuildable as Any } + localTable["inputPrizeInfoBuildable-any InputPrizeInfoBuildable"] = { self.inputPrizeInfoBuildable as Any } + localTable["inputProjectInfoBuildable-any InputProjectInfoBuildable"] = { self.inputProjectInfoBuildable as Any } + localTable["mainBuildable-any MainBuildable"] = { self.mainBuildable as Any } + localTable["myPageBuildable-any MyPageBuildable"] = { self.myPageBuildable as Any } + localTable["techStackAppendBuildable-any TechStackAppendBuildable"] = { self.techStackAppendBuildable as Any } + localTable["studentDetailBuildable-any StudentDetailBuildable"] = { self.studentDetailBuildable as Any } + localTable["filterBuildable-any FilterBuildable"] = { self.filterBuildable as Any } + localTable["splashBuildable-any SplashBuildable"] = { self.splashBuildable as Any } + localTable["gsmAuthenticationBuildable-any GSMAuthenticationBuildable"] = { self.gsmAuthenticationBuildable as Any } + localTable["authDomainBuildable-any AuthDomainBuildable"] = { self.authDomainBuildable as Any } + localTable["studentDomainBuildable-any StudentDomainBuildable"] = { self.studentDomainBuildable as Any } + localTable["majorDomainBuildable-any MajorDomainBuildable"] = { self.majorDomainBuildable as Any } + localTable["fileDomainBuildable-any FileDomainBuildable"] = { self.fileDomainBuildable as Any } + localTable["userDomainBuildable-any UserDomainBuildable"] = { self.userDomainBuildable as Any } + localTable["techStackDomainBuildable-any TechStackDomainBuildable"] = { self.techStackDomainBuildable as Any } + localTable["jwtStoreBuildable-any JwtStoreBuildable"] = { self.jwtStoreBuildable as Any } + localTable["keychainBuildable-any KeychainBuildable"] = { self.keychainBuildable as Any } + localTable["authenticationDomainBuildable-any AuthenticationDomainBuildable"] = { self.authenticationDomainBuildable as Any } } } extension KeychainComponent: Registration { @@ -508,6 +511,7 @@ extension KeychainComponent: Registration { extension GSMAuthenticationComponent: Registration { public func registerItems() { keyPathToName[\GSMAuthenticationDependency.authenticationDomainBuildable] = "authenticationDomainBuildable-any AuthenticationDomainBuildable" + keyPathToName[\GSMAuthenticationDependency.fileDomainBuildable] = "fileDomainBuildable-any FileDomainBuildable" } } extension SplashComponent: Registration { @@ -665,7 +669,7 @@ private func registerProviderFactory(_ componentPath: String, _ factory: @escapi #if !NEEDLE_DYNAMIC -@inline(never) private func register1() { +private func register1() { registerProviderFactory("^->AppComponent->JwtStoreComponent", factoryb27d5aae1eb7e73575a6f47b58f8f304c97af4d5) registerProviderFactory("^->AppComponent", factoryEmptyDependencyProvider) registerProviderFactory("^->AppComponent->KeychainComponent", factoryEmptyDependencyProvider) diff --git a/Projects/Core/DesignSystem/Sources/File/SMSFileField.swift b/Projects/Core/DesignSystem/Sources/File/SMSFileField.swift index f02b6442..9132ed12 100644 --- a/Projects/Core/DesignSystem/Sources/File/SMSFileField.swift +++ b/Projects/Core/DesignSystem/Sources/File/SMSFileField.swift @@ -39,10 +39,6 @@ public struct SMSFileField: View { isOnClear: false ) .disabled(true) - .overlay(alignment: .trailing) { - SMSIcon(.folder) - .padding(.trailing, 12) - } .simultaneousGesture( TapGesture() .onEnded { diff --git a/Projects/Domain/FileDomain/Interface/DI/FileDomainBuildable.swift b/Projects/Domain/FileDomain/Interface/DI/FileDomainBuildable.swift index f92da64a..b3332d42 100644 --- a/Projects/Domain/FileDomain/Interface/DI/FileDomainBuildable.swift +++ b/Projects/Domain/FileDomain/Interface/DI/FileDomainBuildable.swift @@ -1,4 +1,5 @@ public protocol FileDomainBuildable { var imageUploadUseCase: any ImageUploadUseCase { get } + var fileUploadUseCase: any FileUploadUseCase { get } var fileRepository: any FileRepository { get } } diff --git a/Projects/Domain/FileDomain/Interface/DataSource/RemoteFileDataSource.swift b/Projects/Domain/FileDomain/Interface/DataSource/RemoteFileDataSource.swift index d1a6cbe5..d0f6de6c 100644 --- a/Projects/Domain/FileDomain/Interface/DataSource/RemoteFileDataSource.swift +++ b/Projects/Domain/FileDomain/Interface/DataSource/RemoteFileDataSource.swift @@ -2,4 +2,5 @@ import Foundation public protocol RemoteFileDataSource { func imageUpload(image: Data, fileName: String) async throws -> String + func fileUpload(file: Data, fileName: String) async throws -> String } diff --git a/Projects/Domain/FileDomain/Interface/Error/FileDomainError.swift b/Projects/Domain/FileDomain/Interface/Error/FileDomainError.swift index 6108e650..7e51bbea 100644 --- a/Projects/Domain/FileDomain/Interface/Error/FileDomainError.swift +++ b/Projects/Domain/FileDomain/Interface/Error/FileDomainError.swift @@ -2,6 +2,7 @@ import Foundation public enum FileDomainError: Error { case notImageType + case notFileType case internalServerError } @@ -11,6 +12,9 @@ extension FileDomainError: LocalizedError { case .notImageType: return "이미지 형식이 jpg, jpeg, png, heic인 이미지가 아닙니다." + case .notFileType: + return "파일 형식이 hw, hwpx, png인 파일이 아닙니다." + case .internalServerError: return "서버에서 문제가 발생하였습니다. 지속될 시 문의해주세요." } diff --git a/Projects/Domain/FileDomain/Interface/Repository/FileRepository.swift b/Projects/Domain/FileDomain/Interface/Repository/FileRepository.swift index 913b8175..2dca6179 100644 --- a/Projects/Domain/FileDomain/Interface/Repository/FileRepository.swift +++ b/Projects/Domain/FileDomain/Interface/Repository/FileRepository.swift @@ -2,4 +2,5 @@ import Foundation public protocol FileRepository { func imageUpload(image: Data, fileName: String) async throws -> String + func fileUpload(file: Data, fileName: String) async throws -> String } diff --git a/Projects/Domain/FileDomain/Interface/UseCase/FileUploadUseCase.swift b/Projects/Domain/FileDomain/Interface/UseCase/FileUploadUseCase.swift new file mode 100644 index 00000000..4df39a91 --- /dev/null +++ b/Projects/Domain/FileDomain/Interface/UseCase/FileUploadUseCase.swift @@ -0,0 +1,5 @@ +import Foundation + +public protocol FileUploadUseCase { + func execute(file: Data, fileName: String) async throws -> String +} diff --git a/Projects/Domain/FileDomain/Sources/DI/FileDomainComponent.swift b/Projects/Domain/FileDomain/Sources/DI/FileDomainComponent.swift index c8aa1dbb..e7940634 100644 --- a/Projects/Domain/FileDomain/Sources/DI/FileDomainComponent.swift +++ b/Projects/Domain/FileDomain/Sources/DI/FileDomainComponent.swift @@ -7,6 +7,9 @@ public protocol FileDomainDependency: Dependency { } public final class FileDomainComponent: Component, FileDomainBuildable { + public var fileUploadUseCase: any FileUploadUseCase { + FileUploadUseCaseImpl(fileRepository: fileRepository) + } public var imageUploadUseCase: any ImageUploadUseCase { ImageUploadUseCaseImpl(fileRepository: fileRepository) } diff --git a/Projects/Domain/FileDomain/Sources/DTO/Response/FileUploadResponseDTO.swift b/Projects/Domain/FileDomain/Sources/DTO/Response/FileUploadResponseDTO.swift new file mode 100644 index 00000000..38bbfcc8 --- /dev/null +++ b/Projects/Domain/FileDomain/Sources/DTO/Response/FileUploadResponseDTO.swift @@ -0,0 +1,9 @@ +import Foundation + +public struct FileUploadResponseDTO: Decodable { + public let fileURL: String + + enum CodingKeys: String, CodingKey { + case fileURL = "fileUrl" + } +} diff --git a/Projects/Domain/FileDomain/Sources/DataSource/RemoteFileDataSourceImpl.swift b/Projects/Domain/FileDomain/Sources/DataSource/RemoteFileDataSourceImpl.swift index 687b87de..136d4af1 100644 --- a/Projects/Domain/FileDomain/Sources/DataSource/RemoteFileDataSourceImpl.swift +++ b/Projects/Domain/FileDomain/Sources/DataSource/RemoteFileDataSourceImpl.swift @@ -10,4 +10,11 @@ final class RemoteFileDataSourceImpl: BaseRemoteDataSource, Remote ) .fileURL } + + func fileUpload(file: Data, fileName: String) async throws -> String { + try await request( + .fileUpload(file: file, fileName: fileName), + dto: FileUploadResponseDTO.self + ).fileURL + } } diff --git a/Projects/Domain/FileDomain/Sources/Endpoint/FileEndpoint.swift b/Projects/Domain/FileDomain/Sources/Endpoint/FileEndpoint.swift index 8e23daf0..fc42feb9 100644 --- a/Projects/Domain/FileDomain/Sources/Endpoint/FileEndpoint.swift +++ b/Projects/Domain/FileDomain/Sources/Endpoint/FileEndpoint.swift @@ -5,6 +5,7 @@ import Foundation enum FileEndpoint { case imageUpload(image: Data, fileName: String) + case fileUpload(file: Data, fileName: String) } extension FileEndpoint: SMSEndpoint { @@ -18,6 +19,9 @@ extension FileEndpoint: SMSEndpoint { switch self { case .imageUpload: return .post("/image") + + case .fileUpload: + return .post("") } } @@ -28,6 +32,11 @@ extension FileEndpoint: SMSEndpoint { MultiPartFormData(field: "file", data: image, fileName: fileName) ]) + case let .fileUpload(file, fileName): + return .uploadMultipart([ + MultiPartFormData(field: "file", data: file, fileName: fileName) + ]) + default: return .requestPlain } @@ -38,6 +47,9 @@ extension FileEndpoint: SMSEndpoint { case .imageUpload: return .accessToken + case .fileUpload: + return .accessToken + default: return .none } @@ -54,6 +66,11 @@ extension FileEndpoint: SMSEndpoint { 400: .notImageType, 500: .internalServerError ] + case .fileUpload: + return [ + 400: .notFileType, + 500: .internalServerError + ] } } } diff --git a/Projects/Domain/FileDomain/Sources/Repository/FileRepositoryImpl.swift b/Projects/Domain/FileDomain/Sources/Repository/FileRepositoryImpl.swift index 75172fac..62014a44 100644 --- a/Projects/Domain/FileDomain/Sources/Repository/FileRepositoryImpl.swift +++ b/Projects/Domain/FileDomain/Sources/Repository/FileRepositoryImpl.swift @@ -11,4 +11,8 @@ struct FileRepositoryImpl: FileRepository { func imageUpload(image: Data, fileName: String) async throws -> String { try await remoteFileDataSource.imageUpload(image: image, fileName: fileName) } + + func fileUpload(file: Data, fileName: String) async throws -> String { + try await remoteFileDataSource.fileUpload(file: file, fileName: fileName) + } } diff --git a/Projects/Domain/FileDomain/Sources/UseCase/FileUploadUseCaseImpl.swift b/Projects/Domain/FileDomain/Sources/UseCase/FileUploadUseCaseImpl.swift new file mode 100644 index 00000000..72a76c2b --- /dev/null +++ b/Projects/Domain/FileDomain/Sources/UseCase/FileUploadUseCaseImpl.swift @@ -0,0 +1,14 @@ +import FileDomainInterface +import Foundation + +struct FileUploadUseCaseImpl: FileUploadUseCase { + private let fileRepository: any FileRepository + + init(fileRepository: any FileRepository) { + self.fileRepository = fileRepository + } + + func execute(file: Data, fileName: String) async throws -> String { + try await fileRepository.fileUpload(file: file, fileName: fileName) + } +} diff --git a/Projects/Feature/GSMAuthenticationFormFeature/Project.swift b/Projects/Feature/GSMAuthenticationFormFeature/Project.swift index 727e6169..5418184b 100644 --- a/Projects/Feature/GSMAuthenticationFormFeature/Project.swift +++ b/Projects/Feature/GSMAuthenticationFormFeature/Project.swift @@ -8,7 +8,8 @@ let project = Project.makeModule( targets: [.interface, .unitTest, .demo], internalDependencies: [ .Feature.BaseFeature, - .Domain.AuthenticationDomainInterface + .Domain.AuthenticationDomainInterface, + .Domain.FileDomainInterface ], demoDependencies: [ .Domain.AuthenticationDomainTesting diff --git a/Projects/Feature/GSMAuthenticationFormFeature/Sources/DI/GSMAuthenticationComponent.swift b/Projects/Feature/GSMAuthenticationFormFeature/Sources/DI/GSMAuthenticationComponent.swift index 4e140e80..fa417ff3 100644 --- a/Projects/Feature/GSMAuthenticationFormFeature/Sources/DI/GSMAuthenticationComponent.swift +++ b/Projects/Feature/GSMAuthenticationFormFeature/Sources/DI/GSMAuthenticationComponent.swift @@ -4,9 +4,11 @@ import NeedleFoundation import SwiftUI import GSMAuthenticationFormFeatureInterface import AuthenticationDomainInterface +import FileDomainInterface public protocol GSMAuthenticationDependency: Dependency { var authenticationDomainBuildable: any AuthenticationDomainBuildable { get } + var fileDomainBuildable: any FileDomainBuildable { get } } public final class GSMAuthenticationComponent: Component, GSMAuthenticationBuildable { @@ -15,7 +17,8 @@ public final class GSMAuthenticationComponent: Component