[Spring / Java] DI와 implements

gyeol·2025년 10월 2일

스프링

목록 보기
48/50
post-thumbnail

스프링 빈에 대해 공부하다가 의존성 관리와 implements의 차이가 뭔지 궁금해졌다.
그래서 이에 대해 정리해보고자 한다.

implements

우리는 인터페이스를 구현할 때 implements 키워드를 사용한다.
만약 UserRepository가 인터페이스라면 그 안에 정의된 추상 메서드를 UserService에서 직접 구현해야 한다.

interface UserRepository {
    void save(User user);
}

class UserService implements UserRepository {
    @Override
    public void save(User user) {
        // 저장 로직 직접 구현
    }
}

이 경우에는 UserServiceUserRepository 자체를 구현하는 클래스가 된다. 즉, 둘은 같은 책임을 가지게 되기에 계층 분리가 무너지게 된다.

스프링의 의존성 관리(DI)

스프링에서는 일반적으로 서비스 계층과 레포지토리 계층을 분리한다. 위 예시로 설명해보자면, UserServiceUserRepository를 직접 구현하지 않고 필요로 하기만 한다. 스프링에서는 스프링 컨테이너가 UserRepository 구현체를 대신 생성해 UserService에 주입해준다.

// 서비스어
@Service
public class UserService {
    private final UserRepository userRepository;

    // 생성자 주입
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void register(User user) {
        userRepository.save(user);
    }
}

// 레포지토리
@Repository
public class JpaUserRepository implements UserRepository {
    @Override
    public void save(User user) {
        // JPA로 DB 저장
    }
}

여기서 UserService 입장에서는 UserRepository의 구현체를 알 필요가 없다. 스프링이 알아서 적절한 객체를 생성하고 주입해주기에 코드가 유연하고 테스트하기 더 쉬워진다.

어떻게 스프링이 내부를 연결해주는데?

스프링에서는 @Component, @Service 와 같은 애노테이션이 붙은 클래스를 자동으로 스캔한다. 이렇게 스캔된 클래스들은 스프링 빈이 되어 컨테이너에 등록되고, 스프링 컨테이너가 객체를 생성하고 관리하게 된다.


// 레포지토리로 인식 -> 컨테이너 등록
@Repository
public class JpaUserRepository implements UserRepository {
    @Override
    public void save(User user) {
        // DB 저장 로직
    }
}

// 서비스로 인식 -> 컨테이너 등록
@Service
public class UserService {
    private final UserRepository userRepository;

    // 생성자 주입
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void register(User user) {
        userRepository.save(user);
    }
}

여기서 생성자 주입 원리에 대해 알아보도록 하자.

  • 스프링이 UserService 스캔
  • UserService의 생성자를 확인하고 필요한 타입(UserRepository)이 무엇인지 확인
  • 컨테이너에서 UserRepository 타입의 빈을 찾음
  • 찾은 객체를 생성자 매개변수로 넣어 UserService 찬음
  • 스프링 컨테이너가 알아서 객체를 생성하고 관리하기에 UserService 입장에서는 UserRepository의 구현체를 알 필요가 없어짐
[UserService] ---UserRepository---> [JpaUserRepository]
            ↑ 스프링 컨테이너가 생성자에 넣어줌

즉, 스프링이 인터페이스 타입과 구현체를 매칭해주기에 우리가 직접 new JpaUserRepository()를 호출할 필요가 없어진 것이다.

profile
공부 기록 공간 '◡'

0개의 댓글