From 1dc9a9be82347f0b66c5d6a7b03d73404136c7f0 Mon Sep 17 00:00:00 2001 From: Sangyoon Date: Tue, 30 Sep 2025 19:06:36 +0900 Subject: [PATCH 01/10] =?UTF-8?q?[SETTING]=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=20=EA=B5=AC=EC=A1=B0=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SampoomManagement.xcodeproj/project.pbxproj | 41 +++++++ SampoomManagement/App/ContentView.swift | 19 +++ .../{ => App}/SampoomManagementApp.swift | 0 SampoomManagement/ContentView.swift | 24 ---- .../Core/Network/APIResponse.swift | 15 +++ .../Core/Network/NetworkError.swift | 31 +++++ .../Core/Network/NetworkManager.swift | 51 ++++++++ .../Features/Part/Data/API/PartAPI.swift | 30 +++++ .../Features/Part/Data/DTO/PartDTO.swift | 14 +++ .../Part/Data/Mappers/PartMappers.swift | 18 +++ .../Data/Repository/PartRepositoryImpl.swift | 20 ++++ .../Features/Part/Domain/Models/Part.swift | 14 +++ .../Part/Domain/Models/PartList.swift | 23 ++++ .../Domain/Repository/PartRepository.swift | 12 ++ .../Part/Domain/UseCase/GetPartUseCase.swift | 20 ++++ .../Factories/PartViewFactory.swift | 17 +++ .../ViewModels/PartViewModel.swift | 80 +++++++++++++ .../Views/Components/PartItemView.swift | 39 ++++++ .../Part/Presentation/Views/PartView.swift | 113 ++++++++++++++++++ .../AccentColor.colorset/Contents.json | 0 .../AppIcon.appiconset/Contents.json | 0 .../Assets.xcassets/Contents.json | 0 SampoomManagement/Resources/Info.plist | 7 ++ 23 files changed, 564 insertions(+), 24 deletions(-) create mode 100644 SampoomManagement/App/ContentView.swift rename SampoomManagement/{ => App}/SampoomManagementApp.swift (100%) delete mode 100644 SampoomManagement/ContentView.swift create mode 100644 SampoomManagement/Core/Network/APIResponse.swift create mode 100644 SampoomManagement/Core/Network/NetworkError.swift create mode 100644 SampoomManagement/Core/Network/NetworkManager.swift create mode 100644 SampoomManagement/Features/Part/Data/API/PartAPI.swift create mode 100644 SampoomManagement/Features/Part/Data/DTO/PartDTO.swift create mode 100644 SampoomManagement/Features/Part/Data/Mappers/PartMappers.swift create mode 100644 SampoomManagement/Features/Part/Data/Repository/PartRepositoryImpl.swift create mode 100644 SampoomManagement/Features/Part/Domain/Models/Part.swift create mode 100644 SampoomManagement/Features/Part/Domain/Models/PartList.swift create mode 100644 SampoomManagement/Features/Part/Domain/Repository/PartRepository.swift create mode 100644 SampoomManagement/Features/Part/Domain/UseCase/GetPartUseCase.swift create mode 100644 SampoomManagement/Features/Part/Presentation/Factories/PartViewFactory.swift create mode 100644 SampoomManagement/Features/Part/Presentation/ViewModels/PartViewModel.swift create mode 100644 SampoomManagement/Features/Part/Presentation/Views/Components/PartItemView.swift create mode 100644 SampoomManagement/Features/Part/Presentation/Views/PartView.swift rename SampoomManagement/{ => Resources}/Assets.xcassets/AccentColor.colorset/Contents.json (100%) rename SampoomManagement/{ => Resources}/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename SampoomManagement/{ => Resources}/Assets.xcassets/Contents.json (100%) create mode 100644 SampoomManagement/Resources/Info.plist diff --git a/SampoomManagement.xcodeproj/project.pbxproj b/SampoomManagement.xcodeproj/project.pbxproj index 1f2d7c1..03a5e06 100644 --- a/SampoomManagement.xcodeproj/project.pbxproj +++ b/SampoomManagement.xcodeproj/project.pbxproj @@ -6,13 +6,30 @@ objectVersion = 77; objects = { +/* Begin PBXBuildFile section */ + 533528342E8BD99400F38FD1 /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 533528332E8BD99400F38FD1 /* Alamofire */; }; +/* End PBXBuildFile section */ + /* Begin PBXFileReference section */ 53A7B4BF2E8A43AF00BC946E /* SampoomManagement.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SampoomManagement.app; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + 533528372E8BDAB300F38FD1 /* Exceptions for "SampoomManagement" folder in "SampoomManagement" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Resources/Info.plist, + ); + target = 53A7B4BE2E8A43AF00BC946E /* SampoomManagement */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + /* Begin PBXFileSystemSynchronizedRootGroup section */ 53A7B4C12E8A43AF00BC946E /* SampoomManagement */ = { isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + 533528372E8BDAB300F38FD1 /* Exceptions for "SampoomManagement" folder in "SampoomManagement" target */, + ); path = SampoomManagement; sourceTree = ""; }; @@ -23,6 +40,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 533528342E8BD99400F38FD1 /* Alamofire in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -65,6 +83,7 @@ ); name = SampoomManagement; packageProductDependencies = ( + 533528332E8BD99400F38FD1 /* Alamofire */, ); productName = SampoomManagement; productReference = 53A7B4BF2E8A43AF00BC946E /* SampoomManagement.app */; @@ -94,6 +113,9 @@ ); mainGroup = 53A7B4B62E8A43AF00BC946E; minimizedProjectReferenceProxies = 1; + packageReferences = ( + 533528322E8BD99400F38FD1 /* XCRemoteSwiftPackageReference "Alamofire" */, + ); preferredProjectObjectVersion = 77; productRefGroup = 53A7B4C02E8A43AF00BC946E /* Products */; projectDirPath = ""; @@ -332,6 +354,25 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 533528322E8BD99400F38FD1 /* XCRemoteSwiftPackageReference "Alamofire" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/Alamofire/Alamofire.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 5.10.2; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 533528332E8BD99400F38FD1 /* Alamofire */ = { + isa = XCSwiftPackageProductDependency; + package = 533528322E8BD99400F38FD1 /* XCRemoteSwiftPackageReference "Alamofire" */; + productName = Alamofire; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 53A7B4B72E8A43AF00BC946E /* Project object */; } diff --git a/SampoomManagement/App/ContentView.swift b/SampoomManagement/App/ContentView.swift new file mode 100644 index 0000000..ca00c0b --- /dev/null +++ b/SampoomManagement/App/ContentView.swift @@ -0,0 +1,19 @@ +// +// ContentView.swift +// SampoomManagement +// +// Created by 채상윤 on 9/29/25. +// + +import SwiftUI + +struct ContentView: View { + @StateObject private var partViewModel = PartViewFactory.createViewModel() + + var body: some View { + NavigationView { + PartView() + .environmentObject(partViewModel) + } + } +} diff --git a/SampoomManagement/SampoomManagementApp.swift b/SampoomManagement/App/SampoomManagementApp.swift similarity index 100% rename from SampoomManagement/SampoomManagementApp.swift rename to SampoomManagement/App/SampoomManagementApp.swift diff --git a/SampoomManagement/ContentView.swift b/SampoomManagement/ContentView.swift deleted file mode 100644 index eee12f3..0000000 --- a/SampoomManagement/ContentView.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// ContentView.swift -// SampoomManagement -// -// Created by 채상윤 on 9/29/25. -// - -import SwiftUI - -struct ContentView: View { - var body: some View { - VStack { - Image(systemName: "globe") - .imageScale(.large) - .foregroundStyle(.tint) - Text("Hello, world!") - } - .padding() - } -} - -#Preview { - ContentView() -} diff --git a/SampoomManagement/Core/Network/APIResponse.swift b/SampoomManagement/Core/Network/APIResponse.swift new file mode 100644 index 0000000..907cf15 --- /dev/null +++ b/SampoomManagement/Core/Network/APIResponse.swift @@ -0,0 +1,15 @@ +// +// APIResponse.swift +// SampoomManagement +// +// Created by 채상윤 on 9/29/25. +// + +import Foundation + +struct APIResponse: Codable { + let status: Int + let success: Bool + let message: String + let data: T +} diff --git a/SampoomManagement/Core/Network/NetworkError.swift b/SampoomManagement/Core/Network/NetworkError.swift new file mode 100644 index 0000000..4333563 --- /dev/null +++ b/SampoomManagement/Core/Network/NetworkError.swift @@ -0,0 +1,31 @@ +// +// NetworkError.swift +// SampoomManagement +// +// Created by 채상윤 on 9/29/25. +// + +import Foundation + +enum NetworkError: Error, LocalizedError { + case networkError(Error) + case decodingError(Error) + case invalidURL + case noData + case serverError(Int) + + var errorDescription: String? { + switch self { + case .networkError(let error): + return "네트워크 오류: \(error.localizedDescription)" + case .decodingError(let error): + return "데이터 파싱 오류: \(error.localizedDescription)" + case .invalidURL: + return "잘못된 URL" + case .noData: + return "데이터가 없습니다" + case .serverError(let code): + return "서버 오류: \(code)" + } + } +} diff --git a/SampoomManagement/Core/Network/NetworkManager.swift b/SampoomManagement/Core/Network/NetworkManager.swift new file mode 100644 index 0000000..ff9cb77 --- /dev/null +++ b/SampoomManagement/Core/Network/NetworkManager.swift @@ -0,0 +1,51 @@ +// +// NetworkManager.swift +// SampoomManagement +// +// Created by 채상윤 on 9/29/25. +// + +import Foundation +import Alamofire + +class NetworkManager { + static let shared = NetworkManager() + + private let baseURL = "http://localhost:8080/api/" + + private init() {} + + func request( + endpoint: String, + method: HTTPMethod = .get, + parameters: Parameters? = nil, + responseType: T.Type, + completion: @escaping (Result, NetworkError>) -> Void + ) { + let url = baseURL + endpoint + + AF.request( + url, + method: method, + parameters: parameters, + encoding: JSONEncoding.default + ) + .responseData { response in + switch response.result { + case .success(let data): + Task { @MainActor in + do { + let apiResponse = try JSONDecoder().decode(APIResponse.self, from: data) + completion(.success(apiResponse)) + } catch { + completion(.failure(.decodingError(error))) + } + } + case .failure(let error): + Task { @MainActor in + completion(.failure(.networkError(error))) + } + } + } + } +} diff --git a/SampoomManagement/Features/Part/Data/API/PartAPI.swift b/SampoomManagement/Features/Part/Data/API/PartAPI.swift new file mode 100644 index 0000000..537ecc8 --- /dev/null +++ b/SampoomManagement/Features/Part/Data/API/PartAPI.swift @@ -0,0 +1,30 @@ +// +// PartAPI.swift +// SampoomManagement +// +// Created by 채상윤 on 9/29/25. +// + +import Foundation + +class PartAPI { + private let networkManager = NetworkManager.shared + + func getPartList() async throws -> PartList { + return try await withCheckedThrowingContinuation { continuation in + networkManager.request( + endpoint: "part", + responseType: [PartDTO].self + ) { result in + switch result { + case .success(let response): + let parts = response.data.map { $0.toModel() } + let partList = PartList(items: parts) + continuation.resume(returning: partList) + case .failure(let error): + continuation.resume(throwing: error) + } + } + } + } +} diff --git a/SampoomManagement/Features/Part/Data/DTO/PartDTO.swift b/SampoomManagement/Features/Part/Data/DTO/PartDTO.swift new file mode 100644 index 0000000..4f3c96c --- /dev/null +++ b/SampoomManagement/Features/Part/Data/DTO/PartDTO.swift @@ -0,0 +1,14 @@ +// +// PartDTO.swift +// SampoomManagement +// +// Created by 채상윤 on 9/29/25. +// + +import Foundation + +struct PartDTO: Codable { + let id: Int + let name: String + let count: Int +} diff --git a/SampoomManagement/Features/Part/Data/Mappers/PartMappers.swift b/SampoomManagement/Features/Part/Data/Mappers/PartMappers.swift new file mode 100644 index 0000000..c27e5f5 --- /dev/null +++ b/SampoomManagement/Features/Part/Data/Mappers/PartMappers.swift @@ -0,0 +1,18 @@ +// +// PartMappers.swift +// SampoomManagement +// +// Created by 채상윤 on 9/29/25. +// + +import Foundation + +extension PartDTO { + func toModel() -> Part { + return Part( + id: self.id, + name: self.name, + count: self.count + ) + } +} diff --git a/SampoomManagement/Features/Part/Data/Repository/PartRepositoryImpl.swift b/SampoomManagement/Features/Part/Data/Repository/PartRepositoryImpl.swift new file mode 100644 index 0000000..740f47f --- /dev/null +++ b/SampoomManagement/Features/Part/Data/Repository/PartRepositoryImpl.swift @@ -0,0 +1,20 @@ +// +// PartRepositoryImpl.swift +// SampoomManagement +// +// Created by 채상윤 on 9/29/25. +// + +import Foundation + +class PartRepositoryImpl: PartRepository { + private let api: PartAPI + + init(api: PartAPI) { + self.api = api + } + + func getPartList() async throws -> PartList { + return try await api.getPartList() + } +} diff --git a/SampoomManagement/Features/Part/Domain/Models/Part.swift b/SampoomManagement/Features/Part/Domain/Models/Part.swift new file mode 100644 index 0000000..3f3a96a --- /dev/null +++ b/SampoomManagement/Features/Part/Domain/Models/Part.swift @@ -0,0 +1,14 @@ +// +// Part.swift +// SampoomManagement +// +// Created by 채상윤 on 9/29/25. +// + +import Foundation + +struct Part: Identifiable, Codable, Equatable { + let id: Int + let name: String + let count: Int +} diff --git a/SampoomManagement/Features/Part/Domain/Models/PartList.swift b/SampoomManagement/Features/Part/Domain/Models/PartList.swift new file mode 100644 index 0000000..832c1e2 --- /dev/null +++ b/SampoomManagement/Features/Part/Domain/Models/PartList.swift @@ -0,0 +1,23 @@ +// +// PartList.swift +// SampoomManagement +// +// Created by 채상윤 on 9/29/25. +// + +import Foundation + +struct PartList: Codable, Equatable { + let items: [Part] + var totalCount: Int { items.count } + var isEmpty: Bool { items.isEmpty } + + init(items: [Part]) { + self.items = items + } + + static func empty() -> PartList { + return PartList(items: []) + } +} + diff --git a/SampoomManagement/Features/Part/Domain/Repository/PartRepository.swift b/SampoomManagement/Features/Part/Domain/Repository/PartRepository.swift new file mode 100644 index 0000000..3a703b1 --- /dev/null +++ b/SampoomManagement/Features/Part/Domain/Repository/PartRepository.swift @@ -0,0 +1,12 @@ +// +// PartRepository.swift +// SampoomManagement +// +// Created by 채상윤 on 9/29/25. +// + +import Foundation + +protocol PartRepository { + func getPartList() async throws -> PartList +} diff --git a/SampoomManagement/Features/Part/Domain/UseCase/GetPartUseCase.swift b/SampoomManagement/Features/Part/Domain/UseCase/GetPartUseCase.swift new file mode 100644 index 0000000..1e9fa32 --- /dev/null +++ b/SampoomManagement/Features/Part/Domain/UseCase/GetPartUseCase.swift @@ -0,0 +1,20 @@ +// +// GetPartUseCase.swift +// SampoomManagement +// +// Created by 채상윤 on 9/29/25. +// + +import Foundation + +class GetPartUseCase { + private let repository: PartRepository + + init(repository: PartRepository) { + self.repository = repository + } + + func execute() async throws -> PartList { + return try await repository.getPartList() + } +} diff --git a/SampoomManagement/Features/Part/Presentation/Factories/PartViewFactory.swift b/SampoomManagement/Features/Part/Presentation/Factories/PartViewFactory.swift new file mode 100644 index 0000000..a0410e0 --- /dev/null +++ b/SampoomManagement/Features/Part/Presentation/Factories/PartViewFactory.swift @@ -0,0 +1,17 @@ +// +// PartViewFactory.swift +// SampoomManagement +// +// Created by 채상윤 on 9/29/25. +// + +import Foundation + +class PartViewFactory { + static func createViewModel() -> PartViewModel { + let api = PartAPI() + let repository = PartRepositoryImpl(api: api) + let useCase = GetPartUseCase(repository: repository) + return PartViewModel(getPartUseCase: useCase) + } +} diff --git a/SampoomManagement/Features/Part/Presentation/ViewModels/PartViewModel.swift b/SampoomManagement/Features/Part/Presentation/ViewModels/PartViewModel.swift new file mode 100644 index 0000000..4ff66ea --- /dev/null +++ b/SampoomManagement/Features/Part/Presentation/ViewModels/PartViewModel.swift @@ -0,0 +1,80 @@ +// +// PartViewModel.swift +// SampoomManagement +// +// Created by 채상윤 on 9/29/25. +// + +import Foundation +import SwiftUI +import Combine + +@MainActor +class PartViewModel: ObservableObject { + @Published var uiState = PartUIState() + + private let getPartUseCase: GetPartUseCase + + init(getPartUseCase: GetPartUseCase) { + self.getPartUseCase = getPartUseCase + loadPart() + } + + private func loadPart() { + Task { + uiState = uiState.copy(loading: true, error: nil) + + do { + let partList = try await getPartUseCase.execute() + uiState = uiState.copy( + loading: false, + success: true, + partList: partList.items + ) + } catch { + uiState = uiState.copy( + loading: false, + error: error.localizedDescription + ) + } + } + } + + func refreshPart() { + loadPart() + } +} + +struct PartUIState { + let loading: Bool + let error: String? + let success: Bool + let partList: [Part] + + init( + loading: Bool = false, + error: String? = nil, + success: Bool = false, + partList: [Part] = [] + ) { + self.loading = loading + self.error = error + self.success = success + self.partList = partList + } + + func copy( + loading: Bool? = nil, + error: String? = nil, + success: Bool? = nil, + partList: [Part]? = nil + ) -> PartUIState { + return PartUIState( + loading: loading ?? self.loading, + error: error ?? self.error, + success: success ?? self.success, + partList: partList ?? self.partList + ) + } +} + diff --git a/SampoomManagement/Features/Part/Presentation/Views/Components/PartItemView.swift b/SampoomManagement/Features/Part/Presentation/Views/Components/PartItemView.swift new file mode 100644 index 0000000..6092fd8 --- /dev/null +++ b/SampoomManagement/Features/Part/Presentation/Views/Components/PartItemView.swift @@ -0,0 +1,39 @@ +// +// PartItemView.swift +// SampoomManagement +// +// Created by 채상윤 on 9/29/25. +// + +import SwiftUI + +struct PartItemView: View { + let part: Part + + var body: some View { + HStack { + VStack(alignment: .leading, spacing: 4) { + Text(part.name) + .font(.headline) + .fontWeight(.semibold) + + Text("ID: \(part.id)") + .font(.caption) + .foregroundColor(.secondary) + } + + Spacer() + + Text("\(part.count)개") + .font(.title2) + .fontWeight(.bold) + .foregroundColor(.blue) + } + .padding(16) + .background( + RoundedRectangle(cornerRadius: 12) + .fill(Color(.systemBackground)) + .shadow(color: .black.opacity(0.1), radius: 2, x: 0, y: 1) + ) + } +} diff --git a/SampoomManagement/Features/Part/Presentation/Views/PartView.swift b/SampoomManagement/Features/Part/Presentation/Views/PartView.swift new file mode 100644 index 0000000..504510a --- /dev/null +++ b/SampoomManagement/Features/Part/Presentation/Views/PartView.swift @@ -0,0 +1,113 @@ +// +// PartView.swift +// SampoomManagement +// +// Created by 채상윤 on 9/29/25. +// + +import SwiftUI + +struct PartView: View { + @EnvironmentObject var viewModel: PartViewModel + + var body: some View { + NavigationView { + VStack(spacing: 0) { + headerView + contentView + } + .navigationBarHidden(true) + } + } + + private var headerView: some View { + HStack { + Text("인벤토리") + .font(.title2) + .fontWeight(.bold) + Spacer() + } + .padding(.horizontal, 16) + .padding(.vertical, 12) + .background(Color(.systemBackground)) + } + + @ViewBuilder + private var contentView: some View { + if viewModel.uiState.loading { + loadingView + } else if let error = viewModel.uiState.error { + errorView(error: error) + } else if viewModel.uiState.partList.isEmpty { + emptyView + } else { + listView + } + } + + private var loadingView: some View { + VStack { + Spacer() + ProgressView() + .scaleEffect(1.2) + Text("로딩 중...") + .font(.caption) + .foregroundColor(.secondary) + .padding(.top, 8) + Spacer() + } + } + + private func errorView(error: String) -> some View { + VStack(spacing: 16) { + Spacer() + Image(systemName: "exclamationmark.triangle") + .font(.system(size: 48)) + .foregroundColor(.red) + + Text("오류가 발생했습니다") + .font(.headline) + .foregroundColor(.red) + + Text(error) + .font(.caption) + .foregroundColor(.secondary) + .multilineTextAlignment(.center) + .padding(.horizontal, 32) + + Button("다시 시도") { + viewModel.refreshPart() + } + .buttonStyle(.borderedProminent) + + Spacer() + } + } + + private var emptyView: some View { + VStack(spacing: 16) { + Spacer() + Image(systemName: "tray") + .font(.system(size: 48)) + .foregroundColor(.secondary) + + Text("인벤토리가 비어있습니다") + .font(.headline) + .foregroundColor(.secondary) + + Spacer() + } + } + + private var listView: some View { + ScrollView { + LazyVStack(spacing: 8) { + ForEach(viewModel.uiState.partList) { part in + PartItemView(part: part) + } + } + .padding(.horizontal, 16) + .padding(.vertical, 8) + } + } +} diff --git a/SampoomManagement/Assets.xcassets/AccentColor.colorset/Contents.json b/SampoomManagement/Resources/Assets.xcassets/AccentColor.colorset/Contents.json similarity index 100% rename from SampoomManagement/Assets.xcassets/AccentColor.colorset/Contents.json rename to SampoomManagement/Resources/Assets.xcassets/AccentColor.colorset/Contents.json diff --git a/SampoomManagement/Assets.xcassets/AppIcon.appiconset/Contents.json b/SampoomManagement/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from SampoomManagement/Assets.xcassets/AppIcon.appiconset/Contents.json rename to SampoomManagement/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/SampoomManagement/Assets.xcassets/Contents.json b/SampoomManagement/Resources/Assets.xcassets/Contents.json similarity index 100% rename from SampoomManagement/Assets.xcassets/Contents.json rename to SampoomManagement/Resources/Assets.xcassets/Contents.json diff --git a/SampoomManagement/Resources/Info.plist b/SampoomManagement/Resources/Info.plist new file mode 100644 index 0000000..781d890 --- /dev/null +++ b/SampoomManagement/Resources/Info.plist @@ -0,0 +1,7 @@ + + + + + + + From d28bda15a9dd40d87212bc0e7c220fbae62880db Mon Sep 17 00:00:00 2001 From: Sangyoon Date: Fri, 3 Oct 2025 11:41:54 +0900 Subject: [PATCH 02/10] =?UTF-8?q?#3=20[REFAC]=20Swinject=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EB=9D=BC=EC=9D=B4=EB=B8=8C=EB=9F=AC?= =?UTF-8?q?=EB=A6=AC=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EC=A3=BC=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SampoomManagement.xcodeproj/project.pbxproj | 17 +++ SampoomManagement/App/ContentView.swift | 10 +- .../App/SampoomManagementApp.swift | 6 + SampoomManagement/Core/DI/CoreDIModule.swift | 20 ++++ SampoomManagement/Core/DI/DIContainer.swift | 32 +++++ .../Core/Network/NetworkManager.swift | 2 +- .../Core/UI/Components/AppHeader.swift | 57 +++++++++ .../Core/UI/Components/EmptyView.swift | 56 +++++++++ .../Core/UI/Components/ErrorView.swift | 46 +++++++ .../Core/UI/Components/LoadingView.swift | 35 ++++++ SampoomManagement/Core/UI/State/UIState.swift | 42 +++++++ .../Features/Part/DI/PartDIModule.swift | 35 ++++++ .../Part/Data/{ => Remote}/API/PartAPI.swift | 6 +- .../Part/Data/{ => Remote}/DTO/PartDTO.swift | 0 .../Factories/PartViewFactory.swift | 17 --- .../Part/Presentation/Views/PartView.swift | 113 ------------------ .../Components => UI}/PartItemView.swift | 0 .../Features/Part/UI/PartUIState.swift | 41 +++++++ .../Features/Part/UI/PartView.swift | 68 +++++++++++ .../ViewModels => UI}/PartViewModel.swift | 33 ----- 20 files changed, 470 insertions(+), 166 deletions(-) create mode 100644 SampoomManagement/Core/DI/CoreDIModule.swift create mode 100644 SampoomManagement/Core/DI/DIContainer.swift create mode 100644 SampoomManagement/Core/UI/Components/AppHeader.swift create mode 100644 SampoomManagement/Core/UI/Components/EmptyView.swift create mode 100644 SampoomManagement/Core/UI/Components/ErrorView.swift create mode 100644 SampoomManagement/Core/UI/Components/LoadingView.swift create mode 100644 SampoomManagement/Core/UI/State/UIState.swift create mode 100644 SampoomManagement/Features/Part/DI/PartDIModule.swift rename SampoomManagement/Features/Part/Data/{ => Remote}/API/PartAPI.swift (84%) rename SampoomManagement/Features/Part/Data/{ => Remote}/DTO/PartDTO.swift (100%) delete mode 100644 SampoomManagement/Features/Part/Presentation/Factories/PartViewFactory.swift delete mode 100644 SampoomManagement/Features/Part/Presentation/Views/PartView.swift rename SampoomManagement/Features/Part/{Presentation/Views/Components => UI}/PartItemView.swift (100%) create mode 100644 SampoomManagement/Features/Part/UI/PartUIState.swift create mode 100644 SampoomManagement/Features/Part/UI/PartView.swift rename SampoomManagement/Features/Part/{Presentation/ViewModels => UI}/PartViewModel.swift (57%) diff --git a/SampoomManagement.xcodeproj/project.pbxproj b/SampoomManagement.xcodeproj/project.pbxproj index 03a5e06..930231e 100644 --- a/SampoomManagement.xcodeproj/project.pbxproj +++ b/SampoomManagement.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 533528342E8BD99400F38FD1 /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 533528332E8BD99400F38FD1 /* Alamofire */; }; + 5387CA3A2E8F676E005A3936 /* Swinject in Frameworks */ = {isa = PBXBuildFile; productRef = 5387CA392E8F676E005A3936 /* Swinject */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -41,6 +42,7 @@ buildActionMask = 2147483647; files = ( 533528342E8BD99400F38FD1 /* Alamofire in Frameworks */, + 5387CA3A2E8F676E005A3936 /* Swinject in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -84,6 +86,7 @@ name = SampoomManagement; packageProductDependencies = ( 533528332E8BD99400F38FD1 /* Alamofire */, + 5387CA392E8F676E005A3936 /* Swinject */, ); productName = SampoomManagement; productReference = 53A7B4BF2E8A43AF00BC946E /* SampoomManagement.app */; @@ -115,6 +118,7 @@ minimizedProjectReferenceProxies = 1; packageReferences = ( 533528322E8BD99400F38FD1 /* XCRemoteSwiftPackageReference "Alamofire" */, + 5387CA382E8F676E005A3936 /* XCRemoteSwiftPackageReference "Swinject" */, ); preferredProjectObjectVersion = 77; productRefGroup = 53A7B4C02E8A43AF00BC946E /* Products */; @@ -364,6 +368,14 @@ minimumVersion = 5.10.2; }; }; + 5387CA382E8F676E005A3936 /* XCRemoteSwiftPackageReference "Swinject" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/Swinject/Swinject.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.10.0; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -372,6 +384,11 @@ package = 533528322E8BD99400F38FD1 /* XCRemoteSwiftPackageReference "Alamofire" */; productName = Alamofire; }; + 5387CA392E8F676E005A3936 /* Swinject */ = { + isa = XCSwiftPackageProductDependency; + package = 5387CA382E8F676E005A3936 /* XCRemoteSwiftPackageReference "Swinject" */; + productName = Swinject; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 53A7B4B72E8A43AF00BC946E /* Project object */; diff --git a/SampoomManagement/App/ContentView.swift b/SampoomManagement/App/ContentView.swift index ca00c0b..4db023f 100644 --- a/SampoomManagement/App/ContentView.swift +++ b/SampoomManagement/App/ContentView.swift @@ -8,7 +8,15 @@ import SwiftUI struct ContentView: View { - @StateObject private var partViewModel = PartViewFactory.createViewModel() + @StateObject private var partViewModel: PartViewModel + + init() { + // DI Container에서 ViewModel 주입 + guard let viewModel = DIContainer.shared.resolve(PartViewModel.self) else { + fatalError("PartViewModel을 DIContainer에서 찾을 수 없습니다.") + } + _partViewModel = StateObject(wrappedValue: viewModel) + } var body: some View { NavigationView { diff --git a/SampoomManagement/App/SampoomManagementApp.swift b/SampoomManagement/App/SampoomManagementApp.swift index 0f7d72c..c04c678 100644 --- a/SampoomManagement/App/SampoomManagementApp.swift +++ b/SampoomManagement/App/SampoomManagementApp.swift @@ -9,6 +9,12 @@ import SwiftUI @main struct SampoomManagementApp: App { + + init() { + // DI Container 초기화 + _ = DIContainer.shared + } + var body: some Scene { WindowGroup { ContentView() diff --git a/SampoomManagement/Core/DI/CoreDIModule.swift b/SampoomManagement/Core/DI/CoreDIModule.swift new file mode 100644 index 0000000..898877c --- /dev/null +++ b/SampoomManagement/Core/DI/CoreDIModule.swift @@ -0,0 +1,20 @@ +// +// CoreDIModule.swift +// SampoomManagement +// +// Created by 채상윤 on 9/29/25. +// + +import Foundation +import Swinject + +final class CoreDIModule: Assembly { + func assemble(container: Container) { + // MARK: - Core Layer Dependencies + + // NetworkManager 등록 + container.register(NetworkManager.self) { _ in + NetworkManager() + }.inObjectScope(.container) + } +} diff --git a/SampoomManagement/Core/DI/DIContainer.swift b/SampoomManagement/Core/DI/DIContainer.swift new file mode 100644 index 0000000..8813450 --- /dev/null +++ b/SampoomManagement/Core/DI/DIContainer.swift @@ -0,0 +1,32 @@ +// +// DIContainer.swift +// SampoomManagement +// +// Created by 채상윤 on 9/29/25. +// + +import Foundation +import Swinject + +final class DIContainer { + static let shared = DIContainer() + + private let container: Container + private let assembler: Assembler + + private init() { + container = Container() + assembler = Assembler([ + CoreDIModule(), // Core 레벨 의존성 + PartDIModule() // Part Feature 의존성 + ], container: container) + } + + func resolve(_ type: T.Type) -> T? { + return container.resolve(type) + } + + func resolve(_ type: T.Type, name: String) -> T? { + return container.resolve(type, name: name) + } +} diff --git a/SampoomManagement/Core/Network/NetworkManager.swift b/SampoomManagement/Core/Network/NetworkManager.swift index ff9cb77..ee13375 100644 --- a/SampoomManagement/Core/Network/NetworkManager.swift +++ b/SampoomManagement/Core/Network/NetworkManager.swift @@ -13,7 +13,7 @@ class NetworkManager { private let baseURL = "http://localhost:8080/api/" - private init() {} + init() {} func request( endpoint: String, diff --git a/SampoomManagement/Core/UI/Components/AppHeader.swift b/SampoomManagement/Core/UI/Components/AppHeader.swift new file mode 100644 index 0000000..54799c8 --- /dev/null +++ b/SampoomManagement/Core/UI/Components/AppHeader.swift @@ -0,0 +1,57 @@ +// +// AppHeader.swift +// SampoomManagement +// +// Created by 채상윤 on 9/29/25. +// + +import SwiftUI + +struct AppHeader: View { + let title: String + let showBackButton: Bool + let onBackPressed: (() -> Void)? + + init( + title: String, + showBackButton: Bool = false, + onBackPressed: (() -> Void)? = nil + ) { + self.title = title + self.showBackButton = showBackButton + self.onBackPressed = onBackPressed + } + + var body: some View { + HStack { + if showBackButton { + Button(action: { + onBackPressed?() + }) { + Image(systemName: "chevron.left") + .font(.title2) + .foregroundColor(.primary) + } + } + + Text(title) + .font(.title2) + .fontWeight(.bold) + + Spacer() + } + .padding(.horizontal, 16) + .padding(.vertical, 12) + .background(Color(.systemBackground)) + } +} + +#Preview { + VStack { + AppHeader(title: "인벤토리") + AppHeader(title: "상세보기", showBackButton: true) { + print("Back pressed") + } + Spacer() + } +} diff --git a/SampoomManagement/Core/UI/Components/EmptyView.swift b/SampoomManagement/Core/UI/Components/EmptyView.swift new file mode 100644 index 0000000..95e4a6d --- /dev/null +++ b/SampoomManagement/Core/UI/Components/EmptyView.swift @@ -0,0 +1,56 @@ +// +// EmptyView.swift +// SampoomManagement +// +// Created by 채상윤 on 9/29/25. +// + +import SwiftUI + +struct EmptyStateView: View { + let icon: String + let title: String + let message: String? + + init( + icon: String = "tray", + title: String = "데이터가 없습니다", + message: String? = nil + ) { + self.icon = icon + self.title = title + self.message = message + } + + var body: some View { + VStack(spacing: 16) { + Spacer() + + Image(systemName: icon) + .font(.system(size: 48)) + .foregroundColor(.secondary) + + Text(title) + .font(.headline) + .foregroundColor(.secondary) + + if let message = message { + Text(message) + .font(.caption) + .foregroundColor(.secondary) + .multilineTextAlignment(.center) + .padding(.horizontal, 32) + } + + Spacer() + } + } +} + +#Preview { + EmptyStateView( + icon: "tray", + title: "인벤토리가 비어있습니다", + message: "새로운 부품을 추가해보세요" + ) +} diff --git a/SampoomManagement/Core/UI/Components/ErrorView.swift b/SampoomManagement/Core/UI/Components/ErrorView.swift new file mode 100644 index 0000000..898e1b1 --- /dev/null +++ b/SampoomManagement/Core/UI/Components/ErrorView.swift @@ -0,0 +1,46 @@ +// +// ErrorView.swift +// SampoomManagement +// +// Created by 채상윤 on 9/29/25. +// + +import SwiftUI + +struct ErrorView: View { + let error: String + let onRetry: () -> Void + + var body: some View { + VStack(spacing: 16) { + Spacer() + + Image(systemName: "exclamationmark.triangle") + .font(.system(size: 48)) + .foregroundColor(.red) + + Text("오류가 발생했습니다") + .font(.headline) + .foregroundColor(.red) + + Text(error) + .font(.caption) + .foregroundColor(.secondary) + .multilineTextAlignment(.center) + .padding(.horizontal, 32) + + Button("다시 시도") { + onRetry() + } + .buttonStyle(.borderedProminent) + + Spacer() + } + } +} + +#Preview { + ErrorView(error: "네트워크 연결을 확인해주세요") { + print("Retry tapped") + } +} diff --git a/SampoomManagement/Core/UI/Components/LoadingView.swift b/SampoomManagement/Core/UI/Components/LoadingView.swift new file mode 100644 index 0000000..1717ac0 --- /dev/null +++ b/SampoomManagement/Core/UI/Components/LoadingView.swift @@ -0,0 +1,35 @@ +// +// LoadingView.swift +// SampoomManagement +// +// Created by 채상윤 on 9/29/25. +// + +import SwiftUI + +struct LoadingView: View { + let message: String + + init(message: String = "로딩 중...") { + self.message = message + } + + var body: some View { + VStack(spacing: 16) { + Spacer() + + ProgressView() + .scaleEffect(1.2) + + Text(message) + .font(.caption) + .foregroundColor(.secondary) + + Spacer() + } + } +} + +#Preview { + LoadingView() +} diff --git a/SampoomManagement/Core/UI/State/UIState.swift b/SampoomManagement/Core/UI/State/UIState.swift new file mode 100644 index 0000000..834e9c0 --- /dev/null +++ b/SampoomManagement/Core/UI/State/UIState.swift @@ -0,0 +1,42 @@ +// +// UIState.swift +// SampoomManagement +// +// Created by 채상윤 on 9/29/25. +// + +import Foundation + +protocol UIState { + var loading: Bool { get } + var error: String? { get } + var success: Bool { get } +} + +struct BaseUIState: UIState { + let loading: Bool + let error: String? + let success: Bool + + init( + loading: Bool = false, + error: String? = nil, + success: Bool = false + ) { + self.loading = loading + self.error = error + self.success = success + } + + func copy( + loading: Bool? = nil, + error: String? = nil, + success: Bool? = nil + ) -> BaseUIState { + return BaseUIState( + loading: loading ?? self.loading, + error: error ?? self.error, + success: success ?? self.success + ) + } +} diff --git a/SampoomManagement/Features/Part/DI/PartDIModule.swift b/SampoomManagement/Features/Part/DI/PartDIModule.swift new file mode 100644 index 0000000..b628e6c --- /dev/null +++ b/SampoomManagement/Features/Part/DI/PartDIModule.swift @@ -0,0 +1,35 @@ +// +// PartDIModule.swift +// SampoomManagement +// +// Created by 채상윤 on 9/29/25. +// + +import Foundation +import Swinject + +final class PartDIModule: Assembly { + func assemble(container: Container) { + // MARK: - Part Feature Dependencies + + // PartAPI 등록 + container.register(PartAPI.self) { resolver in + PartAPI(networkManager: resolver.resolve(NetworkManager.self)!) + }.inObjectScope(.container) + + // PartRepository 등록 (Interface -> Implementation) + container.register(PartRepository.self) { resolver in + PartRepositoryImpl(api: resolver.resolve(PartAPI.self)!) + }.inObjectScope(.container) + + // GetPartUseCase 등록 + container.register(GetPartUseCase.self) { resolver in + GetPartUseCase(repository: resolver.resolve(PartRepository.self)!) + }.inObjectScope(.container) + + // PartViewModel 등록 + container.register(PartViewModel.self) { resolver in + PartViewModel(getPartUseCase: resolver.resolve(GetPartUseCase.self)!) + }.inObjectScope(.container) + } +} diff --git a/SampoomManagement/Features/Part/Data/API/PartAPI.swift b/SampoomManagement/Features/Part/Data/Remote/API/PartAPI.swift similarity index 84% rename from SampoomManagement/Features/Part/Data/API/PartAPI.swift rename to SampoomManagement/Features/Part/Data/Remote/API/PartAPI.swift index 537ecc8..9def440 100644 --- a/SampoomManagement/Features/Part/Data/API/PartAPI.swift +++ b/SampoomManagement/Features/Part/Data/Remote/API/PartAPI.swift @@ -8,7 +8,11 @@ import Foundation class PartAPI { - private let networkManager = NetworkManager.shared + private let networkManager: NetworkManager + + init(networkManager: NetworkManager) { + self.networkManager = networkManager + } func getPartList() async throws -> PartList { return try await withCheckedThrowingContinuation { continuation in diff --git a/SampoomManagement/Features/Part/Data/DTO/PartDTO.swift b/SampoomManagement/Features/Part/Data/Remote/DTO/PartDTO.swift similarity index 100% rename from SampoomManagement/Features/Part/Data/DTO/PartDTO.swift rename to SampoomManagement/Features/Part/Data/Remote/DTO/PartDTO.swift diff --git a/SampoomManagement/Features/Part/Presentation/Factories/PartViewFactory.swift b/SampoomManagement/Features/Part/Presentation/Factories/PartViewFactory.swift deleted file mode 100644 index a0410e0..0000000 --- a/SampoomManagement/Features/Part/Presentation/Factories/PartViewFactory.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// PartViewFactory.swift -// SampoomManagement -// -// Created by 채상윤 on 9/29/25. -// - -import Foundation - -class PartViewFactory { - static func createViewModel() -> PartViewModel { - let api = PartAPI() - let repository = PartRepositoryImpl(api: api) - let useCase = GetPartUseCase(repository: repository) - return PartViewModel(getPartUseCase: useCase) - } -} diff --git a/SampoomManagement/Features/Part/Presentation/Views/PartView.swift b/SampoomManagement/Features/Part/Presentation/Views/PartView.swift deleted file mode 100644 index 504510a..0000000 --- a/SampoomManagement/Features/Part/Presentation/Views/PartView.swift +++ /dev/null @@ -1,113 +0,0 @@ -// -// PartView.swift -// SampoomManagement -// -// Created by 채상윤 on 9/29/25. -// - -import SwiftUI - -struct PartView: View { - @EnvironmentObject var viewModel: PartViewModel - - var body: some View { - NavigationView { - VStack(spacing: 0) { - headerView - contentView - } - .navigationBarHidden(true) - } - } - - private var headerView: some View { - HStack { - Text("인벤토리") - .font(.title2) - .fontWeight(.bold) - Spacer() - } - .padding(.horizontal, 16) - .padding(.vertical, 12) - .background(Color(.systemBackground)) - } - - @ViewBuilder - private var contentView: some View { - if viewModel.uiState.loading { - loadingView - } else if let error = viewModel.uiState.error { - errorView(error: error) - } else if viewModel.uiState.partList.isEmpty { - emptyView - } else { - listView - } - } - - private var loadingView: some View { - VStack { - Spacer() - ProgressView() - .scaleEffect(1.2) - Text("로딩 중...") - .font(.caption) - .foregroundColor(.secondary) - .padding(.top, 8) - Spacer() - } - } - - private func errorView(error: String) -> some View { - VStack(spacing: 16) { - Spacer() - Image(systemName: "exclamationmark.triangle") - .font(.system(size: 48)) - .foregroundColor(.red) - - Text("오류가 발생했습니다") - .font(.headline) - .foregroundColor(.red) - - Text(error) - .font(.caption) - .foregroundColor(.secondary) - .multilineTextAlignment(.center) - .padding(.horizontal, 32) - - Button("다시 시도") { - viewModel.refreshPart() - } - .buttonStyle(.borderedProminent) - - Spacer() - } - } - - private var emptyView: some View { - VStack(spacing: 16) { - Spacer() - Image(systemName: "tray") - .font(.system(size: 48)) - .foregroundColor(.secondary) - - Text("인벤토리가 비어있습니다") - .font(.headline) - .foregroundColor(.secondary) - - Spacer() - } - } - - private var listView: some View { - ScrollView { - LazyVStack(spacing: 8) { - ForEach(viewModel.uiState.partList) { part in - PartItemView(part: part) - } - } - .padding(.horizontal, 16) - .padding(.vertical, 8) - } - } -} diff --git a/SampoomManagement/Features/Part/Presentation/Views/Components/PartItemView.swift b/SampoomManagement/Features/Part/UI/PartItemView.swift similarity index 100% rename from SampoomManagement/Features/Part/Presentation/Views/Components/PartItemView.swift rename to SampoomManagement/Features/Part/UI/PartItemView.swift diff --git a/SampoomManagement/Features/Part/UI/PartUIState.swift b/SampoomManagement/Features/Part/UI/PartUIState.swift new file mode 100644 index 0000000..8f5e289 --- /dev/null +++ b/SampoomManagement/Features/Part/UI/PartUIState.swift @@ -0,0 +1,41 @@ +// +// PartUIState.swift +// SampoomManagement +// +// Created by 채상윤 on 9/29/25. +// + +import Foundation + +struct PartUIState: UIState { + let loading: Bool + let error: String? + let success: Bool + let partList: [Part] + + init( + loading: Bool = false, + error: String? = nil, + success: Bool = false, + partList: [Part] = [] + ) { + self.loading = loading + self.error = error + self.success = success + self.partList = partList + } + + func copy( + loading: Bool? = nil, + error: String? = nil, + success: Bool? = nil, + partList: [Part]? = nil + ) -> PartUIState { + return PartUIState( + loading: loading ?? self.loading, + error: error ?? self.error, + success: success ?? self.success, + partList: partList ?? self.partList + ) + } +} diff --git a/SampoomManagement/Features/Part/UI/PartView.swift b/SampoomManagement/Features/Part/UI/PartView.swift new file mode 100644 index 0000000..b0384a0 --- /dev/null +++ b/SampoomManagement/Features/Part/UI/PartView.swift @@ -0,0 +1,68 @@ +// +// PartView.swift +// SampoomManagement +// +// Created by 채상윤 on 9/29/25. +// + +import SwiftUI + +struct PartView: View { + @EnvironmentObject var viewModel: PartViewModel + + var body: some View { + NavigationView { + VStack(spacing: 0) { + headerView + contentView + } + .navigationBarHidden(true) + } + } + + private var headerView: some View { + AppHeader(title: "인벤토리") + } + + @ViewBuilder + private var contentView: some View { + if viewModel.uiState.loading { + loadingView + } else if let error = viewModel.uiState.error { + errorView(error: error) + } else if viewModel.uiState.partList.isEmpty { + emptyView + } else { + listView + } + } + + private var loadingView: some View { + LoadingView() + } + + private func errorView(error: String) -> some View { + ErrorView(error: error) { + viewModel.refreshPart() + } + } + + private var emptyView: some View { + EmptyStateView( + icon: "tray", + title: "인벤토리가 비어있습니다" + ) + } + + private var listView: some View { + ScrollView { + LazyVStack(spacing: 8) { + ForEach(viewModel.uiState.partList) { part in + PartItemView(part: part) + } + } + .padding(.horizontal, 16) + .padding(.vertical, 8) + } + } +} diff --git a/SampoomManagement/Features/Part/Presentation/ViewModels/PartViewModel.swift b/SampoomManagement/Features/Part/UI/PartViewModel.swift similarity index 57% rename from SampoomManagement/Features/Part/Presentation/ViewModels/PartViewModel.swift rename to SampoomManagement/Features/Part/UI/PartViewModel.swift index 4ff66ea..1297624 100644 --- a/SampoomManagement/Features/Part/Presentation/ViewModels/PartViewModel.swift +++ b/SampoomManagement/Features/Part/UI/PartViewModel.swift @@ -45,36 +45,3 @@ class PartViewModel: ObservableObject { } } -struct PartUIState { - let loading: Bool - let error: String? - let success: Bool - let partList: [Part] - - init( - loading: Bool = false, - error: String? = nil, - success: Bool = false, - partList: [Part] = [] - ) { - self.loading = loading - self.error = error - self.success = success - self.partList = partList - } - - func copy( - loading: Bool? = nil, - error: String? = nil, - success: Bool? = nil, - partList: [Part]? = nil - ) -> PartUIState { - return PartUIState( - loading: loading ?? self.loading, - error: error ?? self.error, - success: success ?? self.success, - partList: partList ?? self.partList - ) - } -} - From c86324770c6b80d31a64e188ba357e6d244e5b12 Mon Sep 17 00:00:00 2001 From: Sangyoon Date: Fri, 3 Oct 2025 11:52:41 +0900 Subject: [PATCH 03/10] =?UTF-8?q?#3=20[FEAT]=20TabView=20=EA=B5=AC?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SampoomManagement/App/ContentView.swift | 67 ++++++++++++++++++- .../Features/Part/UI/PartView.swift | 7 +- 2 files changed, 65 insertions(+), 9 deletions(-) diff --git a/SampoomManagement/App/ContentView.swift b/SampoomManagement/App/ContentView.swift index 4db023f..c9c57e6 100644 --- a/SampoomManagement/App/ContentView.swift +++ b/SampoomManagement/App/ContentView.swift @@ -9,6 +9,7 @@ import SwiftUI struct ContentView: View { @StateObject private var partViewModel: PartViewModel + @State private var selectedTab = 0 init() { // DI Container에서 ViewModel 주입 @@ -19,9 +20,69 @@ struct ContentView: View { } var body: some View { - NavigationView { - PartView() - .environmentObject(partViewModel) + TabView(selection: $selectedTab) { + // PartView 탭 + NavigationView { + PartView() + .environmentObject(partViewModel) + } + .tabItem { + Image(systemName: "wrench.and.screwdriver") + Text("부품") + } + .tag(0) + + // InventoryView 탭 (임시) + NavigationView { + VStack(spacing: 20) { + Spacer() + Text("인벤토리") + .font(.largeTitle) + .fontWeight(.bold) + Spacer() + } + .navigationTitle("인벤토리") + } + .tabItem { + Image(systemName: "cube.box") + Text("인벤토리") + } + .tag(1) + + // ProfileView 탭 (임시) + NavigationView { + VStack(spacing: 20) { + Spacer() + Text("프로필") + .font(.largeTitle) + .fontWeight(.bold) + Spacer() + } + .navigationTitle("프로필") + } + .tabItem { + Image(systemName: "person.circle") + Text("프로필") + } + .tag(2) + + // SettingView 탭 (임시) + NavigationView { + VStack(spacing: 20) { + Spacer() + Text("설정") + .font(.largeTitle) + .fontWeight(.bold) + Spacer() + } + .navigationTitle("설정") + } + .tabItem { + Image(systemName: "gearshape") + Text("설정") + } + .tag(3) } + .accentColor(.blue) } } diff --git a/SampoomManagement/Features/Part/UI/PartView.swift b/SampoomManagement/Features/Part/UI/PartView.swift index b0384a0..893f9c8 100644 --- a/SampoomManagement/Features/Part/UI/PartView.swift +++ b/SampoomManagement/Features/Part/UI/PartView.swift @@ -13,17 +13,12 @@ struct PartView: View { var body: some View { NavigationView { VStack(spacing: 0) { - headerView contentView } - .navigationBarHidden(true) + .navigationBarTitle(Text("부품")) } } - private var headerView: some View { - AppHeader(title: "인벤토리") - } - @ViewBuilder private var contentView: some View { if viewModel.uiState.loading { From 6f7ed2994ace383f0fb25b7eac0de78a8e09cb58 Mon Sep 17 00:00:00 2001 From: Sangyoon Date: Fri, 3 Oct 2025 12:36:21 +0900 Subject: [PATCH 04/10] =?UTF-8?q?#3=20[FEAT]=20TabBarr=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=EB=B2=84=ED=8A=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SampoomManagement/App/ContentView.swift | 102 ++++++++++-------- .../Features/Part/UI/PartView.swift | 2 +- 2 files changed, 58 insertions(+), 46 deletions(-) diff --git a/SampoomManagement/App/ContentView.swift b/SampoomManagement/App/ContentView.swift index c9c57e6..332c6dc 100644 --- a/SampoomManagement/App/ContentView.swift +++ b/SampoomManagement/App/ContentView.swift @@ -7,9 +7,14 @@ import SwiftUI +enum Tabs { + case part, inventory, profile, setting, detail +} + struct ContentView: View { @StateObject private var partViewModel: PartViewModel - @State private var selectedTab = 0 + @State private var selectedTab: Tabs = .part + @State var searchString = "" init() { // DI Container에서 ViewModel 주입 @@ -22,67 +27,74 @@ struct ContentView: View { var body: some View { TabView(selection: $selectedTab) { // PartView 탭 - NavigationView { + Tab("부품", systemImage: "wrench.and.screwdriver", value: .part) { PartView() .environmentObject(partViewModel) } - .tabItem { - Image(systemName: "wrench.and.screwdriver") - Text("부품") - } - .tag(0) // InventoryView 탭 (임시) - NavigationView { - VStack(spacing: 20) { - Spacer() - Text("인벤토리") - .font(.largeTitle) - .fontWeight(.bold) - Spacer() + Tab("인벤토리", systemImage: "cube.box", value: .inventory) { + NavigationView { + VStack(spacing: 20) { + Spacer() + Text("인벤토리") + .font(.largeTitle) + .fontWeight(.bold) + Spacer() + } + .navigationTitle("인벤토리") } - .navigationTitle("인벤토리") } - .tabItem { - Image(systemName: "cube.box") - Text("인벤토리") - } - .tag(1) // ProfileView 탭 (임시) - NavigationView { - VStack(spacing: 20) { - Spacer() - Text("프로필") - .font(.largeTitle) - .fontWeight(.bold) - Spacer() + Tab("프로필", systemImage: "person.circle", value: .profile) { + NavigationView { + VStack(spacing: 20) { + Spacer() + Text("프로필") + .font(.largeTitle) + .fontWeight(.bold) + Spacer() + } + .navigationTitle("프로필") } - .navigationTitle("프로필") - } - .tabItem { - Image(systemName: "person.circle") - Text("프로필") } - .tag(2) // SettingView 탭 (임시) - NavigationView { - VStack(spacing: 20) { - Spacer() - Text("설정") - .font(.largeTitle) - .fontWeight(.bold) - Spacer() + Tab("설정", systemImage: "gearshape", value: .setting) { + NavigationStack { + VStack(spacing: 20) { + Spacer() + Text("설정") + .font(.largeTitle) + .fontWeight(.bold) + NavigationLink { + DetailView() + } label: { + Text("상세 보기") + } + Spacer() + } + .navigationTitle("설정") } - .navigationTitle("설정") } - .tabItem { - Image(systemName: "gearshape") - Text("설정") + + Tab(value: .detail, role: .search) { + NavigationStack { + Text("검색") + } + .navigationTitle("검색") + .searchable(text: $searchString) } - .tag(3) } .accentColor(.blue) } } + +struct DetailView: View { + var body: some View { + NavigationStack { + Text("상세") + } + } +} diff --git a/SampoomManagement/Features/Part/UI/PartView.swift b/SampoomManagement/Features/Part/UI/PartView.swift index 893f9c8..4a0ff66 100644 --- a/SampoomManagement/Features/Part/UI/PartView.swift +++ b/SampoomManagement/Features/Part/UI/PartView.swift @@ -11,7 +11,7 @@ struct PartView: View { @EnvironmentObject var viewModel: PartViewModel var body: some View { - NavigationView { + NavigationStack { VStack(spacing: 0) { contentView } From eab9a5163646e1ba7919b35af9903a4eb76ab293 Mon Sep 17 00:00:00 2001 From: Sangyoon Date: Fri, 3 Oct 2025 12:48:23 +0900 Subject: [PATCH 05/10] =?UTF-8?q?#3=20[FEAT]=20=EC=95=B1=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EC=BD=98,=20=EC=9D=B4=EB=A6=84=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SampoomManagement.xcodeproj/project.pbxproj | 4 ++++ .../AccentColor.colorset/Contents.json | 9 +++++++++ .../AppIcon.appiconset/Contents.json | 3 +++ .../AppIcon.appiconset/sampoom_1024_dark.png | Bin 0 -> 33497 bytes .../AppIcon.appiconset/sampoom_1024_light 1.png | Bin 0 -> 24151 bytes .../AppIcon.appiconset/sampoom_1024_light.png | Bin 0 -> 24151 bytes 6 files changed, 16 insertions(+) create mode 100644 SampoomManagement/Resources/Assets.xcassets/AppIcon.appiconset/sampoom_1024_dark.png create mode 100644 SampoomManagement/Resources/Assets.xcassets/AppIcon.appiconset/sampoom_1024_light 1.png create mode 100644 SampoomManagement/Resources/Assets.xcassets/AppIcon.appiconset/sampoom_1024_light.png diff --git a/SampoomManagement.xcodeproj/project.pbxproj b/SampoomManagement.xcodeproj/project.pbxproj index 930231e..ea94f0a 100644 --- a/SampoomManagement.xcodeproj/project.pbxproj +++ b/SampoomManagement.xcodeproj/project.pbxproj @@ -282,11 +282,13 @@ DEVELOPMENT_TEAM = B9PUAVBBKX; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = "삼품관리"; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 18.6; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -314,11 +316,13 @@ DEVELOPMENT_TEAM = B9PUAVBBKX; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = "삼품관리"; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 18.6; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/SampoomManagement/Resources/Assets.xcassets/AccentColor.colorset/Contents.json b/SampoomManagement/Resources/Assets.xcassets/AccentColor.colorset/Contents.json index eb87897..4c7c8aa 100644 --- a/SampoomManagement/Resources/Assets.xcassets/AccentColor.colorset/Contents.json +++ b/SampoomManagement/Resources/Assets.xcassets/AccentColor.colorset/Contents.json @@ -1,6 +1,15 @@ { "colors" : [ { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFF", + "green" : "0x80", + "red" : "0x80" + } + }, "idiom" : "universal" } ], diff --git a/SampoomManagement/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json b/SampoomManagement/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json index 2305880..a93b0d7 100644 --- a/SampoomManagement/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/SampoomManagement/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,6 +1,7 @@ { "images" : [ { + "filename" : "sampoom_1024_light.png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" @@ -12,6 +13,7 @@ "value" : "dark" } ], + "filename" : "sampoom_1024_dark.png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" @@ -23,6 +25,7 @@ "value" : "tinted" } ], + "filename" : "sampoom_1024_light 1.png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" diff --git a/SampoomManagement/Resources/Assets.xcassets/AppIcon.appiconset/sampoom_1024_dark.png b/SampoomManagement/Resources/Assets.xcassets/AppIcon.appiconset/sampoom_1024_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..94fbde757a421537ffd92eb976faaefea805647b GIT binary patch literal 33497 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 literal 0 HcmV?d00001 diff --git a/SampoomManagement/Resources/Assets.xcassets/AppIcon.appiconset/sampoom_1024_light.png b/SampoomManagement/Resources/Assets.xcassets/AppIcon.appiconset/sampoom_1024_light.png new file mode 100644 index 0000000000000000000000000000000000000000..17ec1bfdb47b7c7e8de3f218e7106e8914fa3927 GIT binary patch literal 24151 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 literal 0 HcmV?d00001 From 98548dd8207e8b20b5004cc48b679372c26ab95b Mon Sep 17 00:00:00 2001 From: Sangyoon Date: Fri, 3 Oct 2025 12:52:47 +0900 Subject: [PATCH 06/10] =?UTF-8?q?#3=20[FEAT]=20StringResource=20=EA=B5=AC?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SampoomManagement/App/ContentView.swift | 74 +++++++++++++++---- .../Core/Resources/StringResources.swift | 70 ++++++++++++++++++ 2 files changed, 130 insertions(+), 14 deletions(-) create mode 100644 SampoomManagement/Core/Resources/StringResources.swift diff --git a/SampoomManagement/App/ContentView.swift b/SampoomManagement/App/ContentView.swift index 332c6dc..9b58523 100644 --- a/SampoomManagement/App/ContentView.swift +++ b/SampoomManagement/App/ContentView.swift @@ -27,63 +27,88 @@ struct ContentView: View { var body: some View { TabView(selection: $selectedTab) { // PartView 탭 - Tab("부품", systemImage: "wrench.and.screwdriver", value: .part) { + Tab(StringResources.Tabs.parts, systemImage: "wrench.and.screwdriver", value: .part) { PartView() .environmentObject(partViewModel) } // InventoryView 탭 (임시) - Tab("인벤토리", systemImage: "cube.box", value: .inventory) { + Tab(StringResources.Tabs.inventory, systemImage: "cube.box", value: .inventory) { NavigationView { VStack(spacing: 20) { Spacer() - Text("인벤토리") + Text(StringResources.Tabs.inventory) .font(.largeTitle) .fontWeight(.bold) + Text(StringResources.Placeholders.inventoryDescription) + .font(.body) + .foregroundColor(.secondary) + .multilineTextAlignment(.center) + .padding(.horizontal, 32) Spacer() } - .navigationTitle("인벤토리") + .navigationTitle(StringResources.Tabs.inventory) } } // ProfileView 탭 (임시) - Tab("프로필", systemImage: "person.circle", value: .profile) { + Tab(StringResources.Tabs.profile, systemImage: "person.circle", value: .profile) { NavigationView { VStack(spacing: 20) { Spacer() - Text("프로필") + Text(StringResources.Tabs.profile) .font(.largeTitle) .fontWeight(.bold) + Text(StringResources.Placeholders.profileDescription) + .font(.body) + .foregroundColor(.secondary) + .multilineTextAlignment(.center) + .padding(.horizontal, 32) Spacer() } - .navigationTitle("프로필") + .navigationTitle(StringResources.Tabs.profile) } } // SettingView 탭 (임시) - Tab("설정", systemImage: "gearshape", value: .setting) { + Tab(StringResources.Tabs.settings, systemImage: "gearshape", value: .setting) { NavigationStack { VStack(spacing: 20) { Spacer() - Text("설정") + Text(StringResources.Tabs.settings) .font(.largeTitle) .fontWeight(.bold) + Text(StringResources.Placeholders.settingsDescription) + .font(.body) + .foregroundColor(.secondary) + .multilineTextAlignment(.center) + .padding(.horizontal, 32) NavigationLink { DetailView() } label: { - Text("상세 보기") + Text(StringResources.Navigation.detail) } + .buttonStyle(.borderedProminent) Spacer() } - .navigationTitle("설정") + .navigationTitle(StringResources.Tabs.settings) } } Tab(value: .detail, role: .search) { NavigationStack { - Text("검색") + VStack { + Text(StringResources.Search.title) + .font(.largeTitle) + .fontWeight(.bold) + Text(StringResources.Placeholders.searchDescription) + .font(.body) + .foregroundColor(.secondary) + .multilineTextAlignment(.center) + .padding(.horizontal, 32) + } } - .navigationTitle("검색") + .navigationTitle(StringResources.Search.title) .searchable(text: $searchString) } } @@ -94,7 +119,28 @@ struct ContentView: View { struct DetailView: View { var body: some View { NavigationStack { - Text("상세") + VStack { + Text(StringResources.Detail.screenTitle) + .font(.largeTitle) + .fontWeight(.bold) + .padding() + + Text(StringResources.Detail.description) + .foregroundColor(.secondary) + .multilineTextAlignment(.center) + .padding() + + Spacer() + } + .navigationTitle(StringResources.Detail.title) + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + Button(StringResources.Navigation.close) { + // 닫기 액션 + } + } + } } } } diff --git a/SampoomManagement/Core/Resources/StringResources.swift b/SampoomManagement/Core/Resources/StringResources.swift new file mode 100644 index 0000000..c4736cd --- /dev/null +++ b/SampoomManagement/Core/Resources/StringResources.swift @@ -0,0 +1,70 @@ +// +// StringResources.swift +// SampoomManagement +// +// Created by 채상윤 on 9/29/25. +// + +import Foundation + +struct StringResources { + + // MARK: - App + struct App { + static let title = "SampoomManagement" + } + + // MARK: - Tabs + struct Tabs { + static let parts = "부품" + static let inventory = "인벤토리" + static let profile = "프로필" + static let settings = "설정" + } + + // MARK: - Messages + struct Messages { + static let loading = "로딩 중..." + static let errorTitle = "오류가 발생했습니다" + static let retryButton = "다시 시도" + static let emptyInventory = "인벤토리가 비어있습니다" + static let emptyStateMessage = "데이터가 없습니다" + } + + // MARK: - Navigation + struct Navigation { + static let back = "뒤로" + static let close = "닫기" + static let detail = "상세 보기" + } + + // MARK: - Placeholders + struct Placeholders { + static let inventoryDescription = "인벤토리 관리 기능이 들어갈 예정입니다" + static let profileDescription = "사용자 프로필 기능이 들어갈 예정입니다" + static let settingsDescription = "앱 설정 기능이 들어갈 예정입니다" + static let searchDescription = "검색 기능이 들어갈 예정입니다" + } + + // MARK: - Search + struct Search { + static let title = "검색" + } + + // MARK: - Detail + struct Detail { + static let title = "상세" + static let screenTitle = "상세 화면" + static let description = "이 화면은 탭바가 완전히 숨겨집니다!" + } + + // MARK: - Common + struct Common { + static let ok = "확인" + static let cancel = "취소" + static let save = "저장" + static let delete = "삭제" + static let edit = "편집" + static let done = "완료" + } +} From 6b6ee1cc1f854c3e1c21f84b58f1f9114b456c59 Mon Sep 17 00:00:00 2001 From: Choo ChangWoo Date: Sat, 4 Oct 2025 20:11:32 +0900 Subject: [PATCH 07/10] =?UTF-8?q?=EA=B8=B0=EB=8A=A5:=20=EA=B3=B5=ED=86=B5?= =?UTF-8?q?=20=EC=9B=8C=ED=81=AC=ED=94=8C=EB=A1=9C=EC=9A=B0=20=ED=85=9C?= =?UTF-8?q?=ED=94=8C=EB=A6=BF=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/assign-issue-creator.yml | 14 ++++++++++++++ .../workflows/close-issues-on-dev-merge.yml | 19 +++++++++++++++++++ .github/workflows/request-pr-review.yml | 17 +++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 .github/workflows/assign-issue-creator.yml create mode 100644 .github/workflows/close-issues-on-dev-merge.yml create mode 100644 .github/workflows/request-pr-review.yml diff --git a/.github/workflows/assign-issue-creator.yml b/.github/workflows/assign-issue-creator.yml new file mode 100644 index 0000000..6dde730 --- /dev/null +++ b/.github/workflows/assign-issue-creator.yml @@ -0,0 +1,14 @@ +# 각 레포지토리의 .github/workflows/assign-issue-creator.yml + +name: Assign issue creator + +on: + issues: + types: [opened] + +jobs: + call-reusable-workflow: + # @main 은 .github 레포지토리의 main 브랜치를 사용한다는 의미입니다. 버전을 위해 @v1과 같이 태그를 사용하는 것을 권장합니다. + uses: 33-Auto/.github/.github/workflows/reusable-assign-issue-creator.yml@main + # 이 워크플로우는 secrets를 전달할 필요가 없지만, 필요 시 아래와 같이 전달합니다. + # secrets: inherit \ No newline at end of file diff --git a/.github/workflows/close-issues-on-dev-merge.yml b/.github/workflows/close-issues-on-dev-merge.yml new file mode 100644 index 0000000..325a846 --- /dev/null +++ b/.github/workflows/close-issues-on-dev-merge.yml @@ -0,0 +1,19 @@ +# 각 레포지토리의 .github/workflows/close-issues-on-dev-merge.yml + +name: Auto Close Issues on dev merge + +on: + pull_request: + types: [closed] + +jobs: + call-reusable-workflow: + if: > + github.event.pull_request.merged == true && + github.event.pull_request.base.ref == 'dev' + uses: 33-Auto/.github/.github/workflows/reusable-close-linked-issues.yml@main + # with를 통해 재사용 워크플로우의 inputs에 값을 전달합니다. + with: + pr-body: ${{ github.event.pull_request.body }} + issue-number: ${{ github.event.pull_request.number }} + secrets: inherit # 재사용 워크플로우가 GITHUB_TOKEN을 사용할 수 있도록 전달 \ No newline at end of file diff --git a/.github/workflows/request-pr-review.yml b/.github/workflows/request-pr-review.yml new file mode 100644 index 0000000..66a31b9 --- /dev/null +++ b/.github/workflows/request-pr-review.yml @@ -0,0 +1,17 @@ +# 각 레포지토리의 .github/workflows/request-pr-review.yml + +name: PR Assignee & Team Review Request + +on: + pull_request: + types: [opened, reopened, ready_for_review] + +jobs: + call-reusable-workflow: + uses: 33-Auto/.github/.github/workflows/reusable-pr-assign-and-review.yml@main + with: + team-slug-for-review: "review_avengers" # 여기에 리뷰를 요청할 팀의 slug를 입력합니다. + pr-author: ${{ github.event.pull_request.user.login }} + pr-number: ${{ github.event.pull_request.number }} + secrets: + ORGANIZATION_TOKEN: ${{ secrets.ORGANIZATION_TOKEN }} # 재사용 워크플로우가 ORGANIZATION_TOKEN을 사용할 수 있도록 전달 From 28a80bc19f8ee080fb673e1c311a04e3f6e3c921 Mon Sep 17 00:00:00 2001 From: Sangyoon Date: Sat, 11 Oct 2025 20:50:25 +0900 Subject: [PATCH 08/10] =?UTF-8?q?#6=20[REFAC]=20=EC=95=B1=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EC=BD=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xcschemes/SampoomManagement.xcscheme | 78 ++++++++++++++++++ .../AppIcon.appiconset/sampoom_1024_dark.png | Bin 33497 -> 9591 bytes .../sampoom_1024_light 1.png | Bin 24151 -> 9491 bytes .../AppIcon.appiconset/sampoom_1024_light.png | Bin 24151 -> 9491 bytes 4 files changed, 78 insertions(+) create mode 100644 SampoomManagement.xcodeproj/xcshareddata/xcschemes/SampoomManagement.xcscheme diff --git a/SampoomManagement.xcodeproj/xcshareddata/xcschemes/SampoomManagement.xcscheme b/SampoomManagement.xcodeproj/xcshareddata/xcschemes/SampoomManagement.xcscheme new file mode 100644 index 0000000..e823c3b --- /dev/null +++ b/SampoomManagement.xcodeproj/xcshareddata/xcschemes/SampoomManagement.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SampoomManagement/Resources/Assets.xcassets/AppIcon.appiconset/sampoom_1024_dark.png b/SampoomManagement/Resources/Assets.xcassets/AppIcon.appiconset/sampoom_1024_dark.png index 94fbde757a421537ffd92eb976faaefea805647b..a2705053f4cbd8b7158ef28a4fb780343a1a07a3 100644 GIT binary patch literal 9591 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 Y|J^bF(;o&UV066evpw56f217$cm9Vd=Kufz literal 33497 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 literal 24151 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 diff --git a/SampoomManagement/Resources/Assets.xcassets/AppIcon.appiconset/sampoom_1024_light.png b/SampoomManagement/Resources/Assets.xcassets/AppIcon.appiconset/sampoom_1024_light.png index 17ec1bfdb47b7c7e8de3f218e7106e8914fa3927..0544cad2c9c5b0cca738adfc656a2fdecc3fae3d 100644 GIT binary patch literal 9491 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 literal 24151 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 From 114e41c6cf98ce9889824aab1d6a97d3d274d159 Mon Sep 17 00:00:00 2001 From: Sangyoon Date: Sat, 11 Oct 2025 21:29:35 +0900 Subject: [PATCH 09/10] =?UTF-8?q?#6=20[REFAC]=20=EB=82=B4=EB=B9=84?= =?UTF-8?q?=EA=B2=8C=EC=9D=B4=EC=85=98=20=EB=9D=BC=EC=9A=B0=ED=8A=B8=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SampoomManagement/App/ContentView.swift | 136 +++++++++--------- .../Core/Resources/StringResources.swift | 11 +- .../Features/Part/UI/PartView.swift | 2 + .../Background.colorset/Contents.json | 38 +++++ .../Background_Card.colorset/Contents.json | 38 +++++ .../Text.colorset/Contents.json | 38 +++++ .../cart.imageset/Contents.json | 12 ++ .../Assets.xcassets/cart.imageset/cart.svg | 10 ++ .../dashboard.imageset/Contents.json | 12 ++ .../dashboard.imageset/dashboard.svg | 3 + .../delivery.imageset/Contents.json | 12 ++ .../delivery.imageset/delivery.svg | 10 ++ .../employee.imageset/Contents.json | 12 ++ .../employee.imageset/employee.svg | 3 + .../orders.imageset/Contents.json | 12 ++ .../orders.imageset/orders.svg | 3 + .../parts.imageset/Contents.json | 12 ++ .../Assets.xcassets/parts.imageset/parts.svg | 3 + .../search.imageset/Contents.json | 12 ++ .../search.imageset/search.svg | 3 + .../settings.imageset/Contents.json | 12 ++ .../settings.imageset/settings.svg | 10 ++ 22 files changed, 335 insertions(+), 69 deletions(-) create mode 100644 SampoomManagement/Resources/Assets.xcassets/Background.colorset/Contents.json create mode 100644 SampoomManagement/Resources/Assets.xcassets/Background_Card.colorset/Contents.json create mode 100644 SampoomManagement/Resources/Assets.xcassets/Text.colorset/Contents.json create mode 100644 SampoomManagement/Resources/Assets.xcassets/cart.imageset/Contents.json create mode 100644 SampoomManagement/Resources/Assets.xcassets/cart.imageset/cart.svg create mode 100644 SampoomManagement/Resources/Assets.xcassets/dashboard.imageset/Contents.json create mode 100644 SampoomManagement/Resources/Assets.xcassets/dashboard.imageset/dashboard.svg create mode 100644 SampoomManagement/Resources/Assets.xcassets/delivery.imageset/Contents.json create mode 100644 SampoomManagement/Resources/Assets.xcassets/delivery.imageset/delivery.svg create mode 100644 SampoomManagement/Resources/Assets.xcassets/employee.imageset/Contents.json create mode 100644 SampoomManagement/Resources/Assets.xcassets/employee.imageset/employee.svg create mode 100644 SampoomManagement/Resources/Assets.xcassets/orders.imageset/Contents.json create mode 100644 SampoomManagement/Resources/Assets.xcassets/orders.imageset/orders.svg create mode 100644 SampoomManagement/Resources/Assets.xcassets/parts.imageset/Contents.json create mode 100644 SampoomManagement/Resources/Assets.xcassets/parts.imageset/parts.svg create mode 100644 SampoomManagement/Resources/Assets.xcassets/search.imageset/Contents.json create mode 100644 SampoomManagement/Resources/Assets.xcassets/search.imageset/search.svg create mode 100644 SampoomManagement/Resources/Assets.xcassets/settings.imageset/Contents.json create mode 100644 SampoomManagement/Resources/Assets.xcassets/settings.imageset/settings.svg diff --git a/SampoomManagement/App/ContentView.swift b/SampoomManagement/App/ContentView.swift index 9b58523..f22696f 100644 --- a/SampoomManagement/App/ContentView.swift +++ b/SampoomManagement/App/ContentView.swift @@ -8,13 +8,12 @@ import SwiftUI enum Tabs { - case part, inventory, profile, setting, detail + case dashboard, delivery, cart, orders, parts } struct ContentView: View { @StateObject private var partViewModel: PartViewModel - @State private var selectedTab: Tabs = .part - @State var searchString = "" + @State private var selectedTab: Tabs = .dashboard init() { // DI Container에서 ViewModel 주입 @@ -26,18 +25,12 @@ struct ContentView: View { var body: some View { TabView(selection: $selectedTab) { - // PartView 탭 - Tab(StringResources.Tabs.parts, systemImage: "wrench.and.screwdriver", value: .part) { - PartView() - .environmentObject(partViewModel) - } - - // InventoryView 탭 (임시) - Tab(StringResources.Tabs.inventory, systemImage: "cube.box", value: .inventory) { - NavigationView { + // Dashboard 탭 (임시) + Tab(value: .dashboard) { + NavigationStack { VStack(spacing: 20) { Spacer() - Text(StringResources.Tabs.inventory) + Text(StringResources.Tabs.dashboard) .font(.largeTitle) .fontWeight(.bold) Text(StringResources.Placeholders.inventoryDescription) @@ -47,100 +40,113 @@ struct ContentView: View { .padding(.horizontal, 32) Spacer() } - .navigationTitle(StringResources.Tabs.inventory) + .navigationTitle(StringResources.Tabs.dashboard) + } + } label: { + Label { + Text(StringResources.Tabs.dashboard) + } icon: { + Image("dashboard") + .renderingMode(.template) + .foregroundStyle(Color.text) } } - // ProfileView 탭 (임시) - Tab(StringResources.Tabs.profile, systemImage: "person.circle", value: .profile) { - NavigationView { + // Delivery 탭 (임시) + Tab(value: .delivery) { + NavigationStack { VStack(spacing: 20) { Spacer() - Text(StringResources.Tabs.profile) + Text(StringResources.Tabs.delivery) .font(.largeTitle) .fontWeight(.bold) - Text(StringResources.Placeholders.profileDescription) + Text(StringResources.Placeholders.inventoryDescription) .font(.body) .foregroundColor(.secondary) .multilineTextAlignment(.center) .padding(.horizontal, 32) Spacer() } - .navigationTitle(StringResources.Tabs.profile) + .navigationTitle(StringResources.Tabs.delivery) + } + } label: { + Label { + Text(StringResources.Tabs.delivery) + } icon: { + Image("delivery") + .renderingMode(.template) + .foregroundStyle(Color.text) } } - // SettingView 탭 (임시) - Tab(StringResources.Tabs.settings, systemImage: "gearshape", value: .setting) { + // Cart 탭 (임시) + Tab(value: .cart) { NavigationStack { VStack(spacing: 20) { Spacer() - Text(StringResources.Tabs.settings) + Text(StringResources.Tabs.cart) .font(.largeTitle) .fontWeight(.bold) - Text(StringResources.Placeholders.settingsDescription) + Text(StringResources.Placeholders.inventoryDescription) .font(.body) .foregroundColor(.secondary) .multilineTextAlignment(.center) .padding(.horizontal, 32) - NavigationLink { - DetailView() - } label: { - Text(StringResources.Navigation.detail) - } - .buttonStyle(.borderedProminent) Spacer() } - .navigationTitle(StringResources.Tabs.settings) + .navigationTitle(StringResources.Tabs.cart) + } + } label: { + Label { + Text(StringResources.Tabs.cart) + } icon: { + Image("cart") + .renderingMode(.template) + .foregroundStyle(Color.text) } } - Tab(value: .detail, role: .search) { + // Orders 탭 (임시) + Tab(value: .orders) { NavigationStack { - VStack { - Text(StringResources.Search.title) + VStack(spacing: 20) { + Spacer() + Text(StringResources.Tabs.orders) .font(.largeTitle) .fontWeight(.bold) - Text(StringResources.Placeholders.searchDescription) + Text(StringResources.Placeholders.inventoryDescription) .font(.body) .foregroundColor(.secondary) .multilineTextAlignment(.center) .padding(.horizontal, 32) + Spacer() } + .navigationTitle(StringResources.Tabs.orders) + } + } label: { + Label { + Text(StringResources.Tabs.orders) + } icon: { + Image("orders") + .renderingMode(.template) + .foregroundStyle(Color.text) } - .navigationTitle(StringResources.Search.title) - .searchable(text: $searchString) - } - } - .accentColor(.blue) - } -} - -struct DetailView: View { - var body: some View { - NavigationStack { - VStack { - Text(StringResources.Detail.screenTitle) - .font(.largeTitle) - .fontWeight(.bold) - .padding() - - Text(StringResources.Detail.description) - .foregroundColor(.secondary) - .multilineTextAlignment(.center) - .padding() - - Spacer() } - .navigationTitle(StringResources.Detail.title) - .navigationBarTitleDisplayMode(.inline) - .toolbar { - ToolbarItem(placement: .navigationBarTrailing) { - Button(StringResources.Navigation.close) { - // 닫기 액션 - } + + // PartView 탭 + Tab(value: .parts, role: .search) { + PartView() + .environmentObject(partViewModel) + } label: { + Label { + Text(StringResources.Tabs.parts) + } icon: { + Image("parts") + .renderingMode(.template) + .foregroundStyle(Color.text) } } } + .accentColor(.blue) } } diff --git a/SampoomManagement/Core/Resources/StringResources.swift b/SampoomManagement/Core/Resources/StringResources.swift index c4736cd..874dd17 100644 --- a/SampoomManagement/Core/Resources/StringResources.swift +++ b/SampoomManagement/Core/Resources/StringResources.swift @@ -16,10 +16,13 @@ struct StringResources { // MARK: - Tabs struct Tabs { - static let parts = "부품" - static let inventory = "인벤토리" - static let profile = "프로필" - static let settings = "설정" + static let dashboard = "대시보드" + static let delivery = "출고목록" + static let cart = "장바구니" + static let orders = "주문관리" + static let parts = "부품조회" + static let employee = "직원관리" + static let setting = "설정" } // MARK: - Messages diff --git a/SampoomManagement/Features/Part/UI/PartView.swift b/SampoomManagement/Features/Part/UI/PartView.swift index 4a0ff66..aee7f01 100644 --- a/SampoomManagement/Features/Part/UI/PartView.swift +++ b/SampoomManagement/Features/Part/UI/PartView.swift @@ -9,6 +9,7 @@ import SwiftUI struct PartView: View { @EnvironmentObject var viewModel: PartViewModel + @State var searchString = "" var body: some View { NavigationStack { @@ -16,6 +17,7 @@ struct PartView: View { contentView } .navigationBarTitle(Text("부품")) + .searchable(text: $searchString) } } diff --git a/SampoomManagement/Resources/Assets.xcassets/Background.colorset/Contents.json b/SampoomManagement/Resources/Assets.xcassets/Background.colorset/Contents.json new file mode 100644 index 0000000..12e74dd --- /dev/null +++ b/SampoomManagement/Resources/Assets.xcassets/Background.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xF5", + "green" : "0xF5", + "red" : "0xF5" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x1B", + "green" : "0x18", + "red" : "0x17" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SampoomManagement/Resources/Assets.xcassets/Background_Card.colorset/Contents.json b/SampoomManagement/Resources/Assets.xcassets/Background_Card.colorset/Contents.json new file mode 100644 index 0000000..8a7172d --- /dev/null +++ b/SampoomManagement/Resources/Assets.xcassets/Background_Card.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFF", + "green" : "0xFF", + "red" : "0xFF" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x3F", + "green" : "0x39", + "red" : "0x36" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SampoomManagement/Resources/Assets.xcassets/Text.colorset/Contents.json b/SampoomManagement/Resources/Assets.xcassets/Text.colorset/Contents.json new file mode 100644 index 0000000..9b2acbc --- /dev/null +++ b/SampoomManagement/Resources/Assets.xcassets/Text.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x3F", + "green" : "0x39", + "red" : "0x36" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFF", + "green" : "0xFF", + "red" : "0xFF" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SampoomManagement/Resources/Assets.xcassets/cart.imageset/Contents.json b/SampoomManagement/Resources/Assets.xcassets/cart.imageset/Contents.json new file mode 100644 index 0000000..c3c69df --- /dev/null +++ b/SampoomManagement/Resources/Assets.xcassets/cart.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "cart.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SampoomManagement/Resources/Assets.xcassets/cart.imageset/cart.svg b/SampoomManagement/Resources/Assets.xcassets/cart.imageset/cart.svg new file mode 100644 index 0000000..6df8577 --- /dev/null +++ b/SampoomManagement/Resources/Assets.xcassets/cart.imageset/cart.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/SampoomManagement/Resources/Assets.xcassets/dashboard.imageset/Contents.json b/SampoomManagement/Resources/Assets.xcassets/dashboard.imageset/Contents.json new file mode 100644 index 0000000..9475a51 --- /dev/null +++ b/SampoomManagement/Resources/Assets.xcassets/dashboard.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "dashboard.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SampoomManagement/Resources/Assets.xcassets/dashboard.imageset/dashboard.svg b/SampoomManagement/Resources/Assets.xcassets/dashboard.imageset/dashboard.svg new file mode 100644 index 0000000..6374b08 --- /dev/null +++ b/SampoomManagement/Resources/Assets.xcassets/dashboard.imageset/dashboard.svg @@ -0,0 +1,3 @@ + + + diff --git a/SampoomManagement/Resources/Assets.xcassets/delivery.imageset/Contents.json b/SampoomManagement/Resources/Assets.xcassets/delivery.imageset/Contents.json new file mode 100644 index 0000000..b5b9b10 --- /dev/null +++ b/SampoomManagement/Resources/Assets.xcassets/delivery.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "delivery.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SampoomManagement/Resources/Assets.xcassets/delivery.imageset/delivery.svg b/SampoomManagement/Resources/Assets.xcassets/delivery.imageset/delivery.svg new file mode 100644 index 0000000..224eaca --- /dev/null +++ b/SampoomManagement/Resources/Assets.xcassets/delivery.imageset/delivery.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/SampoomManagement/Resources/Assets.xcassets/employee.imageset/Contents.json b/SampoomManagement/Resources/Assets.xcassets/employee.imageset/Contents.json new file mode 100644 index 0000000..89cc598 --- /dev/null +++ b/SampoomManagement/Resources/Assets.xcassets/employee.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "employee.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SampoomManagement/Resources/Assets.xcassets/employee.imageset/employee.svg b/SampoomManagement/Resources/Assets.xcassets/employee.imageset/employee.svg new file mode 100644 index 0000000..13048c4 --- /dev/null +++ b/SampoomManagement/Resources/Assets.xcassets/employee.imageset/employee.svg @@ -0,0 +1,3 @@ + + + diff --git a/SampoomManagement/Resources/Assets.xcassets/orders.imageset/Contents.json b/SampoomManagement/Resources/Assets.xcassets/orders.imageset/Contents.json new file mode 100644 index 0000000..9db145f --- /dev/null +++ b/SampoomManagement/Resources/Assets.xcassets/orders.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "orders.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SampoomManagement/Resources/Assets.xcassets/orders.imageset/orders.svg b/SampoomManagement/Resources/Assets.xcassets/orders.imageset/orders.svg new file mode 100644 index 0000000..02401b0 --- /dev/null +++ b/SampoomManagement/Resources/Assets.xcassets/orders.imageset/orders.svg @@ -0,0 +1,3 @@ + + + diff --git a/SampoomManagement/Resources/Assets.xcassets/parts.imageset/Contents.json b/SampoomManagement/Resources/Assets.xcassets/parts.imageset/Contents.json new file mode 100644 index 0000000..b08d848 --- /dev/null +++ b/SampoomManagement/Resources/Assets.xcassets/parts.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "parts.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SampoomManagement/Resources/Assets.xcassets/parts.imageset/parts.svg b/SampoomManagement/Resources/Assets.xcassets/parts.imageset/parts.svg new file mode 100644 index 0000000..e8b992e --- /dev/null +++ b/SampoomManagement/Resources/Assets.xcassets/parts.imageset/parts.svg @@ -0,0 +1,3 @@ + + + diff --git a/SampoomManagement/Resources/Assets.xcassets/search.imageset/Contents.json b/SampoomManagement/Resources/Assets.xcassets/search.imageset/Contents.json new file mode 100644 index 0000000..4a3f4da --- /dev/null +++ b/SampoomManagement/Resources/Assets.xcassets/search.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "search.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SampoomManagement/Resources/Assets.xcassets/search.imageset/search.svg b/SampoomManagement/Resources/Assets.xcassets/search.imageset/search.svg new file mode 100644 index 0000000..35a89bd --- /dev/null +++ b/SampoomManagement/Resources/Assets.xcassets/search.imageset/search.svg @@ -0,0 +1,3 @@ + + + diff --git a/SampoomManagement/Resources/Assets.xcassets/settings.imageset/Contents.json b/SampoomManagement/Resources/Assets.xcassets/settings.imageset/Contents.json new file mode 100644 index 0000000..8446dd8 --- /dev/null +++ b/SampoomManagement/Resources/Assets.xcassets/settings.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "settings.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SampoomManagement/Resources/Assets.xcassets/settings.imageset/settings.svg b/SampoomManagement/Resources/Assets.xcassets/settings.imageset/settings.svg new file mode 100644 index 0000000..4ca7228 --- /dev/null +++ b/SampoomManagement/Resources/Assets.xcassets/settings.imageset/settings.svg @@ -0,0 +1,10 @@ + + + + + + + + + + From 20c85af057c3d3b0903c4186080d8fb0c33cac5d Mon Sep 17 00:00:00 2001 From: Sangyoon Date: Sat, 11 Oct 2025 21:51:47 +0900 Subject: [PATCH 10/10] =?UTF-8?q?#6=20[FEAT]=20=EA=B3=B5=ED=86=B5=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B5=AC=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Core/UI/Components/CommonButton.swift | 228 +++++++++++++++ .../Core/UI/Components/CommonTextField.swift | 265 ++++++++++++++++++ 2 files changed, 493 insertions(+) create mode 100644 SampoomManagement/Core/UI/Components/CommonButton.swift create mode 100644 SampoomManagement/Core/UI/Components/CommonTextField.swift diff --git a/SampoomManagement/Core/UI/Components/CommonButton.swift b/SampoomManagement/Core/UI/Components/CommonButton.swift new file mode 100644 index 0000000..cb1f91a --- /dev/null +++ b/SampoomManagement/Core/UI/Components/CommonButton.swift @@ -0,0 +1,228 @@ +// +// CommonButton.swift +// SampoomManagement +// +// Created by 채상윤 on 9/29/25. +// + +import SwiftUI + +// MARK: - Button Types +enum ButtonType { + case filled // 채워진 버튼 + case outlined // 테두리만 있는 버튼 +} + +// MARK: - Button Sizes +enum ButtonSize { + case small + case medium + case large + + var height: CGFloat { + switch self { + case .small: return 32 + case .medium: return 44 + case .large: return 52 + } + } + + var font: Font { + switch self { + case .small: return .system(size: 14, weight: .medium) + case .medium: return .system(size: 16, weight: .medium) + case .large: return .system(size: 18, weight: .semibold) + } + } +} + +// MARK: - CommonButton +struct CommonButton: View { + let title: String + let type: ButtonType + let size: ButtonSize + let icon: String? + let iconPosition: IconPosition + let isEnabled: Bool + let backgroundColor: Color? + let textColor: Color? + let borderColor: Color? + let action: () -> Void + + init( + _ title: String, + type: ButtonType = .filled, + size: ButtonSize = .medium, + icon: String? = nil, + iconPosition: IconPosition = .leading, + isEnabled: Bool = true, + backgroundColor: Color? = nil, + textColor: Color? = nil, + borderColor: Color? = nil, + action: @escaping () -> Void + ) { + self.title = title + self.type = type + self.size = size + self.icon = icon + self.iconPosition = iconPosition + self.isEnabled = isEnabled + self.backgroundColor = backgroundColor + self.textColor = textColor + self.borderColor = borderColor + self.action = action + } + + var body: some View { + Button(action: action) { + HStack(spacing: 8) { + if let icon = icon, iconPosition == .leading { + Image(systemName: icon) + .font(size.font) + } + + Text(title) + .font(size.font) + + if let icon = icon, iconPosition == .trailing { + Image(systemName: icon) + .font(size.font) + } + } + .frame(height: size.height) + .frame(maxWidth: .infinity) + .foregroundColor(buttonTextColor) + .background(buttonBackgroundColor) + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(buttonBorderColor, lineWidth: borderWidth) + ) + .cornerRadius(8) + } + .disabled(!isEnabled) + .opacity(isEnabled ? 1.0 : 0.6) + .animation(.easeInOut(duration: 0.2), value: isEnabled) + } + + // MARK: - Button Styling + private var buttonBackgroundColor: Color { + if !isEnabled { + return .gray + } + + if let customColor = backgroundColor { + return customColor + } + + switch type { + case .filled: + return Color(red: 0.5, green: 0.2, blue: 0.8) // 기본 보라색 + case .outlined: + return .clear + } + } + + private var buttonTextColor: Color { + if !isEnabled { + return .white + } + + if let customColor = textColor { + return customColor + } + + switch type { + case .filled: + return .white + case .outlined: + return borderColor ?? .blue + } + } + + private var buttonBorderColor: Color { + if !isEnabled { + return .clear + } + + if let customColor = borderColor { + return customColor + } + + switch type { + case .filled: + return .clear + case .outlined: + return .blue + } + } + + private var borderWidth: CGFloat { + switch type { + case .filled: + return 0 + case .outlined: + return 1 + } + } +} + +// MARK: - Icon Position +enum IconPosition { + case leading + case trailing +} + +// MARK: - Preview +#Preview { + VStack(spacing: 16) { + // Filled Button (기본 보라색) + CommonButton("Button", type: .filled) { + print("Filled button tapped") + } + + // Filled Button with Custom Color + CommonButton("Button", type: .filled, backgroundColor: .blue, textColor: .white) { + print("Custom filled button tapped") + } + + // Filled Button with Icon + CommonButton("Button", type: .filled, icon: "phone.fill", backgroundColor: .green, textColor: .white) { + print("Filled button with icon tapped") + } + + // Outlined Button (기본 파란색) + CommonButton("Button", type: .outlined) { + print("Outlined button tapped") + } + + // Outlined Button with Custom Color + CommonButton("Button", type: .outlined, textColor: .red, borderColor: .red) { + print("Custom outlined button tapped") + } + + // Outlined Button (Gray) + CommonButton("Button", type: .outlined, textColor: .gray, borderColor: .gray) { + print("Gray outlined button tapped") + } + + // Disabled Button + CommonButton("Button", isEnabled: false) { + print("Disabled button tapped") + } + + // Size Examples + HStack(spacing: 16) { + CommonButton("Small", size: .small, backgroundColor: .orange) { } + CommonButton("Medium", size: .medium, backgroundColor: .purple) { } + CommonButton("Large", size: .large, backgroundColor: .pink) { } + } + + // Icon Position Examples + HStack(spacing: 16) { + CommonButton("Leading", icon: "star.fill", iconPosition: .leading, backgroundColor: .yellow, textColor: .black) { } + CommonButton("Trailing", type: .outlined, icon: "arrow.right", iconPosition: .trailing, textColor: .cyan, borderColor: .cyan) { } + } + } + .padding() + .background(Color.black) +} diff --git a/SampoomManagement/Core/UI/Components/CommonTextField.swift b/SampoomManagement/Core/UI/Components/CommonTextField.swift new file mode 100644 index 0000000..1225005 --- /dev/null +++ b/SampoomManagement/Core/UI/Components/CommonTextField.swift @@ -0,0 +1,265 @@ +// +// CommonTextField.swift +// SampoomManagement +// +// Created by 채상윤 on 9/29/25. +// + +import SwiftUI + +// MARK: - TextField Types +enum TextFieldType { + case email + case password + case text + case number +} + +// MARK: - TextField Sizes +enum TextFieldSize { + case small + case medium + case large + + var height: CGFloat { + switch self { + case .small: return 36 + case .medium: return 44 + case .large: return 52 + } + } + + var font: Font { + switch self { + case .small: return .system(size: 14, weight: .regular) + case .medium: return .system(size: 16, weight: .regular) + case .large: return .system(size: 18, weight: .regular) + } + } + + var padding: EdgeInsets { + switch self { + case .small: return EdgeInsets(top: 8, leading: 12, bottom: 8, trailing: 12) + case .medium: return EdgeInsets(top: 12, leading: 16, bottom: 12, trailing: 16) + case .large: return EdgeInsets(top: 16, leading: 20, bottom: 16, trailing: 20) + } + } +} + +// MARK: - CommonTextField +struct CommonTextField: View { + @Environment(\.colorScheme) private var colorScheme + @State private var isPasswordVisible = false + @State private var text = "" + + let placeholder: String + let type: TextFieldType + let size: TextFieldSize + let textColor: Color? + let backgroundColor: Color? + let borderColor: Color? + let onTextChange: (String) -> Void + + init( + placeholder: String, + type: TextFieldType = .text, + size: TextFieldSize = .medium, + textColor: Color? = nil, + backgroundColor: Color? = nil, + borderColor: Color? = nil, + onTextChange: @escaping (String) -> Void = { _ in } + ) { + self.placeholder = placeholder + self.type = type + self.size = size + self.textColor = textColor + self.backgroundColor = backgroundColor + self.borderColor = borderColor + self.onTextChange = onTextChange + } + + var body: some View { + HStack { + // Text Field + Group { + if type == .password && !isPasswordVisible { + SecureField(placeholder, text: $text) + .textFieldStyle(PlainTextFieldStyle()) + } else { + TextField(placeholder, text: $text) + .keyboardType(keyboardType) + .textInputAutocapitalization(autocapitalization) + .disableAutocorrection(disableAutocorrection) + .textFieldStyle(PlainTextFieldStyle()) + } + } + .font(size.font) + .foregroundColor(buttonTextColor) + + // Password Toggle Button (inside TextField) + if type == .password { + Button(action: { + isPasswordVisible.toggle() + }) { + Image(systemName: isPasswordVisible ? "eye.slash" : "eye") + .foregroundColor(iconColor) + .font(.system(size: 16, weight: .medium)) + } + .padding(.trailing, 8) + } + } + .padding(size.padding) + .background(buttonBackgroundColor) + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(buttonBorderColor, lineWidth: 1) + ) + .onChange(of: text) { oldValue, newValue in + onTextChange(newValue) + } + } + + // MARK: - Computed Properties + private var buttonTextColor: Color { + if let textColor = textColor { + return textColor + } + + // 다크모드 고려한 기본 색상 + switch colorScheme { + case .dark: + return text.isEmpty ? .gray : .white + case .light: + return text.isEmpty ? .gray : .black + @unknown default: + return text.isEmpty ? .gray : .primary + } + } + + private var buttonBackgroundColor: Color { + if let backgroundColor = backgroundColor { + return backgroundColor + } + + // 다크모드 고려한 기본 배경색 + switch colorScheme { + case .dark: + return Color(red: 0.1, green: 0.1, blue: 0.1) // 매우 어두운 회색 + case .light: + return .white + @unknown default: + return Color(.systemBackground) + } + } + + private var buttonBorderColor: Color { + if let borderColor = borderColor { + return borderColor + } + + // 다크모드 고려한 기본 테두리색 + switch colorScheme { + case .dark: + return .gray.opacity(0.3) + case .light: + return .gray.opacity(0.3) + @unknown default: + return .gray.opacity(0.3) + } + } + + private var iconColor: Color { + switch colorScheme { + case .dark: + return .gray + case .light: + return .gray + @unknown default: + return .gray + } + } + + private var keyboardType: UIKeyboardType { + switch type { + case .email: + return .emailAddress + case .number: + return .numberPad + case .password, .text: + return .default + } + } + + private var autocapitalization: TextInputAutocapitalization { + switch type { + case .email: + return .never + case .password: + return .never + case .text: + return .sentences + case .number: + return .never + } + } + + private var disableAutocorrection: Bool { + switch type { + case .email, .password, .number: + return true + case .text: + return false + } + } +} + +// MARK: - Preview +#Preview { + VStack(spacing: 16) { + // Email Input (Placeholder) + CommonTextField( + placeholder: "이메일 입력", + type: .email + ) { text in + print("Email: \(text)") + } + + // Email Input (Filled) + CommonTextField( + placeholder: "이메일 입력", + type: .email + ) { text in + print("Email: \(text)") + } + + // Password Input (Placeholder) + CommonTextField( + placeholder: "비밀번호 입력", + type: .password + ) { text in + print("Password: \(text)") + } + + // Password Input (Filled) + CommonTextField( + placeholder: "비밀번호 입력", + type: .password + ) { text in + print("Password: \(text)") + } + + // Custom Colors + CommonTextField( + placeholder: "커스텀 색상", + type: .text, + textColor: .blue, + backgroundColor: .yellow.opacity(0.1), + borderColor: .blue + ) { text in + print("Custom: \(text)") + } + } + .padding() + .background(Color(.systemBackground)) +} +