init: 클린 기반 auth 서버 설계
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
# dependency injection 예시
|
||||
|
||||
## 좋은 예시 1: 필수 의존성은 생성자 주입
|
||||
|
||||
```java
|
||||
@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 생략
|
||||
|
||||
```java
|
||||
@Component
|
||||
public class JwtTokenIssuer {
|
||||
|
||||
private final Clock clock;
|
||||
|
||||
public JwtTokenIssuer(Clock clock) {
|
||||
this.clock = clock;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**왜 좋은가:**
|
||||
|
||||
- Spring은 단일 생성자를 자동으로 사용할 수 있다
|
||||
- annotation noise를 줄인다
|
||||
|
||||
## 좋은 예시 3: 선택 의존성은 setter/config method 검토
|
||||
|
||||
```java
|
||||
@Component
|
||||
public class AuditClient {
|
||||
|
||||
private RetryTemplate retryTemplate = RetryTemplate.defaultInstance();
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setRetryTemplate(RetryTemplate retryTemplate) {
|
||||
this.retryTemplate = retryTemplate;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**왜 좋은가:**
|
||||
|
||||
- 선택 의존성이라는 점이 드러난다
|
||||
- reasonable default가 있다
|
||||
|
||||
## 좋은 예시 4: 다중 구현은 qualifier로 명시
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class OAuthLoginService {
|
||||
|
||||
private final OAuthClient googleOAuthClient;
|
||||
|
||||
public OAuthLoginService(@Qualifier("googleOAuthClient") OAuthClient googleOAuthClient) {
|
||||
this.googleOAuthClient = googleOAuthClient;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**왜 좋은가:**
|
||||
|
||||
- 여러 구현체 중 무엇을 주입받는지 명확하다
|
||||
- 우연한 후보 선택에 기대지 않는다
|
||||
|
||||
## 나쁜 예시 1: production field injection
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class RegisterUserService {
|
||||
|
||||
@Autowired
|
||||
private UserReader userReader;
|
||||
|
||||
@Autowired
|
||||
private UserAppender userAppender;
|
||||
}
|
||||
```
|
||||
|
||||
**문제:**
|
||||
|
||||
- 필수 의존성이 시그니처에 안 드러난다
|
||||
- final field 사용이 어렵다
|
||||
- plain unit test가 불편하다
|
||||
|
||||
## 나쁜 예시 2: service locator 사용
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class RegisterUserService {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
public void register(...) {
|
||||
UserAppender userAppender = applicationContext.getBean(UserAppender.class);
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**문제:**
|
||||
|
||||
- DI가 아니라 lookup으로 퇴행한다
|
||||
- 숨은 의존성이 생긴다
|
||||
|
||||
## 나쁜 예시 3: 생성자 인자 과다를 setter로 숨김
|
||||
|
||||
```java
|
||||
@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 재설계
|
||||
- 설정 묶기 검토
|
||||
Reference in New Issue
Block a user