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

[Feature] - 카카오 로그인 구현 #55

Merged
merged 23 commits into from
Jul 19, 2024
Merged

[Feature] - 카카오 로그인 구현 #55

merged 23 commits into from
Jul 19, 2024

Conversation

slimsha2dy
Copy link

@slimsha2dy slimsha2dy commented Jul 18, 2024

✅ 작업 내용

  • kakao Oauth 로그인 구현
  • jwtToken 생성 구현

🙈 참고 사항

  • 앞으로 빌드 시, 옵션으로 jasypt 시크릿 키를 환경변수로 전달해 주어야 합니다. 안그러면 인수 테스트가 깨집니다.
    • 다음과 같이 빌드하시면 됩니다.
    • ./gradlew build -Pjasypt.encryptor.password={secret_key}

  • jasypt를 쓰면서 민감한 정보들이 yml에서 암호화된 형태로 관리됨
  • 해당 정보를 복호화해서 프로젝트에 적용하기 위해서는 노션에 기재된 secret-key를 형식에 맞게 환경 변수로 설정해야 함
  • 테스트, 로컬 어플리케이션 등 프로파일 환경마다 적용이 필요함
  • 도커에서 환경변수 지정 및 카카오 OAuth 관련 url을 dev에 맞게 프로파일 분리 필요함

@slimsha2dy slimsha2dy added the BE label Jul 18, 2024
@slimsha2dy slimsha2dy added this to the sprint 2 milestone Jul 18, 2024
@slimsha2dy slimsha2dy linked an issue Jul 18, 2024 that may be closed by this pull request
3 tasks
@slimsha2dy slimsha2dy requested a review from hangillee July 18, 2024 09:07
Copy link

github-actions bot commented Jul 18, 2024

Unit Test Results

0 tests   0 ✔️  0s ⏱️
0 suites  0 💤
0 files    0

Results for commit 46bcaac.

♻️ This comment has been updated with latest results.

Copy link

@eunjungL eunjungL left a 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)

Choose a reason for hiding this comment

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

이거 Inner class로 해둔 이유가 있나용? 궁금

Copy link

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}"
    }
}

카카오에서 내려주는 응답이 요렇게 생겼습니다.

매핑이 원활히 되도록 진행한 처리에요 👍

Choose a reason for hiding this comment

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

내부 클래스는 코드를 읽을 때 매끄럽게 읽히지가 않는 것 같아서 분리하는 걸 슬쩍 제안드려봅니다~ 실제로 코드 읽으면서도 이해가 안돼서 질문 남기게 되기도 했고요
DTO 내부에 DTO를 넣어두어도 직렬화가 잘되더라구요~!

Copy link

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(

Choose a reason for hiding this comment

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

Suggested change
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;
}

괄호 내려주면 다른 파일이랑 컨벤션이 더 잘 맞을 것 같습니다

Copy link

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;

Choose a reason for hiding this comment

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

여긴 왜 떨어트렸나용?

Copy link

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

Choose a reason for hiding this comment

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

생성자 로직이 많은 것 같아서 메서드로 빼면 어떨깝쇼

Copy link

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())

Choose a reason for hiding this comment

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

Suggested change
.header("Authorization", "Bearer " + kakaoAccessTokenResponse.accessToken())
.header(HttpHeaders.AUTHORIZATION, "Bearer " + kakaoAccessTokenResponse.accessToken())

이왕이면 spring에서 제공해주는 것을 활용해봅시다

Copy link

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<>();

Choose a reason for hiding this comment

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

MutliValueMap 사용하신 특별한 이유가 있나요? 지금 코드만 보면 key - value 한 쌍씩 이뤄지는 것 같아서 궁금해용

Copy link

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 을 이용해야 하는 것으로 알고 있습니다.
관련 링크 첨부드려요!

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

Choose a reason for hiding this comment

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

밖에서 쓸 일 없으면 private으로 막아줘도 좋을 것 같네용

Copy link

Choose a reason for hiding this comment

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

나중에 생길 것 같긴한데 접근 제어 레벨 개선해 두었습니다.

nak-honest and others added 2 commits July 18, 2024 20:08
- `build.gradle`에 테스트 시 jasypt secret key를 환경변수로 지정한다.
- `be-cd-dev.yml`와 `be-ci.yml`에서 -P 옵션으로 jasypt secret key를 환경변수로 지정한다.
Copy link

@hangillee hangillee left a comment

Choose a reason for hiding this comment

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

이미 클로버가 상세하게 리뷰해줘서 한 가지 코멘트 외에는 더 말씀드릴 게 없는 것 같습니다.

코멘트 확인만 부탁드려요!

Comment on lines 26 to 45
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();
}

Choose a reason for hiding this comment

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

- 생성자는 인자의 할당과 검증 로직만 수행한다.
    - 검증 로직은 메서드를 분리해 호출한다.
- 이외의 로직 수행이 필요한 경우 정적 팩토리 메서드 혹은 팩토리 클래스를 사용한다.

저희 코드 컨벤션에 따르면 생성자에서 필드 할당과 검증 외의 로직이 수행이 필요한 경우 메소드 분리, 정적 팩토리 메소드나 팩토리 클래스 구현 등의 방법을 사용하기로 했었습니다. 물론 필드 할당 과정에서 필요한 로직이어서 생성자 내부에 있는 것은 이해가 되나, 메소드 분리가 필요한 시점인 것 같습니다.

Copy link

Choose a reason for hiding this comment

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

반영 완!

@Libienz Libienz marked this pull request as draft July 19, 2024 05:01
@Libienz Libienz marked this pull request as ready for review July 19, 2024 06:57
@Libienz Libienz requested a review from eunjungL July 19, 2024 07:05
Copy link

@eunjungL eunjungL left a 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() {

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<>();

Choose a reason for hiding this comment

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

신기하네요

@Libienz Libienz merged commit 872557a into develop/be Jul 19, 2024
3 checks passed
@Libienz Libienz deleted the feature/be/#47 branch July 19, 2024 10:42
eunjungL pushed a commit that referenced this pull request Jul 30, 2024
* 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]>
hangillee pushed a commit to hangillee/2024-touroot that referenced this pull request Aug 20, 2024
* 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]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Feature] - 카카오 로그인 구현
5 participants