개발자의 오르막

Thymeleaf 에서 자바스크립트로 객체 값 전송 본문

Trouble Shouting

Thymeleaf 에서 자바스크립트로 객체 값 전송

계단 2019. 10. 31. 10:17

어떤 방법을 쓰더라도 객체가 아닌 string 값은 스크립트에 모두 전송이 됐다.

그러나 컨트롤러에서 받은 객체 값을 전송하는 건 스크립트 단으로 안됐다..ㅠㅠ

 

객체가 아닌 string 값은 스크립트에 모두 전송이 됐다.

 

그러나 컨트롤러에서 받은 객체 값을 전송하는 건 스크립트 단으로 안됐다..ㅠㅠ

일반적으로 사용하는 방법이 CDATA인데,, 일단 방법을 찾아보도록 하겠다.

 

# CDATA

<script th:inline="javascript">
    /*<![CDATA[*/
    var query = [[${query}]];
    console.log(message);
    /*]]>*/
</script>

- 거의 주로 객체를 전달하는 방식이 CDATA이다.

 

# 객체 전달

var exampleDto = [[${dto}]];
console.log("exampleDto : ",exampleDto);

- 이 방법이 내가 주로 썼던 방법이다. 근데 이 방법도 아래와 같은 에러로 안먹히는 걸 보면

  타임 리프의 표현이 잘못된게 아니라 다른 요인이 있다는 것이다.

 


# 해결답안

 

- 이 문제의 원인은 CDATA 에 전송한 객체의 NULL 값 처리를 안해줬기 때문에 생긴 일이었다.

  스크립트에 전달할 dto 가 id에 존재여부에 따라 null 값이 될수 있고, 값을 가질 수 있었다.

  그런데 id 가 없을 때는 null 값임으로 serialize 가 불가한 것이었다.

  밑의 코드처럼 NULL 값 처리를 해주니까 스크립트로 데이터가 잘 넘어갔다.

 

@RequestMapping({"new", "edit/{id}"})
    public ModelAndView resister(@PathVariable(name = "id", required = false) Long id) throws NotFoundException {
        ModelAndView mav = new ModelAndView("cep/homecc/editor");
        if (id != null) {
            ExampleDto dto = exampleService.getExample(id);
            mav.addObject("dto", dto);
        }
        return mav;
    }

 

 


--------------------------------------------------삽질 과정입니다..-------------------------------------------------------------

 

 

# Error Message

An error happened during template parsing

  위의 에러메시지는 템플릿 특정 화면을 랜더링할 때 발생했다는 것을 알려주는 메시지이다.

 

 

An exception was raised while trying to serialize object to JavaScript using Jackson

  이 메시지가 원인인데, serialize object to Javascript using jackson 을 해결해야 한다.

  이 오류메시지를 해결하는 방안은 크게 @JsonIgnore 이나 ObjectMapper 를 활용한다고 소개되어 있다.

  아마 자바스크립트에서 JSON 형태의 데이터를 파싱할 때 오류가 나는 모양이다. ( 객체 → JSON 데이터 )

 

 

