Files
project-auth-server/docs/examples/spring/dependency-injection.md
T

3.3 KiB

dependency injection 예시

좋은 예시 1: 필수 의존성은 생성자 주입

@Service
public class RegisterUserService implements RegisterUserUseCase {

    private final UserReader userReader;
    private final UserAppender userAppender;
    private final PasswordHasher passwordHasher;

    public RegisterUserService(
            UserReader userReader,
            UserAppender userAppender,
            PasswordHasher passwordHasher
    ) {
        this.userReader = userReader;
        this.userAppender = userAppender;
        this.passwordHasher = passwordHasher;
    }
}

왜 좋은가:

  • 필수 의존성이 시그니처에 드러난다
  • final field를 사용할 수 있다
  • 객체가 완전한 상태로 생성된다

좋은 예시 2: single constructor면 @Autowired 생략

@Component
public class JwtTokenIssuer {

    private final Clock clock;

    public JwtTokenIssuer(Clock clock) {
        this.clock = clock;
    }
}

왜 좋은가:

  • Spring은 단일 생성자를 자동으로 사용할 수 있다
  • annotation noise를 줄인다

좋은 예시 3: 선택 의존성은 setter/config method 검토

@Component
public class AuditClient {

    private RetryTemplate retryTemplate = RetryTemplate.defaultInstance();

    @Autowired(required = false)
    public void setRetryTemplate(RetryTemplate retryTemplate) {
        this.retryTemplate = retryTemplate;
    }
}

왜 좋은가:

  • 선택 의존성이라는 점이 드러난다
  • reasonable default가 있다

좋은 예시 4: 다중 구현은 qualifier로 명시

@Service
public class OAuthLoginService {

    private final OAuthClient googleOAuthClient;

    public OAuthLoginService(@Qualifier("googleOAuthClient") OAuthClient googleOAuthClient) {
        this.googleOAuthClient = googleOAuthClient;
    }
}

왜 좋은가:

  • 여러 구현체 중 무엇을 주입받는지 명확하다
  • 우연한 후보 선택에 기대지 않는다

나쁜 예시 1: production field injection

@Service
public class RegisterUserService {

    @Autowired
    private UserReader userReader;

    @Autowired
    private UserAppender userAppender;
}

문제:

  • 필수 의존성이 시그니처에 안 드러난다
  • final field 사용이 어렵다
  • plain unit test가 불편하다

나쁜 예시 2: service locator 사용

@Service
public class RegisterUserService {

    @Autowired
    private ApplicationContext applicationContext;

    public void register(...) {
        UserAppender userAppender = applicationContext.getBean(UserAppender.class);
        ...
    }
}

문제:

  • DI가 아니라 lookup으로 퇴행한다
  • 숨은 의존성이 생긴다

나쁜 예시 3: 생성자 인자 과다를 setter로 숨김

@Service
public class ComplexService {

    @Autowired
    public void setA(A a) { ... }

    @Autowired
    public void setB(B b) { ... }

    @Autowired
    public void setC(C c) { ... }

    @Autowired
    public void setD(D d) { ... }

    @Autowired
    public void setE(E e) { ... }

    @Autowired
    public void setF(F f) { ... }
}

문제:

  • 책임이 큰 문제를 주입 방식으로 숨긴다
  • 객체의 필수/선택 의존성이 흐려진다

개선:

  • collaborator 분리
  • orchestration 재설계
  • 설정 묶기 검토