Skip to content

refactor(prisma): PrismaService 캐스팅 제거 + abstract class DI 토큰 (P2-2)#121

Merged
chanwoo7 merged 1 commit into
developfrom
refactor/prisma-service-casting-removal
Jun 3, 2026
Merged

refactor(prisma): PrismaService 캐스팅 제거 + abstract class DI 토큰 (P2-2)#121
chanwoo7 merged 1 commit into
developfrom
refactor/prisma-service-casting-removal

Conversation

@chanwoo7
Copy link
Copy Markdown
Member

Summary

P2-2. PrismaService 의 생성자 return 트릭 + as PrismaService 캐스팅이 만들던 타입 함정을 제거. abstract class DI 토큰 + useFactory 로 정공법 정리.

검토 과정에서 (a) Symbol+@Inject 33 곳 / (a') class+interface declaration merging+ESLint rule disable / (a'') abstract class extends PrismaClient 셋 비교 후, (a'') 가 disable / 캐스팅 / 매직 패턴 모두 0 인 진짜 최적안임을 확인.

Scope

prisma.service.ts

Before

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
  constructor() {
    super();
    const extended = this.$extends(softDeleteExtension) as PrismaService;  // ← 캐스팅 거짓말
    extended.onModuleInit = async () => { await extended.$connect(); };
    extended.onModuleDestroy = async () => { await extended.$disconnect(); };
    return extended;  // ← 생성자 return 트릭
  }
  async onModuleInit() { await this.$connect(); }   // ← dead code
  async onModuleDestroy() { await this.$disconnect(); }  // ← dead code
}

문제:

  • as PrismaService — extended client 가 PrismaService 라고 거짓 선언
  • 생성자 return — new PrismaService() 가 다른 객체 반환 (JS 트릭, 가독성 저하)
  • 클래스의 onModuleInit/onModuleDestroy 는 호출되지 않는 dead code (인스턴스에 동명 메서드가 덮어쓰임)

After

export function createExtendedPrismaClient() {
  return new PrismaClient().$extends(softDeleteExtension);
}

/**
 * NestJS DI 토큰 역할의 abstract class.
 * - new PrismaService() 는 abstract 라 compile error → 잘못된 인스턴스화 원천 차단
 * - extends PrismaClient 로 model accessor / $connect 등 API 타입을 그대로 노출
 * - 실제 주입되는 인스턴스는 PrismaModule 의 useFactory 가 반환한 createExtendedPrismaClient() 결과
 */
export abstract class PrismaService extends PrismaClient {}

prisma.module.ts

@Global()
@Module({
  providers: [
    { provide: PrismaService, useFactory: createExtendedPrismaClient },
  ],
  exports: [PrismaService],
})
export class PrismaModule implements OnModuleInit, OnModuleDestroy {
  constructor(private readonly prisma: PrismaService) {}
  async onModuleInit()    { await this.prisma.$connect(); }
  async onModuleDestroy() { await this.prisma.$disconnect(); }
}

라이프사이클이 모듈로 이동 — owner 명확화.

prisma.service.spec.ts

  • factory 직접 호출 + softDelete 확장 자동 필터 검증
  • Test.createTestingModule({ imports: [PrismaModule] }) 로 모듈 인스턴스화 후 init/close 시 $connect/$disconnect 호출 검증
  • PrismaService 토큰 주입 인스턴스의 모델 accessor 노출 검증

비교표

측면 (a) Symbol + @Inject (a') class+interface merging (a'') abstract class extends PrismaClient
callsite 변경 33 곳에 @Inject(PRISMA_SERVICE) 추가 0 0
disable 한 ESLint 룰 0 no-unsafe-declaration-merging 필요 0
new PrismaService() 차단 N/A (Symbol 토큰) 런타임 빈 객체 (TS 통과 — 위험) compile error (abstract)
매직 패턴 없음 declaration merging 없음 (단순 abstract class)
캐스팅 없음 없음 없음
생성자 return 트릭 없음 없음 없음

Impact

  • FE: 없음
  • DB: 변경 없음
  • callsite (Repository / Service / 테스트 모듈 빌더 등 33 파일): 변경 0 — abstract class 의 TS 타입 (extends PrismaClient) 이 그대로라 prisma.account.findMany(...) 같은 호출이 그대로 작동
  • Coverage: prisma.service.ts 100/100/100/100 유지
  • 전체 테스트 1240 → 1241 (+1, PrismaModule 라이프사이클 케이스 추가)

Test plan

  • prisma.service.spec 신규 4 케이스 — factory / softDelete / 모듈 라이프사이클 / 토큰 주입 모두 통과
  • 기존 33 callsite 회귀 0 (pre-push 전체 1241 테스트 통과)
  • lint, tsc, dto:check 통과 (ESLint rule disable 없이)
  • CI 통과 확인

…ctory (P2-2)

기존: PrismaClient 상속 + 생성자 return + 'as PrismaService' 캐스팅 (타입 함정).
신규: abstract class extends PrismaClient + useFactory.

- prisma.service.ts
  - createExtendedPrismaClient() factory 함수
  - PrismaService: abstract class extends PrismaClient
    - abstract 라 `new PrismaService()` compile error → 잘못된 인스턴스화 원천 차단
    - extends PrismaClient 로 model accessor / $connect 등 API 타입 노출
    - declaration merging 불필요, ESLint 룰 disable 0, 캐스팅 0
  - 생성자 return 트릭 / 'as PrismaService' / dead 메서드 모두 제거
- prisma.module.ts
  - useFactory 로 PrismaService 토큰에 factory 결과 등록
  - 모듈이 OnModuleInit/OnModuleDestroy 구현 (라이프사이클 owner)
- prisma.service.spec.ts
  - factory 직접 호출 + softDelete 자동 필터 검증
  - PrismaModule init/destroy 시 $connect/$disconnect 호출 검증
  - PrismaService 토큰 주입 인스턴스의 모델 accessor 노출 검증

callsite 영향 0 (33 파일 그대로) — abstract class 의 TS 타입 그대로, NestJS DI 가 factory
결과를 토큰에 매핑.

검토 과정에서 (a) Symbol+@Inject 33 곳, (a') class+interface merging+rule disable,
(a'') abstract class extends 셋 비교 후 (a'') 가 disable/캐스팅/매직 패턴 모두 0 인 최적안임을
확인.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 31, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 297da2b5-9469-4ac0-8bc4-22214e6ee52b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/prisma-service-casting-removal

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 31, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@github-actions
Copy link
Copy Markdown

Coverage report

St.
Category Percentage Covered / Total
🟢 Statements 97.41% 3534/3628
🟢 Branches 93.84% 1128/1202
🟢 Functions 94.02% 645/686
🟢 Lines 97.65% 3236/3314

Test suite run success

1241 tests passing in 144 suites.

Report generated by 🧪jest coverage report action from c89ab19

@chanwoo7 chanwoo7 merged commit c955137 into develop Jun 3, 2026
10 checks passed
@chanwoo7 chanwoo7 deleted the refactor/prisma-service-casting-removal branch June 3, 2026 18:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant