12 KiB
12 KiB
category, tags, title, last_updated
| category | tags | title | last_updated | ||||
|---|---|---|---|---|---|---|---|
| Unified |
|
|
2026-05-02 |
Single Responsibility Principle (SRP)
📌 Brief Summary
"하나의 모듈은 오직 하나의 변경 이유(Reason to change)만을 가져야 한다: 코드의 응집도를 높이고 복잡성을 분산하여, 버그 수정과 기능 확장이 다른 영역에 미치는 부작용을 최소화하는 설계의 기초."
단일 책임 원칙(SRP)은 객체 지향 프로그래밍의 5가지 기초 설계 원칙인 SOLID 중 첫 번째 원칙으로, "클래스는 오직 하나의 변경 이유만 가져야 한다(하나의 작업만 수행해야 한다)"는 개념입니다 [1, 2]. 이를 적용하면 각 컴포넌트의 역할이 명확히 분리되어 소프트웨어 설계를 더 이해하기 쉽고, 유연하며, 유지보수하기 좋게 만듭니다 [1, 2]. 코드베이스를 분석하거나 시스템을 설계할 때 가장 우선적이고 쉽게 적용할 수 있는 핵심 지침입니다 [3].
📖 Core Content
SRP는 객체 지향 설계의 첫 번째 단추이자 가장 보편적인 리뷰 기준입니다.
- 단일 책임의 기준:
- 클래스나 함수가 수행하는 '일(Task)'이 아니라, 그 코드를 관리하고 요구사항을 변경하는 '주체(Actor)'가 누구인가에 집중합니다.
- 비즈니스 로직, 데이터베이스 접근, UI 렌더링 등이 하나의 파일에 섞여 있다면 이는 명백한 SRP 위반입니다.
- 코드 리뷰의 핵심 필터:
- 리뷰어는 거대한 함수나 클래스를 발견했을 때 이를 논리적 단위로 쪼개도록 권고합니다.
- 모듈이 작아질수록 테스트 코드를 작성하기 쉬워지며, 특정 기능만 떼어내어 재사용하기 용이해집니다.
- 결합도와 응집도:
- 책임이 명확히 분리된 코드는 낮은 결합도(Low Coupling)와 높은 응집도(High Cohesion)를 가지게 되어, 전체 시스템의 유지보수 비용을 낮춥니다.
- SOLID 원칙의 핵심 기반: SRP는 로버트 C. 마틴(Robert C. Martin)이 대중화한 SOLID 원칙(단일 책임, 개방/폐쇄, 리스코프 치환, 인터페이스 분리, 의존성 역전)의 시작점입니다 [1]. 이 원칙들은 서로 의존성을 줄이고 시스템의 재사용성을 높여 코드의 부패(code rot)를 방지하는 역할을 합니다 [1].
- 클래스와 모듈의 단일 작업(Job): 클래스는 단 한 가지의 책임(작업)만을 가져야 합니다 [2]. 소스에서 제시된 예시에 따르면,
UserPersistence라는 클래스가 있다면 이 클래스는 사용자 데이터를 데이터베이스에 저장하고 검색하는 역할만 수행해야 하며, 사용자 입력을 검증(validating)하는 책임까지 동시에 가져서는 안 됩니다 [2]. - 코드 분석 및 해독에서의 역할: 복잡하고 거대한 코드베이스를 읽고 분석할 때, 특정 클래스를 보며 "이 클래스가 단일 책임 아이디어를 준수하고 있는가?", "이 클래스를 더 작은 객체들로 분해해야 하는가?"라고 비판적으로 질문하는 것은 시스템의 추상화 수준과 응집도를 평가하는 훌륭한 방법입니다 [4]. 이러한 시각은 단순히 코드를 이해하는 것을 넘어 아키텍처를 개선할 수 있는 통찰력을 제공합니다 [4].
- 점진적 도입과 즉각적인 이점: SRP는 종종 적용하기 가장 쉬운 원칙으로 여겨지며 즉각적인 코드 품질 향상 이점을 제공합니다 [3]. 기존 레거시 시스템 전체를 한 번에 수정할 필요 없이, 새로운 클래스를 작성하거나 기존 코드를 수정할 때 "이 클래스의 단일 책임은 무엇인가?"라고 자문하며 점진적으로 적용하는 것이 권장됩니다 [3].
⚖️ Trade-offs & Caveats
- 과도한 파편화: SRP를 극단적으로 적용할 경우 클래스와 파일 수가 기하급수적으로 증가하여 전체 시스템의 가독성을 해칠 수 있습니다. '논리적 연관성'이 높은 코드들은 적절한 수준에서 함께 유지하는 실용적 균형이 필요합니다.
- 아키텍처적 부채: 초기 설계 시 SRP를 무시하면 시간이 흐를수록 '신(God) 객체'가 탄생하며, 이는 리팩토링 비용을 기하급수적으로 증가시키는 주요 원인이 됩니다.
소스에 단일 책임 원칙(SRP)만을 특정한 부작용이나 세부적인 제약 사항(Trade-off)에 대한 구체적인 정보는 부족합니다.
다만, SRP를 포함한 SOLID 원칙 전체를 시스템에 적용할 때 발생할 수 있는 특징과 반대 급부가 다음과 같이 언급되어 있습니다.
- 구현 복잡성과 숙련도 요구: 객체 지향 설계 규율과 패턴을 엄격하게 적용해야 하므로 구현 복잡도(Implementation Complexity)가 중간에서 높음(Medium-High) 수준으로 증가하며, 의존성 주입(DI) 프레임워크 등을 다룰 수 있는 숙련된 개발자가 필요합니다 [5].
- 점진적 리팩토링의 필요성: 과도한 설계 변경을 피하기 위해 기존 모놀리식 모듈을 일시에 분해하기보다는, 새로운 기능을 추가하거나 기존 코드를 수정할 때 점진적으로 원칙을 적용(Apply Incrementally)하는 주의가 필요합니다 [3].
🔗 Knowledge Connections
- SOLID Principles: 5대 원칙의 시작점.
- 테스트 용이성 (Testability): 테스트하기 좋은 코드를 만드는 직접적 원인.
- Refactoring: SRP 위반 시 리뷰어가 내리는 핵심 처방.
- Clean Architecture: 책임을 계층별로 격리하는 거시적 구조.
- Code Readability: 단순해진 코드가 가져오는 가독성 향상.
Related Concepts
[관계 유형 A (아키텍처 및 기반 원칙)]
- SOLID 원칙 (SOLID Principles)
- 연결 이유: SRP는 SOLID 원칙의 첫 번째 글자를 담당하는 구성 요소로, 객체 지향 설계의 기초 철학을 형성합니다 [1].
- 이 개념을 통해 더 깊게 이해할 수 있는 부분: 클래스 설계 시 단일 책임(SRP)을 넘어 확장에는 열려있고 수정에는 닫혀있는 구조(OCP), 혹은 의존성 역전(DIP) 등 시스템 전체의 유연성을 확보하는 종합적 아키텍처 관점을 이해할 수 있습니다 [1, 2].
- 관심사의 분리 (Separation of Concerns, SoC)
- 연결 이유: SoC는 시스템을 겹치지 않고 뚜렷한 기능적 섹션(관심사)으로 나누는 소프트웨어 엔지니어링의 핵심 원칙으로, SRP의 철학과 매우 밀접하게 맞닿아 있습니다 [6].
- 이 개념을 통해 더 깊게 이해할 수 있는 부분: 클래스 수준의 단일 책임을 넘어 프레젠테이션, 비즈니스 로직, 데이터 접근 계층 등 아키텍처 전반에서 어떻게 역할을 분리하고 코드를 구성해야 하는지 파악할 수 있습니다 [6].
- 도메인 주도 설계 (Domain-Driven Design, DDD)
- 연결 이유: SRP를 준수하여 책임을 분할할 때 비즈니스 도메인(바운디드 컨텍스트 등)에 따라 경계를 나누는 것이 구조화의 핵심입니다 [7-9].
- 이 개념을 통해 더 깊게 이해할 수 있는 부분: 코드를 분리할 때 단순한 기술적 기준이 아닌, 비즈니스 규칙과 용어(Ubiquitous Language)를 중심으로 모델과 클래스의 책임을 분배하는 방법을 학습할 수 있습니다 [7, 8, 10].
[관계 유형 B (구현 패턴 및 탐색 전략)]
- 의존성 주입 (Dependency Injection, DI)
- 연결 이유: 단일 책임을 가진 작은 클래스들이 서로 통신하기 위해 강한 결합도를 생성하지 않도록 만들어주는 구현 도구이자 패턴입니다 [2, 11, 12].
- 이 개념을 통해 더 깊게 이해할 수 있는 부분: 각 컴포넌트가 자신의 단일 책임에만 집중하면서 다른 객체와의 협력을 인터페이스에 의존하게 만드는 구체적인 설계 기법을 배울 수 있습니다 [3, 12, 13].
- 하향식 및 상향식 접근법 (Top-down and Bottom-up Approaches)
- 연결 이유: 복잡한 코드베이스를 읽을 때, 시스템의 특정 클래스가 단일 책임을 올바르게 지키고 있는지 분석하기 위해 정보의 흐름을 추적하는 인지적 탐색 전략입니다 [14].
- 이 개념을 통해 더 깊게 이해할 수 있는 부분: 공용 API 등 최상위 진입점에서 시작하거나(하향식) DB 스토리지에서 역추적하며(상향식) 모듈 간의 책임 분배 상태를 해독하는 방법을 익힐 수 있습니다 [14].
Deeper Research Questions
- 코드 리뷰 시 특정 클래스나 모듈이 단일 책임(SRP)을 위반하여 여러 가지 기능을 처리하고 있는지 신속하게 식별할 수 있는 판단 기준은 무엇인가? [2, 4]
- 대규모 레거시 코드베이스에서 책임이 혼재된 거대한 클래스(God Class)를 발견했을 때, SRP를 적용하여 안전하게 분리하기 위한 점진적 리팩토링 전략은 무엇인가? [3, 15]
- 시스템 설계에서 '관심사의 분리(SoC)'와 '단일 책임 원칙(SRP)'을 동시에 달성하기 위해 비즈니스 로직과 기술적 인프라 코드를 어떻게 격리해야 하는가? [2, 6, 13]
- SRP를 과도하게 적용하여 지나치게 작은 클래스들이 수없이 생성되었을 때, 런타임 상의 호출 흐름을 효율적으로 추적하고 이해하는 방법은 무엇인가? [4, 16, 17]
- 객체 지향 프로그래밍에서 클래스가 단일 책임을 가지게 될 때, 개발자가 작성해야 하는 단위 테스트(Unit Test)의 구조와 품질은 어떻게 변화하는가? [1, 12, 18]
Practical Application Contexts
- Implementation: 개발자가 새로운 클래스나 함수를 작성할 때 "이 코드가 변경되어야 할 이유가 여러 개인가?"를 점검하여, 데이터 영속성 로직과 UI 검증 로직이 한 곳에 섞이는 것을 예방합니다 [2].
- System Design: 소프트웨어 모듈을 설계할 때 가장 먼저 고려하는 마인드셋으로 적용되어, 이후 인터페이스 설계(ISP)나 의존성 역전(DIP) 패턴을 성공적으로 도입하기 위한 튼튼한 토대를 제공합니다 [1, 3].
- Operation / Maintenance: 수백만 줄의 코드로 이루어진 레거시 시스템을 유지보수할 때, 변경 범위가 예상치 못하게 퍼지는 원인을 추적하며 클래스의 책임을 재분배(Refactoring)하는 기준점이 됩니다 [4, 19].
- Learning Path: 낯선 대규모 코드베이스에 진입하는 신규 개발자가 특정 파일을 열었을 때, 해당 파일의 단일 책임이 무엇인지 파악하고 문서화하며 시스템의 전체 오케스트레이션을 학습해 나가는 렌즈로 활용됩니다 [4, 14, 17].
- My Project Relevance: 코드의 복잡성을 관리하고 팀원 간의 협업을 원활하게 하기 위해, 각 모듈과 클래스의 명확한 목적(Job)을 부여하고 코드 독해에 걸리는 인지적 부하를 최소화하는 데 사용됩니다 [2, 6].
Adjacent Topics
- 디자인 패턴 (Design Patterns)
- 확장 방향: 단일 책임 원칙에 따라 잘게 분리된 객체들이 시스템 내에서 서로 어떻게 생성(Creational)되고, 구조를 이루며(Structural), 통신(Behavioral)하는지에 대한 구체적인 해법과 패턴(Factory, Observer, Strategy 등)을 확장하여 학습할 수 있습니다 [17, 20-22].
Last updated: 2026-05-02