-
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
[Feat] 홈 화면 플로우 구현 #71
Conversation
📝 WalkthroughWalkthrough이번 PR은 프로젝트의 의존성과 UI 구조를 정리하는 변경 사항을 포함합니다. Changes
Sequence Diagram(s)sequenceDiagram
participant U as 사용자
participant CC as ClipboardContainer
participant UC as useClipboard
participant API as Clipboard API
participant T as Toaster
U->>CC: 복사 버튼 클릭
CC->>UC: handleCopy 호출
UC->>API: window.clipboard.writeText 호출
API-->>UC: 복사 완료 응답
UC-->>CC: isCopied 상태 업데이트
CC->>T: 성공 토스트 표시
Suggested labels
Suggested reviewers
Tip ⚡🧪 Multi-step agentic review comment chat (experimental)
📜 Recent review detailsConfiguration used: .coderabbit.yml 📒 Files selected for processing (3)
🧰 Additional context used🧬 Code Definitions (3)src/features/home/components/BusinessCard/AddCard.tsx (2)
src/app/page.tsx (6)
src/features/home/hooks/prefetches/useCardPrefetch.ts (1)
🔇 Additional comments (10)
✨ 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
Documentation and Community
|
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: 4
🧹 Nitpick comments (7)
src/features/home/containers/HeaderContainer.tsx (1)
10-10
: 불필요한 빈 줄을 제거하세요.파일 끝에 있는 불필요한 빈 줄을 제거하는 것이 좋습니다.
); }; -
src/features/home/components/icons/CopyLinkIcon.tsx (1)
1-20
: SVG 아이콘 구현이 잘 되었습니다.
CopyLinkIcon
컴포넌트가 SVG 표준을 잘 따르고 있으며 깔끔하게 구현되었습니다. clipPath를 사용하여 아이콘 영역을 적절히 제한하고 있습니다.개선 제안: 아이콘의 크기나 색상을 커스터마이징할 수 있도록 props를 받도록 확장하면 더 유연하게 사용할 수 있을 것입니다.
- export const CopyLinkIcon = () => { + export const CopyLinkIcon = ({ width = 16, height = 16, color = "white", ...props }) => { return ( - <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> + <svg width={width} height={height} viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}> <g clipPath="url(#clip0_994_20373)"> <path d="M8.79335 5.79202C9.22657 5.99886 9.60382 6.30664 9.89342 6.68951C10.183 7.07238 10.3765 7.51916 10.4576 7.99232C10.5388 8.46548 10.5052 8.9512 10.3596 9.40867C10.2141 9.86614 9.96093 10.282 9.62135 10.6214L6.62135 13.6213C6.05874 14.184 5.29567 14.5 4.50002 14.5C3.70436 14.5 2.9413 14.184 2.37869 13.6213C1.81607 13.0587 1.5 12.2957 1.5 11.5C1.5 10.7044 1.81607 9.9413 2.37869 9.37868L3.55002 8.20735M12.45 7.79268L13.6213 6.62135C14.184 6.05874 14.5 5.29567 14.5 4.50002C14.5 3.70436 14.184 2.9413 13.6213 2.37869C13.0587 1.81607 12.2957 1.5 11.5 1.5C10.7044 1.5 9.9413 1.81607 9.37868 2.37869L6.37868 5.37868C6.03911 5.71802 5.78593 6.13389 5.64041 6.59137C5.49488 7.04884 5.46127 7.53456 5.5424 8.00772C5.62352 8.48087 5.81701 8.92765 6.10661 9.31053C6.39621 9.6934 6.77347 10.0012 7.20669 10.208" - stroke="white" + stroke={color} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" /> </g> <defs> <clipPath id="clip0_994_20373"> <rect width="16" height="16" fill="white" /> </clipPath> </defs> </svg> ); };src/features/home/containers/ClipboardContainer.tsx (1)
18-31
: 불필요한 프래그먼트와 접근성 개선 필요
- 컴포넌트가 단일 div만 포함하고 있어 프래그먼트(
<>...</>
)가 불필요합니다.- 버튼에 접근성 속성(aria-label)이 없습니다.
다음과 같이 개선할 수 있습니다:
return ( - <> <div className="mt-6 flex w-full items-center justify-center"> <button className="bg flex h-[40px] w-[252px] items-center justify-center gap-1 rounded-full bg-primary" onClick={onClickCopyClipboard} + aria-label="내 명함 링크 복사하기" > <CopyLinkIcon /> <Typography variant="body-4">내 명함 링크 복사하기</Typography> </button> </div> - </> );src/features/home/components/BusinessCard/Background/HomeBackground.tsx (2)
8-10
: 반응형 디자인에 대한 브라우저 호환성 고려 필요
h-dvh
는 최신 CSS 기능으로, 오래된 브라우저에서는 지원되지 않을 수 있습니다.호환성을 위해 폴백(fallback) 값을 제공하는 것이 좋습니다:
- <div {...restProps} className={cn('relative h-dvh w-full', className)}> + <div {...restProps} className={cn('relative h-screen h-dvh w-full', className)}>이렇게 하면
h-dvh
를 지원하지 않는 브라우저에서도h-screen
을 사용할 수 있습니다.
11-31
: SVG의 접근성 및 성능에 대한 개선 필요SVG에
aria-hidden
속성이 없고, 불필요하게 고정된 마진 값(ml-[44px], mt-[88px])을 사용하고 있습니다. 이는 다양한 화면 크기에서 문제를 일으킬 수 있습니다.다음과 같이 개선할 수 있습니다:
<svg - className="ml-[44px] mt-[88px]" + className="absolute left-[10%] top-[15%]" width="340" height="354" viewBox="0 0 340 354" fill="none" + aria-hidden="true" xmlns="http://www.w3.org/2000/svg" >이렇게 하면 다양한 화면 크기에서도 SVG의 상대적 위치가 유지되며, 스크린 리더에서는 무시됩니다.
src/app/page.tsx (1)
14-25
: 레이아웃 구조가 절대 위치에 과도하게 의존하고 있습니다.현재 레이아웃이
absolute
포지셔닝에 크게 의존하고 있어 향후 유지보수나 확장 시 문제가 발생할 수 있습니다. 또한translate
변환을 사용한 중앙 정렬은 성능에 영향을 줄 수 있습니다.좀 더 유연하고 선언적인 레이아웃 구조를 사용하는 것이 좋습니다:
<> - <div className="relative mx-auto h-dvh w-full max-w-[600px] justify-center"> - <HomeBackground className="absolute" /> - <HeaderContainer /> - <section className="absolute left-1/2 top-1/2 h-auto w-full -translate-x-1/2 -translate-y-1/2 pb-20"> - <CardContainer /> - <ClipboardContainer /> - </section> - <Navbar /> + <div className="relative mx-auto h-screen h-dvh w-full max-w-[600px] flex flex-col"> + <HomeBackground className="absolute inset-0" /> + <HeaderContainer className="z-10" /> + <section className="flex-1 flex flex-col justify-center items-center pb-20 z-10"> + <CardContainer /> + <ClipboardContainer /> + </section> + <Navbar className="z-10" /> </div> <Toaster position="top-center" /> </>이렇게 하면 Flexbox를 활용하여 더 안정적인 레이아웃을 구성할 수 있으며, z-index를 명시적으로 지정하여 레이어 순서를 명확히 할 수 있습니다.
src/features/home/components/BusinessCard/Background/AddCradBackground.tsx (1)
1-15
: SVG 배경 컴포넌트 구현이 잘 되었습니다만 몇 가지 개선 사항이 있습니다.SVG 기반 배경 구현은 좋은 접근이지만, 다음과 같은 개선사항을 고려해보세요:
- 파일명에 오타가 있습니다:
AddCradBackground.tsx
가 아닌AddCardBackground.tsx
로 수정해야 합니다.- 접근성 측면에서, 이 SVG가 장식용이라면
aria-hidden="true"
를 추가하는 것이 좋습니다.- SVG path의 좌표가 음수 값(-49)을 포함하고 있어 일부 환경에서 잘려 보일 수 있습니다. viewBox 설정을 조정하여 전체 그래픽이 제대로 표시되도록 해보세요.
- <svg width="123" height="137" viewBox="0 0 123 137" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}> + <svg width="123" height="137" viewBox="0 0 123 186" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" {...props}>
📜 Review details
Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (12)
package.json
(0 hunks)src/app/page.tsx
(1 hunks)src/features/home/components/BusinessCard/AddCard.tsx
(1 hunks)src/features/home/components/BusinessCard/Background/AddCradBackground.tsx
(1 hunks)src/features/home/components/BusinessCard/Background/HomeBackground.tsx
(1 hunks)src/features/home/components/icons/CopyLinkIcon.tsx
(1 hunks)src/features/home/containers/ClipboardContainer.tsx
(1 hunks)src/features/home/containers/HeaderContainer.tsx
(1 hunks)src/features/home/hooks/sample.ts
(0 hunks)src/features/home/hooks/useClipboard.ts
(1 hunks)src/shared/ui/Navigation/index.tsx
(1 hunks)tailwind.config.ts
(1 hunks)
💤 Files with no reviewable changes (2)
- package.json
- src/features/home/hooks/sample.ts
🔇 Additional comments (8)
tailwind.config.ts (1)
140-140
:✅ Verification successful
@tailwindcss/line-clamp
플러그인 제거 확인
@tailwindcss/line-clamp
플러그인이 제거되었습니다. Tailwind CSS v3.3.0 이상을 사용 중이라면 이 기능은 이미 코어에 통합되어 있어 플러그인이 필요 없습니다. 그러나 v3.3.0 미만을 사용 중이거나 기존에 이 플러그인의 line-clamp 유틸리티(line-clamp-2
,line-clamp-3
등)를 사용하던 컴포넌트가 있다면 영향을 받을 수 있습니다.
🏁 Script executed:
#!/bin/bash # 현재 tailwind CSS 버전을 확인하고 line-clamp 유틸리티 사용 여부 검사 # tailwind 버전 확인 echo "Tailwind CSS 버전 확인:" cat package.json | grep tailwindcss # line-clamp 유틸리티 사용 검사 echo -e "\nline-clamp 유틸리티 사용 컴포넌트 검사:" rg "line-clamp-[0-9]" --type ts --type tsxLength of output: 342
검증 결과: @tailwindcss/line-clamp 플러그인 제거 유효
Tailwind CSS 버전이 현재 ^3.4.1로 확인되었으며, 이는 플러그인 기능이 코어에 통합된 버전입니다.
기존 검증 스크립트에서 TSX 파일 처리 오류가 발생했으므로, TS 및 TSX 파일을 모두 대상으로 하는 재검증 스크립트를 실행해보세요. 예시 스크립트:
#!/bin/bash echo "line-clamp 유틸리티 사용 컴포넌트 검사:" rg "line-clamp-[0-9]" -g "*.ts" -g "*.tsx" || true이 재검증 결과 코드베이스 내에서
line-clamp-[0-9]
패턴을 사용하는 컴포넌트가 발견되지 않는다면, 플러그인 제거가 올바르게 적용된 것으로 판단됩니다.src/features/home/containers/HeaderContainer.tsx (1)
1-9
: 컴포넌트 구현이 잘 되었습니다.
HeaderContainer
컴포넌트가 간결하고 명확하게 구현되었습니다. 단일 책임 원칙을 잘 따르고 있습니다.고려할 점:
<section>
요소에 스타일링이나 className이 없는데, 의도적인 것인지 확인해보세요.- 향후 재사용성을 고려하여 props를 받을 수 있도록 확장하는 것을 고려해볼 수 있습니다.
src/shared/ui/Navigation/index.tsx (1)
32-32
: 네비게이션 바 포지셔닝 변경이 적절합니다.네비게이션 바를
relative
에서fixed bottom-0
으로 변경한 것은 모바일 중심 UI에 매우 적합합니다. 사용자가 스크롤하더라도 항상 하단에 네비게이션 바가 고정되어 접근성이 향상됩니다. z-index 값(100)을 추가한 것도 다른 요소들과의 적절한 계층 구조를 보장해 줍니다.src/features/home/components/BusinessCard/AddCard.tsx (1)
9-10
: 배경 구현이 개선되었습니다.배경 투명도를 0.2에서 0.1로 변경하고 전용 배경 컴포넌트를 추가한 것은 디자인 개선에 도움이 됩니다. absolute 포지셔닝을 사용하여 층위를 적절히 구성한 점도 좋습니다.
해당 UI가 디자이너의 의도와 일치하는지 확인해보세요. PR 설명에 "design elements that need further development"가 언급되어 있으므로, 디자인팀과 협의가 필요할 수 있습니다.
src/features/home/containers/ClipboardContainer.tsx (1)
1-9
: 클라이언트 컴포넌트와 의존성이 명확하게 정의되어 있습니다.'use client' 지시문을 사용하고 필요한 의존성들을 잘 가져왔습니다. toast 알림을 위한 sonner, 타이포그래피 컴포넌트, 아이콘 및 클립보드 기능을 위한 훅이 모두 적절하게 임포트되었습니다.
src/features/home/components/BusinessCard/Background/HomeBackground.tsx (1)
1-7
: 컴포넌트 속성 타입이 적절히 정의되어 있습니다.React의 HTMLAttributes를 활용해 컴포넌트 props의 타이핑을 잘 구현했습니다. 또한 className을 확장 가능하도록 cn 유틸리티를 사용한 점이 좋습니다.
src/features/home/hooks/useClipboard.ts (1)
1-5
: 훅의 기본 구조가 잘 설계되었습니다.useState를 활용하여 클립보드 상태를 관리하는 기본 구조가 잘 설계되었습니다.
src/app/page.tsx (1)
4-11
: 컴포넌트 임포트가 적절히 구성되었습니다.필요한 모든 컴포넌트들이 명확하게 임포트되어 있습니다. Toaster 및 새로 추가된 홈 화면 관련 컴포넌트들이 잘 정의되어 있습니다.
export const ClipboardContainer = () => { | ||
const { handleCopy } = useClipboard(); | ||
|
||
const onClickCopyClipboard = () => { | ||
handleCopy(); | ||
toast.success('명함 링크를 클립보드에 복사했어요.'); | ||
}; | ||
|
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
클립보드 기능 구현에 에러 처리가 누락되었습니다.
handleCopy
함수 호출 시 클립보드 API가 실패할 경우에 대한 에러 처리가 되어 있지 않습니다. 특히 일부 브라우저나 권한이 없는 경우 문제가 발생할 수 있습니다.
다음과 같이 에러 처리를 추가하는 것을 권장합니다:
const onClickCopyClipboard = () => {
- handleCopy();
- toast.success('명함 링크를 클립보드에 복사했어요.');
+ try {
+ handleCopy();
+ toast.success('명함 링크를 클립보드에 복사했어요.');
+ } catch (error) {
+ toast.error('클립보드 복사에 실패했습니다. 다시 시도해주세요.');
+ console.error('클립보드 복사 실패:', error);
+ }
};
📝 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 ClipboardContainer = () => { | |
const { handleCopy } = useClipboard(); | |
const onClickCopyClipboard = () => { | |
handleCopy(); | |
toast.success('명함 링크를 클립보드에 복사했어요.'); | |
}; | |
export const ClipboardContainer = () => { | |
const { handleCopy } = useClipboard(); | |
const onClickCopyClipboard = () => { | |
try { | |
handleCopy(); | |
toast.success('명함 링크를 클립보드에 복사했어요.'); | |
} catch (error) { | |
toast.error('클립보드 복사에 실패했습니다. 다시 시도해주세요.'); | |
console.error('클립보드 복사 실패:', error); | |
} | |
}; | |
// ... other component logic | |
}; |
const handleCopy = () => { | ||
navigator.clipboard.writeText(window.location.href).then(() => setIsCopied(true)); | ||
setTimeout(() => setIsCopied(false), 1500); | ||
}; |
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.
클립보드 API 사용 시 에러 처리 및 타이밍 문제
현재 구현에는 두 가지 주요 문제가 있습니다:
- 클립보드 API 실패 시 에러 처리가 없습니다.
- setTimeout이 Promise의 성공 여부와 관계없이 항상 실행됩니다.
더 견고한 구현을 위해 다음과 같이 수정하세요:
const handleCopy = () => {
- navigator.clipboard.writeText(window.location.href).then(() => setIsCopied(true));
- setTimeout(() => setIsCopied(false), 1500);
+ navigator.clipboard.writeText(window.location.href)
+ .then(() => {
+ setIsCopied(true);
+ setTimeout(() => setIsCopied(false), 1500);
+ })
+ .catch((error) => {
+ console.error('클립보드 복사 실패:', error);
+ throw error; // 호출자가 에러를 처리할 수 있도록 재발생
+ });
};
📝 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 handleCopy = () => { | |
navigator.clipboard.writeText(window.location.href).then(() => setIsCopied(true)); | |
setTimeout(() => setIsCopied(false), 1500); | |
}; | |
const handleCopy = () => { | |
navigator.clipboard.writeText(window.location.href) | |
.then(() => { | |
setIsCopied(true); | |
setTimeout(() => setIsCopied(false), 1500); | |
}) | |
.catch((error) => { | |
console.error('클립보드 복사 실패:', error); | |
throw error; // 호출자가 에러를 처리할 수 있도록 재발생 | |
}); | |
}; |
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.
@semnil5202 저도 이 의견에 동의합니다. 에러가 발생할 수 있는 포인트에 try catch 문으로 감싸주어 예외 처리를 하면 좋을거 같습니다. 물론 throw로 error를 던지는 부분은 고려 대상이긴합니다.
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.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
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.
handleError와 같은 공통 에러처리 함수를 만들면 편리하게 사용 가능할 것 같아요.
try {
// ...
} catch (e) {
handleError(e);
}
handleError의 경우, 개발 환경에서만 throw로 디버깅을 도울 수 있게 하고,
live환경에서는 return 되어 동작에 방해가 없도록 하면 좋을 것 같습니다. (또는 로그로만 에러를 확인할 수 있게 할 수 있겠네요)
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.
좋은 관점이네요~~ 👍
export const useClipboard = () => { | ||
const [isCopied, setIsCopied] = useState(false); | ||
|
||
const handleCopy = () => { | ||
navigator.clipboard.writeText(window.location.href).then(() => setIsCopied(true)); | ||
setTimeout(() => setIsCopied(false), 1500); | ||
}; | ||
|
||
return { isCopied, handleCopy }; | ||
}; |
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
브라우저 호환성 검사 필요
일부 브라우저나 환경(특히 오래된 버전이나 보안 제한이 있는 환경)에서는 navigator.clipboard
API가 지원되지 않을 수 있습니다.
브라우저 호환성을 확인하고 폴백 메커니즘을 추가하는 것이 좋습니다:
export const useClipboard = () => {
const [isCopied, setIsCopied] = useState(false);
const handleCopy = () => {
+ // 클립보드 API 지원 확인
+ if (!navigator.clipboard) {
+ console.error('클립보드 API가 지원되지 않습니다.');
+ // 폴백: 텍스트 선택 및 복사 시도
+ try {
+ const tempInput = document.createElement('input');
+ tempInput.value = window.location.href;
+ document.body.appendChild(tempInput);
+ tempInput.select();
+ document.execCommand('copy');
+ document.body.removeChild(tempInput);
+ setIsCopied(true);
+ setTimeout(() => setIsCopied(false), 1500);
+ return;
+ } catch (err) {
+ console.error('폴백 복사 메커니즘 실패:', err);
+ throw new Error('클립보드 복사가 지원되지 않습니다.');
+ }
+ }
+
navigator.clipboard.writeText(window.location.href)
.then(() => {
setIsCopied(true);
setTimeout(() => setIsCopied(false), 1500);
})
.catch((error) => {
console.error('클립보드 복사 실패:', error);
throw error;
});
};
return { isCopied, handleCopy };
};
📝 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 useClipboard = () => { | |
const [isCopied, setIsCopied] = useState(false); | |
const handleCopy = () => { | |
navigator.clipboard.writeText(window.location.href).then(() => setIsCopied(true)); | |
setTimeout(() => setIsCopied(false), 1500); | |
}; | |
return { isCopied, handleCopy }; | |
}; | |
export const useClipboard = () => { | |
const [isCopied, setIsCopied] = useState(false); | |
const handleCopy = () => { | |
// 클립보드 API 지원 확인 | |
if (!navigator.clipboard) { | |
console.error('클립보드 API가 지원되지 않습니다.'); | |
// 폴백: 텍스트 선택 및 복사 시도 | |
try { | |
const tempInput = document.createElement('input'); | |
tempInput.value = window.location.href; | |
document.body.appendChild(tempInput); | |
tempInput.select(); | |
document.execCommand('copy'); | |
document.body.removeChild(tempInput); | |
setIsCopied(true); | |
setTimeout(() => setIsCopied(false), 1500); | |
return; | |
} catch (err) { | |
console.error('폴백 복사 메커니즘 실패:', err); | |
throw new Error('클립보드 복사가 지원되지 않습니다.'); | |
} | |
} | |
navigator.clipboard.writeText(window.location.href) | |
.then(() => { | |
setIsCopied(true); | |
setTimeout(() => setIsCopied(false), 1500); | |
}) | |
.catch((error) => { | |
console.error('클립보드 복사 실패:', error); | |
throw error; | |
}); | |
}; | |
return { isCopied, handleCopy }; | |
}; |
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.
이 부분 적용해두겠습니다. 추가로 URL 복사가 홈이 아니라 -> 사용자별 명함 링크로 변경되어야함
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.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for code related to business card links or user-specific URLs
rg -A 5 -B 5 "business card link|명함 링크|user.*link|user.*card" --ignore-case
Length of output: 1810
🏁 Script executed:
#!/bin/bash
# Look for more details about user cards and routing patterns
rg -A 5 -B 5 "user.*(card|profile|detail)" --ignore-case
# Search for route definitions for user cards or profiles
rg -A 5 -B 5 "(route|path).*user|user.*(route|path)" --ignore-case
# Search for API endpoints related to user cards
rg -A 3 -B 3 "api.*user|user.*api" --ignore-case
Length of output: 4727
@semnil5202 window.location.href
를 사용하는 대신 사용자별 명함 링크를 복사하도록 useClipboard
훅을 수정해야겠네요. 다음과 같이 수정하는 것을 제안합니다:
// useClipboard.ts
export const useClipboard = () => {
const [isCopied, setIsCopied] = useState(false);
const handleCopy = (url?: string) => {
const textToCopy = url || window.location.href;
// 클립보드 API 지원 확인
if (!navigator.clipboard) {
console.error('클립보드 API가 지원되지 않습니다.');
// 폴백: 텍스트 선택 및 복사 시도
try {
const tempInput = document.createElement('input');
tempInput.value = textToCopy;
document.body.appendChild(tempInput);
tempInput.select();
document.execCommand('copy');
document.body.removeChild(tempInput);
setIsCopied(true);
setTimeout(() => setIsCopied(false), 1500);
return;
} catch (err) {
console.error('폴백 복사 메커니즘 실패:', err);
throw new Error('클립보드 복사가 지원되지 않습니다.');
}
}
navigator.clipboard.writeText(textToCopy)
.then(() => {
setIsCopied(true);
setTimeout(() => setIsCopied(false), 1500);
})
.catch((error) => {
console.error('클립보드 복사 실패:', error);
throw error;
});
};
return { isCopied, handleCopy };
};
그리고 ClipboardContainer에서는 다음과 같이 사용합니다:
// ClipboardContainer.tsx의 onClickCopyClipboard 함수 수정
const onClickCopyClipboard = () => {
// 사용자 ID 또는 필요한 파라미터로 URL 생성
const userCardUrl = `${window.location.origin}/users/{userId}/card`; // 실제 URL 구조에 맞게 수정 필요
handleCopy(userCardUrl);
toast.success('명함 링크를 클립보드에 복사했어요.');
};
사용자별 명함 링크 URL 패턴은 프로젝트의 라우팅 구조에 따라 적절히 수정해야 합니다.
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.
너무 수고 많으셨어요🤩 궁금한 점은 코멘트로 남겨두었어요!
<HeaderContainer /> | ||
<section className="absolute left-1/2 top-1/2 h-auto w-full -translate-x-1/2 -translate-y-1/2 pb-20"> | ||
<CardContainer /> | ||
<ClipboardContainer /> |
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.
오... 확실히 Container로 한 번 감싸주니까 코드도 엄청 깔끔해지는군요..! 참고할게요!!
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.
현재 CopyLinkIcon이 svg인데, 이를 컴포넌트로 만든 이유를 알 수 있을까요? 만약 특별한 이유가 없다면, 아이콘을 public/icons와 같은 폴더에 넣고 필요할 때 import 해서 사용하는 방식이 더 효율적일 수 있을 것 같아서요!
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.
@youngju6143 개인적으로 svg 파일을 정적으로 사용할 때, 동적으로 사용할 때의 장단점이 명확한 것 같아요.
상황에 따라 취사선택하면 좋을 것 같습니다.
icons/와 같은 디렉토리에 넣어놓고 정적으로 파일을 Import한다면, 불필요 번들 크기를 줄일 수 있고 렌더링 성능이 약간이나마 개선될 수 있을 것 같아요.
컴포넌트로써 동적으로 사용하게 된다면 스타일과 같은 요소들의 커스터마이징이 용이하고, 컴포넌트의 특성을 활용하여 다양하게 재조합하여 사용할 수 있습니다.
제 경우에는 복잡한 로고 같은 svg 파일은 정적으로, 화살표나 아이콘 같이 다양한 상황에서 사용되는 아이콘은 컴포넌트로 만들어 사용합니다.
예시로 화살표 아이콘은 컴포넌트로 만들어 direction이나 fill, stroke 등을 바꿔가면서 사용했던 것 같아요.
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.
고생하셨습니다~~
const handleCopy = () => { | ||
navigator.clipboard.writeText(window.location.href).then(() => setIsCopied(true)); | ||
setTimeout(() => setIsCopied(false), 1500); | ||
}; |
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.
@semnil5202 저도 이 의견에 동의합니다. 에러가 발생할 수 있는 포인트에 try catch 문으로 감싸주어 예외 처리를 하면 좋을거 같습니다. 물론 throw로 error를 던지는 부분은 고려 대상이긴합니다.
너무 고생하셨습니다!! 👍👍 |
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.
👍
const handleCopy = () => { | ||
navigator.clipboard.writeText(window.location.href).then(() => setIsCopied(true)); | ||
setTimeout(() => setIsCopied(false), 1500); | ||
}; |
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.
handleError와 같은 공통 에러처리 함수를 만들면 편리하게 사용 가능할 것 같아요.
try {
// ...
} catch (e) {
handleError(e);
}
handleError의 경우, 개발 환경에서만 throw로 디버깅을 도울 수 있게 하고,
live환경에서는 return 되어 동작에 방해가 없도록 하면 좋을 것 같습니다. (또는 로그로만 에러를 확인할 수 있게 할 수 있겠네요)
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: 0
🧹 Nitpick comments (3)
src/features/home/types/index.ts (3)
1-1
: 타입 이름에 오타가 있습니다.타입 이름
JopType
은 오타로 보입니다. 문맥상JobType
이 더 적절해 보입니다.-export type JopType = 'designer' | 'developer'; +export type JobType = 'designer' | 'developer';이 변경사항이 적용되면
Card
타입의previewInfoType
프로퍼티의 타입도 업데이트해야 합니다.
34-46
: 중복된 타입 구조를 통합하는 것이 좋습니다.
Project
와Content
타입이 동일한 구조를 가지고 있습니다. 코드 중복을 줄이기 위해 공통 타입을 만들어 재사용하는 것이 좋습니다.+type MediaItem = { + title: string; + link: string; + imageUrl: string; + description: string; +}; + -type Project = { - title: string; - link: string; - imageUrl: string; - description: string; -}; +type Project = MediaItem; -type Content = { - title: string; - link: string; - imageUrl: string; - description: string; -}; +type Content = MediaItem;
48-51
: SNS 타입의 'type' 필드를 더 구체적으로 정의해보세요.
SNS
타입의type
필드는 특정 값으로 제한될 수 있습니다. 가능한 SNS 유형을 정의하면 타입 안전성이 더 향상됩니다.+type SNSType = 'instagram' | 'facebook' | 'twitter' | 'linkedin' | 'other'; + type SNS = { - type: string; + type: SNSType; link: string; };
📜 Review details
Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/features/home/hooks/queries/useCardQuery.ts
(1 hunks)src/features/home/types/index.ts
(1 hunks)src/shared/ui/Navigation/index.tsx
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/shared/ui/Navigation/index.tsx
🔇 Additional comments (6)
src/features/home/types/index.ts (2)
3-10
: API 응답 구조를 잘 정의했습니다.API 응답 구조를 명확하게 정의하여 타입 안전성을 높였습니다. 백엔드 API와의 일관성을 유지하는 것이 중요합니다.
12-23
: 카드 타입 구조가 잘 정의되었습니다.카드 데이터를 위한 타입 정의가 상세하게 되어 있습니다.
previewInfoType
이 앞서 정의한JopType
을 참조하고 있으므로,JopType
을JobType
으로 수정할 경우 이 부분도 함께 수정해야 합니다.src/features/home/hooks/queries/useCardQuery.ts (4)
6-6
: 새로운 타입 임포트가 적절하게 추가되었습니다.
MyCardDto
타입을 임포트하여 API 응답에 대한 타입 안전성을 향상시켰습니다.
8-8
: 쿼리 키 이름 변경이 적절합니다.쿼리 키 이름을
MY_CARD_QUERY_KEY
로 변경하여 목적이 더 명확해졌습니다.
10-11
: 함수 서명 간소화와 타입 지정이 잘 되었습니다.
_getCard
함수에서 불필요한 매개변수를 제거하고MyCardDto
타입을 적용하여 코드가 간결해졌습니다.
16-20
: 모의 데이터 사용에 대한 고려가 필요합니다.현재 코드는 실제 API 호출을 수행하지만 결과는 무시하고 모의 데이터(
CARD_MOCK
)를 반환합니다. 이것이 개발 목적으로 의도된 것인지 확인하고, 실제 배포 전에 모의 데이터를 실제 데이터로 대체해야 합니다.또한 API 호출 실패 시 오류 처리 로직이 없습니다. 다음과 같이 개선할 수 있습니다:
export const useCardQuery = () => { - const { data: _ } = useQuery({ + const { data, error, isLoading } = useQuery({ queryKey: [MY_CARD_QUERY_KEY], queryFn: () => _getCard(), + enabled: process.env.NODE_ENV !== 'development', // 개발 환경에서만 API 호출 비활성화 }); - return { data: CARD_MOCK }; + // 개발 환경에서는 모의 데이터 사용, 그 외에는 실제 API 데이터 사용 + return { + data: process.env.NODE_ENV === 'development' ? CARD_MOCK : data, + error, + isLoading + }; };
📌 개요
📋 변경사항
화면
2025-03-17.01.44.58.mov
✅ 체크사항
Summary by CodeRabbit
Summary by CodeRabbit
New Features
HomeBackground
,ClipboardContainer
,HeaderContainer
,AddCardBackground
, 및CopyLinkIcon
컴포넌트가 추가되었습니다.useClipboard
훅이 도입되어 클립보드 기능이 향상되었습니다.Style