[FEATURE] 리뷰 조회, 알바 검색 필터링 조건 추가#68
Conversation
WalkthroughAdds store review retrieval functionality via a new controller endpoint, service layer, and repository query. Enhances alba search with JWT authentication and filters out postings the authenticated user has already applied to by propagating user UUID as a buffer through the call stack. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant Controller as Search Alba<br/>Controller
participant Service as Search Alba<br/>Service
participant Repo as Search Alba<br/>Repository
participant DB as Database
User->>Controller: GET /search (JWT token)
Controller->>Controller: `@Security`('jwt') validates<br/>Extracts req.user.id
Controller->>Controller: Convert UUID to Buffer<br/>(userBuffer)
Controller->>Service: getFilteredAlba(params, userBuffer)
Service->>Repo: findPostingByFilter({...params, userBuffer})
Repo->>Repo: Apply filter: user_alba none<br/>(exclude user's applications)
Repo->>DB: Query with exclusion filter
DB-->>Repo: Alba postings (excluding user's)
Repo-->>Service: Results
Service-->>Controller: SearchAlbaResponseDto[]
Controller-->>User: 200 OK + filtered results
sequenceDiagram
actor User
participant Controller as Store Review<br/>Controller
participant Service as Store Review<br/>Service
participant Repo as Store Review<br/>Repository
participant DB as Database
User->>Controller: GET /reviews/{storeId} (JWT token)
Controller->>Controller: `@Security`('jwt') validates<br/>@Get('/{storeId}') extracts path param
Controller->>Service: getStoreReviewById(storeId)
Service->>Service: Convert storeId string<br/>to Buffer
Service->>Repo: findReviewsByStoreId(storeId)
Repo->>DB: Query reviews by store ID<br/>(with user + store relations)
DB-->>Repo: Review records
Repo-->>Service: Raw review data
Service->>Service: Map to StoreReviewDto<br/>(reviews array)
Service-->>Controller: StoreReviewDto
Controller->>Controller: Set HTTP 201
Controller-->>User: 201 Created + reviews
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 6✅ Passed checks (6 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
🤖 Fix all issues with AI agents
In `@src/controller/search_alba_controller.ts`:
- Line 22: Remove the mandatory `@Security`('jwt') decorator so requests aren't
rejected; instead, make authentication optional by removing or commenting out
`@Security`('jwt') and within the search handler (the method that computes
userBuffer) attempt to extract and verify the JWT manually (e.g., read
Authorization header, call the existing token verification helper or jwt
service), set userBuffer to the decoded user when verification succeeds and to
undefined when there is no token or verification fails, and ensure the rest of
the logic in that method uses userBuffer possibly being undefined to return
unfiltered results for unauthenticated users.
- Around line 30-32: The code unsafely assumes req.user exists when creating
userBuffer from uuidToBuffer; update the handler in search_alba_controller to
defensively check req.user (e.g., if (!req.user || typeof (req.user as any).id
!== 'string') ) before reading the id, and handle the missing user according to
auth semantics (return a 401/unauthorized error or proceed as anonymous) rather
than directly casting; then derive userId from the validated object and call
uuidToBuffer(userId) to set userBuffer.
In `@src/controller/store_review_controller.ts`:
- Around line 41-53: The GET handler getStoreReview declares a 200
SuccessResponse but calls this.setStatus(201); update getStoreReview to use the
correct status for a retrieval (call this.setStatus(200) or remove the setStatus
call so default 200 is used) and ensure the call sites around getStoreReviewById
remain unchanged; adjust only the this.setStatus invocation in the
getStoreReview method.
In `@src/service/search_alba_service.ts`:
- Line 10: The getFilteredAlba function signature requires a Buffer but should
accept unauthenticated calls: change the parameter in getFilteredAlba(params:
SearchAlbaRequestDto, userBuffer?: Buffer): Promise<SearchAlbaResponseDto[]> and
update all call sites to pass undefined when no user is present; also remove or
make the controller's `@Security`('jwt') decorator optional so unauthenticated
requests reach the controller, and ensure any logic inside getFilteredAlba that
assumes userBuffer is present guards for undefined (e.g., only apply filtering
when userBuffer is truthy).
🧹 Nitpick comments (3)
src/repository/store_review_repository.ts (1)
49-58: Consider usingselectinstead ofinclude: trueto avoid fetching sensitive columns.
include: { user: true, store: true }loads every column from theuserandstoretables. If those tables contain sensitive fields (password hashes, emails, etc.), they'll be held in memory even though the service only usesuser_nameandstore_name. The same pattern exists increateStoreReviewabove.♻️ Suggested change
export const findReviewsByStoreId = async (storeId: Buffer) => { return await prisma.store_review.findMany({ where: { store_id: new Uint8Array(storeId) }, include: { - user:true, - store:true + user: { select: { user_name: true } }, + store: { select: { store_name: true } } }, orderBy: { created_at: 'desc' } }); };src/service/store_review_service.ts (2)
25-49: No validation onstoreIdStr— invalid UUID will throw an unhandled error.If
storeIdStris not a valid UUID,uuidToBufferwill throw, resulting in a 500 to the client with no meaningful message. Consider validating the input or wrapping with a try/catch that returns a 400-level error.Also, the function name
getStoreReviewByIdsuggests fetching a single review;getReviewsByStoreIdwould better describe its behavior.
22-25: Inconsistent indentation — this top-level function is indented as if it were a class member.Lines 22–49 have extra indentation compared to
addReviewabove. Minor formatting nit.
| * @returns 조건에 맞는 알바 리스트 | ||
| */ | ||
| @Get('search') | ||
| @Security('jwt') |
There was a problem hiding this comment.
@Security('jwt') blocks unauthenticated users entirely.
Per the PR objective, unauthenticated users should still see unfiltered results. With @Security('jwt'), they'll receive a 401 instead. Consider making authentication optional (e.g., extract the user from the token manually without enforcing @Security) so unauthenticated requests pass through with userBuffer as undefined.
🤖 Prompt for AI Agents
In `@src/controller/search_alba_controller.ts` at line 22, Remove the mandatory
`@Security`('jwt') decorator so requests aren't rejected; instead, make
authentication optional by removing or commenting out `@Security`('jwt') and
within the search handler (the method that computes userBuffer) attempt to
extract and verify the JWT manually (e.g., read Authorization header, call the
existing token verification helper or jwt service), set userBuffer to the
decoded user when verification succeeds and to undefined when there is no token
or verification fails, and ensure the rest of the logic in that method uses
userBuffer possibly being undefined to return unfiltered results for
unauthenticated users.
| //UUID 문자열 Buffer로 변환 | ||
| const userId = (req.user as unknown as { id: string }).id; | ||
| const userBuffer=Buffer.from(uuidToBuffer(userId)) |
There was a problem hiding this comment.
Unsafe access to req.user — will crash if user context is absent.
The double cast (req.user as unknown as { id: string }).id will throw a TypeError at runtime if req.user is undefined. While @Security('jwt') should guarantee a user, if authentication is made optional (per the other comment), this will break. Even with mandatory auth, a defensive check is safer.
Proposed fix (supports optional auth)
- const userId = (req.user as unknown as { id: string }).id;
- const userBuffer=Buffer.from(uuidToBuffer(userId))
-
- const result = await getFilteredAlba(params,userBuffer)
+ let userBuffer: Buffer | undefined;
+ if (req.user) {
+ const userId = (req.user as unknown as { id: string }).id;
+ userBuffer = Buffer.from(uuidToBuffer(userId));
+ }
+
+ const result = await getFilteredAlba(params, userBuffer)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| //UUID 문자열 Buffer로 변환 | |
| const userId = (req.user as unknown as { id: string }).id; | |
| const userBuffer=Buffer.from(uuidToBuffer(userId)) | |
| //UUID 문자열 Buffer로 변환 | |
| let userBuffer: Buffer | undefined; | |
| if (req.user) { | |
| const userId = (req.user as unknown as { id: string }).id; | |
| userBuffer = Buffer.from(uuidToBuffer(userId)); | |
| } | |
| const result = await getFilteredAlba(params, userBuffer) |
🤖 Prompt for AI Agents
In `@src/controller/search_alba_controller.ts` around lines 30 - 32, The code
unsafely assumes req.user exists when creating userBuffer from uuidToBuffer;
update the handler in search_alba_controller to defensively check req.user
(e.g., if (!req.user || typeof (req.user as any).id !== 'string') ) before
reading the id, and handle the missing user according to auth semantics (return
a 401/unauthorized error or proceed as anonymous) rather than directly casting;
then derive userId from the validated object and call uuidToBuffer(userId) to
set userBuffer.
| @Get('/{storeId}') | ||
| @Security('jwt') | ||
| @SuccessResponse('200','조회 성공') | ||
| @Response('400','Bad Request') | ||
| @Response('500','Internal Server Error') | ||
| public async getStoreReview( | ||
| @Path() storeId: string, | ||
| ): Promise<StoreReviewDto>{ | ||
|
|
||
| const result= await getStoreReviewById(storeId) | ||
| this.setStatus(201); | ||
| return result; | ||
| } |
There was a problem hiding this comment.
Status code mismatch: setStatus(201) on a GET endpoint that declares @SuccessResponse('200').
Line 43 declares @SuccessResponse('200', ...) but Line 51 sets status to 201. For a retrieval endpoint, the correct status is 200. This mismatch is also visible in the PR screenshot (Image 1) showing "201 Undocumented".
🐛 Fix
- this.setStatus(201);
+ this.setStatus(200);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @Get('/{storeId}') | |
| @Security('jwt') | |
| @SuccessResponse('200','조회 성공') | |
| @Response('400','Bad Request') | |
| @Response('500','Internal Server Error') | |
| public async getStoreReview( | |
| @Path() storeId: string, | |
| ): Promise<StoreReviewDto>{ | |
| const result= await getStoreReviewById(storeId) | |
| this.setStatus(201); | |
| return result; | |
| } | |
| `@Get`('/{storeId}') | |
| `@Security`('jwt') | |
| `@SuccessResponse`('200','조회 성공') | |
| `@Response`('400','Bad Request') | |
| `@Response`('500','Internal Server Error') | |
| public async getStoreReview( | |
| `@Path`() storeId: string, | |
| ): Promise<StoreReviewDto>{ | |
| const result= await getStoreReviewById(storeId) | |
| this.setStatus(200); | |
| return result; | |
| } |
🤖 Prompt for AI Agents
In `@src/controller/store_review_controller.ts` around lines 41 - 53, The GET
handler getStoreReview declares a 200 SuccessResponse but calls
this.setStatus(201); update getStoreReview to use the correct status for a
retrieval (call this.setStatus(200) or remove the setStatus call so default 200
is used) and ensure the call sites around getStoreReviewById remain unchanged;
adjust only the this.setStatus invocation in the getStoreReview method.
| * @returns 조건에 맞는 알바 리스트 | ||
| */ | ||
| export const getFilteredAlba = async (params:SearchAlbaRequestDto):Promise<SearchAlbaResponseDto[]>=>{ | ||
| export const getFilteredAlba = async (params:SearchAlbaRequestDto,userBuffer:Buffer):Promise<SearchAlbaResponseDto[]>=>{ |
There was a problem hiding this comment.
userBuffer should be optional to support unauthenticated users.
The PR objective states that unauthenticated users should see unfiltered results, but userBuffer is typed as a required Buffer. Combined with the @Security('jwt') decorator on the controller, unauthenticated requests will be rejected with 401 instead of returning unfiltered results.
If the intent is to support both authenticated and unauthenticated access, make userBuffer optional here and in the controller (remove @Security('jwt') or make it optional), passing undefined when no user is present.
Proposed fix
-export const getFilteredAlba = async (params:SearchAlbaRequestDto,userBuffer:Buffer):Promise<SearchAlbaResponseDto[]>=>{
+export const getFilteredAlba = async (params:SearchAlbaRequestDto,userBuffer?:Buffer):Promise<SearchAlbaResponseDto[]>=>{📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export const getFilteredAlba = async (params:SearchAlbaRequestDto,userBuffer:Buffer):Promise<SearchAlbaResponseDto[]>=>{ | |
| export const getFilteredAlba = async (params:SearchAlbaRequestDto,userBuffer?:Buffer):Promise<SearchAlbaResponseDto[]>=>{ |
🤖 Prompt for AI Agents
In `@src/service/search_alba_service.ts` at line 10, The getFilteredAlba function
signature requires a Buffer but should accept unauthenticated calls: change the
parameter in getFilteredAlba(params: SearchAlbaRequestDto, userBuffer?: Buffer):
Promise<SearchAlbaResponseDto[]> and update all call sites to pass undefined
when no user is present; also remove or make the controller's `@Security`('jwt')
decorator optional so unauthenticated requests reach the controller, and ensure
any logic inside getFilteredAlba that assumes userBuffer is present guards for
undefined (e.g., only apply filtering when userBuffer is truthy).
name: PR 템플릿
about: PR 생성 시 이 템플릿을 사용해 주세요.
title: "[PR] "
#️⃣ 연관된 이슈
#️⃣ 작업 내용
쿼리 파라미터의 storeId를 통해 특정 가게의 모든 리뷰를 불러올 수 있음. 리뷰 생성시에 가져오는 응답과 형식 자체는 같고 리스트를 가져오는 것만 변했음
기존 아르바이트 검색 기능은 유저 인증을 하지 않고 지원된 아르바이트라도 날짜 조건만 맞다면 모두 가져왔음.
개선된 기능은 만약 유저가 로그인을 한 상태라면 이미 지원했던 아르바이트는 검색 시 나타나지 않게 하였음. 이를 통해 불필요한 공고를 지워냄으로서 UI 개선
#️⃣ 테스트 결과
swagger UI 및 로컬 테스트 성공
#️⃣ 변경 사항 체크리스트
#️⃣ 스크린샷 (선택)
#️⃣ 리뷰 요구사항 (선택)
📎 참고 자료 (선택)
Summary by CodeRabbit
Release Notes