diff --git a/.eslintrc.js b/.eslintrc.js
index 8112dc1..6c121be 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -53,6 +53,7 @@ module.exports = {
'react/react-in-jsx-scope': 'off',
'react/require-default-props': 'off',
'react/jsx-no-constructed-context-values': 'off',
+ 'react/no-array-index-key': 'off',
'import/order': [
'error',
{
diff --git a/src/components/common/Table/index.tsx b/src/components/common/Table/index.tsx
new file mode 100644
index 0000000..42a12f2
--- /dev/null
+++ b/src/components/common/Table/index.tsx
@@ -0,0 +1,104 @@
+import { ReactNode, ReactElement, ComponentProps } from 'react';
+import { css } from 'styled-components';
+
+import FlexBox from '@components/common/FlexBox';
+import Text from '@components/common/Text';
+import { offscreen } from '@styles/commonStyles';
+
+type TableDefaultProps = {
+ children: ReactElement | ReactElement[];
+};
+
+export interface TableProps extends TableDefaultProps {
+ caption: string;
+ columnsWidth?: string[];
+}
+
+const Table = ({ caption, columnsWidth, children }: TableProps) => (
+
`0.1rem solid ${theme.palette.borderLine}`};
+ table-layout: fixed;
+ `}
+ >
+ {caption}
+ {columnsWidth && (
+
+ {columnsWidth.map((width, index) => (
+
+ ))}
+
+ )}
+ {children}
+
+);
+
+const Thead = ({ children }: TableDefaultProps) => {children};
+
+const Tbody = ({ children }: TableDefaultProps) => (
+ theme.palette.hoverColor};
+ }
+ `}
+ >
+ {children}
+
+);
+
+const Tr = ({ children }: TableDefaultProps) => (
+
+ `0.1rem solid ${theme.palette.borderLine}`};
+ `}
+ >
+ {children}
+
+);
+
+type CellSX = {
+ width?: string;
+ justifyContent?: 'center' | 'flex-start';
+};
+
+const flexCss = {
+ justifyContent: 'center',
+ alignItems: 'center',
+ gap: '1rem',
+} as Pick, 'sx'>;
+
+type ThProps = {
+ colSpan?: number;
+ sx?: CellSX;
+ children: ReactNode;
+};
+
+const Th = ({ sx = {}, children, ...restProps }: ThProps) => (
+
+
+ {children}
+
+ |
+);
+
+export interface TdProps extends ThProps {
+ rowSpan?: number;
+}
+
+const Td = ({ sx = {}, children, ...restProps }: TdProps) => (
+
+ {children}
+ |
+);
+
+Table.Head = Thead;
+Table.Body = Tbody;
+Table.Row = Tr;
+Table.HeadCell = Th;
+Table.BodyCell = Td;
+
+export default Table;
diff --git a/src/components/common/Table/table.stories.tsx b/src/components/common/Table/table.stories.tsx
new file mode 100644
index 0000000..5532ae4
--- /dev/null
+++ b/src/components/common/Table/table.stories.tsx
@@ -0,0 +1,197 @@
+import { ComponentStory, ComponentMeta } from '@storybook/react';
+
+import Icon from '@components/common/Icon';
+import Table from '@components/common/Table';
+import Text from '@components/common/Text';
+
+export default {
+ title: 'common/Table',
+ component: Table,
+} as ComponentMeta;
+
+const noticeList = [
+ {
+ id: 1,
+ title: '스터디 가입전 해당 공지사항을 숙지해주세요.',
+ writer: '파크',
+ date: '2022.10.22',
+ view: 101,
+ isFixed: true,
+ isNew: false,
+ commentCount: 14,
+ },
+ {
+ id: 2,
+ title: '스터디 전체 공지 드립니다.',
+ writer: '파크크크크크크크크',
+ date: '1시간 전',
+ view: 11,
+ isFixed: true,
+ isNew: true,
+ commentCount: 0,
+ },
+];
+
+export const NoticeList: ComponentStory = () => (
+
+
+
+ 번호
+ 제목
+ 작성자
+ 날짜
+ 조회수
+
+
+
+ {noticeList
+ .reverse()
+ .map(({ id, title, writer, date, view, commentCount }) => (
+
+
+ {id}
+
+
+ {title}
+ {commentCount > 0 && (
+
+ {commentCount}
+
+ )}
+
+
+ {writer}
+
+
+ {date}
+
+
+ {view}
+
+
+ ))}
+
+
+);
+
+const memberManagementList = [
+ {
+ id: 1,
+ nickName: 'Hemdi',
+ status: 'waiting',
+ dateOfSubscription: '2022.10.22',
+ periodOfActivity: 0,
+ uncheckin: 0,
+ uncheckout: 0,
+ penaltyPoint: 0,
+ },
+ {
+ id: 2,
+ nickName: 'Jamie',
+ status: 'warning',
+ dateOfSubscription: '2022.10.22',
+ periodOfActivity: 0,
+ uncheckin: 0,
+ uncheckout: 10,
+ penaltyPoint: 990,
+ },
+ {
+ id: 3,
+ nickName: 'Park',
+ status: 'member',
+ dateOfSubscription: '2022.10.22',
+ periodOfActivity: 0,
+ uncheckin: 0,
+ uncheckout: 0,
+ penaltyPoint: 0,
+ },
+];
+
+const STATUS = {
+ member: {
+ text: '활동 중',
+ color: 'primary',
+ },
+ waiting: {
+ text: '가입 승인 대기 중',
+ color: 'fontColor',
+ },
+ warning: {
+ text: '벌점 초과',
+ color: 'warning',
+ },
+ danger: {
+ text: '경고 대상',
+ color: 'danger',
+ },
+};
+
+export const MemberManagementList: ComponentStory = () => (
+
+
+
+ 닉네임
+ 상태
+ 가입일
+ 활동일
+
+ 미 체크인
+
+
+
+ 미 체크아웃
+
+
+ 벌점
+
+
+
+ {memberManagementList.map((props) => (
+
+
+
+ {props.nickName}
+
+
+
+
+ {STATUS[props.status].text}
+
+
+
+
+ {props.dateOfSubscription}
+
+
+ {props.periodOfActivity}일
+
+
+ {props.uncheckin}회
+
+
+ {props.uncheckout}회
+
+
+ {props.penaltyPoint}점
+
+
+ ))}
+
+
+);
diff --git a/src/styles/commonStyles.ts b/src/styles/commonStyles.ts
new file mode 100644
index 0000000..2132695
--- /dev/null
+++ b/src/styles/commonStyles.ts
@@ -0,0 +1,10 @@
+import { CSSObject } from 'styled-components';
+
+export const offscreen: CSSObject = {
+ position: 'absolute',
+ width: '1px',
+ height: '1px',
+ margin: '-1px',
+ overflow: 'hidden',
+ clipPath: 'polygon(0 0, 0 0, 0 0)',
+};