JWT 인증에서 로그인 사용자 정보를 담는
CustomUserPrincipal을
record로 만들지 class로 만들지 고민했다.이번 과정의 핵심은 "record가 더 짧다"가 아니라,
"record의 접근자 규칙이 기존 팀 코드와 맞는가"를 이해하는 것이었다.
CustomUserPrincipal은 memberId · email · role을 담아 전달만 하는 불변 값 객체다.
이런 용도엔 record가 어울린다. record 한 줄이면 아래가 전부 자동 생성된다.
public record CustomUserPrincipal(Long memberId, String email, String role) { }
| record가 자동 생성하는 것 | 내용 |
|---|---|
| 필드 | private final memberId, email, role |
| 생성자 | 세 값을 받는 canonical 생성자 |
| 접근자 | memberId(), email(), role() — get 접두어 없음 |
| 부가 메서드 | equals(), hashCode(), toString() |
핵심은 접근자 이름이다. record는 getMemberId()가 아니라 memberId()를 만든다.
우리 팀 코드는 전부 JavaBean 관례(getXxx())로 principal을 호출하고 있었다.
// MemberController
memberService.getMe(principal.getMemberId());
// OrderController (여러 곳)
orderService.createOrder(userPrincipal.getMemberId(), request);
orderService.cancelOrder(userPrincipal.getMemberId(), orderId, request);
record로 만들면 이 호출들이 전부 getMemberId()를 찾지 못해 깨진다.
그래서 다음 이유로 final 필드 + @Getter 일반 class를 택했다.
getXxx() 관례와 호환되어야 하므로final 필드로 record의 장점인 불변성은 그대로 유지할 수 있으므로getMemberId()를 따로 또 만들면 record를 쓰는 의미가 없어지므로@Getter
public class CustomUserPrincipal {
private final Long memberId;
private final String email;
private final String role;
public CustomUserPrincipal(Long memberId, String email, String role) {
this.memberId = memberId;
this.email = email;
this.role = role;
}
}
| 문제 | 원인 | 해결 |
|---|---|---|
principal.getMemberId() 컴파일 에러 | record의 접근자는 memberId() (get 없음) | @Getter 붙인 class로 전환해 getMemberId() 생성 |
| record인데 값이 안 바뀌길 원함 | (record는 기본 불변) | class에서도 필드를 final로 두어 동일하게 불변 확보 |
| getter를 일일이 쓰기 번거로움 | class는 getter 수동 작성 필요 | Lombok @Getter로 boilerplate 제거 |
record가 더 세련됐다고 무조건 좋은 게 아니라,
주변 코드와의 호환을 함께 봐야 한다는 점을 실제로 부딪혀 배웠다.
getXxx() JavaBean 관례를 쓰거나,final 필드로 동일하게 만들 수 있다.record는 불변 데이터 운반용으로 훌륭하고 코드를 크게 줄여준다.
하지만 접근자가 getX()가 아니라 x()라서, 이미 getX() 관례로 짜인
팀 코드와는 충돌한다. 이번엔 여러 컨트롤러가 getMemberId()로 호출하는
공용 객체였기 때문에 final 필드 + @Getter class가 정답이었다.
record는 불변 값 객체엔 최고지만 접근자가
x()다.
팀이getX()를 쓰면 안 맞으므로, 공용CustomUserPrincipal은
final필드 +@Getter클래스로 불변성과 관례를 모두 챙겼다.