Backend
home

자바 개발자를 위한 SOLID 원칙 정리

생성 일시
2025/05/01 14:56
태그
Java
게시일
2025/05/01
최종 편집 일시
2025/05/01 15:15

1. 단일 책임 원칙 (SRP: Single Responsibility Principle)

하나의 클래스는 하나의 책임만 가져야 한다.

 위반한 코드

class UserService { public void registerUser(String username) { // 회원 가입 로직 } public void sendEmail(String message) { // 이메일 발송 로직 (책임 2개!) } }
Java
복사
이 클래스는 회원가입 + 이메일 발송 두 가지 책임을 가진다. 수정 시 영향 범위가 커짐.

올바른 코드

class UserService { public void registerUser(String username) { // 회원 가입 로직 } } class EmailService { public void sendEmail(String message) { // 이메일 발송 로직 } }
Java
복사
책임 분리: UserService는 회원 관리, EmailService는 이메일만 담당!

2. 개방-폐쇄 원칙 (OCP: Open/Closed Principle)

확장에는 열려 있고, 수정에는 닫혀 있어야 한다.

위반한 코드

class PaymentService { public void pay(String type) { if (type.equals("kakao")) { // 카카오 결제 } else if (type.equals("naver")) { // 네이버 결제 } } }
Java
복사
새로운 결제 수단 추가 시 기존 코드를 수정해야 함.

올바른 코드 (다형성 활용)

interface PayStrategy { void pay(); } class KakaoPay implements PayStrategy { public void pay() { System.out.println("카카오 결제 완료"); } } class NaverPay implements PayStrategy { public void pay() { System.out.println("네이버 결제 완료"); } } class PaymentService { public void processPayment(PayStrategy strategy) { strategy.pay(); // 확장에는 열려있고, 기존 코드는 수정 필요 없음 } }
Java
복사
새로운 결제 방식은 PayStrategy만 구현하면 끝! OCP 준수

3. 리스코프 치환 원칙 (LSP: Liskov Substitution Principle)

자식 클래스는 부모 클래스를 대체할 수 있어야 한다.

위반한 코드

class Bird { public void fly() { System.out.println("날아갑니다"); } } class Ostrich extends Bird { public void fly() { throw new UnsupportedOperationException("타조는 날지 못해요"); } }
Java
복사
Bird 타입으로 Ostrich를 사용하면 예외 발생 → LSP 위반

올바른 코드 (추상화 분리)

abstract class Bird { } interface Flyable { void fly(); } class Sparrow extends Bird implements Flyable { public void fly() { System.out.println("참새가 날아갑니다"); } } class Ostrich extends Bird { // 날지 않는 새 }
Java
복사
날 수 있는 새만 Flyable을 구현 → 부모-자식 대체 가능!

4. 인터페이스 분리 원칙 (ISP: Interface Segregation Principle)

큰 인터페이스를 여러 개의 작은 인터페이스로 나누어야 한다.

위반한 코드

interface Worker { void work(); void eat(); } class Robot implements Worker { public void work() { /* 작업 */ } public void eat() { // 로봇은 밥을 먹지 않음! (불필요한 메소드) } }
Java
복사
Roboteat()이 필요 없음 → ISP 위반

올바른 코드

interface Workable { void work(); } interface Eatable { void eat(); } class Human implements Workable, Eatable { public void work() { /* 일하기 */ } public void eat() { /* 밥먹기 */ } } class Robot implements Workable { public void work() { /* 일하기 */ } }
Java
복사
필요한 인터페이스만 구현하게 분리!

5. 의존 역전 원칙 (DIP: Dependency Inversion Principle)

상위 모듈은 하위 모듈에 의존하지 말고, 둘 다 추상화에 의존해야 한다.

위반한 코드

class MySQLDatabase { public void save() { System.out.println("MySQL에 저장"); } } class OrderService { private MySQLDatabase database = new MySQLDatabase(); // 구체 클래스에 의존 public void order() { database.save(); } }
Java
복사
OrderService가 구체 MySQLDatabase에 강하게 결합 → DIP 위반

올바른 코드 (인터페이스 도입)

interface Database { void save(); } class MySQLDatabase implements Database { public void save() { System.out.println("MySQL에 저장"); } } class OrderService { private Database database; // 인터페이스에 의존 public OrderService(Database database) { this.database = database; } public void order() { database.save(); } }
Java
복사
OrderServiceDatabase 인터페이스에만 의존 → 다른 DB로 교체도 유연!

정리 요약

원칙
핵심 한 줄 요약
SRP
클래스는 하나의 책임만 가져야 함
OCP
확장엔 열려있고, 수정엔 닫혀야 함
LSP
자식 클래스는 부모를 대체 가능해야 함
ISP
인터페이스는 작게 나누고 필요한 것만 구현
DIP
구현체가 아닌 추상화에 의존해야 함