214 lines
4.3 KiB
Markdown
214 lines
4.3 KiB
Markdown
# enum / constants 예시
|
|
|
|
이 문서는 [enum / constants 기준](../../standards/language/enum-constants.md)을 코드 예시로 확인하기 위한 자료입니다.
|
|
핵심 기준은 고정된 의미 집합을 enum으로 표현하고, external code / display label / internal name을 섞지 않는 것입니다.
|
|
|
|
## 좋은 예시 1: 문자열 상수 대신 enum
|
|
|
|
```java
|
|
public enum AuthProvider {
|
|
LOCAL,
|
|
GOOGLE,
|
|
KAKAO
|
|
}
|
|
|
|
if (user.getProvider() == AuthProvider.LOCAL) {
|
|
// local login flow
|
|
}
|
|
```
|
|
|
|
왜 좋은가:
|
|
|
|
- 고정된 값 집합을 타입으로 표현한다.
|
|
- 오타와 매직 스트링 분기를 줄인다.
|
|
|
|
## 좋은 예시 2: external code를 명시적 필드로 분리
|
|
|
|
```java
|
|
public enum AuthProvider {
|
|
LOCAL("local"),
|
|
GOOGLE("google"),
|
|
KAKAO("kakao");
|
|
|
|
private final String code;
|
|
|
|
AuthProvider(String code) {
|
|
this.code = code;
|
|
}
|
|
|
|
public String code() {
|
|
return code;
|
|
}
|
|
}
|
|
```
|
|
|
|
왜 좋은가:
|
|
|
|
- `name()`에 외부 계약을 맡기지 않는다.
|
|
- 내부 enum 이름 변경과 외부 계약을 분리할 수 있다.
|
|
|
|
## 좋은 예시 3: EnumSet 사용
|
|
|
|
```java
|
|
private static final EnumSet<AuthProvider> SOCIAL_PROVIDERS =
|
|
EnumSet.of(AuthProvider.GOOGLE, AuthProvider.KAKAO);
|
|
```
|
|
|
|
왜 좋은가:
|
|
|
|
- enum 집합이라는 의도가 직접 드러난다.
|
|
- 비트 플래그나 일반 `Set`보다 타입 안전하고 목적에 맞다.
|
|
|
|
## 좋은 예시 4: EnumMap 사용
|
|
|
|
```java
|
|
private final EnumMap<AuthProvider, OAuthClient> clients =
|
|
new EnumMap<>(AuthProvider.class);
|
|
```
|
|
|
|
왜 좋은가:
|
|
|
|
- enum key 전용 자료구조라는 점이 명확하다.
|
|
- 일반 `HashMap`보다 목적에 더 잘 맞는다.
|
|
|
|
## 좋은 예시 5: 진짜 상수만 상수로 둠
|
|
|
|
```java
|
|
private static final Duration ACCESS_TOKEN_TTL = Duration.ofMinutes(30);
|
|
private static final List<String> PUBLIC_PATHS = List.of("/", "/login");
|
|
```
|
|
|
|
왜 좋은가:
|
|
|
|
- 값이 immutable이다.
|
|
- `static final`뿐 아니라 실제 의미도 안정적이다.
|
|
|
|
## 나쁜 예시 1: ordinal 저장/분기
|
|
|
|
```java
|
|
int providerCode = provider.ordinal();
|
|
```
|
|
|
|
문제:
|
|
|
|
- enum 순서 변경이나 값 추가에 취약하다.
|
|
- stable contract가 아니다.
|
|
|
|
개선:
|
|
|
|
```java
|
|
String providerCode = provider.code();
|
|
```
|
|
|
|
## 나쁜 예시 2: name/toString 문자열 비교
|
|
|
|
```java
|
|
if (provider.name().equals("GOOGLE")) {
|
|
// google login flow
|
|
}
|
|
```
|
|
|
|
문제:
|
|
|
|
- enum 의미 비교를 문자열 비교로 내린다.
|
|
- 타입 안전성이 사라진다.
|
|
|
|
개선:
|
|
|
|
```java
|
|
if (provider == AuthProvider.GOOGLE) {
|
|
// google login flow
|
|
}
|
|
```
|
|
|
|
## 나쁜 예시 3: 잡다한 constants class
|
|
|
|
```java
|
|
public final class AppConstants {
|
|
|
|
public static final String PROVIDER_LOCAL = "LOCAL";
|
|
public static final String PROVIDER_GOOGLE = "GOOGLE";
|
|
public static final String PROVIDER_KAKAO = "KAKAO";
|
|
|
|
private AppConstants() {
|
|
}
|
|
}
|
|
```
|
|
|
|
문제:
|
|
|
|
- 고정된 의미 집합을 타입으로 표현하지 않는다.
|
|
- 문자열 오타와 분기 누락에 취약하다.
|
|
|
|
개선:
|
|
|
|
```java
|
|
public enum AuthProvider {
|
|
LOCAL,
|
|
GOOGLE,
|
|
KAKAO
|
|
}
|
|
```
|
|
|
|
## 나쁜 예시 4: mutable collection을 상수처럼 사용
|
|
|
|
```java
|
|
private static final Set<String> PUBLIC_PATHS = new HashSet<>();
|
|
```
|
|
|
|
문제:
|
|
|
|
- `static final`이어도 내부 상태는 바뀔 수 있다.
|
|
- 진짜 상수라고 보기 어렵다.
|
|
|
|
개선:
|
|
|
|
```java
|
|
private static final Set<String> PUBLIC_PATHS = Set.of("/", "/login");
|
|
```
|
|
|
|
## 나쁜 예시 5: default로 enum 추가 누락 숨김
|
|
|
|
```java
|
|
return switch (provider) {
|
|
case LOCAL -> localHandler();
|
|
default -> socialHandler();
|
|
};
|
|
```
|
|
|
|
문제:
|
|
|
|
- 새 enum 값이 생겨도 의도치 않게 `default`에 흡수될 수 있다.
|
|
- 분기 누락이 컴파일 시점에 드러나기 어렵다.
|
|
|
|
개선:
|
|
|
|
```java
|
|
return switch (provider) {
|
|
case LOCAL -> localHandler();
|
|
case GOOGLE, KAKAO -> socialHandler();
|
|
};
|
|
```
|
|
|
|
## 나쁜 예시 6: null 회피용 UNKNOWN 남용
|
|
|
|
```java
|
|
public enum AuthProvider {
|
|
UNKNOWN,
|
|
LOCAL,
|
|
GOOGLE,
|
|
KAKAO
|
|
}
|
|
```
|
|
|
|
문제:
|
|
|
|
- `UNKNOWN`이 실제 비즈니스 상태가 아니라면 의미 없는 상태가 생긴다.
|
|
- 단순 null 회피가 enum 모델에 섞인다.
|
|
|
|
개선 방향:
|
|
|
|
- boundary 입력은 검증하거나 nullable로 명시한다.
|
|
- 조회 결과의 부재는 필요하면 `Optional<AuthProvider>`로 표현한다.
|
|
- 실제 비즈니스 상태일 때만 `UNKNOWN` 또는 `UNSPECIFIED`를 둔다.
|