본문 바로가기
Tips/Spring Boot

[Spring Boot] Infinite recursion (StackOverflowError) 오류 날 때

by DevJaewoo 2022. 2. 23.
반응형

Intro

Spring Repository에서 객체를 조회하는 도중에 아래의 에러가 발생했다.

2022-02-23 10:44:22.914 ERROR 58164 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception 
[Request processing failed; nested exception is org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError); 
nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) 
(through reference chain: com.devjaewoo.springtest.entity.Client["authorities"]->
org.hibernate.collection.internal.PersistentSet[0]->
com.devjaewoo.springtest.entity.ClientAuthority["client"]->
com.devjaewoo.springtest.entity.Client["authorities"]->
org.hibernate.collection.internal.PersistentSet[0]->
com.devjaewoo.springtest.entity.ClientAuthority["client"]->
com.devjaewoo.springtest.entity.Client["authorities"]->
org.hibernate.collection.internal.PersistentSet[0]->
com.devjaewoo.springtest.entity.ClientAuthority["client"]->

...

com.devjaewoo.springtest.entity.Client["authorities"]->
org.hibernate.collection.internal.PersistentSet[0]->
com.devjaewoo.springtest.entity.ClientAuthority["client"]->
com.devjaewoo.springtest.entity.Client["authorities"])] with root cause

 

오류를 보면 Client 클래스와 ClientAuthority 클래스를 계속 왔다 갔다 하며 무한 반복되는 것을 볼 수 있다.

현재 ClientClientAuthority 클래스는 Many to Many 관계를 표현하기 위해 아래와 같이 구성되어있다.

 

Client.java

@Entity
@Table(name = "client")
@Getter
@Setter
@Builder
@NoArgsConstructor
public class Client {

    ...

    @OneToMany(mappedBy = "client")
    private Set<ClientAuthority> authorities;
    
    ...
}

 

ClientAuthority.java

@Entity
@Table(name = "client_authority")
@Getter
@Setter
@Builder
@NoArgsConstructor
public class ClientAuthority {

    ...

    @ManyToOne
    @JoinColumn(name = "client_id")
    private Client client;
    
    ...
}
반응형

에러 발생 원인

코드를 보면 알다시피 Client 클래스에 ClientAuthority 변수가 있고, ClientAuthority에 Client 변수가 있다.

그래서 Jackson 라이브러리에서 Client JSONObject를 만드는 도중 아래의 과정을 무한 반복하는 것이다.

 

Client 클래스의 getter 조회 -> getAuthorities를 실행해 Set<ClientAuthority> 반환받음 -> 각 ClientAuthority 클래스의 getter 조회 -> getClient를 실행해 Client를 반환 받음 -> Client 클래스의 getter 조회 -> ...

 

이런 무한반복을 해결하기 위해선 서로 계속 조회하는 굴레를 끊어줘야 하는데,

Authority 관련 getter를 별도로 생성해 주는 방식으로 해결했다.

 

Client.java

public class Client {

    ...

    @JsonIgnore
    @OneToMany(mappedBy = "client")
    private Set<ClientAuthority> clientAuthorities;
    
    ...
}

 

우선 기존의 authorities 변수를 clientAuthorities로 변경하고, @JsonIgnore 어노테이션을 추가했다.

@JsonIgnore 어노테이션을 추가하면 Jackson에서 JSONObject를 만들 때 해당 변수는 추가하지 않는다.

 

하지만 authorities 정보는 들어가야 하기 때문에, ClientAuthority가 아닌 Authority를 직접 반환해주는 함수를 별도로 만들었다.

 

public class Client {

    ...

    @JsonIgnore
    @OneToMany(mappedBy = "client")
    private Set<ClientAuthority> clientAuthorities;

    @JsonProperty("authorities")
    public Set<Authority> getAuthorities() {
        return clientAuthorities.stream().map(ClientAuthority::getAuthority).collect(Collectors.toSet());
    }
    
    ...
}

 

이렇게 수정하면 Jackson에서 Set<ClientAuthority>가 아닌 Set<Authority>를 조회해 계속 서로를 조회하는 일이 없도록 막아준다.


수정 결과

수정 후 다시 해보니 잘 된다.

 

반응형