자바스크립트에서의 순환 참조 문제
🎯 이 문서를 읽고 난 후의 상태
- 순환 참조 문제가 무엇인지 안다.
- 순환 참조 문제를 해결하는 방법을 안다.
- 순환 참조 문제를 예방하는 방법을 안다.
😭 문제 상황
위의 글에서 확인할 수 있는데, 스토리북에서 스토리를 작성하다가 순환 참조 문제를 마주하게 되었다.
평소 순환 참조 문제라는 키워드는 들어봤는데, 정작 제대로 알지 못하고 있다는 생각이 들었다.
그래서 이번 기회에 순환 참조 문제가 무엇인지 확인하고, 이를 해결하는 방법을 알아보려 한다.
🤔 순환 참조 문제란?
순환 참조(Circular Dependencies)
란 두 개 이상의 모듈이 서로를 직접 또는 간접적으로 참조하는 상황을 의미한다.
위의 예시처럼, ModuleA
와 ModuleB
가 서로를 참조하는 상황이 발생하면, 이를 순환 참조 문제라고 한다.
이러한 상황이 발생하면, 모듈이 무한히 서로를 참조하게 되어, 프로그램이 무한 루프에 빠지는 상황이 발생할 수 있으며, 그렇기 때문에 문제
라고 분류한다.
보면 알겠지만, 의외로 소프트웨어 개발에서 흔히 발생하는 문제이며, 특히 모듈 간의 의존성이 복잡해지면 복잡해질 수록 발생할 확률이 높아진다.
코드를 통해 좀 더 살펴보자.
📝 직접 순환 참조 예시
- ModuleA
- ModuleB
// moduleA.js
import { functionB } from './moduleB.js';
export function functionA() {
console.log('Function A');
functionB();
}
// moduleB.js
import { functionA } from './moduleA.js';
export function functionB() {
console.log('Function B');
functionA();
}
위의 코드에서 ModuleA
와 ModuleB
가 서로를 참조하고 있다.
이렇게 되면, functionA
가 호출되면 functionB
가 호출되고, functionB
가 호출되면 functionA
가 호출되는 무한 루프에 빠지게 된다.
이러한 상황이 발생하면, 프로그램이 무한 루프에 빠지게 되어, 프로그램이 정상적으로 동작하지 않게 된다.
📝 간접 순환 참조 예시
순환 참조는 두 개 이상의 모듈이 직접 참조하는 것 뿐만 아니라, 간접적으로 참조하는 경우에도 발생할 수 있다.
- ModuleA
- ModuleB
- ModuleC
// moduleA.js
import { functionC } from './moduleC.js';
export function functionA() {
console.log('Function A');
functionC();
}
// moduleB.js
import { functionA } from './moduleA.js';
export function functionB() {
console.log('Function B');
functionA();
}
// moduleC.js
import { functionB } from './moduleB.js';
export function functionC() {
console.log('Function C');
functionB();
}
위의 코드에서 ModuleA
는 ModuleC
를 참조하고, ModuleC
는 ModuleB
를 참조하고, ModuleB
는 ModuleA
를 참조하고 있다.
여기서 moduleA
→ moduleC
→ moduleB
→ moduleA
순으로 순환 참조가 발생한다.
🤔 순환 참조는 왜 문제가 되는가?
단순히 무한 루프에 빠지는 것 말고 좀 더 구체적인 이유를 살펴보자.
문제 | 설명 |
---|---|
모듈 초기화 문제 | JavaScript 모듈 시스템 (특히 ES 모듈)은 모듈을 로드할 때 먼저 의존성을 모두 로드한 후 실행한다.순환 참조가 있을 경우, 모듈이 완전히 초기화되기 전에 다른 모듈을 참조하게 되어 초기화되지 않은 상태의 값을 참조하게 된다. 이는 undefined 또는 예상치 못한 값이 반환되게 만들 수 있다. |
메모리 누수 | 순환 참조는 가비지 컬렉션(garbage collection 을 방해할 수 있다.특히 객체 간 순환 참조는 메모리 누수를 일으킬 수 있다. 가비지 컬렉션은 어떤 변수의 참조 카운트가 0이 될 때 실행이 되는데, 순환 참조가 발생하면 결코 0이 될 수 없기 때문이다. |
코드 유지보수의 어려움 | 순환 참조는 코드를 이해하고 유지보수하기 어렵게 만든다. 특히 순환 참조가 발생하면, 모듈 간의 의존성이 복잡해지고, 코드를 이해하기 어려워진다. 이는 코드의 재사용성과 확장성을 저해시킨다. |
테스트의 어려움 | 순환 참조는 테스트하기 어렵게 만든다. 특히 모듈 간의 의존성이 복잡해지면, 테스트 코드를 작성하기 어려워진다. 이게 앞서 말한 스토리북 작성 과정에서 내가 겪은 문제기도 했다. |