24.05.24_TIL ( 팀 프로젝트 : AI NOST Django ) _ 2. mainpage 수정 & 로그아웃 로직 연결
[ 세번째 프로젝트 ]
AI를 이용한 소설 사이트를 만들어 보자.
++ 팀 스로젝트로 팀과의 협업이 중요하다.
++ 장고 공식 문서는 항상 확인하기
https://docs.djangoproject.com/en/4.2/
++ 리액트 공식문서
https://ko.legacy.reactjs.org/ # 한국어
https://ko.react.dev/
https://github.com/1489ehdghks/NOST.git
1. frontend 파일을 깃허브에서 가져와서 실행해보자.
- package.json 이 실행이 안된다고 어디있냐고 한다...
- 리액트 실행할 수 있는 폴더로 들어가서 실행해주어야 한다.
- 동환님이 작업하신 작업물을 가져와서 이제 내가 코드해석을 해야한다.
- 흠... 백앤드랑 연결 어떻게 하는가?
- 백엔드에 cors 를 넣어주어야 한다. 리액트와 연결 될 수 있게
#CORS
CORS_ORIGIN_WHITELIST = [
'http://127.0.0.1:8000/',
'http://localhost:3000/',
]
CORS_ALLOW_CREDENTIALS = True
이거에 대해서는 더 알아봐야할 듯....ㅠㅠ
vs 코드에서 백앤드와 프론트앤드를 새창을 열어서 따로 켜준다.
.venv 실행
python manage.py makemigrations
python manage.py migrate
python manage.py runserver
장고의 시크릿키를 .env 파일을 만들어서 그 안에 넣어주면 실행이 된다.
2. list 페이지를 바꿔보자.
테마별로 바뀌어서 테마의 색상을 자동 상속받아 진행이 된다ㅜㅜ
일단 저 배경색부터 투명으로 만들고 싶은데...
어디에 background-color: transparent; 로 투명으로 만들기를 넣어도 없어지지 않아서 최상위로 가서 강제적으로 넣어주려고 함
background-color: transparent !important; // 최 상위로 올려서 배경 투명하게 만들기
이걸 어디에 넣어야 하냐면 위치가 복잡하지만 mainpage.scss 에 넣어야
이렇게 노란 상자가 없어지고 원래 최 상위에 있는 버튼을 누르면 날리는 배경이 되는 것을 볼 수 있다.
코드가 부모상수와 연결되어 있는 코드로 로직이 짜여져 있어서
부모상수를 찾아내고 부모상수의 네임드에 있는 칼라를 수정하여 자식 상수가 받는 칼라가 수정될 수 있게끔 해주어야 상자칼라와 색상이 바뀌었다.
- 브렌치를 생성하여 git에 push 한다.
git checkout -b 브렌치이름 # 브렌치 생성
git add .
git commit -m "first commit"
git push origin 브렌치이름 #브렌치 이름으로 push
- merge 병합하기 - github에서 직접 병합함...ㅎㅎ
git pull origin dev # 먼저 풀 받아옴
git add . # 저장
git commit -m "merge" # 커밋
git push origin hwanhee # 브렌치에 푸쉬
- 브렌치명 변경
git checkout main # 현재 브렌치 말고 다른 브렌치로 이동
git branch -m Comments # 새로운 브렌치 (변경하고 싶은 브렌치) 생성
git pull origin hwanhee # 변경하고 싶은 브렌치 파일 가져옴
git add . # 저장
git push origin Comments # 새로운 브렌치에 푸쉬
git branch -d hwanhee # 로컬에서 브렌치 지움
git push origin --delete hwanhee # 깃허브에서 브렌치 지움
++ 배경과 몇개 더 수정해야 할 것이 있지만 일단 다음에 다시 수정해 보는 것으로! (기능구현이 더 급하다)
3. logout 과 연결해보자.
먼저 로그아웃을 클릭하면 넘어갈 수 있게 로그아웃 버튼을 만들어준다.
- src/ widget/ layout/ sideLayout/ SideLayout.jsx 를 수정해준다.
- 여기서 중요한 것은 경로를 확실하게 정하고 확인하는 것이다.
- navigate 도 중요한 것이니 잘 알아두기!
import React, { useState } from 'react';
import { FaBars } from 'react-icons/fa';
import { useNavigate } from 'react-router-dom';
import { logout } from '../../../features/auth/LogoutInstance'; //경로 확실히! 확인하기
import './SideLayout.scss';
const SideLayout = ({ children }) => {
const [isOpen, setIsOpen] = useState(false);
const navigate = useNavigate();
const toggleSidebar = () => {
setIsOpen(!isOpen);
};
const handleLogout = () => {
logout();
navigate('/'); // HomePage로 리다이렉트
};
return (
<div className="side-layout">
<div className="menu-icon" onClick={toggleSidebar}>
<FaBars />
</div>
<div className={`sidebar ${isOpen ? 'open' : ''}`}>
<button>Main</button>
<button>Profile</button>
<button>My Book</button>
<button>Setting</button>
<button onClick={handleLogout}>logout</button>
</div>
<div className="content">
{children}
</div>
</div>
);
};
export default SideLayout;
- 그 다음은 src/ features/ auth/ LogoutInstance.js 에서 경로를 수정하고 AuthStore 에서 받아온다
import useAuthStore from '../../shared/store/AuthStore';
export const logout = () => {
localStorage.removeItem('accessToken');
localStorage.removeItem('refreshToken');
localStorage.removeItem('nickname');
localStorage.removeItem('email');
localStorage.removeItem('userId');
//세션 스토리지
sessionStorage.removeItem('accessToken');
sessionStorage.removeItem('refreshToken');
//쿠키에서 토큰 삭제
document.cookie = "accessToken=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
document.cookie = "refreshToken=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
useAuthStore.getState().logout();
console.log("로그아웃 성공:")
};
- 그 다음은 src/ shared/ store/ AuthStore 에서 확인하기
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
const useAuthStore = create(persist((set) => ({
token: null,
refreshToken: null,
isLoggedIn: false,
userId: null,
nickname: null,
email: null,
setToken: (token) => set({ token }),
setRefreshToken: (refreshToken) => set({ refreshToken }),
setIsLoggedIn: (isLoggedIn) => set({ isLoggedIn }),
setUserId: (userId) => set({ userId }),
setNickname: (nickname) => set({ nickname }),
setEmail: (email) => set({ email }),
logout: () => set({ token: null, refreshToken: null, isLoggedIn: false, userId: null, nickname: null, email: null }),
}), {
name: 'auth_store',
}));
export default useAuthStore;
이렇게 하고 나면 로그아웃을 하고 나서 잘 Home 화면으로 가는 것을 확인할 수 있다.
로그아웃이 되면서 null 값으로 바뀌면서 로그아웃이 된것도 확인!
4. 사이드바 색상 바꾸어보자.
src/ widget/ layout/ sideLayout/ SideLayout.jsx 를 수정해준다.
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로 리다이렉트
};
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>Profile</button>
<button>My Book</button>
<button>Setting</button>
<button onClick={handleLogout}>logout</button>
</div>
<div className="content">
{children}
</div>
</div>
);
};
export default SideLayout;
css 를 수정해준다.(부모상속받을 수 있게)
.side-layout {
display: flex;
height: 100vh;
background-color: transparent !important; // 최 상위로 올려서 배경 투명하게 만들기
.menu-icon {
position: fixed;
top: 20px;
left: 20px;
font-size: 1.5rem;
cursor: pointer;
z-index: 10;
}
.sidebar {
position: fixed;
top: 0;
left: -200px;
width: 200px;
height: 100%;
background-color: #f0f0f0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
transition: left 0.3s ease;
button {
width: 100px;
padding: 10px;
margin: 10px 0;
cursor: pointer;
border: 1px solid #ffffff;
color: inherit;
background-color: inherit;
border-radius: 5px;
&:hover {
outline: none; //select 태그가 포커스를 받을 때 아웃라인제거
border-color: inherit;
}
}
&.open {
left: 0;
}
}
.content {
margin-left: 200px;
padding: 20px;
flex-grow: 1;
transition: margin-left 0.3s ease;
.book-list {
display: flex;
flex-direction: column;
gap: 10px;
.book-item {
height: 20px;
background-color: #ccc;
border-radius: 5px;
&:nth-child(odd) {
width: 80%;
}
&:nth-child(even) {
width: 60%;
}
}
}
}
.content:not(.open) {
margin-left: 0;
}
}