우선 오류 로그는 아래와 같았다.
2023-11-27 14:14:19.292 WARN 17136 --- [nio-8080-exec-4] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 2 errors<EOL>Field error in object 'schedule2' on field 'endTime': rejected value [2023-10-30T06:30]; codes [typeMismatch.schedule2.endTime,typeMismatch.endTime,typeMismatch.java.time.LocalDateTime,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [schedule2.endTime,endTime]; arguments []; default message [endTime]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.time.LocalDateTime' for property 'endTime'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.format.annotation.DateTimeFormat java.time.LocalDateTime] for value [2023-10-30T06:30]; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [2023-10-30T06:30]]<EOL>Field error in object 'schedule2' on field 'startTime': rejected value [2023-10-30T05:05]; codes [typeMismatch.schedule2.startTime,typeMismatch.startTime,typeMismatch.java.time.LocalDateTime,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [schedule2.startTime,startTime]; arguments []; default message [startTime]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.time.LocalDateTime' for property 'startTime'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.format.annotation.DateTimeFormat java.time.LocalDateTime] for value [2023-10-30T05:05]; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [2023-10-30T05:05]]]
오류를 대충 해석하자면, schedule2객체의 startTime과 endTime 필드에 오류가 있다는 내용이였다.
필자는 타임리프를 사용하여 스프링부트를 구현중이였다.
시작 시간과 종료시간은 form태그에 담에 post요청을 넣어야 하는 상황이였는데, schedule이라는 모델 객체를 사용하여 조회해오고 요청을 보낼 예정이였다.
하지만 아무리 th:field=*{startTime}과 endTime이 안불러와 지고 요청 또한 안되는 상황이였다!!!
신기하게도
th:value="${schedule.startTime}">
모델의 객체가 아닌 value로 사용하니까 또 잘 조회가 되고 있었다!! 이상하지 않은가?!
하지만 나에게는 post요청을 하려면 th:field가 필요한 상황이였다...
우선 해결을 어떻게 했는지부터 설명하겠다
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
어노테이션을 추가하였다. 그랬더니 해결되었다.. 이거떄매 2틀을 날려먹었다 정말 엿 같은 상황이였다.
원인에 대해 설명하자면
웹 폼이나 API를 통해 전송된 데이터는 기본적으로 문자열(String) 형태입니다. Spring에서는 이 문자열 데이터를 해당 필드의 데이터 타입(LocalDateTime 등)으로 자동 변환이 된다고 한다.
LocalDateTime과 같은 날짜/시간 타입의 경우, Spring은 특정 포맷을 기대합니다. 기본적으로 Spring은 ISO-8601 날짜/시간 포맷을 사용한다고 한 (예: yyyy-MM-
@DateTimeFormat 어노테이션은 Spring에게 특정 필드의 날짜/시간 데이터를 어떻게 파싱해야 하는지 알려줍니다. iso = DateTimeFormat.ISO.DATE_TIME을 설정함으로써, Spring은 LocalDateTime 필드에 대한 입력값을 ISO-8601 날짜 및 시간 포맷으로 해석되도록 설정을 한 것이였다.
그렇다면 왜 th:value에서는 잘 조회가 되었던 것일까?!
th:value가 잘 작동했던 이유는 th:value가 HTML 요소의 값을 설정하는데 사용되기 때문이다.
th:value는 Thymeleaf 템플릿 엔진이 서버로부터 받은 데이터 (이 경우에는 LocalDateTime 객체)를 HTML 페이지의 해당 요소 값으로 렌더링하는 데 사용됩니다.
이 과정에서 LocalDateTime 객체는 자동으로 문자열 형태로 변환되어 HTML 요소의 값으로 채워집니다.th:value의 작동 방식
- 데이터 변환: Thymeleaf는 LocalDateTime 객체를 자동으로 문자열로 변환합니다. 이 변환 과정은 Java의 toString() 메소드나 설정된 포맷터에 따라 진행됩니다.
- HTML 렌더링: 변환된 문자열 값은 HTML 요소의 값으로 설정됩니다. 이 경우 type="datetime-local"인 input 요소의 값으로 설정됩니다.
th:field와의 차이점
- th:field는 Spring의 폼 데이터 바인딩과 관련이 깊습니다. th:field를 사용하면, Thymeleaf는 해당 필드가 바인딩된 객체의 속성과 연결하고, 폼 제출 시 해당 데이터를 서버로 전송하기 위한 준비를 합니다.
- 반면에 th:value는 단순히 현재 값으로 HTML 요소의 값을 설정하는 데 사용됩니다. 서버로부터 전달된 데이터를 사용자에게 보여주는 역할만 수행하며, 폼 제출과 관련된 데이터 바인딩이나 처리는 하지 않습니다.
요약
th:value로 잘 작동했던 이유는, 이 방식이 데이터를 문자열로 변환하여 HTML 요소의 값으로 설정하는 데 초점을 맞추고 있기 때문입니다. th:field를 사용할 때 발생하는 데이터 타입 문제는, 폼 데이터의 서버로의 제출과 관련된 복잡한 바인딩 과정에서 발생했던 것으로 보인다.
웹 브라우저에서 전송된 문자열 형식을 Spring MVC가 LocalDateTime 타입으로 올바르게 변환하지 못하는 데 있었습니다. th:field를 사용하면서 이 변환 과정이 필요했고, @DateTimeFormat 어노테이션을 적용함으로써 이 문제를 해결할 수 있었습니다.
'Spring' 카테고리의 다른 글
[Spring Boot] 외장 톰캣에 WAR 배포하는 오류 톰캣을 돌아가는데 스프링실행안됨 오류 해결 (0) | 2024.08.01 |
---|---|
[IntelliJ] No data sources are configured to run this SQL and provide advanced code assistance. (MyBatis 인텔리제이 인식) (0) | 2024.06.04 |
카페24(절약형) 스프링부트 OutOfMemoryError: Metaspace 오류 해결 (1) | 2023.09.06 |
Static import란? (0) | 2023.01.31 |
Controller, Service, Repository 가 무엇일까? (1) | 2023.01.27 |