관리 메뉴

Unfazedβ—οΈπŸŽ―

단일 μ±…μž„ 원칙(Single Responsibility Principle, SRP) λ³Έλ¬Έ

OOP(객체지ν–₯섀계)

단일 μ±…μž„ 원칙(Single Responsibility Principle, SRP)

9taetae9 2025. 1. 17. 14:23
728x90

SRPλŠ” SOLID 원칙 쀑 첫 번째 μ›μΉ™μœΌλ‘œ, ν•œ 개의 λͺ¨λ“ˆμ€ 였직 ν•œ 개의 μ•‘ν„°μ—λ§Œ μ±…μž„μ„ κ°€μ Έμ•Ό ν•œλ‹€λŠ” 원칙이닀. 

A module should be responsible to one, and only one, actor.

(Actor : λͺ¨λ“ˆμ˜ 변경을 μš”κ΅¬ν•˜λŠ” ν•˜λ‚˜ μ΄μƒμ˜ μ΄ν•΄κ΄€κ³„μžλ‚˜ μ‚¬μš©μž κ·Έλ£Ή)

SRPμ—μ„œ μ±…μž„μ˜ 의미 

일반적인 객체지ν–₯ μ„€κ³„μ—μ„œμ˜ μ±…μž„(μ—­ν• , μ±…μž„, ν˜‘λ ₯μ—μ„œμ˜ μ±…μž„)

  • 객체가 μˆ˜ν–‰ν•΄μ•Ό ν•˜λŠ” 행동(behavior)을 의미
  • 객체가 "무엇을 μ•Œκ³ "(knowing) "무엇을 ν•˜λŠ”μ§€"(doing)λ₯Ό λ‚˜νƒ€λƒ„
  • ν˜‘λ ₯ 관계 μ†μ—μ„œ 객체가 μˆ˜ν–‰ν•΄μ•Ό ν•˜λŠ” 역할을 의미
  • 주둜 λ©”μ„œλ“œλ‚˜ κΈ°λŠ₯ λ‹¨μœ„μ˜ μ±…μž„μ„ 의미

SRPμ—μ„œμ˜ μ±…μž„

  • λ³€κ²½μ˜ 이유(reason for change)λ₯Ό 의미
  • νŠΉμ • μ•‘ν„°(μ΄ν•΄κ΄€κ³„μž)의 μš”κ΅¬μ‚¬ν•­μ„ 의미
  • ν•˜λ‚˜μ˜ λͺ¨λ“ˆμ΄ λ³€κ²½λ˜μ–΄μ•Ό ν•˜λŠ” μ΄μœ λŠ” 였직 ν•˜λ‚˜μ—¬μ•Ό 함
  • 더 큰 λ‹¨μœ„μ˜ μ±…μž„μ„ λ‹€λ£Έ
  λ²”μœ„ 적용 μˆ˜μ€€ λͺ©μ  κ²°κ³Ό
일반적 μ±…μž„ 객체의 행동과 μƒνƒœμ— 초점 λ©”μ„œλ“œλ‚˜ κΈ°λŠ₯ λ‹¨μœ„μ˜ λ―Έμ‹œμ  μ±…μž„ 객체의 μ—­ν• κ³Ό ν˜‘λ ₯ 관계 μ •μ˜ 잘 μ •μ˜λœ 객체의 행동
SRPμ—μ„œμ˜ μ±…μž„ λ³€κ²½μ˜ 원인과 μ΄ν•΄κ΄€κ³„μžμ— 초점 ν΄λž˜μŠ€λ‚˜ λͺ¨λ“ˆ λ‹¨μœ„μ˜ κ±°μ‹œμ  μ±…μž„ λ³€κ²½μ˜ 영ν–₯ λ²”μœ„ μ œμ–΄μ™€ 관리 λ…λ¦½μ μœΌλ‘œ λ³€κ²½ κ°€λŠ₯ν•œ λͺ¨λ“ˆ

 

 

 

SRPμ—μ„œ μ€‘μš”ν•œ 점

  1. "μ±…μž„"은 "λ³€κ²½μ˜ 이유"λ₯Ό μ˜λ―Έν•œλ‹€.
  2. ν•˜λ‚˜μ˜ ν΄λž˜μŠ€λŠ” ν•˜λ‚˜μ˜ μ•‘ν„°(μ΄ν•΄κ΄€κ³„μž)에 λŒ€ν•΄μ„œλ§Œ μ±…μž„μ„ μ Έμ•Ό ν•œλ‹€.
  3. μ„œλ‘œ λ‹€λ₯Έ 이유둜 λ³€κ²½λ˜λŠ” 것듀은 λΆ„λ¦¬λ˜μ–΄μ•Ό ν•œλ‹€.

