-
Notifications
You must be signed in to change notification settings - Fork 5
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
[Feature] - 카카오 로그인 구현 #55
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.
보면서 생각나는거 몇개 쓱~ 달았습니당
return kakaoAccount.kakaoProfile.image; | ||
} | ||
|
||
@NoArgsConstructor(access = PRIVATE) |
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.
이거 Inner class로 해둔 이유가 있나용? 궁금
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.
HTTP/1.1 200 OK
{
"id":123456789,
"connected_at": "2022-04-11T01:45:28Z",
"kakao_account": {
// 프로필 또는 닉네임 동의항목 필요
"profile_nickname_needs_agreement ": false,
// 프로필 또는 프로필 사진 동의항목 필요
"profile_image_needs_agreement ": false,
"profile": {
// 프로필 또는 닉네임 동의항목 필요
"nickname": "홍길동",
// 프로필 또는 프로필 사진 동의항목 필요
"thumbnail_image_url": "http://yyy.kakao.com/.../img_110x110.jpg",
"profile_image_url": "http://yyy.kakao.com/dn/.../img_640x640.jpg",
"is_default_image":false,
"is_default_nickname": false
},
// 이름 동의항목 필요
"name_needs_agreement":false,
"name":"홍길동",
// 카카오계정(이메일) 동의항목 필요
"email_needs_agreement":false,
"is_email_valid": true,
"is_email_verified": true,
"email": "[email protected]",
// 연령대 동의항목 필요
"age_range_needs_agreement":false,
"age_range":"20~29",
// 출생 연도 동의항목 필요
"birthyear_needs_agreement": false,
"birthyear": "2002",
// 생일 동의항목 필요
"birthday_needs_agreement":false,
"birthday":"1130",
"birthday_type":"SOLAR",
// 성별 동의항목 필요
"gender_needs_agreement":false,
"gender":"female",
// 카카오계정(전화번호) 동의항목 필요
"phone_number_needs_agreement": false,
"phone_number": "+82 010-1234-5678",
// CI(연계정보) 동의항목 필요
"ci_needs_agreement": false,
"ci": "${CI}",
"ci_authenticated_at": "2019-03-11T11:25:22Z",
},
"properties":{
"${CUSTOM_PROPERTY_KEY}": "${CUSTOM_PROPERTY_VALUE}",
...
},
"for_partner": {
"uuid": "${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.
내부 클래스는 코드를 읽을 때 매끄럽게 읽히지가 않는 것 같아서 분리하는 걸 슬쩍 제안드려봅니다~ 실제로 코드 읽으면서도 이해가 안돼서 질문 남기게 되기도 했고요
DTO 내부에 DTO를 넣어두어도 직렬화가 잘되더라구요~!
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.
분리해보았습니다. 객체의 depth가 깊어지는 듯 해서 저는 개인적으로 별로인 듯 해요.
우선은 분리하지 않은 형태로 남겨두겠습니다.
cf1> 분리한 버젼을 여기에서 확인해보실 수 있는데 한 번 보시고 분리한 버젼이 더 괜찮아보이신다면 다시 말씀 주세요!
cf2> inner class를 inner record로 변경하였습니다. inner record 는 묵시적으로 정적 선언입니다.
private final String secretKey; | ||
private final long validityInMilliseconds; | ||
|
||
public JwtTokenProvider( |
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.
public JwtTokenProvider( | |
public JwtTokenProvider( | |
@Value("${security.jwt.token.secret-key}") String secretKey, | |
@Value("${security.jwt.token.expire-length}") long validityInMilliseconds | |
) { | |
this.secretKey = secretKey; | |
this.validityInMilliseconds = validityInMilliseconds; | |
} |
괄호 내려주면 다른 파일이랑 컨벤션이 더 잘 맞을 것 같습니다
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 final String accessTokenRequestUri; | ||
private final String restApiKey; | ||
private final String redirectUri; | ||
|
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.
원래 상수였다가 속성으로 지정되었었습니다.
현재 떨어트릴이유가 없어서 공백 개선 진행하였습니다.
@Value("${oauth.kakao.redirect-uri}") String redirectUri | ||
) { | ||
|
||
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.DEFAULTS |
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.
좋을것 같답쇼 분리했습니답쇼
|
||
return restClient.get() | ||
.uri(userInformationRequestUri) | ||
.header("Authorization", "Bearer " + kakaoAccessTokenResponse.accessToken()) |
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.
.header("Authorization", "Bearer " + kakaoAccessTokenResponse.accessToken()) | |
.header(HttpHeaders.AUTHORIZATION, "Bearer " + kakaoAccessTokenResponse.accessToken()) |
이왕이면 spring에서 제공해주는 것을 활용해봅시다
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 KakaoAccessTokenResponse requestAccessToken(String authorizationCode) { | ||
MultiValueMap<String, String> params = new LinkedMultiValueMap<>(); |
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.
MutliValueMap
사용하신 특별한 이유가 있나요? 지금 코드만 보면 key - value 한 쌍씩 이뤄지는 것 같아서 궁금해용
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.
In cases where our method parameter is of a type MultiValueMap, we can use either the @RequestParam or @RequestBody annotation to bind it appropriately with the body of the HTTP request. That’s because the Servlet API combines the query parameters and form data into a single map called parameters, and that includes automatic parsing of the request body:
html form 데이터가 안정적으로 body에 바인딩 되기 위해서는 Spring에서 util로 제공하는 MultivalueMap 을 이용해야 하는 것으로 알고 있습니다.
관련 링크 첨부드려요!
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.
신기하네요
import woowacourse.touroot.entity.BaseEntity; | ||
|
||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
@AllArgsConstructor |
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
으로 막아줘도 좋을 것 같네용
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.
나중에 생길 것 같긴한데 접근 제어 레벨 개선해 두었습니다.
- `build.gradle`에 테스트 시 jasypt secret key를 환경변수로 지정한다. - `be-cd-dev.yml`와 `be-ci.yml`에서 -P 옵션으로 jasypt secret key를 환경변수로 지정한다.
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.
이미 클로버가 상세하게 리뷰해줘서 한 가지 코멘트 외에는 더 말씀드릴 게 없는 것 같습니다.
코멘트 확인만 부탁드려요!
public KakaoOauthClient( | ||
@Value("${oauth.kakao.user-information-request-uri}") String userInformationRequestUri, | ||
@Value("${oauth.kakao.access-token-request-uri}") String accessTokenRequestUri, | ||
@Value("${oauth.kakao.rest-api-key}") String restApiKey, | ||
@Value("${oauth.kakao.redirect-uri}") String redirectUri | ||
) { | ||
|
||
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.DEFAULTS | ||
.withConnectTimeout(Duration.ofSeconds(1)) | ||
.withReadTimeout(Duration.ofSeconds(3)); | ||
ClientHttpRequestFactory requestFactory = ClientHttpRequestFactories.get(settings); | ||
|
||
this.userInformationRequestUri = userInformationRequestUri; | ||
this.accessTokenRequestUri = accessTokenRequestUri; | ||
this.restApiKey = restApiKey; | ||
this.redirectUri = redirectUri; | ||
this.restClient = RestClient.builder() | ||
.requestFactory(requestFactory) | ||
.build(); | ||
} |
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.
반영 완!
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.
확인했습니다! 수고하셨어용 👍 👍 LGTM
|
||
private RestClient buildRestClient() { |
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.
걍 의견) build말고 getRestClient
이런 네이밍이 더 직관적일 수도 있을 것 같네용?
} | ||
|
||
private KakaoAccessTokenResponse requestAccessToken(String authorizationCode) { | ||
MultiValueMap<String, String> params = new LinkedMultiValueMap<>(); |
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.
신기하네요
* feat: OAuth 요청에 대한 응답 DTO 작성 * chore: Jasypt 의존성 추가 * feat: 카카오 OAuth Client 객체 구현 * feat: OAuthProvider 구현 * feat: Login 서비스, 컨트롤러 구현 * feat: 소셜 로그인 회원가입 분기 흐름 구현 * feat: jwt를 통한 로그인 기능 구현 * chore: 디버깅 출력 문구 삭제 * fix: jasypt 시크릿 키를 github action에서 환경 변수로 지정 * fix: github action에서 빌드 시 jasypt secret key를 환경변수로 받도록 변경 - `build.gradle`에 테스트 시 jasypt secret key를 환경변수로 지정한다. - `be-cd-dev.yml`와 `be-ci.yml`에서 -P 옵션으로 jasypt secret key를 환경변수로 지정한다. * feat: 카카오 OAuth 로그인 redirect uri 프로파일별로 분리 * fix: 테스트 용 config yml 파일 분리 및 테스트에서 jasypt 제거 * chore: 로컬용 jwt 비밀키와 개발 서버용 키 분리 * chore: 데이터베이스 정보 관리 환경변수 방식에서 jasypt 방식으로 변경 * refactor: DTO inner 클래스 가독성 위해서 별도의 record로 분리 * refactor: 카카오 유저 정보 응답 DTO nested record로 개선 * style: 괄호 재배치, 공백 문자 가독성 개선 * refactor: RestClient 설정 기능 생성자에서 분리 개선 * refactor: 하드 코딩된 헤더 정보 미리 제공되는 상수로 변경 * refactor: 사용되지 않는 생성자 접근 제어 레벨 개선 * chore: github action에서 빌드 시 환경 변수를 지정하지 않도록 변경 --------- Co-authored-by: libienz <[email protected]> Co-authored-by: nhlee98 <[email protected]> Co-authored-by: 이낙헌 <[email protected]>
* feat: OAuth 요청에 대한 응답 DTO 작성 * chore: Jasypt 의존성 추가 * feat: 카카오 OAuth Client 객체 구현 * feat: OAuthProvider 구현 * feat: Login 서비스, 컨트롤러 구현 * feat: 소셜 로그인 회원가입 분기 흐름 구현 * feat: jwt를 통한 로그인 기능 구현 * chore: 디버깅 출력 문구 삭제 * fix: jasypt 시크릿 키를 github action에서 환경 변수로 지정 * fix: github action에서 빌드 시 jasypt secret key를 환경변수로 받도록 변경 - `build.gradle`에 테스트 시 jasypt secret key를 환경변수로 지정한다. - `be-cd-dev.yml`와 `be-ci.yml`에서 -P 옵션으로 jasypt secret key를 환경변수로 지정한다. * feat: 카카오 OAuth 로그인 redirect uri 프로파일별로 분리 * fix: 테스트 용 config yml 파일 분리 및 테스트에서 jasypt 제거 * chore: 로컬용 jwt 비밀키와 개발 서버용 키 분리 * chore: 데이터베이스 정보 관리 환경변수 방식에서 jasypt 방식으로 변경 * refactor: DTO inner 클래스 가독성 위해서 별도의 record로 분리 * refactor: 카카오 유저 정보 응답 DTO nested record로 개선 * style: 괄호 재배치, 공백 문자 가독성 개선 * refactor: RestClient 설정 기능 생성자에서 분리 개선 * refactor: 하드 코딩된 헤더 정보 미리 제공되는 상수로 변경 * refactor: 사용되지 않는 생성자 접근 제어 레벨 개선 * chore: github action에서 빌드 시 환경 변수를 지정하지 않도록 변경 --------- Co-authored-by: libienz <[email protected]> Co-authored-by: nhlee98 <[email protected]> Co-authored-by: 이낙헌 <[email protected]>
✅ 작업 내용
🙈 참고 사항
./gradlew build -Pjasypt.encryptor.password={secret_key}