www.youtube.com/watch?v=_yEH9mczm3g&list=PLRx0vPvlEmdD1pSqKZiTihy5rplxecNpz
나동빈님의 유튜브 강의에서 배운 것 정리
데이터
데이터베이스를 따로 관리하기 위해선 HeidiSQL같은 것을 쓰면 효율적이다. 삭제 시 실제로 삭제하지 않고 isDeleted를 체크하는 방식으로 하였다.
Read
node server.js에선 다음과 같은 방법으로 REST API를 제공한다.
const express = require(`express`);
const bodyParser = require(`body-parser`);
const fs = require(`fs`);
const mysql = require(`mysql`);
const app = express();
const port = process.env.PORT || 5000;
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
//데이터베이스 접근을 위한 아이디/비밀번호/호스트 주소 등(깃허브에 안올라가게 하도록 따로 저장해야 함)
const data = fs.readFileSync(`./database.json`);
const conf = JSON.parse(data);
const connection = mysql.createConnection({
host: conf.host,
user: conf.user,
password: conf.password,
port: conf.port,
database: conf.database
});
connection.connect(); //데이터베이스 연결
//read를 위한 API
app.get(`/api/customers`, (req, res) => {
connection.query(
"SELECT * FROM CUSTOMER WHERE isDeleted = 0",
(err, rows, fields) => res.send(rows)
);
});
read API는 render()직후인 componentDidMount()에서 호출하는 것이 가장 자연스럽다고 한다.
React app에선 fetch로 /api/customers 를 GET 방식으로 json 타입으로 가져온다.
/*app.js*/
class App extends Component {
constructor(props) {super(props); /*~~~*/}
componentDidMount() {
this.timer = setInterval(this.progress, 20);
this.callApi()
.then(res => this.setState({ customers: res }))
.catch(err => console.log(err));
}
callApi = async () => {
const response = await fetch(`/api/customers`);
const body = await response.json();
return body;
}
render() {/*~~~*/;}
}
+
데이터 삭제 혹은 추가 후 전체 페이지를 새로고침하지 않고 결과를 반영하기 위한 Read 부분은 다음과 같다.
/* app.js */
class App extends Component {
constructor(props) ;
stateRefresh = () => {
this.setState({
customers: "",
completed: 0,
searchKeyword: ""
});
this.callApi()
.then(res => this.setState({ customers: res }))
.catch(err => console.log(err));
}
callApi = async () => {
const response = await fetch(`/api/customers`);
const body = await response.json();
return body;
}
render() {
return (
/*...*/
//하위컴포넌트인 CustomerAdd에게 전달해주기 위해 stateRefresh 보내줌
<Customer stateRefresh={this.stateRefresh} key={c.id} id={c.id} image={c.image} name={c.name} birthday={c.birthday} gender={c.gender} job={c.job} />;
<CustomerAdd stateRefresh={this.stateRefresh} />
//->add혹은 delete 후 Read API호출해서 고객데이터를 출력하는 부분만 reload해주기 위함
/*...*/
);
}
}
Create
node server.js에선 다음과 같은 방법으로 REST API를 제공한다.
multer : 파일 업로드 시 필요한 미들웨어 github.com/expressjs/multer/blob/master/doc/README-ko.md
//READ에서 필요한 부분에 추가로
const multer = require('multer'); //파일업로드를 위한 node 미들웨어
const upload = multer({ dest: './upload' }); //실제 사진은 서버 ./upload폴더에 저장
//db에는 실제 이미지를 올리는 것이 아닌 서버에서의 해당 파일 경로를 string(varchar)타입으로 저장할 것
// /image/${filename}꼴로 db에 저장하고,
// 서버에서 다시 SELECT 등으로 가져올 땐 image폴더 대신 upload폴더에서 가져오도록 함
app.use('/image', express.static('./upload'));
//create을 위한 API
//upload.single('image'): 폼 데이터 중 name=='image'인 데이터를 upload폴더에 저장하겠다는 의미
app.post('/api/customers', upload.single('image'), (req, res) => {
let sql = 'INSERT INTO CUSTOMER VALUES (null, ?, ?, ?, ?, ?, now(), 0)';
let image = `/image/${req.file.filename}`; //req.file.filename은 multer가 중복없도록 무작위로 할당
let name = req.body.name;
let birthday = req.body.birthday;
let gender = req.body.gender;
let job = req.body.job;
let params = [image, name, birthday, gender, job];
connection.query(sql, params,
(err, rows, fields) => {
res.send(rows);
});
});
es6표준 fetch가 아닌 axios의 post 사용. (이유: hoorooroob.tistory.com/entry/React-React-Naive-TIPS-axios-%EC%99%80-fetch-%EC%96%B4%EB%96%A4-%EA%B2%83%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C)
<form>에 묶인 <input>들을 보내지 않고, FormData와 state를 post를 사용해서 서버로 보냄
class CustomerAdd extends Component {
constructor(props);
/*...*/
//axios의 post 사용
//input name:'image' value:this.state.file
//input name:'name' value:this.state.userName....
addCustomer = () => {
const url = `/api/customers`;
const formData = new FormData();
formData.append(`image`, this.state.file);
formData.append(`name`, this.state.userName);
formData.append(`birthday`, this.state.birthday);
formData.append(`gender`, this.state.gender);
formData.append(`job`, this.state.job);
formData.append(`fileName`, this.state.fileName);
//파일 전송을 위해 multer와 같이 쓰려면 반드시 multipart/form-data
const config = {
headers: {
'content-type': 'multipart/form-data'
}
}
return post(url, formData, config);
}
/*...*/
render() {
const { classes } = this.props;
return (
/*...*/
<input className={classes.hidden} accept="image/*" id="raised-button-file" type="file" file={this.state.file} value={this.state.fileName} onChange={this.handleFileChange} /> <br />
<label htmlFor="raised-button-file">
<Button variant="contained" component="span" name="file">
{this.state.fileName === "" ? "프로필 이미지 선택" : this.state.fileName}
</Button>
</label><br />
<TextField label="이름" type="text" name="userName" value={this.state.userName} onChange={this.handleValueChange} /><br />
<TextField label="생년월일" type="text" name="birthday" value={this.state.birthday} onChange={this.handleValueChange} /><br />
<TextField label="성별" type="text" name="gender" value={this.state.gender} onChange={this.handleValueChange} /><br />
<TextField label="직업" type="text" name="job" value={this.state.job} onChange={this.handleValueChange} /><br />
/*...*/
<Button variant="contained" color="primary" onClick={this.handleFormSubmit}>추가</Button>
)
}
};
Delete
app.delete('/api/customers/:id', (req, res) => {
let sql = `UPDATE CUSTOMER SET isDeleted = 1 WHERE id = ?`;
let params = [req.params.id];
connection.query(sql, params,
(err, rows, fields) => {
res.send(rows);
});
});
deleteCustomer(id) {
const url = `/api/customers/${id}`;
fetch(url, {
method: "DELETE"
});
this.props.stateRefresh();
}
AWS 연동
www.youtube.com/watch?v=G6O-u6FkjpY&list=PLRx0vPvlEmdD1pSqKZiTihy5rplxecNpz&index=10
기타
react-dev-server : create-react-app이 제공. 수정사항을 실시간으로 반영할 수 있도록 하여 개발을 도와줌.
->이거때문에 node.js server로 GET,POST 등 요청을 보내려면 proxy설정이 필요하다.
proxy: 서버에서 처리할 수 없는 요청을 프록시에 등록된 주소들로 보낼 수 있도록 함.
filter(search)기능
state==input value인 경우 handleValueChange함수로 실시간으로 바꿔줌
로딩창 띄우는 기능
ui 적용법
Dialog 열고 닫힘 조절
github.com/lsn1106/React-Management-Tutorial