Conversation
Walkthrough가게 태그 조회 기능을 추가하고, 쿼리 키/옵션을 확장했다. StoreCheers 컴포넌트는 테마/태그 중심으로 UI를 재구성했고, 관련 CSS와 유틸을 신설했다. StoreInfo는 상세+태그를 동시 조회하고 태그 표시 및 등록 버튼을 추가했다. 타입에 tags 필드와 응답 타입을 도입했다. Changes
Sequence Diagram(s)sequenceDiagram
actor UI as StoreInfo UI
participant Q as React Query (useSuspenseQueries)
participant API as shop.api
participant S as Server
UI->>Q: trigger storeDetailQuery + storeTagsQuery
Q->>API: getStoreDetail(storeId)
Q->>API: getStoreTags(storeId)
API->>S: GET /api/shops/{id}
API->>S: GET /api/shops/{id}/tags
S-->>API: store detail JSON
S-->>API: { tags: string[] }
API-->>Q: parsed results
Q-->>UI: {detail, tags}
UI->>UI: render title/intro + Tag(1st) + "+N" if any
sequenceDiagram
actor UI as StoreCheers List
participant Q as React Query Cache
participant Data as StoreCheers Data
participant Utils as Theme Utils
UI->>Q: read cheers (includes tags[])
Q-->>UI: cheers[]
loop per cheer
UI->>Utils: getHeaderBackgroundColor(theme)
Utils-->>UI: hex color
UI->>Utils: getContentBackgroundColor(theme)
Utils-->>UI: hex color
UI->>UI: render Tag icons (+N), expandable text
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
🎨 Storybook Preview: https://685a32a1c0bbd269fdb67af4-slgnlfcwou.chromatic.com/ |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (14)
src/app/(store)/stores/[storeId]/_components/StoreCheers/types.ts (1)
1-1: 테마 상수 추출 및 타입 파생 제안
Theme유니온 타입과["yellow", "pink", "blue"]배열이 아래 세 곳에서 중복 정의되어 있어 변경 시 누락·불일치 위험이 있습니다. 단일 소스(상수 배열)에서 관리하고, 타입과 사용 로직을 모두 파생하도록 리팩터링을 제안드립니다.· 중복 위치
- src/app/(store)/stores/[storeId]/_components/StoreCheers/types.ts
- src/app/(store)/stores/[storeId]/_components/StoreCheers/StoreCheers.tsx
- src/app/(store)/stores/[storeId]/_components/StoreCheers/utils.ts
· 리팩터링 방안
- types.ts에
THEMES상수를 정의 및 내보내기Theme타입을typeof THEMES[number]로 파생- StoreCheers.tsx와 utils.ts에서 직접 하드코딩된 배열/유니온 대신
THEMES와Theme을 import--- a/src/app/(store)/stores/[storeId]/_components/StoreCheers/types.ts +++ b/src/app/(store)/stores/[storeId]/_components/StoreCheers/types.ts -export type Theme = "yellow" | "pink" | "blue"; +export const THEMES = ["yellow", "pink", "blue"] as const; +export type Theme = typeof THEMES[number];--- a/src/app/(store)/stores/[storeId]/_components/StoreCheers/StoreCheers.tsx - const THEMES = ["yellow", "pink", "blue"] as const; - type Theme = (typeof THEMES)[number]; + import { THEMES, Theme } from "./types";--- a/src/app/(store)/stores/[storeId]/_components/StoreCheers/utils.ts - export const getCheerCardTheme = (memberId: number) => { - return ["yellow", "pink", "blue"][memberId % 3]; - }; + export const getCheerCardTheme = (memberId: number) => + THEMES[memberId % THEMES.length]; - export const getHeaderBackgroundColor = (colorName: Theme) => { + import { THEMES, Theme } from "./types"; + export const getHeaderBackgroundColor = (colorName: Theme) => { switch (colorName) { case "yellow": return "#fceb9c"; /* … */이렇게 하면 색상 목록이 변경돼도 한 군데만 수정하면 되므로 중복·드리프트를 방지할 수 있습니다.
src/app/(store)/_api/shop/shop.types.ts (2)
18-19: StoreCheers에 tags 추가는 합리적 — 런타임 안전성(빈 배열 보장)만 확인해 주세요타입상
string[]로 강제하면 사용 측에서 map/filter 시 깔끔합니다. 다만 BE가 해당 필드를 누락하거나 null을 반환하는 경우가 있는지 확인이 필요합니다. 그런 케이스가 가능하다면 API 계층에서tags ?? []로 normalize 하거나 타입을tags?: string[]로 완화 후 UI에서 기본값을 보장하는 방식을 권장합니다. 또한 불변 의도를 드러내려면readonly string[]도 고려해 볼 수 있습니다.불변 표현을 원하신다면 다음과 같이 변경 가능합니다:
- tags: string[]; + readonly tags: string[];가능하다면
getStoreCheers응답 파싱 시 다음과 같이 기본값을 보장해 주세요(참고용, 파일 외 코드):// ex) api 계층에서 json.cheers = json.cheers.map(c => ({ ...c, tags: c.tags ?? [] }));
39-41: StoreTagsResponse 정의 적절 — 향후 확장성을 위해 주석/스키마 명시 고려
{ tags: string[] }심플하고 명확합니다. 추후 태그의 정렬 기준, 총 개수(total) 등이 필요하면 API/타입 확장을 쉽게 할 수 있도록 JSDoc으로 스키마 의도를 남겨 두면 좋겠습니다.src/app/(store)/stores/[storeId]/_components/StoreCheers/StoreCheers.css.ts (3)
16-21: 헤더 레이아웃 OK — 텍스트/아이콘 대비는 테마 색상에서만 확인Flex 정렬/패딩 구성 무난합니다. 테마 배경 위에서의 텍스트/아이콘 대비는 실제 테마 색상 적용 시 한 번만 점검해 주세요.
33-44: 긴 단어 줄바꿈 방지 보완 제안
-webkit-line-clamp처리 외에 긴 토큰(해시/URL/연속문자)로 인한 오버플로우를 막기 위해overflowWrap/wordBreak를 추가해 두면 안전합니다.export const cheerCardContentText = style({ width: "100%", + overflowWrap: "anywhere", + wordBreak: "break-word", selectors: { "&[data-long-text='true'][data-expanded='false']": { display: "-webkit-box", WebkitLineClamp: 4, WebkitBoxOrient: "vertical", overflow: "hidden", textOverflow: "ellipsis", }, }, });
46-54: 색상 하드코딩(#fff, #f0f0f0) 대신 디자인 토큰/변수 사용 권장다크 모드/테마 확장 시 유지보수가 어려워질 수 있습니다. 가능하다면
@/styles의 색상 토큰 또는 CSS 변수(semantic tokens)로 치환해 주세요.src/app/(store)/_api/shop/shop.queries.ts (1)
49-53: 태그 쿼리 옵션 기본형 완성 — 캐시 전략 옵션(선택)을 고려해보세요태그 변동 빈도가 낮다면 staleTime/gcTime을 부여해 네트워크 호출을 줄일 수 있습니다. 또한 방어적으로
enabled: storeId > 0도 고려 가능합니다.export const storeTagsQueryOptions = (storeId: number) => queryOptions({ queryKey: storeQueryKeys.tags(storeId), queryFn: () => getStoreTags(storeId), + // 선택: 태그 변동이 잦지 않다면 캐시 기간을 늘려 네트워크 절약 + staleTime: 5 * 60 * 1000, // 5분 + gcTime: 10 * 60 * 1000, // 10분 + // 선택: 잘못된 storeId(0 등) 방지 + enabled: storeId > 0, });src/app/(store)/stores/[storeId]/_components/StoreCheers/utils.ts (1)
8-19: switch 문을 매핑 상수로 단순화하고 팔레트 단일 소스화 고려동일한 패턴의 switch 문 두 개를 상수 매핑으로 단순화하면 가독성과 변경 용이성이 좋아집니다. 더 나아가 디자인 토큰(예: CSS 변수, theme 객체)을 사용해 팔레트를 단일 소스에서 관리하는 것도 권장합니다.
-export const getHeaderBackgroundColor = (colorName: Theme) => { - switch (colorName) { - case "yellow": - return "#fceb9c"; - case "pink": - return "#fabdb8"; - case "blue": - return "#b2dfff"; - default: - return "#fceb9c"; - } -}; - -export const getContentBackgroundColor = (colorName: Theme) => { - switch (colorName) { - case "yellow": - return "#fef8dd"; - case "pink": - return "#fde5e3"; - case "blue": - return "#e0f2ff"; - default: - return "#fef8dd"; - } -}; +const HEADER_BG: Record<Theme, string> = { + yellow: "#fceb9c", + pink: "#fabdb8", + blue: "#b2dfff", +}; +const CONTENT_BG: Record<Theme, string> = { + yellow: "#fef8dd", + pink: "#fde5e3", + blue: "#e0f2ff", +}; + +export const getHeaderBackgroundColor = (colorName: Theme) => + HEADER_BG[colorName]; + +export const getContentBackgroundColor = (colorName: Theme) => + CONTENT_BG[colorName];Also applies to: 21-32
src/app/(store)/stores/[storeId]/_components/StoreInfo/StoreInfo.tsx (3)
53-56: 중복 key 제거 및 안정적인 key 사용동일한 index 기반 key가 래퍼 div와 Image 둘 다에 지정되어 있습니다. 내부 Image의 key는 불필요하며, 가능하면 URL 자체를 key로 사용하는 것이 더 안정적입니다.
- {imageUrls.map((image, index) => ( - <div key={index} className={styles.storeInfoImageWrapper}> - <Image - key={index} + {imageUrls.map((image, index) => ( + <div key={image || index} className={styles.storeInfoImageWrapper}> + <Image className={styles.storeInfoImage} src={image} fill
71-80: useSuspenseQueries 결과 튜플 타입 보존
queries배열에as const를 붙여 결과 배열의 튜플 타입을 보존하면 구조 분해 시 타입 안정성이 향상됩니다.- ] = useSuspenseQueries({ - queries: [storeDetailQueryOptions(storeId), storeTagsQueryOptions(storeId)], - }); + ] = useSuspenseQueries({ + queries: [storeDetailQueryOptions(storeId), storeTagsQueryOptions(storeId)] as const, + });
82-85: 표시 태그 개수 계산 로직 보정: 존재하지 않는 태그까지 카운트하는 문제
+{tags.length - 1}는 ALL_TAGS에 존재하지 않는 태그명까지 포함해 개수를 표시할 수 있습니다. 매칭된 태그 기준으로 개수를 계산하세요.- const firstTag = head(ALL_TAGS.filter(tag => tags.includes(tag.name))); - - const showAdditionalTags = tags.length > 1; + const matchedTags = ALL_TAGS.filter(tag => tags.includes(tag.name)); + const firstTag = head(matchedTags); + const additionalTagsCount = Math.max(0, matchedTags.length - (firstTag ? 1 : 0)); + const showAdditionalTags = additionalTagsCount > 0;- <Tag variant='primaryLow'> - <Text as='span' typo='label1Sb' color='transparent'> - +{tags.length - 1} - </Text> - </Tag> + <Tag variant='primaryLow'> + <Text as='span' typo='label1Sb' color='transparent'> + +{additionalTagsCount} + </Text> + </Tag>Also applies to: 136-141
src/app/(store)/stores/[storeId]/_components/StoreCheers/StoreCheers.tsx (3)
5-6: 불필요한 라이브러리 유틸 의존도 축소: native slice 사용간단한 슬라이싱에
es-toolkit/compat의slice는 과합니다. 네이티브 Array.prototype.slice로 대체하여 번들 크기와 의존성을 줄여주세요.-import { slice } from "es-toolkit/compat"; @@ - const visibleTags = isExpanded ? selectedTags : slice(selectedTags, 0, 2); + const visibleTags = isExpanded ? selectedTags : selectedTags.slice(0, 2);Also applies to: 115-116
42-44: Theme/THEMES 중복 정의 제거로 단일 소스화 권장
Theme타입이./types에 존재하고, 유사한 상수/로직이utils.ts에도 있습니다. 현재 파일의THEMES/로컬Theme정의는 중복이며 드리프트 위험이 있습니다. 공통 상수/타입을 한 곳(예:types.ts또는utils.ts)에서 export해 사용하도록 리팩터링을 제안합니다. 또한 카드 테마 산정은getCheerCardTheme로 통일하면 좋습니다.-const THEMES = ["yellow", "pink", "blue"] as const; -type Theme = (typeof THEMES)[number]; +// 권장: ./types 에서 Theme, ./utils 에서 getCheerCardTheme 재사용 +// import { type Theme } from "./types"; +// import { getCheerCardTheme } from "./utils";또한 아래 매핑에서도 동일 상수를 직접 접근하기보다는 유틸을 호출하도록 변경을 권장합니다.
- theme={THEMES[index % THEMES.length] as Theme} + theme={getCheerCardTheme(index)}
137-141: Avatar memberId 계산 단순화
findIndex대신indexOf가 의도를 더 명확히 드러냅니다. (향후 THEMES를 외부 상수로 단일화 시 함께 적용)- <Avatar - // TODO: 추후 theme 지정 가능하게끔 수정 - memberId={THEMES.findIndex(t => t === theme)} - className={styles.cheerCardAvatar} - /> + <Avatar + // TODO: 추후 theme 지정 가능하게끔 수정 + memberId={THEMES.indexOf(theme)} + className={styles.cheerCardAvatar} + />
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (8)
src/app/(store)/_api/shop/shop.api.ts(2 hunks)src/app/(store)/_api/shop/shop.queries.ts(3 hunks)src/app/(store)/_api/shop/shop.types.ts(2 hunks)src/app/(store)/stores/[storeId]/_components/StoreCheers/StoreCheers.css.ts(2 hunks)src/app/(store)/stores/[storeId]/_components/StoreCheers/StoreCheers.tsx(5 hunks)src/app/(store)/stores/[storeId]/_components/StoreCheers/types.ts(1 hunks)src/app/(store)/stores/[storeId]/_components/StoreCheers/utils.ts(1 hunks)src/app/(store)/stores/[storeId]/_components/StoreInfo/StoreInfo.tsx(3 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/nextjs-folder-structure.mdc)
All files must use TypeScript.
Files:
src/app/(store)/stores/[storeId]/_components/StoreCheers/utils.tssrc/app/(store)/_api/shop/shop.types.tssrc/app/(store)/stores/[storeId]/_components/StoreCheers/types.tssrc/app/(store)/stores/[storeId]/_components/StoreCheers/StoreCheers.tsxsrc/app/(store)/stores/[storeId]/_components/StoreInfo/StoreInfo.tsxsrc/app/(store)/_api/shop/shop.api.tssrc/app/(store)/_api/shop/shop.queries.tssrc/app/(store)/stores/[storeId]/_components/StoreCheers/StoreCheers.css.ts
{src/app/**/_components/**/*.tsx,src/components/**/*.tsx}
📄 CodeRabbit inference engine (.cursor/rules/nextjs-folder-structure.mdc)
Component files must use PascalCase naming (e.g.,
Button.tsx,DomainLayout.tsx).
Files:
src/app/(store)/stores/[storeId]/_components/StoreCheers/StoreCheers.tsxsrc/app/(store)/stores/[storeId]/_components/StoreInfo/StoreInfo.tsx
{src/styles/**/*.css.ts,src/app/**/_components/**/*.css.ts,src/components/**/*.css.ts}
📄 CodeRabbit inference engine (.cursor/rules/nextjs-folder-structure.mdc)
Style files must use camelCase naming with the
.css.tsextension (e.g.,Button.css.ts,theme.css.ts).
Files:
src/app/(store)/stores/[storeId]/_components/StoreCheers/StoreCheers.css.ts
🧬 Code graph analysis (5)
src/app/(store)/stores/[storeId]/_components/StoreCheers/utils.ts (1)
src/app/(store)/stores/[storeId]/_components/StoreCheers/types.ts (1)
Theme(1-1)
src/app/(store)/stores/[storeId]/_components/StoreCheers/StoreCheers.tsx (4)
src/app/(store)/_api/shop/shop.queries.ts (1)
storeCheersQueryOptions(27-31)src/constants/tag.constants.ts (1)
ALL_TAGS(84-84)src/app/(store)/stores/[storeId]/_components/StoreCheers/StoreCheers.css.ts (1)
tag(46-54)src/app/(store)/stores/[storeId]/_components/StoreCheers/utils.ts (2)
getHeaderBackgroundColor(8-19)getContentBackgroundColor(21-32)
src/app/(store)/stores/[storeId]/_components/StoreInfo/StoreInfo.tsx (2)
src/app/(store)/_api/shop/shop.queries.ts (2)
storeDetailQueryOptions(21-25)storeTagsQueryOptions(49-53)src/constants/tag.constants.ts (1)
ALL_TAGS(84-84)
src/app/(store)/_api/shop/shop.api.ts (2)
src/app/(store)/_api/shop/shop.types.ts (1)
StoreTagsResponse(39-41)src/lib/api/client.ts (1)
http(69-74)
src/app/(store)/_api/shop/shop.queries.ts (1)
src/app/(store)/_api/shop/shop.api.ts (1)
getStoreTags(79-83)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: test
🔇 Additional comments (12)
src/app/(store)/stores/[storeId]/_components/StoreCheers/StoreCheers.css.ts (5)
10-14: 카드 컨테이너 스타일 구성 합리적입니다풀폭, radius, overflow 설정이 컴포넌트 구조와 맞습니다.
23-26: 아바타 사이징 무난합니다일관된 정사각 크기 지정으로 레이아웃이 안정적입니다.
28-31: 컨텐츠 패딩 조정 좋습니다내부 여백이 카드 구조와 잘 어울립니다.
56-58: 아이콘 폰트 사이즈 지정 문제 없습니다필요 시 상수화(토큰화)만 고려해 주세요.
3-3: radius[160] 토큰 정의 확인 완료
src/styles/radius.css.ts에서160: "16px"항목이 명시되어 있어radius[160]사용 시 타입 안전성이나 런타임 에러 우려 없습니다.src/app/(store)/_api/shop/shop.api.ts (2)
8-9: 타입 임포트 추가 적절응답 타입과 구현이 정합합니다.
74-83: 가게 태그 API 추가 — JSDoc @param 표기 및 인증 여부 확인 요청간결하게 잘 구현해 주셨습니다. 아래 두 가지만 확인 부탁드립니다:
- JSDoc 표기:
@params→ 표준@param으로 수정- 현재
getStoreTags는http.get(비인증) 사용 중입니다. 해당 엔드포인트가 인증 API라면authHttp.get으로 변경해 주세요. 백엔드 API 명세를 참고해 인증 요구 여부를 확인 부탁드립니다.수정 예시:
/** * 가게별 태그 조회 API - * @params storeId 조회할 가게 ID + * @param storeId 조회할 가게 ID * @returns 가게별 태그 정보 */ export const getStoreTags = async ( storeId: number ): Promise<StoreTagsResponse> => { - return await http.get(`api/shops/${storeId}/tags`).json<StoreTagsResponse>(); + return await http.get(`api/shops/${storeId}/tags`).json<StoreTagsResponse>(); };src/app/(store)/_api/shop/shop.queries.ts (2)
8-9: getStoreTags 임포트 OK쿼리 옵션에서 올바르게 사용되고 있습니다.
18-18: 쿼리 키(tags) 구성 적절
["store", storeId, "tags"]형태로 다른 키들과 일관적입니다.src/app/(store)/stores/[storeId]/_components/StoreInfo/StoreInfo.tsx (2)
151-159: CTA 컴포넌트 추가 LGTM등록 버튼 컴포넌트를 분리해 재사용성과 가독성이 좋아졌습니다. 라우팅 쿼리도 명확합니다.
125-131: Next/Image 원격 아이콘 도메인 허용 설정 검토
next.config.ts의images.remotePatterns에 이미 다음 S3 호스트들이 등록되어 있습니다.
- eatda-storage-dev.s3.ap-northeast-2.amazonaws.com
- eatda-storage-prod.s3.ap-northeast-2.amazonaws.com
src/app/(store)/stores/[storeId]/_components/StoreInfo/StoreInfo.tsx(125–131라인)에서firstTag.iconUrl을 사용하고 있으므로, 해당 URL이 위에 등록된 도메인을 사용하고 있는지 확인해 주세요.
만약 다른 외부 도메인의 URL이 전달될 가능성이 있다면,next.config.ts의images.remotePatterns또는images.domains에 누락된 호스트를 추가해야 런타임 이미지 차단을 방지할 수 있습니다.src/app/(store)/stores/[storeId]/_components/StoreCheers/StoreCheers.tsx (1)
22-23: motion/react vs framer-motion 패키지 호환성 및 whileTap 지원 여부 확인 필요검토 결과
- package.json에
"motion": "^12.23.11"의존성이 선언되어 있고,src/app/(store)/stores/[storeId]/_components/StoreCheers/StoreCheers.tsx에서import { motion } from "motion/react";을 사용 중입니다.따라서
- Motion One(
motion/react) React 패키지가<motion.div>등의 컴포넌트에서whileTap프로퍼티를 지원하는지 공식 문서를 통해 반드시 확인해주세요.- 만약
whileTap사용이 필요하고 Motion One에서 지원되지 않는다면,framer-motion패키지로 교체가 필요합니다.
- 이 경우
npm install framer-motion또는yarn add framer-motion후, import를from "framer-motion"으로 변경- 변경 후 런타임 경고 없이 정상 동작하는지 테스트 바랍니다.
| <TextButton | ||
| size='small' | ||
| variant='assistive' | ||
| onClick={() => setIsExpanded(!isExpanded)} | ||
| > | ||
| {isExpanded ? "접기" : "더보기"} | ||
| </TextButton> | ||
| )} |
There was a problem hiding this comment.
카드 전체 onClick과 “더보기/접기” 버튼 onClick이 중복 호출되어 토글이 무력화됨
부모 motion.div와 자식 TextButton이 모두 토글을 수행하여 버튼 클릭 시 두 번 토글(원상복귀)됩니다. 자식 클릭에서 이벤트 전파를 중단하고, 키보드 접근성도 함께 보강해주세요.
- <motion.div
+ <motion.div
className={styles.cheerCard}
- onClick={() => setIsExpanded(!isExpanded)}
+ onClick={() => setIsExpanded(prev => !prev)}
role='button'
tabIndex={0}
+ aria-expanded={isExpanded}
+ onKeyDown={e => {
+ if (e.key === "Enter" || e.key === " ") {
+ e.preventDefault();
+ setIsExpanded(prev => !prev);
+ }
+ }}
transition={{ duration: 0.3 }}
whileTap={{ scale: 0.99 }}
>
@@
- <TextButton
+ <TextButton
size='small'
variant='assistive'
- onClick={() => setIsExpanded(!isExpanded)}
+ onClick={e => {
+ e.stopPropagation();
+ setIsExpanded(prev => !prev);
+ }}
>Also applies to: 123-131
🤖 Prompt for AI Agents
In src/app/(store)/stores/[storeId]/_components/StoreCheers/StoreCheers.tsx
around lines 166-173 (and also apply the same fix at 123-131), the card wrapper
and the TextButton both toggle expansion so clicking the button triggers both
and cancels the toggle; modify the TextButton handlers to accept the event and
call event.stopPropagation() in onClick, and add an onKeyDown handler that
prevents default and stops propagation while toggling on Enter/Space so keyboard
users work correctly; ensure the toggle logic is invoked only once from the
button handler and keep the parent card click untouched.
| export const getCheerCardTheme = (memberId: number) => { | ||
| return ["yellow", "pink", "blue"][memberId % 3]; | ||
| }; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Theme 반환값이 undefined가 될 수 있는 여지 제거 + 명시적 반환 타입 지정
memberId % 3가 음수이거나 정수가 아닐 경우 인덱싱 결과가 undefined가 될 수 있습니다. 또한 반환 타입이 암묵적이라 호출부 타입 안정성이 떨어집니다. 안전한 인덱스 계산과 Theme 반환 타입을 명시해 주세요.
-export const getCheerCardTheme = (memberId: number) => {
- return ["yellow", "pink", "blue"][memberId % 3];
-};
+export const getCheerCardTheme = (memberId: number): Theme => {
+ const themes: Theme[] = ["yellow", "pink", "blue"];
+ const safeIndex = Math.abs(Math.trunc(memberId)) % themes.length;
+ return themes[safeIndex];
+};📝 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 getCheerCardTheme = (memberId: number) => { | |
| return ["yellow", "pink", "blue"][memberId % 3]; | |
| }; | |
| export const getCheerCardTheme = (memberId: number): Theme => { | |
| const themes: Theme[] = ["yellow", "pink", "blue"]; | |
| const safeIndex = Math.abs(Math.trunc(memberId)) % themes.length; | |
| return themes[safeIndex]; | |
| }; |
🤖 Prompt for AI Agents
In src/app/(store)/stores/[storeId]/_components/StoreCheers/utils.ts around
lines 3 to 5, the function can return undefined for non-integer or negative
memberId and lacks an explicit return type; ensure you validate and normalize
memberId to a safe 0..2 index (e.g. coerce to a finite integer with Math.floor,
then compute ((index % 3) + 3) % 3 to handle negatives), use that index to pick
from the themes array, add an explicit return type Theme (import or declare the
Theme union if needed), and provide a fallback theme as a last-resort default so
the function never returns undefined.
💡 Verification agent
❓ Verification inconclusive
유틸이 실제로 사용되는지 확인 필요
이번 PR에서 getCheerCardTheme가 사용되지 않는 것으로 보입니다. 미사용 유틸은 제거하거나 실제 사용처(예: StoreCheers.tsx)로 연결해 주세요.
다음 스크립트로 사용 여부를 확인할 수 있습니다.
🏁 Script executed:
#!/bin/bash
# getCheerCardTheme 사용처 검색
rg -nP '\bgetCheerCardTheme\s*\(' -S --type ts --type tsx -C2Length of output: 91
getCheerCardTheme의 실제 사용처를 glob 옵션으로 다시 확인해 보겠습니다.
다음 스크립트를 실행해 주세요.
🏁 Script executed:
#!/bin/bash
# getCheerCardTheme 사용처 검색 (glob)
rg -nP '\bgetCheerCardTheme\s*\(' -n --glob '*.ts' --glob '*.tsx' -C2Length of output: 71
getCheerCardTheme 사용처 없음 – 제거 또는 연결 필요
현재 src/app/(store)/stores/[storeId]/_components/StoreCheers/utils.ts에서 정의된 getCheerCardTheme 함수가 코드베이스 내 어디에서도 호출되지 않습니다.
불필요한 dead code 방지를 위해 아래 중 하나를 선택해 주세요:
- 유틸이 더 이상 필요하지 않다면 해당 함수 및 임포트를 삭제
- 실제로 사용하려는 컴포넌트(예:
StoreCheers.tsx)에서 호출하도록 연결
대상 위치
• src/app/(store)/stores/[storeId]/_components/StoreCheers/utils.ts:3-5 (export const getCheerCardTheme …)
🤖 Prompt for AI Agents
In src/app/(store)/stores/[storeId]/_components/StoreCheers/utils.ts lines 3-5,
the exported getCheerCardTheme function is not used; either remove it and any
related imports/exports to eliminate dead code, or wire it into the StoreCheers
component (e.g., import getCheerCardTheme in
src/app/(store)/stores/[storeId]/_components/StoreCheers/StoreCheers.tsx and
call getCheerCardTheme(memberId) to compute the card theme and apply it to the
card’s className/props where member cards are rendered). Ensure after the change
there are no unused exports or linter errors.
wkdtnqls0506
left a comment
There was a problem hiding this comment.
너무너무 숙오하셨읍니다~!
2차 스프린트 마무리가 눈 앞에..✨✨✨
| export const tag = style({ | ||
| display: "flex", | ||
| alignItems: "center", | ||
| gap: "0.4rem", | ||
| padding: "0.6rem 0.8rem", | ||
| backgroundColor: "#ffffff", | ||
| borderRadius: "999px", | ||
| border: "1px solid #f0f0f0", | ||
| }); |
There was a problem hiding this comment.
P3:
옹! 뭔가 사용안하는 스타일인거 같아서 제거해도 될 거 같은 느낌!
| <VStack gap={12}> | ||
| <HStack align='center' gap={8}> | ||
| <Avatar memberId={memberId} className={styles.cheerCardProfileImage} /> | ||
| <motion.div |
There was a problem hiding this comment.
오 배경 색상 지정하는 함수 꽤나 쓰이는거 같은데, 이것도 분리해서 함께 사용하면 좋을 거 같네유 ~ . ~
✅ 이슈 번호
close x
🪄 작업 내용 (변경 사항)
📸 스크린샷
💡 설명
🗣️ 리뷰어에게 전달 사항
📍 트러블 슈팅
Summary by CodeRabbit