Skip to content

[FEAT] 마이비즈니스(My Business) 도메인 API 구현#19

Merged
Takch02 merged 6 commits into
mainfrom
feat/my-business
May 9, 2026
Merged

[FEAT] 마이비즈니스(My Business) 도메인 API 구현#19
Takch02 merged 6 commits into
mainfrom
feat/my-business

Conversation

@BaeSeong-min

@BaeSeong-min BaeSeong-min commented May 8, 2026

Copy link
Copy Markdown
Contributor

개요

마이비즈니스 도메인 신규 모듈을 생성하고, 로그인 사용자의 프로필 조회 및 파트너 기업 목록 조회 API를 구현합니다.

구현 내용

1. GET /my-business/profile

  • 로그인한 사용자의 개인정보 및 XRPL 지갑 주소 반환
  • seller: exportItems, location, sizeBucket 포함
  • buyer: needs 포함

2. GET /my-business/partners

  • 사용자가 파트너로 추가한 기업 목록 반환 (페이지네이션)
  • seller/buyer 혼합 조회 지원
  • savedPartners 추가 순서 유지
  • DB에서 삭제된 기업 자동 제외

변경 파일

  • src/modules/my-business/ — 신규 모듈 (controller, service, module)
  • src/modules/users/schemas/user.schema.tssavedPartners 필드 추가
  • src/app.module.tsMyBusinessModule 등록
  • src/modules/my-business/my-business.service.spec.ts — 서비스 테스트 코드

참고

  • 전 엔드포인트 SessionGuard 적용 (로그인 필수)

Summary by CodeRabbit

릴리스 노트

  • New Features
    • 새로운 "내 사업" 섹션 추가: 인증된 사용자가 자신의 프로필 정보를 조회할 수 있습니다.
    • 파트너 기업 목록 기능 추가: 사용자가 연결된 판매자 및 구매자 파트너를 페이지네이션을 통해 조회할 수 있습니다.

@BaeSeong-min BaeSeong-min requested a review from Takch02 May 8, 2026 16:32
@BaeSeong-min BaeSeong-min self-assigned this May 8, 2026
@coderabbitai

coderabbitai Bot commented May 8, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@Takch02 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 6 minutes and 45 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 96457343-fe4b-460e-b893-6753b3f9dd4e

📥 Commits

Reviewing files that changed from the base of the PR and between 5ab2cff and 162dd7e.

📒 Files selected for processing (2)
  • src/modules/my-business/dto/getPartnersQuery.dto.ts
  • src/modules/my-business/my-business.controller.ts
📝 Walkthrough

Walkthrough

새로운 MyBusinessModule을 추가하여 인증된 사용자가 자신의 프로필과 파트너 회사 목록을 조회할 수 있습니다. User 스키마에 savedPartners 필드를 추가하고, 두 개의 GET 엔드포인트(/my-business/profile, /my-business/partners)를 제공하며, 페이지네이션과 seller/buyer 타입 구분을 지원합니다.

Changes

My Business Module 기능 추가

Layer / File(s) Summary
Data Schema
src/modules/users/schemas/user.schema.ts
User 스키마에 savedPartners 배열 추가 (partnerId, partnerType 포함).
Service Logic
src/modules/my-business/my-business.service.ts
MyBusinessService 구현: getProfile(userId)과 getPartners(userId, page, limit) 메서드 제공. getPartners는 페이지네이션, seller/buyer 병렬 쿼리, 순서 보존 수행.
Controller & Module
src/modules/my-business/my-business.controller.ts, src/modules/my-business/my-business.module.ts
MyBusinessController로 /my-business/profile, /my-business/partners 엔드포인트 노출. SessionGuard로 인증 보호. MyBusinessModule이 Users/Sellers/Buyers 모듈 연결.
App Registration
src/app.module.ts
AppModule의 imports 배열에 MyBusinessModule 등록.
Tests
src/modules/my-business/my-business.service.spec.ts
getProfile/getPartners의 성공, 에러, 페이지네이션, 혼합 파트너 타입, 삭제된 파트너 제외 등 포괄적 테스트 커버리지.

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly Related Issues

Poem

파트너와 프로필을 담는 새 모듈
스키마에는 savedPartners 빛나고 ✨
서비스가 조회하고, 컨트롤러가 응답하니
사용자의 비즈니스, 이제 드러나네 🤝

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 제목이 변경사항의 핵심을 명확하게 반영합니다. '마이비즈니스(My Business) 도메인 API 구현'은 새로운 모듈 추가 및 두 가지 인증된 API 엔드포인트 구현이라는 주요 변경사항을 정확히 요약합니다.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/my-business

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (2)
src/modules/my-business/my-business.controller.ts (1)

24-24: 💤 Low value

req: any 대신 타입이 있는 요청 인터페이스 사용을 권장합니다

req: anysession.userId 접근의 타입 안전성을 잃습니다. 프로젝트 내 다른 컨트롤러에서 사용 중인 세션 타입 인터페이스(예: RequestWithSession)가 있다면 재사용하거나, 간단히 다음과 같이 정의할 수 있습니다:

