로그인 코드 리팩토링 대장정

2025. 3. 19. 12:34·Spring

미뤄두고 미뤄두던 로그인 로직 리팩토링.. 더이상 미룰 수 없다고 생각해서 이번에 코드를 새롭게 갈아엎어보았다. 그 과정을 살펴보자!

기존 코드의 문제점 🤔

SOLID 원칙 위반

기존 코드의 가장 큰 문제점은 객체지향스럽지 못하다는 것이다.

위의 코드는 AuthService의 코드 중 일부로 외부 네트워크와 통신하여 사용자의 정보를 받아오고 또 탈퇴하는 코드이다. 인자인 platform은 사용자가 가입을 할 때, 클라이언트에서 어느 플랫폼으로 가입을 시도했는지 서버에게 보내준 enum 값이다. 이 코드의 문제점은 무엇일까? DIP와 OCP에 위배된다는 점이다. 만약, 기획의 요구사항의 변경으로 구글 로그인이 추가되었다면 어떨까? 구글 서버와 통신하기 위한 로직과 클래스를 생성해야하는 것은 물론이고, 이 AuthService의 코드까지 수정해주어야 한다. 이는 명백히 OCP원칙에 위배되는 코드이다. 해당 문제는 코드가 추상체에 의존하지 않고, 구현체에 의존하기 때문에 발생하기 때문에 발생하는 문제라고 생각하였다.

간략하게 나타낸 것이지만 위 사진처럼, kakaoOAuthProvider와 appleOAuthProvider 상위에 OAuthProvider 인터페이스를 만들고 각각을 구현체로 둔 후, AuthService에서는 해당 추상체(인터페이스)에 의존하도록 한다면, 후에 로그인 플랫폼이 추가되더라도 AuthService의 수정없이 요구사항을 반영할 수 있게 된다.

트랜잭션 내 외부 서버와의 통신

이에 대한 자세한 설명은 따로 글을 적어두었다.
한끼 로그인 코드의 문제점 <- 해당 글을 참고하자.


해결과정

인터페이스 생성

위에서 언급한대로 두 구현체 상위에 OAuthProvider 인터페이스를 만들어, 회원 정보를 받아오는 getSocialInfo메서드와 requestRevoke를 추상 메서드로 선언하였다.

그 후, 인터페이스를 주입시켜 모든 구현체들을 한꺼번에 빈으로 등록한 뒤 요청으로 들어오는 platform에서 빈 이름을 통해 구현체를 가져오도록 하는 방식으로 중개 클래스를 구현하였다.

짠! 코드가 훨씬 간단해졌다!
다만 한가지 문제점이 있었는데, 각 플랫폼마다 회원 정보를 받아올 때, 필요한 정보가 다르다는 것이다. 위 사진의 주석에도 적혀있듯이, oAuthProvider.requestRevoke메서드에서, 첫번째 파라미터인 code는 애플 로그인시에만 필요하고, 두번째 요소인 SerialId는 카카오 로그인 시에만 필요하다. 하지만 requestRevoke는 인터페이스의 추상 메서드이고 구현체들이 이를 오버라이드해서 구현하는 구조이기 때문에 2개의 인자 모두 파라미터 값으로 받아야하는 문제점이 존재했다. 이를 해결하기 위해, 파라미터 또한 인터페이스를 생성하여, 요청 플랫폼에 따라 전략 패턴을 적용해 요청 dto를 만들도록 할까 생각하였지만 현재 우리 프로젝트의 로그인 플랫폼은 단 2개 뿐이고, 이를 위해 클래스를 또 만드는 것은 너무 과하다는 생각이 들었다. 이 부분에 대해서는 후에 OAuth 요청 플랫폼이 증가하게 된다면 리팩할 예정이다.

트랜잭션 분리

이제 2번째 문제점이었던, 외부 서버와 통신하는 과정을 트랜잭션 범위에서 분리해보자. 외부 서버와 통신하는 과정을 다른 클래스로 분리해야 한다. 이를 구현하기 위해서 퍼사드 패턴을 도입하였다.

대충 흐름은 다음과 같다. 사용자의 요청이 들어오면 컨트롤러는 AuthFacade를 호출한다. AuthFacade에서는 OAuth2.0 기반의 사용자 인증을 수행한 후, AuthService의 메서드를 호출한다. 이때, AuthService의 메서드에는 트랜잭션을 적용하여 데이터의 일관성을 보장한다. 수정된 코드는 아래와 같다.

컨트롤러는 Facade에 접근하여 요청에 따른 메서드를 호출하고, Facade는 하위의 클래스들을 호출함으로써 내부호출 문제를 해결한 것이다.

마무리하며

이미 운영 중인 서비스이기도 하고, 로그인은 그 만큼 중요한 부분이기 때문에 리팩토링하기 무서웠다.. 유저수도 2000명이 넘고 멀쩡히 잘 돌아가고 있는데 괜히 고쳤다가 오류가 발생하면 어쩌지? 라는 생각도 있었고,,, 기존 코드 파일이 너무 많았기 때문에 사실 귀찮았던 것도 있다....ㅎ 하지만 걱정과 달리, 생각보다 리팩토링하는데 시간은 오래 걸리지 않았던 것 같다.
여전히 남들이 보기에 부족한 코드일지 모르지만 이렇게 계속 리팩하다보면 언젠가는 모두가 인정하는 클린코드가 되어 있지 않을까?

'Spring' 카테고리의 다른 글

이벤트 기반 처리로 구현하는 회원가입 및 이메일 전송 (feat. Thread pool)  (0) 2025.06.11
재시도 폭풍에서 서버를 지키는 방법 (feat. 동시성 문제)  (2) 2025.05.21
스프링 트랜잭션과 전파  (0) 2025.03.19
[Spring] 스프링 DB 2편 - 섹션 10~11  (0) 2025.03.19
[Spring] 스프링 DB 2편 - 섹션 9  (0) 2025.03.19
'Spring' 카테고리의 다른 글
  • 이벤트 기반 처리로 구현하는 회원가입 및 이메일 전송 (feat. Thread pool)
  • 재시도 폭풍에서 서버를 지키는 방법 (feat. 동시성 문제)
  • 스프링 트랜잭션과 전파
  • [Spring] 스프링 DB 2편 - 섹션 10~11
wing1008
wing1008
휘발을 막기 위한 기록
  • wing1008
    차곡차곡
    wing1008
  • 전체
    오늘
    어제
    • 분류 전체보기 (68)
      • Spring (26)
      • JPA (11)
      • JAVA (2)
      • 데이터베이스 (9)
      • 운영체제 (2)
      • 네트워크 (3)
      • 자료구조&알고리즘 (5)
      • AI (0)
      • Etc (5)
      • 끄적끄적 (5)
  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
wing1008
로그인 코드 리팩토링 대장정
상단으로

티스토리툴바