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

회원가입 및 로그인 & 인가 기능 구현 #82

Merged
merged 32 commits into from
Jul 20, 2023
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
076d96d
feat: (#29) 카카오 OAuth를 통해 정보를 받아서 가입하는 기능 구현
jeomxon Jul 14, 2023
f3ac5d1
chore: (#29) OAuth API요청에 대한 환경변수 설정
jeomxon Jul 14, 2023
0b5e2ed
feat: (#29) Member의 랜덤 닉네임을 지정하기 위한 수 생성기 구현
jeomxon Jul 14, 2023
aa19fd6
chore: (#29) JJWT라이브러리 의존성 추가
jeomxon Jul 16, 2023
0335db6
feat: (#29) 로그인한 회원에 대한 정보를 JWT로 반환하는 기능 추가
jeomxon Jul 16, 2023
6391141
chore: (#29) 토큰 관련 환경변수 설정 추가
jeomxon Jul 16, 2023
147bad7
refactor: (#29) ResponseDto를 record형식으로 변환
jeomxon Jul 16, 2023
149f1ed
feat: (#29) 인증정보를 확인하는 AuthenticationFilter구현
jeomxon Jul 17, 2023
b4015f7
feat: (#29) 멤버가 존재하는지 확인한 후 반환하는 ArgumentResolver구현
jeomxon Jul 18, 2023
b9f6d3e
test: (#29) loginWithKakao메서드에 대한 컨트롤러 단위 테스트 작성
jeomxon Jul 18, 2023
326f668
refactor: (#29) Member엔티티 필드명 수정 및 추가
jeomxon Jul 18, 2023
37b54f0
test: (#29) Member 등록에 대한 검증 추가
jeomxon Jul 18, 2023
861a836
chore: (#29) test를 위한 yaml파일을 추가하여 환경 분리
jeomxon Jul 19, 2023
7f6a981
Merge branch 'dev' into feat/#29
jeomxon Jul 19, 2023
c856828
refactor: (#29) conflict 해결
jeomxon Jul 19, 2023
435348b
refactor: (#29) Controller Swagger를 위한 어노테이션 추가
jeomxon Jul 19, 2023
fd4730e
feat: (#29) CORS설정 및 ArgumentResolver등록
jeomxon Jul 19, 2023
9361516
chore: (#29) test환경 환경변수만 존재하도록 수정
jeomxon Jul 19, 2023
45ef172
refactor: (#29) 네이밍, 상수화, 위치변경 등의 작업 수행
jeomxon Jul 19, 2023
3f4a538
fix: (#29) 멤버의 이름에 포함되는 숫자가 고정되는 문제 해결
jeomxon Jul 19, 2023
b26ea29
refactor: (#29) ObjectMapper Bean으로 등록
jeomxon Jul 19, 2023
33d2a2b
refactor: (#29) 매직넘버 상수화 및 변수, 메서드명 수정
jeomxon Jul 19, 2023
6efed21
refactor: (#29) @JsonProperty를 @JsonNaming으로 변경
jeomxon Jul 19, 2023
1e6ab82
chore: (#29) test용 production url 수정
jeomxon Jul 20, 2023
085d37b
refactor: (#29) 상수 및 변수명 수정
jeomxon Jul 20, 2023
b3e2d2d
test: (#29) 토큰에 대한 검증 추가
jeomxon Jul 20, 2023
d0553ba
refactor: (#29) 토큰을 파싱할 때 유효성 검사 추가
jeomxon Jul 20, 2023
059534a
merge: (#29) conflict 해결
jeomxon Jul 20, 2023
bbac2a0
refactor: (#29) 로그인 api nickname 필드 추가
jeomxon Jul 20, 2023
dc3e480
refactor: (#29) 토큰 검증 DisplayName 변경
jeomxon Jul 20, 2023
62605a7
refactor: (#29) Swagger tag name 변경
jeomxon Jul 20, 2023
c35ae22
refactor: (#29) TokenProcessorTest의 필드를 빈을 사용하도록 변경
jeomxon Jul 20, 2023
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
8 changes: 5 additions & 3 deletions backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,18 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0'
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'

testImplementation 'io.rest-assured:rest-assured'
testImplementation 'io.rest-assured:spring-mock-mvc'

runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'
Copy link
Collaborator

Choose a reason for hiding this comment

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

Q : 0.11.5 버전을 사용하신 이유가 궁금합니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

jjwt 최신 버전으로 가져왔습니다!
최신버전이 배포된 지 1년이 지난 시점이라 안정적이게 사용할 수 있을 것이라 판단한 것도 있습니다.

Copy link
Collaborator

Choose a reason for hiding this comment

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

배포된지 오래되어야 안정적일까요 아니면 배포가 자주 일어나야 안정적일까요 🤔

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

오 그렇게까지 생각을 못해본 것 같아요..!
배포가 자주 일어나는 것이 안정적일 것 같다는 생각이 드네요.
최신버전이 배포된 지 1년이 지난 시점이라 안정적이게 사용할 수 있을 것이라 판단한 것도 있습니다.에 말을 좀 덧붙이자면
최신 버전을 배포하고 1년동안 사람들이 문제없이 사용해왔다면 안정적이라고 할 수 있을 것 같다는 의미였습니다.

runtimeOnly 'com.h2database:h2'

compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'

testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.rest-assured:rest-assured'
testImplementation 'io.rest-assured:spring-mock-mvc'
}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;

@ConfigurationPropertiesScan
@SpringBootApplication
public class VotogetherApplication {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.votogether.domain.auth.controller;

import com.votogether.domain.auth.dto.LoginResponse;
import com.votogether.domain.auth.service.AuthService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@Tag(name = "회원가입", description = "회원가입 및 로그인 API")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
@Tag(name = "회원가입", description = "회원가입 및 로그인 API")
@Tag(name = "인증", description = "인증 API")

P2 : 인증과 관련된 로직들이 모이는 서비스인 것 같은데 인증 정보를 드러내보는건 어떨까요?!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

동의합니다.
수정해보겠습니다~

@RequiredArgsConstructor
@RestController
public class AuthController {

private final AuthService authService;

@Operation(summary = "카카오 로그인 하기", description = "카카오 계정으로 로그인을 한다.")
@ApiResponse(responseCode = "200", description = "로그인 성공")
Copy link
Collaborator

Choose a reason for hiding this comment

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

P2 : 실패하는 상황이 있다면 실패하는 상황에 대한 설명도 적어주면 좋을 것 같아요 :)

Copy link
Collaborator Author

@jeomxon jeomxon Jul 19, 2023

Choose a reason for hiding this comment

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

믿으실지 모르겠지만 이거 지적 안나오면 리뷰 재요청하려고 했습니다.
추가하겠습니다 ㅎㅎ

@GetMapping("/auth/kakao/callback")
public ResponseEntity<LoginResponse> loginByKakao(@RequestParam("code") final String code) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Q : code와 같이 매핑할 파라미터 이름이 같으면 @RequestParam의 name 속성은 생략해도 되는 것으로 알고 있는데, "code"를 다시 한번 지정해준 이유가 있을까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

명시적 표현을 위해서 지정해줬는데 제거하는 것도 깔끔하고 좋아보이네요!

Copy link
Collaborator

Choose a reason for hiding this comment

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

P3 : 메서드이름으로 loginWithKakao 는 어떠신가요? 테스트코드에는 이렇게 작성되어있더라고요

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

좋은 지적 감사합니다 ㅎㅎ
테스트코드에서 loginByKakao로 바꾸는 것이 더 좋을 것 같아서 그렇게 변경해볼게요~

final String token = authService.register(code);
return ResponseEntity.ok(new LoginResponse(token));
Copy link
Collaborator

Choose a reason for hiding this comment

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

Q : 바로 response를 반환하지 않고 String을 반환하신 이유가 궁금합니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

소셜타입별로 추상화를 해보고자 처음에 이렇게 설계했는데,
지금으로써는 response를 반환하는 것이 통일성을 좀 더 가져갈 수 있을 것 같네요.
그리고 확실하게 추상화를 시킬 수 있다는 보장이 없기도 해서 response를 반환하도록 변경해볼게요!

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.votogether.domain.auth.dto;

import com.fasterxml.jackson.annotation.JsonProperty;

public record KakaoMemberResponse(
@JsonProperty("id") Long id,
@JsonProperty("kakao_account") KakaoAccount kakaoAccount
Copy link
Collaborator

Choose a reason for hiding this comment

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

Q : @JsonProperty를 사용하지 않으면 카멜 케이스가 자동으로 스네이크 케이스로 변환되지 않는건가요?
Q : @JsonProperty는 직렬화/역직렬화 과정에서 둘다 동작하는건가요? 🤔

Copy link
Collaborator Author

@jeomxon jeomxon Jul 19, 2023

Choose a reason for hiding this comment

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

  1. @JsonProperty를 사용하지 않으면 자동으로 변환되지 않습니다.
  2. 찾아보니 직렬화/역직렬화 과정에서 둘 다 동작한다고 하네요.
    덕분에 다른 어노테이션을 학습할 수 있었는데요.
    @JsonNaming(value = PropertyNamingStrategies.SnakeCaseStrategy.class)이라는 어노테이션을 클래스 레벨에 붙임으로써 반복되는 필드의 @JsonProperty를 줄이고 스네이크 케이스를 사용했음을 쉽게 파악할 수 있더라구요.
    해당 어노테이션을 사용하는 것으로 변경해보았습니다!

Copy link
Collaborator

Choose a reason for hiding this comment

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

꼼꼼한 학습까지 너무 좋습니다 :)

) {

public record KakaoAccount(
@JsonProperty("email") String email,
@JsonProperty("age_range") String ageRange,
@JsonProperty("birthday") String birthday,
@JsonProperty("gender") String gender
) {
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Q : record안에 record가 있는 이유가 궁금합니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

해당 record내부에서만 사용할 것이고, 추가적인 확장성이 없다고 판단해서 내부에 선언했습니다.


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.votogether.domain.auth.dto;

public record LoginResponse(
String accessToken
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.votogether.domain.auth.dto;

import com.fasterxml.jackson.annotation.JsonProperty;

public record OAuthAccessTokenResponse(
@JsonProperty("token_type") String tokenType,
@JsonProperty("access_token") String accessToken,
@JsonProperty("expires_in") Integer expiresIn,
@JsonProperty("refresh_token") String refreshToken,
@JsonProperty("refresh_token_expires_in") Integer refreshTokenExpiresIn
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.votogether.domain.auth.service;

import com.votogether.domain.auth.dto.KakaoMemberResponse;
import com.votogether.domain.member.entity.Member;
import com.votogether.domain.member.service.MemberService;
import com.votogether.global.jwt.TokenProcessor;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@RequiredArgsConstructor
@Service
public class AuthService {

private final KakaoOAuthClient kakaoOAuthClient;
private final MemberService memberService;
private final TokenProcessor tokenProcessor;

public String register(final String code) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

P1 : @Transactional 어노테이션이 필요해 보입니다! 해당 어노테이션 없이 코드가 동작하면 어떤 문제가 발생할까요?

Copy link
Collaborator Author

@jeomxon jeomxon Jul 19, 2023

Choose a reason for hiding this comment

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

엄청난 문제가 발생할 것 같네요...
lock에 관련된 문제라든가... 롤백이 안된다든가...
큰 걸 깜빡했습니다... 수정할게요!

final String accessToken = kakaoOAuthClient.getAccessToken(code);
final KakaoMemberResponse response = kakaoOAuthClient.getMemberInfo(accessToken);

final Member member = Member.createKakaoMember(response);
Copy link
Collaborator

Choose a reason for hiding this comment

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

P3 : 정적 팩터리 메서드 규칙을 적용해보는건 어떨까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

규칙 적용해보겠습니다~

final Member registeredMember = memberService.register(member);

return tokenProcessor.generateToken(registeredMember);
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

저번에 OAuth 흐름도에 비추어 봤을 때, 이해하기 굉장히 편하도록 추상화가 잘 되어있네요 👍👍

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

이해가 잘 되신다니 다행입니다...ㅎㅎ


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.votogether.domain.auth.service;

import com.votogether.domain.auth.dto.KakaoMemberResponse;
import com.votogether.domain.auth.dto.OAuthAccessTokenResponse;
import lombok.Getter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

@Getter
@ConfigurationProperties(prefix = "oauth.kakao")
@Component
public class KakaoOAuthClient {

private static final RestTemplate restTemplate = new RestTemplate();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Q : RestTemplate를 빈으로 등록하지 않은 이유가 무엇인가요?!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

빈으로 등록하면 추가적인 작업을 커스텀하여 할 수 있을 것 같은데,
더 이상의 작업이 이루어지지 않을 것 같다고 판단하여서 빈으로 등록하지 않았습니다.


private final MultiValueMap<String, String> info = new LinkedMultiValueMap<>();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Q : Map 대신 MultiValueMap을 사용한 이유가 restTemplate의 postForObject 메서드에 MultiValueMap만 들어갈 수 있어서인가요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

그런 이유는 아니예요. 보편적으로 MultiValueMap이 사용되는 것입니다.
MultiValueMap은 하나의 key에 여러 개의 value가 들어갈 수 있는 구조입니다.
예를들어 파라미터로 하나의 키에 다중 값이 들어오는 경우를 생각해볼 수 있습니다.
해당 경우에는 단순 HashMap과 같은 자료구조로는 값을 전부 담기 힘듭니다.
따라서 이와 같은 경우를 대비해서 MultiValueMap을 사용한다고 볼 수 있습니다.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Q : @ConfigurationProperties(prefix = "oauth.kakao")이 어떤 역할인가요??
xml 에 파일에 있는 info정보가 MultiValueMap<String, String> info = new LinkedMultiValueMap<>();와 매핑되어 들어가지는 건가요??

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

맞습니다 yml파일에 있는 환경변수들을 매핑해주기 위해서 사용하고 있습니다.
oauth.kakao.info에 있는 key value가 매핑이 됩니다!


public String getAccessToken(final String code) {
info.add("code", code);

final OAuthAccessTokenResponse response = restTemplate.postForObject(
"https://kauth.kakao.com/oauth/token",
info,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Q : 이렇게 요청을 보내면 info값들이 파라미터로 나가는건가요? 바디로 나가는건가요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

x-www-form-urlencoded의 형식으로 지정되어
key, value형태의 응답이 나가게 됩니다!

OAuthAccessTokenResponse.class
);
return response.accessToken();
}

public KakaoMemberResponse getMemberInfo(final String accessToken) {
final HttpHeaders headers = new HttpHeaders();
headers.setBearerAuth(accessToken);
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
Comment on lines +37 to +39
Copy link
Collaborator

Choose a reason for hiding this comment

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

P3 : 이런 식으로 헤더값을 설정할 수 있군요 배워갑니다 👍🏻


final HttpEntity<Void> request = new HttpEntity<>(headers);

final KakaoMemberResponse response = restTemplate.exchange(
"https://kapi.kakao.com/v2/user/me",
HttpMethod.GET,
request,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Q : 헤더값을 지정하기위해 exchange 메서드를 사용하신건가요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

사실 메서드를 여러가지 활용해보기 위함도 있었는데요, 직접 사용해보면서 장단점을 알게되었습니다.
getForObject는 header를 따로 지정할 수 없더라구요.
따라서 token을 포함한 header를 지정하기 위해서 이 클래스에 존재하는 두개의 역할에 RestTemplate에서 제공하는 다른 메서드를 사용했습니다.

KakaoMemberResponse.class
).getBody();
return response;
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.votogether.domain.member.entity;

import com.votogether.domain.auth.dto.KakaoMemberResponse;
import com.votogether.domain.common.BaseEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
Expand All @@ -8,7 +9,6 @@
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import java.time.LocalDateTime;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
Expand All @@ -23,15 +23,18 @@ public class Member extends BaseEntity {
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(length = 15, nullable = false)
@Column(length = 15, unique = true, nullable = false)
private String nickname;

@Enumerated(value = EnumType.STRING)
@Column(length = 20, nullable = false)
private Gender gender;

@Column(nullable = false)
private LocalDateTime birthDate;
private String ageRange;

@Column(nullable = false)
private String birthday;

@Enumerated(value = EnumType.STRING)
@Column(length = 20, nullable = false)
Expand All @@ -46,20 +49,35 @@ public class Member extends BaseEntity {
@Builder
private Member(
final String nickname,
final LocalDateTime birthDate,
final Gender gender,
final String ageRange,
final String birthday,
final SocialType socialType,
final String socialId,
final Integer point
) {
this.nickname = nickname;
this.birthDate = birthDate;
this.gender = gender;
this.ageRange = ageRange;
this.birthday = birthday;
this.socialType = socialType;
this.socialId = socialId;
this.point = point;
}

public static Member createKakaoMember(final KakaoMemberResponse response) {
final NicknameNumberGenerator nicknameNumberGenerator = new NicknameNumberGenerator();
Copy link
Collaborator

Choose a reason for hiding this comment

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

P1 : 해당 메서드가 호출될 때마다 객체 생성이 일어날 것 같은데 1번 회원만 생성되는게 아닐까요?!

Copy link
Collaborator Author

@jeomxon jeomxon Jul 19, 2023

Choose a reason for hiding this comment

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

레전드 오류를 발견하셨네요...
바로 수정 작업 들어가겠습니다.
그리고 좀 더 효과적인 랜덤 닉네임 방법에 대해서 같이 고민해보면 좋을 것 같아요~

return Member.builder()
.nickname("익명의 손님" + nicknameNumberGenerator.generate())
.gender(Gender.valueOf(response.kakaoAccount().gender().toUpperCase()))
.ageRange(response.kakaoAccount().ageRange())
.birthday(response.kakaoAccount().birthday())
.socialType(SocialType.KAKAO)
.socialId(String.valueOf(response.id()))
.point(0)
.build();
}

public void plusPoint(final int point) {
this.point = this.point + point;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.votogether.domain.member.entity;

public class NicknameNumberGenerator implements NumberGenerator {

private int number = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Q

제가 알기로는 number를 static으로 해놔야 위에서

final NicknameNumberGenerator nicknameNumberGenerator = new NicknameNumberGenerator();

이 코드를 실행해도 증가값이 유지되는 것으로 알고 있습니다.

이 상태라면 위의 createKakaoMember() 메서드에서 new NicknameNumberGenerator() 로 생성될 때마다 number가 0으로 초기화 될 듯 합니다.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

저의 레전드 오류를 찾아주셔서 감사합니다.
휴먼 에러 빠르게 수정하여 반영하겠습니다!

Copy link
Collaborator

Choose a reason for hiding this comment

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

Q 만약 서버를 껐다가 다시 키게되면 랜덤값이 다시 0부터 초기화 되지 않나요??

Copy link
Collaborator Author

@jeomxon jeomxon Jul 19, 2023

Choose a reason for hiding this comment

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

static을 붙여서 서버가 실행되는 도중에는 값이 바뀌지 않도록 변경했습니다.
혹시나 서버가 꺼질 상황도 있을 것 같은데 그에 대비해서 다른 정책에 대해 이야기를 해보고,
적용하는 방식으로 진행하면 좋을 것 같아요!


@Override
public int generate() {
return ++number;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.votogether.domain.member.entity;

public interface NumberGenerator {

int generate();

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

public enum SocialType {

GOOGLE,
KAKAO,
;

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package com.votogether.domain.member.repository;

import com.votogether.domain.member.entity.Member;
import com.votogether.domain.member.entity.SocialType;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface MemberRepository extends JpaRepository<Member, Long> {

Optional<Member> findBySocialIdAndSocialType(final String socialId, final SocialType socialType);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.votogether.domain.member.service;

import com.votogether.domain.member.entity.Member;
import com.votogether.domain.member.repository.MemberRepository;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Transactional(readOnly = true)
Copy link
Collaborator

Choose a reason for hiding this comment

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

P3 : 디폴트로 해두는 것보다 메서드마다 붙이는게 더 직관적이고 어떤 메서드인지 파악하기 쉽다는 느낌이 들었는데 어떻게 생각하시나요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

어떤 방식이든 통일성있게 간다면 좋을 것 같다는 생각이었습니다.
메서드 개수가 작은 지금 시점에서는 각 메서드마다 붙여주는 것이 더 직관적이라는 생각이 드네요.
수정하겠습니다~

@RequiredArgsConstructor
@Service
public class MemberService {

private final MemberRepository memberRepository;

@Transactional
public Member register(final Member member) {
final Optional<Member> maybeMember = memberRepository.findBySocialIdAndSocialType(
member.getSocialId(),
member.getSocialType()
);
return maybeMember.orElseGet(() -> memberRepository.save(member));
}

public Member findById(final Long memberId) {
return memberRepository.findById(memberId)
.orElseThrow(() -> new IllegalArgumentException("해당 Id를 가지 회원은 존재하지 않습니다."));
Copy link
Collaborator

Choose a reason for hiding this comment

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

P1 : 가지 -> 가진

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

시력테스트였습니다^^

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.net.URI;
import java.time.LocalDateTime;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
Expand Down Expand Up @@ -42,12 +41,13 @@ public ResponseEntity<Void> save(
) {
// TODO : 일단 돌아가게 하기 위한 member 저장 (실제 어플에선 삭제될 코드)
final Member member = Member.builder()
.socialType(SocialType.GOOGLE)
.socialType(SocialType.KAKAO)
.socialId("tjdtls690")
.nickname("Abel")
.gender(Gender.MALE)
.point(100)
.birthDate(LocalDateTime.now())
.ageRange("30~39")
.birthday("0101")
.build();

final Long postId = postService.save(request, member, images);
Expand Down
11 changes: 11 additions & 0 deletions backend/src/main/java/com/votogether/global/jwt/Auth.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.votogether.global.jwt;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Auth {
}
Loading