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

카카오 사용자를 애플, 구글 사용자로 전환하는 로직 개발 #605

Merged
merged 8 commits into from
Oct 4, 2024
5 changes: 5 additions & 0 deletions .github/workflows/pr-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ jobs:
mkdir -p src/main/resources/firebase
echo ${{ secrets.BACKEND_FIREBASE_JSON }} > src/main/resources/firebase/serviceAccountKey.json

- name: Apple Auth Key 파일 이동
run: |
mkdir -p src/main/resources/auth
printf "%s" "${{ secrets.APPLE_AUTH_KEY }}" > src/main/resources/auth/AuthKey.p8

- name: gradlew 권한 부여
run: chmod +x ./gradlew

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
import org.springframework.stereotype.Service;

import lombok.RequiredArgsConstructor;
import mouda.backend.auth.business.result.LoginProcessResult;
import mouda.backend.auth.implement.AppleOauthManager;
import mouda.backend.auth.implement.LoginManager;
import mouda.backend.auth.presentation.request.OauthRequest;
import mouda.backend.auth.presentation.request.LegacyOauthRequest;
import mouda.backend.auth.presentation.response.LoginResponse;
import mouda.backend.member.domain.Member;
import mouda.backend.member.domain.OauthType;
Expand All @@ -18,11 +19,14 @@ public class AppleAuthService implements AuthService {
private final LoginManager loginManager;

@Override
public LoginResponse oauthLogin(OauthRequest oauthRequest) {
public LoginResponse oauthLogin(LegacyOauthRequest oauthRequest) {
String socialLoginId = oauthManager.getSocialLoginId(oauthRequest.code());
String accessToken = loginManager.processSocialLogin(OauthType.APPLE, socialLoginId);

return new LoginResponse(accessToken);
if (oauthRequest.memberId() != null) {
String accessToken = loginManager.updateOauth(oauthRequest.memberId(), OauthType.APPLE, socialLoginId);
return new LoginResponse(accessToken);
Copy link
Contributor

Choose a reason for hiding this comment

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

서비스 단애서 정팩메 쓰는게 컨벤션이었지 않나용?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

넵 감사합니다 👍

}
LoginProcessResult loginProcessResult = loginManager.processSocialLogin(OauthType.APPLE, socialLoginId);
return new LoginResponse(loginProcessResult.accessToken());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package mouda.backend.auth.business;

import mouda.backend.auth.presentation.request.OauthRequest;
import mouda.backend.auth.presentation.request.LegacyOauthRequest;
import mouda.backend.auth.presentation.response.LoginResponse;
import mouda.backend.member.domain.Member;

public interface AuthService {

LoginResponse oauthLogin(OauthRequest oauthRequest);
LoginResponse oauthLogin(LegacyOauthRequest oauthRequest);

Member findMember(String token);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
import org.springframework.stereotype.Service;

import lombok.RequiredArgsConstructor;
import mouda.backend.auth.business.result.LoginProcessResult;
import mouda.backend.auth.implement.GoogleOauthManager;
import mouda.backend.auth.implement.LoginManager;
import mouda.backend.auth.presentation.request.GoogleOauthReqeust;
import mouda.backend.auth.presentation.request.GoogleOauthRequest;
import mouda.backend.auth.presentation.response.LoginResponse;
import mouda.backend.member.domain.Member;
import mouda.backend.member.domain.OauthType;
Expand All @@ -17,11 +18,16 @@ public class GoogleAuthService {
private final GoogleOauthManager googleOauthManager;
private final LoginManager loginManager;

public LoginResponse oauthLogin(GoogleOauthReqeust googleOauthReqeust) {
String memberName = googleOauthManager.getMemberName(googleOauthReqeust.idToken());
String socialLoginId = googleOauthManager.getSocialLoginId(googleOauthReqeust.idToken());
String accessToken = loginManager.processSocialLogin(OauthType.GOOGLE, socialLoginId);
return new LoginResponse(accessToken);
public LoginResponse oauthLogin(GoogleOauthRequest googleOauthRequest) {
String memberName = googleOauthManager.getMemberName(googleOauthRequest.idToken());
Copy link
Contributor

Choose a reason for hiding this comment

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

여기서 memberName은 어디서 사용되나요?
loginManager.processSocialLogin에서 회원가입도 진행하던데 그때 넣어줘야하는 건가요?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

이 부분은 아직 구현이 안된 부분이라서요 😅
사용자 이름을 원래 추가해야하는데 그 부분을 누락했네요. 다른 PR에서 반영하겠습니다!

String socialLoginId = googleOauthManager.getSocialLoginId(googleOauthRequest.idToken());
if (googleOauthRequest.memberId() != null) {
String accessToken = loginManager.updateOauth(googleOauthRequest.memberId(), OauthType.GOOGLE,
socialLoginId);
return new LoginResponse(accessToken);
}
LoginProcessResult loginProcessResult = loginManager.processSocialLogin(OauthType.GOOGLE, socialLoginId);
return new LoginResponse(loginProcessResult.accessToken());
}

public Member findMember(String token) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
import org.springframework.stereotype.Service;

import lombok.RequiredArgsConstructor;
import mouda.backend.auth.business.result.LoginProcessResult;
import mouda.backend.auth.implement.KakaoOauthManager;
import mouda.backend.auth.implement.LoginManager;
import mouda.backend.auth.implement.jwt.AccessTokenProvider;
import mouda.backend.auth.presentation.request.OauthRequest;
import mouda.backend.auth.presentation.response.KakaoLoginResponse;
import mouda.backend.auth.presentation.response.LoginResponse;
import mouda.backend.member.domain.LoginDetail;
import mouda.backend.member.domain.Member;
Expand All @@ -16,19 +18,19 @@

@Service
@RequiredArgsConstructor
public class KakaoAuthService implements AuthService {
public class KakaoAuthService {

private final AccessTokenProvider accessTokenProvider;
private final KakaoOauthManager oauthManager;
private final LoginManager loginManager;
private final MemberFinder memberFinder;
private final MemberWriter memberWriter;

public LoginResponse oauthLogin(OauthRequest oauthRequest) {
public KakaoLoginResponse oauthLogin(OauthRequest oauthRequest) {
String kakaoId = oauthManager.getSocialLoginId(oauthRequest.code());
String token = loginManager.processSocialLogin(OauthType.KAKAO, kakaoId);
LoginProcessResult loginProcessResult = loginManager.processSocialLogin(OauthType.KAKAO, kakaoId);

return new LoginResponse(token);
return new KakaoLoginResponse(loginProcessResult.memberId(), loginProcessResult.accessToken());
Copy link
Contributor

Choose a reason for hiding this comment

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

accessToken 필요없을 것 같아요!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

넵 우선 memberId 을 포함하여 사용자 전환 테스트하고 반영하겠습니다 ~!

}

public Member findMember(String token) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package mouda.backend.auth.business.result;

public record LoginProcessResult(
Long memberId,
String accessToken
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public enum AuthErrorMessage {
KAKAO_CONNECT_TIMEOUT("커넥션 타임아웃 되었습니다."),
DARAKBANG_NOT_ENTERED("가입한 다락방이 아닙니다."),
MEMBER_NOT_FOUND("회원가입 이력을 찾을 수 없습니다."),
;
KAKAO_CANNOT_SIGNUP("기존 카카오 로그인 이력이 있는 사용자만 이용할 수 있는 서비스입니다. 새로운 회원은 다른 로그인 서비스를 이용해주세요.");

private final String message;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,53 @@

import java.util.Optional;

import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import mouda.backend.auth.business.result.LoginProcessResult;
import mouda.backend.auth.exception.AuthErrorMessage;
import mouda.backend.auth.exception.AuthException;
import mouda.backend.auth.implement.jwt.AccessTokenProvider;
import mouda.backend.member.domain.LoginDetail;
import mouda.backend.member.domain.Member;
import mouda.backend.member.domain.OauthType;
import mouda.backend.member.implement.MemberFinder;
import mouda.backend.member.implement.MemberWriter;
import mouda.backend.member.infrastructure.MemberRepository;

@Component
@RequiredArgsConstructor
@Slf4j
public class LoginManager {

private final MemberRepository memberRepository;
private final AccessTokenProvider accessTokenProvider;
private final MemberWriter memberWriter;
private final MemberFinder memberFinder;

public String processSocialLogin(OauthType oauthType, String socialLoginId) {
public LoginProcessResult processSocialLogin(OauthType oauthType, String socialLoginId) {
Optional<Member> member = memberRepository.findByLoginDetail_SocialLoginId(socialLoginId);
log.error("1번");

if (member.isPresent()) {
return accessTokenProvider.provide(member.get());
return new LoginProcessResult(member.get().getId(), accessTokenProvider.provide(member.get()));
}

if (oauthType == OauthType.KAKAO) {
throw new AuthException(HttpStatus.BAD_REQUEST, AuthErrorMessage.KAKAO_CANNOT_SIGNUP);
}
Comment on lines +36 to 38
Copy link
Contributor

Choose a reason for hiding this comment

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

카카오 SignUp이 안되는 건 비즈니스 로직같은데 어떻게 생각하시나용??

Copy link
Contributor Author

Choose a reason for hiding this comment

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

네 비즈니스 로직이라는 거 공감해요 ~ 서비스 로직에 추가하고 싶은데 그럴려면 전반적으로 구조를 개선해야해서 시간이 좀 걸릴 것 같아요! 로그인 기능 구현 마무리 후 이슈 파서 진행하겠습니다 ㅎㅎ

log.error("2번");
Member newMember = Member.builder()
.nickname("nickname")
.loginDetail(new LoginDetail(oauthType, socialLoginId))
.build();
memberWriter.append(newMember);
log.error("3번");
return accessTokenProvider.provide(newMember);

return new LoginProcessResult(newMember.getId(), accessTokenProvider.provide(newMember));
}

public String updateOauth(long memberId, OauthType oauthType, String socialLoginId) {
Member member = memberFinder.find(memberId);
memberWriter.updateLoginDetail(memberId, oauthType, socialLoginId);

return accessTokenProvider.provide(member);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
import mouda.backend.auth.business.GoogleAuthService;
import mouda.backend.auth.business.KakaoAuthService;
import mouda.backend.auth.presentation.controller.swagger.AuthSwagger;
import mouda.backend.auth.presentation.request.GoogleOauthReqeust;
import mouda.backend.auth.presentation.request.GoogleOauthRequest;
import mouda.backend.auth.presentation.request.LegacyOauthRequest;
import mouda.backend.auth.presentation.request.OauthRequest;
import mouda.backend.auth.presentation.response.KakaoLoginResponse;
import mouda.backend.auth.presentation.response.LoginResponse;
import mouda.backend.common.response.RestResponse;

Expand All @@ -28,8 +30,8 @@ public class AuthController implements AuthSwagger {

@Override
@PostMapping("/kakao/oauth")
public ResponseEntity<RestResponse<LoginResponse>> loginKakaoOauth(@RequestBody OauthRequest oauthRequest) {
LoginResponse response = kakaoAuthService.oauthLogin(oauthRequest);
public ResponseEntity<RestResponse<KakaoLoginResponse>> loginKakaoOauth(@RequestBody OauthRequest oauthRequest) {
KakaoLoginResponse response = kakaoAuthService.oauthLogin(oauthRequest);

return ResponseEntity.ok().body(new RestResponse<>(response));
}
Expand All @@ -46,15 +48,15 @@ public ResponseEntity<RestResponse<LoginResponse>> loginBasicOauth() {
@Override
@PostMapping("/google/oauth")
public ResponseEntity<RestResponse<LoginResponse>> loginGoogleOauth(
@RequestBody GoogleOauthReqeust googleOauthReqeust) {
LoginResponse response = googleAuthService.oauthLogin(googleOauthReqeust);
@RequestBody GoogleOauthRequest googleOauthRequest) {
LoginResponse response = googleAuthService.oauthLogin(googleOauthRequest);

return ResponseEntity.ok().body(new RestResponse<>(response));
}

@Override
@PostMapping("/apple/oauth")
public ResponseEntity<RestResponse<LoginResponse>> loginAppleOauth(@RequestBody OauthRequest oauthRequest) {
public ResponseEntity<RestResponse<LoginResponse>> loginAppleOauth(@RequestBody LegacyOauthRequest oauthRequest) {
LoginResponse response = appleAuthService.oauthLogin(oauthRequest);

return ResponseEntity.ok().body(new RestResponse<>(response));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import mouda.backend.auth.presentation.request.GoogleOauthReqeust;
import mouda.backend.auth.presentation.request.GoogleOauthRequest;
import mouda.backend.auth.presentation.request.LegacyOauthRequest;
import mouda.backend.auth.presentation.request.OauthRequest;
import mouda.backend.auth.presentation.response.KakaoLoginResponse;
import mouda.backend.auth.presentation.response.LoginResponse;
import mouda.backend.common.response.RestResponse;

Expand All @@ -17,7 +19,7 @@ public interface AuthSwagger {
@ApiResponses({
@ApiResponse(responseCode = "200", description = "로그인 성공!"),
})
ResponseEntity<RestResponse<LoginResponse>> loginKakaoOauth(@RequestBody OauthRequest oauthRequest);
ResponseEntity<RestResponse<KakaoLoginResponse>> loginKakaoOauth(@RequestBody OauthRequest oauthRequest);

@Operation(summary = "테스트 용 로그인", description = "테스트 용 가짜 사용자로 로그인한다(accessToken 발급).")
@ApiResponses({
Expand All @@ -29,11 +31,11 @@ public interface AuthSwagger {
@ApiResponses({
@ApiResponse(responseCode = "200", description = "로그인 성공!"),
})
ResponseEntity<RestResponse<LoginResponse>> loginAppleOauth(@RequestBody OauthRequest oauthRequest);
ResponseEntity<RestResponse<LoginResponse>> loginAppleOauth(@RequestBody LegacyOauthRequest oauthRequest);

@Operation(summary = "구글 oauth 로그인", description = "구글 Oauth Code 를 사용하여 로그인한다(accessToken 발급).")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "로그인 성공!"),
})
ResponseEntity<RestResponse<LoginResponse>> loginGoogleOauth(@RequestBody GoogleOauthReqeust googleOauthReqeust);
ResponseEntity<RestResponse<LoginResponse>> loginGoogleOauth(@RequestBody GoogleOauthRequest googleOauthRequest);
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package mouda.backend.auth.presentation.request;

import jakarta.validation.constraints.NotNull;

public record GoogleOauthRequest(
Long memberId,

@NotNull
String idToken
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package mouda.backend.auth.presentation.request;

import jakarta.validation.constraints.NotNull;

public record LegacyOauthRequest(
Long memberId,

@NotNull
String code
) {
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package mouda.backend.auth.presentation.request;

import jakarta.validation.constraints.NotNull;

public record OauthRequest(
@NotNull
String code
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package mouda.backend.auth.presentation.response;

public record KakaoLoginResponse(
Long memberId,
String accessToken
) {
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package mouda.backend.auth.presentation.response;

public record LoginResponse(String accessToken) {
public record LoginResponse(
String accessToken
) {

}
17 changes: 17 additions & 0 deletions backend/src/main/java/mouda/backend/member/domain/LoginDetail.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package mouda.backend.member.domain;

import java.util.Objects;

import jakarta.persistence.Embeddable;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
Expand All @@ -21,4 +23,19 @@ public LoginDetail(OauthType oauthType, String socialLoginId) {
this.oauthType = oauthType;
this.socialLoginId = socialLoginId;
}

@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
LoginDetail that = (LoginDetail)o;
return oauthType == that.oauthType && Objects.equals(socialLoginId, that.socialLoginId);
}

@Override
public int hashCode() {
return Objects.hash(oauthType, socialLoginId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import lombok.RequiredArgsConstructor;
import mouda.backend.member.domain.Member;
import mouda.backend.member.domain.OauthType;
import mouda.backend.member.infrastructure.MemberRepository;

@Component
Expand All @@ -15,4 +16,8 @@ public class MemberWriter {
public Member append(Member member) {
return memberRepository.save(member);
}

public void updateLoginDetail(long memberId, OauthType oauthType, String socialLoginId) {
memberRepository.updateLoginDetail(memberId, oauthType, socialLoginId);
}
}
Loading