Skip to content

[FEAT/REFACTOR] 회원가입·로그인 구현, 사용자 도메인 구조 개선 및 미사용 모듈 제거#5

Closed
Takch02 wants to merge 130 commits intomainfrom
feat/sign
Closed

[FEAT/REFACTOR] 회원가입·로그인 구현, 사용자 도메인 구조 개선 및 미사용 모듈 제거#5
Takch02 wants to merge 130 commits intomainfrom
feat/sign

Conversation

@Takch02
Copy link
Copy Markdown
Contributor

@Takch02 Takch02 commented Apr 27, 2026

#4

개요

buyer/seller 회원가입 및 Redis 세션 기반 로그인/로그아웃을 구현했습니다.
구현 과정에서 드러난 도메인 구조 문제와 미사용 모듈도 함께 정리했습니다.

변경 사항

feat

  • buyer/seller 회원가입 (XRPL 지갑 자동 생성(현재 Testnet) + 30분 주기 활성화 재시도)
  • Redis 세션 기반 로그인/로그아웃

refactor

  • buyer/company 스키마를 users/schemas/로 이동 — UsersModule이 하위 도메인 스키마를 역방향 참조하던 구조 수정
  • buyer 모듈에서 직접 생성/수정/삭제 제거 — 회원가입 엔드포인트로 대체

chore

  • 미사용 모듈 제거 (admin, insights, consultants)
  • company-images 관련 코드 제거

범위 확대 사유

회원가입 구현 중 UsersModule → buyers/companies 역방향 의존, buyer 모듈의 중복 CRUD가 확인되어 함께 정리했습니다.

테스트

  • npm test 98개 전체 통과
  • auth 모듈 유닛 테스트 추가 (service, controller)

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 사용자 인증 시스템 추가: 구매자 및 판매자 회원가입, 로그인/로그아웃 기능
    • XRPL 기반 지갑 생성 및 암호화된 보안 저장
    • 세션 관리 개선: Redis 기반 안정적인 세션 저장소
  • 기반시설

    • Redis 데이터베이스 추가
    • 환경 설정 확장 (테스트 환경 변수 추가)
    • 주요 의존성 업그레이드

choi-jungsuk and others added 30 commits September 30, 2025 19:53
choi-jungsuk and others added 24 commits April 19, 2026 09:46
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Frontend has been migrated to a separate repository; backend source
is promoted from backend/ to the repo root.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove frontend build job (migrated to separate repo)
- Fix working-directory: drop backend/ prefix now that source is at root
- Fix cache-dependency-path: use relative package-lock.json
- deploy now only waits on test-backend

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Dockerfile: remove backend/ path prefix now that source is at repo root.
README: remove frontend/monorepo references.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace frontend/dist/ with dist/ now that this repo is backend-only.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move all NestJS source (src/, test/) and config files (tsconfig*, nest-cli.json,
eslint.config.mjs) from backend/ subdirectory to repo root.
Remove old Express/JS files: industries.js, Consultation.js, consultations.js.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Swap the minimal monorepo root package with the full NestJS package
(scripts, dependencies, jest config, lint-staged paths updated for root).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Takch02
Copy link
Copy Markdown
Contributor Author

Takch02 commented Apr 27, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 27, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 27, 2026

Warning

Ignoring CodeRabbit configuration file changes. For security, only the configuration from the base branch is applied for open source repositories.

📝 Walkthrough

Walkthrough

대규모 아키텍처 리팩토링으로, 통합된 사용자 인증 시스템과 MongoDB Mongoose 판별자 기반 스키마를 도입하고, 관리자/인사이트/컨설턴트 모듈을 제거하며, Redis 세션 및 XRPL 지갑 통합을 추가합니다.

Changes