λ³€κ²½ν•  이유λ₯Ό λ¬΄μ—‡μœΌλ‘œ μ •μ˜ν•˜λŠ”κ°€? 

  • 버그 μˆ˜μ •μ΄λ‚˜ λ¦¬νŒ©ν† λ§μ— κ΄€ν•œ 것이 μ•„λ‹˜(ν”„λ‘œκ·Έλž˜λ¨Έμ˜ μ±…μž„)
  • ν”„λ‘œκ·Έλž¨μ˜ μ±…μž„κ³Ό λˆ„κ΅¬μ—κ²Œ μ‘λ‹΅ν•˜λŠ”μ§€μ— 초점

μ˜ˆμ‹œ μ½”λ“œ 1

쑰직 ꡬ쑰

  • μ΅œμƒμœ„μ— CEO
  • CEOμ—κ²Œ λ³΄κ³ ν•˜λŠ” C레벨 μž„μ›(Employee)
    • CFO (재무 λ‹΄λ‹Ή)
    • CTO (기술 λ‹΄λ‹Ή)
    • COO (운영 λ‹΄λ‹Ή) 

μ‹œλ‚˜λ¦¬μ˜€ 1) κΈ‰μ—¬ 계산 κ·œμΉ™μ„ 잘λͺ» λͺ…μ„Έν•˜μ—¬ λͺ¨λ“  직원듀이 두 배의 κΈ‰μ—¬λ₯Ό λ°›μŒ

calculatePay λ©”μ„œλ“œκ°€ 치λͺ…μ μœΌλ‘œ 잘λͺ» λͺ…μ„Έλœ κ²ƒμ΄λ―€λ‘œ μ΄ μ±…μž„μ€ CFOκ°€ μ Έμ•Όν•œλ‹€.

 

μ‹œλ‚˜λ¦¬μ˜€ 2) μ €μž₯ν•˜λŠ” 과정에 κΈ°μ—… 데이터가 손상

save λ©”μ„œλ“œμ˜ 치λͺ…적인 잘λͺ»λœ λͺ…μ„Έκ°€ μžˆλŠ” 기술적인 문제둜 이 μ±…μž„μ€ CTOκ°€ μ Έμ•Ό ν•œλ‹€.

 

μ‹œλ‚˜λ¦¬μ˜€ 3) μ§μ›λ“€μ˜ 근무 ν˜„ν™© 및 보상을 μΆ”μ ν•˜λŠ” λ¦¬ν¬νŠΈμ— 였λ₯˜κ°€ μžˆλŠ” 경우

reportHours λ©”μ„œλ“œμ— 잘λͺ»λœ λͺ…μ„Έκ°€ μ‘΄μž¬ν•˜λŠ” μš΄μ˜μƒμ˜ μ±…μž„μœΌλ‘œ 이 μ±…μž„μ€ COOκ°€ μ Έμ•Ό ν•œλ‹€.

// SRP μœ„λ°˜
public class Employee {
    public Money calculatePay(); // CFO 쑰직의 μ±…μž„ (κΈ‰μ—¬ 계산 둜직)
    public void save(); // CTO 쑰직의 μ±…μž„ (데이터 μ €μž₯ 기술)
    public String reportHours(); // COO 쑰직의 μ±…μž„ (운영/감사 보고)
}

μœ„ μ½”λ“œλŠ” ν•˜λ‚˜μ˜ 클래슀 내에 μ„œλ‘œ λ‹€λ₯Έ 쑰직의 μ±…μž„λ“€μ΄ μ„žμ—¬ μžˆλ‹€. 

 

calculatePay λ©”μ„œλ“œ λ‚΄μ˜ μ•Œκ³ λ¦¬μ¦˜μ— 변경이 μ΄λ£¨μ–΄μ§ˆ λ•Œ, κ·Έ λ³€κ²½ μš”μ²­μ€ CFOκ°€ μ΄λ„λŠ” μ‘°μ§μ—μ„œ λ°œμƒν•  것이닀.

