인생을 코딩하다.

[Kotlin, Java] 유지보수하기 좋은 코드란? 및 확장성을 고려한 설계 본문

Kotlin

[Kotlin, Java] 유지보수하기 좋은 코드란? 및 확장성을 고려한 설계

Hyung1 2021. 6. 9. 03:37
728x90
반응형

 @Transactional 
 fun saveUser(userDto: UserDto) { 
 	// 이메일 중복체크 예외처리 로직 
 	if (userRepository.existsByEmail(userDto.email)) { 
    		throw EmailDuplicateException() 
    } 
   
   ... 중간 생략 
   
   userMapper.save(user.toUserEntity()) 
}

위의 코드에서 saveUser메서드는 회원가입을 하는 API다.

이 메서드에는 이메일 중복검사에 관한 기능과 회원 정보를 저장하여 회원가입을 하는 기능이 있다. 또한 위에서 보이는 로직 말고도 추 후 변경이 생겨 다른 로직들이 더 추가 될 수 있다.

서비스에서 회원가입 기능의 흐름상 이메일 중복검사 기능은 어떤식으로든 들어가야 한다. 또 변경이 생겨 다른 기능들도 어떤식으로든 들어가야 할 수 있다. 그래서 saveUser 메서드에서는 위의 코드처럼 구현할 수 있다.

하지만 위의 코드는 문제점이 있다.

만약 이메일 중복체크하는 로직이나 회원정보를 save 로직의 수정이 발생한다면?

위 API는 수정 및 변경이 필요할 때

  • 수정이 필요할 때 한 메서드에서 수정할게 두 가지나 있다. 두 가지의 책임을 가지고 있다.
  • 그에 따라 협업시에 변경이 필요할 때 수정 대상이 명확하지 않다. saveUser 메소드의 로직을 모두 일일이 보고 어떤 것을 수정해야 할지 찾아야 한다.
  • 현재는 User 계정만 있지만 추후 Admin 계정도 구현한다고 했을 때, saveAdmin에도 중복된 로직이 포함된다.

위와 같은 문제점들이 있다.

사실 공부를 하면서 유지보수하기 좋은 코드란 뭘까? 다형성은 우리에게 어떤 장점을 줄까? 를 항상 고민했었다.

위의 코드처럼 구현한 것은 아니였지만 위의 코드처럼 구현했을때 위에서 설명한 문제점들이 생길 것 같다는 생각이 들었기에 아래처럼 구현했다.

 @Transactional 
 fun saveUser(userDto: UserDto) { 
 	emailDuplicateCheck(userDto.email) 
    
    ... 중간 생략 
    
    userMapper.save(user.toUserEntity())
} 

// 메서드로 분리    
fun emailDuplicateCheck(email: String) {
	if (userRepository.existsByEmail(email)) {
		throw EmailDuplicateException() 
       }     
   }

emailDuplicateCheck 메서드를 분리해놓으면 어떤 변경이 필요할 때 수정 대상이 명확해진다고 생각한다.

 

위의 코드를 기반으로 더 구체적인 예를 들어볼까? emailDuplicateCheck 메서드에는 EmailDuplicateException() 예외를 던지는 로직이 담겨 있다. 만약 회원가입 진행 중 이메일 중복 확인의 예외를 던지는 부분에 관한여만 대하여 수정이 필요하다고 가정해보자.


emailDuplicateCheck 메서드를 따로 분리한 후, saveUser 메서드가 아닌 emailDuplicateCheck 메서드에서 예외를 던지는 로직을 수정을 하는것이 훨씬 유지보수 하기가 싶다.

  • emailDuplicateCheck()라는 명확한 이름을 가진 메소드가 존재하기 때문에
  • 더불어 saveUser() 메서드의 내부를 몰라도 emailDuplicateCheck() 메소드만 알고있으면 되기에

협업시에 변경이 필요할 때 수정 대상이 명확해진다. 따라서 유지보수 하는데 걸리는 시간을 줄여주고, 관심사가 더욱 분리된다.

 

즉, 더욱 객체 지향적인 코드가 된다. 더욱 객체지향 적인 코드로 리팩토링한다는건 더욱 유지보수 하기 쉽게 리팩토링한다는 것을 의미한다.

또한 현재는 Admin계정이 없지만 추후에 프로젝트를 확장해서 User가 아닌 Admin으로 회원가입을 한다고 할 때, 이메일 중복 체크를 인터페이스로 분리하여 default 메서드로 만들면

  • saveAdmin에서 오버라이딩 하여 가져다 쓰기만 하면 되기때문에 확장성을 고려한 설계도 가능해진다.
  • 또한 default 메서드로 선언하여 body에 이메일 중복 체크 로직을 가지게 하기 때문에 오버라이딩 해서 다시 로직을 구현하는 일 없이 중복도 줄일 수 있다. 
interface UserAndAdminService {
	fun duplicateEmailCheck(email: String) {
    		if (userMapper.isExistsByEmail(email)) { 
      		  	throw DuplicateEmailException() 
            } 
        }
}        

그럼 saveUser 메서드에서 혹은 saveAdmin메서드 에서는 duplicateEmailCheck 메서드를 오버라이딩하여duplicateEmailCheck 메서드의 파라미터에 필요한 role만 주입해서 갈아끼우면 된다.

 @Transactional 
 fun adminUser(adminDto: AdminDto) : UserAndAdminService { 
 	super.duplicateEmailCheck(adminDto.email)
    
    ... 중간 생략 
    
    userMapper.save(user.toUserEntity())
} 


위처럼 유지보수에 좋은 코드와 더불어 부품화하여 갈아낄 수 있는 다형성을 적용해서 확장이 가능하고, 변경에 유연한 설계를 지향하여 만들 수 있다고 생각한다.

728x90
반응형
Comments