관리 메뉴

공부 기록장 💻

[Spring/Thymeleaf] 타임리프 템플릿 View에서 서버로부터 전달된 model 객체의 필드 값이 null인 경우 해결하기 (Safe Navigation Operator, ${객체?.필드}) 본문

# Tech Studies/Java Spring • Boot

[Spring/Thymeleaf] 타임리프 템플릿 View에서 서버로부터 전달된 model 객체의 필드 값이 null인 경우 해결하기 (Safe Navigation Operator, ${객체?.필드})

dream_for 2023. 2. 16. 17:34

 

문제의 원인

 

알라딘에서 제공하는 Open API 중 키워드를 통한 도서 검색 API, 관심사 카테고리에 따른 도서 목록 API를 이용하여 

입력 값에 따른 도서 목록을 보여주는 프로젝트를 진행하는 중,

도서 목록 결과 페이지에서 model의 attribute으로 등록한 회원 정보가 타임리프 문법의 오류로 나타나지 않는 문제가 발생했다.

 

 

도서 목록 결과를 조회하기 위해 특정 값을 입력하는 경로는, 현재까지 아래의 두 가지 방식이 있다.

  1. 기본적인 회원 정보를 입력하여, 관심사에 따른 추천 도서 목록 추출
  2. 키워드 검색을 통한 도서 목록 추출

 

 

1. 기본적인 회원 정보를 입력하는 페이지

간단하게 이름, 나이를 비롯한 개인 정보를 입력 및 선택하는 방법이다.

 

선택된 관심사에 맞게 추천 도서 목록을 반환한 페이지는 다음과 같다.

위에서 입력한 개인 정보들도 함께 출력되는 것을 확인할 수 있다.

 

 

Controller

컨트롤러 부분에서 MemberInfo 객체를 생성하여 "member" 키에 해당 객체를 모델 속성으로 등록하였고,

 

@RequestMapping(value = "/book/search", method = RequestMethod.GET)
public String createMemberInfo(Model model, BookRecomForm form) {
    MemberInfo memberInfo = new MemberInfo();

    memberInfo.setName(form.getName());
    memberInfo.setAge(form.getAge());
    memberInfo.setGender(form.getGender());
    memberInfo.setInterest(form.getInterest());
    memberInfo.setFeeling(form.getFeeling());

    try {
        memberInfoService.join(memberInfo);
    }catch(IllegalStateException e){
    }
    model.addAttribute("member", form);

    List<BookSearchedResult> results = bookSearchService.searchBooksByCategory(form.getInterest());
    model.addAttribute("results", results);
    return "book/result";
}

 

View

타임리프 문법 th:text="${member.필드}" 를 이용해 위의 각 객체 필드 값들을 출력하였다.

 

<p><a href="/">홈으로 돌아가기</a></p>
<h3> <span th:text="${member?.name}"></span>님에게 추천하는 도서들입니다.</h3>
  <ul>
    <li> 나이 : <span th:text="${member?.age}"></span></li>
    <li> 성별 : <span th:text="${member?.gender}"></span></li>
    <li> 관심사 : <span th:text="${member?.interest}"></span></li>
    <li> 기분 : <span th:text="${member?.feeling}"></span></li>
  </ul>

 

 

2. 키워드 검색 

두번째 방법은 간단하게 메인 화면에서 키워드를 입력하여 도서 목록을 추출하는 방식이다.

예상했던 바와 같이, 위의 Controller에서 입력 받게 되는 member form 이 없기 때문에 내부 서버 에러(HTTP Status Code 500)가 발생하는 것이 당연하다.

 

 

Controller

위의 키워드 검색 컨트롤러는 다음과 같다. 당연히 member 객체가 model 에 등록되는 코드는 존재하지 않는다.
 
@RequestMapping(value = "/book/result", method = RequestMethod.GET)
public String showResult(Model model, BookSearchQuery query) {
    List<BookSearchedResult> results = bookSearchService.searchBooksByQuery(query);
    model.addAttribute("results", results);
    return "book/result";
}

 

 

 

해결 방법

타임리프에서 이러한 null 값을 다루기 위한 방법은 Safe Navigation Operator 을 사용하는 것이다.

(이 글을 통해 해결 방법을 찾을 수 있었다.)

Safe Navigation Operator은 다음의 이름으로도 많이 알려져 있다고 한다. (이름이 4개씩이나..?그만큼 많이 쓰인다는 거겠다..)

Optional chaining operator
safe call operator
null-conditional operator 

이는 이진 오퍼레이터(binary operator)로, 첫 argument가 null 값이면 null 을 리턴한다. 타임리프에서 object의 참조값(그러니까 첫번째 argument)이 null인지 아닌지 확인하여 그 다음 argument들을 확인할 필요가 없다. 따라서 NullPointerException을 발생시키는 대신 그저 null 자체를 리턴하여, th:if, th:unless 와 같은 조건문을 이용해 null을 수동적으로 확인하지 않아도 된다. (아주 간단하다!)

 

 

결국 아래와 같이 각 필드 앖에 ? 을 추가하였다.

 

<p><a href="/">홈으로 돌아가기</a></p>
<h3> <span th:text="${member?.name}"></span>님에게 추천하는 도서들입니다.</h3>
  <ul>
    <li> 나이 : <span th:text="${member?.age}"></span></li>
    <li> 성별 : <span th:text="${member?.gender}"></span></li>
    <li> 관심사 : <span th:text="${member?.interest}"></span></li>
    <li> 기분 : <span th:text="${member?.feeling}"></span></li>
  </ul>

 

그렇게 모든 값이 null로 인식되어 빈 값으로 출력되는 것을 확인할 수 있다.

 

728x90
반응형
Comments