Skip to content

Commit

Permalink
[BE] refactor: Oauth2 네트워크 통신을 Transaction 범위 밖으로 분리 (#435) (#436)
Browse files Browse the repository at this point in the history
* feat: Oauth2 네트워크 통신 트랜잭션 범위 밖으로 분리

* refactor: accessToken 생성 로직 Transaction 범위 밖으로 분리

* refactor: @mock 어노테이션 활용

* refactor: Facade 패턴 적용

* refactor: DTO 정적 팩토리 메서드 정의

* refactor: LoginMemberDto에 Member객체가 아닌 필드를 담도록 수정
  • Loading branch information
xxeol2 authored Sep 11, 2023
1 parent ba9b4a9 commit f0c7e76
Show file tree
Hide file tree
Showing 10 changed files with 203 additions and 107 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.festago.auth.application;

import com.festago.auth.domain.AuthPayload;
import com.festago.auth.domain.AuthProvider;
import com.festago.auth.domain.OAuth2Client;
import com.festago.auth.domain.OAuth2Clients;
import com.festago.auth.domain.Role;
import com.festago.auth.domain.UserInfo;
import com.festago.auth.dto.LoginMemberDto;
import com.festago.auth.dto.LoginRequest;
import com.festago.auth.dto.LoginResponse;
import org.springframework.stereotype.Service;

@Service
public class AuthFacadeService {

private final AuthService authService;
private final OAuth2Clients oAuth2Clients;
private final AuthProvider authProvider;

public AuthFacadeService(AuthService authService, OAuth2Clients oAuth2Clients,
AuthProvider authProvider) {
this.authService = authService;
this.oAuth2Clients = oAuth2Clients;
this.authProvider = authProvider;
}

public LoginResponse login(LoginRequest request) {
LoginMemberDto loginMember = authService.login(getUserInfo(request));
String accessToken = getAccessToken(loginMember.memberId());
return LoginResponse.of(accessToken, loginMember);
}

private String getAccessToken(Long memberId) {
return authProvider.provide(new AuthPayload(memberId, Role.MEMBER));
}

private UserInfo getUserInfo(LoginRequest request) {
OAuth2Client oAuth2Client = oAuth2Clients.getClient(request.socialType());
return oAuth2Client.getUserInfo(request.accessToken());
}

public void deleteMember(Long memberId) {
authService.deleteMember(memberId);
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
package com.festago.auth.application;

import com.festago.auth.domain.AuthPayload;
import com.festago.auth.domain.AuthProvider;
import com.festago.auth.domain.OAuth2Client;
import com.festago.auth.domain.OAuth2Clients;
import com.festago.auth.domain.Role;
import com.festago.auth.domain.UserInfo;
import com.festago.auth.dto.LoginRequest;
import com.festago.auth.dto.LoginResponse;
import com.festago.auth.dto.LoginMemberDto;
import com.festago.domain.Member;
import com.festago.domain.MemberRepository;
import com.festago.exception.ErrorCode;
Expand All @@ -20,35 +14,20 @@
public class AuthService {

private final MemberRepository memberRepository;
private final OAuth2Clients oAuth2Clients;
private final AuthProvider authProvider;

public AuthService(MemberRepository memberRepository, OAuth2Clients oAuth2Clients,
AuthProvider authProvider) {
public AuthService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
this.oAuth2Clients = oAuth2Clients;
this.authProvider = authProvider;
}

public LoginResponse login(LoginRequest request) {
UserInfo userInfo = getUserInfo(request);
public LoginMemberDto login(UserInfo userInfo) {
return memberRepository.findBySocialIdAndSocialType(userInfo.socialId(), userInfo.socialType())
.map(member -> LoginResponse.isExists(getAccessToken(member), member.getNickname()))
.map(LoginMemberDto::isExists)
.orElseGet(() -> {
Member member = signUp(userInfo);
return LoginResponse.isNew(getAccessToken(member), member.getNickname());
return LoginMemberDto.isNew(member);
});
}

private UserInfo getUserInfo(LoginRequest request) {
OAuth2Client oAuth2Client = oAuth2Clients.getClient(request.socialType());
return oAuth2Client.getUserInfo(request.accessToken());
}

private String getAccessToken(Member member) {
return authProvider.provide(new AuthPayload(member.getId(), Role.MEMBER));
}

private Member signUp(UserInfo userInfo) {
return memberRepository.save(userInfo.toMember());
}
Expand Down
18 changes: 18 additions & 0 deletions backend/src/main/java/com/festago/auth/dto/LoginMemberDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.festago.auth.dto;

import com.festago.domain.Member;

public record LoginMemberDto(
boolean isNew,
Long memberId,
String nickname
) {

public static LoginMemberDto isNew(Member member) {
return new LoginMemberDto(true, member.getId(), member.getNickname());
}

public static LoginMemberDto isExists(Member member) {
return new LoginMemberDto(false, member.getId(), member.getNickname());
}
}
8 changes: 2 additions & 6 deletions backend/src/main/java/com/festago/auth/dto/LoginResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,7 @@ public record LoginResponse(
boolean isNew
) {

public static LoginResponse isNew(String accessToken, String nickname) {
return new LoginResponse(accessToken, nickname, true);
}

public static LoginResponse isExists(String accessToken, String nickname) {
return new LoginResponse(accessToken, nickname, false);
public static LoginResponse of(String accessToken, LoginMemberDto loginMember) {
return new LoginResponse(accessToken, loginMember.nickname(), loginMember.isNew());
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.festago.auth.presentation;

import com.festago.auth.annotation.Member;
import com.festago.auth.application.AuthService;
import com.festago.auth.application.AuthFacadeService;
import com.festago.auth.dto.LoginRequest;
import com.festago.auth.dto.LoginResponse;
import io.swagger.v3.oas.annotations.Operation;
Expand All @@ -19,16 +19,16 @@
@Tag(name = "로그인 관련 요청")
public class AuthController {

private final AuthService authService;
private final AuthFacadeService authFacadeService;

public AuthController(AuthService authService) {
this.authService = authService;
public AuthController(AuthFacadeService authFacadeService) {
this.authFacadeService = authFacadeService;
}

@PostMapping("/oauth2")
@Operation(description = "소셜 엑세스 토큰을 기반으로 로그인 요청을 보낸다.", summary = "OAuth2 로그인")
public ResponseEntity<LoginResponse> login(@RequestBody LoginRequest request) {
LoginResponse response = authService.login(request);
LoginResponse response = authFacadeService.login(request);
return ResponseEntity.ok()
.body(response);
}
Expand All @@ -37,7 +37,7 @@ public ResponseEntity<LoginResponse> login(@RequestBody LoginRequest request) {
@SecurityRequirement(name = "bearerAuth")
@Operation(description = "회원 탈퇴 요청을 보낸다.", summary = "유저 회원 탈퇴")
public ResponseEntity<Void> deleteMember(@Member Long memberId) {
authService.deleteMember(memberId);
authFacadeService.deleteMember(memberId);
return ResponseEntity.ok()
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
@DisplayNameGeneration(ReplaceUnderscores.class)
@SuppressWarnings("NonAsciiCharacters")
@ExtendWith(MockitoExtension.class)
class AdminAuthServiceTest {
class AdminAuthFacadeServiceTest {

@Mock
AuthProvider authProvider;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.festago.auth.application;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.mock;

import com.festago.auth.domain.AuthPayload;
import com.festago.auth.domain.AuthProvider;
import com.festago.auth.domain.OAuth2Clients;
import com.festago.auth.domain.SocialType;
import com.festago.auth.domain.UserInfo;
import com.festago.auth.dto.LoginMemberDto;
import com.festago.auth.dto.LoginRequest;
import com.festago.auth.dto.LoginResponse;
import com.festago.auth.infrastructure.FestagoOAuth2Client;
import com.festago.domain.Member;
import com.festago.support.MemberFixture;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayNameGeneration;
import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;

@DisplayNameGeneration(ReplaceUnderscores.class)
@SuppressWarnings("NonAsciiCharacters")
@ExtendWith(MockitoExtension.class)
class AuthFacadeServiceTest {

AuthFacadeService authFacadeService;

AuthService authService;

AuthProvider authProvider;

@BeforeEach
void setUp() {
authService = mock(AuthService.class);
OAuth2Clients oAuth2Clients = OAuth2Clients.builder()
.add(new FestagoOAuth2Client())
.build();
authProvider = mock(AuthProvider.class);

authFacadeService = new AuthFacadeService(authService, oAuth2Clients, authProvider);
}

@Test
void 로그인() {
LoginRequest request = new LoginRequest(SocialType.FESTAGO, "1");

Member member = MemberFixture.member()
.id(1L)
.build();

given(authProvider.provide(any(AuthPayload.class)))
.willReturn("Bearer token");

given(authService.login(any(UserInfo.class)))
.willReturn(new LoginMemberDto(false, member.getId(), member.getNickname()));

// when
LoginResponse response = authFacadeService.login(request);

// then
assertThat(response)
.isNotNull();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,89 +6,77 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.mock;

import com.festago.auth.domain.AuthPayload;
import com.festago.auth.domain.AuthProvider;
import com.festago.auth.domain.OAuth2Clients;
import com.festago.auth.domain.SocialType;
import com.festago.auth.dto.LoginRequest;
import com.festago.auth.dto.LoginResponse;
import com.festago.auth.infrastructure.FestagoOAuth2Client;
import com.festago.auth.domain.UserInfo;
import com.festago.auth.dto.LoginMemberDto;
import com.festago.domain.Member;
import com.festago.domain.MemberRepository;
import com.festago.exception.NotFoundException;
import com.festago.support.MemberFixture;
import java.util.Optional;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayNameGeneration;
import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

@DisplayNameGeneration(ReplaceUnderscores.class)
@SuppressWarnings("NonAsciiCharacters")
@ExtendWith(MockitoExtension.class)
class AuthServiceTest {

@Mock
MemberRepository memberRepository;

AuthProvider authProvider;

@InjectMocks
AuthService authService;

@BeforeEach
void setUp() {
memberRepository = mock(MemberRepository.class);
OAuth2Clients oAuth2Clients = OAuth2Clients.builder()
.add(new FestagoOAuth2Client())
.build();
authProvider = mock(AuthProvider.class);
authService = new AuthService(memberRepository, oAuth2Clients, authProvider);
}
@Nested
class 로그인 {

@Test
void 신규_회원으로_로그인() {
// given
Member member = MemberFixture.member()
.id(1L)
.build();
given(memberRepository.findBySocialIdAndSocialType(anyString(), any(SocialType.class)))
.willReturn(Optional.empty());
given(memberRepository.save(any(Member.class)))
.willReturn(member);
given(authProvider.provide(any(AuthPayload.class)))
.willReturn("Bearer token");
LoginRequest request = new LoginRequest(SocialType.FESTAGO, "1");

// when
LoginResponse response = authService.login(request);

// then
assertThat(response.isNew())
.isTrue();
}
@Test
void 신규_회원으로_로그인() {
// given
Member member = MemberFixture.member()
.id(1L)
.build();
given(memberRepository.findBySocialIdAndSocialType(anyString(), any(SocialType.class)))
.willReturn(Optional.empty());
given(memberRepository.save(any(Member.class)))
.willReturn(member);
UserInfo userInfo = new UserInfo(member.getSocialId(), member.getSocialType(), member.getNickname(),
member.getProfileImage());

// when
LoginMemberDto response = authService.login(userInfo);

// then
assertThat(response.isNew())
.isTrue();
}

@Test
void 기존_회원으로_로그인() {
// given
Member member = MemberFixture.member()
.id(1L)
.build();
given(memberRepository.findBySocialIdAndSocialType(anyString(), any(SocialType.class)))
.willReturn(Optional.of(member));
given(authProvider.provide(any(AuthPayload.class)))
.willReturn("Bearer token");
LoginRequest request = new LoginRequest(SocialType.FESTAGO, "1");

// when
LoginResponse response = authService.login(request);

// then
assertThat(response.isNew())
.isFalse();
@Test
void 기존_회원으로_로그인() {
// given
Member member = MemberFixture.member()
.id(1L)
.build();
given(memberRepository.findBySocialIdAndSocialType(anyString(), any(SocialType.class)))
.willReturn(Optional.of(member));
UserInfo userInfo = new UserInfo(member.getSocialId(), member.getSocialType(), member.getNickname(),
member.getProfileImage());

// when
LoginMemberDto response = authService.login(userInfo);

// then
assertThat(response.isNew())
.isFalse();
}
}

@Nested
Expand Down
Loading

0 comments on commit f0c7e76

Please sign in to comment.