-
Notifications
You must be signed in to change notification settings - Fork 2
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
Feat/#425 카카오 소셜 로그인을 구현한다. #434
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
고생하셨습니다 아코쨩!^^ 소셜로그인의 달인이 되셨군요.
몇 가지 질문과 피드백 남겼습니다!
그리고 OAuthProvider 추상화는 아코가 고민하시는 걸 옆에서 봤고, 지금 제 지식으로는 더 좋은 방법이 떠오르지 않아서 피드백 남기지 않았습니다. (✿◠‿◠)
email과 카카오 고유 ID의 경우에는 UserIdentifier 같은 이름이면 좋을 것 같다는 생각이 들어요.
기존의 PK와는 혼동되지 않아야 할 것 같긴 하지만요 ^__^
@@ -16,19 +14,17 @@ | |||
public class AuthService { | |||
|
|||
private final MemberService memberService; | |||
private final GoogleInfoProvider googleInfoProvider; | |||
private final OAuthExecutionFinder oauthExecutionFinder; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이름이 ExecutionFinder 로 결정된 이유가 궁금해용 ㅎㅎ
Provider를 가져와주는 객체인 것 같아서 OAuthProviderFinder 로 지었을 수도 있었을 것 같아서요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
바론이 말해준 네이밍이 더 와닿는 것 같습니다. 저는 실행기를 찾는다에 집중해서 Execution이라는 네이밍을 이용했었습니다. 네이밍은 항상 어렵네요..😥
final String accessTokenResponse = oAuthInfoProvider.getAccessToken(authorizationCode); | ||
final String memberInfo = oAuthInfoProvider.getMemberInfo(accessTokenResponse); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getAccessToken 은 oauth로부터 받아온 accessToken을 반환하는 메서드고, getMemberInfo 는 해당 accessToken 으로부터 memberInfo 를 파싱해오는 것 같아요.
getAccessToken 메서드가 이 곳에서만 사용되고, accessToken을 oauth로부터 받는 것 ~ memberInfo를 만드는 것 까지 모두 oauthInfoProvider에서 이루어지는 것 같은데 메서드를 분리하신 이유가 있을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
일단 이 accessToken을 가져오는 것
과 memberInfo를 가져오는 것
을 분리한 이유는 일단 두 기능 모두 Oauth에서 핵심이라고 생각을 해서 분리를 했습니다. 그리고 accessToken을 가져오는 것
에 대한 메소드를 public으로 둔 이유는 AuthService에서 이 흐름을 명시해주어서 oauth의 흐름이 더 쉽게 파악할 수 있을 것 같다는 생각을 했었습니다. 또한 각각 기능 모두 핵심이다보니 테스트를 진행하기 위해서 Public으로 두고 테스트를 진행했던 이유도 있습니다.(물론 현재는 RestTemplate을 mock으로 두고 예외테스트만 진행하고 있습니다.)
return Objects.requireNonNull(response.getBody()).getEmail(); | ||
} catch (HttpClientErrorException e) { | ||
throw new OAuthException.InvalidAccessTokenException(); | ||
} catch (HttpServerErrorException e) { | ||
throw new OAuthException.GoogleServerException(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
requireNonNull
를 사용하면 검증하려는 파라미터가 null이면 NullPointerException이 발생한다 하는데, 이에 대한 예외 처리는 없어도 괜찮은가요??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
그 부분까지 고려하지 못했었네요. 일단 여기는 외부 api를 이용해서 요청을 보내고 응답을 받는 과정이기 때문에 null이 넘어오는 상황이 없다고 생각을 했습니다. 즉, null이 넘어온다는 것은 이미 서버 error가 발생한다라고 생각을 했고 이는 이미 try catch를 통해서 처리를 하고 있어서 이 부분을 고려하지 않았었네요 null requireNonNull
을 이용한 만큼 이 부분도 예외처리를 추가하겠습니다!
@@ -76,7 +78,7 @@ public GoogleAccessTokenResponse getAccessToken(final String authorizationCode) | |||
params, | |||
GoogleAccessTokenResponse.class); | |||
|
|||
return Objects.requireNonNull(response.getBody()); | |||
return Objects.requireNonNull(response.getBody()).getAccessToken(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
위 커멘트와 동일합니다! 😄
private static final String TOKEN_PREFIX = "Bearer "; | ||
private static final String GRANT_TYPE = "authorization_code"; | ||
private static final String AUTHORIZATION_HEADER = "Authorization"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GoogleInfoProvider가 가지는 상수와 똑같은 것 같아요! 둘을 통일하는 것은 어떨까요?
oauth에 따라 요구하는 grant type, token prefix 등이 달라질 수 있어서 분리해두신건가요??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
현재 이 부분은 googlelnfoProvider
와 kakaoInfoProvider
에서 모두 공통으로 이용되고 있는데 이 둘을 추상화한 OAuthInfoProvider에서 이용할 수 있게 하려면 public으로 두어야 하는 상황인데 이 부분에 대해서는 어떻게 처리하면 좋을까요?
|
||
public enum OAuthType { | ||
|
||
GOOGLE, KAKAO; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
// @GetMapping("/login/kakao") | ||
// public ResponseEntity<LoginResponse> kakaoLogin( | ||
// @RequestParam("code") final String authorizationCode, | ||
// final HttpServletResponse response | ||
// ) { | ||
// System.out.println(authorizationCode); | ||
// final TokenPair tokenPair = authService.kakaoLogin(authorizationCode); | ||
// final Cookie cookie = cookieProvider.createRefreshTokenCookie(tokenPair.getRefreshToken()); | ||
// response.addCookie(cookie); | ||
// final LoginResponse loginResponse = new LoginResponse(tokenPair.getAccessToken()); | ||
// return ResponseEntity.ok(loginResponse); | ||
// } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
w(゚Д゚)w ┌( ´_ゝ` )┐
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 부분 지웠어야 했는데 실수를 범했네요.. 몹시 부끄럽네요😥
final Member newMember = new Member(email, BASIC_NICKNAME); | ||
final Member savedMember = memberRepository.save(newMember); | ||
savedMember.updateNickname(savedMember.getNickname() + savedMember.getId()); | ||
return savedMember; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여기서 updateNickname을 기본 닉네임 + 고유 id 로 연결된 문자열 (ex. shook1203) 으로 구성되는 것이 맞나용?? 고유 id를 붙이는 이유가 사용자들의 닉네임이 중복되지 않게 하기 위한 것인가요?.?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
중복 닉네임을 막기 위해서라면 UUID 를 사용하면 되지 않을까요?!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
현재는 db의 id로 하는 것이 중복되는 가능성이 없어서 했는데 더 고민해볼께요
assertThat(result).isEqualTo(expect); | ||
} | ||
|
||
private static Stream<Arguments> requestOauthTypeAndResult() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
크게 변경점은 없어서 어프루브했습니다!
email 관련된 부분은 providerId
같은 컬럼으로 두고, 플랫폼을 저장하는 provider
열을 추가하는 건 어떨까요?
|
||
private static final String TOKEN_PREFIX = "Bearer "; | ||
private static final String GRANT_TYPE = "authorization_code"; | ||
private static final String AUTHORIZATION_HEADER = "Authorization"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
해당 상수는 HttpHeaders 에 정의되어 있는 상수인데, 재정의하지 않고 해당 상수를 쓰는 것에 대해서는 어떻게 생각하시나요??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AUTHORIZATION 헤더는 HttpHeaders에 정의된 상수를 이용했습니다 혹시 Bearer
과 authorization_code
도 정의 되어 있는 것인지 궁금합니다. 저는 찾아도 안나타나네요.
params.add("code", authorizationCode); | ||
|
||
HttpHeaders headers = new HttpHeaders(); | ||
headers.set(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded;charset=utf-8"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 부분도 상수를 사용해서 변경해볼 수 있을 것 같은데 어떤가요??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 부분도 상수로 빼겠습니다!
@JsonProperty("id") | ||
private Long id; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
필요한 필드가 스네이크 케이스로 되어 있지 않더라도 JsonProperty 를 붙여주어야 하나요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
불필요한 JsonProperty 제거하겠습니다!
final Member newMember = new Member(email, BASIC_NICKNAME); | ||
final Member savedMember = memberRepository.save(newMember); | ||
savedMember.updateNickname(savedMember.getNickname() + savedMember.getId()); | ||
return savedMember; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
중복 닉네임을 막기 위해서라면 UUID 를 사용하면 되지 않을까요?!
세부사항 - 소셜 로그인 과정 중에 accessToken을 가져오는 것과 사용자 정보를 조회하는 부분을 추상화 - authService는 각 infoProvider를 의존하는것이 아닌 OAuthInfoProvider를 의존하도록 수정
세부사항 - 예외 핸들링 원복 - 불필요한 로그 제거
세부사항 - 명확한 클래스 명칭으로 수정 - requireNonNull을 이용할 시 nullPointException 처리 - 상수 분리
6a131b1
to
e74e5cc
Compare
📝작업 내용
이번에 카카오 소셜 로그인을 구현하고 기존의 구글 소셜 로그인과 추상화하는 작업을 진행했습니다. 발급된 토큰을 저장하는 기능은 #424 가 merge되면 추가하겠습니다!. 그리고 카카오 소셜 로그인과 구글 소셜 로그인이 공존하면서 발생했던 문제점이 하나 있었습니다. 이 부분에 대해서 스플릿과 이야기를 나누었는데 베로와 바론의 의견도 듣고 싶어서 상황설명을 하겠습니다.
💬리뷰 참고사항
#️⃣연관된 이슈