Files
project-auth-server/docs/standards/language/null.md
T

4.7 KiB

null 처리 기준

목적

null은 “값이 없음”을 표현하는 기본 도구가 아니라, 명시적으로 허용된 경계에서만 제한적으로 다루는 값으로 취급한다.

기본값은:

  • non-null
  • 명시적으로 nullable인 경우만 null 허용
  • 내부 로직에서는 가능한 빨리 null을 제거하고 더 명확한 표현으로 바꾼다

공식 의미

  • Java는 null-safety를 타입 시스템으로 직접 표현하지 못한다.
  • 따라서 null 허용 여부는 API 계약, annotation, 검증 코드로 명시해야 한다.
  • Objects.requireNonNull(...)은 메서드/생성자 파라미터 검증 용도로 우선 사용한다.
  • Spring 계열에서는 package/type 수준의 nullability 기본값과 @Nullable 명시를 통해 API 계약을 드러내는 방식을 권장한다.

기본 규칙

1. 기본값은 non-null

명시적으로 nullable이라고 선언되지 않은 값은 non-null로 간주한다.

프로젝트 기본 태도:

  • 파라미터: 기본 non-null
  • 반환값: 기본 non-null
  • 필드: 기본 non-null
  • nullable은 예외적 상황만 명시

2. nullable 여부는 계약으로 드러낸다

다음 중 하나로 null 허용 여부를 명시한다.

  • nullability annotation
  • Optional 반환
  • 빈 컬렉션/빈 문자열이 아닌 명시적 상태 타입
  • API/문서/Javadoc 계약

“읽어보면 알 수 있음” 상태를 금지한다.

3. 경계에서만 null을 받는다

다음 경계에서는 null이 들어올 수 있다고 가정하고 방어한다.

  • 외부 요청 입력
  • DB/JPA 결과
  • 외부 API 응답
  • 설정값/환경변수
  • legacy library API

하지만 경계를 지나 내부 로직으로 들어오면:

  • 즉시 검증하거나
  • Optional/value object/default object/명시적 상태로 변환한다

4. 내부 로직에서 null 전파 금지

application/domain/business 로직에서는 nullable 값을 계속 흘려보내지 않는다.

금지:

  • 여러 계층을 거치며 nullable String/DTO field를 계속 전달
  • null 여부를 business 의미처럼 암묵적으로 해석
  • “일단 null로 두고 나중에 확인” 패턴

5. 파라미터 검증은 가능한 한 즉시

public/protected/package boundary 또는 생성자에서는 필요한 경우 초기에 검증한다.

기본 방식:

  • Objects.requireNonNull(...)
  • 명시적 argument validation
  • request binding / validation annotation
  • value object 생성 시 검증

6. Optional과 null을 섞지 않는다

  • Optional 자체를 null로 두지 않는다
  • Optional을 반환하면 null 반환 금지
  • nullable field를 억지로 Optional field로 바꾸지 않는다

7. 컬렉션/배열/스트림은 null 대신 빈 값 우선

다건 결과는 가능한 한 null 대신 다음을 사용한다.

  • empty list
  • empty set
  • empty map
  • empty stream
  • empty array

null 컬렉션은 기본 금지다.

8. DTO/직렬화 경계는 nullable을 명시적으로 관리

Request/Response DTO에서는 nullable field가 필요할 수 있다.
이 경우:

  • nullable 여부를 명시하고
  • 내부 로직 진입 전에 변환/검증한다.

DTO의 nullable 상태를 domain/application 전체로 전파하지 않는다.

9. Entity와 Domain은 구분해서 본다

  • JPA entity는 DB nullable 제약을 반영할 수 있다
  • domain model은 비즈니스 invariant 기준으로 더 엄격해야 한다

즉:

  • DB가 nullable이어도 domain은 non-null일 수 있다
  • persistence mapper에서 변환/검증 책임을 진다

10. null-check는 가능한 한 한 곳에서 끝낸다

같은 값에 대해 여러 레이어에서 반복 null-check 하지 않는다.

기본 방향:

  • 경계에서 검증
  • 값 객체로 승격
  • 이후는 non-null 가정

11. null을 business state로 쓰지 않는다

다음 패턴을 금지한다.

  • status == null이면 임시 상태
  • provider == null이면 로컬 로그인
  • deletedAt == null 같은 인프라 관례를 business 의미로 직접 사용

business state는 enum, value object, explicit flag, 상태 타입으로 표현한다.

12. annotation 기반 nullability는 일관되게 사용

Spring/JSpecify 스타일을 도입하면:

  • package/type default를 먼저 정하고
  • 예외만 @Nullable로 표시한다
  • 무의미하게 nullable/non-null annotation을 섞지 않는다

프로젝트 기준 요약

  • 기본은 non-null
  • nullable은 계약으로 명시
  • 경계에서 null을 받고 내부에서 제거
  • public/constructor 경계는 requireNonNull 등으로 빠르게 검증
  • 컬렉션/스트림은 null 대신 empty
  • Optional과 null 혼용 금지
  • DTO/JPA nullable을 domain/application에 그대로 전파 금지
  • null을 business meaning으로 사용 금지