본문 바로가기
개발/좋은 개발 습관

순환참조(Circular Reference)란? 개념부터 해결 방법까지

by 뇽.뇽 2025. 5. 12.
반응형

개발을 하다 보면 “순환참조”라는 용어를 자주 접하게 됩니다. 특히 Spring Framework, NestJS 같은 의존성 주입(DI) 기반 프레임워크를 사용할 때 종종 순환참조 오류로 애플리케이션이 실행되지 않거나, 직렬화 에러가 발생하기도 하죠.

이번 글에서는 순환참조의 개념과 발생 원인, 위험성 그리고 실무에서 어떻게 해결할 수 있는지까지 정리해보겠습니다.


순환참조란?

순환참조(Circular Reference)는 두 개 이상의 객체나 모듈이 서로를 참조하면서 순환 구조를 이루는 것을 말합니다. 예를 들어 클래스 A가 클래스 B를 참조하고, 클래스 B가 다시 A를 참조하는 구조라면 이것이 순환참조입니다.

class A {
    B b;
    public A(B b) {
        this.b = b;
    }
}

class B {
    A a;
    public B(A a) {
        this.a = a;
    }
}

 

겉보기엔 간단한 구조지만, 이처럼 생성자에서 서로를 의존하게 되면 실제 실행 시 문제가 발생할 수 있습니다. 특히 스프링에서는 순환참조가 있으면 애플리케이션이 아예 기동되지 않기도 합니다.

 


순환참조의 위험성

순환참조는 단순한 코드 문제를 넘어서 애플리케이션의 안정성과 성능에 직접적인 영향을 미칠 수 있습니다. 주요 위험성은 다음과 같습니다.

  • 무한 루프 발생: 잘못된 호출로 인해 재귀적으로 반복되는 로직이 발생할 수 있습니다.
  • 메모리 누수(Leak): 객체들이 서로를 참조하고 있으면 GC(가비지 컬렉터)가 메모리를 해제하지 못해 누수가 발생할 수 있습니다.
  • 직렬화 오류: 예를 들어 Jackson으로 객체를 JSON으로 변환할 때 무한 참조로 인해 StackOverflowError가 발생할 수 있습니다.
  • 앱 실행 실패: Spring에서는 생성자 주입 시 순환참조가 발생하면 애플리케이션이 아예 실행되지 않고 에러를 출력합니다.
  • 구조 파악 어려움: 코드 구조가 복잡해지고, 유지보수 시 의존관계를 파악하기 힘들어집니다.

 


순환참조가 발생하는 일반적인 상황

  1. 생성자 주입
    생성자에서 서로를 의존할 때 가장 쉽게 순환참조가 발생합니다.
  2. 양방향 연관관계 (ORM, JPA)
    엔티티 간 @OneToMany / @ManyToOne 등의 양방향 관계를 설정할 때 순환참조 이슈가 발생하기 쉽습니다.
  3. 모듈 간 import 순환 (TypeScript, Python 등)
    A.ts에서 B.ts를 import하고, 다시 B.ts에서 A.ts를 import할 경우 import 순환 문제가 발생합니다.

 

 

반응형

 

 


순환참조 해결 방법

1. 생성자 주입 대신 Setter 주입 또는 @Lazy 사용

Spring에서는 생성자 대신 Setter 또는 필드 주입(@Autowired)을 사용하거나, @Lazy 어노테이션으로 참조 시점을 지연시켜 순환을 피할 수 있습니다.

@Component
public class A {
    private B b;

    @Autowired
    public void setB(@Lazy B b) {
        this.b = b;
    }
}

 

2. 설계 개선

가장 근본적인 해결책은 구조 자체를 개선하는 것입니다.
예를 들어 A와 B가 서로를 직접 참조하지 않고, 인터페이스나 중간 매개체(예: Mediator)를 통해 간접적으로 연결되도록 설계합니다.

 

3. Jackson 순환참조 어노테이션 활용

Spring에서 JSON 직렬화 시 순환참조로 인한 오류를 방지하려면 @JsonManagedReference 와 @JsonBackReference 를 사용할 수 있습니다.

@JsonManagedReference
private List<Comment> comments;

@JsonBackReference
private Post post;

 

4. 모듈 분리 및 의존성 구조 개선 (TypeScript 등)

import 루프가 발생하는 경우, 공통 모듈로 분리하거나 모듈 간 의존성을 재정의해야 합니다. index.ts 파일을 잘 정리하는 것만으로도 순환참조를 줄일 수 있습니다.

 


마무리하며

순환참조는 단순한 버그 이상의 문제를 유발할 수 있는 중요한 이슈입니다. 특히 프레임워크에 대한 이해 없이 구조를 짜다 보면 어느 순간 앱이 실행되지 않거나, 이유 없는 직렬화 오류가 발생할 수 있습니다.

순환참조를 예방하려면 ‘의존 관계는 단방향이 기본’이라는 원칙을 기억하고, 구조적으로 책임과 역할을 분리하는 설계 습관을 들이는 것이 중요합니다.

반응형