📌 검증 기능은 아직 구현하지 못 했기 때문에 회원 가입과 ID, Email 중복 체크를 중점으로 보면 된다.
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "USER_INFO")
@ToString
public class UserEntity {
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long userNo;
@Column(name = "USER_NAME", length = 20)
private String userName;
@Column(name = "USER_PN", length = 40)
private String userPn;
@NotNull
@Column(name = "USER_EMAIL", length = 200)
private String userEmail;
@NotNull
@Column(name = "USER_ID", length = 200)
private String userId;
@Column(name = "USER_PW", length = 200)
private String userPw;
@Column(name = "USER_ADDR", length = 200)
private String userAddr;
@Builder
public UserEntity(Long userNo,
String userName,
String userPn,
String userEmail,
String userId,
String userPw,
String userAddr) {
this.userNo = userNo;
this.userName = userName;
this.userPn = userPn;
this.userEmail = userEmail;
this.userId = userId;
this.userPw = userPw;
this.userAddr = userAddr;
}
}
UserEntity.java
Entity에는 builder를 적용했다.
@Data
public class UserSaveForm {
private Long userNo;
@NotBlank(message = "이름을 입력해주세요.")
@NotNull
private String userName;
@NotBlank(message = "연락처를 입력해주세요.")
private String userPn;
@NotBlank(message = "이메일 주소를 입력해주세요.")
@Email(message = "올바른 이메일 주소를 입력해주세요.")
private String userEmail;
@NotBlank(message = "아이디를 입력해주세요.")
private String userId;
@NotBlank(message = "비밀번호를 입력해주세요.")
@Size(min = 8, max = 20, message = "비밀번호는 8자 이상 20자 이하로 입력해주세요.")
private String userPw;
@Column(name = "USER_ADDR", length = 200)
private String userAddr;
}
UserSaveForm.java
@Data
public class UserDTO {
private Long userNo;
private String userName;
private String userPn;
private String userEmail;
private String userId;
private String userPw;
private String userAddr;
public UserDTO(Long userNo,
String userName,
String userPn,
String userEmail,
String userId,
String userPw,
String userAddr) {
this.userNo = userNo;
this.userName = userName;
this.userPn = userPn;
this.userEmail = userEmail;
this.userId = userId;
this.userPw = userPw;
this.userAddr = userAddr;
}
}
UserDTO.java
Entity, DTO, VO를 사용해서 데이터를 전달한다.
/**
* 회원가입 페이지로 이동
*/
@GetMapping("/user/regUser")
public String regUserForm(Model model) {
log.info(this.getClass().getName() + ".user/regUser 회원가입으로 이동 !!");
model.addAttribute("userSaveForm", new UserSaveForm());
return "user/regUser";
}
회원가입 페이지로 이동할 때 Model 객체에 담아 데이터를 담아 올 VO를 보낸다.
<form th:action="@{/user/regUser/insert}" th:object="${userSaveForm}" id="contactForm" data-sb-form-api-token="API_TOKEN" th:method="post">
<div th:hidden="*{userNo}"></div>
<div class="form-floating">
<input class="form-control" id="userName" th:field="*{userName}" type="text" placeholder="이름을 입력하세요" data-sb-validations="required" />
<label for="userName">이름</label>
<div class="invalid-feedback" data-sb-feedback="name:required">A name is required.</div>
</div>
th:object로 값을 받을 vo를 설정하고 th:action으로 다시 Controller로 submit해준다.
/**
* 회원가입 로직 처리
*/
@PostMapping("/user/regUser/insert")
public String InsertRegUser(@Valid UserSaveForm form, BindingResult result) throws Exception{
log.info(this.getClass().getName() + "회원가입 로직 처리 시작");
/**
* 1. @Valid 검증 로직 실행
*/
if (result.hasErrors()) {
log.info(" 회원가입 로직 처리 중 Errors 처리 result ={}", result);
return "user/regUser";
}
/**
* 2. DTO 생성자에 VO 값 세팅
*/
UserDTO userDTO = new UserDTO(
form.getUserNo(),
form.getUserName(),
form.getUserPn(),
form.getUserEmail(),
form.getUserId(),
form.getUserPw(),
form.getUserAddr()
);
log.info("UserDTO ={}", userDTO);
/**
* 3. 회원 가입 정보 DTO를 Controller -> Service 전달
*/
userService.InsertUser(userDTO);
return "/user/logIn";
}
vo로 받아온 값을 DTO에 넣어준다.
찾아보니 가능하면 Setter를 안 쓰는 게 좋다고 해서 DTO에 생성자를 만들고 값을 파라미터로 받아 넘겨주는 식으로 데이터를 옮겨줬다.
vo → dto로 데이터를 담고 Service로 보내준다.
@Slf4j
@RequiredArgsConstructor
@Service
public class UserService implements IUserService {
@Autowired
private UserRepository userRepository;
@Override
public void InsertUser(UserDTO userDTO) throws Exception {
log.info(this.getClass().getName() + "로그인 처리 Controller -> Service");
/**
* 1. builder pattern 활용하여 Entity에 값을 세팅
*/
UserEntity userEntity = UserEntity.builder()
.userNo(userDTO.getUserNo())
.userName(userDTO.getUserName())
.userPn(userDTO.getUserPn())
.userEmail(EncryptUtil.encHashSHA256(userDTO.getUserEmail()))
.userId(userDTO.getUserId())
.userPw(EncryptUtil.encHashSHA256(userDTO.getUserPw()))
.userAddr(userDTO.getUserAddr())
.build();
log.info("userEntity에 담긴 값 확인 ={}", userEntity);
/**
* 2. ID 및 Email 중복 체크
*/
validateDuplicateUser(userEntity);
/**
* 5. 중복 체크 완료 후 회원 가입
*/
userRepository.save(userEntity);
}
private void validateDuplicateUser(UserEntity userEntity) {
log.info(this.getClass().getName() + "중복 체크 로직 실행");
log.info("중복 체크를 위해 받아온 아이디 값 = {}", userEntity.getUserId());
log.info("중복 체크를 위해 받아온 이메일 값 = {}", userEntity.getUserEmail());
/**
* 3. 회원가입 중 ID 중복 체크
*/
List<UserEntity> findUserId = userRepository.findId(userEntity.getUserId());
log.info("로직 후 아이디 ={}", findUserId);
if (!findUserId.isEmpty()) {
throw new IllegalArgumentException("이미 존재하는 아이디입니다.");
}
/**
* 4. 회원가입 중 Email 중복 체크
*/
List<UserEntity> findUserEmail = userRepository.findByEmail(userEntity.getUserEmail());
log.info("로직 후 이메일 ={}", findUserEmail);
if (!findUserEmail.isEmpty()) {
throw new IllegalArgumentException("이미 존재하는 이메일입니다.");
}
log.info(this.getClass().getName() + "중복 체크 로직 종료");
}
}
Service는 interfaceService를 만들고 구현하는 식으로 만들었다.
주석으로 순서를 체크해 뒀다 참고하면 된다.
- 최종적으로 Entity에 값을 담을건데 Entity는 Setter를 사용하지 않는 게 좋다 하여 builder 패턴을 사용했고 이때 dto → entity로 데이터를 넘기면서 암호화 로직을 적용했다.
- ID와 Email을 체크하기 위해 따로 메소드를 만들었고 체크할 값이 담긴 entity를 넘겨준다.
- Repository에 만들어둔 체크 로직 실행을 위해 값을 보내주고 리턴 받은 값에 따라 IllegalArgumentException 을 보낸다.
- 3번과 마찬가지로 적용된다.
- 중복 체크를 완료했다면 다시 Repository에 만들어둔 저장 로직을 실행시키기 위해 entity 객체를 보내준다.
@Slf4j
@Transactional
@Repository //스프링 빈으로 등록한다.
public class UserRepository {
@PersistenceContext
private EntityManager em;
/**
* 회원 데이터 저장
* @param userEntity
*/
public void save(UserEntity userEntity) {
em.persist(userEntity);
}
/**
* UserService에서 회원 가입 시 ID 중복 체크
* @param userId
* @return
*/
public List<UserEntity> findId(String userId) {
log.info("레포지토리에서 이메일 조회 시작");
log.info("입력한 이메일 값 ={}", userId);
log.info("체크 된 이메일 값 = {}", em.createQuery("select m from UserEntity m where m.userId = :userId", UserEntity.class));
log.info("레포지토리에서 이메일 조회 끝");
return em.createQuery("select m from UserEntity m where m.userId = :userId", UserEntity.class)
.setParameter("userId", userId)
.getResultList();
}
/**
* UserService에서 회원 가입 시 Email 중복 체크
* @param userEmail
* @return
*/
public List<UserEntity> findByEmail(String userEmail) {
log.info("레포지토리에서 이메일 조회 시작");
log.info("입력한 이메일 값 ={}", userEmail);
log.info("체크 된 이메일 값 = {}", em.createQuery("select m from UserEntity m where m.userEmail = :userEmail", UserEntity.class));
log.info("레포지토리에서 이메일 조회 끝");
return em.createQuery("select m from UserEntity m where m.userEmail = :userEmail", UserEntity.class)
.setParameter("userEmail", userEmail)
.getResultList();
}
}
EntityManager를 통해 로직을 만들어 주었다. JpaRepository<>를 사용하면 좀 더 편하게 할 수 있지만 JPA를 사용해 보지 못 했기 때문에 자세히 알고 가려면 순수 JPA를 사용해 보는 게 더 좋다고 해서 이렇게 해 봤다.
맨 위에는 일단 저장 로직을 만들었다.
회원 ID 체크와 Email 체크는 JPQL을 사용했다. find()를 통해 조회하려고 했는데 pk값이 아니면 조회가 안 되는 것 같다.
이렇게 되면 회원 가입이 완료된다. DB에 값도 정상 저장되고 이메일과 아이디가 중복되지 않는다.
'Project > 소경관' 카테고리의 다른 글
[소경관] : Session 적용하기 (0) | 2022.04.17 |
---|---|
[소경관] : thymeleaf와 JPA, builder 패턴을 사용하여 로그인 구현하기 (0) | 2022.04.15 |
[소경관] : builder에 값을 넣는 다른 방법 (0) | 2022.04.13 |
[소경관] : @Entity를 @Setter없이 View에서 Controller로 값을 받아와 @Builder를 통해 DB에 넣기 (0) | 2022.04.12 |
[소경관] : 지옥의 Builder (0) | 2022.04.11 |
댓글