Skip to content

Commit fbffcfa

Browse files
authored
Merge pull request #334 from Atcha-Project/env/dev
[Merge] Live 환경 배포 및 심사 (v1.9.6)
2 parents 42f59a0 + aa550ab commit fbffcfa

30 files changed

Lines changed: 399 additions & 164 deletions

Atcha-iOS.xcodeproj/project.pbxproj

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2516,7 +2516,7 @@
25162516
CODE_SIGN_IDENTITY = "Apple Development";
25172517
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
25182518
CODE_SIGN_STYLE = Manual;
2519-
CURRENT_PROJECT_VERSION = 4;
2519+
CURRENT_PROJECT_VERSION = 1;
25202520
DEVELOPMENT_TEAM = "";
25212521
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 23SCTLK482;
25222522
FRAMEWORK_SEARCH_PATHS = (
@@ -2539,7 +2539,7 @@
25392539
"$(inherited)",
25402540
"@executable_path/Frameworks",
25412541
);
2542-
MARKETING_VERSION = 1.9.5;
2542+
MARKETING_VERSION = 1.9.6;
25432543
PRODUCT_BUNDLE_IDENTIFIER = com.atcha.iOS;
25442544
PRODUCT_NAME = "$(TARGET_NAME)";
25452545
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -2563,7 +2563,7 @@
25632563
CODE_SIGN_ENTITLEMENTS = "Atcha-iOS/Atcha-iOS.entitlements";
25642564
CODE_SIGN_IDENTITY = "Apple Development";
25652565
CODE_SIGN_STYLE = Automatic;
2566-
CURRENT_PROJECT_VERSION = 4;
2566+
CURRENT_PROJECT_VERSION = 1;
25672567
DEVELOPMENT_TEAM = 23SCTLK482;
25682568
EXCLUDED_ARCHS = "";
25692569
FRAMEWORK_SEARCH_PATHS = (
@@ -2586,7 +2586,7 @@
25862586
"$(inherited)",
25872587
"@executable_path/Frameworks",
25882588
);
2589-
MARKETING_VERSION = 1.9.5;
2589+
MARKETING_VERSION = 1.9.6;
25902590
OTHER_SWIFT_FLAGS = "";
25912591
PRODUCT_BUNDLE_IDENTIFIER = com.atcha.iOS;
25922592
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -2611,7 +2611,7 @@
26112611
CODE_SIGN_IDENTITY = "Apple Development";
26122612
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
26132613
CODE_SIGN_STYLE = Manual;
2614-
CURRENT_PROJECT_VERSION = 4;
2614+
CURRENT_PROJECT_VERSION = 1;
26152615
DEVELOPMENT_TEAM = "";
26162616
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 23SCTLK482;
26172617
FRAMEWORK_SEARCH_PATHS = (
@@ -2634,7 +2634,7 @@
26342634
"$(inherited)",
26352635
"@executable_path/Frameworks",
26362636
);
2637-
MARKETING_VERSION = 1.9.5;
2637+
MARKETING_VERSION = 1.9.6;
26382638
PRODUCT_BUNDLE_IDENTIFIER = com.atcha.iOS;
26392639
PRODUCT_NAME = "$(TARGET_NAME)";
26402640
PROVISIONING_PROFILE_SPECIFIER = "";

Atcha-iOS/App/AppConfig.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,6 @@ enum AppConfig {
2121
static var kakaoInitKey: String { required("KAKAO_INIT_KEY") }
2222
static var tmapApiKey: String { required("TMAP_API_KEY") }
2323
static var amplitudeApiKey: String { required("AMPLITUDE_API_KEY") }
24+
static var errorWebhookURL: String { required("ERROR_WEBHOOK_URL") }
25+
static var authWebhookURL: String { required("AUTH_WEBHOOK_URL") }
2426
}

Atcha-iOS/App/AppFlowCoordinator.swift

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class AppFlowCoordinator {
2525
self.container = container
2626
}
2727

