Conversation
Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…s 1 directory (#1233) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- sonarcloud issue 반영
…irectory (#1247) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
|
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 ignored due to path filters (9)
📒 Files selected for processing (1)
살펴보기KOIN 웹 프로젝트에 SonarCloud 기반 자동화 파이프라인, 콜반 사용 제한 모달 기능, 캠퍼스 정보 동적 아이콘 시스템을 추가하고 여러 코드 구조를 개선했습니다. 변경사항KOIN 자동화 파이프라인
콜반 사용 제한 모달 기능
캠퍼스 정보 동적 아이콘 시스템
마이너 리팩토링
예상 코드 리뷰 노력🎯 4 (복잡함) | ⏱️ ~75분 관련 가능 PR
제안 라벨
제안 리뷰어
🚥 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)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. 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: 15
🧹 Nitpick comments (3)
src/components/CampusInfo/index.tsx (3)
36-39: 💤 Low valueProps 타입을
type대신interface로 선언해주세요.코딩 가이드라인에 따라 컴포넌트 Props 타입 정의에는
interface사용이 권장됩니다.♻️ 제안 수정
-type ShopIconProps = { +interface ShopIconProps { readonly iconUrl: string | null | undefined; readonly name: string; -}; +}As per coding guidelines,
src/components/**에서는 "Props 타입이 명확하게 정의되어 있는지(interface 사용 권장)"를 확인하도록 되어 있습니다.🤖 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/components/CampusInfo/index.tsx` around lines 36 - 39, Replace the ShopIconProps union type declaration with an interface declaration (keep the same members and readonly modifiers) so the component uses an interface for its props; update the declaration named ShopIconProps in this file to use "interface ShopIconProps { readonly iconUrl: string | null | undefined; readonly name: string; }" and ensure any imports/uses of ShopIconProps remain unchanged.
44-52: ⚡ Quick win
<img>로드 실패 시 텍스트 폴백이 동작하지 않습니다.
iconUrl이 유효한 문자열이지만 실제 URL이 깨져있거나 404를 반환하는 경우,onError핸들러가 없어 부서진 이미지 아이콘이 그대로 노출됩니다.useState로 오류 상태를 관리하거나onError에서src를 제거하는 방식으로 텍스트 폴백과 동일하게 동작하도록 처리해주세요.🛡️ 제안 수정
+import { useState } from 'react'; + type ShopIconProps = { readonly iconUrl: string | null | undefined; readonly name: string; }; function ShopIcon({ iconUrl, name }: ShopIconProps) { + const [imgError, setImgError] = useState(false); + return ( <div className={styles['icon-wrapper']}> - {iconUrl ? ( + {iconUrl && !imgError ? ( // eslint-disable-next-line `@next/next/no-img-element` - <img className={styles['icon-image']} src={iconUrl} alt={name} decoding="async" /> + <img + className={styles['icon-image']} + src={iconUrl} + alt={name} + decoding="async" + onError={() => setImgError(true)} + /> ) : ( <span className={styles['icon-fallback']} aria-hidden="true"> {name.slice(0, 1)} </span> )} </div> ); }🤖 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/components/CampusInfo/index.tsx` around lines 44 - 52, The img element using iconUrl doesn't handle broken image loads, so add an error fallback: inside the CampusInfo component track a local boolean state (e.g., imageError) via useState and set it true in an onError handler on the <img> (or alternatively clear src in onError), then change the conditional that currently checks iconUrl to also require !imageError (render the same <span className={styles['icon-fallback']}> with {name.slice(0,1)} when imageError is true); reference the existing iconUrl, name, styles['icon-image'] and styles['icon-fallback'] to locate where to add the state and onError.
124-156: ⚖️ Poor tradeoffAPI 응답의 시설물 수가 달라지면 레이아웃이 깨질 수 있습니다.
.slice(0, 2),.slice(2, 6),.slice(6)방식으로 3개 컬럼에 고정 분배하고 있어, 백엔드에서 시설물 수가 변경되면 특정 컬럼이 비거나 마지막 컬럼에 아이템이 몰리는 현상이 발생할 수 있습니다. 현재 데이터가 고정적이라면 문제없으나, 향후 유연성을 위해 동적 분배 로직 도입을 검토해주세요.🤖 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/components/CampusInfo/index.tsx` around lines 124 - 156, The current three-column rendering uses hardcoded slices (filteredCampusInfo.slice(0,2), slice(2,6), slice(6)) which breaks layout when the number of items changes; replace that with a small helper (e.g., a function named distributeIntoColumns or chunkIntoColumns) that takes filteredCampusInfo and numberOfColumns = 3 and returns an array of 3 arrays distributing items evenly (use round-robin or compute per-column counts with Math.ceil/Math.floor) and then map over those three arrays in the JSX instead of the fixed slices; update the JSX that maps using ShopIcon and getFormattedShopTime to consume columns[columnIndex] rather than the old slice calls.
🤖 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 @.claude/CLAUDE.md:
- Around line 71-74: Add a blank line immediately before and after the Markdown
table block in .claude/CLAUDE.md to satisfy MD058 (blanks-around-tables); locate
the table starting with the header line "**변경 이력:**" and the pipe header row "|
날짜 | 변경 내용 | 대상 | 사유 |" and insert one empty line above "**변경 이력:**" (or between
preceding paragraph and the table) and one empty line after the table closing
row to remove the lint warning.
In @.claude/skills/feature-planning/SKILL.md:
- Around line 56-66: Update the markdown code block that shows the component
tree (the block starting with "src/components/[FeatureName]/") to include a
language specifier by changing the opening fence from ``` to ```text (and leave
the closing fence unchanged) so the tree block is recognized as text and
resolves the MD040 warning.
In @.claude/skills/github-pr-creation/SKILL.md:
- Around line 37-43: Update the fenced code block that contains the commit
message examples (the block starting with ``` and the lines "feat: [기능명] 추가"
etc.) to include a language tag (e.g., ```text or ```bash) so markdownlint MD040
is satisfied; edit the code block delimiter in
.claude/skills/github-pr-creation/SKILL.md to add the chosen language without
changing the block content.
In @.claude/skills/koin-pipeline/SKILL.md:
- Line 39: Multiple fenced code blocks in SKILL.md are declared with bare ```
which triggers markdownlint MD040; update each backtick fence to include an
appropriate language hint (e.g., ```bash, ```json, ```text) to remove the
warning. Search for the bare triple-backtick blocks in SKILL.md (the occurrences
around the examples referenced) and change them to the correct language for
their contents so each fenced block (those repeated occurrences) has a language
specifier.
- Line 20: 현재 문서의 백업 명령 `mv _workspace/ _workspace_prev/`는 `_workspace_prev/`가
존재하면 실패하거나 덮어쓸 위험이 있으니 `_workspace_prev/` 대신 고유한 이름을 사용하도록 수정하세요: 문서 내 해당 문장(참조
`_workspace/`, `_workspace_prev/`, `mv _workspace/ _workspace_prev/`)을 타임스탬프 또는
유니크 ID를 포함한 폴더명(예: `_workspace_prev_<timestamp>` 또는
`_workspace_YYYYMMDD_HHMMSS`)으로 변경하도록 안내하고, 기존 이름 충돌을 피하기 위해 덮어쓰기 대신 새 이름으로 mv
하도록 설명을 덧붙이세요.
In @.claude/skills/qa-review/SKILL.md:
- Around line 25-29: Replace the non-deterministic "yarn dlx tsc --noEmit" and
the non-working "./node_modules/.bin/tsc" approach with commands that use the
pinned TypeScript in this repo: invoke the package script or the installed tsc
via Yarn PnP (e.g. run "yarn tsc --noEmit" or "yarn run tsc --noEmit"), or reuse
the package.json build script that contains "tsc && next build"; update the
documentation in SKILL.md to show one of these deterministic commands instead of
"yarn dlx tsc" and remove the ./node_modules path suggestion so QA uses the
project's locked TypeScript (v5.9.3).
In @.claude/skills/sonarcloud-analysis/SKILL.md:
- Around line 14-16: Update the placeholder SONAR_PROJECT_KEY example so it
matches the actual SonarCloud project key used elsewhere (replace the current
"koin_web_recode" example with the real project key string used in your other
docs/configs); ensure the SONAR_PROJECT_KEY variable value is identical to the
project key referenced by your SonarCloud organization and any related scripts
that call the issues API so the API calls succeed.
In @.github/workflows/sonarcloud.yml:
- Line 26: The workflow uses the mutable ref
"SonarSource/sonarcloud-github-action@master" which is a supply-chain risk;
replace the mutable tag with an immutable release tag or commit SHA (e.g.,
change uses: SonarSource/sonarcloud-github-action@master to uses:
SonarSource/sonarcloud-github-action@vX.Y.Z or uses:
SonarSource/sonarcloud-github-action@<commit-sha>) so the action is pinned to a
fixed, auditable version.
- Around line 20-24: The workflow lacks a Corepack activation step, so the
runner may use an unpredictable Yarn version; add a step after the
actions/setup-node@v4 step to enable Corepack and prepare the pinned Yarn
version from package.json (e.g., run corepack enable && corepack prepare
yarn@4.10.3 --activate) before the "Install package" / yarn step so the CI uses
Yarn 4 consistently.
In `@src/api/callvan/mutations.ts`:
- Around line 21-22: The literal queryKey in invalidateCallvanNotifications
should use the central factory constant instead of a hardcoded array: add
notificationsRoot: ['callvan', 'notifications'] as const to callvanQueryKeys in
queries.ts, then update invalidateCallvanNotifications (the function taking
QueryClient) to call queryClient.invalidateQueries({ queryKey:
callvanQueryKeys.notificationsRoot }) so it matches the other invalidations and
centralizes the key definition.
In `@src/api/callvan/queries.ts`:
- Around line 15-16: The query keys notifications and restriction currently
embed the raw token; change the functions notifications and restriction to omit
the token from the returned queryKey (e.g., return ['callvan','notifications']
as const and ['callvan','restriction'] as const) and keep the token only as a
queryFn parameter so the token is consumed inside the request function (preserve
existing queryFn signatures to accept token). Also update any query option
helpers that call these keys (the option functions referenced near the other key
usages) so they call notifications()/restriction() without passing the token
into the key generation, and ensure they pass the token only into the queryFn
when invoking or constructing fetch callbacks.
In `@src/api/coopshop/entity.ts`:
- Line 27: The icon_url field is currently declared as required (icon_url:
string | null) but can be undefined in older backend responses or fixtures;
update the declaration to make icon_url optional (add ? to the icon_url
property) so the TypeScript type matches possible runtime values (string | null
| undefined) and adjust any consuming code if it relies on icon_url always being
present; locate the icon_url property in the entity/interface in
src/api/coopshop/entity.ts and change its signature to be optional.
In
`@src/components/Callvan/components/CallvanRestrictionModal/CallvanRestrictionModal.module.scss`:
- Around line 81-96: The button class &__button in
CallvanRestrictionModal.module.scss lacks a :focus-visible rule, so add a
:focus-visible selector for .&__button that displays a clear, high-contrast
focus indicator (e.g., a visible outline or ring using outline or box-shadow
that respects the existing border-radius and uses accessible color contrast),
ensure the focus style does not change layout (use outline-offset or box-shadow)
and include ::focus-visible alongside :focus fallback for broad browser support;
update the SCSS near the &__button block so keyboard users can perceive focus in
the CallvanRestrictionModal.
In `@src/components/Callvan/utils/callvanRestriction.ts`:
- Around line 18-22: The current isCallvanRestrictedError only checks
isKoinError and status === CALLVAN_RESTRICTED_ERROR_STATUS which can misclassify
generic 403s; update isCallvanRestrictedError to also verify the KoinError.code
against a Callvan-specific identifier (e.g., CALLVAN_RESTRICTION_CODE) so it
returns true only when both status and code match; reference the existing
isKoinError, CALLVAN_RESTRICTED_ERROR_STATUS, and (add/use)
CALLVAN_RESTRICTION_CODE constants, and keep
useCallvanRestrictionModal.openFromError’s existing double-checks but make the
utility function defensively validate both fields.
In `@src/pages/timetable/index.tsx`:
- Around line 49-57: The prefetch uses validatedFrameId without checking if it
exists in the timetableFrameList, which can cause
timetableQueries.lectureInfo(token, currentFrameId) to fail during SSR; update
the logic that sets currentFrameId so it only uses validatedFrameId when that ID
is present in timetableFrameList, otherwise fall back to mainFrame?.id (or
null), then build prefetchPromises accordingly before pushing
timetableQueries.lectureInfo(token, currentFrameId); ensure you reference
validatedFrameId, timetableFrameList, mainFrame, currentFrameId and
prefetchPromises when applying this guard.
---
Nitpick comments:
In `@src/components/CampusInfo/index.tsx`:
- Around line 36-39: Replace the ShopIconProps union type declaration with an
interface declaration (keep the same members and readonly modifiers) so the
component uses an interface for its props; update the declaration named
ShopIconProps in this file to use "interface ShopIconProps { readonly iconUrl:
string | null | undefined; readonly name: string; }" and ensure any imports/uses
of ShopIconProps remain unchanged.
- Around line 44-52: The img element using iconUrl doesn't handle broken image
loads, so add an error fallback: inside the CampusInfo component track a local
boolean state (e.g., imageError) via useState and set it true in an onError
handler on the <img> (or alternatively clear src in onError), then change the
conditional that currently checks iconUrl to also require !imageError (render
the same <span className={styles['icon-fallback']}> with {name.slice(0,1)} when
imageError is true); reference the existing iconUrl, name, styles['icon-image']
and styles['icon-fallback'] to locate where to add the state and onError.
- Around line 124-156: The current three-column rendering uses hardcoded slices
(filteredCampusInfo.slice(0,2), slice(2,6), slice(6)) which breaks layout when
the number of items changes; replace that with a small helper (e.g., a function
named distributeIntoColumns or chunkIntoColumns) that takes filteredCampusInfo
and numberOfColumns = 3 and returns an array of 3 arrays distributing items
evenly (use round-robin or compute per-column counts with Math.ceil/Math.floor)
and then map over those three arrays in the JSX instead of the fixed slices;
update the JSX that maps using ShopIcon and getFormattedShopTime to consume
columns[columnIndex] rather than the old slice calls.
🪄 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: e54a9543-ff25-4465-a55c-1f5dffffb7eb
⛔ Files ignored due to path filters (48)
.pnp.cjsis excluded by!.pnp.cjsand included by**.yarn/cache/@babel-runtime-npm-7.23.9-3b96e23cc2-9a520fe1bf.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/@babel-runtime-npm-7.29.2-b49cad1c67-f55ba4052a.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/@esbuild-darwin-arm64-npm-0.27.2-d675c4a521-10.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/@esbuild-linux-x64-npm-0.27.2-11f1a3d9db-10.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/@img-sharp-darwin-arm64-npm-0.34.3-8944698b4c-10.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/@img-sharp-libvips-darwin-arm64-npm-1.2.0-2d65006be7-10.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/@img-sharp-libvips-linux-x64-npm-1.2.0-91cf635ac8-10.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/@img-sharp-linux-x64-npm-0.34.3-aa297ca1ca-10.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/@next-swc-linux-x64-gnu-npm-15.5.15-39e2669d0f-10.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/@rollup-rollup-darwin-arm64-npm-4.59.0-db3495ba42-10.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/@rollup-rollup-linux-x64-gnu-npm-4.59.0-da6c703f69-10.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/@sentry-cli-darwin-npm-2.45.0-76059cfa9f-10.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/@sentry-cli-darwin-npm-2.58.5-1f667e3b9d-10.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/@sentry-cli-linux-x64-npm-2.45.0-6e5f26280b-10.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/@sentry-cli-linux-x64-npm-2.58.5-7cea7778bc-10.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/@trysound-sax-npm-0.2.0-9f763d0295-7379713eca.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/@unrs-resolver-binding-darwin-arm64-npm-1.11.1-c78d4bd2cb-10.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/@unrs-resolver-binding-linux-x64-gnu-npm-1.11.1-93a00570de-10.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/axios-npm-0.31.0-c3cc4d3e42-80536a915c.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/axios-npm-0.31.1-0574a0de7d-097dffdc0a.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/brace-expansion-npm-1.1.11-fb95eb05ad-faf34a7bb0.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/brace-expansion-npm-1.1.14-a997f4f4e7-2de747a589.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/braces-npm-3.0.2-782240b28a-966b1fb48d.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/braces-npm-3.0.3-582c14023c-fad11a0d46.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/fill-range-npm-7.0.1-b8b1817caa-e260f7592f.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/fill-range-npm-7.1.1-bf491486db-a7095cb39e.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/flatted-npm-3.2.9-0462256d3c-dc2b89e46a.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/flatted-npm-3.4.2-e32280259b-a9e78fe5c2.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/immutable-npm-4.3.5-5958499808-dbc1b8c808.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/immutable-npm-4.3.8-9db2d34fb7-27a134cec0.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/js-yaml-npm-3.14.1-b968c6095e-9e22d80b4d.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/js-yaml-npm-3.14.2-debd9d20c3-172e0b6007.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/lodash-es-npm-4.18.1-02cf41b912-8bfad225ef.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/lodash-npm-4.18.1-a64c3070ac-306fea53df.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/minimatch-npm-3.1.2-9405269906-e0b25b04cd.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/minimatch-npm-3.1.5-86958baf50-b11a7ee577.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/minimatch-npm-9.0.3-69d7d6fad5-c81b47d281.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/picomatch-npm-2.3.1-c782cfd986-60c2595003.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/picomatch-npm-2.3.2-4d85543a37-b788ef8148.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/postcss-npm-8.4.47-2f4d4be1fa-f2b50ba9b6.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/postcss-npm-8.5.14-1cf8d01c78-2e3f4dea69.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/regenerator-runtime-npm-0.14.1-a6c97c609a-5db3161abb.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/sax-npm-1.6.0-39dc3ef158-0909cedcd9.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/svgo-npm-3.3.2-69e1d32944-82fdea9b93.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/svgo-npm-3.3.3-f4851edd74-f3c1b4d05d.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**.yarn/cache/tar-npm-6.2.1-237800bb20-bfbfbb2861.zipis excluded by!**/.yarn/**,!**/*.zip,!.yarn/**and included by**yarn.lockis excluded by!**/yarn.lock,!**/*.lock,!yarn.lockand included by**
📒 Files selected for processing (45)
.claude/CLAUDE.md.claude/agents/code-quality-fixer.md.claude/agents/feature-implementer.md.claude/agents/feature-planner.md.claude/agents/pr-creator.md.claude/agents/qa-reviewer.md.claude/agents/sonarcloud-analyzer.md.claude/skills/code-quality-fix/SKILL.md.claude/skills/feature-implementation/SKILL.md.claude/skills/feature-planning/SKILL.md.claude/skills/github-pr-creation/SKILL.md.claude/skills/koin-pipeline/SKILL.md.claude/skills/qa-review/SKILL.md.claude/skills/sonarcloud-analysis/SKILL.md.claude/skills/sonarcloud-analysis/references/sonarcloud-setup.md.github/workflows/sonarcloud.yml.gitignorepackage.jsonsonar-project.propertiessrc/api/callvan/APIDetail.tssrc/api/callvan/entity.tssrc/api/callvan/index.tssrc/api/callvan/mutations.tssrc/api/callvan/queries.tssrc/api/coopshop/entity.tssrc/components/Callvan/components/AddPostForm/index.tsxsrc/components/Callvan/components/CallvanPageLayout/index.tsxsrc/components/Callvan/components/CallvanRestrictionModal/CallvanRestrictionModal.module.scsssrc/components/Callvan/components/CallvanRestrictionModal/index.tsxsrc/components/Callvan/hooks/useCallvanRestrictionModal.tsxsrc/components/Callvan/hooks/useCreateCallvan.tssrc/components/Callvan/hooks/useJoinCallvan.tssrc/components/Callvan/utils/callvanRestriction.tssrc/components/CampusInfo/CampusInfo.module.scsssrc/components/CampusInfo/index.tsxsrc/components/TimetablePage/components/MainTimetable/index.tsxsrc/components/TimetablePage/components/TimetableGridPlaceholder/index.tsxsrc/components/cafeteria/MobileCafeteriaPage/MobileCafeteriaPage.module.scsssrc/components/cafeteria/MobileCafeteriaPage/components/MobileDiningBlocks/MobileDiningBlocks.module.scsssrc/pages/callvan/index.tsxsrc/pages/callvan/notifications/index.tsxsrc/pages/campusinfo/index.tsxsrc/pages/room/[id]/index.tsxsrc/pages/timetable/index.tsxsrc/utils/ts/withCacheControl.ts
| **변경 이력:** | ||
| | 날짜 | 변경 내용 | 대상 | 사유 | | ||
| |------|----------|------|------| | ||
| | 2026-04-21 | 초기 구성 | 전체 | SonarCloud 품질 관리 + 기능 구현 자동화 파이프라인 | |
There was a problem hiding this comment.
표 앞뒤 공백 줄을 추가해 Markdown lint 경고를 제거하세요.
현재 MD058(blanks-around-tables) 경고가 발생합니다.
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 72-72: Tables should be surrounded by blank lines
(MD058, blanks-around-tables)
🤖 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 @.claude/CLAUDE.md around lines 71 - 74, Add a blank line immediately before
and after the Markdown table block in .claude/CLAUDE.md to satisfy MD058
(blanks-around-tables); locate the table starting with the header line "**변경
이력:**" and the pipe header row "| 날짜 | 변경 내용 | 대상 | 사유 |" and insert one empty
line above "**변경 이력:**" (or between preceding paragraph and the table) and one
empty line after the table closing row to remove the lint warning.
| ``` | ||
| src/components/[FeatureName]/ | ||
| ├── index.tsx # 메인 컴포넌트 | ||
| ├── [FeatureName].module.scss # 스타일 | ||
| ├── hooks/ | ||
| │ └── use[FeatureName].ts # 데이터 페칭 훅 | ||
| ├── PCView/ # PC 전용 뷰 (레이아웃이 크게 다를 때) | ||
| │ └── PCView.tsx | ||
| └── MobileView/ # 모바일 전용 뷰 | ||
| └── MobileView.tsx | ||
| ``` |
There was a problem hiding this comment.
디렉터리 트리 코드블록에 language를 지정해주세요.
Line 56 코드블록이 language 없이 선언되어 markdownlint(MD040) 경고가 발생합니다. 트리 구조는 text 지정이 가장 무난합니다.
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 56-56: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 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 @.claude/skills/feature-planning/SKILL.md around lines 56 - 66, Update the
markdown code block that shows the component tree (the block starting with
"src/components/[FeatureName]/") to include a language specifier by changing the
opening fence from ``` to ```text (and leave the closing fence unchanged) so the
tree block is recognized as text and resolves the MD040 warning.
| ``` | ||
| feat: [기능명] 추가 | ||
| fix: [이슈] 수정 | ||
| refactor: [대상] 리팩토링 | ||
| style: 코드 스타일 수정 (SonarCloud 이슈 해결) | ||
| chore: 기타 변경사항 | ||
| ``` |
There was a problem hiding this comment.
컨벤셔널 커밋 예시 코드블록에 language를 지정해주세요.
Line 37 코드블록이 language 없이 선언되어 markdownlint(MD040) 경고가 발생합니다. text 또는 bash로 지정해주세요.
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 37-37: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 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 @.claude/skills/github-pr-creation/SKILL.md around lines 37 - 43, Update the
fenced code block that contains the commit message examples (the block starting
with ``` and the lines "feat: [기능명] 추가" etc.) to include a language tag (e.g.,
```text or ```bash) so markdownlint MD040 is satisfied; edit the code block
delimiter in .claude/skills/github-pr-creation/SKILL.md to add the chosen
language without changing the block content.
| |------|------| | ||
| | `_workspace/` 없음 | 신규 실행 → Phase 1로 | | ||
| | `_workspace/` 있고 부분 수정 요청 | 부분 재실행 → 해당 Phase부터 | | ||
| | `_workspace/` 있고 새 입력 | `mv _workspace/ _workspace_prev/` 후 신규 실행 | |
There was a problem hiding this comment.
_workspace 백업 시 기존 백업 덮어쓰기/충돌 위험을 제거해주세요.
Line 20의 mv _workspace/ _workspace_prev/는 _workspace_prev/가 이미 있을 때 실패하거나 이전 백업을 잃을 수 있습니다. 타임스탬프 백업으로 바꾸는 게 안전합니다.
제안 패치
-| `_workspace/` 있고 새 입력 | `mv _workspace/ _workspace_prev/` 후 신규 실행 |
+| `_workspace/` 있고 새 입력 | `mv _workspace/ "_workspace_prev_$(date +%Y%m%d_%H%M%S)/"` 후 신규 실행 |📝 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.
| | `_workspace/` 있고 새 입력 | `mv _workspace/ _workspace_prev/` 후 신규 실행 | | |
| | `_workspace/` 있고 새 입력 | `mv _workspace/ "_workspace_prev_$(date +%Y%m%d_%H%M%S)/"` 후 신규 실행 | |
🤖 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 @.claude/skills/koin-pipeline/SKILL.md at line 20, 현재 문서의 백업 명령 `mv
_workspace/ _workspace_prev/`는 `_workspace_prev/`가 존재하면 실패하거나 덮어쓸 위험이 있으니
`_workspace_prev/` 대신 고유한 이름을 사용하도록 수정하세요: 문서 내 해당 문장(참조 `_workspace/`,
`_workspace_prev/`, `mv _workspace/ _workspace_prev/`)을 타임스탬프 또는 유니크 ID를 포함한
폴더명(예: `_workspace_prev_<timestamp>` 또는 `_workspace_YYYYMMDD_HHMMSS`)으로 변경하도록
안내하고, 기존 이름 충돌을 피하기 위해 덮어쓰기 대신 새 이름으로 mv 하도록 설명을 덧붙이세요.
|
|
||
| ### A-1. SonarCloud Analyzer 실행 | ||
|
|
||
| ``` |
There was a problem hiding this comment.
Fenced code block에 language를 지정해 markdownlint(MD040) 경고를 없애주세요.
현재 문서의 코드 블록들이 language 없이 선언되어 lint 경고가 반복됩니다. bash, json, text 등으로 명시하면 됩니다.
Also applies to: 54-54, 69-69, 86-86, 105-105, 123-123, 138-138, 155-155, 179-179
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 39-39: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 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 @.claude/skills/koin-pipeline/SKILL.md at line 39, Multiple fenced code
blocks in SKILL.md are declared with bare ``` which triggers markdownlint MD040;
update each backtick fence to include an appropriate language hint (e.g.,
```bash, ```json, ```text) to remove the warning. Search for the bare
triple-backtick blocks in SKILL.md (the occurrences around the examples
referenced) and change them to the correct language for their contents so each
fenced block (those repeated occurrences) has a language specifier.
| notifications: (token: string) => ['callvan', 'notifications', token] as const, | ||
| restriction: (token: string) => ['callvan', 'restriction', token] as const, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# queryKey에 토큰(민감값)이 직접 포함되는 지점 점검 (read-only)
rg -nP "callvanQueryKeys\.(notifications|restriction)\(token\)|\['callvan',\s*'(notifications|restriction)',\s*token\]" src -C2Repository: BCSDLab/KOIN_WEB_RECODE
Length of output: 1393
🏁 Script executed:
# Check for other token-based queryKey patterns in the api folder
rg -nP "queryKey.*token\)|token.*as const" src/api --type tsRepository: BCSDLab/KOIN_WEB_RECODE
Length of output: 1849
🏁 Script executed:
# Check invalidation patterns in callvan queries file
rg -n "invalidate|queryKey" src/api/callvan/queries.ts -A2 -B2Repository: BCSDLab/KOIN_WEB_RECODE
Length of output: 1314
queryKey에 raw 토큰을 포함하지 마세요.
Line 15–16에서 인증 토큰을 queryKey 세그먼트에 직접 포함하면 React Query Devtools, 로그, 메모리 덤프 등에서 민감값이 노출될 수 있습니다. 같은 파일의 list, infiniteList, postDetail, chat 등 다른 쿼리는 이미 토큰을 queryKey에서 제외하고 queryFn에서만 사용하는 패턴을 따르고 있으므로, notifications과 restriction도 동일하게 수정해야 합니다.
수정 예시
- notifications: (token: string) => ['callvan', 'notifications', token] as const,
- restriction: (token: string) => ['callvan', 'restriction', token] as const,
+ notifications: () => ['callvan', 'notifications'] as const,
+ restriction: () => ['callvan', 'restriction'] as const,그리고 쿼리 옵션 함수들에서도 해당 queryKey 호출 시 토큰 인자를 제거하세요 (Line 49, 56).
🤖 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/api/callvan/queries.ts` around lines 15 - 16, The query keys
notifications and restriction currently embed the raw token; change the
functions notifications and restriction to omit the token from the returned
queryKey (e.g., return ['callvan','notifications'] as const and
['callvan','restriction'] as const) and keep the token only as a queryFn
parameter so the token is consumed inside the request function (preserve
existing queryFn signatures to accept token). Also update any query option
helpers that call these keys (the option functions referenced near the other key
usages) so they call notifications()/restriction() without passing the token
into the key generation, and ensure they pass the token only into the queryFn
when invoking or constructing fetch callbacks.
| phone: string | null; | ||
| location: string; | ||
| remarks: string | null; | ||
| icon_url: string | null; |
There was a problem hiding this comment.
icon_url 필드를 선택적(?)으로 표시하는 것을 고려하세요.
백엔드가 아직 이 필드를 내려주지 않는 이전 응답이나 테스트 픽스처에서는 undefined가 될 수 있습니다. 타입이 string | null(필수)로 선언되어 있으면 그런 경우 타입 불일치가 생기지만, 런타임에서는 ShopIconProps가 string | null | undefined를 허용하기 때문에 조용히 처리됩니다. 백엔드가 항상 이 필드를 반환한다고 보장된다면 현행 유지가 맞지만, 그렇지 않다면 ?를 붙여 타입과 실제 데이터를 일치시켜 주세요.
💡 제안 수정
- icon_url: string | null;
+ icon_url?: string | null;📝 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.
| icon_url: string | null; | |
| icon_url?: string | null; |
🤖 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/api/coopshop/entity.ts` at line 27, The icon_url field is currently
declared as required (icon_url: string | null) but can be undefined in older
backend responses or fixtures; update the declaration to make icon_url optional
(add ? to the icon_url property) so the TypeScript type matches possible runtime
values (string | null | undefined) and adjust any consuming code if it relies on
icon_url always being present; locate the icon_url property in the
entity/interface in src/api/coopshop/entity.ts and change its signature to be
optional.
| &__button { | ||
| display: flex; | ||
| width: 100%; | ||
| align-items: center; | ||
| justify-content: center; | ||
| border: none; | ||
| border-radius: 8px; | ||
| background: $callvan-restriction-accent-color; | ||
| padding: 12px; | ||
| color: #fff; | ||
| font-family: $callvan-restriction-font-family; | ||
| font-size: 15px; | ||
| font-weight: 500; | ||
| line-height: 1.6; | ||
| cursor: pointer; | ||
| } |
There was a problem hiding this comment.
키보드 포커스 가시성이 없습니다
Line 81-96의 버튼에 :focus-visible 스타일이 없어 키보드 사용자가 현재 포커스를 인지하기 어렵습니다. 모달은 접근성 민감 구간이라 포커스 링을 명시해 주세요.
제안 diff
&__button {
@@
cursor: pointer;
+
+ &:focus-visible {
+ outline: 2px solid `#111`;
+ outline-offset: 2px;
+ }
}As per coding guidelines src/components/**: 4. 접근성(a11y) 관련 속성이 적절히 사용되고 있는지 확인해주세요.
🤖 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/components/Callvan/components/CallvanRestrictionModal/CallvanRestrictionModal.module.scss`
around lines 81 - 96, The button class &__button in
CallvanRestrictionModal.module.scss lacks a :focus-visible rule, so add a
:focus-visible selector for .&__button that displays a clear, high-contrast
focus indicator (e.g., a visible outline or ring using outline or box-shadow
that respects the existing border-radius and uses accessible color contrast),
ensure the focus style does not change layout (use outline-offset or box-shadow)
and include ::focus-visible alongside :focus fallback for broad browser support;
update the SCSS near the &__button block so keyboard users can perceive focus in
the CallvanRestrictionModal.
| export function isCallvanRestrictedError(error: unknown): boolean { | ||
| if (!isKoinError(error)) return false; | ||
|
|
||
| return error.status === CALLVAN_RESTRICTED_ERROR_STATUS; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# 목적: 제한 모달 트리거 조건이 403 단독인지, 제한 전용 코드가 이미 존재하는지 확인
# 기대: restriction 관련 식별자(code/type/message) 사용 여부를 확인할 수 있어야 함
rg -n -C3 "isCallvanRestrictedError|CALLVAN_RESTRICTED_ERROR_STATUS|status === 403" src/components src/api
rg -n -C3 "restriction|RESTRICT|FORBIDDEN|isKoinError|error\\.code|error\\.status" src/components src/apiRepository: BCSDLab/KOIN_WEB_RECODE
Length of output: 50380
🏁 Script executed:
# Check isKoinError type definition and available fields
rg -n "interface.*KoinError|type.*KoinError" --type ts -A5
# Check how the error is used in useCallvanRestrictionModal to see if there's additional context
cat -n src/components/Callvan/hooks/useCallvanRestrictionModal.tsx | head -50
# Look at the CallvanRestrictionResponse API call and how it's structured
cat -n src/api/callvan/APIDetail.ts | grep -A10 "GetCallvanRestriction"
# Check if there's error message or code in the Koin error that could distinguish
rg -n "error\\.message|error\\.code" src/components/Callvan -A2 -B2Repository: BCSDLab/KOIN_WEB_RECODE
Length of output: 2560
🏁 Script executed:
# Check if there's a specific error code constant for Callvan restrictions
rg -n "CALLVAN|restriction|code.*=" src/components/Callvan src/api/callvan -i
# Check backend error codes or constants that might indicate restriction
rg -n "const.*403|const.*code|error.*code" src/api src/constants -B1 -A1 | head -40
# Look for any error code usage in similar functions
rg -n "error\\.code" src/components -A2 -B2 | head -50Repository: BCSDLab/KOIN_WEB_RECODE
Length of output: 50451
error.code를 함께 확인하거나 에러 식별자를 추가로 검증하세요
현재 status === 403 단독 판별은 Callvan 특화 제한과 다른 Forbidden 에러(비밀번호 오류, 권한 부족 등)를 구분하지 못합니다. KoinError는 code 필드를 지원하므로, 제한 전용 코드가 있다면 다음과 같이 조건을 강화할 수 있습니다:
export function isCallvanRestrictedError(error: unknown): boolean {
if (!isKoinError(error)) return false;
// 제한 전용 코드가 정의되어 있으면 추가 조건으로 검증
return error.status === CALLVAN_RESTRICTED_ERROR_STATUS && error.code === CALLVAN_RESTRICTION_CODE;
}useCallvanRestrictionModal의 openFromError에서 제한 상태를 재확인하는 로직이 이미 있어 이중 방어가 되고 있으나, 유틸리티 함수 자체는 다운스트림 검증에 의존하지 않도록 설계하는 것이 안정성 개선에 도움이 됩니다. 백엔드와 확인하여 제한 전용 코드 값이 있으면 반영해주세요.
🤖 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/components/Callvan/utils/callvanRestriction.ts` around lines 18 - 22, The
current isCallvanRestrictedError only checks isKoinError and status ===
CALLVAN_RESTRICTED_ERROR_STATUS which can misclassify generic 403s; update
isCallvanRestrictedError to also verify the KoinError.code against a
Callvan-specific identifier (e.g., CALLVAN_RESTRICTION_CODE) so it returns true
only when both status and code match; reference the existing isKoinError,
CALLVAN_RESTRICTED_ERROR_STATUS, and (add/use) CALLVAN_RESTRICTION_CODE
constants, and keep useCallvanRestrictionModal.openFromError’s existing
double-checks but make the utility function defensively validate both fields.
| const currentFrameId = validatedFrameId ?? mainFrame?.id ?? null; | ||
|
|
||
| const prefetchPromises = [ | ||
| queryClient.prefetchQuery(timetableQueries.semesterInfo()), | ||
| queryClient.prefetchQuery(deptQueries.list()), | ||
| ]; | ||
|
|
||
| if (currentFrameId !== null) { | ||
| prefetchPromises.push(queryClient.prefetchQuery(timetableQueries.lectureInfo(token, currentFrameId))); |
There was a problem hiding this comment.
존재하지 않는 frameId로 SSR prefetch가 실패할 수 있습니다.
Line 49-57에서 validatedFrameId를 목록 존재 여부 확인 없이 우선 사용하고 있어, 실제 timetableFrameList에 없는 ID면 lectureInfo prefetch에서 실패하고 SSR이 깨질 수 있습니다.
validatedFrameId가 리스트에 존재할 때만 사용하고, 아니면 mainFrame?.id로 fallback 하도록 보완하는 게 안전합니다.
수정 예시
- const mainFrame = timetableFrameList.find((frame) => frame.is_main);
- const currentFrameId = validatedFrameId ?? mainFrame?.id ?? null;
+ const mainFrame = timetableFrameList.find((frame) => frame.is_main);
+ const hasValidatedFrame = validatedFrameId !== null && timetableFrameList.some((frame) => frame.id === validatedFrameId);
+ const currentFrameId = hasValidatedFrame ? validatedFrameId : (mainFrame?.id ?? null);📝 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.
| const currentFrameId = validatedFrameId ?? mainFrame?.id ?? null; | |
| const prefetchPromises = [ | |
| queryClient.prefetchQuery(timetableQueries.semesterInfo()), | |
| queryClient.prefetchQuery(deptQueries.list()), | |
| ]; | |
| if (currentFrameId !== null) { | |
| prefetchPromises.push(queryClient.prefetchQuery(timetableQueries.lectureInfo(token, currentFrameId))); | |
| const mainFrame = timetableFrameList.find((frame) => frame.is_main); | |
| const hasValidatedFrame = validatedFrameId !== null && timetableFrameList.some((frame) => frame.id === validatedFrameId); | |
| const currentFrameId = hasValidatedFrame ? validatedFrameId : (mainFrame?.id ?? null); | |
| const prefetchPromises = [ | |
| queryClient.prefetchQuery(timetableQueries.semesterInfo()), | |
| queryClient.prefetchQuery(deptQueries.list()), | |
| ]; | |
| if (currentFrameId !== null) { | |
| prefetchPromises.push(queryClient.prefetchQuery(timetableQueries.lectureInfo(token, currentFrameId))); |
🤖 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/pages/timetable/index.tsx` around lines 49 - 57, The prefetch uses
validatedFrameId without checking if it exists in the timetableFrameList, which
can cause timetableQueries.lectureInfo(token, currentFrameId) to fail during
SSR; update the logic that sets currentFrameId so it only uses validatedFrameId
when that ID is present in timetableFrameList, otherwise fall back to
mainFrame?.id (or null), then build prefetchPromises accordingly before pushing
timetableQueries.lectureInfo(token, currentFrameId); ensure you reference
validatedFrameId, timetableFrameList, mainFrame, currentFrameId and
prefetchPromises when applying this guard.
What is this PR? 🔍
Changes 📝
✔️ Please check if the PR fulfills these requirements
developbranch unconditionally?main?yarn lintSummary by CodeRabbit
릴리스 노트
새 기능
개선사항
기타