Skip to content

Commit

Permalink
추가: 로그인 & 회원가입
Browse files Browse the repository at this point in the history
  • Loading branch information
rrosiee committed May 23, 2024
1 parent 6b3c2c0 commit 4999ab4
Show file tree
Hide file tree
Showing 12 changed files with 152 additions and 22 deletions.
15 changes: 14 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,20 @@ dependencies {
// S3
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'

// validation
// validation @ApiOperation(value = "회원가입 & 로그인")
// @PostMapping("/login")
// public ResponseEntity login(@Valid @RequestBody MemberLoginDto request) {
// // Member 확인
// Member member = memberService.getMemberBySocial(request.socialId, request.socialType, request.email);
//
// // accessToken과 refreshToken 발급
// String accessToken = jwtService.getAccessToken(member);
//
// // 응답
// MemberRetrieveDto memberRetrieveDto = memberMapper.memberToMemberRetrieveDto(member);
// memberRetrieveDto.setAccessToken(accessToken);
// return new ResponseEntity<>(memberRetrieveDto, HttpStatus.OK);
// }
implementation 'org.springframework.boot:spring-boot-starter-validation'

// QueryDsl
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public ResponseEntity login(
memberService.verifiedNickname(request.nickname);

// socialId, socialType기준 Member 반환, 없다면 새로 생성
Member member = memberService.getMemberBySocial(request.socialId, request.socialType);
Member member = memberService.getMemberBySocial(request.socialId, request.socialType, "[email protected]");

// profile Url 설정
if (profileImage != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,24 @@ public class MemberController {
private final LogoutTokenService logoutTokenService;
private final CategoryService categoryService;

@ApiOperation(value = "회원가입 & 로그인",
notes = "`email` : required False\n" +
"`socialId` : required True\n" +
"`socialType` : required True")
@PostMapping("/login")
public ResponseEntity login(@Valid @RequestBody MemberLoginDto request) {
// Member 확인
Member member = memberService.getMemberBySocial(request.socialId, request.socialType, request.email);

// accessToken과 refreshToken 발급
String accessToken = jwtService.getAccessToken(member);

// 응답
MemberRetrieveDto memberRetrieveDto = memberMapper.memberToMemberRetrieveDto(member);
memberRetrieveDto.setAccessToken(accessToken);
return new ResponseEntity<>(memberRetrieveDto, HttpStatus.OK);
}

@GetMapping("/{memberId}") // todo : 관리자 권한 있어야 실행 가능한 것으로 바꾸기
public ResponseEntity getMember(
@RequestHeader(value = "Authorization", required = false) String accessToken,
Expand All @@ -49,12 +67,12 @@ public ResponseEntity getMember(
@ApiOperation(
value = "통계 조회",
notes = " - Authorization 토큰 필수\n" +
" - month=2023-10(yyyy-mm형식)\n" )
" - month=2023-10(yyyy-mm형식)\n")
@GetMapping("/statistics")
public ResponseEntity getStatistics(
@RequestHeader(value = "Authorization", required = false) String accessToken,
@RequestParam(required = false) String month) {
if (ObjectUtils.isEmpty(accessToken)){
if (ObjectUtils.isEmpty(accessToken)) {
throw new BusinessException(ErrorCode.MISSING_REQUEST);
}
Member member = jwtService.getMemberFromAccessToken(accessToken);
Expand All @@ -68,7 +86,7 @@ public ResponseEntity getStatistics(
@GetMapping("/year-statistics")
public ResponseEntity getYearStatistics(
@RequestHeader(value = "Authorization", required = false) String accessToken) {
if (ObjectUtils.isEmpty(accessToken)){
if (ObjectUtils.isEmpty(accessToken)) {
throw new BusinessException(ErrorCode.MISSING_REQUEST);
}
Member member = jwtService.getMemberFromAccessToken(accessToken);
Expand All @@ -82,7 +100,7 @@ public ResponseEntity getYearStatistics(
@GetMapping("/my-page")
public ResponseEntity getMyPage(
@RequestHeader(value = "Authorization", required = false) String accessToken) {
if (ObjectUtils.isEmpty(accessToken)){
if (ObjectUtils.isEmpty(accessToken)) {
throw new BusinessException(ErrorCode.MISSING_REQUEST);
}
Member member = jwtService.getMemberFromAccessToken(accessToken);
Expand Down Expand Up @@ -127,7 +145,7 @@ public ResponseEntity getMember(
@GetMapping("/list")
public ResponseEntity getMemberList(
@RequestHeader(value = "Authorization", required = false) String accessToken) {
if (ObjectUtils.isEmpty(accessToken)){
if (ObjectUtils.isEmpty(accessToken)) {
throw new BusinessException(ErrorCode.MISSING_REQUEST);
}
List<MemberResponseDto> memberResponseDtoList = memberMapper.membersToMemberResponseDtos(memberService.getMemberList());
Expand All @@ -148,7 +166,7 @@ public ResponseEntity patchMember(
@Valid @RequestPart(value = "request", required = false) MemberPatchRequestDto request,
@RequestPart(value = "categorys", required = false) List<String> categorys
) {
if (ObjectUtils.isEmpty(accessToken)){
if (ObjectUtils.isEmpty(accessToken)) {
throw new BusinessException(ErrorCode.MISSING_REQUEST);
}

Expand All @@ -175,7 +193,7 @@ public ResponseEntity patchMember(
@DeleteMapping
public ResponseEntity deleteMember(
@RequestHeader(value = "Authorization", required = false) String accessToken) {
if (ObjectUtils.isEmpty(accessToken)){
if (ObjectUtils.isEmpty(accessToken)) {
throw new BusinessException(ErrorCode.MISSING_REQUEST);
}
memberService.deleteMember(jwtService.getMemberFromAccessToken(accessToken).getId());
Expand All @@ -186,7 +204,7 @@ public ResponseEntity deleteMember(
@GetMapping("/logout")
public ResponseEntity logoutMember(
@RequestHeader(value = "Authorization", required = false) String accessToken) { // todo : header 안 넣으면 나오는 에러 문구 수정하기
if (ObjectUtils.isEmpty(accessToken)){
if (ObjectUtils.isEmpty(accessToken)) {
throw new BusinessException(ErrorCode.MISSING_REQUEST);
}
logoutTokenService.memberLogout(accessToken);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package project.backend.domain.member.dto;

import io.swagger.annotations.ApiModel;
import lombok.*;
import project.backend.domain.member.entity.SocialType;
import project.backend.global.annotation.SocialTypeSubset;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull;

@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MemberLoginDto {
@NotNull(message = "socialId는 필수값입니다.")
public String socialId;

@NotNull(message = "socialType은 필수값입니다.")
@SocialTypeSubset(anyOf = {SocialType.KAKAO, SocialType.APPLE, SocialType.GOOGLE}, message = "KAKAO, APPLE, GOOGLE 중 하나를 입력해야 합니다.")
public SocialType socialType;

@Email(message = "유효한 이메일 형식을 입력해야 합니다.")
public String email;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package project.backend.domain.member.dto;

import lombok.*;
import project.backend.domain.member.entity.Agree;

import java.time.LocalDateTime;
import java.util.List;

@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MemberRetrieveDto {
public Long id;
public String email;
public Boolean isSignup;
public String accessToken;
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,16 @@ public class Member extends BaseEntity {

public String nickname;

public String email;

public LocalDateTime nicknameChangeDate;

public String profileUrl;

public String refreshToken;

public Boolean isSignup = false;

@Enumerated(EnumType.STRING)
public Agree marketingAgree = Agree.DISAGREE;

Expand All @@ -65,7 +69,7 @@ public class Member extends BaseEntity {
private List<Traffic> traffics = new ArrayList<>();

@Builder
public Member(SocialType socialType, String socialId, String nickname, String profileUrl, String refreshToken, Agree marketingAgree, Agree pushAgree){
public Member(SocialType socialType, String socialId, String nickname, String profileUrl, String refreshToken, Agree marketingAgree, Agree pushAgree) {
this.socialType = socialType;
this.socialId = socialId;
this.nickname = nickname;
Expand All @@ -76,7 +80,7 @@ public Member(SocialType socialType, String socialId, String nickname, String pr
}

// Patch
public Member patchMember(MemberPatchRequestDto memberPatchRequestDto){
public Member patchMember(MemberPatchRequestDto memberPatchRequestDto) {
this.nickname = Optional.ofNullable(memberPatchRequestDto.getNickname()).orElse(this.nickname);
this.profileUrl = Optional.ofNullable(memberPatchRequestDto.getProfileUrl()).orElse(this.profileUrl);
this.refreshToken = Optional.ofNullable(memberPatchRequestDto.getRefreshToken()).orElse(this.refreshToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
@Getter
public enum SocialType {
KAKAO("kakao"),
APPLE("apple");
APPLE("apple"),
GOOGLE("google");

private final String status;

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

import org.mapstruct.Mapper;
import org.mapstruct.ReportingPolicy;
import project.backend.domain.member.dto.MemberMyPageResponseDto;
import project.backend.domain.member.dto.MemberPatchRequestDto;
import project.backend.domain.member.dto.MemberPostRequestDto;
import project.backend.domain.member.dto.MemberResponseDto;
import project.backend.domain.member.dto.*;
import project.backend.domain.member.entity.Member;

import java.util.List;
Expand All @@ -18,6 +15,8 @@ public interface MemberMapper {

MemberResponseDto memberToMemberResponseDto(Member member);

MemberRetrieveDto memberToMemberRetrieveDto(Member member);

MemberMyPageResponseDto MemberToMemberMyPageResponseDto(Member member);

List<MemberResponseDto> membersToMemberResponseDtos(List<Member> member);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,15 @@ public class MemberService {
* @param socialType
* @return Member
*/
public Member getMemberBySocial(String socialId, SocialType socialType) {
return memberRepository.findFirstBySocialIdAndSocialType(socialId, socialType)
public Member getMemberBySocial(String socialId, SocialType socialType, String email) {

Member member = memberRepository.findFirstBySocialIdAndSocialType(socialId, socialType)
.orElseGet(() -> createMember(socialId, socialType));
if (email != null && member.email == null) {
member.email = email;
memberRepository.save(member);
}
return member;
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package project.backend.global.annotation;

import project.backend.domain.member.entity.SocialType;
import project.backend.global.validator.SocialTypeSubsetValidator;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

@Documented
@Constraint(validatedBy = SocialTypeSubsetValidator.class)
@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface SocialTypeSubset {
SocialType[] anyOf();
String message() default "Invalid social type";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@
import static springfox.documentation.builders.RequestHandlerSelectors.withMethodAnnotation;

@Configuration
@EnableSwagger2
public class SwaggerConfig {

// Docket 설정
@Bean
public Docket restApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo()) // optional
.apiInfo(apiInfo())
.securitySchemes(singletonList(apiKey()))
.securityContexts(singletonList(securityContext())) // Security 관련 설정
.securityContexts(singletonList(securityContext()))
.produces(singleton("application/json"))
.consumes(singleton("application/json"))
.useDefaultResponseMessages(false)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package project.backend.global.validator;

import project.backend.domain.member.entity.SocialType;
import project.backend.global.annotation.SocialTypeSubset;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Arrays;

public class SocialTypeSubsetValidator implements ConstraintValidator<SocialTypeSubset, SocialType> {
private SocialType[] subset;

@Override
public void initialize(SocialTypeSubset constraint) {
this.subset = constraint.anyOf();
}

@Override
public boolean isValid(SocialType value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
return Arrays.asList(subset).contains(value);
}
}

0 comments on commit 4999ab4

Please sign in to comment.