[FEAT] 마이비즈니스(My Business) 도메인 API 구현#19
Conversation
|
Warning Rate limit exceeded
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 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 configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthrough새로운 MyBusinessModule을 추가하여 인증된 사용자가 자신의 프로필과 파트너 회사 목록을 조회할 수 있습니다. User 스키마에 ChangesMy Business Module 기능 추가
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly Related Issues
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (2)
src/modules/my-business/my-business.controller.ts (1)
24-24: 💤 Low value
req: any대신 타입이 있는 요청 인터페이스 사용을 권장합니다
req: any는session.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=0및limit=0경계 케이스 테스트가 누락되어 있습니다서비스 로직 상
page=0시 음수 skip으로 빈 데이터가 반환되고,limit=0시totalPages = 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
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (6)
src/app.module.tssrc/modules/my-business/my-business.controller.tssrc/modules/my-business/my-business.module.tssrc/modules/my-business/my-business.service.spec.tssrc/modules/my-business/my-business.service.tssrc/modules/users/schemas/user.schema.ts
| return { | ||
| page, | ||
| limit, | ||
| total, | ||
| totalPages: Math.ceil(total / limit), | ||
| data, | ||
| }; |
There was a problem hiding this comment.
limit=0 전달 시 totalPages가 Infinity가 됩니다
Math.ceil(total / 0)은 Infinity를 반환합니다. ParseIntPipe는 0을 유효한 정수로 허용하므로 컨트롤러에서 차단하지 않으면 이 경로에 도달합니다.
컨트롤러에서 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.
There was a problem hiding this comment.
Controller 에서 limit 검증하고 오기
There was a problem hiding this comment.
@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 — ParseIntPipe의 exceptionFactory 활용 없이, 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에 대한 검증도 함께 처리할 수 있고, 이후 확장에도 유리합니다 👍
| return { | ||
| page, | ||
| limit, | ||
| total, | ||
| totalPages: Math.ceil(total / limit), | ||
| data, | ||
| }; |
There was a problem hiding this comment.
Controller 에서 limit 검증하고 오기
개요
마이비즈니스 도메인 신규 모듈을 생성하고, 로그인 사용자의 프로필 조회 및 파트너 기업 목록 조회 API를 구현합니다.
구현 내용
1.
GET /my-business/profileexportItems,location,sizeBucket포함needs포함2.
GET /my-business/partnerssavedPartners추가 순서 유지변경 파일
src/modules/my-business/— 신규 모듈 (controller, service, module)src/modules/users/schemas/user.schema.ts—savedPartners필드 추가src/app.module.ts—MyBusinessModule등록src/modules/my-business/my-business.service.spec.ts— 서비스 테스트 코드참고
SessionGuard적용 (로그인 필수)Summary by CodeRabbit
릴리스 노트