diff --git a/src/components/home/Home.jsx b/src/components/home/Home.jsx
index ecf1a1e..fbe04f5 100644
--- a/src/components/home/Home.jsx
+++ b/src/components/home/Home.jsx
@@ -12,19 +12,8 @@ import { getUserInfoAPI } from "services/user/user";
import "styles/components/home/Home.scss";
import { getAuthToken } from "utils/token";
import { getUserInfo } from "utils/user";
-import { RankingPreview } from "./RankingPreview";
-const rankingData = [
- { username: "Player1", score: 1000 },
- { username: "Player2", score: 900 },
- { username: "Player3", score: 800 },
- { username: "Player4", score: 700 },
- { username: "Player5", score: 600 },
- { username: "Player6", score: 500 },
- { username: "Player7", score: 400 },
- { username: "Player8", score: 300 },
- { username: "Player9", score: 200 },
- { username: "Player10", score: 100 },
-];
+import { RankingPreview } from "components/home/RankingPreview.jsx";
+
const Home = () => {
const navigate = useNavigate();
const { isSignIn } = useOutletContext();
@@ -91,7 +80,7 @@ const Home = () => {
diff --git a/src/components/home/RankingPreview.jsx b/src/components/home/RankingPreview.jsx
index b894487..c7fcfd0 100644
--- a/src/components/home/RankingPreview.jsx
+++ b/src/components/home/RankingPreview.jsx
@@ -1,5 +1,33 @@
+import { useEffect, useState } from "react";
+import { getMyRankingAPI, getRankingListAPI } from "services/home/ranking";
import "styles/components/home/RankingPreview.scss";
-export const RankingPreview = ({ rankingData }) => {
+export const RankingPreview = () => {
+ const [rankingData, setRankingData] = useState([]);
+ const [myScore, setMyScore] = useState(null);
+
+ useEffect(() => {
+ const fetchRanking = async () => {
+ try {
+ // ๐น ๋ด ๋ญํน ๋จผ์ ๊ฐ์ ธ์ค๊ธฐ
+ const myRes = await getMyRankingAPI();
+ const me = myRes?.data || myRes;
+
+ if (me) {
+ setMyScore(me.exp);
+ }
+
+ // ๐น ์ ์ฒด ๋ญํน ๊ฐ์ ธ์ค๊ธฐ
+ const listRes = await getRankingListAPI();
+ const list = listRes?.data || listRes || [];
+ setRankingData(list);
+ } catch (e) {
+ console.error("๋ญํน ํ๋ฆฌ๋ทฐ ์กฐํ ์คํจ:", e);
+ }
+ };
+
+ fetchRanking();
+ }, []);
+ console.log(rankingData);
return (
๋ญํน
@@ -8,10 +36,10 @@ export const RankingPreview = ({ rankingData }) => {
rankingData.slice(0, 4).map((item, index) => (
- {index + 1}. {item.username}
+ {item.rank}. {item.nickname}
- {item.score} ์
+ {item.exp} ์
))
) : (
@@ -24,7 +52,7 @@ export const RankingPreview = ({ rankingData }) => {
-
9ssssss ์
+
{myScore ?? 0} ์
diff --git a/src/components/home/SideRankingList.jsx b/src/components/home/SideRankingList.jsx
new file mode 100644
index 0000000..bfb9435
--- /dev/null
+++ b/src/components/home/SideRankingList.jsx
@@ -0,0 +1,27 @@
+import "styles/components/home/SideRankingList.scss";
+
+const SideRankingList = ({ title, list, highlightRank }) => {
+ return (
+
+
{title}
+
+
+ {list.map((item) => (
+
+ {item.rank}.
+ {item.nickname}
+
+ {item.exp}์
+
+ ))}
+
+
+ );
+};
+
+export default SideRankingList;
diff --git a/src/constants/api.js b/src/constants/api.js
index 7d65ff2..56be9cc 100644
--- a/src/constants/api.js
+++ b/src/constants/api.js
@@ -75,4 +75,7 @@ export const SERVICE_LIST_API = process.env.REACT_APP_SERVICE_LIST_API;
export const SERVICE_MY_LIST_API = process.env.REACT_APP_SERVICE_MY_LIST_API;
export const SERVICE_QNA_API = process.env.REACT_APP_SERVICE_QNA;
+export const RANKING_API = process.env.REACT_APP_RANKING_API;
+export const RANkING_MY_API = process.env.REACT_APP_MY_RANKING_API;
+
export const AI_SERVER_IP = process.env.REACT_APP_AI_SERVER_IP;
\ No newline at end of file
diff --git a/src/pages/home/RankingPage.jsx b/src/pages/home/RankingPage.jsx
new file mode 100644
index 0000000..81b4aa8
--- /dev/null
+++ b/src/pages/home/RankingPage.jsx
@@ -0,0 +1,89 @@
+import SideRankingList from "components/home/SideRankingList";
+import { useEffect, useState } from "react";
+import { getMyRankingAPI, getRankingListAPI } from "services/home/ranking";
+import "styles/pages/home/RankingPage.scss";
+
+const RankingPage = () => {
+ const [rankingList, setRankingList] = useState([]);
+ const [myRank, setMyRank] = useState(null);
+ const [myScore, setMyScore] = useState(null);
+ const [topList, setTopList] = useState([]);
+ const [nearList, setNearList] = useState([]);
+
+ useEffect(() => {
+ const fetchRanking = async () => {
+ try {
+ // โก ๋ด ๋ญํน ์กฐํ
+ const myRes = await getMyRankingAPI();
+ const me = myRes?.data || myRes;
+
+ if (!me) return;
+
+ // ์ ์ฒด ๋ญํน ์กฐํ (7๋ช
๊น์ง)
+ const fullRes = await getRankingListAPI();
+ const list = fullRes?.data || fullRes || [];
+ setRankingList(list);
+ setTopList(list.slice(0, 7));
+
+ setMyRank(me.rank);
+ setMyScore(me.exp);
+
+ // ๋ด ๊ทผ์ฒ ๋ญํน ๊ณ์ฐ (์ ์๋ 3๋ช
)
+ const myIndex = list.findIndex((item) => item.rank === me.rank);
+ const start = Math.max(myIndex - 3, 0);
+ const end = Math.min(myIndex + 4, list.length);
+ setNearList(list.slice(start, end));
+ } catch (err) {
+ console.error("๋ญํน ์กฐํ ์คํจ:", err);
+ }
+ };
+
+ fetchRanking();
+ }, []);
+
+ // ๋ฐ๋ก ์ ๋ฑ์์์ ์ ์ ์ฐจ์ด
+ const diffScore =
+ myRank > 1 && rankingList.length >= myRank - 1
+ ? rankingList[myRank - 2].exp - myScore
+ : 0;
+
+ return (
+
+
+
+
+
+
๋ด ๋ฑ์
+
+ {myRank ?? "-"}
+ ๋ฑ
+
+
+
+ ๋ด ์ ์
+
+ {myScore ?? 0} ์
+
+
+
+
+
+
+ {myRank === 1 ? (
+
ํ์ฌ 1๋ฑ์
๋๋ค!
+ ) : (
+
+ {diffScore}์ ๋ง ๋ ๋์ผ๋ฉด
+ ๋ฑ์๋ฅผ ์์ง๋ฅผ ์ ์์ด์!
+
+ )}
+
+
+ );
+};
+
+export default RankingPage;
diff --git a/src/routes/home.js b/src/routes/home.js
index af61342..fd8f379 100644
--- a/src/routes/home.js
+++ b/src/routes/home.js
@@ -9,6 +9,7 @@ import ServiceFAQPage from "pages/home/ServiceFAQPage";
import ServiceCenterDetailPage from "pages/home/ServiceCenterDetailPage";
import ServicePostPage from "pages/home/ServicePostPage";
import FAQDetailPage from "pages/home/FAQDetailPage";
+import RankingPage from "pages/home/RankingPage";
const home = [
{
path: "/",
@@ -19,7 +20,10 @@ const home = [
index: true,
element: ,
},
-
+ {
+ path: "ranking",
+ element: ,
+ },
{
path: "character-intro",
element: ,
@@ -55,7 +59,7 @@ const home = [
{
path: "service-center/post",
element:
- }
+ },
],
},
];
diff --git a/src/services/home/ranking.js b/src/services/home/ranking.js
new file mode 100644
index 0000000..3f695ce
--- /dev/null
+++ b/src/services/home/ranking.js
@@ -0,0 +1,10 @@
+import { RANKING_API, RANkING_MY_API } from "constants/api";
+import { apiInterface } from "services/axiosForm";
+
+export const getRankingListAPI = async (params = {}) => {
+ return await apiInterface("get", RANKING_API, {}, params, false);
+};
+
+export const getMyRankingAPI = async () => {
+ return await apiInterface("get", RANkING_MY_API, {}, {}, true);
+};
\ No newline at end of file
diff --git a/src/styles/components/home/SideRankingList.scss b/src/styles/components/home/SideRankingList.scss
new file mode 100644
index 0000000..eac852e
--- /dev/null
+++ b/src/styles/components/home/SideRankingList.scss
@@ -0,0 +1,52 @@
+.side-ranking {
+ width: 302px;
+
+ .side-title {
+ font-size: 20px;
+ font-weight: 400;
+ margin-bottom: 35px;
+ }
+
+ .side-list {
+ display: flex;
+ flex-direction: column;
+ gap: 16.47px;
+ font-family: SUIT-Regular;
+
+ .side-item {
+ display: flex;
+ font-size: 21.08px;
+ padding-left: 5px;
+ padding-right: 5px;
+ align-items: center;
+
+ line-height: 29.65px;
+
+ &.highlight {
+ background: #FFDA37;
+ }
+
+ .rank {
+ padding-right: 10px;
+ }
+
+ .name {
+ padding-right: 5px;
+
+ }
+
+ .dashed-line {
+ flex: 1;
+ border-bottom: 2px dashed #D7CCCC;
+ }
+
+ .score {
+ width: 60px;
+ font-size: 16px;
+ text-align: right;
+ }
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/styles/pages/home/RankingPage.scss b/src/styles/pages/home/RankingPage.scss
new file mode 100644
index 0000000..1ced9e8
--- /dev/null
+++ b/src/styles/pages/home/RankingPage.scss
@@ -0,0 +1,117 @@
+.ranking-page-wrapper {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ width: 100%;
+ min-height: 100vh;
+
+ .ranking-page {
+ width: 1187px;
+ display: flex;
+ justify-content: space-between;
+ padding-top: 222px;
+ font-family: YoonChild;
+
+ .ranking-center {
+ position: relative;
+ margin-top: 90px;
+ width: 476px;
+
+ .rank-label {
+ font-size: 20px;
+ }
+
+ .my-rank {
+ display: flex;
+ align-items: flex-end;
+ justify-content: flex-end;
+ gap: 6px;
+
+ .rank-number {
+ font-family: SUIT-Regular;
+ font-size: 128px;
+ font-weight: 600;
+ }
+
+ .rank-unit {
+ font-family: SUIT-Regular;
+ font-size: 32px;
+ margin-bottom: 20px;
+ margin-left: 21px;
+ }
+ }
+
+ /* ์ ์ ๋ผ์ธ */
+ .score-row {
+ margin-top: 60px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 12px;
+
+ .score-label {
+ font-size: 20px;
+ }
+
+ .dashed-line {
+ flex: 1;
+ border-bottom: 3px dashed #F6AA30;
+ }
+
+ .my-score {
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ width: 152px;
+ height: 40px;
+
+ font-family: SUIT-Regular;
+ font-size: 32px;
+ font-weight: 700;
+
+ background: #FFDA37;
+
+ &::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 9px;
+ height: 100%;
+ background: #FF9F03
+ }
+ }
+ }
+ }
+
+ }
+
+
+ .bottom-banner {
+ margin-top: 50px;
+ width: 100%;
+ height: 66px;
+ font-family: YoonChild-ManSeh;
+
+ background: #E9FF9F;
+ box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.15);
+
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ p {
+ font-size: 16px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .banner-score {
+ font-size: 32px;
+ font-weight: 500px;
+ }
+ }
+}
\ No newline at end of file