Front-end/React

React 기초(db 연동)- AWS 풀스택 과정 42일차

awspspgh 2024. 9. 19. 18:53

 

목차
1. 게시판
2. db 연동
3. 느낀 점

 

1. 게시판

▣ board-ui-app

- App.js

import './App.css';
import BoardHome from './component/BoardHome';

function App() {
  return (
    <div className="App">
      <BoardHome />
    </div>
  );
}

export default App;

 

- BoardHome.jsx

import React from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import BoardList from './BoardList';
import BoardDetail from './BoardDetail';
import '../component/board-style.css';
import BoardRegister from './BoardRegister';
import BoardModify from './BoardModify';

const BoardHome = () => {
    return (
        <div className='boardHome'>
            <h1 className='title'>My First React Board Project</h1>
            <BrowserRouter>
                <Routes>
                    <Route path='/' element={<BoardList/>}/>
                    <Route path='/list' element={<BoardList/>}/>
                    <Route path="/detail/:id" element={<BoardDetail/>} />
                    <Route path='/register' element={<BoardRegister/>}/>
                    <Route path='/modify/:id' element={<BoardModify/>}/>
                </Routes>
            </BrowserRouter>
        </div>
    );
};

export default BoardHome;

 

- BoardList.jsx

import React from 'react';
import { boardList } from '../data/data';
import '../component/board-style.css';
import { Link } from 'react-router-dom';

const BoardList = () => {
    return (
        <div className='boardList'>
            <h2>Board List Page</h2>
            <table>
                <thead>
                    <tr className='subTitle'>
                        <th>번호</th>
                        <th>제목</th>
                        <th>작성자</th>
                        <th>작성일</th>
                    </tr>
                </thead>
                <tbody>
                    {boardList.map(b => (
                        <tr key={b.id}>
                            <td className='center'>{b.id}</td>
                            <td><Link to={`/detail/${b.id}`}>{b.title}</Link></td>
                            <td>{b.writer}</td>
                            <td>{b.reg_date.substring(0, b.reg_date.indexOf("T"))}</td>
                        </tr>
                    ))}
                </tbody>
            </table>
                <Link to={`/register`}><button className='textButton'>글쓰기</button></Link>
        </div>
    );
};

export default BoardList;

 

- BoardRegister.jsx

import React, { useRef, useState } from 'react';
import { boardList } from '../data/data';
import { Link } from 'react-router-dom';

const BoardRegister = () => {
    const nextId = useRef(8);
    console.log(nextId);
    // 오늘 날짜 가져오기
    const today = new Date();
    const formattedDate = `${today.getFullYear()}-${today.getMonth() + 1}-${today.getDate()}`;

    const [ boardListTmp, setBoardListTmp ] = useState([...boardList]);
    console.log(boardListTmp);

    const [ board, setBoard ] = useState({
        title: '',
        writer: '',
        content: '',
        reg_date: formattedDate
    });

    const { title, writer, content, reg_date } = board;

    const onChange = (e) => {
        const { name, value } = e.target;
        setBoard({
            ...board,
            [name]:value
        });
    }

    const onCreate = () => {
        const b = {
            id : nextId.current,
            title : title,
            writer : writer,
            content : content
        };
        setBoardListTmp(boardListTmp.concat(b));
        setBoard({
            title: '',
            writer: '',
            content: '',
            reg_date: formattedDate
        });
        nextId.current += 1;
    }
    return (
        <div className='boardRegister'>
            <h2>Board Register</h2>
            <div className='content'>
                <input type="text" className='content-box' name='reg_date' value={reg_date}/>
                <input type="text" className='content-box' name='title' placeholder='Title' onChange={onChange}/>
                <input type="text" className='content-box' name='writer' placeholder='Writer' onChange={onChange}/>
                <div className='contentContainer'>
                <textarea type="text" className='content-box' name='content' placeholder='Content' onChange={onChange}/>
                </div>
            </div>
            <Link to={'/'}><button onClick={onCreate}>Register</button></Link> 
        </div>
    );
};

export default BoardRegister;

 

- BoardDetail.jsx

import React from 'react';
import { Link, useParams } from 'react-router-dom';
import { boardList } from '../data/data';

