태그
#NESTJS
#NEST
#TYPESCRIPT
#LIBRARY
#TYPEORM
[Nest] Error: A circular dependency has been detected
2025년 1월 2일 15:28

🚀 TypeORM 순환 참조(Circular Dependency) 문제 해결하기
TypeORM을 사용할 때 발생할 수 있는 대표적인 문제 중 하나가
순환 참조(Circular Dependency)
입니다. 이 글에서는 해당 문제가 왜 발생하는지, 어떻게 해결할 수 있는지, 그리고 enum
타입에서도 같은 문제가 발생할 수 있는 이유에 대해 정리합니다.
📚 1. 순환 참조(Circular Dependency)란?
순환 참조란?
두 엔터티가 서로를 참조할 때 발생합니다. 예를 들어 Order
와 ShippingLocation
엔터티가 서로를 참조하게 될 경우, TypeORM은 이를 어떻게 해결해야 할지 알 수 없어 에러를 발생시킵니다.
문제 상황 예시
Order 엔터티
import { Entity, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
import { ShippingLocation } from './shipping-location.entity';
@Entity()
export class Order {
@PrimaryGeneratedColumn()
id: number;
@ManyToOne(() => ShippingLocation, (location) => location.orders)
shippingLocation: ShippingLocation;
}
ShippingLocation Entity
import { Entity, PrimaryGeneratedColumn, OneToMany } from 'typeorm';
import { Order } from './order.entity';
@Entity()
export class ShippingLocation {
@PrimaryGeneratedColumn()
id: number;
@OneToMany(() => Order, (order) => order.shippingLocation)
orders: Order[];
}
에러 메시지
Error: A circular dependency has been detected (property key: "shippingLocation").
Please, make sure that each side of a bidirectional relationship are using lazy resolvers ("type: () => ClassType").
🛠️ 2. 순환 참조의 원인
- TypeScript의 메타데이터 리플렉션: TypeORM은 TypeScript의 리플렉션(
reflect-metadata
)을 사용하여 타입을 분석합니다. - 동일한 파일 로딩 타이밍: 엔터티가 서로를 참조할 때, 어느 한쪽이 완전히 정의되기 전에 다른 한쪽이 이를 참조하려고 시도하면서 문제가 발생합니다.
- 열거형(Enum) 타입 문제:
type
에enum
을 직접 사용하면 TypeORM이 해당enum
의 타입을 즉시 확인하려고 하여 문제가 발생할 수 있습니다.
✅ 3. 해결 방법
방법 1: Lazy Resolver 사용하기
type
속성에 직접 클래스를 지정하지 말고,
람다 함수(() => ClassType
)
를 사용해 TypeORM이 참조를 지연하도록 합니다.
Order 엔터티 수정
import { Entity, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
import { ShippingLocation } from './shipping-location.entity';
@Entity()
export class Order {
@PrimaryGeneratedColumn()
id: number;
@ManyToOne(() => ShippingLocation, (location) => location.orders)
shippingLocation: ShippingLocation;
}
ShippingLocation 엔터티 수정
import { Entity, PrimaryGeneratedColumn, OneToMany } from 'typeorm';
import { Order } from './order.entity';
@Entity()
export class ShippingLocation {
@PrimaryGeneratedColumn()
id: number;
@OneToMany(() => Order, (order) => order.shippingLocation)
orders: Order[];
}
📝 핵심 포인트:
type
에() => ShippingLocation
와 같은 람다 표현식을 사용해 TypeORM이 지연 로딩하도록 합니다.
방법 2: Import 순환 참조 해결
경우에 따라
import 순서
가 원인일 수도 있습니다. 아래와 같이 import
를 변경해보세요.
Order 엔터티 수정
import { Entity, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
@Entity()
export class Order {
@PrimaryGeneratedColumn()
id: number;
@ManyToOne(() => import('./shipping-location.entity').then(m => m.ShippingLocation), (location) => location.orders)
shippingLocation: ShippingLocation;
}
import()
동적 임포트를 사용하여 런타임에 참조가 설정되도록 만듭니다.
방법 3: Enum 타입 사용 문제 해결
enum
타입을 type
에 직접 사용하면 순환 참조와 유사한 문제가 발생할 수 있습니다. type
속성 대신 () =>
를 사용해 명확하게 정의합니다.
잘못된 예시
@Column({
type: 'enum',
enum: MyEnum,
})
status: MyEnum;
올바른 예시
@Column({
type: 'enum',
enum: () => MyEnum,
})
status: MyEnum;
📝 핵심 포인트:
type
에enum
을 직접 사용하지 말고 람다 표현식으로 감싸서 사용하세요.
🧠 4. 순환 참조를 피하기 위한 권장 사항
- Lazy Resolver(
() => ClassType
) 를 사용하여 참조를 지연합니다. - import() 동적 임포트를 사용하여 순환 참조를 피합니다.
enum
을 사용할 때는() => MyEnum
과 같은 방식으로 참조합니다.- 가능한 경우, 양방향 관계를 단방향 관계로 바꾸는 것도 고려해보세요.
- 리팩토링을 통해 공통된 부분을 별도의 중간 테이블로 분리할 수도 있습니다.
📊 5. 비교 요약
문제 원인 | 해결 방법 | 예시 |
---|---|---|
양방향 관계 | Lazy Resolver 사용 | () => ClassType |
import 순환 참조 | 동적 import 사용 | import('./entity').then(m => m.Class) |
Enum 타입 | Lazy Resolver 사용 | enum: () => MyEnum |
복잡한 의존성 | 중간 엔터티로 분리 | JoinTable 등 사용 |
🚀 6. 마무리
- 순환 참조는 TypeORM에서 흔히 발생할 수 있는 문제입니다.
- 올바른
type
설정과Lazy Resolver
사용은 대부분의 문제를 해결할 수 있습니다. - enum 타입에서도 같은 원인이 발생할 수 있으므로 주의해야 합니다.
이제 TypeORM 순환 참조 문제를 이해하고 해결할 수 있을 것입니다. 여러분의 프로젝트에 맞는 해결 방법을 선택하여 안정적인 관계를 설계하세요! 🚀✨
🔗 참고 링크: