✅ Part 3: 인터페이스 기반 DI (Dependency Injection)
📌 1. 개념 요약
의존성 주입(DI) 은 객체가 직접 의존 대상을 생성하지 않고,
외부에서 필요한 구현체(의존 객체)를 주입받는 설계 방식입니다.
그리고 이때 의존 대상을 "인터페이스 타입"으로 선언하면 →
유연성, 확장성, 테스트 용이성이 모두 크게 향상됩니다.
🔧 2. 구조 및 핵심 개념
[인터페이스] ← 역할 정의 (불변)
[구현 클래스들] ← 실제 동작 정의 (변화 가능)
[서비스 클래스] ← 인터페이스에만 의존
즉, "구현체가 아닌 역할(인터페이스)에 의존" → 느슨한 결합
✅ 3. 예제: 사용자 저장 시스템
🔹 인터페이스
public interface UserRepository {
void save(User user);
}
🔹 구현체 1: MySQL 저장소
public class MySqlUserRepository implements UserRepository {
public void save(User user) {
System.out.println("[MySQL] 사용자 저장: " + user.getName());
}
}
🔹 구현체 2: 메모리 저장소 (테스트용)
public class InMemoryUserRepository implements UserRepository {
public void save(User user) {
System.out.println("[Memory] 사용자 저장: " + user.getName());
}
}
🔹 사용자 서비스: 의존성 주입
public class UserService {
private final UserRepository repository;
public UserService(UserRepository repository) {
this.repository = repository;
}
public void register(User user) {
repository.save(user);
}
}
🔹 실행 코드
UserService service = new UserService(new MySqlUserRepository());
service.register(new User("홍길동"));
🧪 4. 테스트 시 Mock 교체도 간편
UserService testService = new UserService(new InMemoryUserRepository());
testService.register(new User("테스트 유저"));
또는 JUnit + Mockito 사용 시:
UserRepository mockRepo = Mockito.mock(UserRepository.class);
UserService service = new UserService(mockRepo);
- 테스트에서는 DB 연결 없이도 검증 가능
- 서비스는 오직 UserRepository 인터페이스만 알면 됨
💡 5. 실무에서의 효과
장점 | 설명 |
---|---|
느슨한 결합 | 구현체가 바뀌어도 서비스 코드 변경 없음 |
확장성 | 새로운 저장 방식 도입(예: MongoDB) 시 구현체만 추가 |
테스트 용이 | 테스트용 Repository로 교체 가능 |
SRP, OCP 준수 | 하나의 책임, 구현체 추가 가능(Open), 기존 수정 없음(Closed) |
🛠️ 6. Spring에서의 DI 구조
@Component
public class MySqlUserRepository implements UserRepository {
public void save(User user) { ... }
}
@Service
public class UserService {
private final UserRepository repo;
@Autowired
public UserService(UserRepository repo) {
this.repo = repo; // 자동 주입
}
}
- 인터페이스에 의존하면,
@Component
를 붙인 구현체가 자동 주입됨 - 테스트 시
@MockBean
으로 쉽게 교체 가능
📝 7. 정리
항목 | 설명 |
---|---|
목적 | 구현체가 아닌 인터페이스에 의존하여 유연성 확보 |
구성 | 인터페이스 + 구현체 + 서비스 클래스 |
효과 | 테스트/확장/유지보수/전략 교체 모두 유리 |
실무 적용 | Repository, MailSender, AuthProvider, API Client 등 모든 계층 가능 |
'Java' 카테고리의 다른 글
[Java] 자바 제네릭(Generic)의 기본 개념과 사용 이유 (0) | 2025.05.13 |
---|---|
[Java] Interface/Abstract Class 실무 활용 패턴 - Part 2: 전략 패턴 (Strategy Pattern) (0) | 2025.05.07 |
[Java] Interface/Abstract Class 실무 활용 패턴 - Part 1: 템플릿 메서드 패턴 (Template Method Pattern) (0) | 2025.05.07 |
[Java] 인터페이스와 추상 클래스 (Interface and Abstract Class) (0) | 2025.05.07 |
[Java] 왜 `this()` 생성자 체이닝을 사용하는가? (0) | 2025.04.08 |
댓글