이 글은 복잡한 서비스 클래스를 리팩토링하면서 퍼사드(Facade) 패턴을 적용한 경험을 공유합니다.
기존 코드
public class UserService {
public void registerUser(String email, String password) {
if(email != null && email.contains("@")){
throw new ValidationException();
}
if(password != null && password.length() > 6){
throw new ValidationException();
}
// 기타 유효성 체크 ...
// register 로직...
}
}
원래의 서비스 클래스는 약 1000줄에 달했고, 특히 유효성 검사 로직이 길고 복잡했습니다.
이 로직을 각각의 메서드로 분리한 후, 여러 유효성 검사 메서드를 하나의 `Validation` 클래스로 정리하여 책임과 역할을 명확히 분리하고자 했습니다.
개선 코드
public class UserValidation {
public boolean validateEmail(String email) {
return email != null && email.contains("@");
}
public boolean validatePassword(String password) {
return password != null && password.length() > 6;
}
}
@RequiredArgsConstructor
public class UserService {
private final UserValidation userValidation;
public void registerUser(String email, String password) {
if(userValidation.validateEmail(email)){ // service 클래스에 있던 유효성 체크 로직들을 UserValidation으로 분리
// exception
}
if(userValidation.vaildatePassword(password){ // service 클래스에 있던 유효성 체크 로직들을 UserValidation으로 분리
// exception
}
// register 로직...
}
}
그러나 이렇게 분리한 후에도 `Validation` 클래스의 메서드를 여전히 서비스 클래스에서 직접 호출해야 했기 때문에, 유효성 검사가 추가될 때마다 서비스 클래스도 변경해야 하는 문제가 있었습니다.
이 문제를 해결하기 위해 `Validation` 클래스 내에서 모든 유효성 검사 메서드를 호출하는 통합 메서드를 만들었습니다.
이로써 서비스 클래스는 이 통합 메서드만을 호출함으로써 필요한 모든 유효성 검사를 수행할 수 있게 되었습니다.
public class UserValidation {
public boolean validateUser(String email, String password) { // 하나의 메서드로 유효성 검사
return validateEmail(email) && validatePassword(password);
}
public boolean validateEmail(String email) {
return email != null && email.contains("@");
}
public boolean validatePassword(String password) {
return password != null && password.length() > 6;
}
}
@RequiredArgsConstructor
public class UserService {
private final UserValidation userValidation;
public void registerUser(String email, String password) { // service에서 하나의 메소드 호출만으로 유효성 검사 진행
if(userValidation.validateUser(email, password)){
// exception
}
// register 로직...
}
}
이후 코드 리뷰 과정에서 동료의 제안으로 유효성 검사 메서드들을 더욱 적절하게 분리할 수 있는 가능성을 발견했습니다.
구체적으로는, 유효성 검사 자체를 수행하는 메서드와 여러 검사를 조합하는 메서드를 분리하는 것이었습니다.
이 분리를 통해 한 클래스는 유효성 검사만을 담당하고, 다른 클래스는 이러한 검사들을 조합하는 역할만을 맡게 되었습니다.
후자의 역할을 수행하는 클래스에서 적용된 패턴이 바로 퍼사드 패턴입니다.
public class UserValidation {
public boolean validateEmail(String email) {
return email != null && email.contains("@");
}
public boolean validatePassword(String password) {
return password != null && password.length() > 6;
}
}
public class ValidationFacade {
private UserValidation userValidation;
public ValidationFacade() {
this.userValidation = new UserValidation();
}
public boolean validateUser(String email, String password) { // 어떤 유효성이 필요한지 조립만 하는 역할을 수행함
return userValidation.validateEmail(email) && userValidation.validatePassword(password);
}
}
public class UserService {
private ValidationFacade validationFacade;
public UserService() {
this.validationFacade = new ValidationFacade();
}
public void registerUser(String email, String password) {
if (validationFacade.validateUser(email, password)) {
System.out.println("User registered successfully.");
// 등록 로직 구현 (예: 데이터베이스에 저장)
} else {
System.out.println("Validation failed. User not registered.");
}
}
}
퍼사드 패턴을 적용함으로써, 각 클래스는 한 가지 역할만을 수행하게 되어 객체지향의 원칙에 더욱 부합하게 되었습니다.
또한, 이 패턴은 서비스 레이어에서 비즈니스 로직을 개별적으로 구현하고, 이를 조립하는 클래스를 별도로 만드는 방식으로 확장될 수 있습니다. 컨트롤러에서는 이 퍼사드 클래스만을 호출함으로써, 더 깔끔하고 관리하기 쉬운 코드 구조를 갖출 수 있습니다.