리팩토링(Refactoring)
오늘의 TIL은 스프링을 이용한 간단한 Timline 게시판을 구현하는데 있어 내가 작성한 코드를 리팩토링 해보았다.
Controller에서 넘어온 id값에 해당되는 비밀번호를 데이터에 저장되어있는 비밀번호와 대조하여 일치하면 true를 불일치하면 false값을 배출하게 하는 기능을 Service 클래스에서 구현을 하였다.
이 기능이 update와 delete시 모두 필요한 기능이였기에 처음 코드를 작성할 때는 필요한 두 기능이 동일한데 두 가지를 분리하여 아래와 같이 작성을 하였었다.
public class BlogService {
private final BlogRepository blogRepository;
@Transactional //이게 업데이트될 때 DB에 정말 반영이 되야된다고 말해줌
public boolean update(Long id, BlogRequestDto requestDto) { //boolean 타입으로 변환시켜 밑에서 true or false값을 반환
Blog blog = blogRepository.findById(id).orElseThrow( //할 수 있게 함
() -> new IllegalArgumentException("아이디가 존재하지 않습니다.")
);
if (!blog.getPassword().equals(requestDto.getPassword())) { // ! equals 를 사용하자!! // !== 은 먹히지 않음
System.out.println("비밀번호가 일치하지 않습니다."); //repository에 저장된 값이 requestDto(수정된 값)과 다를 때
return false;
} else {
System.out.println("수정완료!");
blog.update(requestDto);
return true;
}
}
@Transactional
public boolean delete(Long id, BlogRequestDto requestDto) {
Blog blog = blogRepository.findById(id).orElseThrow(
() -> new IllegalArgumentException("아이디가 존재하지 않습니다.")
);
if (!blog.getPassword().equals(requestDto.getPassword())) {
System.out.println("비밀번호가 일치하지 않습니다.");
return false;
} else {
System.out.println("삭제완료!");
blogRepository.deleteById(id);
return true;
}
}
}
위 메서드는 먼저 id값에 따른 Repository를 찾고 해당 아이디가 없을 때의 예외 처리를 해준 후, 데이터에 저장되어 있는 비밀번호와 사용자쪽에서 입력한 비밀번호가 담긴 DTO의 비밀번호와 대조하여 맞으면 true 틀리면 false를 반환하는 구조이다.
그런데 이렇게 동일한 기능을 필요로 하는데 위 처럼 작성을 하였을 시, 나중에 유지보수를 할 때 만약 서로 다른 기능을 필요로 하게 될 때는 아니지만 안쪽의 기능을 동일하게 바꿔줘야 한다면 똑같은 일을 두 번 반복해야 한다.
그러한 측면을 고려하였을 때 위 코드는 효율성이 떨어지는 것 같다...
기술멘토님의 조언을 받아 동일한 기능을 필요로 하는 것은 따로 메서드를 분리하여 구연해놓고 delete 메서드나 update 메서드에서 끌어다 쓰는 방식이 적절하다는 것을 알게 되었다.
@Transactional //이게 업데이트될 때 DB에 정말 반영이 되야된다고 말해줌
public boolean update(Long id, BlogRequestDto requestDto) { //boolean 타입으로 변환시켜 밑에서 true or false값을 반환
Blog blog = testMethod(id, requestDto.getPassword());
if (blog == null) {
System.out.println("비밀번호가 일치하지 않습니다");
return false;
} else {
System.out.println("수정완료");
blog.update(requestDto);
return true;
}
}
@Transactional
public boolean delete(Long id, BlogRequestDto requestDto) {
Blog blog = testMethod(id, requestDto.getPassword());
if (blog == null) {
System.out.println("비밀번호가 일치하지 않습니다");
return false;
} else {
System.out.println("삭제완료");
blogRepository.deleteById(id);
return true;
}
}
public Blog testMethod(Long id, String password) {
Blog blog = blogRepository.findById(id).orElseThrow(
() -> new NullPointerException("아이디가 존재하지 않습니다")
);
if (!blog.getPassword().equals(password)) {
return null;
} else {
return blog;
}
}
위 코드는 update와 delete시 동일하게 필요한 비밀번호 확인 기능을 testMethod라는 메서드를 따로 만들어 구현을 해두고 update와 delete 메서드에서 끌어다 쓰는 방식이다.
이렇게 만들어놓으면 결과 값은 동일하고 나중에 수정이 필요할 때 testMethod 안에서 해결을 하거나 기능이 서로 달라질때는 한곳에서만 수정을 하면 되기 때문에 좀 더 확장성 측면에서도 좋은 코드가 된다.
코드를 작성하면서 이러한 것들을 고려하여 작성하면 더할나위 없겠지만 우선 기능을 다 구현해놓고 리팩토링을 통해 좀 더 나은 코드로 변환시키는 것도 정말 중요한 과정인 것 같다는 생각이 드는 하루였다!