@@ -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
82161private extension Date {
83162 var kstString : String {
84163 let formatter = DateFormatter ( )
0 commit comments