diff --git a/packages/common/src/assets/svg/DefaultProfile.svg b/packages/common/src/assets/svg/DefaultProfile.svg
new file mode 100644
index 00000000..37dc9765
--- /dev/null
+++ b/packages/common/src/assets/svg/DefaultProfile.svg
@@ -0,0 +1,17 @@
+
\ No newline at end of file
diff --git a/packages/common/src/components/RankingCard/index.stories.tsx b/packages/common/src/components/RankingCard/index.stories.tsx
new file mode 100644
index 00000000..716ba8a2
--- /dev/null
+++ b/packages/common/src/components/RankingCard/index.stories.tsx
@@ -0,0 +1,91 @@
+'use client';
+
+import RankingCard from '.';
+
+import type { Meta, StoryObj } from '@storybook/react';
+
+export default {
+ title: 'common/RankingCard',
+ component: RankingCard,
+ parameters: {
+ backgrounds: {
+ default: 'dark',
+ },
+ },
+} as Meta;
+
+type Story = StoryObj;
+
+export const First: Story = {
+ args: {
+ rank: 1,
+ id: 'userId',
+ cumulatePoint: 1000,
+ user: {
+ id: 'userId',
+ email: 'moondgod@gmail.com',
+ name: '이정우',
+ profileImage:
+ 'https://s3.ap-northeast-2.amazonaws.com/st.dangidata/billing/course/image/133823_20230516174511852.png',
+ },
+ },
+};
+
+export const Second: Story = {
+ args: {
+ rank: 2,
+ id: 'userId',
+ cumulatePoint: 999,
+ user: {
+ id: 'userId',
+ email: 'moondgod@gmail.com',
+ name: '전지환님',
+ profileImage:
+ 'https://image.rocketpunch.com/user/352358/352358_1606388335.jpg?s=200x200&t=cover',
+ },
+ },
+};
+
+export const Third: Story = {
+ args: {
+ rank: 3,
+ id: 'userId',
+ cumulatePoint: 700,
+ user: {
+ id: 'userId',
+ email: 'moondgod@gmail.com',
+ name: '형록이형',
+ profileImage:
+ 'https://publy.imgix.net/user-uploaded/463804/2023.04/d07558f49fda2c53d9c02a038337c88e84fff56ad0d04eed8f37c774c0eca49c.jpeg?w=400&h=400&auto=format&fm=jpeg',
+ },
+ },
+};
+
+export const Fourth: Story = {
+ args: {
+ rank: 4,
+ id: 'userId',
+ cumulatePoint: 600,
+ user: {
+ id: 'userId',
+ email: 'moondgod@gmail.com',
+ name: '이정우교수',
+ profileImage:
+ 'https://file.newswire.co.kr/data/datafile2/thumb_480/2021/08/1889381261_20210805181428_7137680622.jpg',
+ },
+ },
+};
+
+export const NoIMG: Story = {
+ args: {
+ rank: 4,
+ id: 'userId',
+ cumulatePoint: 600,
+ user: {
+ id: 'userId',
+ email: 'moondgod@gmail.com',
+ name: '이정우교수',
+ profileImage: null,
+ },
+ },
+};
diff --git a/packages/common/src/components/RankingCard/index.tsx b/packages/common/src/components/RankingCard/index.tsx
new file mode 100644
index 00000000..3a951318
--- /dev/null
+++ b/packages/common/src/components/RankingCard/index.tsx
@@ -0,0 +1,35 @@
+'use client';
+
+import * as S from './style';
+
+import { RankingPropsType } from 'types';
+
+import DefaultProfile from '../../assets/svg/DefaultProfile.svg';
+
+import Image from 'next/image';
+
+interface RankingCardType extends RankingPropsType {
+ rank: number;
+}
+
+const RankingCard: React.FC = ({
+ rank,
+ cumulatePoint,
+ user: { profileImage, name },
+}) => {
+ return (
+
+ {rank <= 3 && {rank}}
+
+
+
+ {name}
+
+ {cumulatePoint}
+ M
+
+
+ );
+};
+
+export default RankingCard;
diff --git a/packages/common/src/components/RankingCard/style.ts b/packages/common/src/components/RankingCard/style.ts
new file mode 100644
index 00000000..f01886dc
--- /dev/null
+++ b/packages/common/src/components/RankingCard/style.ts
@@ -0,0 +1,77 @@
+import styled from '@emotion/styled';
+
+export const CardWrapper = styled.div`
+ width: 14rem;
+ height: 19rem;
+ border-radius: 1.25rem;
+ background: ${({ theme }) => theme.color.white};
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-direction: column;
+ position: relative;
+
+ // 후에 리스트 래퍼에 이 코드를 작성합니다.
+ /* .medal:nth-child(1n) {
+ background-color: #ffd79b;
+ }
+ .medal:nth-child(2n) {
+ background-color: ${({ theme }) => theme.color.gray['040']};
+ }
+ .medal:nth-child(3n) {
+ background-color: #ce865d;
+ } */
+`;
+
+export const ProfileWrapper = styled.div`
+ width: 5.625rem;
+ height: 5.625rem;
+ border-radius: 50%;
+
+ overflow: hidden;
+ position: relative;
+ img {
+ object-fit: cover;
+ }
+`;
+
+export const UserName = styled.span`
+ ${({ theme }) => theme.typo.h5};
+ color: ${({ theme }) => theme.color.black};
+ font-weight: 700;
+ margin: 0.75rem 0 5.1875rem;
+`;
+
+export const FlexWrapper = styled.div`
+ display: flex;
+ align-items: center;
+`;
+
+export const Point = styled.span`
+ ${({ theme }) => theme.typo.button};
+ color: ${({ theme }) => theme.color.black};
+ font-weight: 500;
+ margin-right: 0.25rem;
+`;
+
+export const PointUnit = styled.span`
+ ${({ theme }) => theme.typo.body2};
+ color: ${({ theme }) => theme.color.black};
+ font-weight: 500;
+`;
+
+export const Medal = styled.div`
+ ${({ theme }) => theme.typo.body2}
+ color: ${({ theme }) => theme.color.black};
+ font-weight: 500;
+
+ width: 2.5rem;
+ height: 2.5rem;
+ border-radius: 50%;
+ position: absolute;
+ top: -0.75rem;
+ left: -0.75rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+`;
diff --git a/packages/common/src/components/RankingItem/index.stories.tsx b/packages/common/src/components/RankingItem/index.stories.tsx
index 1f130fec..3c2ee68d 100644
--- a/packages/common/src/components/RankingItem/index.stories.tsx
+++ b/packages/common/src/components/RankingItem/index.stories.tsx
@@ -7,6 +7,11 @@ import type { Meta, StoryObj } from '@storybook/react';
export default {
title: 'common/RankingItem',
component: RankingItem,
+ parameters: {
+ backgrounds: {
+ default: 'dark',
+ },
+ },
} as Meta;
type Story = StoryObj;
@@ -70,3 +75,17 @@ export const Hund: Story = {
},
},
};
+
+export const NoIMG: Story = {
+ args: {
+ ranking: 100,
+ id: '100등',
+ cumulatePoint: 0,
+ user: {
+ id: '100등',
+ email: 'sample@gmail.com',
+ name: '100등',
+ profileImage: null,
+ },
+ },
+};
diff --git a/packages/common/src/components/RankingItem/index.tsx b/packages/common/src/components/RankingItem/index.tsx
index 5d03d539..76d752c2 100644
--- a/packages/common/src/components/RankingItem/index.tsx
+++ b/packages/common/src/components/RankingItem/index.tsx
@@ -3,6 +3,8 @@
import * as S from './style';
import { slicePoint } from '../../utils';
+import DefaultProfile from '../../assets/svg/DefaultProfile.svg';
+
import { RankingPropsType } from 'types';
interface RankingItemProps extends RankingPropsType {
@@ -21,7 +23,7 @@ const RankingItem: React.FC = ({
alt='profile image'
width={40}
height={40}
- src={profileImage}
+ src={profileImage ?? DefaultProfile}
/>
{name}
diff --git a/packages/common/src/components/RankingItem/style.ts b/packages/common/src/components/RankingItem/style.ts
index 85e9b52d..310fd238 100644
--- a/packages/common/src/components/RankingItem/style.ts
+++ b/packages/common/src/components/RankingItem/style.ts
@@ -12,6 +12,7 @@ export const ItemWrapper = styled.div`
align-items: center;
transition: ease-in-out 0.2s;
cursor: pointer;
+ background-color: ${({ theme }) => theme.color.white};
&:hover {
background: #ffd79b;
diff --git a/packages/common/src/image.d.ts b/packages/common/src/image.d.ts
index 03f7e4ab..ccb76282 100644
--- a/packages/common/src/image.d.ts
+++ b/packages/common/src/image.d.ts
@@ -2,3 +2,4 @@ declare module '*.jpg';
declare module '*.png';
declare module '*.jpeg';
declare module '*.gif';
+declare module '*.svg';
diff --git a/packages/common/tsconfig.json b/packages/common/tsconfig.json
index 6754fd45..32a82c6c 100644
--- a/packages/common/tsconfig.json
+++ b/packages/common/tsconfig.json
@@ -1,6 +1,6 @@
{
"extends": "tsconfig/react-components.json",
- "include": ["**/*.ts", "**/*.tsx"],
+ "include": ["**/*.ts", "**/*.tsx", "src", "src/custom.d.ts"],
"exclude": ["node_modules"],
"compilerOptions": {
"paths": {
diff --git a/packages/types/src/ranking.ts b/packages/types/src/ranking.ts
index 4d07dad7..e9e174ce 100644
--- a/packages/types/src/ranking.ts
+++ b/packages/types/src/ranking.ts
@@ -5,6 +5,6 @@ export interface RankingPropsType {
id: string;
email: string;
name: string;
- profileImage: string;
+ profileImage: string | null;
};
}