Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor: findMemberIdAfterSaveMember 메서드와 관련된 로직과 도메인을 분리 #68

Merged
merged 18 commits into from
Feb 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions application/wypl-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ dependencies {
implementation project(':domain:jpa-member-domain')
implementation project(':domain:auth-domain')
implementation project(':domain:redis-embedded-domain')
implementation project(':domain:redis-token-domain')
implementation project(':common')

implementation 'org.springframework.boot:spring-boot-starter-actuator'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,22 @@
import com.wypl.googleoauthclient.annotation.Authenticated;
import com.wypl.googleoauthclient.domain.AuthMember;
import com.wypl.wyplcore.auth.data.response.AuthTokensResponse;
import com.wypl.wyplcore.auth.service.AuthServiceImpl;
import com.wypl.wyplcore.facade.AuthMemberFacade;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@RequestMapping("/auth")
@RestController
public class AuthController {
private final AuthServiceImpl authService;
private final AuthMemberFacade authMemberFacade;

@PostMapping("/v1/sign-in/{provider}")
public WyplResponseEntity<AuthTokensResponse> signIn(
@PathVariable("provider") String provider,
@RequestParam("code") String code
) {
AuthTokensResponse response = authService.generateToken(provider, code);
AuthTokensResponse response = authMemberFacade.generateToken(provider, code);
return WyplResponseEntity.ok(response, "로그인에 성공하였습니다.");
}

Expand All @@ -36,15 +36,15 @@ public WyplResponseEntity<AuthTokensResponse> reissue(
@RequestParam("access_token") String accessToken,
@RequestParam("refresh_token") String refreshToken
) {
AuthTokensResponse response = authService.reissueToken(accessToken, refreshToken);
AuthTokensResponse response = authMemberFacade.reissueToken(accessToken, refreshToken);
return WyplResponseEntity.created(response, "토큰 재발급에 성공하였습니다.");
}

@DeleteMapping("/v1/logout")
public WyplResponseEntity<Void> logout(
@Authenticated AuthMember authMember
) {
authService.logout(authMember);
authMemberFacade.logout(authMember);
return WyplResponseEntity.ok("로그아웃에 성공하였습니다.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import org.springframework.stereotype.Component;

import com.wypl.authdomain.auth.service.AuthDomainServiceImpl;
import com.wypl.googleoauthclient.GoogleOAuthClient;
import com.wypl.googleoauthclient.data.response.GoogleTokenValidationResponse;
import com.wypl.googleoauthclient.domain.AuthMember;
Expand All @@ -12,6 +11,7 @@
import com.wypl.jpamemberdomain.member.domain.SocialMember;
import com.wypl.jpamemberdomain.member.repository.SocialMemberRepository;
import com.wypl.jpamemberdomain.member.utils.SocialMemberRepositoryUtils;
import com.wypl.wyplcore.token.service.TokenService;

import lombok.RequiredArgsConstructor;

Expand All @@ -20,7 +20,7 @@
public class AuthMemberServiceImpl implements AuthMemberService {
private final GoogleOAuthClient googleOAuthClient;
private final SocialMemberRepository socialMemberRepository;
private final AuthDomainServiceImpl authDomainService;
private final TokenService tokenService;

@Override
public AuthMember getValidatedMemberId(String accessToken) {
Expand All @@ -37,7 +37,7 @@ private GoogleTokenValidationResponse getGoogleTokenValidationResponse(String ac
try {
return googleOAuthClient.validateToken(accessToken);
} catch (GoogleOAuthException e) {
if (authDomainService.checkExistsToken(accessToken)) {
if (tokenService.checkExistsToken(accessToken)) {
throw new GoogleOAuthException(GoogleOAuthErrorCode.REFRESH_TOKEN);
}
throw new GoogleOAuthException(GoogleOAuthErrorCode.INVALID_TOKEN);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,111 +1,14 @@
package com.wypl.wyplcore.auth.service;

import java.time.LocalDate;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.wypl.authdomain.auth.service.AuthDomainServiceImpl;
import com.wypl.googleoauthclient.GoogleOAuthClient;
import com.wypl.googleoauthclient.data.response.GoogleTokenResponse;
import com.wypl.googleoauthclient.data.response.GoogleUserInfoResponse;
import com.wypl.googleoauthclient.domain.AuthMember;
import com.wypl.googleoauthclient.exception.GoogleOAuthErrorCode;
import com.wypl.googleoauthclient.exception.GoogleOAuthException;
import com.wypl.jpamemberdomain.member.OauthProvider;
import com.wypl.jpamemberdomain.member.data.MemberSaveDto;
import com.wypl.jpamemberdomain.member.data.SocialMemberSaveDto;
import com.wypl.jpamemberdomain.member.repository.SocialMemberRepository;
import com.wypl.jpamemberdomain.member.utils.SocialMemberRepositoryUtils;
import com.wypl.wyplcore.auth.data.response.AuthTokensResponse;
import com.wypl.wyplcore.member.service.MemberServiceImpl;

import lombok.RequiredArgsConstructor;

@Transactional(readOnly = true)
@RequiredArgsConstructor
@Service
public class AuthServiceImpl {
private final GoogleOAuthClient googleOAuthClient;
private final SocialMemberRepository socialMemberRepository;
private final AuthDomainServiceImpl authDomainService;
private final MemberServiceImpl memberService;

@Transactional
public AuthTokensResponse generateToken(final String provider, final String code) {
GoogleTokenResponse googleTokenResponse = googleOAuthClient.fetchGoogleOAuthToken(code);

GoogleUserInfoResponse googleUserInfoResponse = googleOAuthClient.fetchUserInfo(
googleTokenResponse.accessToken());

long memberId = findMemberIdAfterSaveMember(googleTokenResponse.accessToken(), googleUserInfoResponse);

authDomainService.saveToken(googleTokenResponse.accessToken(), googleTokenResponse.refreshToken());

return AuthTokensResponse.of(memberId, googleTokenResponse);
}

@Transactional
public AuthTokensResponse reissueToken(final String accessToken, final String refreshToken) {
if (isInvalidRefreshToken(accessToken, refreshToken)) {
throw new GoogleOAuthException(GoogleOAuthErrorCode.NOT_AUTHORIZATION_MEMBER);
}

GoogleTokenResponse googleTokenResponse = googleOAuthClient.fetchRefreshGoogleOAuthToken(refreshToken);

authDomainService.deleteToken(accessToken);
authDomainService.saveToken(googleTokenResponse.accessToken(), refreshToken);

return AuthTokensResponse.of(googleTokenResponse.accessToken(), refreshToken);
}

@Transactional
public void logout(AuthMember authMember) {
deleteToken(authMember);
}

@Transactional
public void quitMember(AuthMember authMember) {
// Todo : 회원 탈퇴 로직 논의
deleteToken(authMember);
memberService.deleteMember(authMember);
}

private void deleteToken(AuthMember authMember) {
authDomainService.deleteToken(authMember.accessToken());
}

private boolean isInvalidRefreshToken(String accessToken, String refreshToken) {
return refreshToken.isEmpty() || !refreshToken.equals(authDomainService.getRefreshToken(accessToken));
}

// todo:
private long findMemberIdAfterSaveMember(String accessToken, GoogleUserInfoResponse googleUserInfoResponse) {
if (isNewMember(googleUserInfoResponse)) {
LocalDate birthday = googleOAuthClient.fetchBirthday(accessToken);

MemberSaveDto memberSaveDto = MemberSaveDto.builder()
.email(googleUserInfoResponse.email())
.birthday(birthday)
.nickname(googleUserInfoResponse.name())
.profileImage(googleUserInfoResponse.picture())
.build();

SocialMemberSaveDto socialMemberSaveDto = SocialMemberSaveDto.builder()
.oauthProvider(OauthProvider.GOOGLE)
.oauthId(googleUserInfoResponse.id())
.build();

return authDomainService.saveAuthData(memberSaveDto, socialMemberSaveDto);
}

return SocialMemberRepositoryUtils.getSocialMember(socialMemberRepository, googleUserInfoResponse.id()).getId();
}

// todo:
private boolean isNewMember(GoogleUserInfoResponse googleUserInfoResponse) {
return !socialMemberRepository.existsByOauthProviderAndOauthId(OauthProvider.GOOGLE,
googleUserInfoResponse.id());
}
}

// package com.wypl.wyplcore.auth.service;
//
// import org.springframework.stereotype.Service;
// import org.springframework.transaction.annotation.Transactional;
//
// import lombok.RequiredArgsConstructor;
//
// @Transactional(readOnly = true)
// @RequiredArgsConstructor
// @Service
// public class AuthServiceImpl {
//
// }
//
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.wypl.wyplcore.facade;

import com.wypl.googleoauthclient.domain.AuthMember;
import com.wypl.wyplcore.auth.data.response.AuthTokensResponse;

public interface AuthMemberFacade {
Copy link
Member

Choose a reason for hiding this comment

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

개인적으로 뒤에 Service를 붙여주면 좋을 것 같아요!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Facade는 필요한 Service를 조합해서 사용하기 때문에 Facade Layer와 Service Layer를 명확히 구분하기 위해서 Service는 붙이지 않았습니다!
이 부분에 대해서는 어떻게 생각하시나요?

AuthTokensResponse generateToken(final String provider, final String code);
AuthTokensResponse reissueToken(final String accessToken, final String refreshToken);
void logout(final AuthMember authMember);
void quitMember(final AuthMember authMember);
Copy link
Member

Choose a reason for hiding this comment

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

quitMember은 인증 관련 서비스가 아닌 회원 관련 서비스로 옮겨야 할 것 같습니다!

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package com.wypl.wyplcore.facade;

import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.wypl.googleoauthclient.GoogleOAuthClient;
import com.wypl.googleoauthclient.data.response.GoogleTokenResponse;
import com.wypl.googleoauthclient.data.response.GoogleUserInfoResponse;
import com.wypl.googleoauthclient.domain.AuthMember;
import com.wypl.googleoauthclient.exception.GoogleOAuthErrorCode;
import com.wypl.googleoauthclient.exception.GoogleOAuthException;
import com.wypl.wyplcore.auth.data.response.AuthTokensResponse;
import com.wypl.wyplcore.member.service.MemberService;
import com.wypl.wyplcore.token.service.TokenService;

import lombok.RequiredArgsConstructor;

@Transactional(readOnly = true)
@RequiredArgsConstructor
@Component
public class AuthMemberFacadeImpl implements AuthMemberFacade {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
public class AuthMemberFacadeImpl implements AuthMemberFacade {
public class AuthMemberFacadeService implements AuthMemberService {

어떠한 기능을 하는 서비스가 있는데 해당 서비스를 구성한 디자인패턴과 같은.. 내용을 클래스에 붙힘

private final GoogleOAuthClient googleOAuthClient;
private final TokenService tokenService;
private final MemberService memberService;

@Override
@Transactional
public AuthTokensResponse generateToken(final String provider, final String code) {
GoogleTokenResponse googleTokenResponse = googleOAuthClient.fetchGoogleOAuthToken(code);

GoogleUserInfoResponse googleUserInfoResponse = googleOAuthClient.fetchUserInfo(
googleTokenResponse.accessToken());

long memberId = memberService.findMemberIdOrSaveMember(googleTokenResponse.accessToken(), googleUserInfoResponse);

tokenService.saveToken(googleTokenResponse.accessToken(), googleTokenResponse.refreshToken());

return AuthTokensResponse.of(memberId, googleTokenResponse);
}

@Override
@Transactional
public AuthTokensResponse reissueToken(final String accessToken, final String refreshToken) {
if (isInvalidRefreshToken(accessToken, refreshToken)) {
throw new GoogleOAuthException(GoogleOAuthErrorCode.NOT_AUTHORIZATION_MEMBER);
}

GoogleTokenResponse googleTokenResponse = googleOAuthClient.fetchRefreshGoogleOAuthToken(refreshToken);

tokenService.deleteToken(accessToken);
tokenService.saveToken(googleTokenResponse.accessToken(), refreshToken);

return AuthTokensResponse.of(googleTokenResponse.accessToken(), refreshToken);
}

@Override
@Transactional
public void logout(final AuthMember authMember) {
deleteToken(authMember);
}

@Override
@Transactional
public void quitMember(final AuthMember authMember) {
// Todo : 회원 탈퇴 로직 논의
// Todo : deleteToken은 스프링에서 비동기 처리하고(토큰이 아니라 멤버 탈퇴가 중요한거다), deleteMember는 Member 쪽으로 옮기자
deleteToken(authMember);
memberService.deleteMember(authMember);
}

private void deleteToken(AuthMember authMember) {
tokenService.deleteToken(authMember.accessToken());
}

private boolean isInvalidRefreshToken(String accessToken, String refreshToken) {
return refreshToken.isEmpty() || !refreshToken.equals(tokenService.getRefreshToken(accessToken));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.wypl.wyplcore.member.service;

import java.time.LocalDate;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.wypl.googleoauthclient.GoogleOAuthClient;
import com.wypl.googleoauthclient.data.response.GoogleUserInfoResponse;
import com.wypl.googleoauthclient.domain.AuthMember;
import com.wypl.jpamemberdomain.member.OauthProvider;
import com.wypl.jpamemberdomain.member.data.MemberSaveDto;
import com.wypl.jpamemberdomain.member.data.SocialMemberSaveDto;
import com.wypl.jpamemberdomain.member.domain.Member;
import com.wypl.jpamemberdomain.member.domain.SocialMember;
import com.wypl.jpamemberdomain.member.repository.MemberRepository;
import com.wypl.jpamemberdomain.member.repository.SocialMemberRepository;
import com.wypl.jpamemberdomain.member.utils.SocialMemberRepositoryUtils;

import lombok.RequiredArgsConstructor;

@Transactional(readOnly = true)
@RequiredArgsConstructor
@Service
public class MemberService {
private final GoogleOAuthClient googleOAuthClient;
private final MemberRepository memberRepository;
private final SocialMemberRepository socialMemberRepository;

@Transactional
public void deleteMember(AuthMember authMember) {
memberRepository.deleteById(authMember.id());
}

@Transactional
public long findMemberIdOrSaveMember(String accessToken, GoogleUserInfoResponse googleUserInfoResponse) {
if (isNewMember(googleUserInfoResponse)) {
LocalDate birthday = googleOAuthClient.fetchBirthday(accessToken);

MemberSaveDto memberSaveDto = MemberSaveDto.builder()
.email(googleUserInfoResponse.email())
.birthday(birthday)
.nickname(googleUserInfoResponse.name())
.profileImage(googleUserInfoResponse.picture())
.build();

SocialMemberSaveDto socialMemberSaveDto = SocialMemberSaveDto.builder()
.oauthProvider(OauthProvider.GOOGLE)
.oauthId(googleUserInfoResponse.id())
.build();

return saveNewMember(memberSaveDto, socialMemberSaveDto);
}

return SocialMemberRepositoryUtils.getSocialMember(socialMemberRepository, googleUserInfoResponse.id()).getId();
}

private boolean isNewMember(GoogleUserInfoResponse googleUserInfoResponse) {
return !socialMemberRepository.existsByOauthProviderAndOauthId(OauthProvider.GOOGLE,
googleUserInfoResponse.id());
}

private long saveNewMember(MemberSaveDto memberSaveDto, SocialMemberSaveDto socialMemberSaveDto) {
Member newMember = memberRepository.save(Member.of(memberSaveDto));
SocialMember socialMember = socialMemberRepository.save(SocialMember.of(newMember, socialMemberSaveDto));
return socialMember.getId();
}
}

This file was deleted.

Loading