프론트엔드를 만들다가 하나 간과한 것이 데이터베이스에서 데이터를 가져오려면 연결을 먼저 성공시켜야 한다는 것이었다. AXIOS를 통해 데이터를 불러와도 연결이 되어 있지 않아서 불러올 수가 없으니 내가 잘 만들고 있는 것인지 화면은 잘 구성되어 있는 것인지 알 수 있는 방법이 없었다. 그래서 프론트엔드를 완성하기 전에 백엔드와 프론트엔드를 연결하고, 다시 프론트엔드를 완성하고자 한다.
미리 만들어진거에 적용하는게 생각보다 어려워서 일단 리액트와 node.js를 새로운 폴더를 만들어서 거기서 연결한 후에 지금까지 적은 코드들을 적용할 예정이다.
같이 교육을 듣는 친구가 프록시 없이 리덕스 자체로 연결을 했다고 해서 그 방법을 찾을려고 했는데 안찾아지고 잘 모르겠어서 프록시를 이용해서 서버를 통합해보고자한다.
https://whiteknight3672.tistory.com/264
이 블로그를 참고하며 연결했다.
그리고 이전에 했던 방식을 살펴보면 서버의 index.js(app.js의 역할)에서 리액트에서 빌드한 html 파일을 연결한 것이었는데, 사실 값을 받아오지 못하면 뜨는 로딩중...이 잘뜨고 있다. 그런데 값을 받아오지 못하는 이유는 포트번호 때문이 아닐지 생각이 들었다.
일단 지금까지 한 것을 살펴보도록 하겠다.
프론트엔드 (client)
App.js
import Todolist from "./todolist";
function App() {
return (
<>
<Todolist />
</>
);
}
export default App;
index.js
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
todolist.js
import axios from 'axios';
import { useEffect } from 'react';
import { useState } from 'react';
export default function Todolist () {
const [todos, setTodos] = useState([]);
const [loading, setLo] = useState(true);// 데이터를 제대로 받아오면 false 아니면 true
useEffect(() => {
const tododata = async () => {
const res = await axios({
method: "GET",
url: "/todos"
});
console.log(res.data.data);
setTodos(res.data.data);
setLo(false);
}
tododata();
}, []);
const Add_todolist = async () => {
const update_data = document.querySelector('.todo_text').value
const res = await axios({
method: "POST",
url: "/todos",
data: {
title: update_data,
done: 0,
},
});
todos.push({
id: res.id,
title: res.title,
done: res.done,
});
}
return (
<>
<div>
{
loading ? (
<h1>로딩중...</h1>
) : (
<>
<input className='todo_text' type='text' placeholder='추가할 todolist 작성'></input><button onClick={Add_todolist}>추가</button>
<ul>
{todos.map((data) => {
return <li key={data.id}>{data.title}</li>;
})}
</ul>
</>
)
}
</div>
</>
);
}
백엔드(server) : 컨트롤러는 넘어가고 연결되는 코드만 보여주도록하겠다.
router.js
const express = require('express');
const router = express.Router();
const controller = require('../controller/Ctodo');
const path = require('path');
//리액트 연결
router.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'client/build/index.html'));
});
router.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'client/build/index.html'));
});
router.get('/todos', controller.get_todo);
router.post('/todos', controller.post_todo);
router.delete('/todo/:todoId', controller.del_todo);
router.patch('/todo/:todoId', controller.patch_todo);
module.exports = router;
index.js
const express = require('express');
const app = express();
const PORT = 8080;
const db = require('./models');
const path = require('path');
// app.set('view engine', 'ejs'); //ejs를 사용할 때 설정
//body-parser
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(express.static(path.join(__dirname, 'client/build')));
//router
const useRouter = require('./routes/router');
app.use('/', useRouter);
//404
// app.use('*', (req, res)=>{
// res.render('404');
// });
//서버 오픈
db.sequelize.sync({force : false}).then(()=>{
app.listen(PORT, () => {
console.log(`http://localhost:${PORT}`);
});
});
이 상태에서 리액트는 기본적으로 localhost:3000에서 접근할 수 있도 express는 내가 설정한 localhost:8080에서 접근할 수 있다. 이 접근을 같은 곳에서 하게 해주면 되는 것 같다.
이는 node에서 cors를 설정해주고, React에서 proxy를 설정해주면된다. 위에서는 앞서 말했듯이 리액트에서 빌드를 한 후에 백엔드에서 app.use로 경로를 설정해주고, 라우터에서 빌드 파일과 연결을 해준 상태다. 이 다음 단계는 Concurrently와 http-proxy-middleware를 설치한 후 적용해야한다.
먼저 Concurrently를 설치해주자.
npm i concurrently
두 개를 모두 포함하고 있는 파일에 설치해준다. 그리고 client 폴더로 이동해서
npm i http-proxy-middleware
를 설치 해준다.
concurrently는 한 개의 터미널에서 두 개의 서버 작동이 가능하도록 해주는 역할을 한다. package.json의 script 섹션을 다음과 같이 추가해주면 npm run dev를 통해 두 서버를 동시에 실행시킬 수 있다.
"scripts": {
"test": "node app",
"dev": "concurrently \"npm run dev:server\" \"npm run dev:client\"",
"dev:server": "cd server && node index.js",
"dev:client": "cd client && npm start"
},
리액트에서 setup.js를 만들어주고
const { createProxyMiddleware } = require("http-proxy-middleware");
module.exports = function (app) {
app.use(
createProxyMiddleware("/api/", {
target: "http://localhost:8080",
changeOrigin: true,
})
);
};
이렇게 만들어주면 이제 localhost:3000/api 이하의 경로로 시작되는 요청이 http://localhost:5000/api 에서 처리되기 때문에 axios를 이용할 수 있다.
근데 계속 axios를 받아오지 못한다ㅠㅠ 이유를 도저히 모르겠다 리더님한테 가서 물어봐야겠다.
Node.js와 React 연결 성공!
드디어 해결했다 Concurrently와 http-proxy-middleware를 이용하는 것은 다 수동으로 할 수 있는 동작이었다.
터미널을 두개를 열어서 서버와 리액트에서 각각 실행해주면 되고, 프록시 없이 리액트에서 http://localhost8080/todos로 연결을하면 된다. 그래서 데이터베이스의 정보를 가져오는 것은 성공했다.
서버 터미널
클라이언트 터미널과 주소
이런 방식으로 하면 (위를 보면 리액트 앱인 client에서 주소를 호출할때는 node.js에서 연결된 localhost:8080으로 url을 지정해준 것을 확인할 수 있다.)
데이터가 넘어온것을 확인할 수 있다. 이제 다시 작업을 해보자
여기서 문제점은 받아노 데이터가 객체이기 때문에 map함수를 사용해서 화면에 띄우는 것은 어렵다는 것이다. 그래서 계속 고민을 하다가
여기서 Object.valuse함수를 통해 객체를 배열로 바꿀 수 있다는 것을 알게 되었고, 바로 적용하였다.
연결 하는 법을 알았고 이를 기반으로 투두리스트 추가와 리스트 불러오기 기능은 완성했다. 다음 포스팅에서 리스트 불러오기와 추가한 부분까지 정리를 한 후에 수정과 삭제 기능을 구현하고, css까지 완성할 예정이다.