본문 바로가기

🚨[Nextjs] TypeORM 순환참조 Error - TypeORM N+1 - Lazy Loading

@Jeeqong 2024. 10. 1. 17:36
반응형

에러 메세지

ReferenceError: Cannot access 'User' before initialization at Module.User (webpack-internal:///(api)/./src/models/user.ts:3:51) at eval (webpack-internal:///(api)/./src/models/user-info.ts:24:129)

 

import { Entity, PrimaryGeneratedColumn, Column, OneToOne, OneToMany, CreateDateColumn, UpdateDateColumn } from 'typeorm';
import { UserInfo } from '@/models/user-info';
import { Task } from '@/models/task';
import { TaskLabel } from '@/models/task_label';

@Entity({ name: 'user_orm_v2' })
export class User {
	@PrimaryGeneratedColumn('uuid')
	id: string;
	...중략...

	@OneToOne(() => UserInfo, (userInfo) => userInfo.user, { cascade: true })
	user_info: UserInfo;  // UserInfo와 1:1 관계
}

 

import { Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn, UpdateDateColumn } from 'typeorm';
import { User } from '@/models/user';

@Entity({ name: 'user_info_orm_v2' })
export class UserInfo {
	@PrimaryGeneratedColumn('uuid')
	id: string;

	@OneToOne(() => User, (user) => user.user_info)
	@JoinColumn()
	user: User;  // User와 1:1 관계

	..중략..
}

원인

순환 참조(circular reference) 문제로 인해 발생하는 것입니다. User와 UserInfo 두 엔티티가 서로를 참조하는 과정에서, 한쪽이 아직 초기화되지 않았을 때 다른 쪽에서 접근하려고 하면서 발생하는 문제입니다. TypeORM에서 두 엔티티가 서로를 동시에 참조할 때 자주 발생하는 문제


해결 방법: 지연 로딩(Lazy Loading)

TypeORM에서는 람다 함수를 사용하여 참조가 필요할 때 해당 엔티티를 불러오도록 지연 로딩을 설정할 수 있습니다. 이 방법을 사용하면 순환 참조 문제를 해결할 수 있다.
또한 서로 참조하는 양방향 관계이기 때문에 User 쪽도 Promise<UserInfo>로 바꿔주는 게 좋음.

import { Entity, PrimaryGeneratedColumn, Column, OneToOne, OneToMany, CreateDateColumn, UpdateDateColumn } from 'typeorm';
import { UserInfo } from '@/models/user-info';
import { Task } from '@/models/task';
import { TaskLabel } from '@/models/task_label';

@Entity({ name: 'user_orm_v2' })
export class User {
	@PrimaryGeneratedColumn('uuid')
	id: string;

	..중략..
    
	@OneToOne(() => UserInfo, (userInfo) => userInfo.user, { cascade: true })
	user_info: UserInfo;  // UserInfo와 1:1 관계
}
import { Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn, UpdateDateColumn } from 'typeorm';
import { User } from '@/models/user';

@Entity({ name: 'user_info_orm_v2' })
export class UserInfo {
	@PrimaryGeneratedColumn('uuid')
	id: string;
    
    // 지연 로딩으로 순환 참조 문제 해결
	@OneToOne(() => User, (user) => user.user_info, { lazy: true })
	@JoinColumn({ name: 'userId' })
	user: Promise<User>; // User와 1:1 관계

	..중략..
}
// user.ts
@OneToOne(() => UserInfo, (userInfo) => userInfo.user, { cascade: true, lazy: true })
user_info: Promise<UserInfo>;
// user-info.ts
@OneToOne(() => User, (user) => user.user_info, { lazy: true })
@JoinColumn({ name: 'userId' })
user: Promise<User>;

 

💡 지연 로딩을 사용할 경우, 해당 필드는 Promise<Entity> 타입으로 정의됩니다.
즉, user.user_info에 바로 접근하는 것이 아니라 await user.user_info처럼 비동기 방식으로 접근해야 합니다.


결론

- TypeORM에서 서로를 참조하는 엔티티끼리 `ReferenceError`가 나면 순환 참조 문제일 가능성이 높습니다.
- 이 경우 지연 로딩(lazy loading)을 사용하면, 참조를 `Promise<Entity>` 형태로 바꾸어 문제를 해결할 수 있습니다.
- 지연 로딩 사용 시 `await` 키워드를 사용해야 실제 데이터에 접근할 수 있습니다.

반응형
Jeeqong
@Jeeqong :: JQVAULT

Jeeqong's vault : 정보/기록을 쌓아두는 공간 웹개발 포스팅 일상 리뷰를 기록하는 공간입니다.

공감하셨다면 ❤️ 구독도 환영합니다! 🤗

목차