본문 바로가기

개발 고민

단일 책임 원칙과 클린코드 V2

해당 게시글은 과거 개인 블로그에 작성한 내용을 옮긴 내용입니다.
https://tech.dpot.xyz/post/52

알죠..? 알면서..  (출처:https://www.huffingtonpost.kr/entry/solid_kr_5a9df0e2e4b0479c02564332)

 

개발하며 다른 개발자와 협업하는 상황이 많이 온다. 서비스의 기획, 설계를 함께하면 상관없어도, 프로젝트 중간에 개발자가 참여하면 서비스의 전반적 이해를 위해 적지 않은 시간이 필요하다.

이러한 상황에서 SOLID의 S인 단일 책임 원칙에 준수하여 작성한 소스는 빛난다. 실무에서 개발하며, 느낀 단일책임원칙을 준수하기 위한 간단한 규칙을 정리해 보려 한다.


 

1. Setter 금지

 

생각해 보면 Lombok 찬양론을 말하던 개발자였다. @Data하나면 모든 것을 만들어주던 라이브러리의 문제는 생각하지도 않고 사용했던 것부터 클린코드와 이별한 듯하다.

JPA와 lombok을 같이 사용할 경우 영속성이 유지된 엔티티에서 update쿼리를 위해 set을 무차별적으로 사용하거나 insert를 위해 서비스단에서 여러 번의 setter을 사용한다.

 

예시1) 책을 빌리면 빌렸다고 상태를 바꿔야 해요
public void rent01() {
    BookRent bookRent = new BookRent();
    bookRent.setRentDateTime(LocalDateTime.now());
    bookRent.setIsRent(true);
}

 

예시2)책임은 클래스가 서비스는 호출만
public void rent02() {
    BookRent bookRent = new BookRent();
    bookRent.rent();
}
public class BookRent {
    @Id
    private Long id;

    @Column
    private LocalDateTime rentDateTime;

    @Column
    private Boolean isRent;

    public BookRent rent(){
        this.rentDateTime = LocalDateTime.now();
        this.isRent = true;

        return this;
    }
}

 

위 2개의 소스를 보면 더욱 이해가 쉽다. 예시 1번의 경우 새로 프로젝트에 참여한 개발자에게 책을 대여할 때 시간과 상태를 바꿔야 한다고 말해야 한다. 하지만, 예시 2번을 보면 책을 대여하는 메소드를 알려주면 끝난다.

당연히, 값의 오류는 방지되고 수정이 필요한 값만 설정할 수 있다. 조금 생각해보면, id 칼럼 역시 jpa가 persist될때 자동으로 세팅 되며, 개발자는 수정할 일이 없다. 하지만, @Setter는 잘못 사용하면 id도 변경 가능한 것처럼 보여주기에 자칫 원치 않는 상황을 만들 수 있다.


 

2. DTO에서는 다양한 생성자 사용

 

DTO세팅의 책임도 DTO가 가져가도록.
public class BookInfoDto {
    private String bookName;

    private LocalDateTime createdAt;

    private Boolean rentAble = false;

    public BookInfoDto(Book book) {
        this.bookName = book.getBookName();
        this.createdAt = book.getCreatedAt();

        List<BookRent> bookRentList = book.getBookRentList();
        if(!bookRentList.isEmpty()){
            this.rentAble = true;
        }
    }

}

 

서비스단에서 DTO를 만들고 값을 넣을 때도 set보다 생성자를 쓰길 권장한다. 또한, 다양한 객체를 받는 생성자를 만들어서 어떠한 값을 받아도 DTO안에서 모든 변환 로직을 관리 할 수 있다.

이 방법을 통해 혹시라도 여러 서비스에서 DTO를 사용할 수 있으며, DTO의 값이 옳바르지 않음을 보장한다.


 

3. validation을 통한 올바른 값만 받기

 

Validation은 생각보다 강력합니다.
@JsonProperty('repeatDay')
@Pattern(regexp = '[1-7](\\|[1-7]{1}){0,6}')
@Schema(description = '반복 요일. 1~7: 월~일', example = '1|2|3|4|5|6|7')
private String repeatDayStr;//반복 요일

@JsonProperty('startDate')
@DateTimeFormat(pattern = 'yyyy-MM-dd')
@Schema(description = '시작 일자', example = 'YYYY-MM-DD')
private LocalDate startDate;//시작 일자

 

종종 client가 이상한 값을 보낼 때가 존재한다. 이상한 값의 정의는 다음과 같다. 날짜를 받아야 하는데 이상한 형식으로 주기도 하고 새로운 client 개발자는 Long 타입의 id 대신 string 0을 주는 일도 있다. 물론, 서비스단에서 이러한 값이 틀렸음을 예외 처리하면 된다. 하지만 서비스에서는 서비스 로직의 문제만 예외 처리해야 한다고 생각하기에 틀린 값이 아닌 이상한 값은 아예 안 받는 것을 고려해 볼 수 있다.

더욱이, Validation은 다양한 어노테이션을 지원한다. 가볍게는 숫자 타입의 변수 확인부터 필자가 많이 사용하는 @Pattern까지 어지간한 값은 모두 검사할 수 있다. Validation을 통해 컨트롤러단에 들어온 요청의 값은 틀릴 수 있어도, 이상할 수 없음이 보장되었다.


 

Spring Boot로 개발하며 의존성 역전은 autowired로, 인터페이스 분리 원칙은 Repository를 만들며, OCP와 LSP는 엔티티에서 공통 컬럼을 분리하면 자연스럽게 적용하게 된다. 하지만 SRP (단일 책임 원칙)은 자연스럽게 되는 것이 아닌 신경을 써야 준수하게 되는 원칙인 것 같다.

물론, 서비스단에서 보기에 너무 소스가 흩뿌려진다고 할 수 있지만, 유지보수를 하거나 기획이 변경되었을 때 변경된 객체만 건드리면 되니 단일 책임 원칙을 준수하며 코딩하는 습관을 기르는 것이 좋다.

 

 

 

'개발 고민' 카테고리의 다른 글

JPA IdentifierGenerator의 connection에 대해..  (0) 2023.09.04
MultipartFile 업로드에 관하여  (0) 2023.05.30