const BoardDetail = () => {

    const { id } = useParams();

    // 특정 조건을 만족하는 요소의 index를 찾는 함수 findIndex()
    // boardList.findIndex(b => b.id === Number(id))
    // params는 String으로 값을 가져옴 ==> 따라서 Number로 형변환
    // 굳이 findIndex를 사용하는 이유는 id의 값과 index(boardList의 index)가 맞지 않기 때문

    const idx = boardList.findIndex(b => b.id === Number(id));
    console.log(idx);
    
    const board = boardList[idx];
    console.log(board);

    return (
        <div className='boardDetailBoard'>
            <h2>No.{board.id} / Board Detail Page</h2>
            <div className='boardDetailContainer'>
                <span className='boardTitle'>{board.title}</span>
                <span className='bold'>{board.writer} [{board.reg_date.substring(0, board.reg_date.indexOf("T"))}]</span>
                <div>{board.contents}</div>
            </div>
            <div>
                <Link to={`/modify/${board.id}`}><button>Modify</button></Link>
                <button>Remove</button>
                <Link to={'/list'}><button>List</button></Link>
            </div>
        </div>
    );
};

export default BoardDetail;

 

- BoardModify.jsx

import React, { useState, useEffect } from 'react';
import { boardList } from '../data/data';
import { Link, useParams } from 'react-router-dom';

const BoardModify = () => {

    const [ boardListTmp, setBoardListTmp ] = useState([...boardList]);
    console.log(boardListTmp);

    const { id } = useParams();

    const [ mod, setMod ] = useState({
        title: '',
        writer: '',
        content: '',
        reg_date: ''
    });

    const { title, writer, contents, reg_date } = mod;

    useEffect(() => {
        const board = Number(id);
        const boardToModify = boardListTmp.find(I => I.id === board);
        setMod(boardToModify);
    }, [id, boardListTmp]);

    const onChange = (e) => {
        const { name, value } = e.target;
        setMod({
            ...mod,
            [name]:value
        });
    }

    const onCreate = () => {
        const b = {
            id : id,
            title : title,
            writer : writer,
            content : contents
        };
        setBoardListTmp(boardListTmp.concat(b));
        setMod({
            title: '',
            writer: '',
            content: '',
            reg_date: ''
        });
    }
    return (
        <div className='boardRegister'>
            <h2>Board Modify</h2>
            <div className='content'>
                <input type="text" className='content-box' name='reg_date' value={reg_date.substring(0, reg_date.indexOf("T"))}/>
                <input type="text" className='content-box' name='title' placeholder='Title' value={title} onChange={onChange}/>
                <input type="text" className='content-box' name='writer' placeholder='Writer' value={writer} onChange={onChange}/>
                <div className='contentContainer'>
                <textarea type="text" className='content-box' name='content' placeholder='Content' value={contents} onChange={onChange}/>
                </div>
            </div>
            <Link to={'/'}><button onClick={onCreate}>Modify</button></Link> 
        </div>
    );
};

export default BoardModify;

 

- board-style.css