interface RequestWithSession extends Request {
  session: { userId: string };
}

Also applies to: 51-51

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/modules/my-business/my-business.controller.ts` at line 24, Replace the
use of the untyped parameter "req: any" in getProfile (and the other controller
method at the same file) with a proper request type that includes
session.userId; add or import an interface like RequestWithSession that extends
Express's Request and declares session: { userId: string }, then update the
method signatures (e.g., getProfile(`@Req`() req: RequestWithSession)) so
accessing req.session.userId is type-safe and consistent with other controllers.
src/modules/my-business/my-business.service.spec.ts (1)

94-185: 💤 Low value

page=0limit=0 경계 케이스 테스트가 누락되어 있습니다

서비스 로직 상 page=0 시 음수 skip으로 빈 데이터가 반환되고, limit=0totalPages = Infinity가 됩니다. 서비스에서 직접 방어 처리하는 경우라면 이에 대한 테스트가 필요합니다.

it("limit=0 → totalPages가 Infinity가 되지 않아야 함", async () => {
  // ... 컨트롤러/서비스에서 BadRequestException을 던지는지 검증
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/modules/my-business/my-business.service.spec.ts` around lines 94 - 185,
Add boundary tests for page=0 and limit=0 against the getPartners flow: create
two new it blocks in the my-business.service.spec.ts suite that call
service.getPartners with page=0 and with limit=0 (e.g.,
service.getPartners(USER_ID, 0, 10) and service.getPartners(USER_ID, 1, 0)),
mock userModel.findById (use buildFindMock as in other tests) and assert the
expected defensive behavior (e.g., expect rejection with BadRequestException or
the service's chosen error/handling). Ensure tests reference
service.getPartners, userModel.findById mocks, and the specific behavior you
want enforced so the edge cases (negative skip and Infinity totalPages) are
covered.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/modules/my-business/my-business.controller.ts`:
- Around line 50-56: The getPartners controller accepts page and limit with only
ParseIntPipe, allowing invalid values (page=0, limit=0 or very large) that break
pagination and DB queries; update the parameter validation on getPartners to
enforce page >= 1 and limit within a safe range (e.g., 1..100) by adding Min/Max
validation pipes or a small custom ValidPaginationPipe (apply to the
`@Query`("page") and `@Query`("limit") parameters), sanitize/fallback to defaults
before calling this.myBusinessService.getPartners(req.session.userId, page,
limit), and ensure the service receives the clamped/validated numbers to prevent
Infinity totalPages or huge $in queries.

In `@src/modules/my-business/my-business.service.ts`:
- Around line 124-130: When computing totalPages in the service return object,
guard against limit being 0 (which makes Math.ceil(total / limit) === Infinity);
update the calculation so it uses a safe divisor (e.g., Math.ceil(total /
Math.max(1, limit)) or explicitly set totalPages = 0 when limit < 1) and/or add
validation in the controller to enforce limit >= 1; change the expression that
constructs totalPages (currently Math.ceil(total / limit)) to use the safe
divisor to prevent Infinity when limit is 0.
- Around line 75-78: The pagination currently uses total = savedPartners.length
which counts partnerIds that may no longer exist in the DB, causing incorrect
pagination; in the getSavedPartners logic in MyBusinessService (around
savedPartners / total / pageSlice) change the response to include both the
original saved count and the actual active count by querying the Partner
repository for existing partnerIds (e.g., filter savedPartners by existence to
produce activePartners), set totalSaved = savedPartners.length and totalActive =
activePartners.length (use activePartners for data and totalActive/totalPages),
and add a TODO note or implement a cleanup routine to remove deleted partnerIds
from users' savedPartners on partner deletion.
- Around line 59-66: Add ObjectId validation at the start of both getProfile and
getPartners by using Types.ObjectId.isValid(userId) before calling
userModel.findById; if invalid, throw the same NotFoundException("사용자를 찾을 수
없습니다."); so that userModel.findById (used with
MyBusinessService.PROFILE_PROJECTION in getProfile) no longer triggers a
Mongoose CastError and returns the appropriate 4xx response instead of a 500.

---

Nitpick comments:
In `@src/modules/my-business/my-business.controller.ts`:
- Line 24: Replace the use of the untyped parameter "req: any" in getProfile
(and the other controller method at the same file) with a proper request type
that includes session.userId; add or import an interface like RequestWithSession
that extends Express's Request and declares session: { userId: string }, then
update the method signatures (e.g., getProfile(`@Req`() req: RequestWithSession))
so accessing req.session.userId is type-safe and consistent with other
controllers.

In `@src/modules/my-business/my-business.service.spec.ts`:
- Around line 94-185: Add boundary tests for page=0 and limit=0 against the
getPartners flow: create two new it blocks in the my-business.service.spec.ts
suite that call service.getPartners with page=0 and with limit=0 (e.g.,
service.getPartners(USER_ID, 0, 10) and service.getPartners(USER_ID, 1, 0)),
mock userModel.findById (use buildFindMock as in other tests) and assert the
expected defensive behavior (e.g., expect rejection with BadRequestException or
the service's chosen error/handling). Ensure tests reference
service.getPartners, userModel.findById mocks, and the specific behavior you
want enforced so the edge cases (negative skip and Infinity totalPages) are
covered.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 749f2028-68c7-4f60-9bff-1a31dfc954a3

📥 Commits

Reviewing files that changed from the base of the PR and between 62c9ef7 and 5ab2cff.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (6)
  • src/app.module.ts
  • src/modules/my-business/my-business.controller.ts
  • src/modules/my-business/my-business.module.ts
  • src/modules/my-business/my-business.service.spec.ts
  • src/modules/my-business/my-business.service.ts
  • src/modules/users/schemas/user.schema.ts

Comment thread src/modules/my-business/my-business.controller.ts
Comment thread src/modules/my-business/my-business.service.ts
Comment thread src/modules/my-business/my-business.service.ts
Comment on lines +124 to +130
return {
page,
limit,
total,
totalPages: Math.ceil(total / limit),
data,
};

@coderabbitai coderabbitai Bot May 8, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

limit=0 전달 시 totalPagesInfinity가 됩니다

Math.ceil(total / 0)Infinity를 반환합니다. ParseIntPipe0을 유효한 정수로 허용하므로 컨트롤러에서 차단하지 않으면 이 경로에 도달합니다.

컨트롤러에서 limit >= 1 검증을 추가하거나, 서비스에서 방어적으로 처리할 수 있습니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/modules/my-business/my-business.service.ts` around lines 124 - 130, When
computing totalPages in the service return object, guard against limit being 0
(which makes Math.ceil(total / limit) === Infinity); update the calculation so
it uses a safe divisor (e.g., Math.ceil(total / Math.max(1, limit)) or
explicitly set totalPages = 0 when limit < 1) and/or add validation in the
controller to enforce limit >= 1; change the expression that constructs
totalPages (currently Math.ceil(total / limit)) to use the safe divisor to
prevent Infinity when limit is 0.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Controller 에서 limit 검증하고 오기

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@Takch02, 컨트롤러에서 limit >= 1을 강제하는 방법을 제안드립니다.

옵션 1 — ParseIntPipe + 인라인 min 검증 파이프 체이닝 (간단)

// my-business.controller.ts
import {
  Controller, Get, Req, Query,
  UseGuards, DefaultValuePipe, ParseIntPipe,
  BadRequestException, PipeTransform, Injectable, ArgumentMetadata,
} from "@nestjs/common";

`@Injectable`()
class MinValuePipe implements PipeTransform {
  constructor(private readonly min: number) {}
  transform(value: number, _metadata: ArgumentMetadata) {
    if (value < this.min) {
      throw new BadRequestException(`limit은 ${this.min} 이상이어야 합니다.`);
    }
    return value;
  }
}

// 엔드포인트 파라미터에 적용
`@Get`("partners")
getPartners(
  `@Req`() req: any,
  `@Query`("page", new DefaultValuePipe(1), ParseIntPipe) page: number,
  `@Query`("limit", new DefaultValuePipe(10), ParseIntPipe, new MinValuePipe(1)) limit: number,
) {
  return this.myBusinessService.getPartners(req.session.userId, page, limit);
}

옵션 2 — ParseIntPipeexceptionFactory 활용 없이, DTO + class-validator 사용 (더 정석)

// get-partners.query.dto.ts
import { IsInt, Min, Max } from "class-validator";
import { Type } from "class-transformer";

export class GetPartnersQueryDto {
  `@Type`(() => Number)
  `@IsInt`()
  `@Min`(1)
  page: number = 1;

  `@Type`(() => Number)
  `@IsInt`()
  `@Min`(1)
  `@Max`(100)
  limit: number = 10;
}
// 컨트롤러에서
`@Get`("partners")
getPartners(`@Req`() req: any, `@Query`() query: GetPartnersQueryDto) {
  return this.myBusinessService.getPartners(req.session.userId, query.page, query.limit);
}

DTO 방식이 page에 대한 검증도 함께 처리할 수 있고, 이후 확장에도 유리합니다 👍

Comment thread src/modules/my-business/my-business.controller.ts
Comment thread src/modules/my-business/my-business.service.ts
Comment thread src/modules/my-business/my-business.service.ts
Comment on lines +124 to +130
return {
page,
limit,
total,
totalPages: Math.ceil(total / limit),
data,
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Controller 에서 limit 검증하고 오기

@Takch02 Takch02 merged commit ab9e5c6 into main May 9, 2026
3 checks passed
@Takch02 Takch02 deleted the feat/my-business branch May 9, 2026 12:47
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.

[FEAT] 마이비즈니스 도메인 백엔드 API 구현

2 participants