관심쟁이 영호

[Spring Boot] Form 객체를 분리하여 검증 로직 다르게 적용 본문

카테고리 없음

[Spring Boot] Form 객체를 분리하여 검증 로직 다르게 적용

관심쟁이 영호 2021. 8. 3. 15:38
반응형

이번 기록은 Form 객체를 분리를 통해서 검증 로직을 서로 다르게 적용하는 것이다.

 

이 말이 무슨 뜻인가!?

다음과 같은 상황으로 이해해보자.


"회원 가입 시에는 id의 길이가 최대 10, 회원 정보 수정에는 id의 길이가 최대 15로 수정이 가능하게 해 주세요."

 

위와 같은 상황에 Form 객체를 분리하여 검증 로직을 다르게 적용한다는 말이다!

 


목차

  • Form 객체 분리란?
  • Form 객체 분리하기
  • 분리에 따른 후 처리
  • 번외 - 겪은 에러

 

Form 객체 분리란?

 

Form 객체로 분리한다는 말이 무슨 뜻일까?

코드로 살펴보자.

 

User.java

@Data
@Entity(name = "user")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    Long sequenceId;


    @NotNull
    @NotBlank
    @Column(length = 25, nullable = false)
    String userId;

    @NotNull
    @NotBlank
    @Column(nullable = false)
    String password;

};

 

SignupController.java

@PostMapping("/signup")
    public String signupForm(@Validated @ModelAttribute User user, BindingResult bindingResult, RedirectAttributes redirectAttributes){

        log.info("userid ={}", user.getUserId());
        if(bindingResult.hasErrors()){
            log.info("errors ={}", bindingResult);
            return "/signup";
        }

        signUpService.signup(user);

        return "redirect:/";
    }

 

이전에는 Post Mapping에서 user라는 객체를 사용한 것을 볼 수 있다.

이제는 user를 사용하지 않고 user를 가지고 있는

User sign Up Form, User Modify Form 객체로 나누어

따로따로 어노테이션을 설정할 것이다.

 


Form 객체 분리하기

 

두 가지의 객체를 생성해보자!

먼저 검증 로직은 처음에 있던


"회원 가입 시에는 id의 길이가 10, 회원 정보 수정에는 id의 길이 15로 수정이 가능하게 해 주세요."

을 적용해보자!

 

UserSignUpForm.java

@Data
public class UserModifyForm {

    String beforeId;
    String beforePassword;

    @NotNull
    @NotBlank
    @Size(max = 15)
    String userId;

    @NotNull
    @NotBlank
    String password;
};

 

UserModifyForm.java

@Data
public class UserSignUpForm {

    @NotNull
    @NotBlank
    @Size(max=10)
    String userId;

    @NotNull
    @NotBlank
    String password;
};

 

코드 설명 :

User객체를 UserModifyForm과 UserSignUpForm객체로 나눈 이유는, 각각 다른 검증을 해주기 위해서이다.

Group을 사용하여 묶을 수도 있지만 그거 또한 한계가 있어서 차라리 관리하기 쉽게 두 가지의 폼으로 나누어 관리하자!

 

먼저 JPA에 사용될 어노테이션은 모두 제거해주었다.

그리고 회원 가입과 회원 수정에 각각 맞는 검증 어노테이션을 추가했다. (max=10, max=15)

 


분리에 따른 후처리

 

분리가 되었으니 제각각 코드를 바꿔주어야 할 것이다!

먼저 어떤 코드를 바꿔야 할지 생각해보자.

 

  1. Controller에 넘어오는 @ModelAttribute 부분의 객체를 바꿔주어야 한다.
  2. Service 부분에서 Form객체로 되어있는 값을 User 객체로 변환해주어야 한다.

이렇게 두 가지의 작업이 추가되었을 것이다.

 

컨트롤러와 서비스를 보자!

 

SignUpController

@Controller
@Slf4j
@RequiredArgsConstructor
public class SignUpContoller {


    @Autowired
    private final SignUpService signUpService;

    @GetMapping("/signup")
    public String signup(Model model){

        model.addAttribute("user", new User());

        return "/signup";
    }

