--- id: wiki-2026-0508-single-responsibility-principle- title: Single Responsibility Principle (SRP) category: 10_Wiki/Topics status: verified canonical_id: self aliases: [SRP, Single Responsibility, 단일 책임 원칙] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [oop, design-principles, solid, clean-code] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: typescript framework: none --- # Single Responsibility Principle (SRP) ## 매 한 줄 > **"매 클래스(모듈)는 매 단 하나의 변경 이유를 가져야 한다"**. SOLID의 S. Robert C. Martin은 매 후기에 매 정의를 매 *"하나의 actor에게만 책임진다"*로 매 정련 — 즉 매 같은 stakeholder의 요구사항만 매 그 모듈에 매 모인다. 2026년에도 매 module/class boundary 결정의 매 first heuristic. ## 매 핵심 ### 매 정의 (refined) - "Reason to change" → "Actor (stakeholder group)". - 매 다른 actor가 매 같은 모듈을 매 다른 방향으로 매 당기면 매 SRP 위반. ### 매 신호 - 매 클래스 이름에 "and"/"또". - 매 import block 매 두 다른 도메인에서. - 매 메서드 절반은 매 DB, 절반은 매 UI. - 매 PR에서 매 매번 매 다른 팀이 review. ### 매 응용 1. 매 service / repository / presenter 분리. 2. 매 React: 매 hook 분리 (useData / useUI). 3. 매 microservices boundary. 4. 매 LLM agent: 매 tool별 분리. ## 💻 패턴 ### 매 Before — 책임 혼재 ```typescript class Report { data: Row[]; load(file: string) { /* CSV 파싱 */ } // 매 IO actor calculate() { /* 매 도메인 로직 */ } // 매 회계 actor printPdf() { /* 매 PDF 렌더 */ } // 매 publishing actor email(to: string) { /* SMTP */ } // 매 ops actor } ``` ### 매 After — actor별 분리 ```typescript class CsvLoader { load(file: string): Row[] { /* ... */ return []; } } class Reporter { calculate(rows: Row[]): Stats { /* ... */ return {} as Stats; } } class PdfRenderer { render(stats: Stats): Buffer { /* ... */ return Buffer.from(""); } } class EmailSender { send(to: string, attachment: Buffer): Promise { return Promise.resolve(); } } // 매 orchestration async function generateAndSend(file: string, to: string) { const rows = new CsvLoader().load(file); const stats = new Reporter().calculate(rows); const pdf = new PdfRenderer().render(stats); await new EmailSender().send(to, pdf); } ``` ### 매 React hook 분리 ```tsx // X — fat hook function useUserPage(id: string) { const [user, setUser] = useState(null); const [open, setOpen] = useState(false); useEffect(() => { fetch(`/api/users/${id}`).then(r => r.json()).then(setUser); }, [id]); return { user, open, setOpen }; } // O — actor 분리 function useUser(id: string) { return useQuery(["user", id], () => fetch(`/api/users/${id}`).then(r => r.json())); } function useDisclosure(initial = false) { const [open, setOpen] = useState(initial); return { open, openIt: () => setOpen(true), closeIt: () => setOpen(false) }; } ``` ### 매 NestJS — controller / service / repo ```typescript @Controller("orders") class OrderController { constructor(private svc: OrderService) {} @Post() place(@Body() dto: PlaceDto) { return this.svc.place(dto); } } @Injectable() class OrderService { constructor(private repo: OrderRepo, private mailer: Mailer) {} async place(dto: PlaceDto) { const o = await this.repo.create(dto); await this.mailer.send(o.user, "ordered"); return o; } } @Injectable() class OrderRepo { async create(dto: PlaceDto) { /* DB */ return {} as Order; } } ``` ### 매 Module boundary (file-level SRP) ``` src/ payment/ # 매 payment actor PaymentService.ts StripeAdapter.ts shipping/ # 매 logistics actor ShippingService.ts FedexAdapter.ts ``` ### 매 LLM agent — tool SRP ```typescript const tools = [ { name: "search_web", handler: searchWeb }, // 매 retrieval actor { name: "send_email", handler: sendEmail }, // 매 messaging actor { name: "create_calendar", handler: createCalendar } // 매 scheduling actor ]; // 매 한 tool은 매 한 가지 책임만. ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | 매 새 기능 추가 시 매 두 actor 영향 | 매 분리. | | 매 클래스가 매 두 다른 ticket queue로 routing | 매 SRP 위반 신호. | | 매 100줄 이내 매 단일 actor | 매 그대로 유지. | | 매 분리 이후 매 cross-talk 폭증 | 매 over-split. 매 통합 검토. | **기본값**: 매 actor 매 따라 매 split. 매 line count 기준 X. ## 🔗 Graph - 부모: [[SOLID Principles]] - 변형: [[Cohesion]] · [[Bounded_Context]] - 응용: [[Hexagonal Architecture]] · [[Clean Architecture]] - Adjacent: [[Open Closed Principle]] · [[Dependency Injection]] · [[Microservices]] ## 🤖 LLM 활용 **언제**: 매 클래스 split 결정, 매 module boundary, 매 PR review 시 책임 혼재 탐지. **언제 X**: 매 tiny script. ## ❌ 안티패턴 - **매 line count 기반 split**: 매 actor 무시 → 매 artificial. - **매 method 1개 = class 1개 강박**: 매 noise. - **매 god class 유지** (anti). - **매 SRP를 매 layered "service/repo" 만으로 해석**: 매 layer 안에서도 매 actor 분리 필요. ## 🧪 검증 / 중복 - Verified (R.C. Martin, *Clean Architecture*, ch. SRP refined). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — actor-based SRP + before/after refactor patterns |