diff --git a/.github/badges/branches.svg b/.github/badges/branches.svg
index 2e69d9c..154eea8 100644
--- a/.github/badges/branches.svg
+++ b/.github/badges/branches.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/.github/badges/jacoco.svg b/.github/badges/jacoco.svg
index a944a8d..d6f2758 100644
--- a/.github/badges/jacoco.svg
+++ b/.github/badges/jacoco.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/module-domain/src/main/java/com/gaethering/moduledomain/domain/member/Member.java b/module-domain/src/main/java/com/gaethering/moduledomain/domain/member/Member.java
index 2a7020c..9e567fe 100644
--- a/module-domain/src/main/java/com/gaethering/moduledomain/domain/member/Member.java
+++ b/module-domain/src/main/java/com/gaethering/moduledomain/domain/member/Member.java
@@ -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;
@@ -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;
diff --git a/module-member/build.gradle b/module-member/build.gradle
index 7c9ca4f..d0be785 100644
--- a/module-member/build.gradle
+++ b/module-member/build.gradle
@@ -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'
diff --git a/module-member/src/main/java/com/gaethering/modulemember/controller/MemberController.java b/module-member/src/main/java/com/gaethering/modulemember/controller/MemberController.java
index 04198f1..b22a2bc 100644
--- a/module-member/src/main/java/com/gaethering/modulemember/controller/MemberController.java
+++ b/module-member/src/main/java/com/gaethering/modulemember/controller/MemberController.java
@@ -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;
@@ -19,6 +20,14 @@ public class MemberController {
private final MemberService memberService;
+ @PostMapping("/sign-up")
+ public ResponseEntity signUp(@RequestBody SignUpRequest signUpRequest) {
+
+ memberService.signUp(signUpRequest);
+
+ return new ResponseEntity<>(HttpStatus.CREATED);
+ }
+
@PostMapping("/email-auth")
public ResponseEntity sendEmailAuthCode(@RequestBody EmailAuthRequest emailAuthRequest) {
memberService.sendEmailAuthCode(emailAuthRequest.getEmail());
diff --git a/module-member/src/main/java/com/gaethering/modulemember/dto/SignUpRequest.java b/module-member/src/main/java/com/gaethering/modulemember/dto/SignUpRequest.java
new file mode 100644
index 0000000..b7f87c1
--- /dev/null
+++ b/module-member/src/main/java/com/gaethering/modulemember/dto/SignUpRequest.java
@@ -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;
+
+}
diff --git a/module-member/src/main/java/com/gaethering/modulemember/exception/errorcode/MemberErrorCode.java b/module-member/src/main/java/com/gaethering/modulemember/exception/errorcode/MemberErrorCode.java
index 028600f..8a7e598 100644
--- a/module-member/src/main/java/com/gaethering/modulemember/exception/errorcode/MemberErrorCode.java
+++ b/module-member/src/main/java/com/gaethering/modulemember/exception/errorcode/MemberErrorCode.java
@@ -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;
diff --git a/module-member/src/main/java/com/gaethering/modulemember/exception/member/NotMatchPasswordException.java b/module-member/src/main/java/com/gaethering/modulemember/exception/member/NotMatchPasswordException.java
new file mode 100644
index 0000000..2f8446a
--- /dev/null
+++ b/module-member/src/main/java/com/gaethering/modulemember/exception/member/NotMatchPasswordException.java
@@ -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);
+ }
+}
diff --git a/module-member/src/main/java/com/gaethering/modulemember/service/MemberService.java b/module-member/src/main/java/com/gaethering/modulemember/service/MemberService.java
index 336112f..b8321c8 100644
--- a/module-member/src/main/java/com/gaethering/modulemember/service/MemberService.java
+++ b/module-member/src/main/java/com/gaethering/modulemember/service/MemberService.java
@@ -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);
+
}
diff --git a/module-member/src/main/java/com/gaethering/modulemember/service/MemberServiceImpl.java b/module-member/src/main/java/com/gaethering/modulemember/service/MemberServiceImpl.java
index c1ae8cd..99a16d6 100644
--- a/module-member/src/main/java/com/gaethering/modulemember/service/MemberServiceImpl.java
+++ b/module-member/src/main/java/com/gaethering/modulemember/service/MemberServiceImpl.java
@@ -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;
@@ -14,6 +21,7 @@
@Transactional(readOnly = true)
public class MemberServiceImpl implements MemberService {
+ private final PasswordEncoder passwordEncoder;
private final EmailService emailService;
private final MemberRepository memberRepository;
@@ -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();
+ }
+
+ 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();
+ }
+
}
diff --git a/module-member/src/test/java/com/gaethering/modulemember/service/MemberServiceTest.java b/module-member/src/test/java/com/gaethering/modulemember/service/MemberServiceTest.java
new file mode 100644
index 0000000..3f4a189
--- /dev/null
+++ b/module-member/src/test/java/com/gaethering/modulemember/service/MemberServiceTest.java
@@ -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 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();
+
+ 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());
+ }
+
+}
\ No newline at end of file