안녕하세요. 성조입니다.
오늘은 객체지향 설계 5대 원칙인 SOILD에 대해서 정리해 보려 합니다. 또한, 원칙 중 S인 SRP에 대해서 포스팅해 보려 합니다.
자바 코드를 활용해서 SRP를 설명하는 것이므로 Java 카테고리에 들어오게 됐습니다.
올바르지 못한 지식 전달이 있다면 언제든지 댓글 남겨주시면 감사드리겠습니다!
단일 책임 원칙(Single Responsibility Principle, SRP)이란?
클래스가 단 하나의 책임을 가져야 한다는 의미를 갖는다.
조금 더 풀어서 얘기하면 클래스를 변경해야 하는 이유가 발생하게 되면. 변경 이유는 하나만 있어야 한다.
즉, 클래스는 하나의 기능만을 관리하고, 그것에만 집중해야 한다는 의미이다.
만약 'A'라는 클래스가 B와 C라는 기능에 책임을 가지고 있을 때. A 클래스는 두 가지 이상의 기능에 대한 책임을 가지고 있다는 것이다.
A를 수정하면 내부에 있는 B와 C라는 기능을 고쳐야 한다는 것이다.
예를 들면, A 클래스가 [사용자 정보를 데이터베이스에 저장 기능, 사용자에게 이메일 통지하는 기능]라는 두 가지 보내는 기능이 A 클래스에 종속했을 경우. A 클래스는 두 가지에 대해서 책임지고 있다고 말한다.
만약 A 클래스 내부에 있는 '사용자 정보를 데이터 베이스 저장 기능' 기능의 로직이 변경됐을 때. B 기능 때문에 비즈니스 로직 변경을 위해서 A 클래스를 수정한다면 A 클래스로부터 같은 값을 공유받고 있는 '사용자에게 이메일 통지하는 기능' 또한 로직이 변경된다.
이러한 문제가 발생되는 것 때문에 A 클래스 내부에 B 기능이 변경되어도 C 기능에 영향을 주지 않기 위해서 독립적으로 변경되고, 확장할 수 있도록 만들기 위해서 SRP 원칙을 따르는 것이다.
[단일 책임 원칙(SRP)이 적용되지 않은 예시]
public class User {
private String name;
private String email;
// ... 기타 필드 및 생성자, getter, setter 생략 ...
public void saveUser() {
// 데이터베이스에 유저를 저장하는 로직
}
public void mailUser() {
// 유저에게 메일을 보내는 로직
}
}
만약 User 클래스의 변수 값이 변경되면 'saveUser'와 'mailUser'에 있는 값이 모두 영향을 받는 문제가 발생한다.
[설명]
처음에는 name이라는 이름으로 'saveUser()'와 'mailUser()'에 값을 공유했다. 시간이 지나서 'saveUser()'라는 기능에 비즈니스 로직이 변경되면서 name이라는 변수 대신 userName이라는 변수가 필요하게 됐다. 이때 'mailUser()'라는 기능 로직은 변경된 것이 없지만 User 클래스를 'saveUser()'와 공유하고 있기 때문에 'mailUser()' 또한 내부에서 name으로 사용되던 변수를 nameUser로 변경하여 사용해야 한다.
즉, B 기능이 변경됐다 해서 C 기능에 있는 변수들도 모두 변경해야 하는 문제가 발생하는데 이런 문제를 방지하기 위해서 단일 책임 원칙(SRP)을 활용하는 것이다.
[단일 책임 원칙(SRP)이 적용된 예시]
public class User {
private String name;
private String email;
// ... 기타 필드 및 생성자, getter, setter 생략 ...
}
public class UserDB {
public void saveUser(User user) {
// 데이터베이스에 유저를 저장하는 로직
}
}
public class UserMailer {
public void mailUser(User user) {
// 유저에게 메일을 보내는 로직
}
}
위와 같이 단일 책임 원칙을 지키는 경우 'User' 클래스에서 각각 따로 접근하면서 값을 변경해서 비즈니스 로직에서 사용하므로 기능 내부에서 'name'이 변경됐을 때. 다른 기능을 중복 수정시키는 문제를 발생시키지 않게 된다.
단일 책임 원칙(Single Responsibility Principle, SRP) 장점과 단점
[장점]
1. 유지 보수성 향상
각 클래스가 하나의 책임만을 가지고 있기 때문에 해당 책임과 관련된 변경이 발생하면 해당 클래스만 수정하면 되기 때문에 유지 보수성이 좋아진다.
2. 가독성 향상
각 클래스는 하나의 책임을 가지고 있기 때문에 코드를 읽는 사람이 해당 클래스가 만들어진 목적을 이해하기 더 쉬워진다. 그렇기 때문에 구현을 이해하기 위한. 코드 가독성이 향상된다.
3. 테스트 용이성
클래스가 하나의 책임만을 가지고 있으면, 그 클래스의 테스트 케이스를 작성하기 더 쉽다. 각 책임을 분리하기 때문에 테스트 케이스의 범위를 각 기능에 맞춰서 할 수 있고, 하나의 기능을 비즈니스 레벨까지 만들어내기 더 좋아지는 장점이다.
[단점]
1. 과도한 클래스 분리
단일 책임 원칙(SRP)을 지키기 위해서 너무나 많은 클래스를 가지고 있는 경우. 클래스 간의 관계를 명확하게 이해하는데 어려움을 겪을 수 있다.
소규모 개발팀의 경우 코드 규모가 비교적 작을 수 있고(물론 소규모 개발 팀도 코드 규모가 매우 클 수 있다.), 대규모 개발 팀의 조직의 경우. 본인이 담당한 개발 파트만을 이해하는 것도 매우 어울 수 있다. 그렇기 때문에 상황에 맞게 유연하게 어떻게 진행할 것인지 소통하는 것이 중요하다 생각한다.
2. 개발 시간 증가
적절한 책임 분리를 위해 더 많은 시간과 고민을 활용할 수 있다.
흔히 '뽀모도로'를 활용해서 같이 시간 관리를 철저하게 할 수 있다면 매우 좋은 분배로 기능 개발 시간을 유용하게 할 수 있으나, 무작정 붙잡고 있다면 각 클래스의 책임을 명확하게 구분하다가 많은 시간을 헛 개발 시간으로 보낼 수 있다.
개발 숙련도나 언어 숙련도가 낮을 때. 무작정 기능 개발을 위해서 IDE를 켜놓고 멍 때리는 것을 더욱 주의해야 한다.
구현하기로 한 목표를 정확하게 구현할 수 있도록 적절한 언어 숙련도와 프레임워크 숙련도를 키우는 것이 중요하다고 생각한다.
3. 중복 코드 발생 가능성
단일 책임 원칙(SRP)을 엄격하게 지키다 보면 동일한 로직이 있는지 모르고 서로 다른 클래스에 같은 기능을 구현하여 코드 중복이 발생할 수 있다. 이런 중복 코드를 제거하면 충분히 좋지만 급하게 처리하느라 중복된 코드를 여러 곳에서 만들게 되면 효율성을 떨어뜨리고 유지 보수를 어렵게 만든다.
PR 하면서 많은 과정을 리뷰하면서 검토하는 것이 중요하다고 생각한다.
SRP 단점의 경우는 팀이 커뮤니케이션을 잘 이뤄내고, 충분히 신경을 더 쓴다면 충분히 완화할 수 있을 것이라 생각한다.
오타나 궁금한 부분이 있다면 언제든 댓글 남겨주시면 최대한 답변드릴 수 있도록 하겠습니다.
감사합니다. 다음 포스팅 때 뵙겠습니다!
'Java ☕ > Java' 카테고리의 다른 글
[Java] 개방-폐쇄 원칙 (Open-Closed Principle , OCP) (1) | 2023.05.30 |
---|---|
[Java] 접근 지정자(Access Modifier) Private/Protected/Default/Public 정리 (0) | 2023.05.29 |
[Java] 박싱(Boxing)과 언박싱(Unboxing) (0) | 2023.05.28 |
[Java] Call by Value와 Call by Reference 정리. (값 호출과 참조 호출.) (0) | 2023.05.24 |
[Java] 메소드 오버로딩과 오버라이딩 (with 아삭 한입 정리하기) (0) | 2023.05.23 |