λ§ˆμ°¬κ°€μ§€λ‘œ reportHours λ©”μ„œλ“œμ— λŒ€ν•œ 변경은 COO의 μ‘°μ§μ—μ„œ μš”μ²­ν•  것이고, save λ©”μ„œλ“œμ— λŒ€ν•œ 변경은 CTO의 μ‘°μ§μ—μ„œ μš”μ²­ν•  것이닀. 

 

ν˜„μž¬ ν•΄λ‹Ή ν΄λž˜μŠ€μ—λŠ” μ„œλ‘œ λ‹€λ₯Έ μ•‘ν„°(CFO, CTO, COO 쑰직)에 μ‘λ‹΅ν•˜λŠ” 3개의 λ©”μ„œλ“œκ°€ μžˆλ‹€. (SRP μœ„λ°˜)

μœ„μ˜ μ½”λ“œμ²˜λŸΌ ν•œ 클래슀 내에 3개의 λ‹€λ₯Έ 그룹의 λΉ„μ§€λ‹ˆμŠ€ κΈ°λŠ₯이 있게 될 경우, CTOκ°€ μš”μ²­ν•œ 변경이 μ˜λ„μΉ˜ μ•Šκ²Œ COOκ°€ μ±…μž„μ„ μ Έμ•Ό λ˜λŠ” μƒν™©μ΄ λ°œμƒν•  수 μžˆλ‹€. (calculatePay λ©”μ„œλ“œμ˜ 변경이 μ˜λ„μΉ˜ μ•Šκ²Œ reportHours λ©”μ„œλ“œλ₯Ό λ§κ°€λœ¨λ¦° 상황)

 

λ”°λΌμ„œ, μ†Œν”„νŠΈμ›¨μ–΄ λͺ¨λ“ˆμ„ μž‘μ„±ν•  λ•Œ, 변경이 μš”μ²­λ  λ•Œ κ·Έ 변경은 λ‹¨μΌν•˜κ³  쒁게 μ •μ˜λœ λΉ„μ¦ˆλ‹ˆμŠ€ κΈ°λŠ₯을 λŒ€ν‘œν•˜λŠ” λ°€μ ‘ν•˜κ²Œ κ²°ν•©λœ κ·Έλ£ΉμœΌλ‘œλΆ€ν„°λ§Œ λ°œμƒν•  수 μžˆλ„λ‘ ν•˜λŠ” 것이 이상적이닀. 즉, λͺ¨λ“ˆλ“€μ„ 쑰직 μ „μ²΄μ˜ λ³΅μž‘μ„±μœΌλ‘œλΆ€ν„° κ²©λ¦¬μ‹œν‚€κ³ , 각 λͺ¨λ“ˆμ΄ ν•˜λ‚˜μ˜ λΉ„μ¦ˆλ‹ˆμŠ€ κΈ°λŠ₯의 ν•„μš”μ—λ§Œ μ±…μž„μ„ 지도둝 μ‹œμŠ€ν…œμ„ 섀계해야 ν•œλ‹€.

 

SRPλ₯Ό μ€€μˆ˜ν•˜λ €λ©΄ λ‹€μŒ μ½”λ“œμ™€ 같이 λΆ„λ¦¬λ˜μ–΄μ•Ό ν•œλ‹€. (ν•œ ν΄λž˜μŠ€κ°€ ν•œ 엑터에 λŒ€ν•œ μ±…μž„(λ³€κ²½μ˜ 이유)만 가지도둝)

// SRP μ€€μˆ˜
// CFO 쑰직의 μ±…μž„
public class EmployeePayCalculator {
    public Money calculatePay(); // CFO 쑰직의 μ±…μž„ (κΈ‰μ—¬ 계산 둜직)
}

// CTO 쑰직의 μ±…μž„
public class EmployeeRepository {
    public void save(); // CTO 쑰직의 μ±…μž„ (데이터 μ €μž₯ 기술)
}

// COO 쑰직의 μ±…μž„
public class EmployeeTimeReporter {
    public String reportHours(); // COO 쑰직의 μ±…μž„ (운영/감사 보고)
}

μœ„μ²˜λŸΌ 각 ν΄λž˜μŠ€κ°€ ν•˜λ‚˜μ˜ λ³€κ²½μ˜ 이유(μ±…μž„)만 가지도둝 μ„€κ³„ν•˜λ©΄ λ‹€μŒκ³Ό 같은 이점이 μžˆλ‹€.

  • 각 ν΄λž˜μŠ€λŠ” ν•˜λ‚˜μ˜ μ‘°μ§μ—μ„œλ§Œ λ³€κ²½ μš”μ²­μ΄ λ°œμƒ
  • ν•œ κΈ°λŠ₯의 변경이 λ‹€λ₯Έ κΈ°λŠ₯에 영ν–₯을 μ£Όμ§€ μ•ŠμŒ(μ±…μž„ ν˜Όν•© 피함)
  • 각 쑰직의 μš”κ΅¬μ‚¬ν•­ 변경을 λ…λ¦½μ μœΌλ‘œ 반영 κ°€λŠ₯

