반응형

Intorduction


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


	@RequestMapping(value = "/file/list", method = RequestMethod.GET)
	@ResponseBody
	public ModelMap list() {
		ModelMap mm = new ModelMap();
		
		List<File> list = fileService.list();
		mm.put("list", list);
		
		return mm;
	}

만약 변환해야 하는 객체가 아래와 같이 순환 참조[각주:1]되는 객체일 경우 JSON 으로 변환을 하다가 에러가 나게 된다.



아래 소스 코드와 같이 간단하게 돌려보면 아래와 같은 에러를 볼 수 있다.


import com.fasterxml.jackson.databind.ObjectMapper;

public class JacksonTest {

	public static void main(String[] args) throws Exception {
		A a = new A();

		for (int i = 0; i < 10; i++) {
			B b = new B();
			b.setA(a);
			a.addB(b);
		}

		ObjectMapper om = new ObjectMapper();
		String json = om.writeValueAsString(a);

		System.out.println(json);
	}

}



A 객체의 bs 속성(List)을 보고 B 객체의 A 객체를 보고 다시 A 객체의 bs 속성을 보고 무한으로 반복 된다.


그러다가 최종적으로 java.lang.StackOverflowError 가 떨어지게 된다.




Solution


순환 참조를 하지 말아야 하는 속성에 @JsonBackReference 어노테이션을 달아주자.


import com.fasterxml.jackson.annotation.JsonBackReference;

public class B {

	private String name = "B";
	// 이거
	@JsonBackReference
	private A a;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public A getA() {
		return a;
	}

	public void setA(A a) {
		this.a = a;
	}

}


다시 테스트 해보면 아래와 같이 B 의 a 속성을 출력하지 않게 된다.


{
  "a": "A",
  "bs": [
    {
      "name": "B"
      // as 속성이 빠지게 된다.
      // , "as": []
    },
    {
      "name": "B"
    },
    {
      "name": "B"
    },
    {
      "name": "B"
    },
    {
      "name": "B"
    },
    {
      "name": "B"
    },
    {
      "name": "B"
    },
    {
      "name": "B"
    },
    {
      "name": "B"
    },
    {
      "name": "B"
    }
  ]
}


  1. 순환 참조(Circular reference)란, 참조하는 대상이 서로 물려 있어서 참조할 수 없게 되는 현상을 말한다. [본문으로]
반응형
//