본문 바로가기
Java

[Java] Interface/Abstract Class 실무 활용 패턴 - Part 3: 인터페이스 기반 DI (Dependency Injection)

by clolee 2025. 5. 7.

✅ 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 등 모든 계층 가능

댓글