-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[refactor] 모노레포 구조로 폴더 구조 리팩토링 #34
base: develop
Are you sure you want to change the base?
Conversation
Warning There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure. 🔧 eslint
client/user/eslint.config.mjsOops! Something went wrong! :( ESLint: 9.18.0 ESLint couldn't find an eslint.config.(js|mjs|cjs) file. From ESLint v9.0.0, the default configuration file is now eslint.config.js. https://eslint.org/docs/latest/use/configure/migration-guide If you still have problems after following the migration guide, please stop by 개요워크스루이 풀 리퀘스트는 프로젝트의 구조를 대대적으로 리팩토링하고 있습니다. 주요 변경 사항은 모노레포 접근 방식으로의 전환, 공유 구성 요소 및 유틸리티 패키지 도입, 그리고 컴포넌트 및 구성 파일의 경로 재구성을 포함합니다. 이러한 변경은 코드 재사용성과 일관성을 향상시키는 것을 목표로 합니다. 변경 사항
시퀀스 다이어그램sequenceDiagram
participant Dev as 개발자
participant Monorepo as 모노레포 구조
participant Components as 공유 컴포넌트
participant Config as 공유 구성
Dev->>Monorepo: 코드 변경
Monorepo->>Components: 컴포넌트 참조
Monorepo->>Config: 구성 파일 참조
Components-->>Dev: 일관된 UI 제공
Config-->>Dev: 표준화된 설정 적용
관련 가능한 PR
토끼의 시 🐰
Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 15
🔭 Outside diff range comments (3)
client/user/src/app/(nonRoot)/profile/(order)/orderHistory/page.tsx (1)
Line range hint
48-63
: 페이지네이션 로직 개선 필요현재 구현된 청크 기반 페이지네이션은 다음과 같은 제한사항이 있습니다:
- 고정된 청크 크기 (4페이지)
- 하드코딩된 최대 청크 수 (2개)
- 전체 아이템 수에 기반한 동적 계산 부재
더 유연한 구현을 위해 다음과 같은 개선을 제안합니다:
interface PaginationProps { totalItems: number; itemsPerPage: number; currentPage: number; onPageChange: (page: number) => void; } const calculatePageCount = (totalItems: number, itemsPerPage: number) => Math.ceil(totalItems / itemsPerPage); const calculateVisiblePages = (currentPage: number, totalPages: number) => { // 동적으로 표시할 페이지 범위 계산 로직 };client/user/src/app/_components/ScrollPages/3stPage.tsx (1)
Line range hint
8-99
: 인라인 스타일을 Tailwind 클래스로 마이그레이션 필요인라인 스타일을 사용하면 일관성 있는 스타일 관리가 어렵고, 반응형 디자인 구현이 복잡해집니다.
다음과 같은 개선사항을 제안드립니다:
- 고정 크기를 Tailwind의 유틸리티 클래스로 변환
- 반응형 디자인을 위한 Tailwind의 브레이크포인트 활용
- 공통 스타일을 Tailwind 설정에 추가
예시:
-style={{ - height: 'calc(100vh - 90px)', -}} +className="min-h-[calc(100vh-90px)]"client/user/src/app/(nonRoot)/(auth)/password/reset/page.tsx (1)
Line range hint
44-51
: 보안 취약점: 하드코딩된 인증 코드인증 코드가 하드코딩되어 있어 보안상 취약점이 될 수 있습니다.
if (verificationCode === '111') { // 하드코딩된 값서버 측 검증을 구현하고 환경 변수나 API를 통해 안전하게 처리하는 것을 강력히 권장드립니다.
🧹 Nitpick comments (22)
client/user/src/app/(nonRoot)/layout.tsx (1)
6-6
: clsx 사용 최적화가 필요합니다.단일 문자열만 사용하는 경우 clsx는 불필요합니다. 직접 className을 사용하는 것이 더 간단합니다.
다음과 같이 수정하는 것을 추천드립니다:
- <div className={clsx('min-h-screen w-full bg-blue-secondary')}> + <div className="min-h-screen w-full bg-blue-secondary">client/user/src/app/(nonRoot)/apply/layout.tsx (1)
7-7
: 불필요한 빈 className을 제거해주세요.빈 문자열로 된 className은 불필요합니다.
다음과 같이 수정하는 것을 추천드립니다:
- <div className={clsx('')}> + <div>client/user/src/app/(nonRoot)/apply/page.tsx (1)
11-13
: 컴포넌트 구조 개선이 필요합니다.빈 div 대신 의미 있는 시맨틱 태그를 사용하고, 접근성을 고려한 구조로 개선하는 것이 좋습니다.
- <div> + <section aria-label="도서 구매"> <Purchase books={books} onDelete={handleRemoveBook} /> - </div> + </section>client/user/src/app/layout.tsx (1)
19-19
: 하드코딩된 패딩 값을 CSS 변수로 관리하는 것이 좋습니다.
pt-[90px]
와 같은 하드코딩된 값은 유지보수를 어렵게 만들 수 있습니다. 헤더 높이가 변경될 경우 여러 곳을 수정해야 할 수 있습니다.- <main className="flex-1 overflow-hidden pt-[90px]">{children}</main> + <main className="flex-1 overflow-hidden pt-[--header-height]">{children}</main>CSS 변수는
@tookscan/styles/globals.css
에 추가하시면 됩니다::root { --header-height: 90px; }client/user/src/app/(nonRoot)/apply/_components/Purchase.tsx (1)
30-30
: 가격 포맷팅을 개선해주세요.현재 가격이 단순히 숫자와 '원'으로 표시되고 있습니다. 천 단위 구분자를 추가하고 통화 포맷팅을 적용하면 사용자 경험이 향상될 것입니다.
- <p className="text-sm text-gray-500">{calculateTotalPrice()}원</p> + <p className="text-sm text-gray-500"> + {calculateTotalPrice().toLocaleString('ko-KR')}원 + </p>packages/components/ui/Banner/Banner3.tsx (1)
1-26
: 컴포넌트 구조가 명확하고 일관성이 있습니다.모노레포 구조로의 리팩토링 과정에서 컴포넌트의 구조와 스타일링이 잘 유지되었습니다. 다만, 재사용성 향상을 위한 제안사항이 있습니다.
배너의 텍스트 콘텐츠를 props로 받도록 개선하는 것을 고려해보세요:
-const Banner3 = () => { +interface Banner3Props { + title: string; + description: string; +} + +const Banner3 = ({ title, description }: Banner3Props) => { return ( <div className="flex h-full w-[1135px] items-center justify-between px-6"> <div className="flex flex-col justify-center text-left"> - <p className="text-[32px] font-bold leading-[1.5]">신청하기</p> + <p className="text-[32px] font-bold leading-[1.5]">{title}</p> <p className="mt-2 text-[12px] leading-[1.5] opacity-50"> - 툭은 안전하고 편리한 스캔서비스를 제공합니다. <br /> - 지금 스캔 신청하기를 통해 빠르게 견적을 확인해보세요. + {description} </p>client/user/src/types/svg.d.ts (1)
1-4
: SVG 모듈 타입 선언 개선 제안SVG 파일의 타입을 더 구체적으로 정의하면 좋을 것 같습니다. React 컴포넌트로 사용할 수 있도록 타입을 확장하는 것을 고려해보세요.
declare module '*.svg' { - const content: string; // 경로를 문자열로 가져오기 - export default content; + import { FunctionComponent, SVGProps } from 'react'; + const content: FunctionComponent<SVGProps<SVGSVGElement>>; + export default content; }packages/shared/eslint/eslint.config.mjs (1)
6-9
: ESLint 규칙 확장 제안현재 설정된 기본 규칙들이 적절해 보입니다. 다음과 같은 추가 규칙들도 고려해보시면 좋을 것 같습니다:
rules: { semi: ["error", "never"], quotes: ["error", "single"], + "no-unused-vars": "error", + "no-console": "warn", + "@typescript-eslint/explicit-function-return-type": "warn", },client/user/eslint.config.mjs (1)
4-10
: 환경별 ESLint 설정 추가 제안현재 설정은 잘 되어있지만, 개발 환경과 프로덕션 환경에 따라 다른 규칙을 적용하는 것을 고려해보세요.
export default { ...sharedConfig, rules: { ...sharedConfig.rules, 'no-console': 'warn', + ...(process.env.NODE_ENV === 'production' ? { + 'no-console': 'error', + 'no-debugger': 'error', + } : { + 'no-console': 'warn', + 'no-debugger': 'warn', + }), }, }client/user/next.config.ts (2)
6-8
: 이미지 도메인 설정을 환경 변수로 관리하는 것을 고려해보세요.프로덕션과 개발 환경에서 유연하게 대응할 수 있도록 이미지 도메인을 환경 변수로 관리하는 것이 좋습니다.
images: { - domains: ['ibb.co'], + domains: process.env.NEXT_PUBLIC_IMAGE_DOMAINS?.split(',') ?? ['ibb.co'], formats: ['image/webp'], },
10-16
: 웹팩 캐시 디렉토리 경로를 모노레포 구조에 맞게 조정하세요.현재 캐시 디렉토리가 각 프로젝트 내부에 생성되고 있습니다. 모노레포 구조에서는 루트 레벨에서 캐시를 관리하는 것이 더 효율적일 수 있습니다.
webpack: (config) => { config.cache = { type: 'filesystem', - cacheDirectory: path.resolve(__dirname, '.webpack-cache'), + cacheDirectory: path.resolve(__dirname, '../../.webpack-cache'), }; return config; },package.json (1)
11-14
: 빌드 스크립트에 타입 체크와 린트 과정을 추가하세요.현재 빌드 스크립트는 단순 빌드만 수행합니다. 안정적인 빌드를 위해 타입 체크와 린트 과정을 포함하는 것이 좋습니다.
"scripts": { "dev:user": "yarn workspace user dev", "dev:admin": "yarn workspace admin dev", - "build:user": "yarn workspace user build", - "build:admin": "yarn workspace admin build", + "build:user": "yarn lint && yarn workspace user tsc && yarn workspace user build", + "build:admin": "yarn lint && yarn workspace admin tsc && yarn workspace admin build",client/user/src/app/(nonRoot)/profile/(order)/orderHistory/page.tsx (2)
Line range hint
7-45
: 주문 데이터 모킹 분리 필요현재 컴포넌트 내부에 하드코딩된 주문 데이터를 별도의 mock 파일로 분리하는 것이 좋습니다. 이는 테스트와 유지보수를 용이하게 만들 것입니다.
+// mocks/orderData.ts +export const mockOrders: OrderInfo[] = [ + { + userName: '민경훈', + dateTime: '2024.12.15 (12시 45분)', + // ... 나머지 데이터 + }, + // ... 나머지 주문들 +]
Line range hint
76-89
: 정렬 기능 구현 및 확장 필요현재 정렬 기능에 대한 개선사항:
- 실제 정렬 로직이 구현되어 있지 않습니다.
- 정렬 옵션이 최신순/오래된순으로만 제한되어 있습니다.
추가적인 정렬 옵션(가격순, 상품명순 등)과 함께 실제 정렬 로직을 구현하는 것이 좋습니다.
const sortOrders = (orders: OrderInfo[], option: string) => { switch (option) { case 'latest': return [...orders].sort((a, b) => new Date(b.dateTime).getTime() - new Date(a.dateTime).getTime()); case 'oldest': return [...orders].sort((a, b) => new Date(a.dateTime).getTime() - new Date(b.dateTime).getTime()); // 추가 정렬 옵션들... default: return orders; } };packages/components/ui/Label/ConsentLabel.tsx (2)
5-11
: 인터페이스에 JSDoc 문서화를 추가하면 좋을 것 같습니다.각 prop의 용도와 제약사항을 명확히 설명하는 JSDoc 주석을 추가하면 컴포넌트 사용자의 이해를 도울 수 있습니다.
예시:
+/** + * 동의 레이블 컴포넌트의 속성을 정의합니다. + */ interface ConsentLabelProps { + /** 레이블에 표시될 텍스트 내용 */ content: string; + /** 현재 동의 상태 */ consentStatus: boolean; + /** 동의 상태를 변경하는 콜백 함수 */ setConsentStatus: (status: boolean) => void; + /** 레이블 크기 - "sm": 작은 크기, "lg": 큰 크기 */ size: "sm" | "lg"; + /** 선택적으로 제공되는 클릭 핸들러 */ onClick?: () => void; }
21-31
: 사용자 상호작용 상태에 대한 스타일을 추가하면 좋을 것 같습니다.hover, focus 상태에 대한 스타일이 없어 사용자 피드백이 부족할 수 있습니다.
<div className={clsx( "flex flex-row items-center", "font-normal leading-[1.25rem] tracking-[-0.00438rem]", "text-black-600", + "transition-colors duration-200", + "hover:opacity-80", size === "sm" && "text-2xs", size === "lg" && "text-sm", consentStatus ? "text-blue-primary" : "text-black-600" )}packages/shared/tailwind/tailwind.config.ts (2)
2-2
: 상대 경로 대신 별칭(alias) 경로 사용을 권장합니다.현재 colors 파일을 가져오는 방식이 상대 경로를 사용하고 있습니다. 모노레포 구조에서는
@shared/config/color
와 같은 별칭 경로를 사용하는 것이 더 안정적입니다.-import { colors } from "../../config/color"; +import { colors } from "@shared/config/color";
28-28
: 필요한 Tailwind 플러그인이 누락되지 않았는지 확인해주세요.현재 플러그인이 비어있습니다. 프로젝트에서 필요할 수 있는 다음 플러그인들의 사용을 고려해보세요:
@tailwindcss/forms
@tailwindcss/typography
@tailwindcss/aspect-ratio
packages/utils/cn.ts (1)
1-4
: JSDoc 문서화 추가 필요유틸리티 함수의 용도와 사용 방법에 대한 문서화가 필요합니다.
다음과 같이 JSDoc을 추가하는 것을 추천드립니다:
import classNames from 'classnames' +/** + * CSS 클래스명들을 조건부로 결합하는 유틸리티 함수 + * @param classes - 결합할 클래스명들 (문자열, undefined, null, 또는 false) + * @returns 결합된 클래스명 문자열 + * @example + * cn('foo', 'bar') // => 'foo bar' + * cn('foo', undefined, 'bar') // => 'foo bar' + */ export const cn = (...classes: (string | undefined | null | false)[]): string => classNames(classes)client/user/src/app/_components/ScrollPages/3stPage.tsx (1)
Line range hint
71-79
: 접근성 개선 필요비밀번호 표시/숨김 토글 버튼에 접근성 속성이 누락되었습니다.
다음과 같은 접근성 속성 추가를 제안드립니다:
-<button onClick={() => setIsPasswordVisible(!isPasswordVisible)}> +<button + onClick={() => setIsPasswordVisible(!isPasswordVisible)} + type="button" + aria-label={isPasswordVisible ? "비밀번호 숨기기" : "비밀번호 표시"} +>packages/components/ui/InputField.tsx (1)
8-18
: 타입 안전성 개선 필요InputField 컴포넌트의 타입 정의를 더 안전하게 개선할 수 있습니다.
다음과 같은 개선사항을 제안드립니다:
+type InputType = "simple" | "number" | "suffix" | "search" | "password"; + interface InputFieldProps { - type: "simple" | "number" | "suffix" | "search" | "password"; + type: InputType; value?: string; inputRef?: React.RefObject<HTMLInputElement>; onChange: (e: React.ChangeEvent<HTMLInputElement>) => void; placeholder?: string; helperText?: string; disabled?: boolean; isError?: boolean; isSuccess?: boolean; + 'aria-label'?: string; + 'aria-describedby'?: string; }client/user/src/app/(nonRoot)/(auth)/password/reset/page.tsx (1)
Line range hint
9-19
: 상태 관리 최적화 필요여러 개의 독립적인 상태들이 useState로 관리되고 있어 복잡성이 증가할 수 있습니다.
다음과 같이 관련 상태들을 그룹화하여 관리하는 것을 추천드립니다:
interface VerificationState { code: string; timeLeft: number; isVerified: boolean; result: string | null; } interface UserInputState { id: string; name: string; phone: string; } const [verificationState, setVerificationState] = useState<VerificationState>({ code: '', timeLeft: 0, isVerified: false, result: null, }); const [userInput, setUserInput] = useState<UserInputState>({ id: '', name: '', phone: '', });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (12)
client/user/.yarn/install-state.gz
is excluded by!**/.yarn/**
,!**/*.gz
client/user/src/assets/images/landing/Book.svg
is excluded by!**/*.svg
client/user/src/assets/images/landing/Hammer.svg
is excluded by!**/*.svg
client/user/src/assets/images/landing/MainCard.svg
is excluded by!**/*.svg
client/user/src/assets/images/login/Copy.svg
is excluded by!**/*.svg
client/user/src/assets/images/login/GoogleCircle.svg
is excluded by!**/*.svg
client/user/src/assets/images/login/KakaoCircle.svg
is excluded by!**/*.svg
client/user/src/assets/images/signup/Google.svg
is excluded by!**/*.svg
client/user/src/assets/images/signup/Kakao.svg
is excluded by!**/*.svg
client/user/src/assets/images/signup/asdf.png
is excluded by!**/*.png
packages/assets/fonts/PretendardVariable.ttf
is excluded by!**/*.ttf
yarn.lock
is excluded by!**/yarn.lock
,!**/*.lock
📒 Files selected for processing (62)
.gitignore
(2 hunks)client/user/.prettierrc
(1 hunks)client/user/eslint.config.mjs
(1 hunks)client/user/next.config.ts
(1 hunks)client/user/package.json
(1 hunks)client/user/postcss.config.mjs
(1 hunks)client/user/src/app/(nonRoot)/(auth)/id/page.tsx
(1 hunks)client/user/src/app/(nonRoot)/(auth)/id/reset/page.tsx
(1 hunks)client/user/src/app/(nonRoot)/(auth)/layout.tsx
(1 hunks)client/user/src/app/(nonRoot)/(auth)/login/page.tsx
(1 hunks)client/user/src/app/(nonRoot)/(auth)/password/page.tsx
(1 hunks)client/user/src/app/(nonRoot)/(auth)/password/reset/page.tsx
(1 hunks)client/user/src/app/(nonRoot)/(auth)/signUp/page.tsx
(1 hunks)client/user/src/app/(nonRoot)/apply/_components/Purchase.tsx
(1 hunks)client/user/src/app/(nonRoot)/apply/layout.tsx
(1 hunks)client/user/src/app/(nonRoot)/apply/page.tsx
(1 hunks)client/user/src/app/(nonRoot)/guest/(order)/layout.tsx
(1 hunks)client/user/src/app/(nonRoot)/guest/(order)/order/check/page.tsx
(1 hunks)client/user/src/app/(nonRoot)/guest/(order)/order/list/page.tsx
(1 hunks)client/user/src/app/(nonRoot)/layout.tsx
(1 hunks)client/user/src/app/(nonRoot)/profile/(order)/orderHistory/page.tsx
(1 hunks)client/user/src/app/(nonRoot)/profile/layout.tsx
(1 hunks)client/user/src/app/(nonRoot)/welcome/page.tsx
(1 hunks)client/user/src/app/_components/Modal.tsx
(1 hunks)client/user/src/app/_components/ReviewCard.tsx
(1 hunks)client/user/src/app/_components/ScrollPages/1stPage.tsx
(1 hunks)client/user/src/app/_components/ScrollPages/3stPage.tsx
(1 hunks)client/user/src/app/_components/ScrollPages/5stPage.tsx
(1 hunks)client/user/src/app/_components/SmallCard.tsx
(1 hunks)client/user/src/app/layout.tsx
(1 hunks)client/user/src/types/common.ts
(1 hunks)client/user/src/types/svg.d.ts
(1 hunks)client/user/tailwind.config.ts
(1 hunks)client/user/tsconfig.json
(1 hunks)eslint.config.mjs
(0 hunks)next.config.ts
(0 hunks)package.json
(1 hunks)packages/components/package.json
(1 hunks)packages/components/ui/Banner/Banner.tsx
(2 hunks)packages/components/ui/Banner/Banner1.tsx
(2 hunks)packages/components/ui/Banner/Banner2.tsx
(2 hunks)packages/components/ui/Banner/Banner3.tsx
(1 hunks)packages/components/ui/Button.tsx
(1 hunks)packages/components/ui/InputField.tsx
(1 hunks)packages/components/ui/Label/ConsentLabel.tsx
(1 hunks)packages/config/package.json
(1 hunks)packages/shared/eslint/eslint.config.mjs
(1 hunks)packages/shared/prettier/package.json
(1 hunks)packages/shared/tailwind/package.json
(1 hunks)packages/shared/tailwind/tailwind.config.ts
(1 hunks)packages/shared/typescript/package.json
(1 hunks)packages/shared/typescript/tsconfig.base.json
(1 hunks)packages/styles/package.json
(1 hunks)packages/utils/cn.ts
(1 hunks)packages/utils/package.json
(1 hunks)src/app/(user)/layout.tsx
(0 hunks)src/app/_components/ui/InputField.tsx
(0 hunks)src/app/_components/ui/Label/ConsentLabel.tsx
(0 hunks)src/app/layout.tsx
(0 hunks)src/types/common.ts
(0 hunks)tailwind.config.ts
(0 hunks)tsconfig.json
(0 hunks)
💤 Files with no reviewable changes (9)
- eslint.config.mjs
- src/app/layout.tsx
- src/types/common.ts
- src/app/_components/ui/Label/ConsentLabel.tsx
- tailwind.config.ts
- src/app/_components/ui/InputField.tsx
- next.config.ts
- tsconfig.json
- src/app/(user)/layout.tsx
✅ Files skipped from review due to trivial changes (30)
- client/user/.prettierrc
- packages/shared/prettier/package.json
- packages/components/ui/Banner/Banner1.tsx
- packages/shared/tailwind/package.json
- packages/styles/package.json
- packages/config/package.json
- packages/utils/package.json
- packages/components/ui/Banner/Banner.tsx
- packages/shared/typescript/package.json
- packages/components/ui/Button.tsx
- packages/components/package.json
- client/user/src/app/_components/Modal.tsx
- client/user/src/app/(nonRoot)/(auth)/layout.tsx
- client/user/src/app/(nonRoot)/(auth)/id/page.tsx
- client/user/src/app/(nonRoot)/guest/(order)/layout.tsx
- client/user/src/app/(nonRoot)/guest/(order)/order/check/page.tsx
- client/user/src/app/_components/ScrollPages/5stPage.tsx
- client/user/src/app/_components/ReviewCard.tsx
- client/user/tsconfig.json
- client/user/src/app/(nonRoot)/(auth)/password/page.tsx
- client/user/package.json
- client/user/src/app/(nonRoot)/guest/(order)/order/list/page.tsx
- client/user/src/app/_components/SmallCard.tsx
- packages/components/ui/Banner/Banner2.tsx
- packages/shared/typescript/tsconfig.base.json
- client/user/src/app/(nonRoot)/welcome/page.tsx
- client/user/src/app/_components/ScrollPages/1stPage.tsx
- client/user/src/app/(nonRoot)/(auth)/signUp/page.tsx
- client/user/src/app/(nonRoot)/(auth)/id/reset/page.tsx
- client/user/src/app/(nonRoot)/(auth)/login/page.tsx
🔇 Additional comments (13)
client/user/src/app/(nonRoot)/profile/layout.tsx (1)
3-3
: 모노레포 구조에 맞게 import 경로가 수정되었습니다.공통 컴포넌트를
@tookscan/components
로 이동한 것이 모노레포 구조화에 적절해 보입니다.새로운 import 경로가 올바르게 구성되어 있는지 확인이 필요합니다:
✅ Verification successful
import 경로가 올바르게 구성되어 있습니다.
@tookscan/components
패키지가 올바르게 설정되어 있습니다- Banner 컴포넌트가
packages/components/ui/Banner/Banner.tsx
에 존재합니다- 여러 레이아웃 파일에서 일관되게 사용되고 있습니다
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify the new import path configuration # Check tsconfig paths configuration echo "Checking tsconfig paths..." fd -e json tsconfig. | xargs cat | jq '.compilerOptions.paths' # Check package.json workspace configuration echo "Checking workspace configuration..." cat package.json | jq '.workspaces' # Verify Banner component exists in the new location echo "Verifying Banner component location..." fd -e tsx -e ts Banner packages/Length of output: 644
Script:
#!/bin/bash # Check package.json in components package echo "Checking components package configuration..." cat packages/components/package.json | jq '{name, main, types}' # Verify the import is used correctly echo "Checking Banner import usage..." rg "@tookscan/components/ui/Banner/Banner" -A 2Length of output: 1404
client/user/src/types/common.ts (1)
3-13
: 인터페이스 구조가 잘 설계되었습니다!타입 정의가 명확하고 재사용 가능하게 잘 구성되어 있습니다.
restoreOption
에 리터럴 타입을 사용한 것이 특히 좋습니다.client/user/src/app/layout.tsx (1)
16-18
: 헤더의 고정 위치 처리 방식을 검토해주세요.고정 위치로 설정된 헤더는 다음과 같은 잠재적인 문제를 일으킬 수 있습니다:
- 모바일 브라우저에서 주소 표시줄이 보이거나 숨겨질 때 레이아웃 깨짐
- iOS Safari에서 스크롤 시 헤더가 사라지는 문제
스크롤 동작과 모바일 환경에서의 테스트가 필요합니다.
packages/components/ui/Banner/Banner3.tsx (2)
1-2
: clsx 라이브러리 사용이 적절합니다.조건부 클래스네임 관리를 위한 clsx 라이브러리 도입이 코드 가독성과 유지보수성을 향상시킵니다.
8-12
: 텍스트 스타일링이 개선되었습니다.opacity-50 클래스를 활용한 부가 설명 텍스트의 시각적 계층 구조가 사용자 경험을 향상시킵니다.
client/user/postcss.config.mjs (1)
5-5
: 설정이 적절해 보입니다!autoprefixer 플러그인 추가는 크로스 브라우저 호환성을 위해 좋은 선택입니다.
client/user/src/app/(nonRoot)/profile/(order)/orderHistory/page.tsx (1)
3-3
: 모노레포 패키지 경로 설정 확인 필요새로운 import 경로가 올바르게 설정되어 있는지 확인이 필요합니다.
✅ Verification successful
모노레포 패키지 구조가 올바르게 구성되어 있음을 확인했습니다
- @tookscan/components 패키지가 정상적으로 등록되어 있습니다
- OrderCard 컴포넌트가 packages/components/ui 디렉토리에 올바르게 위치해 있습니다
- import 경로가 모노레포 구조에 맞게 정확하게 설정되어 있습니다
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify the package alias configuration in tsconfig and package.json # Check tsconfig paths configuration cat $(fd -t f tsconfig.json) | jq -r 'select(.compilerOptions.paths != null) | .compilerOptions.paths' # Check package.json workspace configuration cat package.json | jq -r '.workspaces'Length of output: 251
Script:
#!/bin/bash # Find OrderCard component file fd OrderCard # Check if @tookscan exists in packages ls -la packages/@tookscan 2>/dev/null || echo "No @tookscan directory found" # Search for package.json files that might define @tookscan scope fd -t f package.json -x cat {} | jq -r 'select(.name != null) | select(.name | startswith("@tookscan"))'Length of output: 558
packages/components/ui/Label/ConsentLabel.tsx (1)
1-3
: 모노레포 구조에 맞게 잘 구성된 import문입니다!절대 경로를 사용한 import 구조가 모노레포 설계 의도와 잘 부합합니다.
client/user/tailwind.config.ts (2)
1-3
: 공유 설정을 올바르게 가져오고 타입을 정의했습니다!설정 파일의 기본 구조가 깔끔하며, TypeScript 타입이 잘 정의되어 있습니다.
4-7
: 컨텐츠 경로가 user 프로젝트에 특화되어 있는지 확인이 필요합니다.현재 설정된 경로
'./src/**/*.{js,ts,jsx,tsx}'
가 user 프로젝트의 실제 소스 코드 위치와 일치하는지 확인해주세요.✅ Verification successful
설정된 컨텐츠 경로가 정확합니다.
현재 설정된
'./src/**/*.{js,ts,jsx,tsx}'
경로가 user 프로젝트의 실제 소스 코드 위치와 정확히 일치하는 것을 확인했습니다. 모든 컴포넌트, 페이지 및 유틸리티 파일들이 이 경로 패턴에 포함되어 있습니다.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify the existence of source files in the specified path fd -e js -e ts -e jsx -e tsx . client/user/src/Length of output: 2071
packages/shared/tailwind/tailwind.config.ts (1)
23-25
: fontSize 설정이 디자인 시스템과 일치하는지 확인해주세요.추가된
2xs
사이즈(0.625rem = 10px)가 디자인 시스템의 타이포그래피 스케일과 일치하는지 확인이 필요합니다.client/user/src/app/_components/ScrollPages/3stPage.tsx (1)
5-5
: 임포트 경로가 올바르게 업데이트됨모노레포 구조에 맞게 Button 컴포넌트의 임포트 경로가 정확하게 변경되었습니다.
client/user/src/app/(nonRoot)/(auth)/password/reset/page.tsx (1)
5-7
: 임포트 경로가 올바르게 업데이트됨모노레포 구조에 맞게 컴포넌트들의 임포트 경로가 정확하게 변경되었습니다.
const [books, setBooks] = useState<Books[]>([]) | ||
const handleRemoveBook = (id: number) => { | ||
setBooks((prev) => prev.filter((book) => book.id !== id)) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
상태 관리 개선이 필요합니다.
다음 사항들을 고려해주시기 바랍니다:
- 초기 상태 유효성 검증
- 에러 처리 추가
- 로딩 상태 관리
예시 구현:
const [books, setBooks] = useState<Books[]>([])
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState<Error | null>(null)
const handleRemoveBook = async (id: number) => {
try {
setIsLoading(true)
setBooks((prev) => prev.filter((book) => book.id !== id))
} catch (err) {
setError(err instanceof Error ? err : new Error('알 수 없는 오류가 발생했습니다'))
} finally {
setIsLoading(false)
}
}
|
||
export default function RootLayout({ children }: LayoutProps) { | ||
return ( | ||
<html lang="en"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
HTML 언어 속성을 한국어로 변경해주세요.
현재 lang
속성이 "en"으로 설정되어 있지만, 애플리케이션이 한국어를 사용하는 것으로 보입니다. 웹 접근성과 SEO를 위해 적절한 언어 설정이 필요합니다.
- <html lang="en">
+ <html lang="ko">
📝 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.
<html lang="en"> | |
<html lang="ko"> |
} | ||
|
||
const Purchase = ({ books, onDelete }: PurchaseProps) => { | ||
const PRICE_PER_PAGE = 10 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
상수 값들을 별도의 상수 파일로 분리해주세요.
가격과 관련된 매직 넘버들(PRICE_PER_PAGE
, 5000
, 2500
)을 상수 파일로 분리하여 관리하면 유지보수가 용이해집니다.
// constants/pricing.ts
export const PRICE_PER_PAGE = 10;
export const RESTORE_COSTS = {
loose: 5000,
spiral: 2500,
default: 0,
} as const;
또한 restoreOption
의 타입 안전성을 위해 다음과 같이 타입을 정의하는 것이 좋습니다:
export type RestoreOption = keyof typeof RESTORE_COSTS;
Also applies to: 15-20
const calculateTotalPrice = () => { | ||
return books.reduce((total, book) => { | ||
const pageCost = book.pages * PRICE_PER_PAGE | ||
const restoreCost = | ||
book.restoreOption === 'loose' | ||
? 5000 | ||
: book.restoreOption === 'spiral' | ||
? 2500 | ||
: 0 | ||
|
||
return total + pageCost + restoreCost | ||
}, 0) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
가격 계산 로직에 대한 에러 처리가 필요합니다.
현재 calculateTotalPrice
함수는 다음과 같은 엣지 케이스를 처리하지 않고 있습니다:
- 음수 페이지 수
- 유효하지 않은
restoreOption
값 undefined
또는null
값
다음과 같이 개선하는 것을 추천드립니다:
const calculateTotalPrice = () => {
return books.reduce((total, book) => {
if (!book.pages || book.pages < 0) {
console.error(`Invalid page count for book: ${book.id}`);
return total;
}
const pageCost = book.pages * PRICE_PER_PAGE;
const restoreCost = RESTORE_COSTS[book.restoreOption as RestoreOption] ?? RESTORE_COSTS.default;
return total + pageCost + restoreCost;
}, 0);
};
<Icon | ||
id="logo" | ||
width={150} | ||
height={66} | ||
className={clsx("opacity-60")} | ||
/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
로고의 접근성 개선이 필요합니다
현재 구현상의 문제점:
- opacity-60 클래스로 인한 시각적 대비 저하
- 스크린리더 사용자를 위한 aria-label 부재
개선 제안:
<Icon
id="logo"
width={150}
height={66}
- className={clsx("opacity-60")}
+ className={clsx("opacity-75")}
+ aria-label="툭스캔 로고"
/>
- opacity 값을 0.75로 높여 가시성 개선
- aria-label 추가로 스크린리더 지원
🔗 Analysis chain
아이콘 투명도 적용이 적절합니다만, 접근성 고려가 필요합니다.
opacity-60 클래스 적용으로 시각적 효과는 개선되었으나, 로고의 가시성이 저하될 수 있습니다. 접근성 관점에서 검토가 필요합니다.
다음 사항들을 확인해주세요:
- 로고의 대비율(contrast ratio)이 WCAG 기준을 충족하는지
- 스크린리더 사용자를 위한 적절한 대체 텍스트가 제공되는지
아이콘에 aria-label 속성을 추가하는 것을 고려해보세요:
<Icon
id="logo"
width={150}
height={66}
className={clsx("opacity-60")}
+ aria-label="툭스캔 로고"
/>
🏁 Scripts executed
The following scripts were executed for the analysis:
Error: No shell scripts found
Script:
#!/bin/bash
# Find Icon component implementation
ast-grep --pattern 'export $_ function Icon'
# Find other Icon usages with aria-label
rg "aria-label.*Icon" -A 2 -B 2
# Check Banner3 component's full implementation for background context
cat packages/components/ui/Banner/Banner3.tsx
# Find similar logo implementations
rg "id=\"logo\"" -A 2 -B 2
Length of output: 3680
content: [ | ||
"./client/user/src/**/*.{js,ts,jsx,tsx,mdx}", | ||
"./client/admin/src/**/*.{js,ts,jsx,tsx,mdx}", | ||
"./packages/**/*.{js,ts,jsx,tsx,mdx}", | ||
], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Tailwind 설정에서 수정이 필요한 부분이 있습니다.
client/admin/src
디렉토리가 존재하지 않습니다. 해당 경로를 제거하거나 올바른 경로로 수정해주세요.- 현재 codebase에서
.mdx
파일이 사용되지 않고 있습니다. 불필요한 경우 확장자 목록에서 제거하는 것을 고려해주세요.
🔗 Analysis chain
컨텐츠 경로 설정이 올바른지 확인이 필요합니다.
현재 설정된 경로들이 모노레포 구조의 실제 파일 위치와 일치하는지 확인해주세요. 특히 mdx
확장자 파일의 사용 여부를 확인해주세요.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify the existence of source files in the specified paths
echo "Checking user client files..."
fd -e js -e ts -e jsx -e tsx -e mdx . client/user/src/
echo "Checking admin client files..."
fd -e js -e ts -e jsx -e tsx -e mdx . client/admin/src/
echo "Checking package files..."
fd -e js -e ts -e jsx -e tsx -e mdx . packages/
Length of output: 3329
fontFamily: { | ||
sans: [ | ||
"Pretendard", | ||
"ui-sans-serif", | ||
"system-ui", | ||
"Arial", | ||
"sans-serif", | ||
], | ||
pretendard: ["Pretendard", "sans-serif"], | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
폰트 설정에서 몇 가지 개선이 필요합니다.
- 폰트 파일 경로가 일치하지 않습니다:
- 현재 @font-face에서는
/src/assets/fonts/
를 참조하지만, 실제 파일은packages/assets/fonts/
에 있습니다.
- 현재 @font-face에서는
- 최적화 제안:
- PretendardVariable.ttf (가변 폰트)를 활용하여 더 효율적인 폰트 제공이 가능합니다.
- 폰트 preload 태그 추가를 고려해주세요.
- 한글 폰트 대체(fallback) 제안:
'Malgun Gothic', '맑은 고딕'
을 fallback으로 추가해주세요.
예시:
fontFamily: {
sans: [
"Pretendard",
"Malgun Gothic",
"맑은 고딕",
"ui-sans-serif",
"system-ui",
"Arial",
"sans-serif",
],
}
🔗 Analysis chain
폰트 패밀리 설정에 대한 검토가 필요합니다.
- Pretendard 폰트가 프로젝트에 포함되어 있는지 확인해주세요.
- 폰트 로딩 전략(font loading strategy)을 고려해보세요.
- 한글 폰트를 위한 추가적인 대체 폰트(fallback)를 고려해보세요.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check for font files and font loading configuration
echo "Checking for font files..."
fd -e woff -e woff2 -e ttf -e otf . packages/
echo "Checking for font loading configuration..."
rg -l "font-face" .
Length of output: 300
Script:
#!/bin/bash
# Check font-face declaration and loading strategy
echo "Font-face declaration in globals.css:"
cat packages/styles/globals.css | grep -A 5 "@font-face"
echo -e "\nChecking for font preload tags:"
rg "rel=.preload.*font" .
echo -e "\nChecking for Korean fallback fonts:"
rg "Malgun Gothic|맑은 고딕|Nanum|나눔|Dotum|돋움|Gulim|굴림" .
Length of output: 806
export const cn = (...classes: (string | undefined | null | false)[]): string => | ||
classNames(classes) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
classNames 함수 사용 방식 개선 필요
classNames 함수에 배열을 직접 전달하면 예상치 못한 결과가 발생할 수 있습니다.
다음과 같이 수정하는 것을 추천드립니다:
-export const cn = (...classes: (string | undefined | null | false)[]): string =>
- classNames(classes)
+export const cn = (...classes: (string | undefined | null | false)[]): string =>
+ classNames(...classes)
📝 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 cn = (...classes: (string | undefined | null | false)[]): string => | |
classNames(classes) | |
export const cn = (...classes: (string | undefined | null | false)[]): string => | |
classNames(...classes) |
{type === "password" && ( | ||
<button onClick={() => setIsPasswordVisible(!isPasswordVisible)}> | ||
<Icon | ||
id={isPasswordVisible ? "openEyes" : "closeEyes"} | ||
width={24} | ||
height={24} | ||
className="text-black-800" | ||
/> | ||
</button> | ||
)} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
비밀번호 토글 버튼 접근성 개선 필요
비밀번호 표시/숨김 토글 버튼에 접근성 속성이 누락되었습니다.
다음과 같은 개선사항을 제안드립니다:
{type === "password" && (
- <button onClick={() => setIsPasswordVisible(!isPasswordVisible)}>
+ <button
+ type="button"
+ onClick={() => setIsPasswordVisible(!isPasswordVisible)}
+ aria-label={isPasswordVisible ? "비밀번호 숨기기" : "비밀번호 표시"}
+ aria-pressed={isPasswordVisible}
+ >
<Icon
id={isPasswordVisible ? "openEyes" : "closeEyes"}
width={24}
height={24}
- className="text-black-800"
+ className="text-black-800"
+ aria-hidden="true"
/>
</button>
)}
📝 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.
{type === "password" && ( | |
<button onClick={() => setIsPasswordVisible(!isPasswordVisible)}> | |
<Icon | |
id={isPasswordVisible ? "openEyes" : "closeEyes"} | |
width={24} | |
height={24} | |
className="text-black-800" | |
/> | |
</button> | |
)} | |
{type === "password" && ( | |
<button | |
type="button" | |
onClick={() => setIsPasswordVisible(!isPasswordVisible)} | |
aria-label={isPasswordVisible ? "비밀번호 숨기기" : "비밀번호 표시"} | |
aria-pressed={isPasswordVisible} | |
> | |
<Icon | |
id={isPasswordVisible ? "openEyes" : "closeEyes"} | |
width={24} | |
height={24} | |
className="text-black-800" | |
aria-hidden="true" | |
/> | |
</button> | |
)} |
<input | ||
ref={inputRef} | ||
value={value} | ||
onFocus={() => setIsFocused(true)} | ||
onBlur={() => setIsFocused(false)} | ||
onChange={onChange} | ||
disabled={disabled} | ||
type={isPasswordVisible ? "text" : "password"} | ||
placeholder={placeholder} | ||
className="w-full bg-transparent text-black-800 outline-none" | ||
/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
input 요소의 타입 속성 개선 필요
현재 구현은 모든 input 타입을 text나 password로만 처리하고 있습니다.
다음과 같이 input 타입을 올바르게 매핑하는 것을 추천드립니다:
+const getInputType = (type: InputType, isPasswordVisible: boolean) => {
+ if (type === "password") return isPasswordVisible ? "text" : "password";
+ if (type === "number") return "number";
+ return "text";
+};
<input
ref={inputRef}
value={value}
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
onChange={onChange}
disabled={disabled}
- type={isPasswordVisible ? "text" : "password"}
+ type={getInputType(type, isPasswordVisible)}
placeholder={placeholder}
className="w-full bg-transparent text-black-800 outline-none"
/>
Committable suggestion skipped: line range outside the PR's diff.
#️⃣연관된 이슈
#28
📝작업 내용
@shared/tailwind, @shared/eslint, @shared/prettier 등.
Summary by CodeRabbit
릴리스 노트
새로운 기능
개선 사항
마이그레이션