init: 클린 기반 auth 서버 설계
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
# bean registration 기준
|
||||
|
||||
## 목적
|
||||
|
||||
Spring bean 등록은 “컨테이너가 생명주기와 의존성을 관리해야 하는 객체”에만 사용한다.
|
||||
아무 객체나 bean으로 올리지 않고, stereotype scanning과 `@Configuration` + `@Bean`을 역할에 따라 구분한다.
|
||||
|
||||
## 공식 의미
|
||||
|
||||
- Spring IoC container는 configuration metadata를 읽어 bean definition을 만들고 객체를 관리한다.
|
||||
- 설정 메타데이터는 주로 annotation-based component class, `@Configuration` + `@Bean`, 또는 외부 설정으로 표현할 수 있다.
|
||||
- `@Component`와 그 특수화(`@Repository`, `@Service`, `@Controller`)는 classpath scanning 대상이다.
|
||||
- `@Bean`은 객체를 생성·설정·초기화하는 factory method를 bean definition으로 등록한다.
|
||||
- `@Bean`은 `@Configuration` 클래스에서 사용하는 것이 기본 권장 방식이다.
|
||||
- bean overriding은 일반적으로 권장되지 않으며 설정 가독성을 해친다.
|
||||
- bean/runtime registration은 초기 단계가 아니라 live access 중에는 공식적으로 지원되지 않는다.
|
||||
- 일반적으로 fine-grained domain object는 Spring container에 등록하지 않는다.
|
||||
|
||||
## 기본 규칙
|
||||
|
||||
### 1. bean은 “컨테이너 관리 가치”가 있는 객체만 등록
|
||||
다음은 bean 등록 후보이다.
|
||||
|
||||
- application service / use case entry object
|
||||
- repository adapter
|
||||
- external API client
|
||||
- configuration / security / filter / interceptor
|
||||
- shared infrastructure object
|
||||
- framework integration object
|
||||
|
||||
다음은 기본적으로 bean 등록하지 않는다.
|
||||
|
||||
- domain entity
|
||||
- value object
|
||||
- request / response DTO
|
||||
- command / result DTO
|
||||
- 단순 임시 helper object
|
||||
- 매 요청/매 호출마다 새로 만들어도 되는 순수 data object
|
||||
|
||||
### 2. 애플리케이션 주 컴포넌트는 stereotype annotation 우선
|
||||
다음은 stereotype을 우선 검토한다.
|
||||
|
||||
- `@Service`
|
||||
- `@Repository`
|
||||
- `@Controller` / `@RestController`
|
||||
- generic component면 `@Component`
|
||||
|
||||
즉 프로젝트 코드 안의 “주된 역할 객체”는 scanning 기반 등록을 기본값으로 한다.
|
||||
|
||||
### 3. `@Bean`은 명시적 조립이 필요할 때 사용
|
||||
다음은 `@Configuration` + `@Bean`을 우선 검토한다.
|
||||
|
||||
- 외부 라이브러리 타입 등록
|
||||
- 생성자가 복잡하거나 factory method가 필요한 경우
|
||||
- 조건부 조립이 필요한 경우
|
||||
- 여러 collaborator를 엮어 명시적으로 wiring해야 하는 경우
|
||||
- infrastructure object / client / encoder / formatter / strategy bean 생성
|
||||
- 같은 config 안에서 inter-bean wiring을 명시적으로 보여주고 싶은 경우
|
||||
|
||||
### 4. `@Bean`은 기본적으로 `@Configuration` 안에서만
|
||||
`@Bean` method는 기본적으로 `@Configuration` 클래스 안에 둔다.
|
||||
|
||||
이유:
|
||||
- full configuration mode가 inter-bean dependency를 더 안전하게 다룬다
|
||||
- lite mode의 subtle bug 가능성을 줄일 수 있다
|
||||
|
||||
기본 금지:
|
||||
- 일반 `@Component` 안에 습관적으로 `@Bean` method 두기
|
||||
|
||||
예외:
|
||||
- 아주 제한된 factory-style component가 필요하고, inter-bean dependency 호출을 하지 않는 경우
|
||||
|
||||
### 5. bean 등록 이유가 이름만 보고 드러나야 한다
|
||||
- scanning bean이면 stereotype이 역할을 드러내야 한다
|
||||
- `@Configuration` 클래스는 조립 목적이 이름에 드러나야 한다
|
||||
|
||||
좋은 방향:
|
||||
- `SecurityConfiguration`
|
||||
- `WebConfiguration`
|
||||
- `VaultClientConfiguration`
|
||||
- `OAuth2SecurityConfiguration`
|
||||
|
||||
지양:
|
||||
- `CommonConfig`
|
||||
- `AppBeans`
|
||||
- `GeneralConfiguration`
|
||||
|
||||
### 6. domain object를 bean으로 등록하지 않는다
|
||||
Spring 공식 문서도 fine-grained domain object는 보통 container가 아니라 repository/business logic이 만들고 로드한다고 설명한다.
|
||||
|
||||
기본 금지:
|
||||
- `User`, `Money`, `UserEmail`, `CreateUserCommand`를 bean으로 등록
|
||||
- domain 생성 책임을 container로 넘기기
|
||||
|
||||
### 7. bean 이름은 기본 규칙을 따르고, 명시적 이름은 정말 필요할 때만
|
||||
Spring은 scanning bean의 이름을 일반적으로 decapitalize된 simple class name으로 만든다.
|
||||
|
||||
기본:
|
||||
- 이름 충돌이 없으면 기본 이름 사용
|
||||
- qualifier/alias/explicit name은 실제 필요가 있을 때만 사용
|
||||
|
||||
무분별한 명시적 이름 지정 지양:
|
||||
- `"mySpecialUserServiceBean"`
|
||||
- `"appMainPrimaryService"`
|
||||
|
||||
### 8. bean overriding 기본 금지
|
||||
같은 이름의 bean을 덮어쓰는 방식으로 조립하지 않는다.
|
||||
|
||||
이유:
|
||||
- 설정 가독성이 나빠진다
|
||||
- 어떤 bean이 실제로 쓰이는지 추적이 어려워진다
|
||||
|
||||
테스트에서만 예외적으로 필요하면 별도 테스트 설정/지원 메커니즘을 사용한다.
|
||||
|
||||
### 9. 런타임 동적 bean 등록 금지
|
||||
애플리케이션 실행 중 live container에 새 bean을 동적으로 등록하는 방식은 기본 금지한다.
|
||||
|
||||
기본:
|
||||
- bean definition은 startup 시점에 확정
|
||||
- 동적 확장이 필요하면 registry/plugin/factory 전략을 따로 설계
|
||||
|
||||
### 10. bean은 역할 단위로 등록하고, 잡동사니 helper를 bean으로 올리지 않는다
|
||||
container가 관리할 필요가 없는 순수 helper는 bean 대신:
|
||||
- static utility
|
||||
- package-private helper
|
||||
- mapper instance
|
||||
- plain object 생성
|
||||
을 우선 검토한다.
|
||||
|
||||
### 11. configuration class는 “조립”만 하고 business logic은 넣지 않는다
|
||||
`@Configuration` 클래스는 bean wiring과 설정 소유만 담당한다.
|
||||
|
||||
금지:
|
||||
- 비즈니스 흐름
|
||||
- 상태 전이
|
||||
- 외부 호출 오케스트레이션
|
||||
- 의미 있는 계산 로직
|
||||
|
||||
### 12. stereotype는 의미에 맞게 쓴다
|
||||
- persistence adapter면 `@Repository`
|
||||
- use case/application service면 `@Service`
|
||||
- web adapter면 `@Controller` / `@RestController`
|
||||
- 그 외 일반 Spring-managed component면 `@Component`
|
||||
|
||||
의미 없는 전부 `@Component` 관성 사용은 지양한다.
|
||||
|
||||
### 13. public API 역할이 없는 내부 구현은 과도한 bean 분해를 피한다
|
||||
container bean 수를 늘리는 것이 곧 좋은 설계는 아니다.
|
||||
|
||||
질문:
|
||||
- lifecycle 관리가 필요한가?
|
||||
- 외부에서 주입받아야 하는가?
|
||||
- 테스트 seam 가치가 있는가?
|
||||
- 명시적 wiring으로 읽기 쉬워지는가?
|
||||
|
||||
아니면 plain object가 더 낫다.
|
||||
|
||||
## 프로젝트 기준 요약
|
||||
|
||||
- bean은 컨테이너 관리 가치가 있는 객체만 등록
|
||||
- application 주 컴포넌트는 stereotype scanning 우선
|
||||
- 외부 라이브러리/명시적 조립은 `@Configuration` + `@Bean`
|
||||
- `@Bean`은 기본적으로 `@Configuration` 안에서만
|
||||
- domain object / DTO / value object는 bean 등록 금지
|
||||
- bean overriding, 런타임 동적 등록 기본 금지
|
||||
- configuration class는 조립만 담당
|
||||
Reference in New Issue
Block a user