λ”°λΌμ„œ SRP μ€€μˆ˜ν•˜λ©΄ "κΈ‰μ—¬ 계산 λ‘œμ§μ„ μˆ˜μ •ν–ˆλŠ”λ° κ·Όλ¬΄μ‹œκ°„ λ³΄κ³ μ„œμ— 결함이 λ°œμƒν•˜λŠ”" 상황을 μ˜ˆλ°©ν•  수 μžˆλ‹€.

 

μ˜ˆμ‹œ μ½”λ“œ 2

리포트 λͺ¨λ“ˆ 사둀

  • λ³€κ²½ 이유 1: 리포트 λ‚΄μš© λ³€κ²½
  • λ³€κ²½ 이유 2: 리포트 ν˜•μ‹ λ³€κ²½
  • μ„œλ‘œ λ‹€λ₯Έ 이유둜 λ³€κ²½λ˜λ―€λ‘œ 별도 λͺ¨λ“ˆλ‘œ 뢄리해야 함
// SRP μœ„λ°˜
class Report {
    private String content;
    private String format;

    public Report(String content, String format) {
        this.content = content;
        this.format = format;
    }

    // 리포트 데이터 생성 μ±…μž„
    public void generateReport() {
        // λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ 데이터λ₯Ό κ°€μ Έμ˜€λŠ” 둜직
        System.out.println("리포트 λ‚΄μš© 생성 쀑...");
        this.content = "리포트 데이터...";
    }

    // 리포트 ν¬λ§·νŒ… μ±…μž„
    public void formatReport() {
        // ν¬λ§·νŒ… 둜직
        System.out.println("리포트 ν¬λ§·νŒ… 쀑...");
    }

    // 리포트 좜λ ₯ μ±…μž„
    public void printReport() {
        // 좜λ ₯ 둜직
        System.out.println("리포트 좜λ ₯ 쀑...");
    }
}

 

SRP μœ„λ°˜

  • Report ν΄λž˜μŠ€κ°€ μ—¬λŸ¬ μ±…μž„μ„ κ°€μ§€κ³  μžˆλ‹€.
    • 리포트 데이터 생성(generateReport())
    • 리포트 ν¬λ§·νŒ…(formatReport())
    • 리포트 좜λ ₯(printReport())
  • λ°œμƒκ°€λŠ₯ν•œ 문제
    • 데이터 생성 방식이 λ³€κ²½λ˜λ©΄ 클래슀 전체(Report 클래슀)에 영ν–₯
    • ν¬λ§·νŒ… μš”κ΅¬μ‚¬ν•­ λ³€κ²½ μ‹œ (Report 클래슀) μˆ˜μ • ν•„μš”
    • 좜λ ₯ 방식 λ³€κ²½ μ‹œμ—λ„ (Report 클래슀) μˆ˜μ • ν•„μš”
// SRP μ€€μˆ˜
// 1. 리포트 데이터 생성 μ±…μž„
class ReportGenerator {
    private String content;

    public String generateReport() {
        // λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ 데이터λ₯Ό κ°€μ Έμ˜€λŠ” 둜직
        System.out.println("리포트 λ‚΄μš© 생성 쀑...");
        return "리포트 데이터...";
    }
}

// 2. 리포트 ν¬λ§·νŒ… μ±…μž„
class ReportFormatter {
    public String formatReport(String content, String format) {
        // ν¬λ§·νŒ… 둜직
        System.out.println("리포트 ν¬λ§·νŒ… 쀑...");
        return "ν¬λ§·νŒ…λœ " + content;
    }
}

// 3. 리포트 좜λ ₯ μ±…μž„
class ReportPrinter {
    public void printReport(String formattedReport) {
        // 좜λ ₯ 둜직
        System.out.println("리포트 좜λ ₯: " + formattedReport);
    }
}

 