    @PostMapping("/signup")
    public String signupForm(@Validated @ModelAttribute UserSignUpForm user, BindingResult bindingResult, RedirectAttributes redirectAttributes){

        log.info("userid ={}", user.getUserId());
        if(bindingResult.hasErrors()){
            log.info("errors ={}", bindingResult);
            return "/signup";
        }

        User user2 = new User();
        user2.setUserId(user.getUserId());
        user2.setPassword(user.getPassword());
        signUpService.signup(user2);
        return "redirect:/";
    }

};

 

ModifyUserController

@Controller
@RequiredArgsConstructor
@Slf4j
public class ModifyUserController {

    private final ModifyUserService modifyUserService;

    @GetMapping("/modify")
    public String ModifyUser(){return "/modify";}

    @PostMapping("/modify")
    public String ModifyUser(@Validated @ModelAttribute UserModifyForm userModifyForm, BindingResult bindingResult){
        if(bindingResult.hasErrors()){
            return "/modify";
        }
        modifyUserService.modifyUser(userModifyForm);
        return "redirect:/";

    }

};

 

컨트롤러 코드 설명:

  • signup에서는 UserSignUpForm 객체가 넘어온다.
  • modify에서는 UserModifyForm 객체가 넘어온다.

다른 점이 두 가지가 있는데 signup은 controller에서 user로 변환하였고, modify는 서비스에서 user로 변환을 해주었다!

당연히 서비스에서 하는 것이 올바르겠지!?

 

그럼 각각의 서비스를 보자!

 

SignUpService

@Service
@RequiredArgsConstructor
public class SignUpService {

    @Autowired
    private final UserRepository userRepository;

    public void signup(User user){
        userRepository.save(user);
    }
};

 

ModifyUserService

@Service
@RequiredArgsConstructor
@Slf4j
public class ModifyUserService {

    private final UserRepository userRepository;

    public void modifyUser(UserModifyForm userModifyForm){

       User user = userRepository.finduser(userModifyForm.getBeforeId(), userModifyForm.getBeforePassword());

       user.setUserId(userModifyForm.getUserId());
       user.setPassword(userModifyForm.getPassword());

        userRepository.save(user);

    }
};

 

이제 완벽히 수행되는 것을 볼 수 있었다.

 


번외 - 겪은 문제

 

1. JPA와 연동하면서 JPA Repository에 구현되어 있는 내용을 보니까 Long id(PK)로 getOne, findById가 수행되는 것을 볼 수 있었다.

 

사용자들은 분명 자신의 id와 password를 입력할 건데, "JpaRepository"의 구현체에는 sequence로 등록된 id만 인자로 받는다.. 그럼 어떻게 조회를 해야 하나 ㅠ

 

일단은 아래와 같이 인터페이스를 확장했다.

 

public interface UserRepository extends JpaRepository<User, Long> {
    @Query("select m from user m where m.userId = ?1")
    User finduser(String id, String password);
};

 

해결 :

Spring Data JPA를 사용하면 정해진 규칙대로 인터페이스에 등록하면 간단한 쿼리문은 자동 구현된다!

해당 구현 메서드명 규칙은 아래 글을 참고하자!

https://minkwon4.tistory.com/130

 

[JPA] Spring Data JPA (2) - 메소드 네임 쿼리, @Query

메소드 네임 쿼리 JPA는 기본적으로 JpaRepository<엔티티 타입, 식별자(PK)타입>을 상속 받음으로써 기본적인 CRUD를 할 수 있는 쿼리를 제공한다. 또한 이것과 별게로 추가적으로 규칙에 의한 메소드

minkwon4.tistory.com

 

그래서 변경된 코드!

public interface UserRepository extends JpaRepository<User, Long> {

     //@Query("select m from user m where m.userId = ?1")
     //User finduser(String id, String password);


    User findByUserId(String id);
};
public void modifyUser(UserModifyForm userModifyForm){

       //User user = userRepository.finduser(userModifyForm.getBeforeId(), userModifyForm.getBeforePassword());
       User user = userRepository.findByUserId(userModifyForm.getBeforeId());
       user.setUserId(userModifyForm.getUserId());
       user.setPassword(userModifyForm.getPassword());

        userRepository.save(user);

    }

 

300x250
Comments