Skip to content

Commit

Permalink
Merge pull request #64 from YAPP-Github/TNT-221-apiConnectionBase-use…
Browse files Browse the repository at this point in the history
…rApi

[TNT-221] 둜그인/νšŒμ›κ°€μž…/λ‘œκ·Έμ•„μ›ƒ/νšŒμ›νƒˆν‡΄ κ°„ 인증 정보 처리 둜직 μž‘μ„±
  • Loading branch information
FpRaArNkK authored Feb 10, 2025
2 parents a551f08 + d5872f9 commit 801341a
Show file tree
Hide file tree
Showing 20 changed files with 334 additions and 67 deletions.
9 changes: 9 additions & 0 deletions TnT/Projects/DIContainer/Sources/DIContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,17 @@ private enum SocialUseCaseKey: DependencyKey {
static let liveValue: SocialLoginUseCase = SocialLoginUseCase(socialLoginRepository: SocialLogInRepositoryImpl(loginManager: SNSLoginManager()))
}

private enum KeyChainManagerKey: DependencyKey {
static let liveValue: KeyChainManager = keyChainManager
}

// MARK: - DependencyValues
public extension DependencyValues {
var keyChainManager: KeyChainManager {
get { self[KeyChainManagerKey.self] }
set { self[KeyChainManagerKey.self] = newValue }
}

var userUseCase: UserUseCase {
get { self[UserUseCaseKey.self] }
set { self[UserUseCaseKey.self] = newValue }
Expand Down
14 changes: 8 additions & 6 deletions TnT/Projects/Data/Sources/LocalStorage/KeyChainManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

import Foundation

public let keyChainManager = KeyChainManager()

/// KeyChainManager
/// - 킀체인을 톡해 데이터λ₯Ό CRUD ν•˜κΈ° μœ„ν•œ μœ ν‹Έλ¦¬ν‹°μž…λ‹ˆλ‹€.
public struct KeyChainManager {
Expand All @@ -18,7 +20,7 @@ public struct KeyChainManager {
/// - value: μ €μž₯ν•  데이터 (Generic νƒ€μž…)
/// - key: Key μ—΄κ±°ν˜•μœΌλ‘œ μ •μ˜λœ ν‚€
/// - Throws: νƒ€μž… 뢈일치, 데이터 λ³€ν™˜ μ‹€νŒ¨, 킀체인 μ €μž₯ μ‹€νŒ¨ μ—λŸ¬
public static func save<T>(_ value: T, for key: Key) throws {
public func save<T>(_ value: T, for key: Key) throws {

guard type(of: value) == key.converter.type else {
throw KeyChainError.typeMismatch(
Expand Down Expand Up @@ -50,7 +52,7 @@ public struct KeyChainManager {
/// - Parameter key: Key μ—΄κ±°ν˜•μœΌλ‘œ μ •μ˜λœ ν‚€
/// - Returns: Generic νƒ€μž…μœΌλ‘œ λ³€ν™˜λœ κ°’ (데이터가 μ—†μœΌλ©΄ nil)
/// - Throws: 데이터 λ³€ν™˜ μ‹€νŒ¨, 읽기 μ‹€νŒ¨, νƒ€μž… 뢈일치 μ—λŸ¬
public static func read<T>(for key: Key) throws -> T? {
public func read<T>(for key: Key) throws -> T? {
let keyString: String = key.keyString

let query: [String: Any] = [
Expand Down Expand Up @@ -89,7 +91,7 @@ public struct KeyChainManager {
/// ν‚€μ²΄μΈμ—μ„œ 데이터λ₯Ό μ‚­μ œν•©λ‹ˆλ‹€.
/// - Parameter key: Key μ—΄κ±°ν˜•μœΌλ‘œ μ •μ˜λœ ν‚€
/// - Throws: μ‚­μ œ μ‹€νŒ¨ μ—λŸ¬
public static func delete(_ key: Key) throws {
public func delete(_ key: Key) throws {
let keyString: String = key.keyString

let query: [String: Any] = [
Expand All @@ -107,21 +109,21 @@ public struct KeyChainManager {
public extension KeyChainManager {
/// Key μ—΄κ±°ν˜•: 킀체인에 μ €μž₯ν•  데이터λ₯Ό μ •μ˜
enum Key {
case token
case sessionId
case userId

/// ν‚€ 고유 λ¬Έμžμ—΄
var keyString: String {
switch self {
case .token: return "com.TnT.token"
case .sessionId: return "com.TnT.sessionId"
case .userId: return "com.TnT.userId"
}
}

/// 각 데이터 별 νƒ€μž… & λ³€ν™˜ 둜직 μ •μ˜
var converter: KeyConverter {
switch self {
case .token: return KeyConverter(type: String.self, convert: { $0 })
case .sessionId: return KeyConverter(type: String.self, convert: { $0 })
case .userId: return KeyConverter(type: Int.self, convert: { Int($0) })
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,18 @@
//

import Foundation
import Dependencies

/// λ„€νŠΈμ›Œν¬ μš”μ²­μ— 인증 정보λ₯Ό μΆ”κ°€ν•˜λŠ” 인터셉터
struct AuthTokenInterceptor: Interceptor {
let priority: InterceptorPriority = .highest

func adapt(request: URLRequest) async throws -> URLRequest {
var request: URLRequest = request
guard let token: String = try KeyChainManager.read(for: .token) else {
guard let sessionId: String = try keyChainManager.read(for: .sessionId) else {
return request
}
request.setValue(token, forHTTPHeaderField: "Authorization")
request.setValue("SESSION-ID \(sessionId)", forHTTPHeaderField: "Authorization")
return request
}
}
5 changes: 5 additions & 0 deletions TnT/Projects/Data/Sources/Network/NetworkService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

import Foundation

import Domain

/// λ„€νŠΈμ›Œν¬ μš”μ²­μ„ μ²˜λ¦¬ν•˜λŠ” μ„œλΉ„μŠ€ 클래슀
public final class NetworkService {

Expand Down Expand Up @@ -85,6 +87,9 @@ private extension NetworkService {

/// JSON 데이터 λ””μ½”λ”©
func decodeData<T: Decodable>(_ data: Data, as type: T.Type) throws -> T {
// 빈 데이터인 경우 EmptyResponse νƒ€μž…μœΌλ‘œ 처리
if data.isEmpty, let emptyValue = EmptyResponse() as? T { return emptyValue }

do {
return try JSONDecoder(setting: .defaultSetting).decode(T.self, from: data)
} catch let decodingError as DecodingError {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ public struct UserRepositoryImpl: UserRepository {

public init() {}

/// 둜그인 μ„Έμ…˜ 유효 체크 μš”μ²­μ„ μˆ˜ν–‰
public func getSessionCheck() async throws -> GetSessionCheckResDTO {
return try await networkService.request(UserTargetType.getSessionCheck, decodingType: GetSessionCheckResDTO.self)
}

/// μ†Œμ…œ 둜그인 μš”μ²­μ„ μˆ˜ν–‰
public func postSocialLogin(_ reqDTO: PostSocialLoginReqDTO) async throws -> PostSocialLoginResDTO {
return try await networkService.request(
Expand All @@ -35,4 +40,14 @@ public struct UserRepositoryImpl: UserRepository {
decodingType: PostSignUpResDTO.self
)
}

/// λ‘œκ·Έμ•„μ›ƒ μš”μ²­μ„ μˆ˜ν–‰
public func postLogout() async throws -> PostLogoutResDTO {
return try await networkService.request(UserTargetType.postLogout, decodingType: PostLogoutResDTO.self)
}

/// νšŒμ›νƒˆν‡΄ μš”μ²­μ„ μˆ˜ν–‰
public func postWithdrawal() async throws -> PostWithdrawalResDTO {
return try await networkService.request(UserTargetType.postWithdrawal, decodingType: PostWithdrawalResDTO.self)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,16 @@ import Domain

/// μ‚¬μš©μž κ΄€λ ¨ API μš”μ²­ νƒ€μž… μ •μ˜
public enum UserTargetType {
/// 둜그인 μ„Έμ…˜ 유효 확인
case getSessionCheck
/// μ†Œμ…œ 둜그인 μš”μ²­
case postSocialLogin(reqDTO: PostSocialLoginReqDTO)
/// νšŒμ›κ°€μž… μš”μ²­
case postSignUp(reqDTO: PostSignUpReqDTO, imgData: Data?)
/// λ‘œκ·Έμ•„μ›ƒ μš”μ²­
case postLogout
/// νšŒμ› νƒˆν‡΄ μš”μ²­
case postWithdrawal
}

extension UserTargetType: TargetType {
Expand All @@ -25,23 +31,38 @@ extension UserTargetType: TargetType {

var path: String {
switch self {
case .getSessionCheck:
return "/check-session"

case .postSocialLogin:
return "/login"

case .postSignUp:
return "/members/sign-up"

case .postLogout:
return "/logout"

case .postWithdrawal:
return "/members/withdraw"
}
}

var method: HTTPMethod {
switch self {
case .postSocialLogin, .postSignUp:
case .getSessionCheck:
return .get

case .postSocialLogin, .postSignUp, .postLogout, .postWithdrawal:
return .post
}
}

var task: RequestTask {
switch self {
case .getSessionCheck, .postLogout, .postWithdrawal:
return .requestPlain

case .postSocialLogin(let reqDto):
return .requestJSONEncodable(encodable: reqDto)

Expand All @@ -59,6 +80,9 @@ extension UserTargetType: TargetType {

var headers: [String: String]? {
switch self {
case .getSessionCheck, .postLogout, .postWithdrawal:
return nil

case .postSocialLogin:
return ["Content-Type": "application/json"]

Expand All @@ -71,10 +95,20 @@ extension UserTargetType: TargetType {
}

var interceptors: [any Interceptor] {
return [
LoggingInterceptor(),
ResponseValidatorInterceptor(),
RetryInterceptor(maxRetryCount: 2)
]
switch self {
case .getSessionCheck, .postLogout, .postWithdrawal:
return [
LoggingInterceptor(),
AuthTokenInterceptor(),
ResponseValidatorInterceptor(),
RetryInterceptor(maxRetryCount: 2)
]
default:
return [
LoggingInterceptor(),
ResponseValidatorInterceptor(),
RetryInterceptor(maxRetryCount: 2)
]
}
}
}
14 changes: 14 additions & 0 deletions TnT/Projects/Domain/Sources/DTO/EmptyResponse.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// EmptyResponse.swift
// Data
//
// Created by λ°•λ―Όμ„œ on 2/9/25.
// Copyright Β© 2025 yapp25thTeamTnT. All rights reserved.
//

import Foundation

/// λΉ„μ–΄ μžˆλŠ” 응닡을 μ²˜λ¦¬ν•  빈 ꡬ쑰체
public struct EmptyResponse: Decodable {
public init() {}
}
13 changes: 13 additions & 0 deletions TnT/Projects/Domain/Sources/DTO/User/UserResponseDTO.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@

import Foundation

/// 둜그인 μ„Έμ…˜ 유효 확인 응닡 DTO
public struct GetSessionCheckResDTO: Decodable {
public let memberType: MemberTypeResDTO
}

/// μ†Œμ…œ 둜그인 응닡 DTO
public struct PostSocialLoginResDTO: Decodable {
/// μ„Έμ…˜ ID
Expand Down Expand Up @@ -49,3 +54,11 @@ public struct PostSignUpResDTO: Decodable {
/// ν”„λ‘œν•„ 이미지 URL
let profileImageUrl: String?
}

/// λ‘œκ·Έμ•„μ›ƒ 응닡 DTO
public struct PostLogoutResDTO: Decodable {
let sessionId: String
}

/// νšŒμ›νƒˆν‡΄ 응닡 DTO
public typealias PostWithdrawalResDTO = EmptyResponse
15 changes: 15 additions & 0 deletions TnT/Projects/Domain/Sources/Repository/UserRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ import Foundation
/// μ‚¬μš©μž κ΄€λ ¨ 데이터λ₯Ό κ΄€λ¦¬ν•˜λŠ” UserRepository ν”„λ‘œν† μ½œ
/// - μ‹€μ œ λ„€νŠΈμ›Œν¬ μš”μ²­μ€ 이 μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•œ `UserRepositoryImpl`μ—μ„œ μˆ˜ν–‰λ©λ‹ˆλ‹€.
public protocol UserRepository {
/// 둜그인 μ„Έμ…˜ 유효 확인
/// - Returns: μ„Έμ…˜ 유효 μ‹œ, 멀버 νƒ€μž… 정보λ₯Ό ν¬ν•¨ν•œ 응닡 DTO (`GetSessionCheckResDTO`)
/// - Throws: λ„€νŠΈμ›Œν¬ 였λ₯˜ λ˜λŠ” μ„œλ²„μ—μ„œ λ°˜ν™˜ν•œ 였λ₯˜λ₯Ό λ°œμƒμ‹œν‚¬ 수 있음
func getSessionCheck() async throws -> GetSessionCheckResDTO

/// μ†Œμ…œ 둜그인 μš”μ²­
/// - Parameter reqDTO: μ†Œμ…œ 둜그인 μš”μ²­μ— ν•„μš”ν•œ 데이터 (μ•‘μ„ΈμŠ€ 토큰 λ“±)
/// - Returns: 둜그인 성곡 μ‹œ, μ‚¬μš©μž 정보λ₯Ό ν¬ν•¨ν•œ 응닡 DTO (`PostSocialLoginResDTO`)
Expand All @@ -24,4 +29,14 @@ public protocol UserRepository {
/// - Returns: νšŒμ›κ°€μž… 성곡 μ‹œ, μ‚¬μš©μž 정보λ₯Ό ν¬ν•¨ν•œ 응닡 DTO (`PostSignUpResDTO`)
/// - Throws: λ„€νŠΈμ›Œν¬ 였λ₯˜ λ˜λŠ” μ„œλ²„μ—μ„œ λ°˜ν™˜ν•œ 였λ₯˜λ₯Ό λ°œμƒμ‹œν‚¬ 수 있음
func postSignUp(_ reqDTO: PostSignUpReqDTO, profileImage: Data?) async throws -> PostSignUpResDTO

/// λ‘œκ·Έμ•„μ›ƒ μš”μ²­
/// - Returns: λ‘œκ·Έμ•„μ›ƒ μ™„λ£Œμ‹œ SessionID ν¬ν•¨ν•œ 응닡 DTO (`PostLogoutResDTO`)
/// - Throws: λ„€νŠΈμ›Œν¬ 였λ₯˜ λ˜λŠ” μ„œλ²„μ—μ„œ λ°˜ν™˜ν•œ 였λ₯˜λ₯Ό λ°œμƒμ‹œν‚¬ 수 있음
func postLogout() async throws -> PostLogoutResDTO

/// νšŒμ›νƒˆν‡΄ μš”μ²­
/// - Returns: νšŒμ› νƒˆν‡΄ μ™„λ£Œ μ‹œ 응닡 DTO (`PostWithdrawalResDTO`)
/// - Throws: λ„€νŠΈμ›Œν¬ 였λ₯˜ λ˜λŠ” μ„œλ²„μ—μ„œ λ°˜ν™˜ν•œ 였λ₯˜λ₯Ό λ°œμƒμ‹œν‚¬ 수 있음
func postWithdrawal() async throws -> PostWithdrawalResDTO
}
13 changes: 12 additions & 1 deletion TnT/Projects/Domain/Sources/UseCase/UserUseCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,23 @@ public struct DefaultUserUseCase: UserRepository, UserUseCase {

// MARK: - Repository
extension DefaultUserUseCase {
public func getSessionCheck() async throws -> GetSessionCheckResDTO {
return try await userRepostiory.getSessionCheck()
}

public func postSocialLogin(_ reqDTO: PostSocialLoginReqDTO) async throws -> PostSocialLoginResDTO {
return try await userRepostiory.postSocialLogin(reqDTO)
}

public func postSignUp(_ reqDTO: PostSignUpReqDTO, profileImage: Data?) async throws -> PostSignUpResDTO {
return try await userRepostiory.postSignUp(reqDTO, profileImage: profileImage)
}

public func postLogout() async throws -> PostLogoutResDTO {
return try await userRepostiory.postLogout()
}

public func postWithdrawal() async throws -> PostWithdrawalResDTO {
return try await userRepostiory.postWithdrawal()
}
}

Loading

0 comments on commit 801341a

Please sign in to comment.