-
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: 스켈레톤 적용 #181
base: main
Are you sure you want to change the base?
feat: 스켈레톤 적용 #181
Conversation
Walkthrough이 PR은 UI의 로딩 상태 관리를 개선하기 위해 스켈레톤 컴포넌트를 도입하고 이를 재사용하도록 수정했습니다. History 및 WorkoutCalendar 컴포넌트에서 isLoading 변수를 활용하여 로딩 상태에 따라 Skeleton 또는 실제 콘텐츠를 렌더링하며, 캘린더 및 댓글 섹션 역시 새로운 스켈레톤 컴포넌트를 사용하도록 업데이트되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant U as 사용자
participant H as History 컴포넌트
participant WC as WorkoutCalendar 컴포넌트
participant API as 데이터 API
U->>H: 페이지 로드 요청
H->>API: 사용자 및 히스토리 데이터 요청
API-->>H: 데이터 응답 (지연 가능)
alt 로딩중 (isLoading == true)
H->>Skeleton: Skeleton 렌더링
H->>WC: isLoading 상태 전달
WC->>Skeleton: CalendarSkeleton 렌더링
else 로딩 완료 (isLoading == false)
H->>Text: 텍스트 컴포넌트 렌더링
WC->>Calendar: 실제 달력 콘텐츠 렌더링
end
Assessment against linked issues
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Tip 🌐 Web search-backed reviews and chat
✨ Finishing Touches
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 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: 0
🧹 Nitpick comments (7)
components/Skeleton/CalendarSkeleton.tsx (2)
8-13
: 배열 인덱스를 키로 사용하는 것을 개선해 보세요.현재 배열 인덱스를 키로 사용하고 있는데, 이는 React의 권장 사항에 맞지 않습니다. 고유한 식별자를 생성하는 것이 더 좋은 방법입니다.
다음과 같이 개선해 보세요:
- key={`calendar-skeleton-${rowIndex}`} + key={`calendar-skeleton-row-${Date.now()}-${rowIndex}`}
6-20
: 상수 값을 분리하면 좋을 것 같습니다.매직 넘버들을 상수로 분리하면 코드의 가독성과 유지보수성이 향상될 것 같습니다.
다음과 같이 개선해 보세요:
+const CALENDAR_CONSTANTS = { + ROWS: 5, + ROW_GAP: 10, + HEADER_HEIGHT: 15, + ROW_HEIGHT: 10, + CELL_SIZE: 30, + CELL_COUNT: 7, +} as const; + export default function CalendarSkeleton() { return ( - <View className="mt-[24px] w-full gap-[10px] px-[3px]"> - <Skeleton height={15} /> - {Array.from({ length: 5 }, (_, rowIndex) => ( + <View className="mt-[24px] w-full gap-[10px] px-[3px]"> + <Skeleton height={CALENDAR_CONSTANTS.HEADER_HEIGHT} /> + {Array.from({ length: CALENDAR_CONSTANTS.ROWS }, (_, rowIndex) => ( <View className="gap-[12px]" key={`calendar-skeleton-${rowIndex}`} > - <Skeleton height={10} /> + <Skeleton height={CALENDAR_CONSTANTS.ROW_HEIGHT} /> <View className="flex-row justify-between"> - <Skeleton width={30} height={30} circle count={7} /> + <Skeleton + width={CALENDAR_CONSTANTS.CELL_SIZE} + height={CALENDAR_CONSTANTS.CELL_SIZE} + circle + count={CALENDAR_CONSTANTS.CELL_COUNT} + /> </View> </View> ))}components/Skeleton/Skeleton.tsx (2)
3-9
: Props 타입에 대한 유효성 검사가 필요합니다.Props의 타입은 잘 정의되어 있지만, 런타임에서의 유효성 검사가 없습니다.
다음과 같이 개선해 보세요:
+const isValidCount = (count: number) => count > 0 && Number.isInteger(count); + interface SkeletonProps { className?: string; width?: ViewStyle["width"]; height?: ViewStyle["height"]; - count?: number; + count?: number; // should be positive integer circle?: boolean; }
11-30
: 성능 최적화를 위한 메모이제이션을 고려해보세요.컴포넌트가 자주 리렌더링될 수 있으므로, React.memo를 사용하여 최적화하는 것이 좋습니다.
다음과 같이 개선해 보세요:
-export default function Skeleton({ +const Skeleton = React.memo(function Skeleton({ className = "", width, height, count = 1, circle = false, }: SkeletonProps) { + if (!isValidCount(count)) { + console.warn('Skeleton: count should be a positive integer'); + return null; + } + return ( <> {Array.from({ length: count }, (_, index) => ( <View key={`skeleton-component-${index}of${count}`} className={`animate-pulse bg-gray-20 ${circle ? "rounded-full" : "rounded-[5px]"} ${className}`} style={{ width, height }} aria-hidden={true} /> ))} </> ); -} +}); + +Skeleton.displayName = 'Skeleton'; + +export default Skeleton;components/Skeleton/CommentSkeleton.tsx (2)
4-6
: Props 타입에 대한 유효성 검사가 필요합니다.count prop에 대한 타입은 정의되어 있지만, 런타임에서의 유효성 검사가 없습니다.
다음과 같이 개선해 보세요:
+const MIN_COUNT = 1; +const MAX_COUNT = 10; + +const isValidCount = (count: number) => + count >= MIN_COUNT && count <= MAX_COUNT && Number.isInteger(count); + interface CommentSkeletonProps { - count?: number; + count?: number; // should be between MIN_COUNT and MAX_COUNT }
8-38
: 상수 값을 분리하고 메모이제이션을 적용하면 좋을 것 같습니다.하드코딩된 값들을 상수로 분리하고, 컴포넌트를 메모이제이션하면 코드의 가독성과 성능이 향상될 것 같습니다.
다음과 같이 개선해 보세요:
+const COMMENT_CONSTANTS = { + AVATAR_SIZE: 48, + MENU_SIZE: 28, + USERNAME_WIDTH: 64, + USERNAME_HEIGHT: 16, + TIMESTAMP_WIDTH: 42, + TIMESTAMP_HEIGHT: 13, + CONTENT_HEIGHT: 18, + REPLY_WIDTH: 42, + REPLY_HEIGHT: 14, +} as const; + -export default function CommentSkeleton({ count = 1 }: CommentSkeletonProps) { +const CommentSkeleton = React.memo(function CommentSkeleton({ + count = MIN_COUNT, +}: CommentSkeletonProps) { + if (!isValidCount(count)) { + console.warn( + `CommentSkeleton: count should be between ${MIN_COUNT} and ${MAX_COUNT}` + ); + return null; + } + return ( <> {Array.from({ length: count }, (_, index) => ( <View key={`comment-skeleton-${index}of${count}`} className="mb-4 gap-[13px] pb-[16px]" aria-hidden={true} > <View className="flex-row items-center justify-between gap-[8px]"> - <Skeleton width={48} height={48} circle /> + <Skeleton + width={COMMENT_CONSTANTS.AVATAR_SIZE} + height={COMMENT_CONSTANTS.AVATAR_SIZE} + circle + /> <View className="flex-1 items-start gap-2"> - <Skeleton width={64} height={16} /> - <Skeleton width={42} height={13} /> + <Skeleton + width={COMMENT_CONSTANTS.USERNAME_WIDTH} + height={COMMENT_CONSTANTS.USERNAME_HEIGHT} + /> + <Skeleton + width={COMMENT_CONSTANTS.TIMESTAMP_WIDTH} + height={COMMENT_CONSTANTS.TIMESTAMP_HEIGHT} + /> </View> - <Skeleton width={28} height={28} circle /> + <Skeleton + width={COMMENT_CONSTANTS.MENU_SIZE} + height={COMMENT_CONSTANTS.MENU_SIZE} + circle + /> </View> - <Skeleton width="100%" height={18} /> - <Skeleton width={42} height={14} /> + <Skeleton width="100%" height={COMMENT_CONSTANTS.CONTENT_HEIGHT} /> + <Skeleton + width={COMMENT_CONSTANTS.REPLY_WIDTH} + height={COMMENT_CONSTANTS.REPLY_HEIGHT} + /> </View> ))} </> ); -} +}); + +CommentSkeleton.displayName = 'CommentSkeleton'; + +export default CommentSkeleton;components/comments/CommentsSection.tsx (1)
1-1
: 스켈레톤 컴포넌트 적용이 잘 되었습니다!기존의 복잡한 스켈레톤 UI 구현을 재사용 가능한
CommentSkeleton
컴포넌트로 대체한 것이 좋습니다.하지만
count
prop을 하드코딩된 숫자 대신LIMIT
상수를 사용하면 더 좋을 것 같습니다:- <CommentSkeleton count={5} /> + <CommentSkeleton count={LIMIT} />Also applies to: 301-301
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
app/(protected)/(tabs)/history.tsx
(4 hunks)components/Skeleton/CalendarSkeleton.tsx
(1 hunks)components/Skeleton/CommentSkeleton.tsx
(1 hunks)components/Skeleton/Skeleton.tsx
(1 hunks)components/Skeleton/index.ts
(1 hunks)components/WorkoutCalendar.tsx
(3 hunks)components/comments/CommentItem.tsx
(3 hunks)components/comments/CommentsSection.tsx
(2 hunks)
✅ Files skipped from review due to trivial changes (1)
- components/comments/CommentItem.tsx
🔇 Additional comments (4)
components/Skeleton/index.ts (1)
1-3
: 내보내기가 잘 구성되어 있습니다!컴포넌트 내보내기가 깔끔하게 구성되어 있으며, 배럴(barrel) 패턴을 잘 따르고 있습니다.
components/WorkoutCalendar.tsx (1)
3-3
: 로딩 상태 처리가 깔끔하게 구현되었습니다!
isLoading
prop과CalendarSkeleton
컴포넌트를 사용하여 로딩 상태를 효과적으로 처리했습니다.Also applies to: 21-22, 86-88
app/(protected)/(tabs)/history.tsx (2)
12-12
: 로딩 상태 통합이 잘 되었습니다!사용자 데이터와 히스토리 데이터의 로딩 상태를 하나의
isLoading
변수로 통합하여 관리하는 것이 좋습니다.Also applies to: 50-50
78-85
: 스켈레톤 UI가 일관성 있게 적용되었습니다!제목과 캘린더에 대한 스켈레톤 UI가 적절하게 구현되었습니다.
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.
스켈레톤 고생하셨습니다!
댓글 쪽도 같이 해주셔서 감사합니다! 🙇♂️
댓글 크기가 좀 작군요 안드로이드는 Inspector
가 댓글창보다 뒤에 있어서 제대로 확인을 못했는데 이러면 px
로 바꾸는게 더 좋을거 같긴합니다
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.
스켈레톤 적용 확인했습니다😊😊👍
-
댓글 스켈레톤 디자인 약간 변경
-> 내용 확인했어요.. 개선해주셔서 감사해요 너무 좋아요 꼼꼼한 승헌님 최고🥹 -
댓글 스켈레톤과 댓글의 크기가 다름
-> px로 통일하는게 좋을 것 같습니다!!
📝 PR 설명
스켈레톤을 여러 곳에서 각자 만들어 사용해서 스타일이 통일되지 않는 문제가 있었습니다.
그래서 스켈레톤 컴포넌트를 만들고, 기존 스켈레톤들을 컴포넌트로 분리했습니다.
그리고 기록 페이지의 타이틀
n월 n일 운동 완료!
에도 스켈레톤이 적용되면 좋겠다는 피드백 반영했습니다.🔍 변경사항
Skeleton 컴포넌트
width와 height은 react native의 스타일 지정 방식을 따릅니다.
숫자는 px으로 적용되고, 문자열로 %도 지정 가능합니다.
예시
폴더 구조
Skeleton 폴더 안에 기본적인 스타일이 정의된 Skeleton 컴포넌트와, Skeleton 컴포넌트를 이용해 만든 특수 스켈레톤 컴포넌트들이 들어가 있습니다.
index.tsx
파일은 단순 배럴 파일이라서 아래처럼 스켈레톤들을 가져다 쓸 수 있습니다.📸 스크린샷
댓글창
기록 페이지
🔗 관련 이슈
📌 기타 참고사항
댓글 스켈레톤 디자인 약간 변경
기존 댓글 스켈레톤 디자인은 아래 첫번째 사진처럼 댓글 내용 부분의 오른쪽에 여백이 있습니다.
근데 여백의 너비가 동그라미의 너비와 동일해서, 사용자 입장에서는 스켈레톤만 봤을 때 실제 댓글도 오른쪽에 여백이 있을 것이라고 착각할 것 같습니다.
그래서 댓글 내용 부분에 여백 없이 꽉 차도록 수정했는데요. 혹시 여백 있는게 더 나으면 말해주세요! @YehaYoo
댓글 스켈레톤과 댓글의 크기가 다름⚠️
스켈레톤 만들 때 px을 기준으로 스타일했습니다.
왜냐면.. 실제로 기록 페이지 만들 때도 px 기준으로 스타일했고 피그마에도 px 기준인거 같아서...
그랬더니 댓글 스켈레톤은 px 기준으로 스타일했는데, 댓글은 rem 기준으로 스타일돼서 크기가 다르게 표시되더라구요.
둘 중 하나로 통일해야 할 것 같아요.
기록 페이지는 절대 단위(px)로 하기로 했던 거 같은데, 댓글 쪽도 px로 하는 게 맞나요? @YehaYoo
Summary by CodeRabbit
새로운 기능
리팩토링