소프트웨어 개발에서 순환 참조(Circular Reference)란 두 개 이상의 모듈, 객체 또는 클래스가 서로를 직접적 또는 간접적으로 참조할 때 발생하는 현상이다. 순환 참조는 소프트웨어의 유지보수를 어렵게 하고, 메모리 누수(memory leak)나 성능 저하 등의 문제를 유발할 수 있으므로, 이를 방지하거나 해결하는 것이 중요하다.
이번 포스팅에서는 순환 참조의 발생 원인과 예시, 문제점, 그리고 이를 해결하는 방법까지 자세히 알아본다.
순환 참조는 다음과 같은 형태로 발생할 수 있다.
클래스 또는 모듈 간 서로를 직접 참조할 때
복수의 클래스나 모듈이 연쇄적으로 서로 참조할 때 (간접 참조)
이러한 순환 구조는 주로 다음과 같은 상황에서 발생한다.
다음은 간단한 예시로 설명한 클래스 간 순환 참조이다.
# a.py
from b import ClassB
class ClassA:
def __init__(self):
self.b = ClassB()
# b.py
from a import ClassA
class ClassB:
def __init__(self):
self.a = ClassA()
위와 같이 클래스 ClassA와 ClassB가 서로를 직접 참조하면 순환 참조가 발생하게 된다.
순환 참조로 인해 발생할 수 있는 주요 문제점은 다음과 같다.
순환 참조 문제를 해결하는 방법은 크게 다음과 같이 정리할 수 있다.
순환 참조 문제를 해결하는 가장 일반적인 방법은 중간 추상 계층(인터페이스)을 두어 직접적인 참조를 끊는 것이다.
# interface.py
from abc import ABC, abstractmethod
class IClassB(ABC):
@abstractmethod
def method_b(self):
pass
class IClassA(ABC):
@abstractmethod
def method_a(self):
pass
위 인터페이스를 통해 간접 참조로 변환하면 다음과 같이 구현한다.
# a.py
from interface import IClassB
class ClassA:
def __init__(self, b: IClassB):
self.b = b
# b.py
from interface import IClassA
class ClassB:
def __init__(self, a: IClassA):
self.a = a
이러한 방식을 사용하면 순환 참조를 피할 수 있다.
객체의 책임을 분명히 하고 클래스를 잘게 나누어 단방향 참조 구조로 변경한다.
순환 참조:
A ↔ B
해결된 구조:
A → C ← B
위와 같이 공통적으로 의존하는 클래스를 새로 만들어 클래스 간 직접 참조를 끊는다.
객체의 생성을 외부에서 주입하여 클래스 내에서 직접 객체를 생성하지 않도록 한다. DI를 통해 순환 참조를 방지할 수 있다.
예를 들어, Python 코드에서 DI 적용은 다음과 같이 나타난다.
# main.py
from a import ClassA
from b import ClassB
a = ClassA(None)
b = ClassB(a)
a.b = b
이 방식은 객체의 생성과 관리를 외부에서 담당하여 순환 참조 문제를 제거한다.
Python에서는 약한 참조(Weak Reference)를 통해 순환 참조를 방지할 수 있다. 이는 객체가 서로를 참조하지만 참조 카운트를 증가시키지 않게 하여, 메모리 누수를 방지한다.
import weakref
class A:
def __init__(self):
self.b = None
class B:
def __init__(self, a):
self.a = weakref.ref(a) # 약한 참조 사용
a = A()
b = B(a)
a.b = b
이 방식으로 참조하면 객체는 가비지 컬렉터가 자동으로 수거할 수 있게 된다.
순환 참조는 소프트웨어 설계 및 유지보수 측면에서 중대한 문제를 야기할 수 있다. 이러한 순환 참조를 해결하기 위해서는 추상화 계층의 도입, 의존성 주입(DI), 클래스의 책임 분리 및 Weak Reference와 같은 기술적 해결책을 적극적으로 활용하는 것이 중요하다. 개발자는 소프트웨어 설계 초기에 이러한 순환 참조 문제를 인지하고 예방하는 습관을 들여야 한다.