28-
func startApp() {
28+
func startApp(launchType: LaunchType = .main) {
2929
let navigationController = UINavigationController()
3030
window.rootViewController = navigationController
3131
window.makeKeyAndVisible()
@@ -35,11 +35,14 @@ class AppFlowCoordinator {
3535
DispatchQueue.main.async {
3636
AppDIContainer.shared.tokenStorage.clearAllTokens()
3737
UserDefaultsWrapper.shared.set(false, forKey: UserDefaultsWrapper.Key.hasSeenIntro.rawValue)
38-
self?.startApp()
38+
self?.startApp(launchType: .fast)
3939
}
4040
}
4141

42-
let splashCoordinator = container.makeSplashCoordinator(navigationController: navigationController)
42+
let splashCoordinator = container.makeSplashCoordinator(
43+
navigationController: navigationController,
44+
launchType: launchType
45+
)
4346
splashCoordinator.routerHandler = { [weak self] router in
4447
guard let self = self else { return }
4548
switch router {
@@ -114,7 +117,7 @@ class AppFlowCoordinator {
114117
mainCoordinator.withdrawFinish = { [weak self] in
115118
DispatchQueue.main.async {
116119
// 앱 데이터를 다 지웠으니, 스플래시부터 앱을 아예 새로 시작(리부팅)합니다!
117-
self?.startApp()
120+
self?.startApp(launchType: .fast)
118121
}
119122
}
120123

Atcha-iOS/App/DIContainer/AppCompositionRoot.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ extension AppCompositionRoot: SplashCoordinatorFactory,
5757
MainCoordinatorFactory,
5858
LockScreenCoordinatorFactory,
5959
IntroCoordinatorFactory {
60-
func makeSplashCoordinator(navigationController: UINavigationController) -> SplashCoordinator {
61-
return splashDIContainer.makeSplashCoordinator(navigationController: navigationController)
60+
func makeSplashCoordinator(navigationController: UINavigationController, launchType: LaunchType) -> SplashCoordinator {
61+
return splashDIContainer.makeSplashCoordinator(navigationController: navigationController, launchType: launchType)
6262
}
6363

6464
func makeLoginCoordinator(navigationController: UINavigationController) -> LoginCoordinator {

Atcha-iOS/App/DIContainer/CoordinatorFactory.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import Foundation
1313
/// to avoid service locator style lookups and to enable constructor injection of dependencies.
1414

1515
protocol SplashCoordinatorFactory {
16-
func makeSplashCoordinator(navigationController: UINavigationController) -> SplashCoordinator
16+
func makeSplashCoordinator(navigationController: UINavigationController, launchType: LaunchType) -> SplashCoordinator
1717
}
1818

1919
protocol LoginCoordinatorFactory {
@@ -45,8 +45,8 @@ extension AppDIContainer: SplashCoordinatorFactory,
4545
MainCoordinatorFactory,
4646
LockScreenCoordinatorFactory,
4747
IntroCoordinatorFactory {
48-
func makeSplashCoordinator(navigationController: UINavigationController) -> SplashCoordinator {
49-
return splashDIContainer.makeSplashCoordinator(navigationController: navigationController)
48+
func makeSplashCoordinator(navigationController: UINavigationController, launchType: LaunchType) -> SplashCoordinator {
49+
return splashDIContainer.makeSplashCoordinator(navigationController: navigationController, launchType: launchType)
5050
}
5151

5252
func makeLoginCoordinator(navigationController: UINavigationController) -> LoginCoordinator {

Atcha-iOS/App/DIContainer/Splash/SplashDIContainer.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,21 +32,23 @@ final class SplashDIContainer {
3232
return UpdateAppVersionUseCaseImpl(repository: repository)
3333
}
3434

35-
func makeSplashViewModel() -> SplashViewModel {
35+
func makeSplashViewModel(launchType: LaunchType) -> SplashViewModel {
3636
return SplashViewModel(
3737
fetchUserUseCase: makeFetchUserUseCase(),
3838
checkAppVersionUseCase: makeCheckAppVersionUseCase(),
3939
updateAppVersionUseCase: makeUpdateAppVersionUseCase(),
40-
tokenStorage: tokenStorage
40+
tokenStorage: tokenStorage,
41+
launchType: launchType
4142
)
4243
}
4344

4445
func makeSplashViewController(viewModel: SplashViewModel) -> SplashViewController {
4546
return SplashViewController(viewModel: viewModel)
4647
}
4748

48-
func makeSplashCoordinator(navigationController: UINavigationController) -> SplashCoordinator {
49+
func makeSplashCoordinator(navigationController: UINavigationController, launchType: LaunchType) -> SplashCoordinator {
4950
SplashCoordinator(navigationController: navigationController,
50-
diContainer: self)
51+
diContainer: self,
52+
launchType: launchType)
5153
}
5254
}

Atcha-iOS/App/SceneDelegate.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
1717
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
1818
guard let windowScene = (scene as? UIWindowScene) else { return }
1919
let window = UIWindow(windowScene: windowScene)
20+
var launchType: LaunchType = .main
2021

2122
appFlowCoordinator = AppFlowCoordinator(window: window, container: diContainer)
22-
appFlowCoordinator?.startApp()
23+
appFlowCoordinator?.startApp(launchType: launchType)
2324

2425
self.window = window
2526
}

Atcha-iOS/Base.lproj/LaunchScreen.storyboard

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23727" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
2+
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="24506" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
33
<device id="retina6_12" orientation="portrait" appearance="light"/>
44
<dependencies>
55
<deployment identifier="iOS"/>
6-
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23721"/>
6+
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24504"/>
77
<capability name="Named colors" minToolsVersion="9.0"/>
88
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
99
</dependencies>

Atcha-iOS/Core/Manager/Discord/DiscordWebhookManager.swift

Lines changed: 96 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ final class DiscordWebhookManager {
1111
static let shared = DiscordWebhookManager()
1212
private init() {}
1313

14-
private let webhookURLString = "https://discord.com/api/webhooks/1483870710018474066/qyzNBI1Bwr7J5tQDrPx2-mOcej_9yLSOk5Bmlmza2D-4nSWqvWgcMd4CZDziG4vkpKrm"
14+
private let errorWebhookURLString = AppConfig.errorWebhookURL
15+
private let authWebhookURLString = AppConfig.authWebhookURL
1516

17+
// MARK: - 오류 로그
1618
func sendErrorLog(
1719
baseURL: String,
1820
statusCode: Int,
@@ -24,14 +26,10 @@ final class DiscordWebhookManager {
2426
requestBody: [String: Any]? = nil,
2527
requestParameters: [String: Any]? = nil
2628
) {
27-
guard let url = URL(string: webhookURLString) else { return }
29+
guard let url = URL(string: errorWebhookURLString) else { return }
2830

29-
// Authorization 토큰 앞 30자만 노출
30-
let headersText = requestHeaders.map { key, value in
31-
return "\(key): \(value)"
32-
}.joined(separator: "\n")
31+
let headersText = requestHeaders.map { "\($0.key): \($0.value)" }.joined(separator: "\n")
3332

34-
// body JSON 변환
3533
let bodyText: String
3634
if let body = requestBody,
3735
let data = try? JSONSerialization.data(withJSONObject: body, options: .prettyPrinted),
@@ -54,22 +52,58 @@ final class DiscordWebhookManager {
5452
"content": "🚨 [Atcha-iOS] API 에러 발생!",
5553
"embeds": [[
5654
"title": "서버 에러 상세 보고",
57-
"color": 16711680,
55+
"color": 16711680, // 빨강
5856
"fields": [
59-
["name": "Base URL", "value": "`\(baseURL)`", "inline": false],
60-
["name": "Method & Path", "value": "`\(method) \(path)`", "inline": false],
61-
["name": "HTTP Status", "value": "\(statusCode)", "inline": true],
62-
["name": "responseCode", "value": responseCode, "inline": true],
63-
["name": "App Version", "value": AppInfoProvider.currentVersion, "inline": true],
64-
["name": "Error Message", "value": message, "inline": false],
65-
["name": "Request Headers", "value": "```\n\(headersText)\n```", "inline": false],
66-
["name": "Request Parameters", "value": paramsText, "inline": false],
67-
["name": "Request Body", "value": bodyText, "inline": false]
57+
["name": "Base URL", "value": "`\(baseURL)`", "inline": false],
58+
["name": "Method & Path", "value": "`\(method) \(path)`", "inline": false],
59+
["name": "HTTP Status", "value": "\(statusCode)", "inline": true],
60+
["name": "responseCode", "value": responseCode, "inline": true],
61+
["name": "App Version", "value": AppInfoProvider.currentVersion, "inline": true],
62+
["name": "Error Message", "value": message, "inline": false],
63+
["name": "Request Headers", "value": "```\n\(headersText)\n```", "inline": false],
64+
["name": "Request Parameters", "value": paramsText, "inline": false],
65+
["name": "Request Body", "value": bodyText, "inline": false]
6866
],
6967
"footer": ["text": "발생 시각: \(Date().kstString)"]
7068
]]
7169
]
7270

71+
sendToWebhook(url: url, payload: payload)
72+
}
73+
74+
// MARK: - 로그인/탈퇴 로그
75+
func sendAuthLog(event: AuthEvent, userID: String, provider: String? = nil, reason: String? = nil) {
76+
guard let url = URL(string: authWebhookURLString) else { return }
77+
78+
var fields: [[String: Any]] = [
79+
["name": "이벤트", "value": event.title, "inline": true],
80+
["name": "유저 ID", "value": "`\(userID)`", "inline": true],
81+
["name": "App Version", "value": AppInfoProvider.currentVersion, "inline": true]
82+
]
83+
84+
if let provider {
85+
fields.append(["name": "로그인 방식", "value": provider, "inline": true])
86+
}
87+
88+
if let reason {
89+
fields.append(["name": "탈퇴 사유", "value": reason, "inline": false])
90+
}
91+
92+
let payload: [String: Any] = [
93+
"content": event.headerMessage,
94+
"embeds": [[
95+
"title": event.embedTitle,
96+
"color": event.color,
97+
"fields": fields,
98+
"footer": ["text": "발생 시각: \(Date().kstString)"]
99+
]]
100+
]
101+
102+
sendToWebhook(url: url, payload: payload)
103+
}
104+
105+
// MARK: - 공통 전송
106+
private func sendToWebhook(url: URL, payload: [String: Any]) {
73107
var request = URLRequest(url: url)
74108
request.httpMethod = "POST"
75109
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
@@ -79,6 +113,51 @@ final class DiscordWebhookManager {
79113
}
80114
}
81115

116+
// MARK: - Auth Event 타입
117+
enum AuthEvent {
118+
case login
119+
case signup
120+
case logout
121+
case withdraw
122+
123+
var title: String {
124+
switch self {
125+
case .login: return "로그인"
126+
case .signup: return "회원가입"
127+
case .logout: return "로그아웃"
128+
case .withdraw: return "회원탈퇴"
129+
}
130+
}
131+
132+
var embedTitle: String {
133+
switch self {
134+
case .login: return "로그인 이벤트"
135+
case .signup: return "회원가입 이벤트"
136+
case .logout: return "로그아웃 이벤트"
137+
case .withdraw: return "회원탈퇴 이벤트"
138+
}
139+
}
140+
141+
var headerMessage: String {
142+
switch self {
143+
case .login: return "✅ [Atcha-iOS] 로그인"
144+
case .signup: return "🎉 [Atcha-iOS] 회원가입"
145+
case .logout: return "👋 [Atcha-iOS] 로그아웃"
146+
case .withdraw: return "❌ [Atcha-iOS] 회원탈퇴"
147+
}
148+
}
149+
150+
var color: Int {
151+
switch self {
152+
case .login: return 3066993 // 초록
153+
case .signup: return 5814783 // 파랑
154+
case .logout: return 16776960 // 노랑
155+
case .withdraw: return 10038562 // 보라
156+
}
157+
}
158+
}
159+
160+
// MARK: - Date Extension
82161
private extension Date {
83162
var kstString: String {
84163
let formatter = DateFormatter()

Atcha-iOS/Core/Network/Token/SessionController.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ final class SessionController {
2626
storage.clearRefreshToken()
2727
UserDefaultsWrapper.shared.removeAll()
2828
AppDIContainer.shared.locationStateHolder.clear()
29+
UserDefaultsWrapper.shared.set(true, forKey: UserDefaultsWrapper.Key.isGuest.rawValue)
30+
UserDefaultsWrapper.shared.set(false, forKey: UserDefaultsWrapper.Key.hasSeenIntro.rawValue)
2931

3032
// 로그인 화면으로
3133
DispatchQueue.main.async { [weak self] in

0 commit comments

Comments
 (0)