Skip to content

신규 AuthFeature를 구성합니다.#322

Merged
pinocchio22 merged 16 commits intodevfrom
feat/#316-new-authFeature
Apr 20, 2026
Merged

신규 AuthFeature를 구성합니다.#322
pinocchio22 merged 16 commits intodevfrom
feat/#316-new-authFeature

Conversation

@dongglehada
Copy link
Copy Markdown
Member

@dongglehada dongglehada commented Apr 13, 2026

📌 이슈

✅ 작업 사항

1. MLSAuthFeature SPM 패키지 생성

인증 관련 코드를 독립 SPM 패키지로 분리하고 4개의 타깃으로 구성했습니다.

MLSAuthFeatureInterface  ← 외부 공개 계약 (프로토콜, 엔티티, 에러)
MLSAuthFeature           ← Presentation + Domain + Data 구현체
MLSAuthFeatureTesting    ← Mock 객체 모음
MLSAuthFeatureTests      ← 단위 테스트

2. 아키텍처 경계 재정립 — DomainInterface 폴더 제거

초기 설계에서는 패키지 내부에 DomainInterface 폴더를 두어 Repository/UseCase 프로토콜을 관리했지만, Testing 모듈이 구현체(MLSAuthFeature)를 몰라도 Mock을 만들 수 있어야 한다는 원칙 때문에 별도 폴더가 아닌 MLSAuthFeatureInterface 타깃 자체로 통합했습니다.

결론: "외부 소비자가 알아야 하는 모든 것 = MLSAuthFeatureInterface"

MLSAuthFeatureInterface
├── Entities/        (Credential, LoginResponse, SignUpResponse, Job, LoginPlatform ...)
├── Errors/          (AuthError, TokenRepositoryError, TokenType)
├── Repositories/    (AuthAPIRepository, TokenRepository, UserDefaultsRepository)
├── UseCases/        (SocialLoginUseCase, SocialSignUpUseCase, CheckValidLevelUseCase ...)
├── Providers/       (SocialCredentialProvider)
└── Factories/       (LoginFactory, TermsAgreementFactory, OnBoardingInputFactory ...)

3. UseCase 통합 — Apple/Kakao 분리 구조 제거

플랫폼별로 분리된 UseCase를 LoginPlatform 파라미터로 분기하는 단일 UseCase로 통합했습니다.

// Before
AppleLoginUseCase.execute(credential:)
KakaoLoginUseCase.execute(credential:)
AppleSignUpUseCase.execute(credential:isMarketing:fcmToken:)
KakaoSignUpUseCase.execute(credential:isMarketing:fcmToken:)

// After
SocialLoginUseCase.execute(credential:platform:)
SocialSignUpUseCase.execute(credential:platform:isMarketingAgreement:fcmToken:)

4. 불필요한 UseCase 제거 — Repository 직접 호출로 전환

단순히 Repository 메서드를 한 번 감싸기만 하는 UseCase는 레이어를 늘릴 이유가 없다고 판단해 제거했습니다. 해당 Reactor에서 Repository를 직접 주입받아 호출합니다.

대표 예시: OnBoardingInputReactor의 직업 목록 조회

// Before — FetchJobListUseCase 경유
fetchJobListUseCase.execute()
    .map { .setJobList(jobList: $0.jobList) }

// After — Repository 직접 호출
authRepository.fetchJobList()
    .map { .setJobList(jobList: $0.jobList) }

5. CheckUseCase — 불필요한 Observable 래핑 제거

레벨 유효성 검사, 레벨·직업 공백 검사는 네트워크·I/O 없는 순수 동기 연산이므로 Observable로 감쌀 이유가 없었습니다.

// Before
func execute(level: Int?) -> Observable<Bool?>
func execute(level: Int?, job: String?) -> Observable<Bool>

// After
func execute(level: Int?) -> Bool?
func execute(level: Int?, job: String?) -> Bool

Reactor에서도 .map, .flatMap 체인 대신 결과를 바로 Observable.of(...)에 담는 방식으로 단순해졌습니다.

