/ #트러블 슈팅

No.001 - 첵크박스 null값 전송 문제

문제 발생

insertBoard 뷰를 통해 /Board 로 POST 요청시 java.lang.IllegalArgumentException 발생했습니다

img001

원인 파악

board 객체의 isNotice 필드가 null이기 때문에 발생한 오류로 판단했습니다.

템플릿을 확인

먼저 템플릿을 살펴보았습니다.

insertBoard.html
<label class="row label is_notice">
    <input th:if="${isBoardExist}" type="checkbox" th:field="*{isNotice}">
    <input th:unless="${isBoardExist}" type="checkbox" name="isNotice">
</label>

일반적으로 첵크박스의 상태가 checked 일때 “on”이라는 String 타입의 데이터를 전송하는데, 스프링에서 Boolean 타입으로 데이터를 받을 경우 컨버터에 의하여 자동으로 true를 반환합니다.

문제는 checked 상태가 아닐때 입니다. checked 상태가 아니면 클라이언트에서는 값 자체를 전송하지 않습니다. 즉, 서버에는 null값을 받게 됩니다.

템플릿 작성때부터 인지하고 있던 사안인지라 타임리프의 th:field 문법을 통해 해당 문제를 사전에 막았습니다. 템플릿에는 이상없어 보입니다.

Board 객체 확인

오류를 다시 확인해보니 ‘board’ 객체의 필드에 Binding을 하지 못하고 있습니다.

img001

그러므로 Board 클래스를 확인해보았습니다.

Board.java
...
public class Board extends BaseTime {
    ...
    @Column(length = 1)
    @ColumnDefault("'N'")
    @Convert(converter = BooleanToYNConverter.class)
    private boolean isNotice;
    ...
}

문제는 이곳에 있었습니다.

원인 확인

우선 템플릿의 경우 타임리프의 th:field 문법을 사용했는데, 이 문법은 제가 전송한 isNotice의 값을 변환시키는 것이 아니라 hidden 필드를 추가하여 체크박스 존재 자체를 알리는 문법이었던 것입니다. 이 경우 다음과 같은 hidden 필드가 추가되어 서버로 전송됩니다.

th:field 문법
<input type="hidden" name="_isNotice" value="on" />

즉, 히든 필드에 의하여 스프링 서버에 ‘isNotice’ 필드의 값이 체크박스 상태를 담고 있다는 사실을 알리고, checked 속성의 추가 여부를 확인하는 문법이었던 것입니다.

문제는 Board 클래스에 있었습니다. 위에 보시는 것처럼 boolean 타입으로 선언했습니다. 즉, 원시타입으로 선언했습니다. boolean 원시타입의 경우, true, false만 값으로 받을 수 있고, null은 값으로 받을 수 없습니다.

클라이언트에서 체크박스를 클릭하지 않아 isNotice 파라미터의 값은 null로 전송이 되고, 서버는 이 값을 Board 객체의 isNotice 필드에 할당하려 한 것입니다. 그러므로, 타입 캐스팅 오류였던 것입니다.

해결

isNotice 필드가 null값을 할당 받을 수 있도록 boolean 타입의 래퍼 클래스 타입인 Boolean 타입으로 선언해주었습니다.

Board.java
...
public class Board extends BaseTime {
    ...
    @Column(length = 1)
    @ColumnDefault("'N'")
    @Convert(converter = BooleanToYNConverter.class)
    private Boolean isNotice;
    ...
}

Boolean 타입은 참조타입이며, Serializable 인터페이스를 상속받고 있습니다.

public final class Boolean implements java.io.Serializable, java.lang.Comparable, java.lang.constant.Constable {
    ...
}

즉, true, false 뿐만 아니라 null값 또한 인식할 수 있습니다.