Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .github/badges/branches.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion .github/badges/jacoco.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
Expand Down Expand Up @@ -51,7 +52,7 @@ public class Member extends BaseTimeEntity {
@Enumerated(EnumType.STRING)
private MemberRole role;

@OneToOne(fetch = FetchType.LAZY)
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "member_profile_id")
private MemberProfile memberProfile;

Expand Down
1 change: 1 addition & 0 deletions module-member/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-mail'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
runtimeOnly 'com.h2database:h2'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.gaethering.modulemember.dto.ConfirmEmailRequest;
import com.gaethering.modulemember.dto.ConfirmEmailResponse;
import com.gaethering.modulemember.dto.EmailAuthRequest;
import com.gaethering.modulemember.dto.SignUpRequest;
import com.gaethering.modulemember.service.MemberService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
Expand All @@ -19,6 +20,14 @@ public class MemberController {

private final MemberService memberService;

@PostMapping("/sign-up")
public ResponseEntity<Void> signUp(@RequestBody SignUpRequest signUpRequest) {

memberService.signUp(signUpRequest);

return new ResponseEntity<>(HttpStatus.CREATED);
}

@PostMapping("/email-auth")
public ResponseEntity<Void> sendEmailAuthCode(@RequestBody EmailAuthRequest emailAuthRequest) {
memberService.sendEmailAuthCode(emailAuthRequest.getEmail());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.gaethering.modulemember.dto;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.gaethering.moduledomain.domain.type.Gender;
import java.time.LocalDate;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class SignUpRequest {

private String email;

private String nickname;

private String password;

private String passwordCheck;

private String name;

private String phone;

private LocalDate birth;

private Gender gender;

@JsonProperty("isEmailAuth")
private boolean isEmailAuth;

Copy link
Contributor

Choose a reason for hiding this comment

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

dto에서 validation을 넣으면 좋을 것 같습니다

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 @@ -9,7 +9,8 @@ public enum MemberErrorCode {
MEMBER_NOT_FOUND("E001", "존재하지 않는 회원입니다."),
INVALID_EMAIL_AUTH_CODE("E901", "이메일 인증 코드가 유효하지 않습니다."),
DUPLICATED_EMAIL("E902", "중복된 이메일입니다."),
FAILED_SEND_EMAIL("E903", "이메일 전송에 실패하였습니다.");
FAILED_SEND_EMAIL("E903", "이메일 전송에 실패하였습니다."),
NOT_MATCH_PASSWORD("E904", "비밀번호가 서로 일치하지 않습니다.");

private final String code;
private final String message;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.gaethering.modulemember.exception.member;

import com.gaethering.modulemember.exception.errorcode.MemberErrorCode;

public class NotMatchPasswordException extends MemberException {

public NotMatchPasswordException() {
super(MemberErrorCode.NOT_MATCH_PASSWORD);
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package com.gaethering.modulemember.service;

import com.gaethering.modulemember.dto.SignUpRequest;

public interface MemberService {

void sendEmailAuthCode(String email);

void confirmEmailAuthCode(String code);

String signUp(SignUpRequest signUpRequest);

}
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
package com.gaethering.modulemember.service;

import com.gaethering.moduledomain.domain.member.Member;
import com.gaethering.moduledomain.domain.member.MemberProfile;
import com.gaethering.moduledomain.domain.type.MemberRole;
import com.gaethering.moduledomain.domain.type.MemberStatus;
import com.gaethering.moduledomain.repository.member.MemberRepository;
import com.gaethering.modulemember.dto.SignUpRequest;
import com.gaethering.modulemember.exception.member.DuplicatedEmailException;
import com.gaethering.modulemember.exception.member.NotMatchPasswordException;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -14,6 +21,7 @@
@Transactional(readOnly = true)
public class MemberServiceImpl implements MemberService {

private final PasswordEncoder passwordEncoder;
private final EmailService emailService;
Copy link
Contributor

Choose a reason for hiding this comment

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

저번에 말씁드리려다가 못 드렸는데 MemberService에서 다른 Service 계층에 의존하는 것이 조금 어색한 것 같습니다. 일반 Component로 등록하는 방식은 어떠신가요?

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 Author

Choose a reason for hiding this comment

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

클래스명 변경없이 어노테이션의 수정만을 말씀하시는게 맞을까요?

Copy link
Contributor

Choose a reason for hiding this comment

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

클래스 명도 수정 하는 것이 좋을 것 같습니다! 이름을 보면 service 계층이구나 할 수 있기 때문에요

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 MemberRepository memberRepository;

Expand All @@ -39,4 +47,34 @@ public void confirmEmailAuthCode(String code) {

}

@Override
@Transactional
public String signUp(SignUpRequest signUpRequest) {

if (memberRepository.existsByEmail(signUpRequest.getEmail())) {
throw new DuplicatedEmailException();
}

if (!signUpRequest.getPassword().equals(signUpRequest.getPasswordCheck())) {
throw new NotMatchPasswordException();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

이 부분은 Dto에서 Validation처리를 통해 할 수 있을 것 같습니다.


Member newMember = Member.builder()
.email(signUpRequest.getEmail())
.nickname(signUpRequest.getNickname())
.password(passwordEncoder.encode(signUpRequest.getPassword()))
.role(MemberRole.ROLE_USER)
.status(MemberStatus.ACTIVE)
.isEmailAuth(signUpRequest.isEmailAuth())
.memberProfile(MemberProfile.builder()
.phoneNumber(signUpRequest.getPhone())
.gender(signUpRequest.getGender())
.build())
.build();

memberRepository.save(newMember);

return newMember.getNickname();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package com.gaethering.modulemember.service;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

import com.gaethering.moduledomain.domain.member.Member;
import com.gaethering.moduledomain.domain.type.Gender;
import com.gaethering.moduledomain.repository.member.MemberProfileRepository;
import com.gaethering.moduledomain.repository.member.MemberRepository;
import com.gaethering.modulemember.dto.SignUpRequest;
import com.gaethering.modulemember.exception.errorcode.MemberErrorCode;
import com.gaethering.modulemember.exception.member.DuplicatedEmailException;
import com.gaethering.modulemember.exception.member.NotMatchPasswordException;
import java.time.LocalDate;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.security.crypto.password.PasswordEncoder;

@ExtendWith(MockitoExtension.class)
class MemberServiceTest {

@Mock
private MemberRepository memberRepository;

@Mock
private MemberProfileRepository memberProfileRepository;

@Mock
private PasswordEncoder passwordEncoder;

@InjectMocks
private MemberServiceImpl memberService;

@Test
@DisplayName("회원 가입 성공")
void signUp_Success() {
//given
SignUpRequest request = SignUpRequest.builder()
.email("gaethering@gmail.com")
.nickname("개더링")
.password("1234qwer!")
.passwordCheck("1234qwer!")
.name("김진호")
.phone("010-3230-2498")
.birth(LocalDate.of(2022, 02, 15))
.gender(Gender.MALE)
.isEmailAuth(true)
.build();

given(memberRepository.existsByEmail(anyString()))
.willReturn(false);

given(passwordEncoder.encode(anyString()))
.willReturn(anyString());

ArgumentCaptor<Member> captor = ArgumentCaptor.forClass(Member.class);

//when
String nickname = memberService.signUp(request);

//then
assertEquals(request.getNickname(), nickname);
verify(memberRepository, times(1)).save(captor.capture());
}

@Test
@DisplayName("회원가입 실패_중복된 이메일이 있을 경우")
void signUp_ExceptionThrown_DuplicatedEmail() {
//given
SignUpRequest request = SignUpRequest.builder()
.email("gaethering@gmail.com")
.nickname("개더링")
.password("1234qwer!")
.passwordCheck("1234qwer!")
.name("김진호")
.phone("010-3230-2498")
.birth(LocalDate.of(2022, 02, 15))
.gender(Gender.MALE)
.isEmailAuth(true)
.build();

given(memberRepository.existsByEmail(anyString()))
.willReturn(true);

//when
DuplicatedEmailException exception = assertThrows(
DuplicatedEmailException.class, () -> memberService.signUp(request));

//then
assertEquals(MemberErrorCode.DUPLICATED_EMAIL.getCode(),
exception.getErrorCode().getCode());
}

@Test
@DisplayName("회원가입 실패_비밀번호가 일치하지 않는 경우")
void signUp_ExceptionThrown_NotMatchPassword() {
//given
SignUpRequest request = SignUpRequest.builder()
.email("gaethering@gmail.com")
.nickname("개더링")
.password("1234qwer!")
.passwordCheck("1234qwer")
.name("김진호")
.phone("010-3230-2498")
.birth(LocalDate.of(2022, 02, 15))
.gender(Gender.MALE)
.isEmailAuth(true)
.build();
Copy link
Collaborator

Choose a reason for hiding this comment

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

SignUpRequest를 생성하는 부분이 여러 번 반복되고 있는 것 같아
따로 메서드로 추출하면 중복코드가 줄어들 수 있을 것 같습니다!


given(memberRepository.existsByEmail(anyString()))
.willReturn(false);

//when
NotMatchPasswordException exception = assertThrows(
NotMatchPasswordException.class, () -> memberService.signUp(request));

//then
assertEquals(MemberErrorCode.NOT_MATCH_PASSWORD.getCode(),
exception.getErrorCode().getCode());
}

}