목차 | |
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"
}
]
▷ 출력
※ 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 권한 주기
■ 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;
■ 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');
- aixos('http://localhost:5000/list')
const boards = await axios('http://localhost:5000/list');
or
- package.json파일에서 proxy 설정해주기
"proxy" : "http//localhost:5000/",
※ db의 데이터를 활용함
3. 느낀 점
data.js에 값을 추가해도 새로고침을 하게 되면서 data.js에 추가되어야 할 값이 반영이 되지 않아서 아쉬웠고 이번에 db와 연동을 해봤으니 다음 번에는 한 번 db를 활용하여 todoList를 마무리해보고 싶다.
'Front-end > React' 카테고리의 다른 글
React 기초(게시판)- AWS 풀스택 과정 43일차 (0) | 2024.09.20 |
---|---|
React 기초(useReducer)- AWS 풀스택 과정 41일차 (0) | 2024.09.10 |
React 기초(todoList)- AWS 풀스택 과정 40일차 (0) | 2024.09.09 |
React 기초(배열)- AWS 풀스택 과정 39일차 (0) | 2024.09.06 |
React 기초(input, param)- AWS 풀스택 과정 38일차 (0) | 2024.09.05 |