diff --git a/week03/Megabox/MegaboxApp.swift b/week03/Megabox/MegaboxApp.swift deleted file mode 100644 index b48eb5c..0000000 --- a/week03/Megabox/MegaboxApp.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// MegaboxApp.swift -// Megabox -// -// Created by 엄지용 on 9/19/25. -// - -import SwiftUI - -@main -struct MegaboxApp: App { - @State private var userViewModel = UserViewModel() // 이 객체 생성하고 State로 메모리에 유지 - var body: some Scene { - - WindowGroup { - SplashView() - .environment(userViewModel) - } - } -} diff --git a/week03/Megabox/Model/LoginModel.swift b/week03/Megabox/Model/LoginModel.swift deleted file mode 100644 index b2841c9..0000000 --- a/week03/Megabox/Model/LoginModel.swift +++ /dev/null @@ -1,10 +0,0 @@ -import Foundation -import SwiftUI - -struct LoginModel { - - @AppStorage("username") private var username: String = "" - @AppStorage("password") private var password: String = "" - - -} diff --git a/week03/Megabox/ViewModels/LoginViewModel.swift b/week03/Megabox/ViewModels/LoginViewModel.swift deleted file mode 100644 index 637722e..0000000 --- a/week03/Megabox/ViewModels/LoginViewModel.swift +++ /dev/null @@ -1,27 +0,0 @@ -// LoginViewModel.swift - -import Foundation -import SwiftUI - -@Observable -class LoginViewModel { - - private var userViewModel: UserViewModel - - init(userViewModel: UserViewModel) { - self.userViewModel = userViewModel - } - - // 로그인 성공 여부를 Bool 값으로 반환 - func login(username: String, password: String) { - if username == "Eom175" && password == "eom175" { - print("로그인 성공!") - - // 로그인 성공 시, 공유 userViewModel의 상태를 변경 - userViewModel.username = username - userViewModel.isLoggedIn = true - } else { - print("로그인 실패: 아이디 또는 비밀번호가 다릅니다.") - } - } -} diff --git a/week03/Megabox/ViewModels/MovieCardsViewModel.swift b/week03/Megabox/ViewModels/MovieCardsViewModel.swift deleted file mode 100644 index cebc21f..0000000 --- a/week03/Megabox/ViewModels/MovieCardsViewModel.swift +++ /dev/null @@ -1,23 +0,0 @@ -import SwiftUI - -import Foundation - -@Observable -class MovieCardsViewModel{ - - var movieCards: [MovieCards] = [] - - init() { - let azzul = MovieCards(image: Image("어쩔수가없다"), booking: true, movieName: "어쩔수가없다",watchedStatus: "20만", movieNameEn: "Can't Help It") - let guikal = MovieCards(image: Image("귀멸의칼날"), booking: true, movieName: "극장판 귀멸의칼날",watchedStatus: "1", movieNameEn: "Demon Slayer") - let f1 = MovieCards(image: Image("f1"), booking: true, movieName: "F1 더 무비", watchedStatus: "1", movieNameEn: "F1: The Movie") - let face = MovieCards(image: Image("얼굴"), booking: true, movieName: "얼굴", watchedStatus: "20만", movieNameEn: "Face") - let hime = MovieCards(image: Image("모노노케히메"), booking: true, movieName: "모노노케히메", watchedStatus: "20만", movieNameEn: "Princess Mononoke") - - self.movieCards = [azzul, guikal, f1, hime] - - } - - - -} diff --git a/week03/Megabox/ViewModels/UserViewModel.swift b/week03/Megabox/ViewModels/UserViewModel.swift deleted file mode 100644 index d95f492..0000000 --- a/week03/Megabox/ViewModels/UserViewModel.swift +++ /dev/null @@ -1,32 +0,0 @@ -//여기서는 사용자 정보만 처리 -import Foundation -import SwiftUI - -@Observable -class UserViewModel { - var username: String = "" - var password: String = "" - var isLoggedIn: Bool = false - - private let usernameKey = "username" - private let passwordKey = "password" - - init() { - self.username = UserDefaults.standard.string(forKey: usernameKey) ?? "" - self.password = UserDefaults.standard.string(forKey: passwordKey) ?? "" - } - - func saveUsername(newUsername: String) { - UserDefaults.standard.set(newUsername, forKey: usernameKey) - self.username = newUsername - print("\(newUsername)으로 이름 변경 및 저장 완료") - } - - func saveCredentials(username: String, password: String) { - UserDefaults.standard.set(username, forKey: usernameKey) - UserDefaults.standard.set(password, forKey: passwordKey) - self.username = username - self.password = password - print("아이디와 비밀번호 저장 완료") - } -} diff --git a/week03/Megabox/Views/Tabviews/Tabs.swift b/week03/Megabox/Views/Tabviews/Tabs.swift deleted file mode 100644 index 5a828f0..0000000 --- a/week03/Megabox/Views/Tabviews/Tabs.swift +++ /dev/null @@ -1,56 +0,0 @@ -import SwiftUI - -struct Tabs: View { - @State private var selectedTab = 0 - - @Environment(UserViewModel.self) private var userViewModel - - - var body: some View { - - TabView(selection: $selectedTab){ - - - HomeView() - .tabItem { - Image(systemName: "house.fill") - Text("홈") - } - .tag(0) // 첫 번째 탭의 태그 - - Text("바로 예매") - .tabItem { - Image(systemName: "movieclapper") - Text("바로 예매") - } - .tag(1) // 두 번째 탭의 태그 - - Text("모바일 오더") - .tabItem { - Image(systemName: "popcorn") - Text("모바일오더") - } - .tag(2) // 세 번째 탭의 태그 - - NavigationStack { - ProfileView() - } - .tabItem { - Image(systemName: "person.fill") - Text("마이 페이지") - } - .tag(3) - } - .navigationBarBackButtonHidden(true) - - - - } -} - -#Preview { - Tabs().environment(UserViewModel()) -} - - - diff --git a/week03/Megabox.xcodeproj/project.pbxproj b/week06/Megabox.xcodeproj/project.pbxproj similarity index 77% rename from week03/Megabox.xcodeproj/project.pbxproj rename to week06/Megabox.xcodeproj/project.pbxproj index 6794ff4..aec64df 100644 --- a/week03/Megabox.xcodeproj/project.pbxproj +++ b/week06/Megabox.xcodeproj/project.pbxproj @@ -6,6 +6,15 @@ objectVersion = 77; objects = { +/* Begin PBXBuildFile section */ + 2C1BA1012EC0B50B00271AFC /* KakaoSDKCommon in Frameworks */ = {isa = PBXBuildFile; productRef = 2C1BA1002EC0B50B00271AFC /* KakaoSDKCommon */; }; + 2C1BA1032EC0B51300271AFC /* KakaoSDKAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 2C1BA1022EC0B51300271AFC /* KakaoSDKAuth */; }; + 2C1BA1052EC0B51900271AFC /* KakaoSDKUser in Frameworks */ = {isa = PBXBuildFile; productRef = 2C1BA1042EC0B51900271AFC /* KakaoSDKUser */; }; + 2C37507F2ECACB4300342F7B /* CombineMoya in Frameworks */ = {isa = PBXBuildFile; productRef = 2C37507E2ECACB4300342F7B /* CombineMoya */; }; + 2C3750812ECACB4300342F7B /* Moya in Frameworks */ = {isa = PBXBuildFile; productRef = 2C3750802ECACB4300342F7B /* Moya */; }; + 2CA6BFBE2EBF98740073B32F /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 2CA6BFBD2EBF98740073B32F /* Alamofire */; }; +/* End PBXBuildFile section */ + /* Begin PBXContainerItemProxy section */ 2C80F02E2E7CE1EB00752AE4 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -24,6 +33,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 2C3750662ECA163000342F7B /* TMDBAPI.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = TMDBAPI.xcconfig; sourceTree = ""; }; 2C80F0202E7CE1EB00752AE4 /* Megabox.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Megabox.app; sourceTree = BUILT_PRODUCTS_DIR; }; 2C80F02D2E7CE1EB00752AE4 /* MegaboxTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MegaboxTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 2C80F0372E7CE1EB00752AE4 /* MegaboxUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MegaboxUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -65,6 +75,12 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 2C1BA1052EC0B51900271AFC /* KakaoSDKUser in Frameworks */, + 2C1BA1012EC0B50B00271AFC /* KakaoSDKCommon in Frameworks */, + 2C3750812ECACB4300342F7B /* Moya in Frameworks */, + 2CA6BFBE2EBF98740073B32F /* Alamofire in Frameworks */, + 2C37507F2ECACB4300342F7B /* CombineMoya in Frameworks */, + 2C1BA1032EC0B51300271AFC /* KakaoSDKAuth in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -91,6 +107,7 @@ 2C80F0222E7CE1EB00752AE4 /* Megabox */, 2C80F0302E7CE1EB00752AE4 /* MegaboxTests */, 2C80F03A2E7CE1EB00752AE4 /* MegaboxUITests */, + 2CA6BFBB2EBF970A0073B32F /* Frameworks */, 2C80F0212E7CE1EB00752AE4 /* Products */, ); sourceTree = ""; @@ -105,6 +122,13 @@ name = Products; sourceTree = ""; }; + 2CA6BFBB2EBF970A0073B32F /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -125,6 +149,12 @@ ); name = Megabox; packageProductDependencies = ( + 2CA6BFBD2EBF98740073B32F /* Alamofire */, + 2C1BA1002EC0B50B00271AFC /* KakaoSDKCommon */, + 2C1BA1022EC0B51300271AFC /* KakaoSDKAuth */, + 2C1BA1042EC0B51900271AFC /* KakaoSDKUser */, + 2C37507E2ECACB4300342F7B /* CombineMoya */, + 2C3750802ECACB4300342F7B /* Moya */, ); productName = Megabox; productReference = 2C80F0202E7CE1EB00752AE4 /* Megabox.app */; @@ -208,6 +238,11 @@ ); mainGroup = 2C80F0172E7CE1EB00752AE4; minimizedProjectReferenceProxies = 1; + packageReferences = ( + 2CA6BFBC2EBF97820073B32F /* XCRemoteSwiftPackageReference "Alamofire" */, + 2CA6BFC12EC0AA040073B32F /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */, + 2C374FE32EC9FC6C00342F7B /* XCRemoteSwiftPackageReference "Moya" */, + ); preferredProjectObjectVersion = 77; productRefGroup = 2C80F0212E7CE1EB00752AE4 /* Products */; projectDirPath = ""; @@ -282,8 +317,9 @@ /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ - 2C80F03F2E7CE1EB00752AE4 /* Debug */ = { + 2C80F03F2E7CE1EB00752AE4 /* Relaese */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 2C3750662ECA163000342F7B /* TMDBAPI.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; @@ -344,10 +380,11 @@ SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; - name = Debug; + name = Relaese; }; - 2C80F0402E7CE1EB00752AE4 /* Release */ = { + 2C80F0402E7CE1EB00752AE4 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 2C3750662ECA163000342F7B /* TMDBAPI.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; @@ -401,9 +438,9 @@ SWIFT_COMPILATION_MODE = wholemodule; VALIDATE_PRODUCT = YES; }; - name = Release; + name = Debug; }; - 2C80F0422E7CE1EB00752AE4 /* Debug */ = { + 2C80F0422E7CE1EB00752AE4 /* Relaese */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; @@ -430,9 +467,9 @@ SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; - name = Debug; + name = Relaese; }; - 2C80F0432E7CE1EB00752AE4 /* Release */ = { + 2C80F0432E7CE1EB00752AE4 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; @@ -459,9 +496,9 @@ SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; - name = Release; + name = Debug; }; - 2C80F0452E7CE1EB00752AE4 /* Debug */ = { + 2C80F0452E7CE1EB00752AE4 /* Relaese */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; @@ -478,9 +515,9 @@ TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Megabox.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Megabox"; }; - name = Debug; + name = Relaese; }; - 2C80F0462E7CE1EB00752AE4 /* Release */ = { + 2C80F0462E7CE1EB00752AE4 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; @@ -497,9 +534,9 @@ TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Megabox.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Megabox"; }; - name = Release; + name = Debug; }; - 2C80F0482E7CE1EB00752AE4 /* Debug */ = { + 2C80F0482E7CE1EB00752AE4 /* Relaese */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; @@ -514,9 +551,9 @@ TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = Megabox; }; - name = Debug; + name = Relaese; }; - 2C80F0492E7CE1EB00752AE4 /* Release */ = { + 2C80F0492E7CE1EB00752AE4 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; @@ -531,7 +568,7 @@ TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = Megabox; }; - name = Release; + name = Debug; }; /* End XCBuildConfiguration section */ @@ -539,40 +576,100 @@ 2C80F01B2E7CE1EB00752AE4 /* Build configuration list for PBXProject "Megabox" */ = { isa = XCConfigurationList; buildConfigurations = ( - 2C80F03F2E7CE1EB00752AE4 /* Debug */, - 2C80F0402E7CE1EB00752AE4 /* Release */, + 2C80F03F2E7CE1EB00752AE4 /* Relaese */, + 2C80F0402E7CE1EB00752AE4 /* Debug */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = Relaese; }; 2C80F0412E7CE1EB00752AE4 /* Build configuration list for PBXNativeTarget "Megabox" */ = { isa = XCConfigurationList; buildConfigurations = ( - 2C80F0422E7CE1EB00752AE4 /* Debug */, - 2C80F0432E7CE1EB00752AE4 /* Release */, + 2C80F0422E7CE1EB00752AE4 /* Relaese */, + 2C80F0432E7CE1EB00752AE4 /* Debug */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = Relaese; }; 2C80F0442E7CE1EB00752AE4 /* Build configuration list for PBXNativeTarget "MegaboxTests" */ = { isa = XCConfigurationList; buildConfigurations = ( - 2C80F0452E7CE1EB00752AE4 /* Debug */, - 2C80F0462E7CE1EB00752AE4 /* Release */, + 2C80F0452E7CE1EB00752AE4 /* Relaese */, + 2C80F0462E7CE1EB00752AE4 /* Debug */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = Relaese; }; 2C80F0472E7CE1EB00752AE4 /* Build configuration list for PBXNativeTarget "MegaboxUITests" */ = { isa = XCConfigurationList; buildConfigurations = ( - 2C80F0482E7CE1EB00752AE4 /* Debug */, - 2C80F0492E7CE1EB00752AE4 /* Release */, + 2C80F0482E7CE1EB00752AE4 /* Relaese */, + 2C80F0492E7CE1EB00752AE4 /* Debug */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = Relaese; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 2C374FE32EC9FC6C00342F7B /* XCRemoteSwiftPackageReference "Moya" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/Moya/Moya"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 15.0.3; + }; + }; + 2CA6BFBC2EBF97820073B32F /* XCRemoteSwiftPackageReference "Alamofire" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/Alamofire/Alamofire"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 5.10.2; + }; + }; + 2CA6BFC12EC0AA040073B32F /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/kakao/kakao-ios-sdk"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.25.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 2C1BA1002EC0B50B00271AFC /* KakaoSDKCommon */ = { + isa = XCSwiftPackageProductDependency; + package = 2CA6BFC12EC0AA040073B32F /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */; + productName = KakaoSDKCommon; + }; + 2C1BA1022EC0B51300271AFC /* KakaoSDKAuth */ = { + isa = XCSwiftPackageProductDependency; + package = 2CA6BFC12EC0AA040073B32F /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */; + productName = KakaoSDKAuth; + }; + 2C1BA1042EC0B51900271AFC /* KakaoSDKUser */ = { + isa = XCSwiftPackageProductDependency; + package = 2CA6BFC12EC0AA040073B32F /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */; + productName = KakaoSDKUser; + }; + 2C37507E2ECACB4300342F7B /* CombineMoya */ = { + isa = XCSwiftPackageProductDependency; + package = 2C374FE32EC9FC6C00342F7B /* XCRemoteSwiftPackageReference "Moya" */; + productName = CombineMoya; + }; + 2C3750802ECACB4300342F7B /* Moya */ = { + isa = XCSwiftPackageProductDependency; + package = 2C374FE32EC9FC6C00342F7B /* XCRemoteSwiftPackageReference "Moya" */; + productName = Moya; + }; + 2CA6BFBD2EBF98740073B32F /* Alamofire */ = { + isa = XCSwiftPackageProductDependency; + package = 2CA6BFBC2EBF97820073B32F /* XCRemoteSwiftPackageReference "Alamofire" */; + productName = Alamofire; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 2C80F0182E7CE1EB00752AE4 /* Project object */; } diff --git a/week03/Megabox.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/week06/Megabox.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from week03/Megabox.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to week06/Megabox.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/week06/Megabox.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/week06/Megabox.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..d347a95 --- /dev/null +++ b/week06/Megabox.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,51 @@ +{ + "originHash" : "3533c5572a10f0f42dc3ce5cd8e2910aaf27946641326e7d371aad9bd73dd9f7", + "pins" : [ + { + "identity" : "alamofire", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Alamofire/Alamofire", + "state" : { + "revision" : "513364f870f6bfc468f9d2ff0a95caccc10044c5", + "version" : "5.10.2" + } + }, + { + "identity" : "kakao-ios-sdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/kakao/kakao-ios-sdk", + "state" : { + "revision" : "e14a8d1fad75645fd5677a295a8b1956ebd14d3d", + "version" : "2.25.0" + } + }, + { + "identity" : "moya", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Moya/Moya", + "state" : { + "revision" : "c263811c1f3dbf002be9bd83107f7cdc38992b26", + "version" : "15.0.3" + } + }, + { + "identity" : "reactiveswift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ReactiveCocoa/ReactiveSwift.git", + "state" : { + "revision" : "c43bae3dac73fdd3cb906bd5a1914686ca71ed3c", + "version" : "6.7.0" + } + }, + { + "identity" : "rxswift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ReactiveX/RxSwift.git", + "state" : { + "revision" : "5004a18539bd68905c5939aa893075f578f4f03d", + "version" : "6.9.1" + } + } + ], + "version" : 3 +} diff --git a/week03/Megabox.xcodeproj/xcuserdata/eomjiyong.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/week06/Megabox.xcodeproj/xcuserdata/eomjiyong.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist similarity index 100% rename from week03/Megabox.xcodeproj/xcuserdata/eomjiyong.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist rename to week06/Megabox.xcodeproj/xcuserdata/eomjiyong.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist diff --git a/week03/Megabox.xcodeproj/xcuserdata/eomjiyong.xcuserdatad/xcschemes/xcschememanagement.plist b/week06/Megabox.xcodeproj/xcuserdata/eomjiyong.xcuserdatad/xcschemes/xcschememanagement.plist similarity index 100% rename from week03/Megabox.xcodeproj/xcuserdata/eomjiyong.xcuserdatad/xcschemes/xcschememanagement.plist rename to week06/Megabox.xcodeproj/xcuserdata/eomjiyong.xcuserdatad/xcschemes/xcschememanagement.plist diff --git a/week06/Megabox/APIService/APIKey.swift b/week06/Megabox/APIService/APIKey.swift new file mode 100644 index 0000000..50ad010 --- /dev/null +++ b/week06/Megabox/APIService/APIKey.swift @@ -0,0 +1,7 @@ +import Foundation + +enum APIKey { + static var tmdb: String { + Bundle.main.object(forInfoDictionaryKey: "TMDB_API_KEY") as? String ?? "" + } +} diff --git a/week06/Megabox/APIService/BaseTarget.swift b/week06/Megabox/APIService/BaseTarget.swift new file mode 100644 index 0000000..8b8119d --- /dev/null +++ b/week06/Megabox/APIService/BaseTarget.swift @@ -0,0 +1,22 @@ +import Moya +import Foundation + +protocol BaseTarget: TargetType {} + +extension BaseTarget { + + // TMDB v3 movie base URL + var baseURL: URL { + URL(string: "https://api.themoviedb.org/3/movie")! + } + + // 샘플 데이터 (안 쓰면 빈 Data 반환) + var sampleData: Data { + Data() + } + + // 공통 헤더 (필요 없으면 nil) + var headers: [String : String]? { + nil + } +} diff --git a/week06/Megabox/APIService/MovieInfoAPI.swift b/week06/Megabox/APIService/MovieInfoAPI.swift new file mode 100644 index 0000000..33e46ed --- /dev/null +++ b/week06/Megabox/APIService/MovieInfoAPI.swift @@ -0,0 +1,45 @@ +import Moya +import Foundation + +enum MovieInfoAPI { + // page는 기본 1, region은 옵션 + case nowPlaying(page: Int = 1, region: String? = nil) +} + +extension MovieInfoAPI: BaseTarget { + + var path: String { + switch self { + case .nowPlaying: + return "/now_playing" + } + } + + var method: Moya.Method { + return .get + } + + var task: Task { + switch self { + case let .nowPlaying(page, region): + var params: [String: Any] = [ + "language": "ko-KR", + "page": page, + "region": region ?? "KR" // 👈 이 부분 수정 + ] + + + return .requestParameters( + parameters: params, + encoding: URLEncoding.queryString + ) + } + } + + var headers: [String : String]? { + [ + "Accept": "application/json", + "Authorization": "Bearer \(APIKey.tmdb)" + ] + } +} diff --git a/week06/Megabox/APIService/MoyaProvider.swift b/week06/Megabox/APIService/MoyaProvider.swift new file mode 100644 index 0000000..c9fa280 --- /dev/null +++ b/week06/Megabox/APIService/MoyaProvider.swift @@ -0,0 +1,16 @@ +import Moya + +extension MoyaProvider { + func asyncRequest(_ target: Target) async throws -> Response { + try await withCheckedThrowingContinuation { continuation in + self.request(target) { result in + switch result { + case .success(let response): + continuation.resume(returning: response) + case .failure(let error): + continuation.resume(throwing: error) + } + } + } + } +} diff --git a/week03/Megabox/Assets.xcassets/AccentColor.colorset/Contents.json b/week06/Megabox/Assets.xcassets/AccentColor.colorset/Contents.json similarity index 100% rename from week03/Megabox/Assets.xcassets/AccentColor.colorset/Contents.json rename to week06/Megabox/Assets.xcassets/AccentColor.colorset/Contents.json diff --git a/week03/Megabox/Assets.xcassets/AppIcon.appiconset/Contents.json b/week06/Megabox/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from week03/Megabox/Assets.xcassets/AppIcon.appiconset/Contents.json rename to week06/Megabox/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/week03/Megabox/Assets.xcassets/Apple.imageset/Apple.pdf b/week06/Megabox/Assets.xcassets/Apple.imageset/Apple.pdf similarity index 100% rename from week03/Megabox/Assets.xcassets/Apple.imageset/Apple.pdf rename to week06/Megabox/Assets.xcassets/Apple.imageset/Apple.pdf diff --git a/week03/Megabox/Assets.xcassets/Apple.imageset/Contents.json b/week06/Megabox/Assets.xcassets/Apple.imageset/Contents.json similarity index 100% rename from week03/Megabox/Assets.xcassets/Apple.imageset/Contents.json rename to week06/Megabox/Assets.xcassets/Apple.imageset/Contents.json diff --git a/week03/Megabox/Assets.xcassets/Button_1.imageset/Button_1.pdf b/week06/Megabox/Assets.xcassets/Button_1.imageset/Button_1.pdf similarity index 100% rename from week03/Megabox/Assets.xcassets/Button_1.imageset/Button_1.pdf rename to week06/Megabox/Assets.xcassets/Button_1.imageset/Button_1.pdf diff --git a/week03/Megabox/Assets.xcassets/Button_1.imageset/Contents.json b/week06/Megabox/Assets.xcassets/Button_1.imageset/Contents.json similarity index 100% rename from week03/Megabox/Assets.xcassets/Button_1.imageset/Contents.json rename to week06/Megabox/Assets.xcassets/Button_1.imageset/Contents.json diff --git a/week03/Megabox/Assets.xcassets/Contents.json b/week06/Megabox/Assets.xcassets/Contents.json similarity index 100% rename from week03/Megabox/Assets.xcassets/Contents.json rename to week06/Megabox/Assets.xcassets/Contents.json diff --git a/week03/Megabox/Assets.xcassets/Kakao.imageset/Contents.json b/week06/Megabox/Assets.xcassets/Kakao.imageset/Contents.json similarity index 100% rename from week03/Megabox/Assets.xcassets/Kakao.imageset/Contents.json rename to week06/Megabox/Assets.xcassets/Kakao.imageset/Contents.json diff --git a/week03/Megabox/Assets.xcassets/Kakao.imageset/Kakao.pdf b/week06/Megabox/Assets.xcassets/Kakao.imageset/Kakao.pdf similarity index 100% rename from week03/Megabox/Assets.xcassets/Kakao.imageset/Kakao.pdf rename to week06/Megabox/Assets.xcassets/Kakao.imageset/Kakao.pdf diff --git a/week03/Megabox/Assets.xcassets/Naver.imageset/Contents.json b/week06/Megabox/Assets.xcassets/Naver.imageset/Contents.json similarity index 100% rename from week03/Megabox/Assets.xcassets/Naver.imageset/Contents.json rename to week06/Megabox/Assets.xcassets/Naver.imageset/Contents.json diff --git a/week03/Megabox/Assets.xcassets/Naver.imageset/LoginBtn.pdf b/week06/Megabox/Assets.xcassets/Naver.imageset/LoginBtn.pdf similarity index 100% rename from week03/Megabox/Assets.xcassets/Naver.imageset/LoginBtn.pdf rename to week06/Megabox/Assets.xcassets/Naver.imageset/LoginBtn.pdf diff --git a/week03/Megabox/Assets.xcassets/arrow.imageset/Contents.json b/week06/Megabox/Assets.xcassets/arrow.imageset/Contents.json similarity index 100% rename from week03/Megabox/Assets.xcassets/arrow.imageset/Contents.json rename to week06/Megabox/Assets.xcassets/arrow.imageset/Contents.json diff --git a/week03/Megabox/Assets.xcassets/arrow.imageset/arrow.pdf b/week06/Megabox/Assets.xcassets/arrow.imageset/arrow.pdf similarity index 100% rename from week03/Megabox/Assets.xcassets/arrow.imageset/arrow.pdf rename to week06/Megabox/Assets.xcassets/arrow.imageset/arrow.pdf diff --git a/week03/Megabox/Assets.xcassets/image 2.imageset/Contents.json b/week06/Megabox/Assets.xcassets/image 2.imageset/Contents.json similarity index 100% rename from week03/Megabox/Assets.xcassets/image 2.imageset/Contents.json rename to week06/Megabox/Assets.xcassets/image 2.imageset/Contents.json diff --git a/week03/Megabox/Assets.xcassets/image 2.imageset/image 2.pdf b/week06/Megabox/Assets.xcassets/image 2.imageset/image 2.pdf similarity index 100% rename from week03/Megabox/Assets.xcassets/image 2.imageset/image 2.pdf rename to week06/Megabox/Assets.xcassets/image 2.imageset/image 2.pdf diff --git a/week03/Megabox/Assets.xcassets/image 3.imageset/Contents.json b/week06/Megabox/Assets.xcassets/image 3.imageset/Contents.json similarity index 100% rename from week03/Megabox/Assets.xcassets/image 3.imageset/Contents.json rename to week06/Megabox/Assets.xcassets/image 3.imageset/Contents.json diff --git a/week03/Megabox/Assets.xcassets/image 3.imageset/image 3.pdf b/week06/Megabox/Assets.xcassets/image 3.imageset/image 3.pdf similarity index 100% rename from week03/Megabox/Assets.xcassets/image 3.imageset/image 3.pdf rename to week06/Megabox/Assets.xcassets/image 3.imageset/image 3.pdf diff --git "a/week03/Megabox/Assets.xcassets/\354\226\264\354\251\224\354\210\230\352\260\200\354\227\206\353\213\244.imageset/Contents.json" b/week06/Megabox/Assets.xcassets/m-001.imageset/Contents.json similarity index 100% rename from "week03/Megabox/Assets.xcassets/\354\226\264\354\251\224\354\210\230\352\260\200\354\227\206\353\213\244.imageset/Contents.json" rename to week06/Megabox/Assets.xcassets/m-001.imageset/Contents.json diff --git "a/week03/Megabox/Assets.xcassets/\354\226\264\354\251\224\354\210\230\352\260\200\354\227\206\353\213\244.imageset/\354\226\264\354\251\224\354\210\230\352\260\200\354\227\206\353\213\244.pdf" "b/week06/Megabox/Assets.xcassets/m-001.imageset/\354\226\264\354\251\224\354\210\230\352\260\200\354\227\206\353\213\244.pdf" similarity index 100% rename from "week03/Megabox/Assets.xcassets/\354\226\264\354\251\224\354\210\230\352\260\200\354\227\206\353\213\244.imageset/\354\226\264\354\251\224\354\210\230\352\260\200\354\227\206\353\213\244.pdf" rename to "week06/Megabox/Assets.xcassets/m-001.imageset/\354\226\264\354\251\224\354\210\230\352\260\200\354\227\206\353\213\244.pdf" diff --git a/week03/Megabox/Assets.xcassets/f1.imageset/Contents.json b/week06/Megabox/Assets.xcassets/m-002.imageset/Contents.json similarity index 100% rename from week03/Megabox/Assets.xcassets/f1.imageset/Contents.json rename to week06/Megabox/Assets.xcassets/m-002.imageset/Contents.json diff --git a/week03/Megabox/Assets.xcassets/f1.imageset/f1.pdf b/week06/Megabox/Assets.xcassets/m-002.imageset/f1.pdf similarity index 100% rename from week03/Megabox/Assets.xcassets/f1.imageset/f1.pdf rename to week06/Megabox/Assets.xcassets/m-002.imageset/f1.pdf diff --git "a/week03/Megabox/Assets.xcassets/\352\267\200\353\251\270\354\235\230\354\271\274\353\202\240.imageset/Contents.json" b/week06/Megabox/Assets.xcassets/m-003.imageset/Contents.json similarity index 100% rename from "week03/Megabox/Assets.xcassets/\352\267\200\353\251\270\354\235\230\354\271\274\353\202\240.imageset/Contents.json" rename to week06/Megabox/Assets.xcassets/m-003.imageset/Contents.json diff --git "a/week03/Megabox/Assets.xcassets/\352\267\200\353\251\270\354\235\230\354\271\274\353\202\240.imageset/\352\267\200\353\251\270\354\235\230\354\271\274\353\202\240.pdf" "b/week06/Megabox/Assets.xcassets/m-003.imageset/\352\267\200\353\251\270\354\235\230\354\271\274\353\202\240.pdf" similarity index 100% rename from "week03/Megabox/Assets.xcassets/\352\267\200\353\251\270\354\235\230\354\271\274\353\202\240.imageset/\352\267\200\353\251\270\354\235\230\354\271\274\353\202\240.pdf" rename to "week06/Megabox/Assets.xcassets/m-003.imageset/\352\267\200\353\251\270\354\235\230\354\271\274\353\202\240.pdf" diff --git "a/week03/Megabox/Assets.xcassets/\354\226\274\352\265\264.imageset/Contents.json" b/week06/Megabox/Assets.xcassets/m-004.imageset/Contents.json similarity index 100% rename from "week03/Megabox/Assets.xcassets/\354\226\274\352\265\264.imageset/Contents.json" rename to week06/Megabox/Assets.xcassets/m-004.imageset/Contents.json diff --git "a/week03/Megabox/Assets.xcassets/\354\226\274\352\265\264.imageset/\354\226\274\352\265\264.pdf" "b/week06/Megabox/Assets.xcassets/m-004.imageset/\354\226\274\352\265\264.pdf" similarity index 100% rename from "week03/Megabox/Assets.xcassets/\354\226\274\352\265\264.imageset/\354\226\274\352\265\264.pdf" rename to "week06/Megabox/Assets.xcassets/m-004.imageset/\354\226\274\352\265\264.pdf" diff --git "a/week03/Megabox/Assets.xcassets/\353\252\250\353\205\270\353\205\270\354\274\200\355\236\210\353\251\224.imageset/Contents.json" b/week06/Megabox/Assets.xcassets/m-005.imageset/Contents.json similarity index 100% rename from "week03/Megabox/Assets.xcassets/\353\252\250\353\205\270\353\205\270\354\274\200\355\236\210\353\251\224.imageset/Contents.json" rename to week06/Megabox/Assets.xcassets/m-005.imageset/Contents.json diff --git "a/week03/Megabox/Assets.xcassets/\353\252\250\353\205\270\353\205\270\354\274\200\355\236\210\353\251\224.imageset/\353\252\250\353\205\270\353\205\270\354\274\200\355\236\210\353\251\224.pdf" "b/week06/Megabox/Assets.xcassets/m-005.imageset/\353\252\250\353\205\270\353\205\270\354\274\200\355\236\210\353\251\224.pdf" similarity index 100% rename from "week03/Megabox/Assets.xcassets/\353\252\250\353\205\270\353\205\270\354\274\200\355\236\210\353\251\224.imageset/\353\252\250\353\205\270\353\205\270\354\274\200\355\236\210\353\251\224.pdf" rename to "week06/Megabox/Assets.xcassets/m-005.imageset/\353\252\250\353\205\270\353\205\270\354\274\200\355\236\210\353\251\224.pdf" diff --git a/week06/Megabox/Assets.xcassets/m-006.imageset/Contents.json b/week06/Megabox/Assets.xcassets/m-006.imageset/Contents.json new file mode 100644 index 0000000..181483a --- /dev/null +++ b/week06/Megabox/Assets.xcassets/m-006.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "보스.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git "a/week06/Megabox/Assets.xcassets/m-006.imageset/\353\263\264\354\212\244.pdf" "b/week06/Megabox/Assets.xcassets/m-006.imageset/\353\263\264\354\212\244.pdf" new file mode 100644 index 0000000..109ca20 Binary files /dev/null and "b/week06/Megabox/Assets.xcassets/m-006.imageset/\353\263\264\354\212\244.pdf" differ diff --git a/week06/Megabox/Assets.xcassets/m-007.imageset/Contents.json b/week06/Megabox/Assets.xcassets/m-007.imageset/Contents.json new file mode 100644 index 0000000..202433e --- /dev/null +++ b/week06/Megabox/Assets.xcassets/m-007.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "야당.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git "a/week06/Megabox/Assets.xcassets/m-007.imageset/\354\225\274\353\213\271.pdf" "b/week06/Megabox/Assets.xcassets/m-007.imageset/\354\225\274\353\213\271.pdf" new file mode 100644 index 0000000..0a9b3ef Binary files /dev/null and "b/week06/Megabox/Assets.xcassets/m-007.imageset/\354\225\274\353\213\271.pdf" differ diff --git a/week06/Megabox/Assets.xcassets/m-008.imageset/Contents.json b/week06/Megabox/Assets.xcassets/m-008.imageset/Contents.json new file mode 100644 index 0000000..c09e07f --- /dev/null +++ b/week06/Megabox/Assets.xcassets/m-008.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "로즈.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git "a/week06/Megabox/Assets.xcassets/m-008.imageset/\353\241\234\354\246\210.pdf" "b/week06/Megabox/Assets.xcassets/m-008.imageset/\353\241\234\354\246\210.pdf" new file mode 100644 index 0000000..e8e77e3 Binary files /dev/null and "b/week06/Megabox/Assets.xcassets/m-008.imageset/\353\241\234\354\246\210.pdf" differ diff --git a/week03/Megabox/Assets.xcassets/meboxLogo 1.imageset/Contents.json b/week06/Megabox/Assets.xcassets/meboxLogo 1.imageset/Contents.json similarity index 100% rename from week03/Megabox/Assets.xcassets/meboxLogo 1.imageset/Contents.json rename to week06/Megabox/Assets.xcassets/meboxLogo 1.imageset/Contents.json diff --git a/week03/Megabox/Assets.xcassets/meboxLogo 1.imageset/meboxLogo 1.pdf b/week06/Megabox/Assets.xcassets/meboxLogo 1.imageset/meboxLogo 1.pdf similarity index 100% rename from week03/Megabox/Assets.xcassets/meboxLogo 1.imageset/meboxLogo 1.pdf rename to week06/Megabox/Assets.xcassets/meboxLogo 1.imageset/meboxLogo 1.pdf diff --git a/week03/Megabox/Assets.xcassets/meboxLogo 2.imageset/Contents.json b/week06/Megabox/Assets.xcassets/meboxLogo 2.imageset/Contents.json similarity index 100% rename from week03/Megabox/Assets.xcassets/meboxLogo 2.imageset/Contents.json rename to week06/Megabox/Assets.xcassets/meboxLogo 2.imageset/Contents.json diff --git a/week03/Megabox/Assets.xcassets/meboxLogo 2.imageset/meboxLogo 1.pdf b/week06/Megabox/Assets.xcassets/meboxLogo 2.imageset/meboxLogo 1.pdf similarity index 100% rename from week03/Megabox/Assets.xcassets/meboxLogo 2.imageset/meboxLogo 1.pdf rename to week06/Megabox/Assets.xcassets/meboxLogo 2.imageset/meboxLogo 1.pdf diff --git a/week03/Megabox/Assets.xcassets/movie.imageset/Contents.json b/week06/Megabox/Assets.xcassets/movie.imageset/Contents.json similarity index 100% rename from week03/Megabox/Assets.xcassets/movie.imageset/Contents.json rename to week06/Megabox/Assets.xcassets/movie.imageset/Contents.json diff --git a/week03/Megabox/Assets.xcassets/movie.imageset/movie.pdf b/week06/Megabox/Assets.xcassets/movie.imageset/movie.pdf similarity index 100% rename from week03/Megabox/Assets.xcassets/movie.imageset/movie.pdf rename to week06/Megabox/Assets.xcassets/movie.imageset/movie.pdf diff --git a/week03/Megabox/Assets.xcassets/moviefeed.imageset/Contents.json b/week06/Megabox/Assets.xcassets/moviefeed.imageset/Contents.json similarity index 100% rename from week03/Megabox/Assets.xcassets/moviefeed.imageset/Contents.json rename to week06/Megabox/Assets.xcassets/moviefeed.imageset/Contents.json diff --git a/week03/Megabox/Assets.xcassets/moviefeed.imageset/moviefeed.pdf b/week06/Megabox/Assets.xcassets/moviefeed.imageset/moviefeed.pdf similarity index 100% rename from week03/Megabox/Assets.xcassets/moviefeed.imageset/moviefeed.pdf rename to week06/Megabox/Assets.xcassets/moviefeed.imageset/moviefeed.pdf diff --git a/week03/Megabox/Assets.xcassets/sony.imageset/Contents.json b/week06/Megabox/Assets.xcassets/sony.imageset/Contents.json similarity index 100% rename from week03/Megabox/Assets.xcassets/sony.imageset/Contents.json rename to week06/Megabox/Assets.xcassets/sony.imageset/Contents.json diff --git a/week03/Megabox/Assets.xcassets/sony.imageset/sony.pdf b/week06/Megabox/Assets.xcassets/sony.imageset/sony.pdf similarity index 100% rename from week03/Megabox/Assets.xcassets/sony.imageset/sony.pdf rename to week06/Megabox/Assets.xcassets/sony.imageset/sony.pdf diff --git a/week03/Megabox/Assets.xcassets/umc 1.imageset/Contents.json b/week06/Megabox/Assets.xcassets/umc 1.imageset/Contents.json similarity index 100% rename from week03/Megabox/Assets.xcassets/umc 1.imageset/Contents.json rename to week06/Megabox/Assets.xcassets/umc 1.imageset/Contents.json diff --git a/week03/Megabox/Assets.xcassets/umc 1.imageset/umc 1.pdf b/week06/Megabox/Assets.xcassets/umc 1.imageset/umc 1.pdf similarity index 100% rename from week03/Megabox/Assets.xcassets/umc 1.imageset/umc 1.pdf rename to week06/Megabox/Assets.xcassets/umc 1.imageset/umc 1.pdf diff --git "a/week03/Megabox/Assets.xcassets/\352\267\271\354\236\245\353\263\204\354\230\210\353\247\244.imageset/Contents.json" "b/week06/Megabox/Assets.xcassets/\352\267\271\354\236\245\353\263\204\354\230\210\353\247\244.imageset/Contents.json" similarity index 100% rename from "week03/Megabox/Assets.xcassets/\352\267\271\354\236\245\353\263\204\354\230\210\353\247\244.imageset/Contents.json" rename to "week06/Megabox/Assets.xcassets/\352\267\271\354\236\245\353\263\204\354\230\210\353\247\244.imageset/Contents.json" diff --git "a/week03/Megabox/Assets.xcassets/\352\267\271\354\236\245\353\263\204\354\230\210\353\247\244.imageset/\352\267\271\354\236\245\353\263\204\354\230\210\353\247\244.pdf" "b/week06/Megabox/Assets.xcassets/\352\267\271\354\236\245\353\263\204\354\230\210\353\247\244.imageset/\352\267\271\354\236\245\353\263\204\354\230\210\353\247\244.pdf" similarity index 100% rename from "week03/Megabox/Assets.xcassets/\352\267\271\354\236\245\353\263\204\354\230\210\353\247\244.imageset/\352\267\271\354\236\245\353\263\204\354\230\210\353\247\244.pdf" rename to "week06/Megabox/Assets.xcassets/\352\267\271\354\236\245\353\263\204\354\230\210\353\247\244.imageset/\352\267\271\354\236\245\353\263\204\354\230\210\353\247\244.pdf" diff --git "a/week03/Megabox/Assets.xcassets/\353\252\250\353\260\224\354\235\274\354\230\244\353\215\224.imageset/Contents.json" "b/week06/Megabox/Assets.xcassets/\353\252\250\353\260\224\354\235\274\354\230\244\353\215\224.imageset/Contents.json" similarity index 100% rename from "week03/Megabox/Assets.xcassets/\353\252\250\353\260\224\354\235\274\354\230\244\353\215\224.imageset/Contents.json" rename to "week06/Megabox/Assets.xcassets/\353\252\250\353\260\224\354\235\274\354\230\244\353\215\224.imageset/Contents.json" diff --git "a/week03/Megabox/Assets.xcassets/\353\252\250\353\260\224\354\235\274\354\230\244\353\215\224.imageset/\353\252\250\353\260\224\354\235\274\354\230\244\353\215\224.pdf" "b/week06/Megabox/Assets.xcassets/\353\252\250\353\260\224\354\235\274\354\230\244\353\215\224.imageset/\353\252\250\353\260\224\354\235\274\354\230\244\353\215\224.pdf" similarity index 100% rename from "week03/Megabox/Assets.xcassets/\353\252\250\353\260\224\354\235\274\354\230\244\353\215\224.imageset/\353\252\250\353\260\224\354\235\274\354\230\244\353\215\224.pdf" rename to "week06/Megabox/Assets.xcassets/\353\252\250\353\260\224\354\235\274\354\230\244\353\215\224.imageset/\353\252\250\353\260\224\354\235\274\354\230\244\353\215\224.pdf" diff --git "a/week03/Megabox/Assets.xcassets/\354\230\201\355\231\224\353\263\204\354\230\210\353\247\244.imageset/Contents.json" "b/week06/Megabox/Assets.xcassets/\354\230\201\355\231\224\353\263\204\354\230\210\353\247\244.imageset/Contents.json" similarity index 100% rename from "week03/Megabox/Assets.xcassets/\354\230\201\355\231\224\353\263\204\354\230\210\353\247\244.imageset/Contents.json" rename to "week06/Megabox/Assets.xcassets/\354\230\201\355\231\224\353\263\204\354\230\210\353\247\244.imageset/Contents.json" diff --git "a/week03/Megabox/Assets.xcassets/\354\230\201\355\231\224\353\263\204\354\230\210\353\247\244.imageset/\354\230\201\355\231\224\353\263\204\354\230\210\353\247\244.pdf" "b/week06/Megabox/Assets.xcassets/\354\230\201\355\231\224\353\263\204\354\230\210\353\247\244.imageset/\354\230\201\355\231\224\353\263\204\354\230\210\353\247\244.pdf" similarity index 100% rename from "week03/Megabox/Assets.xcassets/\354\230\201\355\231\224\353\263\204\354\230\210\353\247\244.imageset/\354\230\201\355\231\224\353\263\204\354\230\210\353\247\244.pdf" rename to "week06/Megabox/Assets.xcassets/\354\230\201\355\231\224\353\263\204\354\230\210\353\247\244.imageset/\354\230\201\355\231\224\353\263\204\354\230\210\353\247\244.pdf" diff --git "a/week03/Megabox/Assets.xcassets/\355\212\271\353\263\204\352\264\200\354\230\210\353\247\244.imageset/Contents.json" "b/week06/Megabox/Assets.xcassets/\355\212\271\353\263\204\352\264\200\354\230\210\353\247\244.imageset/Contents.json" similarity index 100% rename from "week03/Megabox/Assets.xcassets/\355\212\271\353\263\204\352\264\200\354\230\210\353\247\244.imageset/Contents.json" rename to "week06/Megabox/Assets.xcassets/\355\212\271\353\263\204\352\264\200\354\230\210\353\247\244.imageset/Contents.json" diff --git "a/week03/Megabox/Assets.xcassets/\355\212\271\353\263\204\352\264\200\354\230\210\353\247\244.imageset/\355\212\271\353\263\204\352\264\200\354\230\210\353\247\244.pdf" "b/week06/Megabox/Assets.xcassets/\355\212\271\353\263\204\352\264\200\354\230\210\353\247\244.imageset/\355\212\271\353\263\204\352\264\200\354\230\210\353\247\244.pdf" similarity index 100% rename from "week03/Megabox/Assets.xcassets/\355\212\271\353\263\204\352\264\200\354\230\210\353\247\244.imageset/\355\212\271\353\263\204\352\264\200\354\230\210\353\247\244.pdf" rename to "week06/Megabox/Assets.xcassets/\355\212\271\353\263\204\352\264\200\354\230\210\353\247\244.imageset/\355\212\271\353\263\204\352\264\200\354\230\210\353\247\244.pdf" diff --git "a/week03/Megabox/Assets.xcassets/\355\217\254\354\212\244\355\204\260 \354\202\254\354\247\204.imageset/Contents.json" "b/week06/Megabox/Assets.xcassets/\355\217\254\354\212\244\355\204\260 \354\202\254\354\247\204.imageset/Contents.json" similarity index 100% rename from "week03/Megabox/Assets.xcassets/\355\217\254\354\212\244\355\204\260 \354\202\254\354\247\204.imageset/Contents.json" rename to "week06/Megabox/Assets.xcassets/\355\217\254\354\212\244\355\204\260 \354\202\254\354\247\204.imageset/Contents.json" diff --git "a/week03/Megabox/Assets.xcassets/\355\217\254\354\212\244\355\204\260 \354\202\254\354\247\204.imageset/\355\217\254\354\212\244\355\204\260 \354\202\254\354\247\204.pdf" "b/week06/Megabox/Assets.xcassets/\355\217\254\354\212\244\355\204\260 \354\202\254\354\247\204.imageset/\355\217\254\354\212\244\355\204\260 \354\202\254\354\247\204.pdf" similarity index 100% rename from "week03/Megabox/Assets.xcassets/\355\217\254\354\212\244\355\204\260 \354\202\254\354\247\204.imageset/\355\217\254\354\212\244\355\204\260 \354\202\254\354\247\204.pdf" rename to "week06/Megabox/Assets.xcassets/\355\217\254\354\212\244\355\204\260 \354\202\254\354\247\204.imageset/\355\217\254\354\212\244\355\204\260 \354\202\254\354\247\204.pdf" diff --git a/week03/Megabox/Model/Type.swift b/week06/Megabox/EnumTypes/ChartType.swift similarity index 75% rename from week03/Megabox/Model/Type.swift rename to week06/Megabox/EnumTypes/ChartType.swift index 91b12f0..73d3439 100644 --- a/week03/Megabox/Model/Type.swift +++ b/week06/Megabox/EnumTypes/ChartType.swift @@ -6,8 +6,3 @@ enum ChartType { case movieChart case comingSoon } - -enum InfoType{ - case details - case reviews -} diff --git a/week06/Megabox/EnumTypes/InfoType.swift b/week06/Megabox/EnumTypes/InfoType.swift new file mode 100644 index 0000000..2fb4911 --- /dev/null +++ b/week06/Megabox/EnumTypes/InfoType.swift @@ -0,0 +1,6 @@ +import SwiftUI +enum InfoType{ + case details + case reviews +} + diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Blue/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Blue/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Blue/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Blue/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Blue/blue00.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Blue/blue00.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Blue/blue00.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Blue/blue00.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Blue/blue01.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Blue/blue01.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Blue/blue01.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Blue/blue01.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Blue/blue02.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Blue/blue02.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Blue/blue02.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Blue/blue02.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Blue/blue03.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Blue/blue03.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Blue/blue03.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Blue/blue03.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Blue/blue04.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Blue/blue04.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Blue/blue04.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Blue/blue04.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Blue/blue05.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Blue/blue05.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Blue/blue05.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Blue/blue05.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Blue/blue06.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Blue/blue06.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Blue/blue06.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Blue/blue06.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Blue/blue07.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Blue/blue07.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Blue/blue07.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Blue/blue07.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Blue/blue08.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Blue/blue08.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Blue/blue08.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Blue/blue08.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Blue/blue09.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Blue/blue09.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Blue/blue09.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Blue/blue09.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Etc/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Etc/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Etc/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Etc/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Etc/tag.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Etc/tag.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Etc/tag.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Etc/tag.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Grey/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Grey/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Grey/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Grey/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Grey/grey00.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Grey/grey00.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Grey/grey00.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Grey/grey00.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Grey/grey01.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Grey/grey01.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Grey/grey01.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Grey/grey01.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Grey/grey02.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Grey/grey02.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Grey/grey02.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Grey/grey02.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Grey/grey03.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Grey/grey03.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Grey/grey03.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Grey/grey03.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Grey/grey04.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Grey/grey04.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Grey/grey04.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Grey/grey04.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Grey/grey05.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Grey/grey05.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Grey/grey05.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Grey/grey05.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Grey/grey06.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Grey/grey06.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Grey/grey06.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Grey/grey06.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Grey/grey07.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Grey/grey07.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Grey/grey07.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Grey/grey07.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Grey/grey08.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Grey/grey08.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Grey/grey08.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Grey/grey08.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Grey/grey09.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Grey/grey09.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Grey/grey09.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Grey/grey09.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Purple/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Purple/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Purple/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Purple/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Purple/purple00.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Purple/purple00.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Purple/purple00.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Purple/purple00.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Purple/purple01.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Purple/purple01.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Purple/purple01.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Purple/purple01.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Purple/purple02.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Purple/purple02.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Purple/purple02.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Purple/purple02.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Purple/purple03.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Purple/purple03.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Purple/purple03.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Purple/purple03.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Purple/purple04.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Purple/purple04.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Purple/purple04.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Purple/purple04.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Purple/purple05.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Purple/purple05.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Purple/purple05.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Purple/purple05.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Purple/purple06.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Purple/purple06.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Purple/purple06.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Purple/purple06.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Purple/purple07.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Purple/purple07.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Purple/purple07.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Purple/purple07.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Purple/purple08.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Purple/purple08.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Purple/purple08.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Purple/purple08.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/Purple/purple09.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/Purple/purple09.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/Purple/purple09.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/Purple/purple09.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/WhiteBlack/Black.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/WhiteBlack/Black.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/WhiteBlack/Black.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/WhiteBlack/Black.colorset/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/WhiteBlack/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/WhiteBlack/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/WhiteBlack/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/WhiteBlack/Contents.json diff --git a/week03/Megabox/Fonts/ColorSets.xcassets/WhiteBlack/White.colorset/Contents.json b/week06/Megabox/Fonts/ColorSets.xcassets/WhiteBlack/White.colorset/Contents.json similarity index 100% rename from week03/Megabox/Fonts/ColorSets.xcassets/WhiteBlack/White.colorset/Contents.json rename to week06/Megabox/Fonts/ColorSets.xcassets/WhiteBlack/White.colorset/Contents.json diff --git a/week03/Megabox/Fonts/Font.swift b/week06/Megabox/Fonts/Font.swift similarity index 100% rename from week03/Megabox/Fonts/Font.swift rename to week06/Megabox/Fonts/Font.swift diff --git a/week03/Megabox/Fonts/Pretendard-Black.otf b/week06/Megabox/Fonts/Pretendard-Black.otf similarity index 100% rename from week03/Megabox/Fonts/Pretendard-Black.otf rename to week06/Megabox/Fonts/Pretendard-Black.otf diff --git a/week03/Megabox/Fonts/Pretendard-Bold.otf b/week06/Megabox/Fonts/Pretendard-Bold.otf similarity index 100% rename from week03/Megabox/Fonts/Pretendard-Bold.otf rename to week06/Megabox/Fonts/Pretendard-Bold.otf diff --git a/week03/Megabox/Fonts/Pretendard-ExtraBold.otf b/week06/Megabox/Fonts/Pretendard-ExtraBold.otf similarity index 100% rename from week03/Megabox/Fonts/Pretendard-ExtraBold.otf rename to week06/Megabox/Fonts/Pretendard-ExtraBold.otf diff --git a/week03/Megabox/Fonts/Pretendard-ExtraLight.otf b/week06/Megabox/Fonts/Pretendard-ExtraLight.otf similarity index 100% rename from week03/Megabox/Fonts/Pretendard-ExtraLight.otf rename to week06/Megabox/Fonts/Pretendard-ExtraLight.otf diff --git a/week03/Megabox/Fonts/Pretendard-Light.ttf b/week06/Megabox/Fonts/Pretendard-Light.ttf similarity index 100% rename from week03/Megabox/Fonts/Pretendard-Light.ttf rename to week06/Megabox/Fonts/Pretendard-Light.ttf diff --git a/week03/Megabox/Fonts/Pretendard-Medium.ttf b/week06/Megabox/Fonts/Pretendard-Medium.ttf similarity index 100% rename from week03/Megabox/Fonts/Pretendard-Medium.ttf rename to week06/Megabox/Fonts/Pretendard-Medium.ttf diff --git a/week03/Megabox/Fonts/Pretendard-Regular.ttf b/week06/Megabox/Fonts/Pretendard-Regular.ttf similarity index 100% rename from week03/Megabox/Fonts/Pretendard-Regular.ttf rename to week06/Megabox/Fonts/Pretendard-Regular.ttf diff --git a/week03/Megabox/Fonts/Pretendard-SemiBold.ttf b/week06/Megabox/Fonts/Pretendard-SemiBold.ttf similarity index 100% rename from week03/Megabox/Fonts/Pretendard-SemiBold.ttf rename to week06/Megabox/Fonts/Pretendard-SemiBold.ttf diff --git a/week03/Megabox/Fonts/Pretendard-Thin.ttf b/week06/Megabox/Fonts/Pretendard-Thin.ttf similarity index 100% rename from week03/Megabox/Fonts/Pretendard-Thin.ttf rename to week06/Megabox/Fonts/Pretendard-Thin.ttf diff --git a/week03/Megabox/Fonts/PretendardVariable.ttf b/week06/Megabox/Fonts/PretendardVariable.ttf similarity index 100% rename from week03/Megabox/Fonts/PretendardVariable.ttf rename to week06/Megabox/Fonts/PretendardVariable.ttf diff --git a/week03/Megabox/Info.plist b/week06/Megabox/Info.plist similarity index 69% rename from week03/Megabox/Info.plist rename to week06/Megabox/Info.plist index 84b21d7..bd95bcd 100644 --- a/week03/Megabox/Info.plist +++ b/week06/Megabox/Info.plist @@ -2,6 +2,15 @@ + TMDB_API_KEY + $(TMDB_API_KEY) + KAKAO_APP_KEY + kakao$e795673090d362bc069f906c37141855 + LSApplicationQueriesSchemes + + kakaokompassauth + kakaotalk + UIAppFonts Pretendard-Light.otf diff --git a/week03/Megabox/LoginNavigation/ContentView.swift b/week06/Megabox/LoginNavigation/ContentView.swift similarity index 100% rename from week03/Megabox/LoginNavigation/ContentView.swift rename to week06/Megabox/LoginNavigation/ContentView.swift diff --git a/week06/Megabox/MegaboxApp.swift b/week06/Megabox/MegaboxApp.swift new file mode 100644 index 0000000..66f862f --- /dev/null +++ b/week06/Megabox/MegaboxApp.swift @@ -0,0 +1,27 @@ +// MegaboxApp.swift (수정) + +import SwiftUI +import KakaoSDKCommon +import KakaoSDKAuth + +@main +struct MegaboxApp: App { + @State private var userViewModel = UserViewModel() + + init(){ + KakaoSDK.initSDK(appKey: "e795673090d362bc069f906c37141855") + } + + var body: some Scene { + WindowGroup { + SplashView() // 👈 이 뷰가 userViewModel을 물려받음 + .environment(userViewModel) + // ✅ 2. [필수 추가] 카카오 로그인 콜백을 받기 위해 이 코드가 꼭 필요합니다. + .onOpenURL { url in + if (AuthApi.isKakaoTalkLoginUrl(url)) { + _ = AuthController.handleOpenUrl(url: url) + } + } + } + } +} diff --git a/week03/Megabox/Model/ColorExtension.swift b/week06/Megabox/Model/ColorExtension.swift similarity index 100% rename from week03/Megabox/Model/ColorExtension.swift rename to week06/Megabox/Model/ColorExtension.swift diff --git a/week06/Megabox/Model/DTO/APIResponseDTO.swift b/week06/Megabox/Model/DTO/APIResponseDTO.swift new file mode 100644 index 0000000..7737f0b --- /dev/null +++ b/week06/Megabox/Model/DTO/APIResponseDTO.swift @@ -0,0 +1,144 @@ +import Foundation +import SwiftUI + +// MARK: - 최상위 응답 +struct APIResponseDTO: Codable { + // 기존 구조: status, message, data + // 변경 구조: dates, page, results(영화목록) + + let dates: DateRange? // dates 객체는 없을 수도 있으므로 Optional + let page: Int + let results: [MovieDTO] // 기존의 MovieData.movies 역할 + let totalPages: Int + let totalResults: Int + + enum CodingKeys: String, CodingKey { + case dates, page, results + case totalPages = "total_pages" + case totalResults = "total_results" + } +} + +// dates 객체 처리를 위한 구조체 추가 +struct DateRange: Codable { + let maximum: String + let minimum: String +} + +// MARK: - 영화 상세 정보 (스크린샷 기반) +struct MovieDTO: Codable { + // 기존: id(String), title, age_rating, schedules + // 변경: id(Int), title, overview, poster_path 등등... + + let id: Int // API에서는 Int로 옴 + let title: String + let originalTitle: String + let overview: String // 줄거리 (기존의 로컬 설명 대체 가능) + let posterPath: String? // 포스터 이미지 경로 + let releaseDate: String + let voteAverage: Double + let voteCount: Int // 👈 [추가] "vote_count"를 받기 위해 추가 + let adult: Bool + + // ⚠️ 주의: 스크린샷의 API에는 '상영 시간표(schedules)' 정보가 없습니다. + // 따라서 아래 구조체들은 이 API에서 파싱되지 않습니다. + // let schedules: [MovieInfoDTO] + + enum CodingKeys: String, CodingKey { + case id, title, overview, adult + case originalTitle = "original_title" + case posterPath = "poster_path" + case releaseDate = "release_date" + case voteAverage = "vote_average" + case voteCount = "vote_count" // 👈 [추가] JSON 키 매핑 + } +} + +// ----------------------------------------------------------- +// ⚠️ 중요: 아래 구조체들(스케줄 관련)은 현재 스크린샷의 JSON에 포함되어 있지 않습니다. +// 다른 API(예: 예매 상세 API)를 호출할 때 사용하거나, 더미 데이터로 남겨두어야 합니다. +// 일단 에러 방지를 위해 코드는 남겨두지만, 위 APIResponseDTO에서 연결은 끊겨 있습니다. +// ----------------------------------------------------------- + +struct MovieInfoDTO: Codable { + let date: String + let areas: [MovieAreaDTO] +} + +struct MovieAreaDTO: Codable { + let area: String + let items: [MovieItemsDTO] +} + +struct MovieItemsDTO: Codable { + let auditorium: String + let format: String + let showtimes: [ShowTimesDTO] +} + +struct ShowTimesDTO: Codable { + let start: String + let end: String + let available: Int + let total: Int +} + +// MARK: - Extensions (Domain Mapping) + +extension MovieDTO { + // MovieDTO(DTO) -> MovieCards(Domain) + func toDomainCard() -> MovieCards { + + // 포스터 이미지 URL 처리 + let imageBaseURL = "https://image.tmdb.org/t/p/w500" + var fullPosterURL: URL? = nil + if let posterPath = self.posterPath { + fullPosterURL = URL(string: imageBaseURL + posterPath) + } + + // DTO의 id는 Int, Domain은 String일 경우 형변환 필요 + return MovieCards( + id: String(self.id), + // image: Image(systemName: "film"), // 👈 [삭제] + posterURL: fullPosterURL, // 👈 [수정] 생성된 URL을 전달 + booking: true, // API에 없으므로 하드코딩 + movieName: self.title, + watchedStatus: "\(self.voteCount)명", // 👈 [수정] voteCount를 사용 + movieNameEn: self.originalTitle, + movieDescription: MovieDescription(details: [self.overview]) + ) + } +} + +// 아래 Extension들은 현재 API 응답에 데이터가 없으므로 실제로는 호출되지 않지만, +// 기존 코드 호환성을 위해 남겨둡니다. + +extension ShowTimesDTO { + func toDomain() -> Time { + return Time( + startTime: self.start, + endTime: "~\(self.end)", + remainingSeats: self.available, + totalSeats: self.total + ) + } +} + +extension MovieItemsDTO { + func toDomain() -> ScreenSchedule { + return ScreenSchedule( + screenName: self.auditorium, + format: self.format, + times: self.showtimes.map { $0.toDomain() } + ) + } +} + +extension MovieAreaDTO { + func toDomain() -> TheaterSchedule { + return TheaterSchedule( + theaterName: self.area, + screens: self.items.map { $0.toDomain() } + ) + } +} diff --git a/week06/Megabox/Model/KeychainService.swift b/week06/Megabox/Model/KeychainService.swift new file mode 100644 index 0000000..e70e62d --- /dev/null +++ b/week06/Megabox/Model/KeychainService.swift @@ -0,0 +1,123 @@ +import Foundation +import Security + +//account가 아이디 +//service가 보여주는 페이지 + + +class KeychainService{ + + static let shared = KeychainService() //싱글톤으로 선언 + private init() {} + + //Token 인코딩 + func save(item: T, service: String, account: String){ + do{ + let data = try JSONEncoder().encode(item) + saveInfo(data: data, service: service, account: account) // 이 함수에 인코딩한 토큰 전달 + }catch { + print("Keychain 저장 실패 (Encoding): \(error.localizedDescription)") + } + + } + + // + func read(service: String, account: String, type: T.Type) -> T? { + // Keychain에서 Token가져오기 + guard let data = readInfo(service: service, account: account) else { + return nil + } + + // Data를 Codable 객체로 디코딩 + do { + let item = try JSONDecoder().decode(T.self, from: data) + return item + } catch { + print("Keychain 읽기 실패 (Decoding): \(error.localizedDescription)") + return nil + } + } + + // ⭐️ [추가] String을 위한 저장 함수 + func saveString(_ string: String, service: String, account: String) { + // String을 Data로 변환 + guard let data = string.data(using: .utf8) else { + print("Keychain String 저장 실패 (Data 변환)") + return + } + // 기존의 Data 저장 함수 호출 + saveInfo(data: data, service: service, account: account) + } + + // ⭐️ [추가] String을 위한 읽기 함수 + func readString(service: String, account: String) -> String? { + // 기존의 Data 읽기 함수 호출 + guard let data = readInfo(service: service, account: account) else { + return nil + } + // Data를 String으로 변환하여 반환 + return String(data: data, encoding: .utf8) + } + + + + //인코딩된 상태로 전달 + func saveInfo(data: Data, service: String, account: String){ + + deleteInfo(service: service, account: account) + + let query: [String: Any] = [ + kSecClass as String: kSecClassGenericPassword, + kSecAttrService as String: service, + kSecAttrAccount as String: account, + kSecValueData as String: data + ] + + let status = SecItemAdd(query as CFDictionary, nil) //KeyChain에다가 저장 + + if status != errSecSuccess { + print("Keychain 저장 실패 (Status: \(status))") + } + } + + + func readInfo(service: String, account: String) -> Data?{ + + let query: [String: Any] = [ + kSecClass as String: kSecClassGenericPassword, + kSecAttrService as String: service, + kSecAttrAccount as String: account, + kSecReturnData as String: true, + kSecMatchLimit as String: kSecMatchLimitOne + ] + + var result: AnyObject? + let status = SecItemCopyMatching(query as CFDictionary, &result) + + if status == errSecSuccess{ + return result as? Data + }else{ + // errSecItemNotFound는 실패가 아닌, '항목 없음'이므로 조용히 처리 + if status != errSecItemNotFound { + print("Keychain 읽기 실패 (Status: \(status))") + } + return nil + } + } + + func deleteInfo(service: String, account:String){ + + + let query: [String: Any] = [ + kSecClass as String: kSecClassGenericPassword, + kSecAttrService as String: service, + kSecAttrAccount as String: account + ] + + let status = SecItemDelete(query as CFDictionary) + if status != errSecSuccess && status != errSecItemNotFound { + print("Keychain 삭제 실패 (Status: \(status))") + } + } + +} diff --git a/week03/Megabox/Model/MovieCards.swift b/week06/Megabox/Model/MovieCards.swift similarity index 67% rename from week03/Megabox/Model/MovieCards.swift rename to week06/Megabox/Model/MovieCards.swift index a247b56..1bbba4c 100644 --- a/week03/Megabox/Model/MovieCards.swift +++ b/week06/Megabox/Model/MovieCards.swift @@ -4,11 +4,14 @@ import Foundation struct MovieCards: Identifiable{ - let id = UUID() - var image: Image + let id : String + var posterURL: URL? var booking: Bool var movieName: String var watchedStatus: String var movieNameEn: String // 영문 제목 추가 + + var movieDescription: MovieDescription + } diff --git a/week06/Megabox/Model/MovieDescription.swift b/week06/Megabox/Model/MovieDescription.swift new file mode 100644 index 0000000..43c9c40 --- /dev/null +++ b/week06/Megabox/Model/MovieDescription.swift @@ -0,0 +1,7 @@ +import SwiftUI +import Foundation + +struct MovieDescription: Identifiable { + let id = UUID() + let details: [String] +} diff --git a/week06/Megabox/Model/Theater.swift b/week06/Megabox/Model/Theater.swift new file mode 100644 index 0000000..5f0d111 --- /dev/null +++ b/week06/Megabox/Model/Theater.swift @@ -0,0 +1,25 @@ +import Foundation + +// 이미지 레이아웃에 필요한 모든 정보를 담도록 모델을 변경합니다. +struct Time: Identifiable { + let id = UUID() + let startTime: String // 예: "11:30" + let endTime: String // 예: "~13:58" + let remainingSeats: Int // 예: 109 + let totalSeats: Int // 예: 116 +} + +/// "크리클라이너 1관" 처럼 상영관 하나의 정보를 담는 모델 +struct ScreenSchedule: Identifiable { + let id = UUID() + let screenName: String // "크리클라이너 1관" + let format: String // "2D" + let times: [Time] // 이 상영관의 시간표 목록 +} + +/// "강남" 극장처럼, 여러 상영관을 보유하는 모델 +struct TheaterSchedule: Identifiable { + let id = UUID() + let theaterName: String // "강남" + let screens: [ScreenSchedule] // 이 극장의 상영관 목록 +} diff --git a/week06/Megabox/Model/TokenInfo.swift b/week06/Megabox/Model/TokenInfo.swift new file mode 100644 index 0000000..a7fbcd3 --- /dev/null +++ b/week06/Megabox/Model/TokenInfo.swift @@ -0,0 +1,6 @@ +struct TokenInfo: Codable{ + let accessToken: String + let refreshToken: String +} + +//여기다가 토큰 저장 diff --git a/week06/Megabox/TMDBAPI.xcconfig b/week06/Megabox/TMDBAPI.xcconfig new file mode 100644 index 0000000..357dbea --- /dev/null +++ b/week06/Megabox/TMDBAPI.xcconfig @@ -0,0 +1 @@ +TMDB_API_KEY = eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiI1NTUwYTg1YjE4ZjNkMzA1NzM2ZGVhMDMzZTczZWQ5MyIsIm5iZiI6MTc2MzI5NDM4NS43OCwic3ViIjoiNjkxOWJjYjFkYzdiNzNmNDdjMzFkNjAzIiwic2NvcGVzIjpbImFwaV9yZWFkIl0sInZlcnNpb24iOjF9.7OJGvLAb5lb57invNk94DXJ3fyY7v5G_5G8Ny0AN_wI diff --git a/week06/Megabox/ViewModels/LoginVM/LoginViewModel.swift b/week06/Megabox/ViewModels/LoginVM/LoginViewModel.swift new file mode 100644 index 0000000..64b6741 --- /dev/null +++ b/week06/Megabox/ViewModels/LoginVM/LoginViewModel.swift @@ -0,0 +1,139 @@ +// LoginViewModel.swift (수정) + +import Foundation +import SwiftUI + +import Alamofire +import KakaoSDKCommon +import KakaoSDKAuth +import KakaoSDKUser + +@Observable +class LoginViewModel { + + private var userViewModel: UserViewModel + + // 1. 연습용 + private let isMockMode = true //실제 API가 생기면 false로 + + // 2. 실제 API 주소 (나중에 백엔드) + private var loginAPIURL = "https://api.my-server.com/login" + + init(userViewModel: UserViewModel) { + self.userViewModel = userViewModel + } + + func login(username: String, password: String) { + + if isMockMode { + + print("[Mock Mode] 로그인 성공을 시뮬레이션합니다.") + + // (가상) 2초 딜레이 (서버 응답 시간 척하기) + DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { + + // (가상) 서버로부터 받았다고 가정하는 가짜 토큰 + let mockTokenInfo = TokenInfo( + accessToken: "fake-access-token-for-\(username)", + refreshToken: "fake-refresh-token-12345" + ) + + // UserViewModel의 loginSuccess 함수를 직접 호출 + self.userViewModel.loginSuccess(username: username, tokens: mockTokenInfo) + } + + } else { + //(실제 모드) isMockMode가 false일 때만 Alamofire 통신 + + print("🚀 [Real Mode] 실제 서버로 로그인을 요청합니다.") + + let parameters:[String : String] = [ + "username" : username, + "password" : password + ] + + //서버에서는 password맞는지 확인하고 다시 반환하지 않음, 대신에 토큰을 발급 + AF.request(loginAPIURL, method: .post, parameters: parameters, encoder: JSONParameterEncoder.default) + .validate(statusCode: 200..<300) + .responseDecodable(of: TokenInfo.self) { response in + switch response.result { + case .success(let tokenInfo): + print("로그인 성공") + + DispatchQueue.main.async { + self.userViewModel.loginSuccess(username: username, tokens: tokenInfo) + } + case .failure(let error): + print("로그인 실패: \(error.localizedDescription)") + } + } + } + } + // --- 카카오 로그인 함수 (이 함수가 fetchKakaoUserInfo를 호출) --- + func loginWithKakao() { + if (UserApi.isKakaoTalkLoginAvailable()) { + UserApi.shared.loginWithKakaoTalk { (oauthToken, error) in + if let error = error { + print("카카오톡 로그인 실패: \(error)") + } else if let oauthToken = oauthToken { + print("카카오톡 로그인 성공!") + // 👇 [성공 시] 이 함수를 호출합니다. + self.fetchKakaoUserInfo(oauthToken: oauthToken) + } + } + } else { + UserApi.shared.loginWithKakaoAccount { (oauthToken, error) in + if let error = error { + print("카카오 계정 로그인 실패: \(error)") + } else if let oauthToken = oauthToken { + print("카카오 계정 로그인 성공!") + // 👇 [성공 시] 이 함수를 호출합니다. + self.fetchKakaoUserInfo(oauthToken: oauthToken) + } + } + } + } + + + // --- 👇 [요청하신] fetchKakaoUserInfo 전체 코드 --- + /** + 카카오 SDK에서 성공적으로 토큰을 받은 후, + 해당 사용자의 정보를 가져오기 위해 호출됩니다. + - Parameter oauthToken: 카카오 SDK가 전달해준 인증 토큰 + */ + private func fetchKakaoUserInfo(oauthToken: OAuthToken) { + + // 1. 카카오 서버에 '방금 로그인한 사용자가 누구인지' 물어봅니다. + UserApi.shared.me() { (user, error) in + if let error = error { + print("카카오 사용자 정보 가져오기 실패: \(error)") + } + else if let user = user { + print("카카오 사용자 정보 가져오기 성공") + + // 2. [필수] 카카오에서 받은 사용자 고유 ID + guard let kakaoID = user.id else { + print("카카오 사용자 ID가 없습니다.") + return + } + + // 3. 카카오에서 받은 토큰을 -> 우리가 만든 TokenInfo 모델로 변환 + // (자체 서버가 없으므로 카카오 토큰을 그대로 키체인에 저장) + let ourTokenInfo = TokenInfo( + accessToken: oauthToken.accessToken, + refreshToken: oauthToken.refreshToken + ) + + // 4. [핵심] UserViewModel의 loginSuccess 함수를 호출합니다. + // (UI 업데이트이므로 메인 스레드에서 실행) + DispatchQueue.main.async { + self.userViewModel.loginSuccess( + username: String(kakaoID), // 카카오 ID(숫자)를 String으로 변환 + tokens: ourTokenInfo + ) + } + + } + } + } + } diff --git a/week06/Megabox/ViewModels/LoginVM/UserViewModel.swift b/week06/Megabox/ViewModels/LoginVM/UserViewModel.swift new file mode 100644 index 0000000..b29302a --- /dev/null +++ b/week06/Megabox/ViewModels/LoginVM/UserViewModel.swift @@ -0,0 +1,84 @@ +// UserViewModel.swift (전체 수정) +import Foundation +import SwiftUI + +@Observable +class UserViewModel { + var username: String = "" + var isLoggedIn: Bool = false + + private var tokenInfo: TokenInfo? + + private let tokenService = ".com" + private let tokenAccount = "userTokens" // 토큰 저장을 위한 키 + private let usernameAccount = "username" // ⭐️ 아이디 저장을 위한 키 (추가) + + // private let usernameKey = "username" // ⬅️ UserDefaults 키 삭제 + + init() { + // 1. ⭐️ Keychain에서 '아이디' 불러오기 + let loadedUsername = KeychainService.shared.readString( + service: tokenService, + account: usernameAccount + ) + + // 2. Keychain에서 '토큰' 불러오기 + let loadedTokens = KeychainService.shared.read( + service: tokenService, + account: tokenAccount, + type: TokenInfo.self + ) + + // 3. ⭐️ 아이디와 토큰이 '둘 다' 존재해야 자동 로그인 + if let username = loadedUsername, let tokens = loadedTokens { + self.username = username + self.tokenInfo = tokens + self.isLoggedIn = true // ⬅️ 자동 로그인 성공! + print("Keychain에서 아이디/토큰 로드 성공. 자동 로그인합니다.") + } else { + self.isLoggedIn = false + print("저장된 아이디 또는 토큰 없음. 로그인 화면으로 이동합니다.") + } + } + + func loginSuccess(username: String, tokens: TokenInfo) { + // 1. 토큰 정보를 Keychain에 (Codable로) 저장 + KeychainService.shared.save(item: tokens, service: tokenService, account: tokenAccount) + + // 2. 사용자 아이디를 Keychain에 (String으로) 저장 + KeychainService.shared.saveString(username, service: tokenService, account: usernameAccount) + + // 3. ViewModel 상태 업데이트 (UI 변경 트리거) + self.tokenInfo = tokens + self.username = username + self.isLoggedIn = true + + print("로그인 성공 및 토큰/아이디 저장 완료.") + } + + func logout() { + // 1. ⭐️ Keychain에서 토큰 삭제 + KeychainService.shared.deleteInfo(service: tokenService, account: tokenAccount) + + // 2. ⭐️ Keychain에서 아이디 삭제 + KeychainService.shared.deleteInfo(service: tokenService, account: usernameAccount) + + // 3. ViewModel 상태 초기화 + self.tokenInfo = nil + self.username = "" // 아이디도 초기화 + self.isLoggedIn = false + + print("로그아웃 및 토큰/아이디 삭제 완료.") + } + + func getAccessToken() -> String? { + // TODO: 실제로는 토큰 만료 여부 확인 및 갱신 로직 필요 + return tokenInfo?.accessToken + } + + func saveNewUserName(newUsername: String){ + + self.username = newUsername + + } +} diff --git a/week06/Megabox/ViewModels/MovieBookingViewModels/DataLogic.swift b/week06/Megabox/ViewModels/MovieBookingViewModels/DataLogic.swift new file mode 100644 index 0000000..dae6444 --- /dev/null +++ b/week06/Megabox/ViewModels/MovieBookingViewModels/DataLogic.swift @@ -0,0 +1,62 @@ +import Foundation + +// ViewModel 안에서의 데이터 처리 로직 +extension MovieBookingViewModel { + + func loadDatesForSelectedMovie() { + // 1. 선택된 영화 ID로 DTO 찾기 + // [변경됨] MovieDTO의 id는 Int이므로 String 변환하여 비교 + guard let movieID = selectedMovieID, + let _ = allMovieDTOs.first(where: { String($0.id) == movieID }) else { + self.weekDates = [] + return + } + + // [변경됨] API에 스케줄 정보가 없으므로, 오늘부터 7일간의 날짜를 자동으로 생성합니다. + let calendar = Calendar.current + let today = Date() + var dates: [Date] = [] + + for i in 0..<7 { + if let date = calendar.date(byAdding: .day, value: i, to: today) { + dates.append(date) + } + } + + self.weekDates = dates + } + + + func loadTimeData(movieID: String, theaters: [String], date: Date) { + + // [변경됨] MovieDTO id 타입 불일치 해결 (Int vs String) + guard let _ = allMovieDTOs.first(where: { String($0.id) == movieID }) else { + self.schedules = [] + return + } + + // [변경됨] API에 스케줄 정보가 없으므로, 더미(가짜) 시간표 데이터를 생성하여 반환합니다. + // 실제 앱에서는 별도의 상영 시간표 API를 호출해야 합니다. + + var loadedSchedules: [TheaterSchedule] = [] + + for theaterName in theaters { + // 임의의 시간표 생성 로직 + let dummyTimes = [ + Time(startTime: "10:00", endTime: "~12:00", remainingSeats: 120, totalSeats: 200), + Time(startTime: "13:30", endTime: "~15:30", remainingSeats: 80, totalSeats: 200), + Time(startTime: "16:00", endTime: "~18:00", remainingSeats: 0, totalSeats: 200), // 매진 예시 + Time(startTime: "19:30", endTime: "~21:30", remainingSeats: 150, totalSeats: 200) + ] + + // 각 극장마다 2D, IMAX 등 임의 포맷 할당 + let screenFormat = (theaterName == "CGV 용산아이파크몰") ? "IMAX" : "2D" + + let screenSchedule = ScreenSchedule(screenName: "1관", format: screenFormat, times: dummyTimes) + + loadedSchedules.append(TheaterSchedule(theaterName: theaterName, screens: [screenSchedule])) + } + + self.schedules = loadedSchedules + } +} diff --git a/week06/Megabox/ViewModels/MovieBookingViewModels/MovieBookingViewModel.swift b/week06/Megabox/ViewModels/MovieBookingViewModels/MovieBookingViewModel.swift new file mode 100644 index 0000000..5e1f95b --- /dev/null +++ b/week06/Megabox/ViewModels/MovieBookingViewModels/MovieBookingViewModel.swift @@ -0,0 +1,124 @@ +import Combine +import SwiftUI +import Moya +import _Concurrency +let imageBaseURL = "https://image.tmdb.org/t/p/w500" +@MainActor +class MovieBookingViewModel: ObservableObject { + + @Published var errorMessage: String? = nil + + @Published var movieCards: [MovieCards] = [] + @Published var selectedMovieID: String? + + @Published var isTheaterButtonEnabled: Bool = false + @Published var selectedTheaters: [String] = [] + + @Published var weekDates: [Date] = [] + @Published var selectedDate: Date? = nil + @Published var isDateSelectionEnabled: Bool = false + + @Published var schedules: [TheaterSchedule] = [] + + @Published var searchText = "" + @Published var filteredMovies: [MovieCards] = [] + @Published var isSearching: Bool = false + + @Published var allMovieDTOs: [MovieDTO] = [] + + // MARK: - Properties + var cancellables = Set() + + // Moya Provider 선언 + private let provider = MoyaProvider() + + var selectedMovie: MovieCards? { + movieCards.first { $0.id == selectedMovieID } + } + + init() { + // 비동기 데이터 로드 시작 + _Concurrency.Task { + await self.loadInitialData() + } + // 구독 설정 + setupButtonStateSubscription() + setupDateSelectionSubscription() + setupTimeSelectionSubscription() + setupSearchSubscription() + } + + @MainActor + private func loadInitialData() async { + do { + // 1. Moya asyncRequest 호출 + let response = try await provider.asyncRequest(.nowPlaying(page: 1)) + + // 2. 디코딩 + let decodedData = try JSONDecoder().decode(APIResponseDTO.self, from: response.data) + + // 3. 원본 DTO 저장 (로직 처리를 위해) + self.allMovieDTOs = decodedData.results + + self.movieCards = decodedData.results.map { dto in + + // 3. posterPath가 nil일 수도 있으므로 옵셔널 처리 + var fullURL: URL? = nil + if let posterPath = dto.posterPath { + fullURL = URL(string: imageBaseURL + posterPath) + } + + return MovieCards( + id: String(dto.id), + // image: Image(systemName: "play.rectangle.fill"), // 👈 기존 + posterURL: fullURL, // 👈 [수정] 조립된 URL 전달 + booking: true, + movieName: dto.title, + watchedStatus: "\(Int(dto.voteCount))명", // 관람객 수 + movieNameEn: dto.originalTitle, + movieDescription: MovieDescription(details: [dto.overview]) + ) + } + + } catch { + print("Failed to load movies: \(error)") + + // 에러 처리 + if let moyaError = error as? MoyaError { + self.errorMessage = "네트워크 오류: \(moyaError.localizedDescription)" + } else { + self.errorMessage = "알 수 없는 오류가 발생했습니다." + } + } + } + + // MARK: - User Actions + + func selectMovie(id: String?) { + DispatchQueue.main.async { + withAnimation { + self.selectedMovieID = id + self.selectedDate = nil + self.schedules = [] + // 날짜 로드 로직 호출 (이전 단계에서 수정한 DataLogic의 함수 사용) + self.loadDatesForSelectedMovie() + } + } + } + + func selectTheater(name: String) { + if let index = selectedTheaters.firstIndex(of: name) { + selectedTheaters.remove(at: index) + } else { + selectedTheaters.append(name) + } + } + + func selectDate(_ date: Date) { + if selectedDate == date { + selectedDate = nil + } else { + selectedDate = date + } + } +} diff --git a/week06/Megabox/ViewModels/MovieBookingViewModels/MovieCardsViewModel.swift b/week06/Megabox/ViewModels/MovieBookingViewModels/MovieCardsViewModel.swift new file mode 100644 index 0000000..1af4d59 --- /dev/null +++ b/week06/Megabox/ViewModels/MovieBookingViewModels/MovieCardsViewModel.swift @@ -0,0 +1,56 @@ +import SwiftUI +import Moya +import _Concurrency + +@MainActor +@Observable +class MovieCardsViewModel { + var movieCards: [MovieCards] = [] + let imageBaseURL = "https://image.tmdb.org/t/p/w500" + + // Moya Provider 생성, 에러확인용 플러그인 + private let provider = MoyaProvider(plugins: [NetworkLoggerPlugin()]) + + init() { + // 초기화 시 비동기로 데이터 로드 시작 + _Concurrency.Task { + await loadMovies() + } + } + + @MainActor + func loadMovies() async { + do { + // 1. async/await를 사용하여 API 요청 + let response = try await provider.asyncRequest(.nowPlaying(page: 1)) + + // 2. JSON 디코딩 + let decodedResponse = try JSONDecoder().decode(APIResponseDTO.self, from: response.data) + + self.movieCards = decodedResponse.results.map { dto in + + // 3. posterPath가 nil일 수도 있으므로 옵셔널 처리 + var fullURL: URL? = nil + if let posterPath = dto.posterPath { + fullURL = URL(string: imageBaseURL + posterPath) + } + + return MovieCards( + id: String(dto.id), + // image: Image(systemName: "play.rectangle.fill"), // 👈 기존 + posterURL: fullURL, // 👈 [수정] 조립된 URL 전달 + booking: true, + movieName: dto.title, + watchedStatus: "\(Int(dto.voteCount))명", // 관람객 수 + movieNameEn: dto.originalTitle, + movieDescription: MovieDescription(details: [dto.overview]) + ) + } + + } catch { + print("영화 목록 로드 실패: \(error)") + // 에러 발생 시 빈 배열 혹은 에러 상태 처리 + self.movieCards = [] + } + } +} diff --git a/week06/Megabox/ViewModels/MovieBookingViewModels/Subscriptions.swift b/week06/Megabox/ViewModels/MovieBookingViewModels/Subscriptions.swift new file mode 100644 index 0000000..bdff24c --- /dev/null +++ b/week06/Megabox/ViewModels/MovieBookingViewModels/Subscriptions.swift @@ -0,0 +1,66 @@ +import Foundation +import Combine + +// ViewModel의 내부 Combine 구독 객체 +extension MovieBookingViewModel { + + //이 두개 함수는 단순히 값 할당만하면 되니 assign 사용 + func setupButtonStateSubscription() { + $selectedMovieID + .map { $0 != nil } + .assign(to: &$isTheaterButtonEnabled) + } + + func setupDateSelectionSubscription() { + $selectedTheaters + .map { !$0.isEmpty } + .assign(to: &$isDateSelectionEnabled) + } + + + // 얘는 정보 받아서 더 복잡한 코드를 수행해야하니 .sink사용 + func setupTimeSelectionSubscription() { + Publishers.CombineLatest3($selectedMovieID, $selectedTheaters, $selectedDate) + //실제로 값이 바뀌었을 때만 동작하도록 보장 + .removeDuplicates { (prev, current) in + return prev.0 == current.0 && prev.1 == current.1 && prev.2 == current.2 + } + .sink { [weak self] (movieID, theaters, date) in + if let movieID = movieID, !theaters.isEmpty, let date = date { + //sink안에서 값을 전달한 후 UI를 업데이트 해야됨 + //파라미터로 sink안에서 업데이트된 값을 전달 + //이렇게 안하고 직접 뷰모델에 접근해서 데이터를 사용하면 레이스 컨디션 발생 + self?.loadTimeData(movieID: movieID, theaters: theaters, date: date) + } else { + self?.schedules = [] + } + } + .store(in: &cancellables) + } + + + func setupSearchSubscription() { + $searchText + .removeDuplicates() + .handleEvents(receiveOutput: { text in + if !text.isEmpty { + self.isSearching = true + } + }) + .debounce(for: .milliseconds(400), scheduler: DispatchQueue.main) + .sink { [weak self] (text) in + guard let self = self else { return } + + self.isSearching = false + + if text.isEmpty { + self.filteredMovies = [] + } else { + self.filteredMovies = self.movieCards.filter { movie in + movie.movieName.localizedCaseInsensitiveContains(text) + } + } + } + .store(in: &cancellables) + } +} diff --git a/week03/Megabox/Views/SingleViews/InfoButtonView.swift b/week06/Megabox/Views/SingleViews/InfoButtonView.swift similarity index 94% rename from week03/Megabox/Views/SingleViews/InfoButtonView.swift rename to week06/Megabox/Views/SingleViews/InfoButtonView.swift index 4b3e9c3..4c946e0 100644 --- a/week03/Megabox/Views/SingleViews/InfoButtonView.swift +++ b/week06/Megabox/Views/SingleViews/InfoButtonView.swift @@ -25,7 +25,7 @@ struct InfoButton: View { } else { Rectangle() .frame(height: 3) - .foregroundColor(.clear) + .foregroundColor(.grey02) } } .padding(.vertical, 8) diff --git a/week03/Megabox/Views/SingleViews/LoginView.swift b/week06/Megabox/Views/SingleViews/LoginView.swift similarity index 95% rename from week03/Megabox/Views/SingleViews/LoginView.swift rename to week06/Megabox/Views/SingleViews/LoginView.swift index 7938afc..3e6ff41 100644 --- a/week03/Megabox/Views/SingleViews/LoginView.swift +++ b/week06/Megabox/Views/SingleViews/LoginView.swift @@ -1,11 +1,13 @@ import SwiftUI + struct LoginView: View { - @Environment(UserViewModel.self) private var userViewModel - //여기다가 먼저 저장한다음, 로그인이 될때 그 정보를 전역으로 전달 + + @State private var loginViewModel: LoginViewModel? + @Environment(UserViewModel.self) private var userViewModel @State private var usernameInput: String = "" @State private var passwordInput: String = "" @@ -126,7 +128,14 @@ struct LoginView: View { HStack(spacing:73){ Image("Naver") - Image("Kakao") + + Button { + loginViewModel?.loginWithKakao() + } label: { + Image("Kakao") + } + + Image("Apple") } diff --git a/week06/Megabox/Views/SingleViews/MovieView/MovieBookingView.swift b/week06/Megabox/Views/SingleViews/MovieView/MovieBookingView.swift new file mode 100644 index 0000000..67b0075 --- /dev/null +++ b/week06/Megabox/Views/SingleViews/MovieView/MovieBookingView.swift @@ -0,0 +1,361 @@ +import SwiftUI + +struct MovieBookingView: View { + + @StateObject private var viewModel = MovieBookingViewModel() + + @State private var isShowingMovieSheet = false + + private var timeGridColumns: [GridItem] = [ + GridItem(.flexible()), + GridItem(.flexible()), + GridItem(.flexible()), + GridItem(.flexible()) + ] + + + // MARK: - Main Body + var body: some View { + NavigationStack { + + VStack(spacing: 0) { + + HStack { + Text("영화별 예매") + .font(.bold22) + .foregroundColor(.white) + } + .frame(maxWidth: .infinity) + .padding(.top, 31) + .padding(.bottom, 10) + .background(.purple03) + ScrollView + { + + VStack(alignment: .leading, spacing: 23) { + + movieSelectionHeader + moviePostersList + theaterSelectionButtons + + dateSelectionView + timeSelectionView + + } + .padding(.horizontal) + .padding(.top) + } + + + } + + } + } + + // MARK: - UI Components (분리된 서브 뷰) + + /// 1. 선택된 영화 정보를 보여주는 헤더 뷰 + private var movieSelectionHeader: some View { + HStack { + Text("15") + .font(.bold18) + .foregroundColor(.white) + .frame(width: 26, height: 24) + .background(.orange) // 순서 수정 + .cornerRadius(4) + .overlay( + RoundedRectangle(cornerRadius: 4) + .stroke(Color.black, lineWidth: 1) // 100% 검은색 외곽선 + ) + .shadow(color: .black.opacity(0.25), radius: 3, x: 0, y: 2) + + Spacer() + + Text(viewModel.selectedMovie?.movieName ?? "영화를 선택하세요") + .font(.semiBold18) + .frame(width: 238, height: 24, alignment: .leading) + + + Button(action: { + + isShowingMovieSheet = true + + + }, label: { + Text("전체영화") + .font(.semiBold14) + .foregroundColor(.black) + .padding(.horizontal, 10) + .padding(.vertical, 10) + .frame(width: 69, height: 30) + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(Color.grey02, lineWidth: 1) + ) + }) + .sheet(isPresented: $isShowingMovieSheet){ + + MovieSheetView(viewModel: self.viewModel) + .presentationDragIndicator(.visible) + + + } + + + + } + + } + + /// 2. 영화 포스터들을 보여주는 가로 스크롤 뷰 + private var moviePostersList: some View { + ScrollView(.horizontal) { + HStack(spacing: 8) { + ForEach(viewModel.movieCards) { movie in + // [수정] AsyncImage로 교체 + AsyncImage(url: movie.posterURL) { image in + image + .resizable() // 👈 기존 UI 수정자 유지 + .frame(width: 62, height: 89) // 👈 기존 UI 수정자 유지 + } placeholder: { + // 원본 프레임과 동일한 크기의 플레이스홀더 + RoundedRectangle(cornerRadius: 10) + .fill(Color.gray.opacity(0.1)) + .frame(width: 62, height: 89) + } + .cornerRadius(10) + .overlay( + RoundedRectangle(cornerRadius: 10) + .stroke(viewModel.selectedMovieID == movie.id ? .purple03 : Color.clear, lineWidth: 4) + ) + .onTapGesture { + withAnimation { + viewModel.selectMovie(id: movie.id) + } + } + } + } + } + + } + + /// 3. 극장 선택 버튼들을 보여주는 뷰 + private var theaterSelectionButtons: some View { + HStack(spacing: 10) { + ForEach(["강남", "홍대", "신촌"], id: \.self) { theaterName in + theaterButton(place: theaterName) + } + } + + } + +// 4. 날짜 버튼들을 보여주는 뷰 + private var dateSelectionView: some View { + ScrollView(.horizontal, showsIndicators: false) { + HStack(spacing: 5) { + ForEach(viewModel.weekDates, id: \.self) { date in + dateButton(for: date) + } + } + } // 극장이 선택되지 않았다면 전체적으로 반투명하게 처리 + .opacity(viewModel.isDateSelectionEnabled ? 1.0 : 0.4) + // 극장이 선택되지 않았다면 터치 불가 + .disabled(!viewModel.isDateSelectionEnabled) + + } + + + private var timeSelectionView: some View { + + + // VStack으로 감싸서 여러 극장 정보를 세로로 나열합니다. + VStack(alignment: .leading, spacing: 25) { + + // ViewModel의 schedules 배열을 순회합니다. (예: "강남", "홍대"가 순서대로 들어옴) + ForEach(viewModel.schedules) { schedule in + + // --- 1. 극장 이름 (예: "강남") --- + Text(schedule.theaterName) + .font(.bold18) + .foregroundColor(.black) + + // --- 2. 상영관 목록 또는 "시간대 없음" --- + + // Check 1: "홍대", "신촌" (screens empty) + if schedule.screens.isEmpty { + // "홍대", "신촌" 등 screens 배열이 비어있는 경우 + Text("해당 영화관에는 시간대가 없습니다.") + .font(.system(size: 14)) + .foregroundColor(.gray) + .padding(.vertical, 20) + .frame(maxWidth: .infinity, alignment: .center) + + // Check 2: "강남" (screens not empty) + } else { + + + VStack(spacing: 15) { + + // 이 극장의 모든 상영관(예: 1관, 2관...)을 순회 + ForEach(schedule.screens) { screen in + + // --- 상영관 이름 (예: "크리클라이너 1관") --- + HStack { + Text(screen.screenName) + .font(.bold18) + Spacer() + Text(screen.format) + .font(.semiBold14) + .foregroundColor(.black) + } + + // --- 시간표 그리드 --- + LazyVGrid(columns: timeGridColumns, spacing: 19) { + ForEach(screen.times) { time in + timeCell(for: time) // + } + } + } + } + } + } + } + // 'schedules 배열이 채워져있을때만 보이도록 + .opacity(!viewModel.schedules.isEmpty ? 1.0 : 0.0) + .animation(.easeInOut, value: !viewModel.schedules.isEmpty) + .disabled(viewModel.schedules.isEmpty) + } + + //------------------------------------ + + private func theaterButton(place: String) -> some View { + + + Button(action: { + + viewModel.selectTheater(name: place) + + + }, label: { + Text(place) + .font(.semiBold16) + .foregroundColor(viewModel.selectedTheaters.contains(place) ? .white : (viewModel.isTheaterButtonEnabled ? .grey05 : .gray)) + .padding(.horizontal, 10) + .padding(.vertical, 10) + .frame(width:55, height: 35) + .background(viewModel.selectedTheaters.contains(place) ? .purple03 : Color.grey01) + .cornerRadius(15) + + + + }).disabled(!viewModel.isTheaterButtonEnabled) + .animation(.easeInOut(duration: 0.2), value: viewModel.selectedTheaters.contains(place)) + .animation(.easeInOut(duration: 0.2), value: viewModel.isTheaterButtonEnabled) + + } + + + // MovieBookingView.swift + + private func dateButton(for date: Date) -> some View { + + + Button(action: { + + viewModel.selectDate(date) + }) { + // 3. 기존 VStack은 Button의 Label(콘텐츠)이 됩니다. + VStack(spacing: 4){ + Text(date.formatted(.dateTime.day())) + .font(.bold18) + + Text(formatWeekday(date)) + .font(.semiBold14) + + } + .padding(.vertical,12) + .padding(.horizontal, 10) + .frame(width: 55, height: 60) + .foregroundColor(viewModel.selectedDate == date ? .white : getWeekdayColor(for: date)) + .background( + RoundedRectangle(cornerRadius: 12) + .fill(viewModel.selectedDate == date ? .purple03: Color.clear) + ) + + + } + + .buttonStyle(.plain) + } + + private func getWeekdayColor(for date: Date) -> Color { + let calendar = Calendar.current + if calendar.isDateInWeekend(date) { + let weekday = calendar.component(.weekday, from: date) + return weekday == 1 ? .red : .tag // 일요일: 빨강, 토요일: 파랑 + } + return .black // 평일: 검정 + } + + private func formatWeekday(_ date: Date) -> String { + let calendar = Calendar.current + if calendar.isDateInToday(date) { + return "오늘" + } else if calendar.isDateInTomorrow(date) { + return "내일" + } else { + + let formatter = DateFormatter() + formatter.locale = Locale(identifier: "ko_KR") + formatter.dateFormat = "E" + return formatter.string(from: date) + } + } + + private func timeCell(for time: Time) -> some View { + VStack(spacing: 4) { + Spacer() + Text(time.startTime) + .foregroundColor(.black) + .font(.bold18) + .frame(width: 55, height: 24) + + + Text(time.endTime) + .font(.regular12) + .foregroundColor(.grey03) + .frame(width: 55, height: 24) + + + HStack(spacing: 4){ + + Text("\(time.remainingSeats)") + .foregroundColor(.purple03) + .font(.semiBold14) + + Text("/") + + Text("\(time.totalSeats)") + .foregroundColor(.grey03) + .font(.semiBold14) + + + } + .frame(width:59, height: 20) + .padding(10) + } + .padding(10) + .frame(width: 75, height: 86) + .background( + RoundedRectangle(cornerRadius: 12) + .stroke(.grey02, lineWidth: 1) + ) + } + +} + + +#Preview { + MovieBookingView() +} + diff --git a/week03/Megabox/Views/SingleViews/MovieCardView.swift b/week06/Megabox/Views/SingleViews/MovieView/MovieCardView.swift similarity index 51% rename from week03/Megabox/Views/SingleViews/MovieCardView.swift rename to week06/Megabox/Views/SingleViews/MovieView/MovieCardView.swift index cd4f1f9..f3845f2 100644 --- a/week03/Megabox/Views/SingleViews/MovieCardView.swift +++ b/week06/Megabox/Views/SingleViews/MovieView/MovieCardView.swift @@ -1,29 +1,44 @@ -// MovieCardView.swift - import SwiftUI struct MovieCardView: View { - let movie: MovieCards + let movie: MovieCards // 이제 'movie.posterURL'을 사용합니다. var body: some View { VStack(alignment: .leading, spacing: 12) { - // 1. 영화 포스터 이미지 - movie.image - .resizable() - .scaledToFit() - .frame(width: 148, height: 212) - - + // 1. 영화 포스터 이미지 (AsyncImage로 수정) + AsyncImage(url: movie.posterURL) { image in + // 로드 성공 시 + image + .resizable() + .aspectRatio(contentMode: .fill) // 원본 비율 유지하며 프레임 채우기 + + } placeholder: { + // 로드 중이거나 URL이 nil일 때 + ZStack { + Color.gray.opacity(0.1) // 배경색 + Image(systemName: "film") // 기본 아이콘 + .font(.largeTitle) + .foregroundStyle(.gray.opacity(0.5)) + } + } + .frame(width: 148, height: 212) // 프레임 고정 + .clipShape(RoundedRectangle(cornerRadius: 8)) // 모서리 살짝 둥글게 + .overlay( + RoundedRectangle(cornerRadius: 8) // 테두리 추가 + .stroke(Color.gray.opacity(0.2), lineWidth: 1) + ) + // 2. 예매 버튼 bookingButton - + // 3. 영화 정보 텍스트 VStack(alignment: .leading, spacing: 2) { Text(movie.movieName) .font(.bold22) .foregroundColor(.black) + .lineLimit(1) // 한 줄로 제한 Text("누적관객수 \(movie.watchedStatus)") .font(.medium18) diff --git a/week03/Megabox/Views/SingleViews/MovieDetailView.swift b/week06/Megabox/Views/SingleViews/MovieView/MovieDetailView.swift similarity index 82% rename from week03/Megabox/Views/SingleViews/MovieDetailView.swift rename to week06/Megabox/Views/SingleViews/MovieView/MovieDetailView.swift index 66ba118..24c6706 100644 --- a/week03/Megabox/Views/SingleViews/MovieDetailView.swift +++ b/week06/Megabox/Views/SingleViews/MovieView/MovieDetailView.swift @@ -7,6 +7,7 @@ struct MovieDetailView: View { @Environment(\.dismiss) private var dismiss let movie: MovieCards + var body: some View { ScrollView{ @@ -84,14 +85,10 @@ struct MovieDetailView: View { .font(.semiBold14) .foregroundColor(.grey03) Group{ - - text(text: "최고가 되지 못한 전설 VS 최고가 되고 싶은 루키") - .padding(.vertical) - - text(text: "한때 주목받는 유망주였지만 끔찍한 사고로 F1에서 우승하지 못하고") - text(text: "한순간에 추락한 드라이버 ‘손; 헤이스'(브래드 피트).") - text(text: "그의 오랜 동료인 ‘루벤 세르반테스'(하비에르 바르뎀)에게") - text(text: "레이싱 복귀를 제안받으며 최하위 팀인 APGXP에 합류한다.") + ForEach(movie.movieDescription.details, id: \.self) { detailLine in + text(text: detailLine) + } + } .padding(.horizontal) } @@ -112,9 +109,19 @@ struct MovieDetailView: View { private var detailsView: some View { HStack(alignment:.top, spacing: 16) { - movie.image// 상세 정보 포스터 이미지 이름 - .resizable() - .frame(width: 100, height: 120) + + // [수정] AsyncImage로 교체 + AsyncImage(url: movie.posterURL) { image in + image + .resizable() // 👈 기존 UI 수정자 유지 + .frame(width: 100, height: 120) // 👈 기존 UI 수정자 유지 + } placeholder: { + // 원본 프레임과 동일한 크기의 플레이스홀더 + RoundedRectangle(cornerRadius: 8) + .fill(Color.gray.opacity(0.1)) + .frame(width: 100, height: 120) + } + .cornerRadius(8) // 👈 플레이스홀더에도 적용하기 위해 밖으로 이동 VStack(alignment: .leading, spacing: 8) { Text("12세 이상 관람가") @@ -162,8 +169,3 @@ struct MovieDetailView: View { } - - -#Preview { - MovieDetailView(movie: MovieCardsViewModel().movieCards[2]) -} diff --git a/week06/Megabox/Views/SingleViews/MovieView/MovieSheetView.swift b/week06/Megabox/Views/SingleViews/MovieView/MovieSheetView.swift new file mode 100644 index 0000000..9ff49ab --- /dev/null +++ b/week06/Megabox/Views/SingleViews/MovieView/MovieSheetView.swift @@ -0,0 +1,136 @@ +import SwiftUI + +struct MovieSheetView: View { + +// @StateObject private var viewModel = MovieBookingViewModel() + @Environment(\.dismiss) var dismiss + @ObservedObject var viewModel: MovieBookingViewModel + + private var movieGridColumns: [GridItem] = [ + GridItem(.flexible()), + GridItem(.flexible()), + GridItem(.flexible()) + ] + + init(viewModel: MovieBookingViewModel) + { + self.viewModel = viewModel + } + + var body: some View { + + VStack{ + Spacer() + + Text("영화 선택") + .font(.regular20) + .foregroundColor(.black) + + searchMovieView + .padding(.horizontal, 16) + + + + ScrollView{ + + if viewModel.isSearching{ + Text("검색중...") + .font(.semiBold14) + .foregroundColor(.gray) + .padding(.top, 50) + } + else if viewModel.searchText.isEmpty{ + movieSheetView(for:self.viewModel.movieCards) + } + else if viewModel.filteredMovies.isEmpty{ + Text("검색 결과가 없습니다.") + .font(.semiBold14) + .foregroundColor(.gray) + .padding(.top, 50) + } + else { + // ViewModel의 'filteredMovies' 목록을 그림 + movieSheetView(for: viewModel.filteredMovies) + } + + } + + + + } + .padding(.top, 16) + } + + private var searchMovieView: some View { + + + HStack{ + Image(systemName: "magnifyingglass") + .foregroundColor(.grey04) + + TextField("Search", text: $viewModel.searchText) + .autocorrectionDisabled() // 자동 수정 비활성화 + .textInputAutocapitalization(.never) // 첫 글자 대문자 비활성화 + + Image(systemName: "mic.fill") + .foregroundColor(.grey04) + } + .padding(.horizontal, 10) + .frame(height: 36) + .background(.grey01) + .cornerRadius(10) + + + + + + + + + + + } + + private func movieSheetView(for movies:[MovieCards]) -> some View{ + + LazyVGrid(columns:movieGridColumns, spacing: 36){ + ForEach(movies) { movie in + VStack{ + // [수정] AsyncImage로 교체 + AsyncImage(url: movie.posterURL) { image in + image + .resizable() // 👈 기존 UI 수정자 유지 + .frame(width: 95, height: 135) // 👈 기존 UI 수정자 유지 + } placeholder: { + // 원본 프레임과 동일한 크기의 플레이스홀더 + RoundedRectangle(cornerRadius: 10) + .fill(Color.gray.opacity(0.1)) + .frame(width: 95, height: 135) + } + .cornerRadius(10) // 👈 플레이스홀더에도 적용하기 위해 밖으로 이동 + .padding(8) + .overlay( + RoundedRectangle(cornerRadius: 10) + .stroke(viewModel.selectedMovieID == movie.id ? .purple03 : Color.clear, lineWidth: 4) + ) + .onTapGesture { + withAnimation { + viewModel.selectMovie(id: movie.id) + } + } + + Text(movie.movieName) + .font(.semiBold14) + .foregroundColor(.black) + + } + } + } + + } + + +} +#Preview { + MovieSheetView(viewModel: MovieBookingViewModel()) +} diff --git a/week03/Megabox/Views/SingleViews/SplashView.swift b/week06/Megabox/Views/SingleViews/SplashView.swift similarity index 100% rename from week03/Megabox/Views/SingleViews/SplashView.swift rename to week06/Megabox/Views/SingleViews/SplashView.swift diff --git a/week03/Megabox/Views/SingleViews/UserInfoView.swift b/week06/Megabox/Views/SingleViews/UserInfoView.swift similarity index 94% rename from week03/Megabox/Views/SingleViews/UserInfoView.swift rename to week06/Megabox/Views/SingleViews/UserInfoView.swift index af5158f..fb50f32 100644 --- a/week03/Megabox/Views/SingleViews/UserInfoView.swift +++ b/week06/Megabox/Views/SingleViews/UserInfoView.swift @@ -38,7 +38,6 @@ struct UserInfoView: View { } HStack { - // 👇 makeBackward 함수 호출 대신 Button을 직접 여기에 넣습니다. Button(action: { dismiss() }, label: { @@ -76,7 +75,7 @@ struct UserInfoView: View { Button(action: { print("변경") - userViewModel.saveUsername(newUsername: newUsername) +userViewModel.saveNewUserName(newUsername: newUsername) }) { Text("변경") diff --git a/week03/Megabox/Views/Tabviews/HomeView.swift b/week06/Megabox/Views/Tabviews/HomeView.swift similarity index 99% rename from week03/Megabox/Views/Tabviews/HomeView.swift rename to week06/Megabox/Views/Tabviews/HomeView.swift index d5e3563..4c32034 100644 --- a/week03/Megabox/Views/Tabviews/HomeView.swift +++ b/week06/Megabox/Views/Tabviews/HomeView.swift @@ -6,7 +6,7 @@ struct HomeView: View { //현재 버튼의 상태 저장 @State private var selectedChart: ChartType = .movieChart @State private var viewModel = MovieCardsViewModel() - + var body: some View { diff --git a/week03/Megabox/Views/Tabviews/ProfileView.swift b/week06/Megabox/Views/Tabviews/ProfileView.swift similarity index 94% rename from week03/Megabox/Views/Tabviews/ProfileView.swift rename to week06/Megabox/Views/Tabviews/ProfileView.swift index 5283d40..a5e7bdb 100644 --- a/week03/Megabox/Views/Tabviews/ProfileView.swift +++ b/week06/Megabox/Views/Tabviews/ProfileView.swift @@ -35,6 +35,7 @@ struct ProfileView: View { } + private var profileHeader: some View { VStack(alignment: .leading){ @@ -54,7 +55,14 @@ struct ProfileView: View { .background(Color.tag) .cornerRadius(6) - + Button(action: { + // 3. 버튼 클릭 시 logout 함수 호출 + userViewModel.logout() + }) { + Text("로그아웃") + .font(.headline) + .foregroundColor(.red) + } Spacer() NavigationLink { diff --git a/week06/Megabox/Views/Tabviews/Tabs.swift b/week06/Megabox/Views/Tabviews/Tabs.swift new file mode 100644 index 0000000..b931923 --- /dev/null +++ b/week06/Megabox/Views/Tabviews/Tabs.swift @@ -0,0 +1,42 @@ +import SwiftUI + +struct Tabs: View { + @State private var selectedTab = 0 + @Environment(UserViewModel.self) private var userViewModel + + var body: some View { + // TabView의 selection 바인딩은 그대로 사용합니다. + TabView(selection: $selectedTab) { + + // 1. HomeView 탭 + Tab("홈", systemImage: "house.fill", value: 0) { + HomeView() + } + + // 2. "바로 예매" 탭 + Tab("바로 예매", systemImage: "movieclapper", value: 1) { + NavigationStack { + MovieBookingView() + } + } + + // 3. "모바일 오더" 탭 + Tab("모바일오더", systemImage: "popcorn", value: 2) { + Text("모바일 오더") + } + + // 4. "마이 페이지" 탭 + Tab("마이 페이지", systemImage: "person.fill", value: 3) { + NavigationStack { + ProfileView() + } + } + } + // 이 수정자는 TabView에 적용되는 것이므로 그대로 둡니다. + .navigationBarBackButtonHidden(true) + } +} + +#Preview { + Tabs().environment(UserViewModel()) +} diff --git a/week03/MegaboxTests/MegaboxTests.swift b/week06/MegaboxTests/MegaboxTests.swift similarity index 100% rename from week03/MegaboxTests/MegaboxTests.swift rename to week06/MegaboxTests/MegaboxTests.swift diff --git a/week03/MegaboxUITests/MegaboxUITests.swift b/week06/MegaboxUITests/MegaboxUITests.swift similarity index 100% rename from week03/MegaboxUITests/MegaboxUITests.swift rename to week06/MegaboxUITests/MegaboxUITests.swift diff --git a/week03/MegaboxUITests/MegaboxUITestsLaunchTests.swift b/week06/MegaboxUITests/MegaboxUITestsLaunchTests.swift similarity index 100% rename from week03/MegaboxUITests/MegaboxUITestsLaunchTests.swift rename to week06/MegaboxUITests/MegaboxUITestsLaunchTests.swift diff --git a/week03/keyword/.gitkeep b/week06/keyword/.gitkeep similarity index 100% rename from week03/keyword/.gitkeep rename to week06/keyword/.gitkeep diff --git a/week03/mission/.gitkeep b/week06/mission/.gitkeep similarity index 100% rename from week03/mission/.gitkeep rename to week06/mission/.gitkeep