순환 참조(circular reference)는 서로 다른 두 객체가 서로에 대한 참조를 주고받을 때 발생합니다.
과거의 구형 브라우저에서는 순환 참조가 메모리 누수(memory leak)의 주요 원인이 되곤 했습니다. 하지만 최신 가비지 컬렉션(Garbage Collection) 알고리즘은 순환 구조와 순환 의존성을 충분히 잘 처리할 수 있도록 개선되었기 때문에, 이제는 더 이상 문제가 되지 않습니다.
그러나 순수한 설계 관점에서 보면 순환 참조는 여전히 바람직하지 않으며, 대표적인 코드 악취(code smell)로 꼽힙니다. 순환 참조가 존재한다는 것은 두 객체가 서로 강하게 결합(tight coupling)되어 있음을 의미합니다. 즉, 한 객체를 수정하면 다른 객체도 함께 수정해야 할 가능성이 높아 유지보수가 어려워집니다.
순환 참조 피하는 방법
자바스크립트에서 순환 참조를 피하는 단 하나의 정답 같은 방법은 없습니다. 사용 사례와 상황에 따라 접근 방식이 달라지며, 경우에 따라서는 오히려 순환 참조가 필요할 수도 있습니다. 예시를 통해 살펴보겠습니다.
예를 들어 Dog(개)와 Person(사람)이라는 두 객체가 있다고 가정해 보겠습니다. dog 객체만으로 개의 주인(owner)에 접근하고, person 객체만으로 사람의 반려동물(pet)에 접근하고 싶다면 다음과 같이 코드를 작성하게 됩니다.
let dog = new Dog();
let person = new Person();
// 순환 참조 생성
dog.owner = person;
person.pet = dog;
대부분의 상황에서는 두 객체 중 하나에만 직접 접근하고, 나머지 하나는 그 객체로부터 파생해서 얻으면 됩니다. 이런 경우에는 두 연결 중 하나를 끊어 순환 참조를 제거할 수 있습니다. 만약 양방향 조회가 꼭 필요하다면, 역방향 관계를 별도의 Map으로 관리하는 방법도 고려할 수 있습니다.
하지만 이러한 편법(hack)을 억지로 적용하기보다는, 정말 필요한 구조라면 순환 참조를 그대로 두는 것이 오히려 더 깔끔하고 현실적인 해결책일 수 있습니다.