[ react ] component 나눠서 career page만들고, career추가하면 database MYSQL에 저장하기

Suji Kang·2023년 10월 5일
0
post-thumbnail

🐾 로그인을 해야지만 들어갈수있는 페이지 만들기❗️

📌 component를 먼저 나눠야한다! 사용하기 좋게

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

🐾 추가하면 database에 저장하기

📝 mysql query 만들기

# 경력을 저장하는 테이블
#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...🌟

profile
나를위한 노트필기 📒🔎📝

0개의 댓글