Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
6e986e1
[변경] Reactor에서 usecase를 통해 authManager 접근하도록 변경
hyeonsik971029 Nov 17, 2025
0169cb8
[변경] Reactor/View에서 usecase를 통해 pushManager/locationManager 접근하도록 변경
hyeonsik971029 Nov 17, 2025
74a5a69
[변경] 설정 > 공지사항 화면에서 임의로 붙였던 `공지` 접두사 제거
hyeonsik971029 Nov 17, 2025
8e8ea06
[삭제] v1 버전 API request 및 model 삭제
hyeonsik971029 Nov 17, 2025
af5f179
[변경] 태그 관련 API 및 클린 아키텍처 관련 파일 업데이트
hyeonsik971029 Nov 17, 2025
e651ae6
[수정] 탈퇴하기 팝업 버튼 색상 변경
hyeonsik971029 Nov 17, 2025
98c6631
[수정] 회원탈퇴 화면 하단 버튼 높이 수정
hyeonsik971029 Nov 17, 2025
4870585
[삭제] ㅎㅎ
hyeonsik971029 Nov 17, 2025
ba43bf8
[수정] 회원탈퇴 화면 view 간격 수정
hyeonsik971029 Nov 17, 2025
83c1589
[수정] 닉네임 입력 시 항상 clear 버튼 표시
hyeonsik971029 Nov 17, 2025
8f78c72
[변경] 홈 화면 공지 뷰 자동 스크롤 조건 변경
hyeonsik971029 Nov 17, 2025
0a4b51b
[수정] 회원탈퇴 화면 기타 입력 글자 수 제한 수정
hyeonsik971029 Nov 17, 2025
db774bf
[수정] 회원탈퇴 화면 `기타` 내용 입력 시 키보드 인터랙션 수정
hyeonsik971029 Nov 18, 2025
c701be6
[수정] 서버 응답 필드 타입 수정
hyeonsik971029 Nov 18, 2025
87644b7
[수정] 오류 발생 시 처리 코드 연결
hyeonsik971029 Nov 18, 2025
1254fee
[버전] Develop 1.18.5(1018050) 버전 업데이트
hyeonsik971029 Nov 18, 2025
0ff309d
[추가] 사용자 닉네임 정보 로컬에서 관리
hyeonsik971029 Nov 18, 2025
963b9a9
[삭제] 불필요한 주석 삭제
hyeonsik971029 Nov 18, 2025
aea9a33
[수정] 재인증 로직 수정
hyeonsik971029 Nov 18, 2025
4b564f8
[버전] Develop 1.18.6(1018060) 버전 업데이트
hyeonsik971029 Nov 18, 2025
6b7e493
[수정] 재인증 과정 종료 후 초기화 과정 추가
hyeonsik971029 Nov 18, 2025
ac8424f
[버전] Develop 1.18.7(1018070) 버전업데이트
hyeonsik971029 Nov 18, 2025
bbc2995
[수정] 회원탈퇴 화면 기타 입력 시 키보드 인터랙션 수정
hyeonsik971029 Nov 19, 2025
c6ad039
[추가] 키체인 로드 실패 시 로깅 더 자세히 추가
hyeonsik971029 Nov 20, 2025
fe8ea2e
[수정] 불필요한 코드 제거
hyeonsik971029 Nov 24, 2025
567a474
[변경] 리프레쉬 컨트롤 로직 변경
hyeonsik971029 Nov 24, 2025
4e680fb
[추가] 개발에 필요한 유틸 추가
hyeonsik971029 Nov 24, 2025
ee120a0
[추가] 프로필 탭에서 상세 화면 전환 추가
hyeonsik971029 Nov 24, 2025
aa32161
[추가] 관심 태그 관련 파일 추가
hyeonsik971029 Nov 24, 2025
b075047
[추가] 인기 태그 관련 파일 추가
hyeonsik971029 Nov 24, 2025
1212942
[추가] 태그 모아보기 화면 관련 파일 추가
hyeonsik971029 Nov 24, 2025
aa0b487
[추가] 태그 검색 + 모아보기 화면 관련 파일 추가
hyeonsik971029 Nov 24, 2025
174197b
[추가] 태그 탭 관련 파일 추가
hyeonsik971029 Nov 24, 2025
11a85c2
[추가] 계정이관 성공 시 온보딩화면으로 이동
hyeonsik971029 Nov 24, 2025
f0598b0
[버전] Develop 1.19.0(1019000) 버전 업데이트
hyeonsik971029 Nov 24, 2025
bc2d9a5
[추가] 상세 화면에서 태그 모아보기 화면 전환 추가
hyeonsik971029 Nov 24, 2025
4079e32
[삭제] 불필요한 주석 삭제
hyeonsik971029 Nov 24, 2025
bd6abd9
[추가] 화면 전환 혹은 api 호출 탭의 경우 throttle 추가
hyeonsik971029 Nov 24, 2025
3b13ae2
[변경] 검색 텍스트필드 로직 수정
hyeonsik971029 Nov 24, 2025
2a1d83d
[수정] 카드추가 화면에서 관련 태그 뷰 레이아웃 수정
hyeonsik971029 Nov 24, 2025
95d13e8
[추가] 알림 화면 태그 알림 추가
hyeonsik971029 Nov 24, 2025
24767d5
[버전] Develop 1.19.1(1019010) 버전 업데이트
hyeonsik971029 Nov 24, 2025
2313c07
[변경] 로그인 실패 시 로직 변경
hyeonsik971029 Nov 25, 2025
ab323d5
[수정] 태그 검색 시 텍스트 인풋 영역 수정
hyeonsik971029 Nov 25, 2025
d1f40cc
[수정] 계정이관 성공 시 기존 토큰 제거
hyeonsik971029 Nov 25, 2025
b3826c7
[수정] 회원 탈퇴화면 기타 입력 시 키보드 인터랙션 수정
hyeonsik971029 Nov 26, 2025
1b454ea
[수정] 온보딩 및 프로필 업데이트 화면에서 에러 로직 수정
hyeonsik971029 Nov 26, 2025
e931885
[수정] 빈 화면일 때 UI 중앙 정렬
hyeonsik971029 Nov 26, 2025
88791cb
[수정] banned와 withdraw 유저 분리해서 처리
hyeonsik971029 Nov 26, 2025
d6f79fc
[추가] 태그 검색 후 모아보기 화면에서 관심 태그 조회 추가
hyeonsik971029 Nov 26, 2025
8f5f457
[추가] 알림 화면 관련 태그 상세 화면 전환 추가
hyeonsik971029 Nov 27, 2025
e02f452
[변경] 계정 이관 성공 후 온보딩 화면으로 전환
hyeonsik971029 Nov 27, 2025
8f84fb5
[변경] 인증 실패 후 온보딩 화면 전환 시 트랜지션 변경
hyeonsik971029 Nov 27, 2025
4990850
[추가] 푸시 알림 통해 앱 진입 시 화면 플로우 추가
hyeonsik971029 Nov 27, 2025
2c51a12
[추가] 탭 전환 추가
hyeonsik971029 Nov 27, 2025
9663983
[삭제] 불필요한 코드 삭제
hyeonsik971029 Nov 27, 2025
113eb11
[수정] 상세 화면 댓글 카드 정렬 수정
hyeonsik971029 Nov 27, 2025
3af8c0e
[버전] Develop 1.20.0(1020000) 버전 업데이트
hyeonsik971029 Nov 27, 2025
ba3a513
[수정] 푸시 알림으로 앱 진입 시 로직 수정
hyeonsik971029 Nov 27, 2025
41884fc
[수정] 홈 화면 우측 알림 아이콘 레이아웃 수정
hyeonsik971029 Nov 27, 2025
2326a10
[버전] Develop 1.20.1(1020010) 버전 업데이트
hyeonsik971029 Nov 27, 2025
44dafc1
[수정] 닉네임 텍스트필드 X 버튼 표시 로직 수정
hyeonsik971029 Nov 29, 2025
d51beaf
[수정] 삭제된 카드에 대한 화면 전환 시 로직 수정
hyeonsik971029 Nov 29, 2025
a166229
[버전] Develop 1.20.2(1020020) 버전 업데이트
hyeonsik971029 Nov 29, 2025
1a9110f
[수정] 삭제된 상세 화면에서 카드추가 시 로직 수정
hyeonsik971029 Nov 30, 2025
93f4a90
[수정] 상세 화면 댓글 카드 top inset 수정
hyeonsik971029 Nov 30, 2025
3a6ac15
rebase 요망
hyeonsik971029 Nov 30, 2025
485597f
[추가] 앱 처음 진입 시 카드추가 가이드 뷰 추가
hyeonsik971029 Nov 30, 2025
faaaa86
[수정] 카드추가 화면 삭제된 카드일 때 플로우 수정
hyeonsik971029 Nov 30, 2025
d8ec564
[수정] UITextView 줄바꿈 시 겹치는 문제 해결
hyeonsik971029 Nov 30, 2025
dedc8bd
[버전] Develop 1.20.3(1020030) 버전 업데이트
hyeonsik971029 Nov 30, 2025
1dc9867
[수정] 상세 화면 댓글 카드 정렬 수정
hyeonsik971029 Dec 1, 2025
82c556a
[수정] 스크롤 뷰에서 아래->위 스크롤 시 위치 고정
hyeonsik971029 Dec 1, 2025
2954cc1
[추가] 스크롤 뷰에서 초기값 설정
hyeonsik971029 Dec 1, 2025
781f051
[추가] 삭제된 피드 카드에서도 홈 화면 전환
hyeonsik971029 Dec 1, 2025
0046747
[버전] Develop 1.20.4(1020040) 버전 업데이트
hyeonsik971029 Dec 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
800 changes: 272 additions & 528 deletions SOOUM/SOOUM.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

