init: 클린 기반 auth 서버 설계
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
# collections / immutability 기준
|
||||
|
||||
## 목적
|
||||
|
||||
컬렉션은 기본적으로 **immutable-first**로 다룬다.
|
||||
변경이 꼭 필요한 로컬 조립 단계에서만 mutable 컬렉션을 허용하고, 경계를 넘길 때는 수정 불가 snapshot 또는 명시적 불변 구조로 바꾼다.
|
||||
|
||||
## 공식 의미
|
||||
|
||||
- JDK의 `List.of`, `Set.of`, `Map.of`, `copyOf`, `toUnmodifiable*`는 수정 불가 컬렉션을 만든다.
|
||||
- `Collections.unmodifiableXXX`는 원본 컬렉션을 감싼 view일 뿐이다. 원본이 바뀌면 view도 바뀐다.
|
||||
- 컬렉션이 수정 불가여도 원소가 mutable이면 내용이 바뀐 것처럼 보일 수 있다.
|
||||
- `List.of` / `List.copyOf` 계열은 null을 허용하지 않는다.
|
||||
- `Set.of`와 `Map.of` 계열은 null을 허용하지 않으며, 중복 원소/중복 키를 허용하지 않는다.
|
||||
- `Set.of`, `Map.of`, `Map.ofEntries`, `toUnmodifiableSet`, `toUnmodifiableMap`는 iteration order가 JVM 실행마다 달라질 수 있다.
|
||||
|
||||
## 기본 규칙
|
||||
|
||||
### 1. 기본은 immutable-first
|
||||
다음 경우에는 mutable보다 immutable/unmodifiable 결과를 기본값으로 사용한다.
|
||||
|
||||
- 상수 컬렉션
|
||||
- 설정/정책/권한 목록
|
||||
- 외부에 반환하는 결과
|
||||
- 공유되는 데이터
|
||||
- 생성 후 더 이상 바뀌지 않아야 하는 상태
|
||||
|
||||
### 2. mutable 컬렉션은 로컬 조립 단계에서만 허용
|
||||
다음 경우에는 mutable 컬렉션을 허용한다.
|
||||
|
||||
- 여러 source를 모아 결과를 만드는 임시 버퍼
|
||||
- 반복적으로 add/remove가 필요한 내부 알고리즘
|
||||
- aggregate/entity 내부에서 실제 상태 변경이 본질인 경우
|
||||
|
||||
단, 경계를 넘기기 전에 immutable/unmodifiable로 바꾼다.
|
||||
|
||||
### 3. 경계를 넘길 때는 defensive copy를 우선
|
||||
다음 상황에서는 원본 컬렉션을 그대로 넘기지 않는다.
|
||||
|
||||
- 생성자에 받아서 필드로 저장할 때
|
||||
- getter/응답 DTO/결과 객체로 반환할 때
|
||||
- 다른 레이어로 넘길 때
|
||||
|
||||
기본 선택:
|
||||
- snapshot이 목적 -> `List.copyOf`, `Set.copyOf`, `Map.copyOf`
|
||||
- 작은 상수 컬렉션 -> `List.of`, `Set.of`, `Map.of`
|
||||
- stream 수집 결과를 수정 불가로 고정 -> `Collectors.toUnmodifiableList/Set/Map`
|
||||
|
||||
### 4. `Collections.unmodifiableXXX`는 “view”가 필요할 때만
|
||||
이 API는 기본 선택이 아니다.
|
||||
|
||||
허용되는 경우:
|
||||
- 원본과 동기화되는 read-only view가 정말 필요할 때
|
||||
- legacy API와의 호환 때문에 wrapper view가 필요한 경우
|
||||
|
||||
기본 금지 이유:
|
||||
- 원본이 바뀌면 view도 바뀐다
|
||||
- defensive copy나 true immutable 의도와 다르다
|
||||
|
||||
### 5. `copyOf`를 snapshot 기본값으로 사용
|
||||
이미 가지고 있는 mutable collection을 안전하게 보관/반환해야 하면 `copyOf`를 우선 검토한다.
|
||||
|
||||
예:
|
||||
- 생성자에서 받은 list를 필드에 저장
|
||||
- service 결과를 외부에 반환
|
||||
- mapper 결과를 response/domain에 전달
|
||||
|
||||
### 6. 컬렉션 null 금지, empty 우선
|
||||
컬렉션 필드/반환값/파라미터는 가능하면 null을 금지한다.
|
||||
|
||||
기본값:
|
||||
- 없음 -> empty list / set / map
|
||||
- null collection 금지
|
||||
|
||||
### 7. 원소의 immutability를 따로 본다
|
||||
컬렉션만 수정 불가여도 원소가 mutable이면 완전한 불변이 아니다.
|
||||
|
||||
기본 원칙:
|
||||
- 공유되는 컬렉션은 가능하면 immutable element를 담는다
|
||||
- mutable element를 담는 경우 “shallow immutable only”라는 점을 의식한다
|
||||
- 외부에서 element mutation이 가능한 구조를 장기 공유 상태로 두지 않는다
|
||||
|
||||
### 8. 결과 컬렉션의 의미를 명시적으로 선택
|
||||
- 순서가 중요하면 `List`
|
||||
- 중복 제거가 목적이면 `Set`
|
||||
- key lookup이 목적이면 `Map`
|
||||
|
||||
불변이 목적이라면:
|
||||
- `List.copyOf`
|
||||
- `Set.copyOf`
|
||||
- `Map.copyOf`
|
||||
- `Collectors.toUnmodifiable*`
|
||||
|
||||
를 우선 검토한다.
|
||||
|
||||
### 9. iteration order를 가정하지 않는다
|
||||
`Set.of`, `Map.of`, `Map.ofEntries`, `toUnmodifiableSet`, `toUnmodifiableMap`는 iteration order가 랜덤화될 수 있다.
|
||||
순서가 중요하면 `List`나 순서를 보장하는 별도 컬렉션 타입을 명시적으로 사용한다.
|
||||
|
||||
### 10. `Collectors.toList()` 결과를 mutable이라고 가정하지 않는다
|
||||
수집 결과의 mutability가 중요하면 명시적으로 선택한다.
|
||||
|
||||
- 수정 불가 결과 필요 -> `stream.toList()` 또는 `Collectors.toUnmodifiableList()`
|
||||
- 구체 mutable 타입 필요 -> `Collectors.toCollection(ArrayList::new)` 등
|
||||
|
||||
### 11. getter는 내부 mutable collection을 노출하지 않는다
|
||||
다음 패턴을 금지한다.
|
||||
|
||||
- 내부 `ArrayList` 참조를 그대로 반환
|
||||
- 생성자에서 받은 collection 참조를 그대로 필드에 저장
|
||||
- 외부에서 수정 가능한 map/set/list를 그대로 보관
|
||||
|
||||
### 12. domain/application에서 컬렉션은 가능한 한 non-null + stable
|
||||
도메인/유스케이스 내부에서는:
|
||||
- null collection 금지
|
||||
- mutable shared state 최소화
|
||||
- 변경 가능성이 없다면 불변 구조로 고정
|
||||
|
||||
## 프로젝트 기준 요약
|
||||
|
||||
- 기본은 immutable-first
|
||||
- mutable은 로컬 조립 단계에서만 허용
|
||||
- 경계에서는 `copyOf`/`of`/`toUnmodifiable*`를 기본값으로 사용
|
||||
- `Collections.unmodifiableXXX`는 view가 필요할 때만 제한적으로 사용
|
||||
- null collection 대신 empty collection
|
||||
- 원소가 mutable이면 컬렉션만 막아도 완전한 불변이 아님
|
||||
- iteration order가 중요한 곳에서 `Set.of`/`Map.of` 계열 순서를 가정하지 않음
|
||||
Reference in New Issue
Block a user