Cohort / File(s) Summary
Authentication System
src/modules/auth/auth.controller.ts, src/modules/auth/auth.service.ts, src/modules/auth/auth.module.ts, src/modules/auth/dto/login.dto.ts, src/modules/auth/dto/register.dto.ts, src/common/decorators/current-user.decorator.ts, src/common/guards/session.guard.ts
새로운 인증 시스템 구현. 판매자/구매자 등록, bcrypt 기반 로그인, XRPL 지갑 생성 및 활성화, 30분 간격 재시도 스케줄러 포함. 세션 기반 사용자 추출 및 보호 가드 추가.
Unified Users Module & Schema Restructuring
src/modules/users/users.module.ts, src/modules/users/schemas/user.schema.ts, src/modules/users/schemas/buyer.schema.ts, src/modules/users/schemas/company.schema.ts, src/common/schemas/wallet-info.schema.ts
MongoDB Mongoose 판별자를 사용한 통합 사용자 스키마 도입. 기본 User 모델과 "buyer", "seller" 하위 타입으로 구성. 지갑 정보(주소, 시드, 공개키) 포함.
Module Deletions
src/modules/admin/*, src/modules/insights/*, src/modules/consultants/*
관리 기능, 인사이트 대시보드, 컨설턴트 요청 관련 전체 모듈 및 서비스 제거. (총 ~1,300줄 삭제)
Module Updates & Schema Migration
src/modules/buyers/buyers.module.ts, src/modules/buyers/buyers.controller.ts, src/modules/buyers/buyers.service.ts, src/modules/companies/companies.module.ts, src/modules/companies/companies.service.ts, src/modules/matches/matches.service.ts, src/modules/consultations/consultations.service.ts, src/modules/partners/partners.module.ts
기존 모듈들을 새로운 UsersModule 스키마로 마이그레이션. 생성/수정/삭제 엔드포인트 제거. exportItems, companyIntroduction 등 필드명 변경.
XRPL & Encryption Integration
src/modules/payments/xrpl.service.ts
지갑 생성, 테스트넷 펀딩, AES-256-GCM 암호화 추가. 인증키 기반 암호화된 시드 저장.
Session & Infrastructure Setup
src/main.ts, .env.test, .env.example, .gitignore, docker-compose.yml, package.json
Redis 클라이언트 및 connect-redis 세션 미들웨어 초기화. Redis 컨테이너 추가. 테스트 환경변수 정의. bcrypt, express-session 등 의존성 추가.
Configuration Updates
src/config/env.config.ts, .coderabbit.yaml, src/app.module.ts
환경변수 로드 순서 변경. Redis, 세션, 보안 암호화키 설정 추가. CodeRabbit 리뷰 프로필 설정.
Type Declarations & Testing
src/types/express-session.d.ts, test/xrpl.e2e-spec.ts, test/jest-e2e.json, src/modules/auth/auth.controller.spec.ts, src/modules/auth/auth.service.spec.ts
세션 데이터 TypeScript 타입 확장. XRPL 지갑/암호화 E2E 테스트 추가. 인증 유닛 테스트 작성.

Sequence Diagram(s)

sequenceDiagram
    actor Client
    participant Server as NestJS Server
    participant Session as Redis Session
    participant AuthService
    participant XrplService
    participant DB as MongoDB

    Client->>Server: POST /auth/register (email, password, name)
    Server->>AuthService: registerSeller/registerBuyer(dto)
    AuthService->>AuthService: Hash password (bcrypt)
    AuthService->>XrplService: generateWallet()
    XrplService->>XrplService: Create XRPL wallet
    XrplService->>XrplService: Encrypt seed (AES-256-GCM)
    AuthService->>DB: Save Company/Buyer (status: PENDING_ACTIVATION)
    AuthService->>XrplService: fundAccount(wallet.address)
    XrplService->>XrplService: Fund testnet account
    AuthService->>DB: Update status to ACTIVE
    AuthService->>Server: Return user (without password/seed)
    Server->>Client: 201 Created
Loading
sequenceDiagram
    actor Client
    participant Server as NestJS Server
    participant Session as Redis Session
    participant AuthService
    participant DB as MongoDB

    Client->>Server: POST /auth/login (email, password)
    Server->>AuthService: login(dto)
    AuthService->>DB: Find User by email
    AuthService->>AuthService: Compare password (bcrypt)
    AuthService->>AuthService: Return user (password-redacted)
    Server->>Session: req.session.userId = user._id
    Server->>Session: req.session.type = "buyer"/"seller"
    Server->>Client: 200 로그인 성공
    
    Note over Client,Session: Subsequent requests
    Client->>Server: GET /api/resource
    Server->>Server: SessionGuard.canActivate()
    Server->>Session: Check req.session.userId
    alt userId exists
        Server->>Server: Allow request
    else userId missing
        Server->>Client: 401 Login required
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 새 구조 속에 사용자 통합되고,
암호화된 지갑이 안전히 저장되네.
판별자로 타입 구분하며,
Redis 세션이 로그인을 지키고,
옛 모듈들은 물러나고,
인증의 새 시대가 밝아온다! 🔐✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목이 PR의 주요 변경사항을 명확하게 요약하고 있습니다. 회원가입·로그인 구현, 사용자 도메인 구조 개선, 미사용 모듈 제거라는 세 가지 핵심 변경사항이 간결하고 구체적으로 표현되어 있습니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/sign

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.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 18

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
src/modules/companies/dto/create-company.dto.ts (1)

57-67: ⚠️ Potential issue | 🟡 Minor

companyIntroduction / productIntroduction 필드의 필수 여부가 스키마 및 다른 DTO와 불일치합니다.

src/modules/users/schemas/user.schema.ts에서 두 필드는 @Prop({ required: true })로 선언되어 있고, RegisterBuyerDto / RegisterSellerDto에서도 @IsNotEmpty()로 필수 처리되어 있습니다. 그러나 본 DTO에서는 @IsOptional()로 지정되어 있어, 컨트롤러 레벨에서는 검증을 통과하지만 Mongoose 단계에서 저장 실패가 발생합니다.

DTO에서도 필수로 통일하십시오. UpdateCompanyDto는 이미 PartialType(CreateCompanyDto)를 사용하므로 자동으로 부분 업데이트를 지원합니다.

제안된 변경
-  `@ApiPropertyOptional`({ example: "한국 자동차 부품 제조사입니다." })
-  `@IsOptional`()
+  `@ApiProperty`({ example: "한국 자동차 부품 제조사입니다." })
   `@IsString`()
+  `@IsNotEmpty`()
   `@MaxLength`(2000)
-  companyIntroduction?: string;
+  companyIntroduction: string;

-  `@ApiPropertyOptional`({ example: "EV 배터리 팩 및 BMS를 생산합니다." })
-  `@IsOptional`()
+  `@ApiProperty`({ example: "EV 배터리 팩 및 BMS를 생산합니다." })
   `@IsString`()
+  `@IsNotEmpty`()
   `@MaxLength`(2000)
-  productIntroduction?: string;
+  productIntroduction: string;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/modules/companies/dto/create-company.dto.ts` around lines 57 - 67, The
CreateCompanyDto currently marks companyIntroduction and productIntroduction as
optional but the schema (user.schema.ts with `@Prop`({ required: true })) and
RegisterBuyerDto/RegisterSellerDto expect them to be required; remove
`@IsOptional`() and add `@IsNotEmpty`() (keep `@IsString`() and `@MaxLength`(2000)) on
both companyIntroduction and productIntroduction in CreateCompanyDto so
validation aligns with the Mongoose schema and other DTOs; UpdateCompanyDto will
continue to work via PartialType(CreateCompanyDto).
src/modules/matches/matches.service.spec.ts (1)

32-41: ⚠️ Potential issue | 🟡 Minor

makeCompany 픽스처가 서비스 변경과 어긋남 — 새 스코어링 브랜치 미커버

서비스가 company.offeringscompany.exportItems로 바뀌고 dart 기반 보너스도 제거되었는데, 이 픽스처는 여전히 offerings/dart를 들고 있습니다. 결과적으로:

  • offerings/dart는 테스트에서 dead field가 됩니다.
  • 어떤 테스트도 exportItems를 세팅하지 않아, 새 스코어링 라인 needs-exportItems overlap(matches.service.ts L66–70)이 한 번도 검증되지 않습니다.

리네이밍 회귀를 잡을 수 있도록 픽스처를 정리하고, 해당 브랜치에 대한 단언을 한 건 추가해 주세요.

♻️ 제안 수정
 const makeCompany = (overrides = {}) => ({
   _id: COMPANY_ID,
   tags: ["전기차"],
   industry: "자동차",
-  offerings: ["공급망"],
+  exportItems: ["공급망"],
   updatedAt: new Date(),
-  dart: null,
   embedding: [],
   ...overrides,
 });

추가로 findMatches describe 블록 안에 needs-exportItems 오버랩이 score/reason에 반영되는지 검증하는 케이스를 추가하는 것을 권장합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/modules/matches/matches.service.spec.ts` around lines 32 - 41, The
makeCompany test fixture still uses company.offerings and dart which are dead
fields after the service refactor; update makeCompany to provide exportItems
(replacing offerings) and remove dart, and ensure embedding/other defaults
remain; then add a new spec inside the findMatches describe that constructs a
company with exportItems overlapping the need and asserts the scoring branch in
matches.service (the "needs-exportItems overlap" logic in findMatches /
matches.service.ts lines ~66–70) produces the expected score and reason. Use the
makeCompany helper and the findMatches test helpers so the new test targets the
specific overlap branch (check score/reason) rather than relying on old
offerings/dart fields.
src/modules/matches/matches.service.ts (1)

50-70: ⚠️ Potential issue | 🟠 Major

테스트 데이터가 스키마와 맞지 않습니다. 저장된 회사 문서의 exportItems 마이그레이션을 확인하세요.

Company 스키마는 이미 exportItems로 정의되어 있고 offerings 필드는 없습니다. 그러나 테스트 mock 데이터(makeCompany)는 여전히 offerings을 사용하고 있으며, 실제 exportItems 채우기 테스트는 없습니다.

저장된 회사 문서들이 정말로 exportItems 필드를 가지고 있는지 확인이 필요합니다. 기존 데이터에 이 필드가 없으면 needs-exportItems 오버랩 점수가 조용히 0이 되어 매칭 품질이 저하될 수 있습니다.

개선 사항
  • 테스트 mock 데이터를 exportItems로 업데이트하세요 (offerings 제거)
  • 저장된 모든 회사 문서가 exportItems를 포함하는지 데이터베이스 검증을 추가하세요
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/modules/matches/matches.service.ts` around lines 50 - 70, The tests and
runtime disagree on the company field name: test factory makeCompany still
populates offerings while the code and Company schema use exportItems, causing
intersectCount/buyerNeeds vs companyExportItems to be empty and needMatches to
be 0; update the test mock makeCompany to populate exportItems (remove
offerings) and add a DB validation/migration check to ensure persisted Company
documents include exportItems (or backfill offerings->exportItems) so that the
logic in matches.service (variables companyExportItems, needMatches,
intersectCount) sees real data.
src/modules/partners/partners.service.ts (1)

393-405: ⚠️ Potential issue | 🟠 Major

잔존하는 profileText 참조 — 필드명 마이그레이션 누락

profileTextcompanyIntroduction 리네이밍에 맞춰 위쪽 mapping(381–390)은 companyIntroduction: item.content로 갱신되었지만, 여기 country 필터에서는 여전히 item.profileText를 참조합니다. 그 결과 매핑된 웹 결과에는 profileText가 존재하지 않아 text는 사실상 item.name만 포함하게 되고, 국가 매칭 페널티(score * 0.7)가 의도와 다르게 동작합니다.

🐛 제안 수정
       if (intentData?.country) {
         const countryLower = (intentData.country as string).toLowerCase();
         mappedWebResults = mappedWebResults.map((item: any) => {
           const text = (
-            (item.profileText || "") +
+            (item.companyIntroduction || "") +
             " " +
             (item.name || "")
           ).toLowerCase();
           if (!text.includes(countryLower))
             return { ...item, score: item.score * 0.7 };
           return item;
         });
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/modules/partners/partners.service.ts` around lines 393 - 405, The country
filter still reads item.profileText which was renamed during mapping; update the
block in the intentData?.country handling that builds text to use
item.companyIntroduction (or fallback to item.content if you prefer the original
source) instead of item.profileText so mappedWebResults entries include the
company introduction when checking country and applying the score *= 0.7 penalty
(refer to mappedWebResults, intentData, item.profileText, companyIntroduction,
and score).
🧹 Nitpick comments (22)
.env.test (1)

1-37: 테스트 환경 디폴트가 운영 안전 수칙과 충돌합니다.

  • CORS_ORIGINS=* 는 테스트 환경이라도 운영 설정에서 우연히 재사용되면 위험합니다. 테스트에서도 가급적 http://localhost:4000 등 명시적 origin을 권장합니다.
  • MONGODB_URI=mongodb://localhost:27017 는 DB 이름이 빠져 있어 다른 로컬 DB와 충돌할 수 있습니다. 전용 테스트 DB(...:27017/kstatra_test)로 분리하면 데이터 오염을 방지할 수 있습니다.

dotenv-linter가 보고한 키 정렬 경고는 정렬 컨벤션을 강제할 계획이 없다면 무시해도 무방합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.env.test around lines 1 - 37, The .env.test has unsafe defaults: change
CORS_ORIGINS from wildcard to a specific test origin (e.g., use localhost
origin) and update MONGODB_URI to include a dedicated test database name (e.g.,
append /kstatra_test) to avoid accidental reuse of production/local DBs; locate
and edit the CORS_ORIGINS and MONGODB_URI entries in the file to replace "*" and
the bare host URI respectively, and ignore or address dotenv-linter ordering
warnings per repo policy.
src/modules/auth/dto/register.dto.ts (3)

32-35: 비밀번호 정책이 MinLength(8) 한 가지뿐입니다.

회원가입 단계에서 영문/숫자/특수문자 조합 등 최소 복잡도 정책을 함께 강제하지 않으면 12345678 같은 약한 비밀번호도 통과합니다. MVP 이후 정식 정책 도입 전이라도 @Matches(/^(?=.*[A-Za-z])(?=.*\d).{8,}$/) 정도의 가벼운 가드를 추가하는 것을 권장합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/modules/auth/dto/register.dto.ts` around lines 32 - 35, The password
field in RegisterDto currently only uses `@MinLength`(8) so weak passwords like
"12345678" pass; update the password validation by adding a `@Matches`(...)
decorator on the password property (the password field in register.dto.ts /
RegisterDto) to enforce at least letters and digits (for example a regex like
/^(?=.*[A-Za-z])(?=.*\d).{8,}$/) or a slightly stricter pattern including
special characters, and adjust the `@ApiProperty` example/description accordingly
so the API docs reflect the new complexity requirement.

38-113: RegisterBuyerDto / RegisterSellerDto 의 공통 필드를 베이스로 끌어올리는 것을 권장합니다.

companyIntroduction, productIntroduction, websiteUrl, tags 가 두 서브클래스에서 동일 검증 규칙으로 반복되고 있습니다. RegisterCommonDto 로 옮기면 향후 검증 규칙 변경 시 한 곳만 수정하면 되고, create-company.dto.ts 와의 일관성 유지도 쉬워집니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/modules/auth/dto/register.dto.ts` around lines 38 - 113, Move the
duplicated fields companyIntroduction, productIntroduction, websiteUrl, and tags
from RegisterBuyerDto and RegisterSellerDto into the shared RegisterCommonDto so
both subclasses inherit the same validation/decorator rules; update
RegisterCommonDto to declare these properties with the same class-validator and
`@ApiProperty/`@ApiPropertyOptional decorators currently used, remove the
duplicated declarations from RegisterBuyerDto and RegisterSellerDto, and adjust
imports/usages if necessary to ensure the DTOs compile and remain consistent
with create-company.dto.ts.

27-30: representativePhone 형식 검증이 없습니다.

@IsString() + @isnotempty() 만 있어 임의 문자열도 허용됩니다. 예시(010-1234-5678)에 맞춰 class-validator@IsPhoneNumber('KR')를 사용하거나, @Matches(/^\d{2,3}-\d{3,4}-\d{4}$/) 를 추가할 것을 권장합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/modules/auth/dto/register.dto.ts` around lines 27 - 30, The
representativePhone DTO field currently only has `@IsString`() and `@IsNotEmpty`(),
allowing arbitrary strings; update the Register DTO (the representativePhone
property in register.dto.ts / the DTO class containing representativePhone) to
enforce phone format by adding a validator such as `@IsPhoneNumber`('KR') or
`@Matches`(/^\d{2,3}-\d{3,4}-\d{4}$/) alongside the existing decorators so the
value must match the example "010-1234-5678".
src/types/express-session.d.ts (1)

4-7: SessionData 필드는 선택적(optional)으로 선언하는 것을 권장합니다.

미인증 요청에서는 req.session.userIdreq.session.type 이 실제로 undefined 입니다. 현재처럼 필수(required)로 선언하면 SessionGuardif (!req.session.userId) 같은 체크가 타입 시스템상 의미를 잃고, 다른 코드에서 미인증 세션의 값을 안전하게 사용한다고 잘못 가정할 위험이 생깁니다.

♻️ 제안 diff
 declare module "express-session" {
   interface SessionData {
-    userId: string;
-    type: "buyer" | "seller";
+    userId?: string;
+    type?: "buyer" | "seller";
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/types/express-session.d.ts` around lines 4 - 7, Make SessionData's userId
and type optional so unauthenticated requests can have undefined session fields:
update the interface SessionData to declare userId?: string and type?: "buyer" |
"seller"; then ensure related checks like in SessionGuard (e.g., if
(!req.session.userId)) remain valid under the type system and adjust any
downstream code that assumed non-null session values to handle undefined safely.
src/main.ts (2)

20-21: session.secret 의 non-null 단언(!) 대신 명시적 검증 권장.

env.config.ts 의 기본값이 "dev-secret-change-in-production" 이라 현재는 undefined 가 되지 않지만, 향후 기본값이 제거되면 ! 가 런타임에 무성한 undefined 시크릿을 그대로 통과시켜 세션 서명이 깨질 수 있습니다. 또한 운영 환경에서 기본 시크릿이 그대로 사용되는 것을 막는 가드도 함께 두면 안전합니다.

🛡️ 제안 diff
-  const sessionSecret = configService.get<string>("session.secret")!;
-  const sessionTtl = configService.get<number>("session.ttl")!;
+  const sessionSecret = configService.get<string>("session.secret");
+  const sessionTtl = configService.get<number>("session.ttl");
+  if (!sessionSecret || !sessionTtl) {
+    throw new Error("session.secret / session.ttl 설정이 누락되었습니다.");
+  }
+  if (
+    process.env.NODE_ENV === "production" &&
+    sessionSecret === "dev-secret-change-in-production"
+  ) {
+    throw new Error("운영 환경에서 기본 session secret 사용은 금지됩니다.");
+  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main.ts` around lines 20 - 21, Replace the non-null assertions for
session.secret and session.ttl with explicit validation: read them via
configService.get (sessionSecret and sessionTtl), verify sessionSecret is a
non-empty string (and if in non-development environment ensure it is not the
default dev value from env.config.ts), and verify sessionTtl is a positive
number; if any check fails, throw an error or terminate startup with a clear
message. Update the initialization logic around the symbols sessionSecret,
sessionTtl and configService to perform these guards before using them.

24-32: Redis 클라이언트 설정에서 ConfigService 를 사용하지 않고 process.env 에 직접 접근하고 있습니다.

같은 함수 내에서 sessionSecret/sessionTtlConfigService 를 통해 읽으면서, Redis 호스트/포트/비밀번호는 process.env 를 직접 읽어 일관성이 깨져 있습니다. env.config.tsredis 블록을 통해 일관되게 가져오는 편이 단일 소스 원칙에 부합합니다.

또한 REDIS_PASSWORD 가 비어있을 때 빈 문자열을 그대로 전달하면, ACL 이 활성화된 Redis 에서는 빈 비밀번호로 AUTH 시도가 발생해 연결이 거절될 수 있습니다. 빈 값일 때는 필드를 생략하는 편이 안전합니다.

♻️ 제안 diff
-  // Redis session store
-  const redisClient = createClient({
-    socket: {
-      host: process.env.REDIS_HOST || "localhost",
-      port: Number(process.env.REDIS_PORT) || 6379,
-    },
-    password: process.env.REDIS_PASSWORD || "",
-  });
+  // Redis session store
+  const redisHost = configService.get<string>("redis.host") ?? "localhost";
+  const redisPort = Number(configService.get("redis.port")) || 6379;
+  const redisPassword = configService.get<string>("redis.password");
+  const redisClient = createClient({
+    socket: { host: redisHost, port: redisPort },
+    ...(redisPassword ? { password: redisPassword } : {}),
+  });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main.ts` around lines 24 - 32, Currently createClient/redisClient reads
Redis host/port/password directly from process.env while
sessionSecret/sessionTtl use ConfigService; update the code to read Redis
settings from ConfigService (the env.config.ts redis block) for consistency (use
the same ConfigService instance used for sessionSecret/sessionTtl), pass host
and port from those config values into createClient, and only include the
password field when the configured password is non-empty (omit the password
property entirely if blank) to avoid sending an empty AUTH to ACL-enabled Redis.
docker-compose.yml (1)

11-17: Redis 이미지 태그 고정 및 인증 구성 검토 권장.

  • redis:alpine 은 rolling 태그여서 시간이 지나면 재현 가능한 빌드가 깨질 수 있습니다. redis:7-alpine 처럼 메이저 버전을 고정하는 것을 권장합니다.
  • 컨테이너가 호스트 6379 포트로 노출되지만 인증 설정이 없습니다. env.config.tsREDIS_PASSWORD 가 실제로 사용될 환경이라면, Redis 서버에도 --requirepass 를 동일하게 적용해야 합니다(미적용 시 클라이언트의 password 옵션이 무력화됩니다).
♻️ 제안 diff
   redis:
-    image: redis:alpine
+    image: redis:7-alpine
     restart: unless-stopped
+    command: ["redis-server", "--save", "60", "1", "--loglevel", "warning"]
     ports:
       - "6379:6379"
     volumes:
       - redis_data:/data
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docker-compose.yml` around lines 11 - 17, Pin the Redis image to a stable tag
(e.g., change the redis service image from "redis:alpine" to a fixed major
version like "redis:7-alpine") and ensure the container enforces authentication
by propagating REDIS_PASSWORD from env.config.ts into the Redis start command
(use --requirepass or equivalent) so the client's password option is effective;
update the docker-compose redis service definition to use the pinned image and
pass the requirepass option referencing REDIS_PASSWORD.
package.json (1)

22-22: husky || true 는 prepare 스크립트 실패를 모두 삼켜 CI/설치 환경의 이상을 숨길 수 있습니다.

git 디렉터리가 없는 환경(예: Docker 빌드 컨텍스트)에서 husky 설치 실패를 회피하려는 의도로 보입니다만, 모든 오류가 무시되어 husky 설정 자체의 회귀(regression)도 가려집니다. 더 좁게 처리하는 것을 권장합니다.

♻️ 제안 diff
-    "prepare": "husky || true"
+    "prepare": "husky install || echo 'husky 설치 건너뜀 (git 디렉터리 없음)'"

또는 is-ci 를 활용해 CI 환경에서만 건너뛰는 구성도 일반적입니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package.json` at line 22, The prepare script ("prepare") currently uses the
broad fallback "husky || true" which masks all installation failures; replace it
with a narrower guard that only skips husky when appropriate (e.g., when no git
metadata is available or when running in CI). Update the "prepare" script to
explicitly check for a git repo or an environment flag (e.g., use is-ci or test
for .git/GIT_DIR) before running husky, so failures installing/configuring husky
are surfaced in normal dev environments while still skipping in Docker/CI
contexts.
src/modules/auth/auth.module.ts (1)

9-9: ScheduleModule.forRoot()는 루트 모듈(AppModule)로 이동하세요.

NestJS 공식 문서에서는 ScheduleModule.forRoot()를 오직 루트 AppModule에서만 등록하도록 권장합니다. 현재 auth.module.ts(기능 모듈)에서 등록하는 것은 NestJS 아키텍처 모범 사례를 위반합니다.

현재는 단일 등록이라 기능상 문제없지만, 다른 모듈들이 같은 패턴을 따르면 스케줄러가 중복 초기화되어 cron 핸들러가 여러 번 트리거될 위험이 있습니다. 또한 ScheduleModule은 글로벌 모듈이므로 모든 서비스에서 @Cron, @Interval 등의 데코레이터를 사용할 수 있어, 기능 모듈에서 명시적으로 등록할 필요가 없습니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/modules/auth/auth.module.ts` at line 9, Move ScheduleModule.forRoot() out
of the feature module: remove ScheduleModule.forRoot() from the imports array in
AuthModule (auth.module.ts) and add ScheduleModule.forRoot() to the imports of
the root AppModule so the scheduler is initialized only once; keep using
`@Cron/`@Interval in feature services without importing ScheduleModule again.
Ensure no other feature modules import ScheduleModule.forRoot() to avoid
duplicate initialization.
src/modules/buyers/buyers.module.ts (1)

7-10: UsersModule re-export 패턴 — 의존성 명시화 검토 권장

BuyersModuleUsersModule을 re-export하면 ConsultationsModule 등 소비자가 BuyersModule만 import해도 Buyer/User 디스크리미네이터 바인딩을 받게 되어 실제 의존성이 가려집니다. 디스크리미네이터 등록 자체는 UsersModule의 책임이므로, 소비자 측에서 UsersModule을 직접 import하도록 정리하면 모듈 경계가 더 명확해집니다. 단기 동작에는 문제 없으니 후속 리팩터로 검토해 주세요.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/modules/buyers/buyers.module.ts` around lines 7 - 10, BuyersModule
currently re-exports UsersModule which hides the real dependency from consumers;
change BuyersModule so that UsersModule remains in imports (so BuyersModule can
use it) but is removed from the exports array — keep exports: [BuyersService]
(and other local exports) so consumers like ConsultationsModule must explicitly
import UsersModule; update BuyersModule's exports list and any module
documentation to reflect that BuyersModule no longer re-exports UsersModule.
src/common/guards/session.guard.ts (1)

10-16: req에 명시적 타입 부여 권장 (정적 분석 경고 해소)

getRequest() 호출 시 타입 파라미터를 지정하지 않으면 reqany로 추론되어 타입 안정성 경고가 발생합니다. <Request> 제네릭을 추가하면 src/types/express-session.d.ts에서 정의한 SessionData augmentation이 활성화되어 userIdtype 접근이 안전하게 타입체크됩니다.

♻️ 제안 수정
 import {
   Injectable,
   CanActivate,
   ExecutionContext,
   UnauthorizedException,
 } from "@nestjs/common";
+import type { Request } from "express";

 `@Injectable`()
 export class SessionGuard implements CanActivate {
   canActivate(ctx: ExecutionContext): boolean {
-    const req = ctx.switchToHttp().getRequest();
+    const req = ctx.switchToHttp().getRequest<Request>();
     if (!req.session?.userId) {
       throw new UnauthorizedException("Login required");
     }
     return true;
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/common/guards/session.guard.ts` around lines 10 - 16, The guard's req is
inferred as any because getRequest() lacks a generic; update canActivate to call
ctx.switchToHttp().getRequest<Request>() and import Request from 'express' so
the augmented SessionData types (userId/type) are picked up and static analysis
warnings go away; ensure references to req.session?.userId remain the same and
no other logic changes in canActivate (keep the UnauthorizedException behavior).
src/modules/companies/companies.service.ts (1)

78-88: 불필요한 items 별칭 제거 권장

DART post-processing 제거 이후 items는 단순히 raw를 그대로 가리키는 의미 없는 별칭이 되었습니다. 가독성을 위해 별칭을 없애고 dataraw를 직접 넣는 것이 더 명확합니다.

♻️ 제안 리팩터
     const [raw, total] = await Promise.all([findQuery.exec(), countQuery]);

-    const items = raw;
-
     return {
       page,
       limit,
       total,
       totalPages: Math.ceil(total / limit),
-      data: items,
+      data: raw,
     };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/modules/companies/companies.service.ts` around lines 78 - 88, Remove the
redundant alias "items" that simply references "raw": in the async block where
you await Promise.all([findQuery.exec(), countQuery]) and assign const [raw,
total], stop creating const items = raw and instead return data: raw directly in
the returned object (preserving page, limit, total, totalPages using
Math.ceil(total / limit)); this simplifies the function around findQuery.exec(),
countQuery and the returned data payload.
src/modules/partners/partners.service.ts (1)

175-186: ATLAS_VECTOR_INDEX 미설정 시 명시적 가드 권장

이전의 "vector_index" 폴백이 제거되어, 환경변수가 비어 있으면 $vectorSearch.indexundefined로 전달되어 매번 aggregation 에러가 발생합니다(현재는 226–229의 try/catch가 흡수). 운영상 디버깅이 어려워지므로, 미설정이면 벡터 검색 단계를 건너뛰고 텍스트 검색 fallback으로 바로 진입하도록 가드하는 편이 더 안전합니다.

♻️ 제안 리팩터
-    if (!forceWebSearch && vector.length > 0) {
+    const atlasIndex = process.env.ATLAS_VECTOR_INDEX;
+    if (!forceWebSearch && vector.length > 0 && atlasIndex) {
       const pipeline: any[] = [
         {
           $vectorSearch: {
-            index: process.env.ATLAS_VECTOR_INDEX,
+            index: atlasIndex,
             path: "embedding",
             queryVector: vector,
             numCandidates: 100,
             limit: Number(limit) * 2,
           },
         },
       ];

부팅 시 ATLAS_VECTOR_INDEX 미설정에 대해 한 번만 logger.warn(...)을 남기는 것도 운영 가시성에 도움이 됩니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/modules/partners/partners.service.ts` around lines 175 - 186, Guard
against a missing ATLAS_VECTOR_INDEX by checking process.env.ATLAS_VECTOR_INDEX
before building the $vectorSearch pipeline: if ATLAS_VECTOR_INDEX is falsy, skip
the vector-search branch (the block using forceWebSearch, vector and the
pipeline with $vectorSearch and limit) and fall back immediately to the
text-search path; also emit a single logger.warn at startup (or the first time
this code runs) to surface the misconfiguration. Update the code around the
vector pipeline construction and the try/catch that currently swallows the
aggregation error so you never pass undefined into $vectorSearch.index and you
get deterministic fallback behavior.
src/modules/payments/xrpl.service.ts (1)

70-78: wallet.seed! non-null 단언 — 방어적 체크 권장

xrpl.js 4.6.0 API 문서에 따르면 Wallet.seed는 선택적 속성(seed?: string)입니다. Wallet.generate()가 시드로부터 지갑을 생성하더라도, 라이브러리는 wallet.seed 속성이 항상 접근 가능함을 보장하지 않습니다. 따라서 non-null 단언(!)보다는 명시적 검증이 안전합니다. 시드 누락은 이후 Wallet.fromSeed, 펀딩 등 전체 워크플로우를 무력화할 수 있으므로, 조기에 실패시키는 방어적 처리가 권장됩니다.

♻️ 제안 리팩터
  generateWallet(): XrplWallet {
    const wallet = Wallet.generate();
+   if (!wallet.seed) {
+     throw new Error("Failed to generate XRPL wallet seed");
+   }
    return {
      address: wallet.address,
-     seed: wallet.seed!,
+     seed: wallet.seed,
      publicKey: wallet.publicKey,
      privateKey: wallet.privateKey,
    };
  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/modules/payments/xrpl.service.ts` around lines 70 - 78, The
generateWallet function uses Wallet.generate() and asserts wallet.seed! which
can be undefined; replace the non-null assertion with an explicit defensive
check in generateWallet: after calling Wallet.generate() (referencing
Wallet.generate and the wallet variable), verify wallet.seed is present and if
not throw a clear Error (or return a Result) so callers know wallet creation
failed early; ensure the XrplWallet return only occurs when seed is defined to
avoid downstream failures in Wallet.fromSeed or funding flows.
src/common/decorators/current-user.decorator.ts (1)

8-13: CurrentUser 데코레이터가 현재 사용되지 않음 - 향후 사용 시 타입 안정성 확보 권장

현재 CurrentUser 데코레이터는 정의되어 있지만 코드베이스 어디서도 import되거나 사용되지 않습니다. 다만 향후 사용될 경우를 대비해, ctx.switchToHttp().getRequest()에서 반환되는 reqany로 추론되어 req.session 접근 시 ESLint no-unsafe-member-access 규칙이 경고를 발생시킬 수 있습니다.

데코레이터를 사용할 계획이 있다면, Request 타입을 명시하여 src/types/express-session.d.ts에서 augment된 SessionData와의 타입 일관성을 맞추는 것이 좋습니다.

♻️ 제안 리팩터
 import { createParamDecorator, ExecutionContext } from "@nestjs/common";
+import type { Request } from "express";

 export interface SessionUser {
   userId: string;
   type: "buyer" | "seller";
 }

 export const CurrentUser = createParamDecorator(
   (_: unknown, ctx: ExecutionContext): SessionUser => {
-    const req = ctx.switchToHttp().getRequest();
+    const req = ctx.switchToHttp().getRequest<Request>();
     return { userId: req.session.userId, type: req.session.type };
   },
 );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/common/decorators/current-user.decorator.ts` around lines 8 - 13, The
CurrentUser decorator currently retrieves req via
ctx.switchToHttp().getRequest() without typing, which can trigger
no-unsafe-member-access; update the decorator to explicitly type the request
(e.g., import and use Express's Request or your augmented interface for session
such as the one declared in src/types/express-session.d.ts) when calling
getRequest/when casting, so that SessionUser creation (userId/type) is
type-safe; modify the createParamDecorator callback signature to use that
Request type (refer to CurrentUser, createParamDecorator, ExecutionContext,
SessionUser) and ensure the Request type import is added.
src/config/env.config.ts (1)

72-81: redis.port 타입 일관성과 session.secret 운영 가드.

  • redis.port는 환경 변수 사용 시 string, fallback 시 number로 타입이 갈라져 다운스트림(ioredis/connect-redis)에서 미묘한 회귀를 만들 수 있습니다. parseInt로 일관화 권장.
  • session.secret도 운영 환경에서 dev fallback이 적용되면 세션 쿠키 위조가 가능합니다. encryptionKey와 동일하게 production에서는 강제 검증을 권장합니다.
♻️ 제안
   redis: {
     password: process.env.REDIS_PASSWORD || "",
     host: process.env.REDIS_HOST || "localhost",
-    port: process.env.REDIS_PORT || 6379,
+    port: parseInt(process.env.REDIS_PORT || "6379", 10),
   },

   session: {
-    secret: process.env.SESSION_SECRET || "dev-secret-change-in-production",
+    secret:
+      process.env.SESSION_SECRET ??
+      (process.env.NODE_ENV === "production"
+        ? (() => {
+            throw new Error("SESSION_SECRET env var is required in production");
+          })()
+        : "dev-secret-change-in-production"),
     ttl: Number(process.env.SESSION_TTL_SECONDS || 7 * 24 * 60 * 60), // 7일
   },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/config/env.config.ts` around lines 72 - 81, Normalize redis.port to
always be a number by parsing the environment value (e.g., use parseInt on
process.env.REDIS_PORT) so redis.port has a consistent numeric type for
downstream clients; additionally, enforce a production check for session.secret
(the session.secret fallback "dev-secret-change-in-production") so that in
production the secret cannot be the dev fallback—throw or exit if SESSION_SECRET
is missing or equals the default—mirroring the existing encryptionKey validation
pattern; update src/config/env.config.ts symbols redis.port and session.secret
accordingly and ensure session.ttl remains Number(...) as before.
src/modules/auth/auth.service.ts (2)

31-35: 중복 이메일 검사에 TOCTOU(check-then-act) 레이스가 있습니다.

exists 응답과 save 사이 시간 동안 같은 이메일로 다른 요청이 통과할 수 있습니다. 이 경우 이메일에 걸린 unique index가 MongoServerError(code: 11000)로 전파되어 클라이언트는 ConflictException 대신 500을 받게 됩니다. unique index를 신뢰하고 duplicate-key 에러를 ConflictException으로 매핑하는 패턴을 권장합니다.

♻️ 제안 (registerSeller에 동일 적용; registerBuyer 동일 패턴)
-    const exists = await this.userModel.exists({
-      email: dto.representativeEmail,
-    });
-    if (exists) throw new ConflictException("Already registered Email");
-
     const wallet = this.xrplService.generateWallet();
     const hashedPassword = await bcrypt.hash(dto.password, BCRYPT_ROUNDS);

     const newSeller = new this.companyModel({ ... });

-    const saved = await newSeller.save();
+    let saved;
+    try {
+      saved = await newSeller.save();
+    } catch (err: any) {
+      if (err?.code === 11000) {
+        throw new ConflictException("Already registered Email");
+      }
+      throw err;
+    }
     this.activateAccountInBackground(wallet, saved._id, "seller");
     return this.toSafeResponse(saved.toObject());

선제적 exists 검사를 빠른 사용자 피드백 용도로 유지하고 싶다면 그대로 두되, save 단계에서도 11000 핸들링을 함께 두는 “양쪽 다” 패턴이 안전합니다.

Also applies to: 65-69

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/modules/auth/auth.service.ts` around lines 31 - 35, The registerSeller
(and similarly registerBuyer) flow has a TOCTOU race: keep the pre-check
(userModel.exists) if you want quick feedback but also wrap the persistent write
(e.g., userModel.create/save in registerSeller and registerBuyer) in a try/catch
that detects Mongo duplicate-key errors (MongoServerError with code === 11000 or
error.name === 'MongoServerError') and rethrow a ConflictException (e.g., new
ConflictException("Already registered Email") or include error.keyValue.email)
instead of allowing a 500; update both registerSeller and registerBuyer to map
11000 -> ConflictException while leaving the exists check optional for UX.

138-141: err.message 직접 접근은 unknown 에러에서 안전하지 않습니다.

TS의 catch 변수는 unknown이며, XRPL/Mongo 드라이버는 종종 plain object 또는 string을 throw 합니다. err.messageundefined로 직렬화되면 운영 트러블슈팅이 어려워집니다.

♻️ 제안
-    } catch (err) {
+    } catch (err: any) {
       this.logger.error(
-        `Failed to activate ${type} (${String(id)}): ${err.message}`,
+        `Failed to activate ${type} (${String(id)})`,
+        err instanceof Error ? err.stack : String(err),
       );

retryOnelogger.warn 호출도 동일하게 정리하면 좋습니다.

Also applies to: 192-196

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/modules/auth/auth.service.ts` around lines 138 - 141, The catch blocks in
auth.service.ts access err.message directly which is unsafe because the catch
variable is unknown and external libs may throw strings or plain objects; update
the catch handlers (the one that calls this.logger.error in the activate flow
and the logger.warn in retryOne) to normalize the error before logging — e.g.,
derive a safe message via a helper like getErrorMessage(err) or using (err
instanceof Error ? err.message : typeof err === 'string' ? err :
JSON.stringify(err)), and log both the normalized message and the original error
object to preserve details for debugging while avoiding undefined.message
runtime issues.
src/modules/auth/auth.service.spec.ts (1)

89-351: 테스트 커버리지 양호 — loginactivateAccountInBackground 보강 권장.

등록/재시도 경로는 잘 다루고 있지만, AuthService.login(이메일 미존재/비밀번호 불일치/성공 시 password·wallet.seed 제거)과 activateAccountInBackground의 성공/실패 분기(상태가 ACTIVE/FAILED_ACTIVATION로 전이되는지)를 직접 검증하는 케이스가 비어 있습니다. 회원가입과 함께 가장 보안 민감한 두 경로이므로 단위 테스트를 보강해 주시면 좋습니다.

원하시면 위 두 경로에 대한 테스트 스니펫을 별도 코멘트/이슈로 제안해 드릴 수 있습니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/modules/auth/auth.service.spec.ts` around lines 89 - 351, Add unit tests
in auth.service.spec.ts to cover AuthService.login and
AuthService.activateAccountInBackground: for login add tests that calling login
with a non-existent email throws UnauthorizedException, calling login with wrong
password throws UnauthorizedException, and a successful login returns user
object with sensitive fields removed (ensure returned object has no password and
no wallet.seed) by mocking userModel.findOne and bcrypt/verification as needed;
for activateAccountInBackground add tests that when xrplService.fundAccount
resolves the relevant model (companyModel or buyerModel) updateOne is called to
set status to "ACTIVE", and when fundAccount rejects the updateOne is called to
set status to "FAILED_ACTIVATION" (mock companyModel.find/ buyerModel.find to
return test documents and assert xrplService.fundAccount calls and subsequent
updateOne calls).
src/modules/users/schemas/user.schema.ts (1)

18-19: unique: true가 이미 인덱스를 생성하므로 index: true는 제거하세요.

unique: true는 자동으로 고유 인덱스를 생성하므로 index: true를 함께 지정하면 중복 인덱스 정의로 인식됩니다. Mongoose 8.9+에서는 이에 대한 명시적 경고가 발생합니다.

♻️ 제안
-  `@Prop`({ required: true, unique: true, index: true })
+  `@Prop`({ required: true, unique: true })
   email: string;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/modules/users/schemas/user.schema.ts` around lines 18 - 19, Remove the
redundant index option from the email property decorator: in user.schema.ts
update the `@Prop` on the email field (the line with `@Prop`({ required: true,
unique: true, index: true }) for email: string) to omit index: true since
unique: true already creates an index; ensure only unique: true (and required:
true) remain so Mongoose won't warn about duplicate index definitions.
src/modules/auth/auth.controller.ts (1)

42-46: @Req() req: any 대신 Request & { session: SessionData } 형태의 타입을 사용하세요.

src/types/express-session.d.ts에 이미 보강된 SessionData로 인해 req.session.userId/req.session.type이 정적으로 검증됩니다. 현재 코드는 any 타입으로 인해 타입 안전성이 손실되고 있습니다.

♻️ 제안
import { Controller, Post, Body, Req, HttpCode } from "@nestjs/common";
+import type { Request } from "express";
import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
-  async login(`@Body`() dto: LoginDto, `@Req`() req: any) {
+  async login(`@Body`() dto: LoginDto, `@Req`() req: Request) {
     const user = await this.authService.login(dto);
     req.session.userId = user._id.toString();
     req.session.type = user.type;
     return { message: "로그인 성공", user };
   }
-  logout(`@Req`() req: any) {
+  logout(`@Req`() req: Request) {
     return new Promise<{ message: string }>((resolve) => {
       req.session.destroy(() => resolve({ message: "로그아웃 성공" }));
     });
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/modules/auth/auth.controller.ts` around lines 42 - 46, Replace the loose
any type on the controller request in the login handler with the proper
Express/session types: update the parameter of async login(...) (in
auth.controller.ts) from `@Req`() req: any to `@Req`() req: Request & { session:
SessionData }, import Request from 'express' and SessionData from your augmented
session types file so that req.session.userId and req.session.type are
statically typed and checked; keep the rest of the login logic unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8a2d45d2-1f1b-4c72-816e-3912c7f945fb

📥 Commits

Reviewing files that changed from the base of the PR and between 00a7032 and 43b260a.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (75)
  • .coderabbit.yaml
  • .env.example
  • .env.test
  • .gitignore
  • docker-compose.yml
  • package.json
  • src/app.module.ts
  • src/common/decorators/current-user.decorator.ts
  • src/common/guards/session.guard.ts
  • src/common/schemas/wallet-info.schema.ts
  • src/config/env.config.ts
  • src/main.ts
  • src/modules/admin/admin.controller.spec.ts
  • src/modules/admin/admin.controller.ts
  • src/modules/admin/admin.module.ts
  • src/modules/admin/admin.service.spec.ts
  • src/modules/admin/admin.service.ts
  • src/modules/admin/dto/audit-logs-query.dto.ts
  • src/modules/admin/dto/list-payments-query.dto.ts
  • src/modules/admin/dto/match-logs-query.dto.ts
  • src/modules/admin/dto/payment-stats-query.dto.ts
  • src/modules/admin/guards/admin-token.guard.spec.ts
  • src/modules/admin/guards/admin-token.guard.ts
  • src/modules/admin/schemas/audit-log.schema.ts
  • src/modules/auth/auth.controller.spec.ts
  • src/modules/auth/auth.controller.ts
  • src/modules/auth/auth.module.ts
  • src/modules/auth/auth.service.spec.ts
  • src/modules/auth/auth.service.ts
  • src/modules/auth/dto/login.dto.ts
  • src/modules/auth/dto/register.dto.ts
  • src/modules/buyers/buyers.controller.spec.ts
  • src/modules/buyers/buyers.controller.ts
  • src/modules/buyers/buyers.module.ts
  • src/modules/buyers/buyers.service.spec.ts
  • src/modules/buyers/buyers.service.ts
  • src/modules/buyers/dto/create-buyer.dto.ts
  • src/modules/buyers/dto/update-buyer.dto.ts
  • src/modules/buyers/schemas/buyer.schema.ts
  • src/modules/companies/companies.module.ts
  • src/modules/companies/companies.service.spec.ts
  • src/modules/companies/companies.service.ts
  • src/modules/companies/company-images.controller.spec.ts
  • src/modules/companies/company-images.controller.ts
  • src/modules/companies/company-images.service.spec.ts
  • src/modules/companies/company-images.service.ts
  • src/modules/companies/dto/create-company.dto.ts
  • src/modules/companies/dto/upload-image.dto.ts
  • src/modules/companies/schemas/company.schema.ts
  • src/modules/consultants/consultants.controller.spec.ts
  • src/modules/consultants/consultants.controller.ts
  • src/modules/consultants/consultants.module.ts
  • src/modules/consultants/consultants.service.spec.ts
  • src/modules/consultants/consultants.service.ts
  • src/modules/consultants/dto/create-consultant-request.dto.ts
  • src/modules/consultants/schemas/consultant-request.schema.ts
  • src/modules/consultations/consultations.service.ts
  • src/modules/insights/insights.controller.spec.ts
  • src/modules/insights/insights.controller.ts
  • src/modules/insights/insights.module.ts
  • src/modules/insights/insights.service.spec.ts
  • src/modules/insights/insights.service.ts
  • src/modules/matches/matches.service.spec.ts
  • src/modules/matches/matches.service.ts
  • src/modules/partners/partners.module.ts
  • src/modules/partners/partners.service.spec.ts
  • src/modules/partners/partners.service.ts
  • src/modules/payments/xrpl.service.ts
  • src/modules/users/schemas/buyer.schema.ts
  • src/modules/users/schemas/company.schema.ts
  • src/modules/users/schemas/user.schema.ts
  • src/modules/users/users.module.ts
  • src/types/express-session.d.ts
  • test/jest-e2e.json
  • test/xrpl.e2e-spec.ts
💤 Files with no reviewable changes (34)
  • src/modules/admin/admin.controller.ts
  • src/modules/buyers/dto/create-buyer.dto.ts
  • src/modules/companies/dto/upload-image.dto.ts
  • src/modules/buyers/dto/update-buyer.dto.ts
  • src/modules/consultants/consultants.controller.spec.ts
  • src/modules/admin/guards/admin-token.guard.ts
  • src/modules/consultants/consultants.controller.ts
  • .env.example
  • src/modules/admin/admin.module.ts
  • src/modules/admin/dto/match-logs-query.dto.ts
  • src/modules/insights/insights.module.ts
  • src/modules/insights/insights.controller.ts
  • src/modules/consultants/consultants.service.ts
  • src/modules/admin/dto/payment-stats-query.dto.ts
  • src/modules/buyers/schemas/buyer.schema.ts
  • src/modules/companies/company-images.service.spec.ts
  • src/modules/consultants/consultants.module.ts
  • src/modules/admin/schemas/audit-log.schema.ts
  • src/modules/companies/company-images.controller.ts
  • src/modules/consultants/schemas/consultant-request.schema.ts
  • src/modules/consultants/dto/create-consultant-request.dto.ts
  • src/modules/admin/admin.service.ts
  • src/modules/insights/insights.service.ts
  • src/modules/admin/guards/admin-token.guard.spec.ts
  • src/modules/companies/company-images.controller.spec.ts
  • src/modules/admin/dto/list-payments-query.dto.ts
  • src/modules/companies/company-images.service.ts
  • src/modules/insights/insights.controller.spec.ts
  • src/modules/admin/dto/audit-logs-query.dto.ts
  • src/modules/admin/admin.controller.spec.ts
  • src/modules/insights/insights.service.spec.ts
  • src/modules/admin/admin.service.spec.ts
  • src/modules/companies/schemas/company.schema.ts
  • src/modules/consultants/consultants.service.spec.ts

Comment thread .coderabbit.yaml
Comment thread .env.test Outdated
Comment thread .gitignore
Comment thread src/app.module.ts
Comment thread src/common/schemas/wallet-info.schema.ts
Comment thread src/modules/partners/partners.module.ts
Comment thread src/modules/payments/xrpl.service.ts
Comment thread src/modules/payments/xrpl.service.ts
Comment thread src/modules/users/schemas/company.schema.ts
Comment thread test/xrpl.e2e-spec.ts
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.

2 participants