증상: nest build 중 아래와 같은 OOM 오류로 프로세스 종료
FATAL ERROR: Ineffective mark-compacts near heap limit
Allocation failed - JavaScript heap out of memory
환경: AWS Lightsail (RAM 512MB / vCPU 2 / SSD 20GB)
원인: TypeScript 타입체크+트랜스파일 단계에서 Node.js 힙 사용량이 시스템 메모리 한도(≈512MB)와 스왑을 넘어섬 → GC가 메모리 회수 불가 → heap out of memory 발생.
목적: 빌드 속도와 메모리 사용량을 줄여 개발 및 CI 안정화.
적용 사항:
패키지 설치
npm i -D @swc/cli @swc/core
nest-cli.json
{ "compilerOptions": { "builder": "swc" } }
(또는 nest build --builder swc, nest start -b swc)
타입체크 필요 시 병렬 수행
CLI: nest start -b swc --type-check
설정:
{ "compilerOptions": { "builder": "swc", "typeCheck": true } }
.swcrc(선택)
{
"sourceMaps": true,
"jsc": { "parser": { "syntax": "typescript", "decorators": true, "dynamicImport": true } },
"minify": false
}
TS 옵션 권장: skipLibCheck: true, incremental: true
에러:
ReferenceError: Cannot access 'Product' before initialization
상황: 엔티티 간 양방향 정적 import(순환 참조) + 데코레이터 메타데이터 조합.
원인 분석:
tsc는 CommonJS 변환에서 순환 참조를 비교적 “관대하게” 처리하는 반면,swc는 초기화 순서에 엄격하여 아직 초기화 전 클래스에 접근하면 ReferenceError 발생.Product ↔ ProductMapping)에서 자주 노출.예)
// product.entity.ts
import { ProductMapping } from './product_mapping.entity';
@OneToMany(() => ProductMapping, m => m.product) mappings: ProductMapping[];
// product_mapping.entity.ts
import { Product } from './product.entity';
@ManyToOne(() => Product, p => p.mappings) product: Product;
require + 람다로 참조 지연:// BEFORE
@ManyToOne(() => Product, p => p.mappings)
// AFTER
@ManyToOne(() => (require('./product.entity').Product), p => p.mappings)
Shop, User, Review, Image 등).Product ↔ Shop의 ManyToMany 제거.
**명시적 조인 엔티티(product_mapping)**로만 연결:
type-only import로 런타임 의존성 제거:
import type { Product } from './product.entity';
관계 필드 타입은 필요 시 any/any[]로 완화
(런타임에는 데코레이터 메타데이터가 타입 정보를 제공하므로 동작에 문제 없음. 서비스/DTO 레이어에서 타입 안전성 보완).
tsconfig.json
{ "compilerOptions": { "experimentalDecorators": true, "emitDecoratorMetadata": true } }
.swcrc에서 decorators: true 확인.
경로 별칭(tsconfig paths)을 사용 중이면 해결 순서가 순환을 강화하지 않도록 import 정리.
product.entity.ts: 다른 엔티티에 대한 직접 관계 제거(필요 최소 컬럼만 유지).
product_mapping.entity.ts:
@ManyToOne(() => (require('./product.entity').Product), ...)
@ManyToOne(() => (require('./shop.entity').Shop), ...)
// FK 컬럼 유지, onDelete: 'CASCADE'
shop.entity.ts: Product와의 직접 ManyToMany 제거. 기존 OneToMany(운영시간/리뷰/매핑)는 유지.
operating-hours.entity.ts:
@ManyToOne(() => (require('./shop.entity').Shop), ...)
review.entity.ts:
User, Shop, Image에 대해 require 지연 + 관계 타입 완화.
image.entity.ts, user.entity.ts, wishlist.entity.ts, region.entity.ts, submit-user.entity.ts:
모두 동일하게 require 지연 참조와 타입 완화 적용.