/
SRP(Single Responsibility Principle, 단일 책임 원칙)

SRP(Single Responsibility Principle, 단일 책임 원칙)

**SRP(Single Responsibility Principle, 단일 책임 원칙)**은 SOLID 원칙 중 하나로, 객체 지향 설계에서 각 클래스나 모듈이 **단 하나의 책임(책임의 축)**만 가져야 한다는 원칙입니다.


SRP의 정의

단일 책임 원칙이란?

  • "클래스는 변경될 이유가 단 한 가지뿐이어야 한다."

    • 즉, 하나의 클래스는 오직 하나의 기능이나 역할에 대한 책임만 가져야 하며, 여러 책임이 섞여 있으면 안 됩니다.

책임의 축이란?

  • 책임은 비즈니스의 요구사항에 따라 구체화됩니다.

  • 책임의 축은 일반적으로 하나의 행위, 도메인 역할, 또는 기능 단위로 정의됩니다.


SRP를 적용하지 않은 경우

문제:

아래 코드에서 OrderManager 클래스는 주문 관리에 필요한 여러 책임을 가지고 있습니다.

  • 주문 생성

  • 결제 처리

  • 영수증 발송

public class OrderManager { public void createOrder(String productId, int quantity) { // 주문 생성 로직 System.out.println("Order created for product: " + productId); } public void processPayment(String orderId) { // 결제 처리 로직 System.out.println("Payment processed for order: " + orderId); } public void sendReceipt(String email) { // 영수증 발송 로직 System.out.println("Receipt sent to: " + email); } }

결과:

  • 여러 책임이 하나의 클래스에 모여 있어, 변경의 이유가 다양해짐.

  • 예를 들어, 결제 처리 로직이 변경되면 영수증 발송 코드도 위험에 노출될 수 있음.

  • 코드의 테스트 및 유지보수가 어려워짐.


SRP를 적용한 경우

해결:

각 책임을 별도의 클래스로 분리하여, 각 클래스가 단일한 책임만 가지도록 설계합니다.

// 주문 생성 담당 public class OrderService { public void createOrder(String productId, int quantity) { System.out.println("Order created for product: " + productId); } } // 결제 처리 담당 public class PaymentService { public void processPayment(String orderId) { System.out.println("Payment processed for order: " + orderId); } } // 영수증 발송 담당 public class ReceiptService { public void sendReceipt(String email) { System.out.println("Receipt sent to: " + email); } } // 통합 관리 public class OrderManager { private final OrderService orderService; private final PaymentService paymentService; private final ReceiptService receiptService; public OrderManager(OrderService orderService, PaymentService paymentService, ReceiptService receiptService) { this.orderService = orderService; this.paymentService = paymentService; this.receiptService = receiptService; } public void handleOrder(String productId, int quantity, String email) { orderService.createOrder(productId, quantity); paymentService.processPayment("12345"); receiptService.sendReceipt(email); } }

 

결과:

  • 각 클래스가 단일 책임만 가짐.

  • 책임이 명확히 분리되어, 하나의 클래스에서 변경이 발생해도 다른 클래스에 영향을 주지 않음.

  • 유지보수성과 테스트 용이성이 크게 향상됨.


SRP 적용의 장점

  1. 유지보수성 향상:

    • 각 클래스의 변경 이유가 명확하므로, 수정이 필요한 부분을 쉽게 파악하고 작업 가능.

  2. 재사용성 증가:

    • 각 클래스가 독립적인 책임을 가지므로, 다른 곳에서 쉽게 재사용 가능.

  3. 테스트 용이성:

    • 각 클래스가 단일 책임만 가지므로, 독립적으로 테스트 가능.

  4. 변경의 영향 최소화:

    • 변경의 이유가 단일하므로, 한 클래스의 변경이 다른 클래스에 미치는 영향을 최소화.


SRP와 관련된 잘못된 설계 예

1. Utility Class 남용

모든 기능을 하나의 클래스에 넣는 경우:

public class Utility { public void calculateDiscount() { /* ... */ } public void sendEmail() { /* ... */ } public void generateReport() { /* ... */ } }

 

문제점:

  • 클래스가 너무 많은 책임을 가짐.

  • 변경이 발생할 때 다른 코드에 영향을 줄 가능성이 큼.

2. God Class

모든 도메인 로직을 한 곳에 집중:

 

문제점:

  • 코드가 복잡해지고, 유지보수가 어려워짐.


SRP와 실제 활용

SRP는 특히 대규모 시스템이나 협업 환경에서 유용하며, 다음과 같은 패턴이나 설계 방식에서 자주 사용됩니다:

  1. 헥사고날 아키텍처:

    • 각 Port와 Adapter는 단일 책임을 가지도록 설계.

  2. CQRS:

    • 명령(Command)와 조회(Query)를 분리하여 각각 단일 책임을 갖도록 구성.

  3. 미들웨어/서비스 계층 설계:

    • 각 서비스 클래스가 단일한 도메인 개념에 집중.


결론

SRP는 객체 지향 설계에서 가장 기본적이면서도 중요한 원칙입니다.
클래스가 단일 책임만 가지도록 설계하면, 유지보수성, 확장성, 테스트 용이성이 향상되고, 시스템의 복잡도를 낮출 수 있습니다. 😊