❗️문제 코드
private static final Long PING_INTERVAL = 60 * 1000L; // 상수 선언
/**
* 1분마다 자동으로 Ping 메시지 전송
*/
@Scheduled(fixedRate = PING_INTERVAL) // 1분마다 ping 전송
public void scheduledPing() {
sendBroadcastPing();
}
❗️오류 상황:
SSE를 구현하는 과정에서, 클라이언트와 서버 간 연결을 유지하기 위해 1분마다 Ping 메시지를 전송하는 스케줄링 작업을 설정하던 중 문제를 발생
@Scheduled 어노테이션을 사용하여 작업을 실행하려고 했으나, 어노테이션의 fixedRate 속성에 전달된 값이 컴파일 타임 상수가 아니어서 오류 발생
❗️오류 문구 : element value must be a constant expression
@Scheduled(fixedRate = PING_INTERVAL)
💡오류 원인:
60 * 1000L의 결과 타입은 long이므로, 래퍼 클래스인 Long 타입이 되려면 auto-boxing이 필요하다.
final 키워드가 붙어있으면 자동으로 boxing이 허용되지 않으므로 오류가 발생한다.
private static final long PING_INTERVAL = 60 * 1000L;❓선택 이유
60 * 1000L의 결과값이 기본형 long 이므로 boxing이 필요 없다.
Long 객체를 생성하지 않으므로 메모리 효율성이 높다.
private static final Long PING_INTERVAL = Long.valueOf(60 * 1000L);
final이므로 auto-boxing이 적용되지 않기 때문에 명시적으로 설정 가능하다.
❗️Long.valueOf()를 사용하면 불필요한 객체 생성이 발생하여 메모리 낭비가 발생할 수 있다.
@Scheduled(fixedRate = 60 * 1000L) // 1분마다 ping 전송
public void scheduledPing() {
sendBroadcastPing();
}
❗️하드코딩 방식이므로 fixedRate 값을 여러 곳에서 사용하면 유지보수가 어려워진다.
Spring Expression Language (SpEL) 사용하여 설정 가능
@Scheduled(fixedRateString = "${ping.interval}")
public void scheduledPing() {
sendBroadcastPing();
}
application.properties 파일
ping.interval = 5000
❗️값이 변경 가능해야 할 경우에 적합하지만, PING_INTERVAL은 변경되지 않는 상수이다.
❗️파일 의존성이 생긴다. 애플리케이션 실행 시 프로퍼티를 읽어와야 하므로, 설정 파일 없이 실행하면 오류 발생 가능.
💡 상수 설정시에 기본형 (primitive type) 타입을 사용해야 한다!
private static final long PING_INTERVAL = 60 * 1000L;
- 불필요한 boxing을 피할 수 있음 → Long 대신 long 사용하여 메모리 절약.
- 가독성이 좋고 유지보수가 용이함 → 코드가 단순하고 직관적.
- final로 고정된 값이므로 런타임 중 변경이 필요 없음 → 굳이 프로퍼티 파일을 사용할 필요 없음.
- 상수이므로 필요하면 여러 곳에서 재사용 가능
📌 단, 값이 동적으로 변경될 가능성이 있다면 application.properties 기반 설정도 고려 가능!