AI웹 개발자 과정 공부 (팀스파르타)/프로젝트
24.05.28_TIL ( 팀 프로젝트 : AI NOST Django ) _ 4. Mybooklist (카드형식), 페이지네이션
티아(tia)
2024. 5. 28. 10:50
728x90
[ 세번째 프로젝트 ]
AI를 이용한 소설 사이트를 만들어 보자.
++ 팀 스로젝트로 팀과의 협업이 중요하다.
++ 장고 공식 문서는 항상 확인하기
https://docs.djangoproject.com/en/4.2/
++ 랭체인 공식 문서
https://www.langchain.com/
++ 리액트 공식문서
https://ko.legacy.reactjs.org/ # 한국어
https://ko.react.dev/
https://github.com/1489ehdghks/NOST.git
참고 사이트 : https://codepen.io/andymerskin/pen/XNMWvQ
1. 카드 형식의 북 리스트를 만들어보자.
먼저 src/ pages/ mybooks/ Mybooklist.jsx 를 만들어서 코드를 넣어준다.
import React, { useState } from 'react';
import useThemeStore from '../../shared/store/Themestore';
import './Mybooklist.scss';
const Card = ({ image, header, content }) => {
return (
<div className="card" style={{ backgroundImage: `url(${image})` }}>
<div className="card-header"><h1>{header}</h1></div>
<div className="card-content"><p>{content}</p></div>
</div>
);
};
const Mybooklist = () => {
const { themes, currentSeason } = useThemeStore();
const currentTheme = themes[currentSeason];
const cards = [
{
image: 'https://images.unsplash.com/photo-1479660656269-197ebb83b540?dpr=2&auto=compress,format&fit=crop&w=1199&h=798&q=80&cs=tinysrgb&crop=',
header: 'Canyons',
content: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit.'
},
{
image: 'https://images.unsplash.com/photo-1479659929431-4342107adfc1?dpr=2&auto=compress,format&fit=crop&w=1199&h=799&q=80&cs=tinysrgb&crop=',
header: 'Beaches',
content: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit.'
},
//카드 형식
];
return (
<div className="container" style={{ color: currentTheme.textColor }}>
<h1 className="title">My Book List</h1>
<div className="cardlist">
{currentCards.map((card, index) => (
<Card
key={index}
image={card.image}
header={card.header}
content={card.content}
/>
))}
</div>
</div>
);
};
export default Mybooklist;
그 다음은 scss 파일을 만들어준다.
.container {
text-align: center;
padding: 2rem;
height: 100%; // 화면 전체 높이 설정
overflow-y: auto; // 세로 스크롤 활성화
}
.title {
font-size: 2.5rem;
margin-bottom: 2rem;
}
.cardlist {
display: flex;
justify-content: center;
gap: 1rem;
flex-wrap: wrap;
}
.card {
width: 200px;
height: 300px;
background-size: cover;
background-position: center;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
overflow: hidden;
position: relative;
transition: all 0.3s ease;
cursor: pointer;
border: 2px solid transparent;
&:hover {
transform: scale(1.05);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);
border-color: #fff;
}
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.2);
transition: background 0.3s ease;
}
&:hover::before {
background: rgba(0, 0, 0, 0.4);
}
.card-header {
position: absolute;
bottom: 1rem;
width: 100%;
background: transparent;
color: #fff;
padding: 1rem;
text-align: left;
z-index: 2;
transition: transform 0.3s ease;
h1 {
margin: 0;
font-size: 1.5rem;
}
}
.card-content {
display: none;
color: #fff;
padding: 1rem;
box-sizing: border-box;
text-align: left;
position: absolute;
bottom: 1rem;
left: 0;
right: 0;
z-index: 2;
max-height: 4.5rem; // 2줄 높이
overflow: hidden;
}
&:hover .card-header {
transform: translateY(-50%);
}
&:hover .card-content {
display: block;
}
}
++ 여기서 카드가 8개로 늘어났을 때 화면조정을 해주어야 한다.
2. 북리스트를 사이드바에서 연결하기
src/ app / Approuter.jsx 를 수정해준다.
import React from 'react';
import { Routes, Route, Navigate } from 'react-router-dom';
import HomePage from '../pages/home/HomePage';
import MainPage from '../pages/main/MainPage';
import useAuthStore from '../shared/store/AuthStore';
import Profile from '../pages/profile/Profile';
import Mybooklist from '../pages/mybooks/Mybooklist';
import SideLayout from '../widgets/layout/sideLayout/SideLayout';
const AppRouter = () => {
const { isLoggedIn } = useAuthStore();
const ProfileWithLayout = () => (
<SideLayout>
<Profile />
</SideLayout>
);
const MybooklistWithLayout = () => (
<SideLayout>
<Mybooklist/>
</SideLayout>
);
return (
<Routes>
<Route path="/" element={isLoggedIn ? <MainPage /> : <HomePage />} />
<Route path="/main" element={<MainPage />} />
<Route path="/profile" element={<ProfileWithLayout />} />
<Route path="/Mybooklist" element={<MybooklistWithLayout />} />
<Route path="*" element={<Navigate to="/" />} />
</Routes>
);
};
export default AppRouter;
src/ widgets/ layout/ sideLayout 수정해주자
import React, { useState } from 'react';
import { FaBars } from 'react-icons/fa';
import { useNavigate } from 'react-router-dom';
import { logout } from '../../../features/auth/LogoutInstance';
import useThemeStore from '../../../shared/store/Themestore';
import './SideLayout.scss';
const SideLayout = ({ children }) => {
const { themes, currentSeason } = useThemeStore(); // 테마 설정 사용
const currentTheme = themes[currentSeason]; // 현재 시즌 테마 색상
const [isOpen, setIsOpen] = useState(false);
const navigate = useNavigate();
const toggleSidebar = () => {
setIsOpen(!isOpen);
};
const handleLogout = () => {
logout();
navigate('/'); // HomePage로 리다이렉트
};
const handleProfile = () => {
navigate('/profile'); // 프로필 페이지로 이동
};
const handleMybooklist = () => {
navigate('/Mybooklist'); // 내 북페이지로 이동
};
return (
<div className="side-layout" style={{ backgroundColor: currentTheme.mainpageBackgroundColor, color: currentTheme.textColor }}>
<div className="menu-icon" onClick={toggleSidebar}>
<FaBars />
</div>
<div className={`sidebar ${isOpen ? 'open' : ''}`} style={{ backgroundColor: currentTheme.buttonBackgroundColor, color: currentTheme.buttonTextColor }}>
<button>Main</button>
<button onClick={handleProfile}>Profile</button>
<button onClick={handleMybooklist}>My Book</button>
<button>Setting</button>
<button onClick={handleLogout}>logout</button>
</div>
<div className="content">
{children}
</div>
</div>
);
};
export default SideLayout;
3. 페이지네이션 만들기
src/ pages/ mybooks/ Mybooklist.jsx 에 추가해준다.
...
const [currentPage, setCurrentPage] = useState(1);
const cardsPerPage = 4;
const indexOfLastCard = currentPage * cardsPerPage;
const indexOfFirstCard = indexOfLastCard - cardsPerPage;
const currentCards = cards.slice(indexOfFirstCard, indexOfLastCard);
const totalPages = Math.ceil(cards.length / cardsPerPage);
const handleClick = (number) => {
setCurrentPage(number);
};
const generatePagination = () => {
const pages = [];
const maxPagesToShow = 5;
const halfPagesToShow = Math.floor(maxPagesToShow / 2);
let startPage = Math.max(currentPage - halfPagesToShow, 1);
let endPage = Math.min(startPage + maxPagesToShow - 1, totalPages);
if (endPage - startPage < maxPagesToShow - 1) {
startPage = Math.max(endPage - maxPagesToShow + 1, 1);
}
for (let i = startPage; i <= endPage; i++) {
pages.push(i);
}
return pages;
};
...
return (
<div className="container" style={{ color: currentTheme.textColor }}>
...
<div className="pagination" >
<button onClick={() => handleClick(1)} disabled={currentPage === 1}> « </button>
<button onClick={() => handleClick(currentPage - 1)} disabled={currentPage === 1}> < </button>
{generatePagination().map((page, index) => (
<button
key={index}
onClick={() => handleClick(page)}
className={currentPage === page ? 'active' : ''}>
{page}
</button>
))}
<button onClick={() => handleClick(currentPage + 1)} disabled={currentPage === totalPages}> > </button>
<button onClick={() => handleClick(totalPages)} disabled={currentPage === totalPages}> » </button>
</div>
</div>
);
++ 여기서 페이지 네이션을 할 때 맨 처음페이지나 맨 마지막페이지를 나타내는 용어
« 는 '<<'
< 는 '<'
> 는 '>'
» 는 '>>' 를 나타낸다
src/ pages/ mybooks/ Mybooklist.scss 에도 추가해준다.
.pagination {
display: flex;
justify-content: center;
align-items: center;
gap: 10px;
margin-top: 20px;
}
.pagination button {
border: none;
background: none;
font-size: 16px;
padding: 10px;
cursor: pointer;
color: inherit;
transition: background 0.3s;
}
.pagination button.active {
background-color: #d4d4d4;
color: white;
border-radius: 5px;
}
.pagination button:hover:not(.active):not(:disabled) {
background-color: #f0f0f0;
border-radius: 5px;
}
반응형