// Before
let validateLevel = checkValidLevelUseCase.execute(level: level)
    .map(Mutation.setLevelValid)
return .merge(changeLevel, validateJob, validateLevel)

// After
let isLevelValid = checkValidLevelUseCase.execute(level: level)
return .of(.setLevel(level), .setButtonEnabled(isButtonEnabled), .setLevelValid(isLevelValid))

6. Credential — Protocol → Struct 단순화

AppleCredential, KakaoCredential 두 구현체의 구조가 token, providerID 로 동일해 프로토콜이 불필요한 추상화였습니다.

// Before
protocol Credential { var token: String { get }; var providerID: String { get } }
struct AppleCredential: Credential { ... }
struct KakaoCredential: Credential { ... }

// After
struct Credential {
    let token: String
    let providerID: String
}

7. MLSAuthFeatureTesting — Mock 객체 모듈화

테스트 파일 내 private struct 형태로 흩어진 Mock들을 MLSAuthFeatureTesting 모듈로 이동해 재사용 가능하게 만들었습니다.

Mock 역할
MockAuthAPIRepository 카카오 로그인 → 미가입, 애플 로그인 → 기가입 반환
FCMFailingMockAuthAPIRepository FCM 등록만 실패, 나머지는 MockAuthAPIRepository에 위임
FailingMockTokenRepository 모든 토큰 연산 실패
MockTokenRepository / MockUserDefaultsRepository 정상 동작
MockSocialLoginUseCase / MockSocialSignUpUseCase Result 주입으로 성공/실패 제어
MockSocialCredentialProviders 목 자격증명 반환
Credential.mock / LoginResponse.registered/.unregistered 테스트용 정적 프로퍼티

8. 단위 테스트 작성

Repository 레이어 테스트는 작성하지 않고, 비즈니스 로직이 있는 곳에만 집중했습니다.

테스트 파일 주요 케이스
CheckUseCaseTests 유효 범위 / 범위 외 / nil 입력
SocialLoginUseCaseTests 플랫폼 저장, 토큰 저장 실패 에러 전파, FCM 실패 시 로그인 성공 유지
TermsAgreementReactorTests 전체동의 토글, 필수 항목 조합, bottomButton 활성화 조건
LoginReactorTests Apple/Kakao 라우팅, 기가입/미가입 분기, userNotFound, 에러 라우팅

@testable import로 internal 접근 — 테스트를 위해 접근제어를 변경하는 것은 설계 오염이라 판단했습니다.


9. BaseViewController — Loggable 프로토콜 적용

os_log 직접 호출 대신 MLSCoreLoggable 프로토콜을 채택해 init/deinit 로그를 일관되게 남기도록 수정했습니다.

open class BaseViewController: UIViewController, Loggable {
    public init() {
        super.init(nibName: nil, bundle: nil)
        logDebug("init \(String(describing: self))")
    }
    deinit { logDebug("deinit: \(String(describing: self))") }
}

구동 영상 (Mock 주입)

Simulator.Screen.Recording.-.iPhone.17.Pro.-.2026-04-13.at.21.59.07.mov

디렉토리 이미지

image

@dongglehada dongglehada requested a review from pinocchio22 April 13, 2026 13:00
@dongglehada dongglehada added feat 새로운 기능을 추가 refactor 프로덕션 코드 리팩토링, 파일 삭제, 네이밍 수정 및 폴더링 test 테스트 코드 추가 labels Apr 13, 2026
@dongglehada dongglehada self-assigned this Apr 13, 2026
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the MLSAuthFeature module, implementing a complete authentication system including social login, terms agreement, and onboarding flows. The changes cover data, domain, and presentation layers using ReactorKit, supported by unit tests and an example app. Feedback focuses on maintaining unidirectional data flow by removing side effects from Reactors, improving layout robustness for multi-window environments, and optimizing performance through the reuse of expensive objects like DateFormatter. Additionally, suggestions were made to enhance the reliability of window detection and social login timing logic.

@pinocchio22
Copy link
Copy Markdown
Contributor

