일반 유저인지, 관리자인지 구분
com.shop.constant.Role.java
package com.shop.constant;
public enum Role {
USER, ADMIN
}
com.shop.dto.MemberFormDto.java
package com.shop.dto;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class MemberFormDto {
private String name;
private String email;
private String password;
private String address;
}
com.shop.entity.Item.java
package com.shop.entity;
import com.shop.constant.Role;
import com.shop.dto.MemberFormDto;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.security.crypto.password.PasswordEncoder;
import javax.persistence.*;
@Entity
@Table(name = "member")
@Getter
@Setter
@ToString
public class Member {
@Id
@Column(name = "member_id")
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
@Column(unique = true)
private String email;
private String password;
private String address;
// Enum 사용시 기본적인 순서 지정되어 있음. 순서가 바뀔 경우를 대비해 EnumType.STRING 옵션을 통해 String으로 지정할 것.드
@Enumerated(EnumType.STRING)
private Role role;
// Member 엔티티에 회원 생성 메소드를 만들어서 관리하면 코드가 변경되더라도 한 군데만 수정하면 됨.
public static Member createMember(MemberFormDto memberFormDto, PasswordEncoder passwordEncoder){
Member member = new Member();
member.setName(memberFormDto.getName());
member.setEmail(memberFormDto.getEmail());
member.setAddress(memberFormDto.getAddress());
String password = passwordEncoder.encode(memberFormDto.getPassword());
member.setPassword(password);
member.setRole(Role.USER);
return member;
}
}
Member 엔티티를 데이터베이스에 저장하기 위함.
com.shop.repository.MemberRepository
package com.shop.repository;
import com.shop.entity.Member;
import org.springframework.data.jpa.repository.JpaRepository;
public interface MemberRepository extends JpaRepository<Member, Long> {
// 회원가입 시 중복된 회원이 있는지 검사하기 위해 이메일로 회원을 검사할 수 있는 쿼리메소드
Member findByEmail(String email);
}
com.shop.service.MemberService
package com.shop.service;
import com.shop.entity.Member;
import com.shop.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional
@RequiredArgsConstructor
public class MemberService {
private final MemberRepository memberRepository;
public Member saveMember(Member member){
validateDuplicateMember(member);
return memberRepository.save(member);
}
private void validateDuplicateMember(Member member) {
Member findMember = memberRepository.findByEmail(member.getEmail());
if(findMember != null){
throw new IllegalStateException("이미 가입된 회원입니다.");
}
}
}
@SpringBootTest
// 테스트 클래스에 @Transactional 어노테이션은 테스트 실행 후 롤백 처리함. >> 같은 메소드 반복 테스트 가능
@Transactional
@TestPropertySource(locations = "classpath:application-test.properties")
class MemberServiceTest {
@Autowired
MemberService memberService;
@Autowired
PasswordEncoder passwordEncoder;
public Member createMember(){
MemberFormDto memberFormDto = new MemberFormDto();
memberFormDto.setEmail("test@email.com");
memberFormDto.setName("홍길동");
memberFormDto.setAddress("서울시 마포구 합정동");
memberFormDto.setPassword("1234");
return Member.createMember(memberFormDto, passwordEncoder);
}
회원가입 DTO 객체를 Service 객체를 통해서 저장(save)
@Test
@DisplayName("회원가입 테스트")
public void saveMemberTest(){
Member member = createMember();
Member savedMember = memberService.saveMember(member);
// assertEqauls: 저장하려고 요청했던 값과 실제 저장된 데이터 비교. (파라미터: 기대값, 실제 저장값)
assertEquals(member.getEmail(), savedMember.getEmail());
assertEquals(member.getName(), savedMember.getName());
assertEquals(member.getAddress(), savedMember.getAddress());
assertEquals(member.getPassword(), savedMember.getPassword());
assertEquals(member.getRole(), savedMember.getRole());
}
@Test
@DisplayName("중복 회원 가입 테스트")
public void saveDuplicateMemberTest(){
Member member1 = createMember();
Member member2 = createMember();
memberService.saveMember(member1);
// 예외 처리 테스트. 첫번째 파라미터에는 발생할 예외 타입 넣어주기
Throwable e = assertThrows(IllegalStateException.class, () -> {
memberService.saveMember(member2);});
assertEquals("이미 가입된 회원입니다.", e.getMessage());
}
@RequestMapping("/members")
@Controller
@RequiredArgsConstructor
public class MemberController {
private final MemberService memberService;
private final PasswordEncoder passwordEncoder;
@GetMapping(value = "/new")
public String memberForm(Model model){
// Get 요청으로 회원가입 페이지 요청할 경우 MemberFormDto 객체 같이 넘김.
model.addAttribute("memberFormDto", new MemberFormDto());
return "member/memberForm";
}
@PostMapping(value = "/new")
// Post 요청으로 넘어온 회원가입 정보를 MemberFormDto 객체로 받음
public String memberForm(MemberFormDto memberFormDto){
Member member = Member.createMember(memberFormDto, passwordEncoder);
memberService.saveMember(member);
return "redirect:/";
}
@Controller
public class MainController {
@GetMapping(value = "/")
public String main(){
return "main";
}
}
}
서버로 넘어오는 값 검증
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
어노테이션 | 설명 |
---|---|
@NotEmpty | NULL 체크 및 문자열의 경우 길이 0인지 검사 |
@NotBlank | NULL 체크 및 문자열의 경우 길이 0 및 빈 문자열(" ") 검사 |
@Length | 최소, 최대 길이 검사 |
이메일 형식인지 검사 | |
@Max | 지정한 값보다 작은지 검사 |
@Min | 지정한 값보다 큰지 검사 |
@Null | 값이 Null인지 검사 |
@NotNull | 값이 Null이 아닌지 검사 |
@Getter
@Setter
public class MemberFormDto {
@NotBlank(message = "이름은 필수 입력 값입니다.")
private String name;
@NotBlank(message = "이메일은 필수 입력 값입니다.")
@Email(message = "이메일 형식으로 입력해주세요.")
private String email;
@NotBlank(message = "비밀번호는 필수 입력 값입니다.")
@Length(min=8, max=16, message = "비밀번호는 8자 이상, 16자 이하로 입력해주세요.")
private String password;
@NotBlank(message = "주소는 필수 입력 값입니다.")
private String address;
}
MemberController.java
@PostMapping(value = "/new")
// Post 요청으로 넘어온 회원가입 정보를 MemberFormDto 객체로 받음
// 검증하려는 객체 앞에 @Valid 추가. 검사 후 결과는 bindingResult에 담김.
public String memberForm(@Valid MemberFormDto memberFormDto,
BindingResult bindingResult, Model model){
// 에러가 있으면 회원가입 페이지로 다시 이동.
if(bindingResult.hasErrors()){
return "member/memberForm";
}
try {
Member member = Member.createMember(memberFormDto, passwordEncoder);
memberService.saveMember(member);
} catch (IllegalStateException e){
model.addAttribute("errorMessage", e.getMessage());
return "member/memberForm";
}
return "redirect:/";
}
@Valid
어노테이션 선언.bindingResult
객체 추가하여 검사 후 결과 담아주기.bindingResult.hasErrors()
호출해 에러가 있다면 회원가입 페이지로 다시 돌아감.입력한 데이터에 문제가 있는 경우
회원가입에 성공한 경우 (메인 페이지로 이동)