SRP μ€€μˆ˜

  • 각 μ±…μž„μ΄ λ³„λ„μ˜ 클래슀둜 λΆ„λ¦¬λ˜μ–΄ μžˆλ‹€.
    • ReportGenerator: 데이터 μƒμ„±λ§Œ λ‹΄λ‹Ή
    • ReportFormatter: ν¬λ§·νŒ…λ§Œ λ‹΄λ‹Ή
    • ReportPrinter: 좜λ ₯만 λ‹΄λ‹Ή
  • μž₯점
    • ν•œ λΆ€λΆ„μ˜ 변경이 λ‹€λ₯Έ 뢀뢄에 영ν–₯을 λ―ΈμΉ  μœ„ν—˜ κ°μ†Œ
      • ex : 리포트 컴파일 ν”„λ‘œμ„ΈμŠ€ 변경이 좜λ ₯ μ½”λ“œμ— 영ν–₯을 μ£Όμ§€ μ•ŠμŒ
    • 변경이 ν•„μš”ν•  λ•Œ κ΄€λ ¨ 클래슀만 μˆ˜μ •(μœ μ§€λ³΄μˆ˜μ„± ν–₯상)
      • 각 μ»΄ν¬λ„ŒνŠΈλ₯Ό λ…λ¦½μ μœΌλ‘œ μˆ˜μ • κ°€λŠ₯
      • 버그 λ°œμƒ μ‹œ 원인 νŒŒμ•… 쉬움
    • ν…ŒμŠ€νŠΈ μš©μ΄μ„±
      • 각 μ±…μž„μ„ λ…λ¦½μ μœΌλ‘œ ν…ŒμŠ€νŠΈ κ°€λŠ₯
      • Mock 객체 μ‚¬μš© 용이

단일 μ±…μž„ 원칙 λ‹€λ₯Έ ν‘œν˜„ 방식 

Gather together the things that change for the same reasons. Separate those things that change for different reasons(Robert C. Martin, 2014)

같은 이유둜 λ³€κ²½λ˜λŠ” 것듀을 ν•¨κ»˜ λͺ¨μœΌκ³ (응집도↑), λ‹€λ₯Έ 이유둜 λ³€κ²½λ˜λŠ” 것듀은 λΆ„λ¦¬ν•˜μž.(결합도).

 

 

SRPλŠ” μ‚¬λžŒμ— κ΄€ν•œ 것

λ§ˆμ§€λ§‰μœΌλ‘œ, λ³€κ²½μ˜ μ΄μœ λŠ” μ‚¬λžŒμ΄λΌλŠ” 것을 μƒκ°ν•˜μž. 변경을 μš”μ²­ν•˜λŠ” 것은 μ‚¬λžŒλ“€μ΄λ‹€.

μš°λ¦¬λŠ” μ„œλ‘œ λ‹€λ₯Έ 이유둜 μ‚¬λžŒλ“€μ΄ 관심을 κ°€μ§€λŠ” μ½”λ“œλ₯Ό ν˜Όν•©μ„ ν”Όν•΄μ•Ό ν•œλ‹€.

  • 변경은 ν•œ μ‚¬λžŒ/κ·Έλ£Ήμ—μ„œλ§Œ λ°œμƒν•΄μ•Ό 함
  • 각 λͺ¨λ“ˆμ€ ν•˜λ‚˜μ˜ λΉ„μ¦ˆλ‹ˆμŠ€ κΈ°λŠ₯μ—λ§Œ 응닡해야 함
  • 쑰직의 λ³΅μž‘μ„±μœΌλ‘œλΆ€ν„° κ²©λ¦¬λ˜μ–΄μ•Ό 함

 

 

참고 자료 :

https://en.wikipedia.org/wiki/Single-responsibility_principle

 

Single-responsibility principle - Wikipedia

From Wikipedia, the free encyclopedia Computer programming principle The single-responsibility principle (SRP) is a computer programming principle that states that "A module should be responsible to one, and only one, actor."[1] The term actor refers to a

en.wikipedia.org

 

https://blog.cleancoder.com/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html

 

Clean Coder Blog

The Single Responsibility Principle 08 May 2014 In 1972 David L. Parnas published a classic paper entitled On the Criteria To Be Used in Decomposing Systems into Modules. It appeared in the December issue of the Communications of the ACM, Volume 15, Number

blog.cleancoder.com

 

728x90

'OOP(객체지ν–₯섀계)' μΉ΄ν…Œκ³ λ¦¬μ˜ λ‹€λ₯Έ κΈ€

개방-폐쇄 원칙(Open-Closed Principle, OCP)  (0) 2025.01.20