스택큐힙리스트

SRP로 깨끗한 코드: 단일 책임 원칙 한 방 정리 본문

개발

SRP로 깨끗한 코드: 단일 책임 원칙 한 방 정리

스택큐힙리스트 2025. 7. 13. 11:35
반응형

“클래스가 바뀌어야 할 이유는 단 하나뿐이어야 한다.”
이 한 줄이 바로 단일 책임 원칙(SRP, Single Responsibility Principle)의 핵심입니다. SOLID 다섯 가지 중 첫 타자로 꼽히는 SRP는 ‘거대한 설계론’이 아니라 내일 당장 리팩터링 PR에서 팀원을 설득하기 위한 실전 기준이죠. 오늘은 SRP 개념부터 적용 팁까지 한눈에 정리해 봅니다.


1. SRP란 무엇인가?

  • 정의: “모듈·클래스·함수는 하나의 책임만 가져야 한다.” 책임이란 변경 이유와 동치입니다. 코드가 수정되는 트리거가 하나만 존재해야 유지보수성이 폭발적으로 올라갑니다.
  • 왜 중요한가?
    1. 변경 범위 최소화 → 버그 격리
    2. 테스트 단순화 → 빠른 CI
    3. 재사용성 상승 → 생산성 향상
      “잘게 쪼갠 클래스가 오히려 읽기 쉽다”는 개발 블로그 후기들이 이를 증명하죠.

2. SRP가 깨지는 순간, 무슨 일이 일어날까?

  • 거대 클래스(God Object): 기능 추가·수정 때마다 연쇄 변경 → 충돌 난무
  • 테스트 지옥: 서로 얽힌 상태 때문에 단위 테스트가 불가능
  • 버그 은폐: 책임이 얽혀 있으면 사이드이펙트가 로그에도 안 남음

3. SRP 적용 4단계

  1. 역할 라벨링
    클래스·메서드 위에 “이 부분은 무엇을 위해 존재하는가?” 질문 스티커를 붙여보세요. 답이 두 개 이상이면 SRP 위반입니다.
  2. 도메인 용어 분리
    비즈니스 책임(예: 주문)과 기술 책임(예: 로깅)을 같은 곳에 두지 마세요.
  3. 인터페이스 추출
    책임별로 인터페이스를 나누고 구현체를 교체 가능하게 만드세요.
  4. 리팩터링→리뷰 루프
    PR에서 “변경 이유가 하나냐?”를 리뷰 체크리스트에 추가하면 팀 전반으로 퍼집니다.

4. Kotlin 예제로 보는 Before ↔ After

❌ Before: 책임 과다 클래스

class OrderService {
    fun create(order: Order) { /* DB 저장 */ }
    fun sendEmail(order: Order) { /* SMTP 발송 */ }
    fun log(order: Order) { /* 파일 로그 */ }
}

✅ After: 책임 분리

class OrderRepository { fun save(order: Order) { /* ... */ } }

interface Notifier { fun notify(order: Order) }
class EmailNotifier : Notifier { /* ... */ }

class OrderService(
    private val repo: OrderRepository,
    private val notifier: Notifier
) {
    fun create(order: Order) {
        repo.save(order)
        notifier.notify(order)
    }
}

이제 ‘저장 방식’이나 ‘알림 채널’이 바뀌어도 OrderService는 끄떡없습니다.


5. 실무 꿀팁

  • Sprint마다 “책임 헬스체크”: 스토리 완료 후 클래스별 변경 이유를 회고에서 리스트업.
  • 폴더 구조로 책임 드러내기: repository/, service/, controller/처럼 물리적 분리.
  • 테스트 더블 적극 사용: 책임이 명확하면 Mock 주입이 쉬워집니다.
  • SRP 라벨링 자동화: SonarQube·Detekt 규칙으로 ‘Lines Per Class’ 제한 걸어두세요.
반응형
Comments