com.fasterxml.jackson.databind.JsonMappingException
Infinite recursion (StackOverflowError) 
(through reference chain: 
net.grovesoft.core.model.ConstructionExample["partner"]->
net.grovesoft.core.model.Partner["constructionExampleList"]->
org.hibernate.collection.internal.PersistentBag[0]->
net.grovesoft.core.model.ConstructionExample["partner"]->
net.grovesoft.core.model.Partner["constructionExampleList"]->
org.hibernate.collection.internal.PersistentBag[0]->
net.grovesoft.core.model.ConstructionExample["partner"]->
net.grovesoft.core.model.Partner["constructionExampleList"]->
org.hibernate.collection.internal.PersistentBag[0]

- 이 메시지를 보면, ConstructionExample 모델의 partner 이란 필드가

  Partner 모델의 constructionExampleList 란 필드로 가게 되고,

  이게 PersostentBag[0] 으로 가는데, 다시 partner 란 필드로 가게되면서 무한 루핑이 되는 상태이다.

 


뷰에서

내가 객체를 조회하려고 하는데,
객체의 정보를 불러올 때, Partner 를 불러오려고하고,
이 Partner 에서 constructionExample List 를 다시 불러오려고 하는데
이 ConstructionList안의 ConstructionExample 에는 또 파트너가 있는 거고,
이래서 순환참조 식으로 무한루프가 동작하게되면서
스택오버플로우 현상이 발생하면서
에러가 발생한거지


그러면 내가 양반향, 단방향을 잘못 지정해줬던가
@JsonIgnore 어노테이션을 활용하거나
@ResponseBody 라는 어노테이션을 활용해서 이 현상을 자동적으로 잡게 해주던가
ObjectMapper 를 통해 직렬화, 비직렬화를 설정해주는 방법이 있다.


근데 궁금한건
내가 cosntructionExample 에서 Partner (시공자) 가 누군지 알고 싶은거고
Partner (시공자) 를 봤을 때 시공자가 행했던 ConstructionExample 들을 List로 알고 싶은 건데,
두개의 필드가 존재해야 하지 않을까?

그러면 양방향인가?

 

 


#  Jackson Library 

  Jackson  은 자바용 json  라이브러리이며, Json 분만 아니라 XML/YAML/CSV  등 다양한 형식의 데이타를

  기술할 수 있으며, Json 의 약점 중 하나인 문서화와 데이터 validation 문제를 해결한다.

 

   POJOs to JSON and back

   POJO 기반의 자바 객체들을 JSON 형태의 데이터로 변환할 때 사용하는 라이브러리

 

   SpringBoot 에서 기본으로 포함됨

   @Bean 에 ObjectMapper 등록해놓고 싱글톤으로 주입 받아서 쓰기 때문에 성능도 우수함.

 

 

- 사용법

ObjectMapper mapper = new ObjectMapper(); // create once, reuse

  먼저 ObjectMapper 객체를 생성한다.

 

MyValue value = mapper.readValue(new File("data.json"), MyValue.class);
// or:
value = mapper.readValue(new URL("http://some.com/api/entry.json"), MyValue.class);
// or:
value = mapper.readValue("{\"name\":\"Bob\", \"age\":13}", MyValue.class);

  사용하고자 하는 클래스의 객체를 JSON 형태로 읽고 싶을 때

  ObjectMapper 클래스의 readValue 메소드에 등록한다.

 

mapper.writeValue(new File("result.json"), myResultObject);
// or:
byte[] jsonBytes = mapper.writeValueAsBytes(myResultObject);
// or:
String jsonString = mapper.writeValueAsString(myResultObject);

   사용하고자 하는 객체를 JSON 형태로 쓰고 싶을 때 위의 메소드를 사용한다.

 

 

package test_java;
 
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
 
public class Main 
{
 
    public static void main(String[] args) throws IOException
    {
        ObjectMapper obm  = new ObjectMapper();
    
 
        /** Json문자열 -> Map */
        String jsonStr = "{ \"name\" : \"민\" , \"age\" : 28 }";
        Map<String, Object > map = new HashMap<String, Object>();
        map = obm.readValue(jsonStr, new TypeReference<Map<String, Object>>()  {});
        //System.out.println("jsonString -> map : " + map);
        
        /**Map -> Json문자열 */
        Map<String ,Object> map2 = new HashMap<String, Object>();
        map2.put("name", "미니미니");
        map2.put("age", 100 );
        String jsonStr2 = obm.writerWithDefaultPrettyPrinter().writeValueAsString(map2);
        //System.out.println("map -> jsonString : " + jsonStr2);
        
 
        
        /** json문자열 -> javaObject */
        User user = obm.readValue(jsonStr, User.class);
        
        /**javaObject -> json문자열 */
        User user1 = new User();
        user1.setName("식빵");
        user1.setAge(3);
        
        String user1Str =  obm.writeValueAsString(user1);
        //System.out.println(user1Str);
        
        
        /**List<Object> -> Json문자열 */
        List<User> userList = new ArrayList<User>();
        
        User u1 = new User();
        u1.setName("dodo");
        u1.setAge(38);
        userList.add(u1);
        
        User u2 = new User();
        u2.setName("동길");
        u2.setAge(20);
        userList.add(u2);
        
        User u3 = new User();
        u3.setName("통통");
        u3.setAge(10);
        userList.add(u3);
        
        String userListStr = obm.writeValueAsString(userList);        
        System.out.println(userListStr);
        
        /** JsonList문자열 -> List<Object> */
        List<User> userList2 = obm.readValue(userListStr, new TypeReference<List<User>>(){});
 
                
    }
    
}

 


에러가 발생했을 때 궁금한게, 이게 JAVA 단에서 Binding 에러가 난 게 아니라 JAVASCRIPT 에서 난거면

JSON 형태를 파싱할 때 문제가 발생한 것 아닐까

JackSon 라이브러리는 POJO 자바 객체를 JSON 형태로 파싱할 때 쓰는 ObjectMapper 이고,

 

근데 이거는 insert 에서 각각 input type 의 value 들을 JSON 형태의 객체로 저장 및 전달하고

그럴 POST 전송할 때 사용하는 메서드들인데..

 

- JSON.stringify()

- JSON.parse(객체)

 

- 참조사이트

  https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse

 

JSON.parse()

JSON.parse() 메서드는 JSON 문자열의 구문을 분석하고, 그 결과에서 JavaScript 값이나 객체를 생성합니다.

developer.mozilla.org

 

내가 하고 싶은 건, thymeleaf 에서 받아온 객체를 JAVASCRIPT 에서 객체로 등록하고 싶은 거고,


 

- @JsonIgnore

  JsonIgnore 은 해당 필드 위에 어노테이션의 형태로 사용되는데,

  Json 형태로 데이터를 주고 받을 때 응답결과에 해당 필드를 포함하지 않겠다고 선언하는 경우에 사용된다.

 

 

 

- 직렬화, 비직렬화

 

 

 

- @ObjectMapper

 

 

 

 

# 참조 링크

https://antop.tistory.com/entry/comfasterxmljacksondatabindJsonMappingException-Infinite-recursion-StackOverflowError

 

com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError)

Intorduction 일반적으로 스프링과 com.fasterxml.jackson를 사용하여 컨트롤러 메소드에서 @ResponseBody 어노테이션을 이용하면 알아서 객체가 JSON 으로 변환되어 나가게 된다. @RequestMapping(value = "/file..

antop.tistory.com

- jackson - databind 깃허브 참조 링크

https://github.com/FasterXML/jackson-databind

 

FasterXML/jackson-databind

General data-binding package for Jackson (2.x): works on streaming API (core) implementation(s) - FasterXML/jackson-databind

github.com

- JAVA Json library jackson 사용법

  https://www.lesstif.com/pages/viewpage.action?pageId=24445183

 

Java Json library jackson 사용법

Jackson 은 자바용 json 라이브러리로 잘 알려져 있지만 Json 뿐만 아니라 XML/YAML/CSV 등 다양한 형식의 데이타를 지원하는 data-processing 툴이다.

www.lesstif.com

 

 

Comments