diff --git a/README.md b/README.md
index 9fa347d00..a38c93464 100644
--- a/README.md
+++ b/README.md
@@ -13,3 +13,24 @@
## Week 2. 1단계 - 페이지 만들기
[🔗 link](https://edu.nextstep.camp/s/hazAC9xa/ls/QzV1ncxk)
+
+### 기능 목록
+
+- [x] Header, Footer와 같은 공통 컴포넌트 만들기 (모든 페이지에서 Header와 Footer는 보여질 수 있게 적용)
+
+- 각 Url Path별로 페이지 만들기 (결과물과 스타일은 결과 링크 참고)
+ - [x] 메인 페이지 (/)
+ - [x] Theme 카테고리 섹션 추가
+ - [x] heme 카테고리 Item 클릭 시 Theme 페이지(/theme/:themeKey)로 이동
+ - [x] 실시간 급상승 선물랭킹 추가
+ - [x] 필터 기능을 hooks를 사용하여 구현 (ex. 전체, 여성이, 남성이, 청소년이 / 받고 싶어한, 많이 선물한, 위시로 받은)
+ - [x] 상품 목록을 처음에는 6개만 보여지게 하기
+ - [x] 더보기를 누르는 경우 상품 목록을 더 보여주기 (접기 버튼을 누르면 다시 6개만 보여지도록)
+ - [x] Theme 페이지(/theme/:themeKey)
+ - [x] Header 섹션 추가
+ - [x] 재사용성을 고려하여 Header 섹션을 만들기 (themeKey에 따라 label, title, description, backgroundColor가 달라짐)
+ - [x] 상품 목록 섹션 추가
+ - [x] 로그인 페이지(/login)
+ - [x] ID와 PW를 입력하면 로그인이 되도록 구현(ID와 PW는 아무 값을 입력해도 통과)
+ - [x] 나의 페이지(/my-account)
+ - [x] 로그아웃을 할 수있는 버튼을 추가
diff --git a/package-lock.json b/package-lock.json
index 8f100a3a8..3ea093a5e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,7 +11,8 @@
"@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0",
"react": "^18.2.0",
- "react-dom": "^18.2.0"
+ "react-dom": "^18.2.0",
+ "react-router-dom": "^6.24.1"
},
"devDependencies": {
"@craco/craco": "^7.1.0",
@@ -6135,6 +6136,14 @@
"@babel/runtime": "^7.13.10"
}
},
+ "node_modules/@remix-run/router": {
+ "version": "1.17.1",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.17.1.tgz",
+ "integrity": "sha512-mCOMec4BKd6BRGBZeSnGiIgwsbLGp3yhVqAD8H+PxiRNEHgDpZb8J1TnrSDlg97t0ySKMQJTHCWBCmBpSmkF6Q==",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
"node_modules/@rollup/plugin-babel": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
@@ -28318,6 +28327,36 @@
}
}
},
+ "node_modules/react-router": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.24.1.tgz",
+ "integrity": "sha512-PTXFXGK2pyXpHzVo3rR9H7ip4lSPZZc0bHG5CARmj65fTT6qG7sTngmb6lcYu1gf3y/8KxORoy9yn59pGpCnpg==",
+ "dependencies": {
+ "@remix-run/router": "1.17.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8"
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.24.1.tgz",
+ "integrity": "sha512-U19KtXqooqw967Vw0Qcn5cOvrX5Ejo9ORmOtJMzYWtCT4/WOfFLIZGGsVLxcd9UkBO0mSTZtXqhZBsWlHr7+Sg==",
+ "dependencies": {
+ "@remix-run/router": "1.17.1",
+ "react-router": "6.24.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8",
+ "react-dom": ">=16.8"
+ }
+ },
"node_modules/react-scripts": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz",
diff --git a/package.json b/package.json
index 8a3e091c7..a50f5fb0e 100644
--- a/package.json
+++ b/package.json
@@ -25,11 +25,10 @@
"@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0",
"react": "^18.2.0",
- "react-dom": "^18.2.0"
+ "react-dom": "^18.2.0",
+ "react-router-dom": "^6.24.1"
},
"devDependencies": {
- "react-scripts": "5.0.1",
- "typescript": "^4.9.5",
"@craco/craco": "^7.1.0",
"@emotion/eslint-plugin": "^11.11.0",
"@storybook/addon-essentials": "^7.6.17",
@@ -65,8 +64,10 @@
"eslint-plugin-storybook": "^0.8.0",
"prettier": "^3.2.5",
"prop-types": "^15.8.1",
+ "react-scripts": "5.0.1",
"storybook": "^7.6.17",
"tsconfig-paths-webpack-plugin": "^4.1.0",
+ "typescript": "^4.9.5",
"webpack": "^5.90.3"
},
"overrides": {
diff --git a/src/App.tsx b/src/App.tsx
index 1df5ce256..4a3a7704d 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,18 +1,7 @@
-import styled from '@emotion/styled';
+import Routes from './Routes/index';
const App = () => {
- const name = 'Josh Perez';
-
- return (
-
-
Hello, {name}
-
- );
+ return ;
};
export default App;
-
-const Title = styled.h1`
- font-size: 1.5em;
- color: gray;
-`;
diff --git a/src/Routes/constants.ts b/src/Routes/constants.ts
new file mode 100644
index 000000000..bad9faf26
--- /dev/null
+++ b/src/Routes/constants.ts
@@ -0,0 +1,6 @@
+export const ROUTE_PATHS = {
+ MAIN: '/',
+ THEME: '/theme/:themeKey',
+ LOGIN: '/login',
+ MY_ACCOUNT: '/my-account',
+};
diff --git a/src/Routes/index.tsx b/src/Routes/index.tsx
new file mode 100644
index 000000000..cfcb1c1fe
--- /dev/null
+++ b/src/Routes/index.tsx
@@ -0,0 +1,45 @@
+import { BrowserRouter as Router, Route, Routes, useLocation } from 'react-router-dom';
+
+import Footer from '@/components/Footer';
+import Header from '@/components/Header';
+import LoginPage from '@/pages/Login';
+import MainPage from '@/pages/Main';
+import MyAccountPage from '@/pages/MyAccount';
+import ThemePage from '@/pages/Theme';
+
+import { ROUTE_PATHS } from './constants';
+
+const RoutesComponent = () => {
+ return (
+
+ } />
+ } />
+ } />
+ } />
+
+ );
+};
+const Layout = ({ children }: { children: React.ReactNode }) => {
+ const location = useLocation();
+ const isLoginPage = location.pathname === ROUTE_PATHS.LOGIN;
+
+ return (
+
+ {!isLoginPage && }
+ {children}
+ {!isLoginPage && }
+
+ );
+};
+
+const AppRoutes = () => {
+ return (
+
+
+
+
+
+ );
+};
+
+export default AppRoutes;
diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx
new file mode 100644
index 000000000..324b549cf
--- /dev/null
+++ b/src/components/Footer.tsx
@@ -0,0 +1,30 @@
+import styled from '@emotion/styled';
+
+const FooterContainer = styled.footer`
+ padding: 28px 16px 88px;
+ width: 100%;
+ max-width: 100vw;
+ background-color: rgb(250, 250, 252);
+ display: flex;
+ justify-content: center;
+
+ position: relative;
+`;
+
+const FooterInner = styled.div`
+ display: flex;
+ justify-content: space-between;
+ width: 80%;
+`;
+
+const Footer = () => {
+ return (
+
+
+ 카카오톡 선물하기
+
+
+ );
+};
+
+export default Footer;
diff --git a/src/components/Header.tsx b/src/components/Header.tsx
new file mode 100644
index 000000000..b270511db
--- /dev/null
+++ b/src/components/Header.tsx
@@ -0,0 +1,80 @@
+import styled from '@emotion/styled';
+import { useEffect, useState } from 'react';
+import { Link } from 'react-router-dom';
+
+import { Container } from '@/components/common/layouts/Container';
+
+const HeaderInner = styled.div`
+ width: 100%;
+ max-width: 1024px;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+`;
+
+const Logo = styled.div`
+ display: flex;
+ align-items: center;
+
+ img {
+ height: 54px;
+ }
+`;
+
+const LoginContainer = styled.div`
+ display: flex;
+ align-items: center;
+`;
+
+const Header = () => {
+ const [isLoggedIn, setIsLoggedIn] = useState(false);
+
+ useEffect(() => {
+ const authToken = sessionStorage.getItem('authToken');
+ setIsLoggedIn(!!authToken); // authToken이 존재하면 true, 아니면 false로 설정
+
+ // authToken 변경 시 sessionStorage 갱신
+ window.addEventListener('storage', handleStorageChange);
+
+ return () => {
+ window.removeEventListener('storage', handleStorageChange);
+ };
+ }, []);
+
+ const handleStorageChange = () => {
+ const authToken = sessionStorage.getItem('authToken');
+ setIsLoggedIn(!!authToken);
+ window.location.reload();
+ };
+
+ return (
+
+
+
+
+
+
+
+ {isLoggedIn ? (
+
+
+ 내 계정
+
+
+ ) : (
+
+
+ 로그인
+
+
+ )}
+
+
+ );
+};
+
+export default Header;
diff --git a/src/components/MainPage/SelectFriend.tsx b/src/components/MainPage/SelectFriend.tsx
new file mode 100644
index 000000000..6a12a91eb
--- /dev/null
+++ b/src/components/MainPage/SelectFriend.tsx
@@ -0,0 +1,43 @@
+import styled from '@emotion/styled';
+
+import { Image } from '@/components/common/Image/index';
+import { Container } from '@/components/common/layouts/Container';
+
+const SelectFriendContainer = styled(Container)`
+ width: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 150px;
+ background-color: rgb(250, 250, 252);
+`;
+
+const SelectFriendInner = styled.section`
+ display: flex;
+ align-items: center;
+ width: 80%;
+ padding: 0px 16px;
+
+ p {
+ padding-left: 16px;
+ font-size: 28px;
+ }
+`;
+
+export const SelectFriend = () => (
+ <>
+
+
+
+ 선물 받을 친구를 선택해주세요.
+
+
+ >
+);
+export default SelectFriend;
diff --git a/src/components/MainPage/ThemeGrid.tsx b/src/components/MainPage/ThemeGrid.tsx
new file mode 100644
index 000000000..6bb382369
--- /dev/null
+++ b/src/components/MainPage/ThemeGrid.tsx
@@ -0,0 +1,192 @@
+import styled from '@emotion/styled';
+import { Link } from 'react-router-dom';
+
+import { Image } from '@/components/common/Image/index';
+
+import { Container } from '../common/layouts/Container';
+import { Grid } from '../common/layouts/Grid';
+
+const ThemeItem = styled.section`
+ margin: 0 auto;
+ text-align: center;
+ padding: 25px 35px 24px;
+ max-width: 100%;
+`;
+
+const ThemeImage = styled(Image)``;
+
+const Wrapper = styled(Grid)`
+ padding: 45px 0 23px 0;
+ margin: 0
+ max-width: 100%;
+`;
+
+const ThemeGrid = () => (
+
+
+
+
+ 생일
+
+
+
+
+
+
+ 졸업선물
+
+
+
+
+
+
+ 스몰럭셔리
+
+
+
+
+
+
+ 명품선물
+
+
+
+
+
+
+ 결혼/집들이
+
+
+
+
+
+
+ 따뜻한선물
+
+
+
+
+
+
+ 가벼운선물
+
+
+
+
+
+
+ 팬심저격
+
+
+
+
+
+
+ 교환권
+
+
+
+
+
+
+ 건강/비타민
+
+
+
+
+
+
+ 과일/한우
+
+
+
+
+
+
+ 출산/키즈
+
+
+
+);
+
+export const ThemeMenu = () => {
+ return (
+
+
+
+ );
+};
+
+export default ThemeMenu;
diff --git a/src/components/MainPage/TrendGiftRanking.tsx b/src/components/MainPage/TrendGiftRanking.tsx
new file mode 100644
index 000000000..f7dff6a22
--- /dev/null
+++ b/src/components/MainPage/TrendGiftRanking.tsx
@@ -0,0 +1,160 @@
+import styled from '@emotion/styled';
+import { useState } from 'react';
+
+import { Container } from '../common/layouts/Container';
+import { Grid } from '../common/layouts/Grid';
+import TrendGiftRankingGrid from './TrendGiftRankingGrid';
+
+const Title = styled.h2`
+ color: rgb(0, 0, 0);
+ width: 100%;
+ text-align: center;
+ font-size: 35px;
+ line-height: 30px;
+ font-weight: 700;
+`;
+
+const FiltersContainer = styled.div``;
+
+const Wrapper = styled(Grid)`
+ padding: 20px 0px 7px;
+ max-width: 100%;
+`;
+
+const Icon = styled.div`
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 60px;
+ height: 60px;
+ border-radius: 24px;
+ font-size: 20px;
+ color: rgb(255, 255, 255);
+ font-weight: 700;
+ background-color: rgb(230, 241, 255);
+ transition: background-color 200ms ease 0s;
+`;
+
+const FilterItem = styled.div`
+ margin: 0 auto;
+ text-align: center;
+ max-width: 100%;
+ cursor: pointer;
+
+ &.active {
+ p {
+ color: rgb(70, 132, 233);
+ font-weight: 700;
+ }
+
+ div {
+ background-color: rgb(70, 132, 233);
+ font-weight: 700;
+ color: white;
+ }
+ }
+`;
+
+const FilterType = styled.div`
+ display: flex;
+ justify-content: center;
+ width: 100%;
+ height: 64px;
+ background-color: rgb(230, 241, 255);
+ margin: 0 auto;
+ border-radius: 12px;
+ border: 1px solid rgba(70, 132, 233, 0.1);
+
+ button {
+ padding: 20px 30px;
+ color: rgba(70, 132, 233, 0.7);
+ font-weight: 400;
+ font-size: 22px;
+ cursor: pointer;
+ outline: none;
+ border: none;
+ background: none;
+ transition: color 0.2s ease-in-out;
+ }
+
+ button.active {
+ color: rgba(70, 132, 233, 1);
+ font-weight: 700;
+ }
+`;
+
+const TrendGiftRanking = () => {
+ const [genderFilter, setGenderFilter] = useState('ALL');
+ const [rankingFilter, setRankingFilter] = useState('RECEIVING');
+
+ const handleGenderFilter = (filter: string) => {
+ setGenderFilter(filter);
+ };
+
+ const handleRankingFilter = (filter: string) => {
+ setRankingFilter(filter);
+ };
+
+ return (
+
+ 실시간 급상승 선물 랭킹
+
+
+ handleGenderFilter('ALL')}
+ >
+ ALL
+ 전체
+
+ handleGenderFilter('FEMALE')}
+ >
+ 👩🏻🦳
+ 여성이
+
+ handleGenderFilter('MALE')}
+ >
+ 👨🏻🦳
+ 남성이
+
+ handleGenderFilter('TEENAGER')}
+ >
+ 👦🏻
+ 청소년이
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default TrendGiftRanking;
diff --git a/src/components/MainPage/TrendGiftRankingGrid.tsx b/src/components/MainPage/TrendGiftRankingGrid.tsx
new file mode 100644
index 000000000..ee8eeba8a
--- /dev/null
+++ b/src/components/MainPage/TrendGiftRankingGrid.tsx
@@ -0,0 +1,97 @@
+import styled from '@emotion/styled';
+import { useState } from 'react';
+
+import { Button } from '../common/Button';
+import { RankingGoodsItems } from '../common/GoodsItem/Ranking';
+import { Container } from '../common/layouts/Container';
+import { Grid } from '../common/layouts/Grid';
+
+const Wrapper = styled.div`
+ padding: 20px 0 80px 0;
+ max-width: 100%;
+`;
+
+const GridContainer = styled(Grid)`
+ max-width: 100%;
+ width: 100%;
+`;
+
+const sampleItem = {
+ rankingIndex: 1,
+ imageSrc:
+ 'https://st.kakaocdn.net/product/gift/product/20231030175450_53e90ee9708f45ffa45b3f7b4bc01c7c.jpg',
+ subtitle: 'BBQ',
+ title: 'BBQ 양념치킨+크림치즈볼+콜라1.25L',
+ amount: 29000,
+};
+
+const TrendGiftRankingGrid = ({ visibleItems }: { visibleItems: number }) => {
+ const rankingItems = Array.from({ length: 21 }, (_, index) => ({
+ ...sampleItem,
+ rankingIndex: index + 1,
+ }));
+
+ return (
+
+ {rankingItems.slice(0, visibleItems).map((item) => (
+
+ ))}
+
+ );
+};
+
+const ToggleButton = styled(Button)`
+ width: 100%;
+ max-width: 480px;
+ margin: 0 auto;
+ margin-top: 30px;
+ border-radius: 4px;
+ display: flex;
+ -webkit-box-pack: center;
+ justify-content: center;
+ -webkit-box-align: center;
+ align-items: center;
+ cursor: pointer;
+ height: 40px;
+ font-size: 15px;
+ box-shadow: rgb(204, 204, 204) 0px 0px 0px 1px inset;
+ background-color: white;
+
+ &:hover {
+ background-color: rgb(248, 248, 248);
+ }
+`;
+
+export const TrendGiftRankings = () => {
+ const [visibleItems, setVisibleItems] = useState(6);
+
+ const handleShowMore = () => {
+ setVisibleItems(21);
+ };
+
+ const handleShowLess = () => {
+ setVisibleItems(6);
+ };
+
+ return (
+
+
+
+ {visibleItems === 6 ? (
+ 더보기
+ ) : (
+ 접기
+ )}
+
+
+ );
+};
+
+export default TrendGiftRankings;
diff --git a/src/components/ThemePage/ThemeGifts.tsx b/src/components/ThemePage/ThemeGifts.tsx
new file mode 100644
index 000000000..a4949674d
--- /dev/null
+++ b/src/components/ThemePage/ThemeGifts.tsx
@@ -0,0 +1,57 @@
+import styled from '@emotion/styled';
+
+import { DefaultGoodsItems } from '@/components/common/GoodsItem/Default'; // 임의의 경로로 수정해주세요.
+
+import { Container } from '../common/layouts/Container';
+import { Grid } from '../common/layouts/Grid';
+
+const Wrapper = styled.div`
+ padding: 20px 0 80px 0;
+ max-width: 100%;
+`;
+
+const GridContainer = styled(Grid)`
+ max-width: 100%;
+ width: 100%;
+`;
+
+const sampleItem = {
+ imageSrc:
+ 'https://st.kakaocdn.net/product/gift/product/20231030175450_53e90ee9708f45ffa45b3f7b4bc01c7c.jpg',
+ subtitle: 'BBQ',
+ title: 'BBQ 양념치킨+크림치즈볼+콜라1.25L',
+ amount: 29000,
+};
+
+const ThemeGiftGrid = () => {
+ const GoodsItems = Array.from({ length: 21 }, (_, index) => ({
+ ...sampleItem,
+ goodsIndex: index + 1,
+ }));
+
+ return (
+
+ {GoodsItems.map((item) => (
+
+ ))}
+
+ );
+};
+
+export const ThemeGifts = () => {
+ return (
+
+
+
+
+
+ );
+};
+
+export default ThemeGifts;
diff --git a/src/components/ThemePage/ThemeHeader.tsx b/src/components/ThemePage/ThemeHeader.tsx
new file mode 100644
index 000000000..611ad9794
--- /dev/null
+++ b/src/components/ThemePage/ThemeHeader.tsx
@@ -0,0 +1,69 @@
+import styled from '@emotion/styled';
+
+import { Container } from '../common/layouts/Container';
+
+const HeaderWrapper = styled.header<{ backgroundColor: string }>`
+ background-color: ${(props) => props.backgroundColor};
+ color: white;
+ p {
+ padding-top: 12px;
+ }
+`;
+
+const Label = styled.p`
+ font-size: 20px;
+ color: rgba(255, 255, 255, 0.7);
+ font-weight: 700;
+`;
+const Title = styled.h1`
+ font-size: 30px;
+ line-height: 40px;
+ padding-top: 12px;
+ word-break: break-word;
+ font-weight: 700;
+ color: rgb(255, 255, 255);
+`;
+
+const Description = styled.p`
+ font-size: 24px;
+ color: rgba(255, 255, 255, 0.55);
+`;
+
+interface Theme {
+ key: string;
+ label: string;
+ title: string;
+ description: string;
+ backgroundColor: string;
+}
+
+const themes: Theme[] = [
+ {
+ key: 'life_small_gift',
+ label: '가벼운 선물',
+ title: '예산은 가볍게, 감동은 무겁게❤️',
+ description: `당신의 센스를 뽐내줄 부담 없는 선물`,
+ backgroundColor: 'rgb(75, 77, 80)',
+ },
+];
+
+interface HeaderProps {
+ themeKey: string;
+}
+const Header = ({ themeKey }: HeaderProps) => {
+ const selectedTheme = themes.find((theme) => theme.key === themeKey) || themes[0]; // 기본 테마는 첫 번째로 설정
+
+ const { label, title, description, backgroundColor } = selectedTheme;
+
+ return (
+
+
+
+ {title}
+ {description}
+
+
+ );
+};
+
+export default Header;
diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx
new file mode 100644
index 000000000..8bd1996ba
--- /dev/null
+++ b/src/pages/Login.tsx
@@ -0,0 +1,85 @@
+import styled from '@emotion/styled';
+import { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+
+import { Button } from '@/components/common/Button/index';
+import { UnderlineTextField } from '@/components/common/Form/Input/UnderlineTextField';
+import { ROUTE_PATHS } from '@/Routes/constants';
+
+const Wrapper = styled.div`
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+`;
+
+const LoginSection = styled.section`
+ width: 100%;
+ max-width: 580px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ border: 1px solid rgba(0, 0, 0, 0.12);
+ padding: 60px 52px;
+`;
+
+const LoginButton = styled(Button)`
+ max-width: 474px;
+ width: 100%;
+`;
+
+const InputField = styled(UnderlineTextField)`
+ max-width: 474px;
+ width: 100%;
+ margin: 8px 0;
+`;
+
+const LogoImage = styled.img`
+ margin-bottom: 16px;
+`;
+
+const LoginPage = () => {
+ const [id, setId] = useState('');
+ const [password, setPassword] = useState('');
+ const navigate = useNavigate();
+
+ const handleLogin = (e: React.FormEvent) => {
+ e.preventDefault();
+ if (!id || !password) {
+ alert('아이디와 비밀번호를 입력해주세요.');
+ } else {
+ sessionStorage.setItem('authToken', id);
+ navigate(ROUTE_PATHS.MAIN);
+ }
+ };
+
+ return (
+
+
+
+
+
+
+ );
+};
+
+export default LoginPage;
diff --git a/src/pages/Main.tsx b/src/pages/Main.tsx
new file mode 100644
index 000000000..01cb355dd
--- /dev/null
+++ b/src/pages/Main.tsx
@@ -0,0 +1,48 @@
+import styled from '@emotion/styled';
+
+import { Button } from '@/components/common/Button';
+import { Container } from '@/components/common/layouts/Container';
+import { SelectFriend } from '@/components/MainPage/SelectFriend';
+import { ThemeMenu } from '@/components/MainPage/ThemeGrid';
+import TrendGiftRanking from '@/components/MainPage/TrendGiftRanking';
+
+const AiSuggestButton = styled(Button)`
+ flex-direction: column;
+ margin: 16px 0 16px 0;
+ padding: 16px 0 16px 0;
+ width: 100%;
+ border-radius: 8px;
+ p {
+ color: rgba(0, 0, 0, 0.4);
+ }
+ h3 {
+ font-weight: 700;
+ }
+`;
+
+const Wrapper = styled.div`
+ height: auto;
+ min-height: 100%;
+ left: 0;
+ right: 0;
+ top: 58px;
+`;
+
+export const MainPage = () => {
+ return (
+
+
+
+
+
+ AI가 추천하는 선물
+ 선물을 추천받고 싶은 친구를 선택해주세요.
+
+
+
+
+
+ );
+};
+
+export default MainPage;
diff --git a/src/pages/MyAccount.tsx b/src/pages/MyAccount.tsx
new file mode 100644
index 000000000..525378d50
--- /dev/null
+++ b/src/pages/MyAccount.tsx
@@ -0,0 +1,58 @@
+import styled from '@emotion/styled';
+import { useEffect, useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+
+import { Button } from '@/components/common/Button';
+import { Container } from '@/components/common/layouts/Container';
+
+const ProfileContainer = styled(Container)`
+ padding: 80px 0px 120px;
+`;
+
+const ProfileName = styled.h1`
+ width: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-weight: 700;
+ font-size: 36px;
+`;
+
+const LogoutButton = styled(Button)`
+ margin: 20px auto;
+ max-width: 200px;
+ max-height: 40px;
+ color: white;
+ background-color: rgb(68, 68, 68);
+
+ &:hover {
+ background-color: rgb(85, 85, 85);
+ }
+`;
+
+const MyAccount = () => {
+ const [userName, setUserName] = useState('');
+ const navigate = useNavigate();
+
+ useEffect(() => {
+ const authToken = sessionStorage.getItem('authToken');
+ if (authToken) {
+ setUserName(authToken);
+ }
+ }, []);
+
+ const handleLogout = () => {
+ sessionStorage.removeItem('authToken');
+ navigate('/');
+ window.location.reload(); // 임시
+ };
+
+ return (
+
+ {`${userName}님 안녕하세요!`}
+ 로그아웃
+
+ );
+};
+
+export default MyAccount;
diff --git a/src/pages/Theme.tsx b/src/pages/Theme.tsx
new file mode 100644
index 000000000..b9fe83896
--- /dev/null
+++ b/src/pages/Theme.tsx
@@ -0,0 +1,24 @@
+import styled from '@emotion/styled';
+import { useParams } from 'react-router-dom';
+
+import ThemeGiftGrid from '@/components/ThemePage/ThemeGifts';
+import Header from '@/components/ThemePage/ThemeHeader';
+
+const Wrapper = styled.div`
+ height: auto;
+ min-height: 100%;
+`;
+
+export const ThemePage = () => {
+ const { themeKey } = useParams<{ themeKey: string }>();
+
+ return (
+
+ {themeKey && }
+
+
+
+ );
+};
+
+export default ThemePage;
diff --git a/src/styles/reset.css b/src/styles/reset.css
index 6acb70655..9bad075af 100644
--- a/src/styles/reset.css
+++ b/src/styles/reset.css
@@ -87,3 +87,8 @@ button:disabled {
border: 0;
padding: 0;
}
+
+a {
+ text-decoration: none;
+ color: black;
+}