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 14 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.TokenServiceImpl;

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 TokenServiceImpl 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(AuthMember authMember);
void quitMember(AuthMember authMember);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
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.MemberServiceImpl;
import com.wypl.wyplcore.token.service.TokenServiceImpl;

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 TokenServiceImpl tokenService;
private final MemberServiceImpl 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(AuthMember authMember) {
deleteToken(authMember);
}

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

Choose a reason for hiding this comment

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

해당 메서드는 인증관련 서비스가 아닌 회원관련 서비스로 이동이 필요해 보입니다!

Copy link
Contributor Author

@sjhjack sjhjack Jan 31, 2025

Choose a reason for hiding this comment

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

해당 메서드는 deleteToken()을 비동기 처리하기로 했어서 별도의 Issue를 생성해서 수정할 생각이었습니다.
우선 현재 Issue에 맞게 해당 메서드를 회원 서비스로 이동시킨 후, 비동기 처리는 Merge 후에 수정하겠습니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

추가 코멘트 남깁니다!

해당 메서드를 MemberService로 옮기게 되면, 비동기 처리를 하더라도 TokenService에 의존성이 생기게 됩니다.
서비스간 의존 관계를 해소하기 위해 Facade Layer를 추가한 것인데, 그 존재 의의가 미약해집니다.
현재 AuthMemberFacadeImpl에서 TokenServiceMemberService를 주입받아 로직을 처리하고 있기 때문에 현재의 위치가 더 좋다는 생각이 듭니다.
그렇다고 똑같이 TokenServiceMemberService를 주입받는 MemberTokenFacadeImpl을 생성하는 것은 바람직하지 않다고 생각합니다.

의견 부탁드립니다 😄


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
@@ -1,21 +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 MemberServiceImpl {
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();
}
}
Loading
Loading