1. 문제 상황
SEMI 표준을 기반으로 장비와 호스트 간 통신을 구현하는 프로젝트를 진행하던 중, 검사일시와 같은 측정 데이터를 SECS-2 메시지를 통해 전송해야 하는 상황이 있었다. 이를 위해 DB에서 측정일시 컬럼의 값을 읽고, 해당 값을 GEM의 SVID(Variable)에 저장한 뒤, 조건에 따라 이벤트를 발생시키는 구조였다.
하지만 SECS-2 메시지에는 Timestamp 타입이 존재하지 않기 때문에, 값을 String 형태로 저장하게 되었고, 이후 해당 값을 다시 꺼내 Timestamp로 캐스팅하여 사용하려는 과정에서 ClassCastException이 발생하게 되었다.
실제 코드는 회사에서 사용하고 있기 때문에 보안상 공개하지 못하고 비슷한 상황을 상정해서 예시를 들고 진행하도록 하겠다.
📌 문제 재현 예시 (유사 상황 구성)
// 잘못된 예: Timestamp를 String으로 저장하고 다시 캐스팅
Map<String, Object> storage = new HashMap<>();
Timestamp ts = Timestamp.valueOf("2024-04-18 14:00:00");
storage.put("timestamp", ts.toString()); // String으로 저장됨
// 나중에 다시 꺼내서 Timestamp로 캐스팅
Timestamp result = (Timestamp) storage.get("timestamp"); // ❌ ClassCastException
Object를 Timestamp로 캐스팅할 때는 컴파일(구문) 오류는 발생하지 않지만, 실행 시점(runtime)에 실제 타입이 다르면 ClassCastException이 발생한다.
발생하는 예외 메시지
java.lang.ClassCastException: java.lang.String cannot be cast to java.sql.Timestamp
2. 원인 분석
- toString()을 통해 Timestamp를 String으로 변환했기 때문에
- Map에는 Object 타입으로 들어있지만 실제 런타임 타입은 String
- 그런데 캐스팅을 Timestamp로 했기 때문에 ClassCastException 발생
타입이 Object이지만 , 실제 런타임 타입은 String이기 때문에 문제가 발생한다.
3. 올바른 해결 방법
방법 1: Timestamp.valueOf(String) 사용
Object raw = storage.get("timestamp");
if (raw instanceof String) {
Timestamp result = Timestamp.valueOf((String) raw);
}
instanceof : Object 타입의 변수 안에 실제로 어떤 타입의 객체가 들어 있는지를 확인
방법 2: 애초에 Timestamp 그대로 저장하고 꺼낼 때 캐스팅
storage.put("timestamp", ts); // Timestamp 그대로 저장
Timestamp result = (Timestamp) storage.get("timestamp");
이 방식은 타입 정보가 손실되지 않는 환경에서만 가능했기 때문에 내 상황에서는 적용하지는 못한다.
4. 교훈
- 객체 저장 시 타입 일관성을 유지하는 것이 매우 중요하다
- 컴파일(구문) 오류가 발생한다고 안심하지 않는다
- Map<String, Object> 또는 유사한 컨텍스트에서 instanceof 체크는 필수
'트러블 슈팅' 카테고리의 다른 글
리눅스에서 .sh 실행 시 ^M (bad interpreter) 오류 트러블슈팅 (0) | 2025.04.24 |
---|---|
[Java/Felix] SQL Server JDBC 연결 오류 트러블슈팅 (0) | 2025.04.21 |
윈도우에서 특정 포트로 외부 요청이 들어오지 않을 때 확인할 것들 (0) | 2025.04.04 |
[Java]UnsupportedClassVersionError 해결하기(JDK 8로 다시 컴파일하는 방법) (0) | 2025.03.18 |
[Struts2] CRUD 게시판 구현을 하면서 생겼던 문제들 (0) | 2025.03.17 |