전반적으로 SPM 패키지 분리가 잘 이루어진 것 같고 Interface 타깃에 Entity, Error, Repository, UseCase 등을 모아 의존성 방향을 명확히 한 점과 Testing 모듈이 구현체를 몰라도 Mock을 생성할 수 있도록 한 부분도 좋은 것 같습니다.
또한 외부 의존성이 없는 단순 검증 로직에서 Observable을 제거하고 동기 함수로 단순화한 부분 역시 Reactive 과사용을 줄여 코드 가독성과 이해도를 높여준 좋은 변경이라고 생각합니다 :)
제가 테스트쪽은 부족한 점이 많아서 Mock 객체들을 별도 MLSAuthFeatureTesting 모듈로 분리하여 재사용성과 테스트 구성 측면에서 이점을 챙긴 부분들은 코드들을 보면서 배워가도록 하겠습니다!!

다만 몇 가지 팀 차원에서 한 번 논의해보면 좋을 만한 내용이 생겼어요...
먼저 passthrough 역할의 UseCase를 제거하고 Reactor에서 Repository를 직접 호출하도록 변경한 부분은 저희가 생각한대로 코드가 단순해지는 장점이 있긴한데 또 레이어 측면에서 생각을 해보니 Presentation 레이어가 Data 레이어에 직접 의존하게 되는 구조가 되므로 레이어 경계를 모호해지는 경향이 있는 것 같아서 또 고민이 되네요 ㅠㅠ
또한 Credential을 protocol에서 struct로 단순화한 변경은 현재 요구사항에서는 충분히 합리적이긴한데 아마 제 기억상 각 플랫폼별 요구하는 id의 명칭이 달라서 정확한 값을 인지할 수 있도록 분리를 했던 것 같아서 이 부분은 한번 찾아보고 논의를 거치면 좋을 것 같습니다!

전체적으로는 모듈 경계가 명확해졌고 테스트 구조까지 잘 정리된 완성도 높은 리팩토링이라고 생각합니다.
이러한 구조를 기준으로 이후 다른 Feature모듈을 분리하면 좋을 것 같습니다!!

@dongglehada
Copy link
Copy Markdown
Member Author

@pinocchio22 먼저 변경사항이 많았음에도 빠르게 리뷰해주셔서 감사합니다!
논의하고 싶다고 말씀해주신 부분 중 몇 가지 제 의도를 전달드리고 싶어서 코멘트 남깁니다.

  1. UseCase 제거 후 Presentation → Repository 직접 호출
    Presentation 레이어가 Data 레이어를 직접 의존하게 된다는 우려로 이해했는데, 저는 조금 다르게 생각하고 있어요. 저희 구조상 모든 Repository는 (기존 Domain Interface, 현재는 Feature Interface에) 추상화되어 정의되어 있고, Presentation에서 의존하는 건 그 인터페이스이지 구현체가 아닙니다. DI를 통해 구현체를 주입받기 때문에 Data 레이어에 대한 직접 의존은 없고, DIP도 충분히 지켜지고 있다고 생각합니다.

  2. Credential Protocol → Struct 단순화
    인코딩 시 변수명이 다른 경우라면 추상화가 유효하다는 의견에는 동의합니다. 다만 현재 코드에서는 두 구현체의 형태가 동일해 보여서 우선 통합을 진행했습니다. 추후 다른 플랫폼 추가나 요구사항 변경이 생기면 그때 다시 추상화하는 방향도 충분히 가능하다고 생각해요.

Copy link
Copy Markdown
Contributor

@pinocchio22 pinocchio22 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제가 단단히 착각한 부분이 있었네요 ㅎㅎ;; 타입 추상화에 관련해서는 동일한 의견입니다!!

@pinocchio22 pinocchio22 merged commit 2ed3ed7 into dev Apr 20, 2026
@pinocchio22 pinocchio22 linked an issue Apr 20, 2026 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat 새로운 기능을 추가 refactor 프로덕션 코드 리팩토링, 파일 삭제, 네이밍 수정 및 폴더링 test 테스트 코드 추가

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AuthFeature를 구성합니다

2 participants