.boardList {
    font-family: Arial, sans-serif;
    margin: 20px;
}
.title{
    background-color: black;
    color: transparent;
    -webkit-background-clip: text;
    margin-bottom: 30px;
}
h2 {
    text-align: center;
    color: #667eea;
}
table {
    width: 1024px;
    border-collapse: collapse;
    margin: 20px auto;
}
.subTitle{
    background: linear-gradient(to right top, #12c2e975, #c471ed7a, #f64f5a8c);
}
.subTitle>th{
    color: #764ba2;
}
th, td {
    border: 1px solid #ddd;
    padding: 8px;
    text-align: left;
}
.center{
    text-align: center;
}
th {
    color: #333;
}
tr:nth-child(even) {
    background-color: #F9F9F9;
}
tr:hover {
    background: linear-gradient(to right, rgba(135, 207, 235, 0.452), rgba(255, 192, 203, 0.479));
    color: #667eea;
    font-weight: 700;
}
table {
    box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
}
/* th:nth-child(1), td:nth-child(1) {
    width: 3%;
    word-break: break-word;
} */
.textButton{
    width: 150px;
    height: 60px;
    color: white;
    font-size: 20px;
    font-weight: 700;
    /* border 그라데이션 - button */
    border: 1px solid transparent;
    border-radius: 20px;
    background-image: linear-gradient(to right top, #12c2e975, #c471ed7a, #f64f5a8c);
    background-origin: border-box;
    background-clip: content-box, border-box;
}
.textButton:hover{
    cursor: pointer;
    transform: scale(1.05);
}
.textButton:active {
    transform: scale(0.95);
}

/* BoardDetail */
a{
    text-decoration-line: none;
}
.boardDetailContainer{
    font-family: Arial, sans-serif;
    margin: 40px;
}
.boardDetailBoard button{
    width: 150px;
    height: 60px;
    color: white;
    font-size: 20px;
    font-weight: 700;
    /* border 그라데이션 - button */
    border: 1px solid transparent;
    border-radius: 20px;
    background-image: linear-gradient(to right top, #12c2e975, #c471ed7a, #f64f5a8c);
    background-origin: border-box;
    background-clip: content-box, border-box;
}
button>a{
    color: white;
}
.boardDetailBoard button:hover{
    cursor: pointer;
    transform: scale(1.05);
}
.boardDetailBoard button:active {
    transform: scale(0.95);
}
.boardDetailContainer{
    margin: 0 auto;
    width: 1000px;
    /* border 그라데이션 */
    border: 2px solid transparent;
    border-radius: 15px;
    background-image: linear-gradient(#fff, #fff), linear-gradient(to right top, #12c2e975, #c471ed7a, #f64f5a8c);
    background-origin: border-box;
    background-clip: content-box, border-box;
}
.boardDetailContainer>span{
    padding: 20px 20px 10px 5px;
}
.boardDetailContainer>div{
    margin: 20px 0 20px 0;
}
.bold{
    font-size: 15px;
    font-weight: bold;
}

/* BoardRegister */
.content{
    width: 1024px;
    margin: 0 auto;
    /* border 그라데이션 */
    border: 2px solid transparent;
    border-radius: 15px;
    background-image: linear-gradient(#fff, #fff), linear-gradient(to right top, #12c2e975, #c471ed7a, #f64f5a8c);
    background-origin: border-box;
    background-clip: content-box, border-box;
}
.content>.content-box{
    width: 100px;
    height: 45px;
    padding: 10px;
    border: none;
    outline: none;
    font-size: 15px;
    font-weight: 500;
    margin: 0 20px 0 33px;
}
.content>.content-box:first-child{
    margin-left: 34px;
}
.content>.content-box:nth-child(2){
    width: 250px;
}
.content>div>.content-box{
    width: 800px;
    height: 300px;
    padding: 10px;
    border: 1px solid pink;
    outline: none;
    font-size: 15px;
    font-weight: 500;
    margin: 10px;
}
.contentContainer{
    background-color: rgba(255, 192, 203, 0.322)
}
.boardRegister button{
    width: 150px;
    height: 60px;
    color: white;
    margin-top: 20px;
    font-size: 20px;
    font-weight: 700;
    /* border 그라데이션 - button */
    border: 1px solid transparent;
    border-radius: 20px;
    background-image: linear-gradient(to right top, #12c2e975, #c471ed7a, #f64f5a8c);
    background-origin: border-box;
    background-clip: content-box, border-box;
}
.boardRegister button:hover{
    cursor: pointer;
    transform: scale(1.05);
}
.boardRegister button:active {
    transform: scale(0.95);
}

 

- data.js

export const boardList = 
[
    {
      "id": 7,
      "title": "React란 무엇인가요?",
      "contents": "React는 몇 가지 종류의 컴포넌트를 가지지만 우리는 React.Component의 하위 클래스를 사용해보겠습니다.",
      "writer": "purple",
      "reg_date": "2024-05-25T07:41:21.000Z"
    },
    {
      "id": 6,
      "title": "브라우저에 코드 작성하기",
      "contents": "먼저 새 탭에서 초기 코드를 열어주세요. 새 탭은 비어있는 틱택토 게임판과 React 코드를 보여줄 것입니다. 우리는 자습서에서 React 코드를 편집할 것입니다.",
      "writer": "navy",
      "reg_date": "2024-05-25T07:41:20.000Z"
    },
    {
      "id": 5,
      "title": "자습서를 시작하기 전에",
      "contents": "우리는 이 자습서에서 작은 게임을 만들겁니다. 게임을 만들고 싶지 않아서 자습서를 건너뛰고 싶을 수 있습니다. 그래도 한번 해보세요!",
      "writer": "blue",
      "reg_date": "2024-05-25T07:41:20.000Z"
    },
    {
      "id": 4,
      "title": "외부 플러그인을 사용하는 컴포넌트",
      "contents": "React는 유연하며 다른 라이브러리나 프레임워크를 함께 활용할 수 있습니다. 이 예제에서는 외부 마크다운 라이브러리인 remarkable을 사용해 \u003Ctextarea\u003E의 값을 실시간으로 변환합니다.",
      "writer": "green",
      "reg_date": "2024-05-25T07:41:19.000Z"
    },
    {
      "id": 3,
      "title": "애플리케이션",
      "contents": "props와 state를 사용해서 간단한 Todo 애플리케이션을 만들 수 있습니다.",
      "writer": "yellow",
      "reg_date": "2024-05-25T07:41:19.000Z"
    },
    {
      "id": 2,
      "title": "상태를 가지는 컴포넌트",
      "contents": "컴포넌트는 this.props를 이용해 입력 데이터를 다루는 것 외에도 내부적인 상태 데이터를 가질 수 있습니다. 이는 this.state로 접근할 수 있습니다.",
      "writer": "orange",
      "reg_date": "2024-05-25T07:41:19.000Z"
    },
    {
      "id": 1,
      "title": "한 번 배워서 어디서나 사용하기",
      "contents": "기술 스택의 나머지 부분에는 관여하지 않기 때문에, 기존 코드를 다시 작성하지 않고도 React의 새로운 기능을 이용해 개발할 수 있습니다.",
      "writer": "red",
      "reg_date": "2024-05-25T07:41:17.000Z"
    }
  ]

 

▷ 출력

출력 (1)

 

※ db와 연동하지 않았기에 data.js에 값을 추가해도 추가된 값이 반영되지 않아서 버튼의 기능들은 없다.

 

2. db 연동

◈ db 연동

■ board-app폴더 생성 후 src 폴더 내부에 server.js 파일 생성 후 붙여넣기

// 설치한 라이브러리 변수로 받아오기
const express = require('express');
const bodyParser = require('body-parser');
const mysql = require('mysql');
const cors = require('cors');

//express 사용하기 위한 app 생성
const app = express();

//express 사용할 서버포트 설정
const PORT = 5000;

app.use(cors());
app.use(bodyParser.json());

//DB 접속
const db = mysql.createConnection({
    host : 'localhost',
    user: 'react',  // 생성
    password: 'mysql',
    port:'3306',
    database:'db_react'  // 생성
});

// express 접속
app.listen(PORT, ()=>{
    console.log(`server connecting on : http://localhost:${PORT}`);
});

//db 연결
db.connect((err)=>{
    if(!err){
        console.log("seccuss");

    }else{
        console.log("fail");
    }
});

 

터미널에서 cd board-app으로 파일 이동 후 다음과 같이 실행
- npm i express ⇒ 설치 : 서버
- npm i mysql ⇒ 설치 : DB
- npm i axios ⇒ 설치 : 비동기
- npm i cors ⇒ 설치 :  서버와 클라이언트 간의 자원공유 관리
- npm i json ⇒ 설치 : json
- npm i nodemon ⇒ 설치 : 자동감지 서버 재시작 도구 (소스코드의 변경이 발생하면 자동으로 서버 재시작)
- npm i react-router-dom ⇒ react 표준 라우팅 라이브러리 (컴포넌트간의 전환이 일어날때 화면 전환)

 

■ MySQL Workbench

- db 생성 및 user 권한 주기

db 연동 (1)

 

db 연동 (2)

 

■ cmd

- mysql -u이름 -p비밀번호

# root 계정으로 실행 (Workbench로 안 할경우)
create database db_react;
create user 'react'@'localhost' identified by 'mysql';
grant all privileges on db_react.* to react@localhost;
ALTER USER 'react'@'localhost' IDENTIFIED WITH mysql_native_password BY 'mysql';
FLUSH PRIVILEGES;

# react 계정으로 실행
use db_react;
drop table board;
create table board (
	id bigint auto_increment primary key,
	title varchar(30),
	contents varchar(500),
	writer varchar(20),
	reg_date timestamp DEFAULT now()
);

insert into board(title, contents, writer) values('한 번 배워서 어디서나 사용하기','기술 스택의 나머지 부분에는 관여하지 않기 때문에, 기존 코드를 다시 작성하지 않고도 React의 새로운 기능을 이용해 개발할 수 있습니다.','red');
insert into board(title, contents, writer) values('상태를 가지는 컴포넌트','컴포넌트는 this.props를 이용해 입력 데이터를 다루는 것 외에도 내부적인 상태 데이터를 가질 수 있습니다. 이는 this.state로 접근할 수 있습니다.','orange');
insert into board(title, contents, writer) values('애플리케이션','props와 state를 사용해서 간단한 Todo 애플리케이션을 만들 수 있습니다.','yellow');
insert into board(title, contents, writer) values('외부 플러그인을 사용하는 컴포넌트','React는 유연하며 다른 라이브러리나 프레임워크를 함께 활용할 수 있습니다. 이 예제에서는 외부 마크다운 라이브러리인 remarkable을 사용해 <textarea>의 값을 실시간으로 변환합니다.','green');
insert into board(title, contents, writer) values('자습서를 시작하기 전에','우리는 이 자습서에서 작은 게임을 만들겁니다. 게임을 만들고 싶지 않아서 자습서를 건너뛰고 싶을 수 있습니다. 그래도 한번 해보세요!','blue');
insert into board(title, contents, writer) values('브라우저에 코드 작성하기','먼저 새 탭에서 초기 코드를 열어주세요. 새 탭은 비어있는 틱택토 게임판과 React 코드를 보여줄 것입니다. 우리는 자습서에서 React 코드를 편집할 것입니다.','navy');
insert into board(title, contents, writer) values('React란 무엇인가요?','React는 몇 가지 종류의 컴포넌트를 가지지만 우리는 React.Component의 하위 클래스를 사용해보겠습니다.','purple');
# (아래는 안 해도 됨)
commit;

select * from board;

 

cmd (1)
cmd (2)

 

■ Terminal

- cd board-app

- cd src/server

- node server

※ DB 서버가 열림!

 

▶ 입력

▣ board-app

- App.js

import './App.css';
import BoardHome from './component/BoardHome';

function App() {
  return (
    <div className="App">
      <BoardHome/>
    </div>
  );
}

export default App;

 

- BoardModify.js

// 설치한 라이브러리 변수로 받아오기
const express = require('express');
const bodyParser = require('body-parser');
const mysql = require('mysql');
const cors = require('cors');

//express 사용하기 위한 app 생성
const app = express();

//express 사용할 서버포트 설정
const PORT = 5000;

app.use(cors());
app.use(bodyParser.json());

//DB 접속
const db = mysql.createConnection({
    host : 'localhost',
    user: 'react',  // 생성
    password: 'mysql',
    port:'3306',
    database:'db_react'  // 생성
});

// express 접속
app.listen(PORT, ()=>{
    console.log(`server connecting on : http://localhost:${PORT}`);
});

//db 연결
db.connect((err)=>{
    if(!err){
        console.log("seccuss");

    }else{
        console.log("fail");
    }
});

// DB에서 값을 가져오기

// / => root 연결시 보여지는 기본화면 설정
app.get('/',(req, res) => {
    res.send("React Server Connect Success!!")
})

// 게시글 목록 가져오기
app.get('/list', (req, res) => {
    // console.log('/list');
    const sql = 'select * from board order by id desc';
    db.query(sql, (err, data)=>{
        if(!err){
            res.send(data);
        }else{
            console.log(err);
            res.send('전송오류');
        }
    })
})

 

- BoardHome.jsx

import React from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import BoardList from './BoardList';
import '../component/board-style.css';
// import BoardDetail from './BoardDetail';
// import BoardRegister from './BoardRegister';
// import BoardModify from './BoardModify';

const BoardHome = () => {
    return (
        <div className='boardHome'>
            <h1 className='title'>My First React Board Project</h1>
            <hr />
            <BrowserRouter>
                <Routes>
                    <Route path='/' element={<BoardList/>}/>
                    <Route path='/list' element={<BoardList/>}/>
                    {/* <Route path="/detail/:id" element={<BoardDetail/>} />
                    <Route path='/register' element={<BoardRegister/>}/>
                    <Route path='/modify/:id' element={<BoardModify/>}/> */}
                </Routes>
            </BrowserRouter>
        </div>
    );
};

export default BoardHome;

 

- BoardList.jsx

import React, { useState } from 'react';
import axios from 'axios';
import { Link } from 'react-router-dom';
import { useEffect } from 'react';

const BoardList = () => {
    // db에 저장되어 있는 board 요소를 가져오기 => boardList 저장
    const [ boardList, setBoardList ] = useState({});

    // 비동기로 db에 접속하여 select로 가져오기
    const getBoardData = async () => {
       const boards = await axios('/list');
       console.log(boards);
       setBoardList(boards.data); 
    }

    // 컴포넌트가 랜더링 될 때, 혹은 업데이트 될 때 실행되는 hooks
    /*
        useEffect(()=>{
            function},[deps]);
        - function : 실행시킬 함수
        - deps :  배열형태로 배열 안에서 검사하고자 하는 특정값
    */
    useEffect(() => {
        getBoardData();
    },[])

    // 서버에서 데이터를 가져오는 것보다 화면에서 핸더링 되는 속도가 더 빠름
    // 조건을 걸어줘서 error 방지

    if(boardList.length > 0){
        return (
            <div className='boardList'>
                <h2>Board List Page</h2>
                <table>
                    <thead>
                        <tr className='subTitle'>
                            <th>번호</th>
                            <th>제목</th>
                            <th>작성자</th>
                            <th>작성일</th>
                        </tr>
                    </thead>
                    <tbody>
                        {boardList.map(b => (
                            <tr key={b.id}>
                                <td className='center'>{b.id}</td>
                                <td><Link to={`/detail/${b.id}`}>{b.title}</Link></td>
                                <td>{b.writer}</td>
                                <td>{b.reg_date.substring(0, b.reg_date.indexOf("T"))}</td>
                            </tr>
                        ))}
                    </tbody>
                </table>
                    <Link to={`/register`}><button className='textButton'>글쓰기</button></Link>
            </div>
        );
    }
};

export default BoardList;

 

- board-style.css

.boardList {
    font-family: Arial, sans-serif;
    margin: 20px;
}
.title{
    background-color: black;
    color: transparent;
    -webkit-background-clip: text;
    margin-bottom: 30px;
}
h2 {
    text-align: center;
    color: #667eea;
}
table {
    width: 1024px;
    border-collapse: collapse;
    margin: 20px auto;
}
.subTitle{
    background: linear-gradient(to right top, #12c2e975, #c471ed7a, #f64f5a8c);
}
.subTitle>th{
    color: #764ba2;
}
th, td {
    border: 1px solid #ddd;
    padding: 8px;
    text-align: left;
}
.center{
    text-align: center;
}
th {
    color: #333;
}
tr:nth-child(even) {
    background-color: #F9F9F9;
}
tr:hover {
    background: linear-gradient(to right, rgba(135, 207, 235, 0.452), rgba(255, 192, 203, 0.479));
    color: #667eea;
    font-weight: 700;
}
table {
    box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
}
/* th:nth-child(1), td:nth-child(1) {
    width: 3%;
    word-break: break-word;
} */
.textButton{
    width: 150px;
    height: 60px;
    color: white;
    font-size: 20px;
    font-weight: 700;
    /* border 그라데이션 - button */
    border: 1px solid transparent;
    border-radius: 20px;
    background-image: linear-gradient(to right top, #12c2e975, #c471ed7a, #f64f5a8c);
    background-origin: border-box;
    background-clip: content-box, border-box;
}
.textButton:hover{
    cursor: pointer;
    transform: scale(1.05);
}
.textButton:active {
    transform: scale(0.95);
}

/* BoardDetail */
a{
    text-decoration-line: none;
}
.boardDetailContainer{
    font-family: Arial, sans-serif;
    margin: 40px;
}
.boardDetailBoard button{
    width: 150px;
    height: 60px;
    color: white;
    font-size: 20px;
    font-weight: 700;
    /* border 그라데이션 - button */
    border: 1px solid transparent;
    border-radius: 20px;
    background-image: linear-gradient(to right top, #12c2e975, #c471ed7a, #f64f5a8c);
    background-origin: border-box;
    background-clip: content-box, border-box;
}
button>a{
    color: white;
}
.boardDetailBoard button:hover{
    cursor: pointer;
    transform: scale(1.05);
}
.boardDetailBoard button:active {
    transform: scale(0.95);
}
.boardDetailContainer{
    margin: 0 auto;
    width: 1000px;
    /* border 그라데이션 */
    border: 2px solid transparent;
    border-radius: 15px;
    background-image: linear-gradient(#fff, #fff), linear-gradient(to right top, #12c2e975, #c471ed7a, #f64f5a8c);
    background-origin: border-box;
    background-clip: content-box, border-box;
}
.boardDetailContainer>span{
    padding: 20px 20px 10px 5px;
}
.boardDetailContainer>div{
    margin: 20px 0 20px 0;
}
.bold{
    font-size: 15px;
    font-weight: bold;
}

/* BoardRegister */
.content{
    width: 1024px;
    margin: 0 auto;
    /* border 그라데이션 */
    border: 2px solid transparent;
    border-radius: 15px;
    background-image: linear-gradient(#fff, #fff), linear-gradient(to right top, #12c2e975, #c471ed7a, #f64f5a8c);
    background-origin: border-box;
    background-clip: content-box, border-box;
}
.content>.content-box{
    width: 100px;
    height: 45px;
    padding: 10px;
    border: none;
    outline: none;
    font-size: 15px;
    font-weight: 500;
    margin: 0 20px 0 33px;
}
.content>.content-box:first-child{
    margin-left: 34px;
}
.content>.content-box:nth-child(2){
    width: 250px;
}
.content>div>.content-box{
    width: 800px;
    height: 300px;
    padding: 10px;
    border: 1px solid pink;
    outline: none;
    font-size: 15px;
    font-weight: 500;
    margin: 10px;
}
.contentContainer{
    background-color: rgba(255, 192, 203, 0.322)
}
.boardRegister button{
    width: 150px;
    height: 60px;
    color: white;
    margin-top: 20px;
    font-size: 20px;
    font-weight: 700;
    /* border 그라데이션 - button */
    border: 1px solid transparent;
    border-radius: 20px;
    background-image: linear-gradient(to right top, #12c2e975, #c471ed7a, #f64f5a8c);
    background-origin: border-box;
    background-clip: content-box, border-box;
}
.boardRegister button:hover{
    cursor: pointer;
    transform: scale(1.05);
}
.boardRegister button:active {
    transform: scale(0.95);
}

 

- package.json

{
  "name": "board-app",
  "version": "0.1.0",
  "private": true,
  "proxy" : "http://localhost:5000/",
  "dependencies": {
    "@testing-library/jest-dom": "^5.17.0",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "axios": "^1.7.7",
    "cors": "^2.8.5",
    "express": "^4.21.0",
    "json": "^11.0.0",
    "mysql": "^2.18.1",
    "nodemon": "^3.1.5",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "react-router-dom": "^6.26.2",
    "react-scripts": "5.0.1",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

 

▷ 출력

- aixos('/list') ( package.json파일에서 proxy 설정 X)

       const boards = await axios('/list');

출력 (2)

 

- aixos('http://localhost:5000/list')

       const boards = await axios('http://localhost:5000/list');

or

- package.json파일에서 proxy 설정해주기

 "proxy" : "http//localhost:5000/",

출력 (3)

※ db의 데이터를 활용함

 

3. 느낀 점

data.js에 값을 추가해도 새로고침을 하게 되면서 data.js에 추가되어야 할 값이 반영이 되지 않아서 아쉬웠고 이번에 db와 연동을 해봤으니 다음 번에는 한 번 db를 활용하여 todoList를 마무리해보고 싶다.