73 changes: 54 additions & 19 deletions SOOUM/SOOUM/App/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,18 @@ class AppDelegate: UIResponder, UIApplicationDelegate {

extension AppDelegate: UNUserNotificationCenterDelegate {

/// Foreground(앱 켜진 상태)에서도 알림 오는 설정
/// Foreground(앱 켜진 상태)에서 알림 왔을 때, 설정 및 데이터 수신
func userNotificationCenter(
_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
) {
// 계정 이관 성공 시 (런치 화면 > 온보딩 화면)으로 전환
let userInfo = notification.request.content.userInfo
self.setupOnboardingWhenTransferSuccessed(userInfo)
guard let infoDic = userInfo as? [String: Any] else { return }

let info = PushNotificationInfo(infoDic)
if info.isTransfered { self.setupOnboarding() }

var options: UNNotificationPresentationOptions
if let isReAddedNotifications = userInfo["isReAddedNotifications"] as? Bool, isReAddedNotifications {
Expand All @@ -109,14 +112,17 @@ extension AppDelegate: UNUserNotificationCenterDelegate {
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
) {
// let userInfo: [AnyHashable: Any] = response.notification.request.content.userInfo
// if let infoDic = userInfo as? [String: Any] {
//
// let info = NotificationInfo(infoDic)
// // 계정 이관 성공 알림일 경우 (런치 화면 > 온보딩 화면), 아닐 경우 메인 홈 탭바 화면 전환
// self.provider.pushManager.setupRootViewController(info, terminated: info.isTransfered)
// }

// 계정 이관 성공 알림일 경우 온보딩 화면, 아닐 경우 메인 홈 탭바 화면 전환
let userInfo: [AnyHashable: Any] = response.notification.request.content.userInfo
guard let infoDic = userInfo as? [String: Any] else { return }

let info = PushNotificationInfo(infoDic)
if info.isTransfered {
self.setupOnboarding()
} else {
self.setupMainTabBar(info)
}

completionHandler()
}

Expand All @@ -127,7 +133,10 @@ extension AppDelegate: UNUserNotificationCenterDelegate {
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
) {
// 계정 이관 성공 시 (런치 화면 > 온보딩 화면)으로 전환
self.setupOnboardingWhenTransferSuccessed(userInfo)
guard let infoDic = userInfo as? [String: Any] else { return }

let info = PushNotificationInfo(infoDic)
if info.isTransfered { self.setupLaunchScreen(info) }

completionHandler(.newData)
}
Expand Down Expand Up @@ -205,13 +214,39 @@ extension AppDelegate {
cache.memoryStorage.config.totalCostLimit = memoryLimit
}

private func setupOnboardingWhenTransferSuccessed(_ userInfo: [AnyHashable: Any]?) {
// guard let infoDic = userInfo as? [String: Any] else { return }

// let info = NotificationInfo(infoDic)
// if info.isTransfered {
//
// self.provider.pushManager.setupRootViewController(info, terminated: true)
// }
func setupOnboarding() {

guard let windowScene: UIWindowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let window: UIWindow = windowScene.windows.first(where: { $0.isKeyWindow })
else { return }

let onboardingViewController = OnboardingViewController()
onboardingViewController.reactor = OnboardingViewReactor(dependencies: self.appDIContainer)
onboardingViewController.modalTransitionStyle = .crossDissolve
window.rootViewController = UINavigationController(rootViewController: onboardingViewController)
}

func setupLaunchScreen(_ info: PushNotificationInfo) {

guard let windowScene: UIWindowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let window: UIWindow = windowScene.windows.first(where: { $0.isKeyWindow })
else { return }

let launchScreenViewController = LaunchScreenViewController()
launchScreenViewController.reactor = LaunchScreenViewReactor(dependencies: self.appDIContainer, pushInfo: info)
launchScreenViewController.modalTransitionStyle = .crossDissolve
window.rootViewController = UINavigationController(rootViewController: launchScreenViewController)
}

func setupMainTabBar(_ info: PushNotificationInfo) {

guard let windowScene: UIWindowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let window: UIWindow = windowScene.windows.first(where: { $0.isKeyWindow })
else { return }

let mainTabBarController = MainTabBarController()
mainTabBarController.reactor = MainTabBarReactor(dependencies: self.appDIContainer, pushInfo: info)
mainTabBarController.modalTransitionStyle = .crossDissolve
window.rootViewController = UINavigationController(rootViewController: mainTabBarController)
}
}
12 changes: 5 additions & 7 deletions SOOUM/SOOUM/App/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,11 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {

/// 앱이 완전히 종료되었을 때 push notification에 대한 응답을 했을 때 실행할 코드 작성
if let response: UNNotificationResponse = connectionOptions.notificationResponse {
// let userInfo: [AnyHashable: Any] = response.notification.request.content.userInfo

// if let infoDic: [String: Any] = userInfo as? [String: Any] {
//
// let info = NotificationInfo(infoDic)
// appDelegate.provider.pushManager.setupRootViewController(info, terminated: true)
// }
let userInfo: [AnyHashable: Any] = response.notification.request.content.userInfo
if let infoDic = userInfo as? [String: Any] {
let info = PushNotificationInfo(infoDic)
appDelegate.setupLaunchScreen(info)
}
}
}

Expand Down
45 changes: 31 additions & 14 deletions SOOUM/SOOUM/Data/Managers/AuthManager/AuthManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -157,14 +157,16 @@ extension AuthManager: AuthManagerDelegate {
.map(\.token)
.flatMapLatest { token -> Observable<Bool> in

// 사용자 닉네임 업데이트
UserDefaults.standard.nickname = nickname

// session token 업데이트
object.authInfo.updateToken(token)

// FCM token 업데이트
provider.networkManager.registerFCMToken(from: #function)
return .just(true)
}
.catchAndReturn(false)
} else {
return .just(false)
}
Expand All @@ -185,7 +187,8 @@ extension AuthManager: AuthManagerDelegate {
let request: AuthRequest = .login(encryptedDeviceId: encryptedDeviceId)
return provider.networkManager.perform(LoginResponse.self, request: request)
.map(\.token)
.flatMapLatest { token -> Observable<Bool> in
.withUnretained(self)
.flatMapLatest { object, token -> Observable<Bool> in

// session token 업데이트
object.authInfo.updateToken(token)
Expand All @@ -194,7 +197,29 @@ extension AuthManager: AuthManagerDelegate {
provider.networkManager.registerFCMToken(from: #function)
return .just(true)
}
.catchAndReturn(false)
.catch { error in

let errorCode = (error as NSError).code
if case 404 = errorCode {

// session token 삭제
object.authInfo.initAuthInfo()

// onboarding screen 전환
DispatchQueue.main.async {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate,
let windowScene: UIWindowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let window: UIWindow = windowScene.windows.first(where: { $0.isKeyWindow })
else { return }

let onBoardingViewController = OnboardingViewController()
onBoardingViewController.reactor = OnboardingViewReactor(dependencies: appDelegate.appDIContainer)
onBoardingViewController.modalTransitionStyle = .crossDissolve
window.rootViewController = UINavigationController(rootViewController: onBoardingViewController)
}
}
return .just(false)
}
} else {
return .just(false)
}
Expand Down Expand Up @@ -266,20 +291,12 @@ extension AuthManager: AuthManagerDelegate {
object.isReAuthenticating = false
},
onError: { object, error in

// TODO: 임시, 리프레쉬 토큰 만료 에러코드가 정의되지 않음
// let errorCode = (error as NSError).code
// if case 403 = errorCode {
//
// object.certification()
// .subscribe(onNext: { isRegistered in
// object.excutePendingResults(isRegistered ? .success : .failure(error))
// })
// .disposed(by: object.disposeBag)
// }
/// 재인증 과정이 실패하면 항상 재로그인 시도
object.certification()
.subscribe(onNext: { isRegistered in
object.excutePendingResults(isRegistered ? .success : .failure(error))

object.isReAuthenticating = false
})
.disposed(by: object.disposeBag)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ class ErrorInterceptor: RequestInterceptor {
}
}
return
case 403:
case 418:
self.goToOnboarding()
completion(.doNotRetry)
return
Expand All @@ -112,21 +112,6 @@ class ErrorInterceptor: RequestInterceptor {

// MARK: Error handling

func goToOnboarding() {

self.provider.authManager.initializeAuthInfo()

DispatchQueue.main.async {
if let window: UIWindow = UIApplication.currentWindow,
let appDelegate = UIApplication.shared.delegate as? AppDelegate {

let onboardingViewController = OnboardingViewController()
onboardingViewController.reactor = OnboardingViewReactor(dependencies: appDelegate.appDIContainer)
window.rootViewController = UINavigationController(rootViewController: onboardingViewController)
}
}
}

func showNetworkErrorDialog() {

let confirmAction = SOMDialogAction(
Expand Down Expand Up @@ -189,4 +174,24 @@ class ErrorInterceptor: RequestInterceptor {
)
}
}


// MARK: go to onboarding

func goToOnboarding() {

self.provider.authManager.initializeAuthInfo()

DispatchQueue.main.async {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate,
let windowScene: UIWindowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let window: UIWindow = windowScene.windows.first(where: { $0.isKeyWindow })
else { return }

let onBoardingViewController = OnboardingViewController()
onBoardingViewController.reactor = OnboardingViewReactor(dependencies: appDelegate.appDIContainer)
onBoardingViewController.modalTransitionStyle = .crossDissolve
window.rootViewController = UINavigationController(rootViewController: onBoardingViewController)
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// NotificationInfo.swift
// SOOUM
//
// Created by 오현식 on 12/27/24.
//

import Foundation

class PushNotificationInfo {

let notificationType: CommonNotificationInfo.NotificationType
let notificationId: String?
let targetCardId: String?

var isTransfered: Bool {
return self.notificationType == .transferSuccess
}

init(_ info: [String: Any]) {
let notificationType = info["notificationType"] as? String ?? ""
self.notificationType = CommonNotificationInfo.NotificationType(rawValue: notificationType) ?? .none
self.notificationId = info["notificationId"] as? String
self.targetCardId = info["targetCardId"] as? String

Log.info(
"""
PushNotificationInfo:
notificationType: \(self.notificationType)
notificationId: \(self.notificationId ?? "")
targetCardId: \(self.targetCardId ?? "")
"""
)
}
}
27 changes: 27 additions & 0 deletions SOOUM/SOOUM/Data/Models/Responses/FavoriteTagInfoResponse.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// FavoriteTagInfoResponse.swift
// SOOUM
//
// Created by 오현식 on 11/18/25.
//

import Alamofire

struct FavoriteTagInfoResponse {

let tagInfos: [FavoriteTagInfo]
}

extension FavoriteTagInfoResponse: EmptyResponse {

static func emptyValue() -> FavoriteTagInfoResponse {
FavoriteTagInfoResponse(tagInfos: [])
}
}

extension FavoriteTagInfoResponse: Decodable {

enum CodingKeys: String, CodingKey {
case tagInfos = "favoriteTags"
}
}
35 changes: 35 additions & 0 deletions SOOUM/SOOUM/Data/Models/Responses/TagCardInfoResponse.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// TagCardInfoResponse.swift
// SOOUM
//
// Created by 오현식 on 11/18/25.
//

import Alamofire

struct TagCardInfoResponse {

let cardInfos: [ProfileCardInfo]
let isFavorite: Bool
}

extension TagCardInfoResponse: EmptyResponse {

static func emptyValue() -> TagCardInfoResponse {
TagCardInfoResponse(cardInfos: [], isFavorite: false)
}
}

extension TagCardInfoResponse: Decodable {

enum CodingKeys: String, CodingKey {
case cardInfos = "cardContents"
case isFavorite
}

init(from decoder: any Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.cardInfos = try container.decode([ProfileCardInfo].self, forKey: .cardInfos)
self.isFavorite = try container.decode(Bool.self, forKey: .isFavorite)
}
}
Loading