From f9854f68aeaa35378ccbecabe03d4442c20e76b4 Mon Sep 17 00:00:00 2001 From: Hwang Sang Hwan <137605270+Hrepay@users.noreply.github.com> Date: Tue, 29 Sep 2026 21:09:15 +0900 Subject: [PATCH 1/4] =?UTF-8?q?[#470]=20=EA=B0=80=EC=9E=85=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C=C2=B7=EA=B2=8C=EC=8A=A4=ED=8A=B8=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=EC=8B=9C=20=ED=99=94=EB=A9=B4=20=EC=A0=84=ED=99=98?= =?UTF-8?q?=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Auth/ViewController/SetNickNameViewController.swift | 7 +++---- .../Home/ViewController/HomeRestaurantViewController.swift | 3 ++- .../TabBar/CustomTabBarContainerController.swift | 3 ++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/EATSSU/App/Sources/Presentation/Auth/ViewController/SetNickNameViewController.swift b/EATSSU/App/Sources/Presentation/Auth/ViewController/SetNickNameViewController.swift index a9ad2c1f..a41e1f66 100644 --- a/EATSSU/App/Sources/Presentation/Auth/ViewController/SetNickNameViewController.swift +++ b/EATSSU/App/Sources/Presentation/Auth/ViewController/SetNickNameViewController.swift @@ -178,12 +178,11 @@ final class SetNickNameViewController: BaseViewController { myPageVC.showToast(message: TextLiteral.Auth.updateSuccess, type: .success) } } else { - let homeVC = HomeViewController() + // 로그인 완료와 같은 탭바 화면으로 이동 (홈만 올리면 지도·찜·마이 탭이 없다) + let customTabVC = CustomTabBarContainerController() if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, let keyWindow = windowScene.windows.first(where: { $0.isKeyWindow }) { - keyWindow.replaceRootViewController( - UINavigationController(rootViewController: homeVC) - ) + keyWindow.replaceRootViewController(customTabVC) } } } diff --git a/EATSSU/App/Sources/Presentation/Home/ViewController/HomeRestaurantViewController.swift b/EATSSU/App/Sources/Presentation/Home/ViewController/HomeRestaurantViewController.swift index afe8d29c..a827623a 100644 --- a/EATSSU/App/Sources/Presentation/Home/ViewController/HomeRestaurantViewController.swift +++ b/EATSSU/App/Sources/Presentation/Home/ViewController/HomeRestaurantViewController.swift @@ -347,7 +347,8 @@ extension HomeRestaurantViewController: UITableViewDataSource { if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, let sceneDelegate = windowScene.delegate as? SceneDelegate, let window = sceneDelegate.window { - window.replaceRootViewController(loginVC) + // 신규 가입자는 로그인 화면에서 닉네임 설정으로 push되므로 네비게이션으로 감싼다 + window.replaceRootViewController(UINavigationController(rootViewController: loginVC)) } } alert.addAction(confirm) diff --git a/EATSSU/App/Sources/Presentation/TabBar/CustomTabBarContainerController.swift b/EATSSU/App/Sources/Presentation/TabBar/CustomTabBarContainerController.swift index 52606301..94de24f1 100644 --- a/EATSSU/App/Sources/Presentation/TabBar/CustomTabBarContainerController.swift +++ b/EATSSU/App/Sources/Presentation/TabBar/CustomTabBarContainerController.swift @@ -238,7 +238,8 @@ final class CustomTabBarContainerController: UITabBarController { if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, let sceneDelegate = windowScene.delegate as? SceneDelegate, let window = sceneDelegate.window { - window.replaceRootViewController(loginVC) + // 신규 가입자는 로그인 화면에서 닉네임 설정으로 push되므로 네비게이션으로 감싼다 + window.replaceRootViewController(UINavigationController(rootViewController: loginVC)) } } } From 34592b7e3074c8280d5ed6171253cf92957eaf68 Mon Sep 17 00:00:00 2001 From: Hwang Sang Hwan <137605270+Hrepay@users.noreply.github.com> Date: Tue, 29 Sep 2026 21:09:23 +0900 Subject: [PATCH 2/4] =?UTF-8?q?[#470]=20=EB=A6=AC=EB=B7=B0=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20=ED=9B=84=20=ED=99=88=20=EB=A9=94=EB=89=B4=EB=A5=BC?= =?UTF-8?q?=20=EA=B0=99=EC=9D=80=20=EB=82=A0=EC=A7=9C=EB=A1=9C=20=EB=8B=A4?= =?UTF-8?q?=EC=8B=9C=20=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Home/ViewController/CustomTimeTabController.swift | 6 ++++++ .../Home/ViewController/HomeViewController.swift | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/EATSSU/App/Sources/Presentation/Home/ViewController/CustomTimeTabController.swift b/EATSSU/App/Sources/Presentation/Home/ViewController/CustomTimeTabController.swift index 5face11d..6c36cd32 100644 --- a/EATSSU/App/Sources/Presentation/Home/ViewController/CustomTimeTabController.swift +++ b/EATSSU/App/Sources/Presentation/Home/ViewController/CustomTimeTabController.swift @@ -163,6 +163,12 @@ final class CustomTimeTabController: BaseViewController { dateSubject.send(date) } + /// 같은 날짜라도 다시 조회한다 (리뷰 작성 후 평점 갱신 등) + /// 날짜 파이프라인은 같은 날짜를 거르므로 거치지 않고 바로 요청한다 + func reloadData(for date: Date) { + performDateUpdate(date) + } + private func setupPageViewController() { addChild(pageViewController) view.addSubview(pageViewController.view) diff --git a/EATSSU/App/Sources/Presentation/Home/ViewController/HomeViewController.swift b/EATSSU/App/Sources/Presentation/Home/ViewController/HomeViewController.swift index 32230d4a..49b82306 100644 --- a/EATSSU/App/Sources/Presentation/Home/ViewController/HomeViewController.swift +++ b/EATSSU/App/Sources/Presentation/Home/ViewController/HomeViewController.swift @@ -121,7 +121,7 @@ final class HomeViewController: BaseViewController { /// 리뷰 작성 후 데이터 새로고침 func refreshAfterReview() { - tabmanController.dateFetchData(for: currentDate) + tabmanController.reloadData(for: currentDate) } /// 하단 탭바에서 학식 탭 클릭 시 오늘 날짜로 초기화 From e8cd11350fd326b098b222dda6b74557fc199626 Mon Sep 17 00:00:00 2001 From: Hwang Sang Hwan <137605270+Hrepay@users.noreply.github.com> Date: Tue, 29 Sep 2026 21:10:53 +0900 Subject: [PATCH 3/4] =?UTF-8?q?[#470]=20=EB=A9=94=EB=89=B4=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EC=8B=A4=ED=8C=A8=EB=A5=BC=20=EC=98=81=EC=97=85=20?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=20=EC=95=84=EB=8B=98=EA=B3=BC=20=EA=B5=AC?= =?UTF-8?q?=EB=B6=84=ED=95=B4=20=EC=95=88=EB=82=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Resources/en.lproj/Localizable.strings | 2 ++ .../Resources/ja.lproj/Localizable.strings | 2 ++ .../Resources/ko.lproj/Localizable.strings | 2 ++ .../Resources/vi.lproj/Localizable.strings | 2 ++ .../RestaurantMenuGroupCell.swift | 9 +++++- .../HomeRestaurantViewController.swift | 30 ++++++++++++++++--- .../Sources/Utility/Literal/TextLiteral.swift | 5 ++++ 7 files changed, 47 insertions(+), 5 deletions(-) diff --git a/EATSSU/App/Resources/en.lproj/Localizable.strings b/EATSSU/App/Resources/en.lproj/Localizable.strings index 9e583b59..8224339e 100644 --- a/EATSSU/App/Resources/en.lproj/Localizable.strings +++ b/EATSSU/App/Resources/en.lproj/Localizable.strings @@ -167,6 +167,8 @@ "home.dodamEtc" = "Asian food, pork cutlet, salad, gukbap, etc.\nCafe"; /// RestaurantMenuGroupCell - "영업 시간이 아니에요." "home.notBusinessHour" = "Closed now"; +/// RestaurantMenuGroupCell - "메뉴를 불러오지 못했어요." +"home.menuLoadFailed" = "Couldn't load the menu"; /// RestaurantTableViewHeader - "기숙사 식당" "home.dormitoryRestaurant" = "Dormitory Restaurant"; diff --git a/EATSSU/App/Resources/ja.lproj/Localizable.strings b/EATSSU/App/Resources/ja.lproj/Localizable.strings index ac2189ef..916416f4 100644 --- a/EATSSU/App/Resources/ja.lproj/Localizable.strings +++ b/EATSSU/App/Resources/ja.lproj/Localizable.strings @@ -168,6 +168,8 @@ "home.dodamEtc" = "アジアンフード、トンカツ、サラダ、クッパなど\nカフェ"; /// RestaurantMenuGroupCell - "영업 시간이 아니에요." "home.notBusinessHour" = "営業時間外です。"; +/// RestaurantMenuGroupCell - "메뉴를 불러오지 못했어요." +"home.menuLoadFailed" = "メニューを読み込めませんでした。"; /// RestaurantTableViewHeader - "기숙사 식당" "home.dormitoryRestaurant" = "寮食堂"; diff --git a/EATSSU/App/Resources/ko.lproj/Localizable.strings b/EATSSU/App/Resources/ko.lproj/Localizable.strings index 2296203f..70abfcc4 100644 --- a/EATSSU/App/Resources/ko.lproj/Localizable.strings +++ b/EATSSU/App/Resources/ko.lproj/Localizable.strings @@ -168,6 +168,8 @@ "home.dodamEtc" = "아시안푸드, 돈까스, 샐러드, 국밥 등\n카페"; /// RestaurantMenuGroupCell - "영업 시간이 아니에요." "home.notBusinessHour" = "영업 시간이 아니에요."; +/// RestaurantMenuGroupCell - "메뉴를 불러오지 못했어요." +"home.menuLoadFailed" = "메뉴를 불러오지 못했어요."; /// RestaurantTableViewHeader - "기숙사 식당" "home.dormitoryRestaurant" = "기숙사 식당"; diff --git a/EATSSU/App/Resources/vi.lproj/Localizable.strings b/EATSSU/App/Resources/vi.lproj/Localizable.strings index 859b299d..ae70223c 100644 --- a/EATSSU/App/Resources/vi.lproj/Localizable.strings +++ b/EATSSU/App/Resources/vi.lproj/Localizable.strings @@ -168,6 +168,8 @@ "home.dodamEtc" = "Món Á, tonkatsu, salad, gukbap, v.v.\nQuán cà phê"; /// RestaurantMenuGroupCell - "영업 시간이 아니에요." "home.notBusinessHour" = "Hiện không trong giờ hoạt động."; +/// RestaurantMenuGroupCell - "메뉴를 불러오지 못했어요." +"home.menuLoadFailed" = "Không thể tải thực đơn."; /// RestaurantTableViewHeader - "기숙사 식당" "home.dormitoryRestaurant" = "Nhà ăn ký túc xá"; diff --git a/EATSSU/App/Sources/Presentation/Home/View/RestaurantTableView/RestaurantMenuGroupCell.swift b/EATSSU/App/Sources/Presentation/Home/View/RestaurantTableView/RestaurantMenuGroupCell.swift index 01a6a5e3..dac0f3ea 100644 --- a/EATSSU/App/Sources/Presentation/Home/View/RestaurantTableView/RestaurantMenuGroupCell.swift +++ b/EATSSU/App/Sources/Presentation/Home/View/RestaurantTableView/RestaurantMenuGroupCell.swift @@ -107,14 +107,21 @@ final class RestaurantMenuGroupCell: BaseTableViewCell { /// 메뉴 데이터를 이용해 스택뷰를 구성 /// - Parameter menus: 메뉴 리스트 (MenuTypeInfo 배열) + /// - Parameter emptyMessage: 메뉴가 없을 때 보여줄 문구 (영업 시간 아님 / 조회 실패) /// - Parameter indexPath: 현재 테이블뷰 indexPath (섹션, row) /// - Parameter onMenuTap: 터치 시 실행할 클로저 (section, index 전달) - func configure(with menus: [MenuTypeInfo], at indexPath: IndexPath, onMenuTap: @escaping (IndexPath, Int) -> Void) { + func configure( + with menus: [MenuTypeInfo], + emptyMessage: String = TextLiteral.Home.notBusinessHour, + at indexPath: IndexPath, + onMenuTap: @escaping (IndexPath, Int) -> Void + ) { // 이전 뷰들 제거 (주의: prepareForReuse와 중복 제거됨 → 안정성 보장용) menuStackView.arrangedSubviews.forEach { $0.removeFromSuperview() } // 메뉴가 없을 경우 → 안내 텍스트 표시 if menus.isEmpty { + emptyLabel.text = emptyMessage emptyLabel.isHidden = false menuStackView.isHidden = true } else { diff --git a/EATSSU/App/Sources/Presentation/Home/ViewController/HomeRestaurantViewController.swift b/EATSSU/App/Sources/Presentation/Home/ViewController/HomeRestaurantViewController.swift index a827623a..5b2d779c 100644 --- a/EATSSU/App/Sources/Presentation/Home/ViewController/HomeRestaurantViewController.swift +++ b/EATSSU/App/Sources/Presentation/Home/ViewController/HomeRestaurantViewController.swift @@ -93,6 +93,9 @@ final class HomeRestaurantViewController: BaseViewController { // 고정 메뉴 데이터 (간식코너) var fixMenuTableViewData: [String: [Menus]] = [:] + + // 메뉴 조회에 실패한 식당. 빈 목록을 "영업 시간 아님"과 구분해 안내한다 + private var failedRestaurants: Set = [] // MARK: - UI Components @@ -227,6 +230,7 @@ final class HomeRestaurantViewController: BaseViewController { private func hideSnackCorner() { isSelectable = false fixMenuTableViewData[RestaurantIdentifier.snackCorner.rawValue] = [] + setLoadFailed(false, for: RestaurantIdentifier.snackCorner.rawValue) if let sectionIndex = getSectionIndex(for: RestaurantIdentifier.snackCorner.rawValue) { restaurantView.restaurantTableView.reloadSections( @@ -264,7 +268,11 @@ extension HomeRestaurantViewController: UITableViewDataSource { ? (fixMenuTableViewData[sectionKey]?.map { .fix($0) } ?? []) : (changeMenuTableViewData[sectionKey]?.map { .change($0) } ?? []) - cell.configure(with: menuList, at: indexPath) { [weak self] indexPath, menuIndex in + let emptyMessage = failedRestaurants.contains(sectionKey) + ? TextLiteral.Home.menuLoadFailed + : TextLiteral.Home.notBusinessHour + + cell.configure(with: menuList, emptyMessage: emptyMessage, at: indexPath) { [weak self] indexPath, menuIndex in self?.handleMenuTap(section: indexPath.section, menuIndex: menuIndex) } return cell @@ -429,13 +437,22 @@ extension HomeRestaurantViewController: UITableViewDelegate { // MARK: - Network extension HomeRestaurantViewController { + private func setLoadFailed(_ didFail: Bool, for restaurant: String) { + if didFail { + failedRestaurants.insert(restaurant) + } else { + failedRestaurants.remove(restaurant) + } + } + // 변경 메뉴 요청 API 호출 func fetchChangeMenuData(date: String, restaurant: String, time: String) async { // UI 업데이트에 사용할 메뉴 목록과 애니메이션 타입을 저장할 변수 let menusToUpdate: [ChangeMenuTableResponse] let animation: UITableView.RowAnimation - + var didFail = false + do { // 비동기 네트워크 요청을 통해 메뉴 데이터 가져오기 let menus: [ChangeMenuTableResponse] = try await withCheckedThrowingContinuation { continuation in @@ -459,11 +476,13 @@ extension HomeRestaurantViewController { print("\(restaurant) 변경 메뉴 조회 실패: \(error.localizedDescription)") menusToUpdate = [] animation = .none + didFail = true } - + // 메인 스레드에서 UI 업데이트 await MainActor.run { self.changeMenuTableViewData[restaurant] = menusToUpdate + self.setLoadFailed(didFail, for: restaurant) if let sectionIndex = self.getSectionIndex(for: restaurant) { self.restaurantView.restaurantTableView.reloadSections(IndexSet(integer: sectionIndex), with: animation) @@ -475,6 +494,7 @@ extension HomeRestaurantViewController { func fetchFixedMenuData(restaurant: String) async { var menuData: [Menus] = [] var animation: UITableView.RowAnimation = .none + var didFail = false do { let response: FixedMenuTableResponse = try await withCheckedThrowingContinuation { continuation in @@ -493,11 +513,13 @@ extension HomeRestaurantViewController { animation = .fade } catch { print("\(restaurant) 고정 메뉴 조회 실패: \(error.localizedDescription)") + didFail = true } - + // 메인 스레드에서 UI 업데이트 await MainActor.run { self.fixMenuTableViewData[restaurant] = menuData + self.setLoadFailed(didFail, for: restaurant) if let sectionIndex = self.getSectionIndex(for: restaurant) { self.restaurantView.restaurantTableView.reloadSections(IndexSet(integer: sectionIndex), with: animation) } diff --git a/EATSSU/App/Sources/Utility/Literal/TextLiteral.swift b/EATSSU/App/Sources/Utility/Literal/TextLiteral.swift index d6d6a739..a894ba96 100644 --- a/EATSSU/App/Sources/Utility/Literal/TextLiteral.swift +++ b/EATSSU/App/Sources/Utility/Literal/TextLiteral.swift @@ -452,6 +452,11 @@ enum TextLiteral { Localization.localized("home.notBusinessHour", fallback: "영업 시간이 아니에요.") } + /// RestaurantMenuGroupCell - "메뉴를 불러오지 못했어요." + static var menuLoadFailed: String { + Localization.localized("home.menuLoadFailed", fallback: "메뉴를 불러오지 못했어요.") + } + /// RestaurantTableViewHeader - "기숙사 식당" static var dormitoryRestaurant: String { Localization.localized("home.dormitoryRestaurant", fallback: "기숙사 식당") From fcddbee19277f9143188907afdcf9946c49bcb4f Mon Sep 17 00:00:00 2001 From: Hwang Sang Hwan <137605270+Hrepay@users.noreply.github.com> Date: Mon, 5 Oct 2026 17:15:23 +0900 Subject: [PATCH 4/4] =?UTF-8?q?[#470]=20=EB=82=A0=EC=A7=9C=EB=A5=BC=20?= =?UTF-8?q?=EB=B0=94=EA=BE=BC=20=EB=92=A4=20=EB=8F=84=EC=B0=A9=ED=95=9C=20?= =?UTF-8?q?=EC=9D=B4=EC=A0=84=20=EB=82=A0=EC=A7=9C=20=EC=9D=91=EB=8B=B5?= =?UTF-8?q?=EC=9D=B4=20=ED=99=94=EB=A9=B4=EC=9D=84=20=EB=8D=AE=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Home/ViewController/HomeRestaurantViewController.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/EATSSU/App/Sources/Presentation/Home/ViewController/HomeRestaurantViewController.swift b/EATSSU/App/Sources/Presentation/Home/ViewController/HomeRestaurantViewController.swift index 5b2d779c..05a92076 100644 --- a/EATSSU/App/Sources/Presentation/Home/ViewController/HomeRestaurantViewController.swift +++ b/EATSSU/App/Sources/Presentation/Home/ViewController/HomeRestaurantViewController.swift @@ -192,6 +192,8 @@ final class HomeRestaurantViewController: BaseViewController { guard let self else { return } let isHoliday = await self.isHoliday(date: date) + // 공휴일을 조회하는 사이 다른 날짜로 바뀌었다면 반영하지 않는다 + guard !_Concurrency.Task.isCancelled else { return } if !FirebaseRemoteConfig.shared.isVacationPeriod, !weekday.isWeekend, @@ -481,6 +483,8 @@ extension HomeRestaurantViewController { // 메인 스레드에서 UI 업데이트 await MainActor.run { + // 네트워크 요청은 취소되지 않으므로, 다른 날짜로 바뀐 뒤 도착한 응답은 버린다 + guard !_Concurrency.Task.isCancelled else { return } self.changeMenuTableViewData[restaurant] = menusToUpdate self.setLoadFailed(didFail, for: restaurant) @@ -518,6 +522,8 @@ extension HomeRestaurantViewController { // 메인 스레드에서 UI 업데이트 await MainActor.run { + // 네트워크 요청은 취소되지 않으므로, 다른 날짜로 바뀐 뒤 도착한 응답은 버린다 + guard !_Concurrency.Task.isCancelled else { return } self.fixMenuTableViewData[restaurant] = menuData self.setLoadFailed(didFail, for: restaurant) if let sectionIndex = self.getSectionIndex(for: restaurant) {