업데이트:

📌 모든 소스는 Github에 있습니다.

예전 학원에 다녔을때와 SI 회사에 근무했을때는 Getter/Setter를 써야 한다고 배웠고, 의심없이 사용하고 있었습니다. 하지만 이직을 하면서, 그리고 공부를 하면서 Setter를 지양한다고 배웠고, 그 이유에 대해 생각해봤습니다.

Setter의 존재 이유?

이전에 Setter를 사용하던 이유를 생각해봤을때, 객체의 상태 값을 변경하고 로직에 사용, DB에 저장하기 위해서였습니다. 제약 없이 객체의 상태값을 변경할 수 있고, 직관적(?)이기 때문에 관용적으로 써왔던 것 같습니다. (주로 SI 회사에서 많이 그랬을 겁니다.)

정말 필요한가?

보통 절차 지향 프로그래밍 방식으로 로직을 짜게된다면 Setter가 필요할 수 밖에 없습니다. 객체 지향 프로그래밍 방식으로 객체에 로직을 심어야만 Setter를 제거 할 수 있습니다.

절차 지향 프로그래밍 방식

예시로 다음과 같이 Postupdate하는 로직을 Setter사용하여 코드를 작성해보았습니다. 해당 코드는 Service Layer에 존재합니다.

@Transactional
public PostResponseDto updateWithSetter(PostRequestDto requestDto) {
    Post post = postRepository.findById(requestDto.getId())
        .orElseThrow(() -> new IllegalArgumentException("해당 게시글이 없습니다. id = " + requestDto.getId()));

    post.setTitle(requestDto.getTitle());
    post.setContent(requestDto.getContent());

    return post.toDto();
}

위의 코드를 보면 Setter를 사용하게 되면 Post에 대한 상태값 변경의 의도를 확실하게 알 수 없습니다. Service Layer에서 Post에 대한 상태 값 변경 책임을 지기 때문입니다. 또한, 개발자가 실수할 확률이 높아집니다. 제 의도는 TitleContent만 변경하고자 하였는데, Setter를 사용하면 다른 필드를 변경하는 Setter를 호출하는 실수를 할 수 있습니다. (여담이지만 절차 지향 프로그래밍 방식에선 가독성도 안좋고, 메소드 분리도 어렵기 마련입니다. 지금이야 Title, Content만 업데이트하는 것이라 단순하지만 로직이 길어지면 길어질수록 가독성이 떨어질 것이고, 메소드가 분리가 되지 않아 유지보수 측면에서 많은 손실이 일어날 수 있습니다. Java를 쓰는 이상 최대한 객체 지향 프로그래밍 방식으로 코드를 작성해야 하지 않을까요?)

객체 지향 프로그래밍 방식

다음은 객체 지향 프로그래밍으로 Setter를 사용하지 않고 코드를 작성해보았습니다. 해당 코드 역시 Service Layer에 존재합니다.

@Transactional
public PostResponseDto updateNoSetter(PostRequestDto requestDto) {
    Post post = postRepository.findById(requestDto.getId())
        .orElseThrow(() -> new IllegalArgumentException("해당 게시글이 없습니다. id = " + requestDto.getId()));

    return post.update(requestDto.getTitle(), requestDto.getContent());
}

위의 코드를 보면 Post에서 Instance 본인의 상태 값을 변경합니다. Post 본인 객체의 상태 값에 대한 책임을 Service가 아닌 본인이 지니게 됩니다. Service는 단순히 Postupdate() 메소드를 호출할 뿐입니다. 이렇게 되면 필요한 값을 넘겨주는 형태가 되기 때문에 의미없는 Setter를 호출할 필요도 없을 뿐더러, 메소드의 네이밍에 따라 Post 상태 값 변경에 대한 의도가 확실해집니다. 또한, 필요한 값을 확실하게 알 수 있기 때문에 다른 상태 값을 변경할 위험도 없게 됩니다.

객체 초기화

아직도 많은 회사에서 Instance를 생성하고 Setter로 객체의 값을 초기화하는 경우가 많습니다. 위와는 반대로 필수인 값을 변경하는 Setter를 빼먹었다면 결국 버그로 발생할 수 밖에 없을 것입니다.

@Transactional
public PostResponseDto insertPost(PostRequestDto requestDto) {
    Post post = new Post();
    post.setTitle(requestDto.getTitle());
    post.setContent(requestDto.getContent());

    /* Author 세팅을 빼먹었다면?? */
    // post.setAuthor(requestDto.getAuthor());

    return new PostResponseDto(postRepository.save(post));
}

따라서 생성자로 필수값을 지정하여 컴파일 시점에 에러를 잡도록 해야 하고, 나아가 Builder 패턴, Static Factory Method 등을 사용하여 좀 더 코드를 우아하게 만들 수 있을 것입니다.

객체 생성 메소드에 대한 내용은 이전 포스트에 작성한 내용을 참고하시기 바랍니다.

💡객체 생성 메소드

외부에 의해 변경하면 안되는 값

다음 블로그에서 좋은 내용이 있어 참고했습니다. (강아지의 코딩공부)

자세한 내용은 위의 블로그를 들어가서 읽어보시기 바랍니다.

간단하게 말하면 ArrayList에는 getSize() 메소드는 있는 반면 setSize() 메소드는 없습니다.

그 이유는 결국 ArrayListSize를 내부에 의한 값이 아닌 외부에 의한 변경을 막아 놓은 것인데, 만약 외부에서 변경하도록 한다면 심각한 버그를 야기할 것이기 때문입니다. 실제로 ArrayList에 들어가 있는 Element의 갯수는 5개 인데, setSize()로 10개로 조정한다? 큰일 날 소리죠.

마무리

위와 같은 이유로 Setter는 사용을 지양해야 한다고 생각합니다. 근데 또 많은 회사에서 절차 지향 프로그래밍 방식으로 코딩하고 있다면 결국 Setter를 쓰고 있을 것이고, 수많은 로직에 녹아있는 Setter를 리팩토링하는 것은 쉽지 않은 선택일 것입니다. 하지만 이는 결국 기술 부채가 될 것이고 개발조직의 발전에 나쁜 영향을 끼칠 것입니다.

모든 회사가 고민인 내용이겠지만 언젠가는 기술 부채를 해결해야하는 시점이 올 것이고, 빠르면 빠를 수록 좋다고 생각합니다.

오늘도 포스팅 읽어 주신 분들 감사하고, 도움이 되셨길 바랍니다.

크리스마스가 지나고 벌써 2021년이 마무리되어가고 있네요. 한 살 더 먹게 되는데, 실감이 안나네요. 전 아직도 어린거 같은데 ㅋㅋㅋ

댓글남기기