티스토리 뷰
✏️ 들어가기 전에 — Lombok의 @ToString이란?
Lombok의 @ToString을 클래스에 붙이면 컴파일 시점에 모든 필드를 포함한 toString() 메서드를 자동으로 생성해준다.
@ToString
public class User {
private Long id;
private String name;
private String password;
}
위 코드는 컴파일 후 아래와 동일하게 동작한다.
편리하지만 password처럼 민감한 필드까지 그대로 출력된다는 문제가 있다. 이를 해결하기 위한 방법이 exclude와 @ToString.Exclude다.
@Override
public String toString() {
return "User(id=" + id + ", name=" + name + ", password=" + password + ")";
}
✏️ 방법 1 — @ToString(exclude = {...}) : 클래스 레벨
클래스 선언부의 어노테이션에 제외할 필드명을 문자열 배열로 나열하는 방식이다. 생성되는 toString()은 아래와 같다.
@ToString(exclude = {"password", "clientSecret"})
public class UserConfig {
private Long id;
private String name;
private String password; // toString()에서 제외
private String clientSecret; // toString()에서 제외
private String redirectUri;
}
@Override
public String toString() {
return "UserConfig(id=" + id + ", name=" + name + ", redirectUri=" + redirectUri + ")";
}
✏️ 방법 2 — @ToString.Exclude : 필드 레벨
제외하고 싶은 필드 바로 위에 직접 어노테이션을 붙이는 방식이다. 생성되는 toString()은 방법 1과 동일하다.
@ToString
public class UserConfig {
private Long id;
private String name;
@ToString.Exclude
private String password; // 이 필드만 제외
@ToString.Exclude
private String clientSecret; // 이 필드만 제외
private String redirectUri;
}
✏️ 결과는 같다 — 그렇다면 진짜 차이는?
1. 리팩토링 안전성
@ToString(exclude = {...})은 필드명을 문자열로 지정한다. 문자열은 컴파일러가 추적하지 않기 때문에, 필드명을 바꿔도 exclude 배열은 자동으로 갱신되지 않는다.
// Before
@ToString(exclude = {"clientSecret"})
public class OAuthConfig {
private String clientSecret;
}
clientSecret이라는 필드가 없어졌으므로 exclude는 아무 효과도 없게 된다. 컴파일 에러도 발생하지 않고, IDE 경고도 없다. secretKey가 toString()에 그대로 노출되는 버그가 조용히 발생한다.
// After - 필드명을 변경했지만 exclude 배열은 그대로
@ToString(exclude = {"clientSecret"}) // ← "clientSecret"은 이제 존재하지 않는 필드명
public class OAuthConfig {
private String secretKey; // 리팩토링으로 이름 변경
}
반면 @ToString.Exclude는 필드 선언 자체에 붙어 있으므로, IntelliJ에서 필드명을 Rename Refactoring하면 어노테이션도 해당 필드와 함께 이동한다. 필드가 삭제되면 어노테이션도 함께 사라진다.
@ToString
public class OAuthConfig {
@ToString.Exclude
private String secretKey; // 필드명을 바꿔도 @ToString.Exclude는 항상 이 필드에 붙어있다
}
2. 가독성
@ToString(exclude = {...})는 제외 규칙이 클래스 상단에 몰려 있다. 필드가 많아질수록 클래스 상단의 문자열 배열이 길어지고, 실제 필드 선언부를 봤을 때 이 필드가 로그에 찍히는지 아닌지를 즉시 파악하기 어렵다. 클래스 상단과 필드 선언부를 번갈아 확인해야 한다.
@ToString(exclude = {"password", "clientSecret", "refreshToken", "apiKey"})
public class SomeService {
private String username;
private String password;
private String clientId;
private String clientSecret;
private String accessToken;
private String refreshToken;
private String apiKey;
private String redirectUri;
}
@ToString.Exclude는 필드 선언 바로 위에 어노테이션이 있으므로, 코드를 읽는 순간 이 필드가 toString()에서 제외된다는 사실을 즉시 파악할 수 있다.
@ToString
public class SomeService {
private String username;
@ToString.Exclude
private String password; // 한눈에 파악 가능
private String clientId;
@ToString.Exclude
private String clientSecret; // 한눈에 파악 가능
private String accessToken;
@ToString.Exclude
private String refreshToken; // 한눈에 파악 가능
@ToString.Exclude
private String apiKey; // 한눈에 파악 가능
private String redirectUri;
}
3. 신규 필드 추가 시 실수 가능성
@ToString(exclude = {...}) 방식에서는 민감한 필드를 새로 추가할 때 exclude 배열에 추가하는 것을 빠뜨릴 수 있다.
@ToString(exclude = {"password", "clientSecret"})
public class OAuthConfig {
private String password;
private String clientSecret;
private String newSensitiveKey; // 새로 추가했지만 exclude에 깜빡 추가 안 함 → 노출
}
@ToString.Exclude 방식은 필드 바로 위에 붙이므로 추가와 동시에 처리하게 되어 누락 가능성이 낮다.
@ToString
public class OAuthConfig {
private String password;
private String clientSecret;
@ToString.Exclude // 추가와 동시에 처리
private String newSensitiveKey;
}
✏️ 한 발 더 — onlyExplicitlyIncluded + @ToString.Include
exclude 방식의 근본적인 문제는 기본적으로 모든 필드가 출력된다는 것이다. 새 필드를 추가할 때마다 민감 여부를 판단해서 exclude에 추가해야 한다. 보안이 중요한 클래스라면 반대로 접근하는 것이 더 안전하다. 기본적으로 모든 필드를 숨기고, 명시적으로 허용한 필드만 출력하는 방식이다.
이 방식의 핵심 장점은 새로운 필드가 추가되더라도 기본적으로 출력에서 제외된다는 점이다. 개발자가 실수로 민감한 필드를 toString()에 노출시키는 사고를 구조적으로 방지할 수 있다.
@ToString(onlyExplicitlyIncluded = true) // 기본적으로 모든 필드 숨김
public class OAuthConfig {
@ToString.Include
private String clientId; // 출력 O — 명시적으로 허용
private String clientSecret; // 출력 X — 명시 안 했으므로 자동 제외
@ToString.Include
private String redirectUri; // 출력 O — 명시적으로 허용
private String password; // 출력 X — 명시 안 했으므로 자동 제외
private String newSecretField; // 출력 X — 새로 추가해도 기본 제외
}
✏️ 전체 비교 요약
| 구분 | @ToString(exclude={}) | @ToString.Exclude | onlyExplicitlyIncluded |
| 적용 위치 | 클래스 선언부 | 개별 필드 | 클래스 선언부 + 필드 |
| 기본 동작 | 전체 출력, 지정 필드 제외 | 전체 출력, 지정 필드 제외 | 전체 숨김, 지정 필드만 출력 |
| 리팩토링 안전성 | 문자열이라 자동 추적 안 됨 | IDE 리팩토링 시 자동 반영 | IDE 리팩토링 시 자동 반영 |
| 신규 필드 추가 시 | 수동으로 exclude에 추가 필요 | 수동으로 어노테이션 추가 필요 | 자동으로 숨겨짐 |
| 가독성 | 클래스 상단에 집중 | 필드 단위로 분산 | 필드 단위로 분산 |
| 권장 상황 | 제외 필드가 1~2개이고 변경이 거의 없을 때 | 제외 필드가 여러 개이고 리팩토링이 잦을 때 | 보안이 중요한 설정 클래스 |
✏️ 정리
셋 중 어느 방법을 선택하느냐는 결국 새 필드가 추가됐을 때 기본값을 노출로 할 것인가, 숨김으로 할 것인가의 문제다.
일반적인 클래스라면 @ToString.Exclude가 리팩토링 안전성과 가독성 면에서 @ToString(exclude = {...})보다 낫다.
하지만 API 키, 시크릿, 비밀번호 등 민감한 설정값이 많은 클래스라면 onlyExplicitlyIncluded + @ToString.Include 조합으로 출력을 허용 목록(allowlist) 방식으로 관리하는 것이 가장 방어적인 선택이다.
'PL > JAVA' 카테고리의 다른 글
| [Java / 자바] 명품 자바 프로그래밍 14장 실습 문제 (0) | 2024.06.26 |
|---|---|
| [Java / 자바] 명품 자바 프로그래밍 12장 실습 문제 (0) | 2024.06.25 |
| [Java / 자바] 명품 자바 프로그래밍 11장 실습 문제 (0) | 2024.06.22 |
| [Java / 자바] 명품 자바 프로그래밍 10장 실습 문제 (0) | 2024.06.21 |
| [Java / 자바] 명품 자바 프로그래밍 9장 실습 문제 (0) | 2024.06.21 |
- Total
- Today
- Yesterday
- CSS
- html
- C++ Stack
- 우선순위 큐
- js
- 스프링 부트 crud 게시판 구현
- 에라토스테네스의 체
- 유클리드 호제법
- c++ string
- HTML5
- 자료구조
- BFS
- 자바스크립트
- C++
- 반복문
- 유니온 파인드
- 백준 풀이
- java
- 백준
- 자바
- DP
- 이분 매칭
- 카운팅 정렬
- 스택
- 알고리즘 공부
- DFS
- 세그먼트 트리
- 알고리즘
- 투 포인터
- Do it!
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |
