java.lang.StackOverflowError: null

한지연·2023년 7월 4일
0

❗️ 문제 상황

api 요청하면 StackOverFlowError를 뱉어냄


🧐 원인 파악

객체를 호출할 때 해당 객체와 참조 관계에 있는 객체와 무한 순환 참조에 걸려서 그렇다고 한다. 잠 시 만 근데 처음에 객체들 맵핑할 때도 관련 문제가 발생해서 연관관계가 있는 부분들에 @JsonIgnore 애노테이션을 넣어서 해결했다.

@Entity
@Table(name = "users")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name ="user_no")
    private Long id;

    @Column(name = "user_id")
    private String userId;

    private String username;

    public void setPassword(String password) {
        this.password = password;
    }

    private String nickname;

    private String password;

    private String email;

    private String phone;

    private String address;

    @Builder
    public User(String userId, String username, String nickname, String password, String email, String phone, String address) {
        this.userId = userId;
        this.username = username;
        this.nickname = nickname;
        this.password =  password;
        this.email = email;
        this.phone = phone;
        this.address = address;
    }

    public void updateNickname(String nickname){ this.nickname = nickname;}
    public void updatePassword(String password){ this.password = password;}
    public void updateEmail(String email){ this.email = email;}
    public void updatePhone(String nickname){ this.phone = phone;}
    public void updateAddress(String address){ this.address = address;}
    
    @JsonIgnore
    @OneToMany(mappedBy = "user")
    private List<Meeting> meetingList = new ArrayList<>();

    @JsonIgnore
    @OneToMany(mappedBy = "user")
    private List<PhotoList> photoList =  new ArrayList<>();

    @JsonIgnore
    @OneToMany(mappedBy = "user")
    private List<Marker> markerList = new ArrayList<>();

    @JsonIgnore
    @OneToMany(mappedBy = "user")
    private List<BeforeList> beforeLists = new ArrayList<>();

    @JsonIgnore
    @OneToMany(mappedBy = "user")
    private List<ChatText> chatTextsList = new ArrayList<>();

이런 식으로 대부분 관련 처리를 해뒀었다. 그래서 뭐가 문제일까 싶어서 검색을 해봤더니.. @Data를 사용한 경우 @Data에 포함된 @toString 때문이라고 한다. 하지만.. 난 toString을 부르는 곳이 없다고 생각했는데? 싶어 계속 검색을 했다. 하다 보니 나처럼 커스텀필터를 만든 사람이 doFilterInternal 메소드를 실행할 때 같은 문제가 발생하는 것을 보고 설마 나도? 하는 마음에 디버깅을 시도해봤다.


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        super.doFilterInternal(request, response, chain);
        String JwtHeader = request.getHeader("Authorization");
        System.out.println("JwtHeader: " + JwtHeader);
        if(JwtHeader == null || !JwtHeader.startsWith("create ")) {
            chain.doFilter(request, response);
            return;
        }

chain.doFilter에 브레이킹 포인트를 걸었다. 관련된 곳들까지 다 보다가 toString을 호출하는 곳을 찾아냈다. proxy객체의 toString을 호출하려고 하니 당연히 무한 순환 참조에 걸릴 수 밖에.. LazyLoading을 걸어놨기 때문에 프록시 객체에 연관관계 객체는 아직 정보가 없다. 그러니 그것을 찾으려고 a에서 b로 b에서 a로 계속 서로 왔다 갔다 하다가 이런 오류가 발생했을 것이다. 근데 왜 authentication toString이 문제가 되는진 모르겠다. 다른 객체도 아니고..? 🧐


🗝️ 문제해결

  • 이전 코드
   @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }


         .addFilter(new JwtAuthenticationFilter(authenticationManager(http.getSharedObject(AuthenticationConfiguration.class))))
         .addFilter(new JwtAuthorizationFilter(authenticationManager(http.getSharedObject(AuthenticationConfiguration.class)), userRepository));
  • 원복한 코드
//SecurityConfig
 http.sessionManagement(httpSecuritySessionManagementConfigurer ->
                httpSecuritySessionManagementConfigurer
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .httpBasic(b -> b.disable())
                .apply(new MyCustomDsl());
                
----------------------------------------------------------------------------

public class MyCustomDsl extends AbstractHttpConfigurer<MyCustomDsl, HttpSecurity> {
        @Override
        public void configure(HttpSecurity http) throws Exception {
            AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
            http
                    .addFilter(new JwtAuthenticationFilter(authenticationManager))
                    .addFilter(new JwtAuthorizationFilter(authenticationManager, userRepository));
        }
    }

authentication이 문제가 되고 있었기 때문에 정말 이것저것 다 시도해보았으나 그냥 코드를 원복 시키는 것으로 해결했다.. 내 스스로 Config 파일을 만들고 해결해보려는 시도는 좋았지만.. 시간이 너무 오래 잡아 먹힐 땐 그냥 되는 것으로 해결하기..^^

profile
배우고 활용하는 것을 즐기는 개발자, 한지연입니다!

0개의 댓글