career.js
import DashboardLayout from "../../components/common/layout";
import { AddBox, Title } from "../../styles/dashboard/career.styles";
import CareerViewTable from "../../components/career/careerViewTable";
const CareerPage = () => {
return (
<DashboardLayout>
<Title>나의 경력을 관리하세요</Title>
<p>회사, 직위, 일자를 입력한 후 경력을 추가해 보세요!</p>
<section style={{ marginBottom: '50px' }}>
<AddBox>
<thead>
<tr>
<th rowSpan={2}>회사명(활동)</th>
<th rowSpan={2}>직책(활동내용)</th>
<th colSpan={2}>활동 일자</th>
</tr>
<tr>
<th>시작일</th>
<th>종료일</th>
</tr>
</thead>
<tbody>
<tr>
<td><input /></td>
<td><input /></td>
<td><input type="date" /></td>
<td><input type="date" /></td>
</tr>
</tbody>
<tfoot>
<tr>
<td colSpan={4}>
<button>추가</button>
</td>
</tr>
</tfoot>
</AddBox>
</section>
<CareerViewTable/>
</DashboardLayout>
)
}
export default CareerPage;
career.styles.js
import styled from "@emotion/styled";
export const Title = styled.h1`
font-size: 20px;
color: red;
`;
export const AddBox = styled.table`
width: 100%;
overflow: auto;
border: 1px solid lightgrey;
& > thead {
background-color: lightgrey;
}
& > td {
padding: 0;
}
& input {
width: 100%;
padding: 5px 10px;
outline: none;
border : 1px solid black;
}
& button {
width: 100%;
cursor: pointer;
padding : 10px;
font-size: 16px;
border-radius: 10px;
background-color: beige;
}
`;
header.js
import { UserText } from "../../styles/common/aside.styles";
import { Header } from "../../styles/common/header.styles";
const DashboardHeader = ()=>{
return(
<Header>
<UserText>강수지<span>님</span></UserText>
<i class="fa-solid fa-bars menu-icon"></i>
</Header>
);
}
export default DashboardHeader;
header.styles.js
import styled from "@emotion/styled";
export const Header = styled.header`
height: 90px;
position:sticky;
top: 0;
display: flex;
align-items: center;
padding: 0 20px;
background-color: hsl(var(--ui-color-background-100));
/* backdrop-filter: blur(5px); */
justify-content: space-between;
`;
sidebar.js
import { AsideLogo, AsideMenuItem, DashboardAside, LogoText, Menu } from "../../styles/common/aside.styles";
import DashboardOutlinedIcon from '@mui/icons-material/DashboardOutlined';
import AssignmentIndOutlinedIcon from '@mui/icons-material/AssignmentIndOutlined';
import TopicOutlinedIcon from '@mui/icons-material/TopicOutlined';
import ChecklistOutlinedIcon from '@mui/icons-material/ChecklistOutlined';
import KeyboardArrowRightOutlinedIcon from '@mui/icons-material/KeyboardArrowRightOutlined';
import KeyboardArrowDownOutlinedIcon from '@mui/icons-material/KeyboardArrowDownOutlined';
const DashboardSidebar = ()=>{
return(
<DashboardAside>
<AsideLogo>
<img src={'/logo.svg'} alt="logo"/>
<LogoText>Portfolio For</LogoText>
</AsideLogo>
<nav>
<Menu>
<li >
<AsideMenuItem active={true}>
<DashboardOutlinedIcon/>
<p>Overview</p>
<KeyboardArrowRightOutlinedIcon/>
</AsideMenuItem>
</li>
<li >
<AsideMenuItem >
<AssignmentIndOutlinedIcon/>
<p>경력</p>
<KeyboardArrowRightOutlinedIcon/>
</AsideMenuItem>
</li>
<li >
<AsideMenuItem>
<TopicOutlinedIcon/>
<p>활동 게시판</p>
<KeyboardArrowDownOutlinedIcon/>
</AsideMenuItem>
</li>
<li >
<AsideMenuItem>
<ChecklistOutlinedIcon/>
<p>할일 목록</p>
<KeyboardArrowRightOutlinedIcon/>
</AsideMenuItem>
</li>
</Menu>
</nav>
</DashboardAside>
);
}
export default DashboardSidebar;
sidebar.styles.js
import styled from "@emotion/styled";
export const DashboardAside = styled.aside`
width: 240px;
position: fixed;
top: 0;
left: 0;
bottom: 0;
overflow-y: scroll;
display: flex;
flex-direction: column;
row-gap: 20px;
padding: 0 20px;
background-color: hsl(var(--ui-color-background-100));
&::-webkit-scrollbar{
display: none;
}
`;
export const AsideLogo = styled.div`
height: 90px;
/* margin-bottom: 50px; */
display: flex;
align-items: center;
justify-content: center;
padding: 20px 0;
column-gap: 10px;
& > img{
height: 100%;
}
`;
export const AsideMenuItem = styled.div`
cursor: pointer;
transition: 500ms;
display: flex;
align-items: center;
border-radius: 10px;
padding: 10px 20px;
column-gap: 10px;
font-size: var(--font-base);
color: hsl(var(--ui-color-foreground-090));
position:relative;
&:hover{
background-color: hsl(var(--ui-color-background-090));
}
& > p{
flex-grow: 1;
margin: 0;
}
& > .sub-icon{
position: absolute;
left: -13px;
top: 7px;
}
& > svg{
font-size: 16px;
}
${(props)=> props.active &&
{backgroundColor : 'hsl(var(--ui-color-background-090))' }
}
`;
export const Menu = styled.ul`
display: flex;
flex-direction: column;
row-gap: 10px;
`;
export const SubMenu = styled.ul`
display: flex;
flex-direction: column;
row-gap: 3px;
margin-top: 3px;
padding-left: 35px;
`;
export const LogoText = styled.p`
font-size: var(--font-xl);
font-weight: bold;
color: hsl(var(--ui-color-primary));
`;
export const UserText = styled.p`
font-size: var(--font-xl);
font-weight: bold;
color: hsl(var(--ui-color-primary));
& > span{
color: hsl(var(--ui-color-foreground-100));
font-size: var(--font-sm);
}
`;
footer.js
import { Footer } from "../../styles/common/footer.styles";
const DashboardFooter = ()=>{
return(
<Footer>
ⓒ 2023. sjk all rights reserved
</Footer>
);
}
export default DashboardFooter;
footer.styles.js
import styled from "@emotion/styled";
export const Footer = styled.footer`
border-top: 1px solid hsl(var(--ui-color-foreground-000));
display: flex;
justify-content: center;
align-items: center;
padding: 50px 0;
`;
layout 컴포넌트
를 만들면❗️다른페이지를 갈때마다 모든 컴포넌트(
hearder
,footer
,sidebar
) 를 쓰지않아도layout컴포넌트
를 사용하면된다.
(props)를 전달
해주어야함 ❗️layout에서 사용하는것
을props key인 children
으로 전달해서, Main 안에 다른 컴포넌트(activiy
,todo
,overview
)에서도 사용가능하게.
sidebar
를 클릭할때마다, 이렇게 그려져야하는 대상이 다르기 때문에 ( 1:21 )👇
layout.js
import { Main, MainWrapper, Wrapper } from "../../styles/common/layout.styles";
import DashboardFooter from "./footer";
import DashboardHeader from "./header";
import DashboardSidebar from "./sidebar";
const DashboardLayout = (props)=>{
return(
<Wrapper>
<DashboardSidebar/>
<MainWrapper>
<DashboardHeader/>
<Main>
{props.children}
</Main>
<DashboardFooter/>
</MainWrapper>
</Wrapper>
);
}
export default DashboardLayout;
layout.styles.js
import styled from "@emotion/styled";
export const Wrapper = styled.div`
display: flex;
border-width: 10px;
padding-left: 240px;
transition: 300ms;
`;
export const MainWrapper = styled.div`
flex-grow: 1;
`;
export const Main = styled.main`
padding: 30px 20px;
`;
# 경력을 저장하는 테이블
#id, 숫자, 자동증가, 기본키
#email, varchar(30), not null, foriegne key(tbl_users의 email컬럼을 참조)
#company, varchar(200), not null
#position, varchar(200), not null
#start_date, date, not null
#end_date, date, null
create table tbl_careers (
id int auto_increment primary key,
email varchar(30) not null references tbl_users(email),
company varchar(200) not null,
position varchar(200) not null,
start_date date not null,
end_date date
);
insert into tbl_careers
(email, company, position, start_date, end_date)
values ('test@test.com', 'Korea IT 아카데미', '교육부' , str_to_date('2020-01-01', '%Y-%m-%d'), str_to_date('2020-12-31', '%Y-%m-%d'));
select * from tbl_careers;
select * from tbl_careers where email='test@test.com';
🌟 만약, 로그인한 사람이 career경력이 엄청 많이 했다고 가정하자 🔎
이렇게, 반복되는 요소가 나타나면map 함수
/(useState())
를 사용해야한다.
📌 처음에 그려질때 mysql 에서
가져와서 최초한번만 실행
해야한다. (useEffect사용)
여기서, section 부분을 컴포넌트로 만들면되겟네 ❗️
그럼careerViewTable
만 만들어진다.
careerViewTable
이 최초로 그려질때,mysql 에서 가져와
야한다.
career.js
에 이었던,<section>...</section>
이친구를 새로운 컴포넌트를 만들어서,
careerViewTable.js
로 옮겨주자 ❗️
careerViewTable.js
import { useState, useEffect } from "react";
import { AddBox } from "../../styles/dashboard/career.styles";
import DeleteSweepIcon from '@mui/icons-material/DeleteSweep';
import axios from "axios";
const CareerViewTable = ({ career }) => {
const [careerList, setCareerList] = useState([]);
useEffect(() => {
//CareerViewTable 컴포넌트가 화면에 보여질 때 실행되는 코드
const fetchCareerList = async () => {
try {
let res = await axios.get('/api/career');
setCareerList(res.data);
} catch (err) {
console.log(err);
}
}
fetchCareerList();
}, []);
return (
<section>
<AddBox>
<thead>
<tr>
<th><input type="checkbox" /></th>
<th>회사명</th>
<th>직책</th>
<th>일자</th>
<th><DeleteSweepIcon /></th>
</tr>
</thead>
<tbody>
{careerList.map((e) => <tr key={e.id}>
<td><input type="checkbox" /></td>
<td>{
e.company
}</td>
<td>{e.position}</td>
<td>
{e.start_date} - {e.end_date}
</td>
<td style={{
display: 'flex', justifyContent: 'center',
alignItems: 'center'
}}><DeleteSweepIcon /></td>
</tr>)}
</tbody>
</AddBox>
</section>
)
}
export default CareerViewTable;
이제 요청을 한번 해볼까 ❓
app.js
app.get('/api/career', async (req, res)=>{
try{
let sql = `select id,
email,
company,
position,
date_format(start_date, '%Y년 %m월 %d일') start_date,
date_format(end_date, '%Y년 %m월 %d일') end_date
from tbl_careers`
// mysql가서 career 리스트 받아오고
let [results, fields] = await pool.query(sql);
// 리액트한테 받아온 배열 응답하기
res.json(results);
}catch(err){
console.log(err);
res.status(500).json('서버쪽 오류 발생');
}
});
리액트에서도 바꿀수있지만, 애초에 mysql에서 date format을 바꿔서 받을수도있다.
현재 모든 유저들의 overview가 나온다.
이제 로그인한 유저들의 정보만 나오게 해보자 ❗️
to be continued...🌟