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

 

GitHub - 1489ehdghks/NOST

Contribute to 1489ehdghks/NOST development by creating an account on GitHub.

github.com

 

 

 

참고 사이트 : https://codepen.io/andymerskin/pen/XNMWvQ

 

Parallax Depth Cards

After playing the Gwent Closed Beta from CD Projekt Red, I had to try this concept of parallaxed backgrounds and layers in cards. I'm thinking this cou...

codepen.io

 

 

 

 

 

 

 

 

 

 

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}> &laquo; </button>
        <button onClick={() => handleClick(currentPage - 1)} disabled={currentPage === 1}> &lt; </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}> &gt; </button>
        <button onClick={() => handleClick(totalPages)} disabled={currentPage === totalPages}> &raquo; </button>
      </div>
    </div>
  );

 

 

 

++ 여기서 페이지 네이션을 할 때 맨 처음페이지나 맨 마지막페이지를 나타내는 용어

&laquo;  는 '<<' 
&lt;     는 '<'
&gt;     는 '>'
&raquo;  는 '>>' 를 나타낸다

 

 

 

 

 

 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;
}

 

 

 

 

 

 

 

 

 

 

반응형