[EZ_Scheduler] - [React, Express] 회원가입 페이지 제작(POST 요청)
[React, Express] 회원가입 페이지 제작(POST 요청)
[EZ_Scheduler] - [React, Express] 서버 구현 및 데이터베이스 연결하기(REST API) [React, Express] 서버 구현 및 데이터베이스 연결하기(REST API)[EZ_Scheduler] - [React] 로그인 페이지 스타일링 [React] 로그인 페이
juyear-coding.tistory.com
이전 개발 일지 읽어보기!
📌일정 추가 페이지 제작
안녕하세요, 대학생 개발자 주이어입니다!
오늘은 일정 추가 페이지를 만들고 schedule 테이블을 생성하여 데이터를 가져오고 추가하는 것 까지 만들어보려고 합니다!
재미있게 읽어주세요!
✅프로젝트 목표
- 일정 추가 페이지 만들기
- schedule 테이블 생성 및 GET 구현
- POST 구현 및 실시간 연동 테스트
ℹ️기술 스택
- 프론트엔드 : Html, Css
- 백엔드 : Node.js
- 프레임워크 : React.js, Express.js
- 데이터베이스 : MySQL
- 빌드도구 : Vite
1. 일정 추가 페이지 만들기 - React.js
1-1. 일정 추가 페이지 스타일링

갤탭으로 간단하게 해주었던 UI/UX를 보면서 제작을 해주었다.
1
2
3
4
5
6
7
8
9
10
11
12
|
import AddSchedule from "../components/AddSchedule";
const AddSchedulePage = () => {
return (
<div>
<AddSchedule></AddSchedule>
</div>
);
};
export default AddSchedulePage;
|
cs |
먼저 AddSchedulePage.jsx에서는 AddSchedule을 불러와 렌더링만 하게 해주었다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
return (
<div className="AddSchedule">
<form className="FormClass" onSubmit={handleSubmit}>
<input
className="inputTitle"
value={input.title}
name="title"
type="text"
placeholder="title"
onChange={onChangeInput}
/>
<textarea
className="description"
value={input.des}
name="des"
type="text"
placeholder="description"
onChange={onChangeInput}
/>
<div className="flexInput2">
<input
value={getStringedDate(input.startDate)}
name="startDate"
type="date"
onChange={onChangeInput}
/>
<input
value={getStringedDate(input.endDate)}
name="endDate"
type="date"
onChange={onChangeInput}
/>
</div>
<div className="flexInput">
<p>인원 : </p>
<input
value={input.person}
name="person"
type="number"
placeholder="person"
onChange={onChangeInput}
/>
</div>
<div className="flexInput">
<p>일수 : </p>
<input
value={input.day}
name="day"
type="number"
placeholder="days"
onChange={onChangeInput}
/>
</div>
<div className="flexInput">
<p>시간제외 : </p>
<input
value={input.check}
name="check"
type="checkbox"
onChange={onChangeInput}
/>
</div>
</form>
<div className="AddBottom">
<Button onClick={handleSubmit} text={"만들기"}></Button>
</div>
</div>
);
|
cs |
위 코드는 AddSchedule 컴포너트의 렌더링 코드 부분이다.(필자는 return문 부분을 렌더링 코드 부분이라고 부른다.) 먼저 일정 추가 페이지는 제목, 설명, 시작날짜, 종료날짜, 인원, 일수, 추가 설정 등 일정 데이터에 들어갈 값을 입력 받고 서버에 보내야 하기 때문에 form과 input을 사용해서 구현해주었다. 버튼은 처음에 만들었던 Button 컴포넌트를 사용해 간단하게 만들어주었다.
함수 부분과 POST 요청을 보내는 코드는 나중에 추가로 설명하려고 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
.AddSchedule {
display: flex;
flex-direction: column;
text-align: center;
height: 100vh;
justify-content: space-around;
}
.inputTitle {
font-size: 24px;
}
.flexInput {
display: flex;
justify-content: space-around;
margin: 20px 0;
}
.flexInput > input {
width: 8vw;
text-align: center;
margin: 10px 0;
}
.flexInput2 {
display: flex;
justify-content: space-evenly;
margin-top: 18px;
}
.flexInput2 > input {
width: 25vw;
font-size: 12px;
}
.description {
width: 67vw;
height: 25%;
margin: 20px 0 10px 0;
}
.AddBottom {
margin-bottom: 30px;
}
|
cs |
AddSchedule 컴포넌트의 css파일이다. 기본적인 input 태그의 스타일링은 로그인 페이지를 만들 때 해줬기 때문에 그 이외의 추가적인 것들만 간단하게 스타일링 해주었다. (사실 내가 원하는 대로 100% 스타일 되진 않았지만 기능적인게 우선이라 추후에 수정하기로 했다.)

위의 사진은 일정 추가 페이지 화면이다. 완벽하진 않지만 처음에 디자인했던 대로 비슷하게 나와주었다.
1-2. get-Stringed-Date 유틸 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
export const getStringedDate = (targetDate) => {
let year = targetDate.getFullYear();
let month = targetDate.getMonth() + 1;
let date = targetDate.getDate();
if (month < 10) {
month = `0${month}`;
}
if (date < 10) {
date = `0${date}`;
}
return `${year}-${month}-${date}`;
};
|
cs |
특별한건 아니고 날짜 데이터를 YYYY-MM-DD 형식으로 바꿔주는 유틸을 만들어주었다. 화면에 렌더링 할 때 YYYY-MM-DD 형식이 필요했기 때문이다. 위에 렌더링 부분 코드를 보면 이 유틸을 사용하고 있는 것을 알 수 있다.
2. scheduleData 테이블 생성 및 GET 구현
2-1. scheduleData 테이블 생성 - MySQL

위의 사진과 같이 scheduleData 테이블을 생성해주었다. FOREIGN KEY를 이용하여 userData와 연결해주었으며 id, userId, title, des, person, day, startDate, endDate, statusData를 저장하는 테이블이다. 추가적으로 ON DELETE CASCADE를 설정하여 userData의 userId 데이터가 삭제되면 이와 연결되어 있는 scheduleData도 삭제되도록 해주었다.
1
2
3
4
5
|
INSERT INTO userData (userId,userPw) VALUES ("juyear","1234");
INSERT INTO userData (userId,userPw) VALUES ("chicken","1011");
INSERT INTO scheduleData (userId, title, person, day, startDate, endDate, statusData) VALUES ("juyear","일본 여행", 5, 3, "2025-01-07", "2025-01-17", "확정");
INSERT INTO scheduleData (userId, title, person, day, startDate, endDate, statusData) VALUES ("juyear","홍콩 여행", 3, 4, "2025-01-23", "2025-01-26", "진행중");
INSERT INTO scheduleData (userId, title, person, day, startDate, endDate, statusData) VALUES ("chicken","강릉 여행", 2, 5, "2025-01-08", "2025-01-11", "기간만료");
|
cs |
위의 코드는 테스트를 위한 데이터를 추가해주는 쿼리문이다.(des는 일정의 설명을 담는 데이터로 따로 넣어주지 않았다.)
2-2. GET 구현 - Express.js
1
2
3
4
5
6
7
8
9
|
app.get("/scheduledata", (req, res) => {
db.query("SELECT * FROM scheduledata", (err, results) => {
if (err) {
res.status(500).json({ error: "DB 조회 실패" });
} else {
res.json(results);
}
});
});
|
cs |
먼저 위에서 만든 schedule 데이터를 가져오는 GET 함수를 server.js에 만들어주었다. 간단하게 테이블에 연결하여 데이터를 가져오는 식으로 구현하였다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
function App() {
const [isUserData, setIsUserData] = useState(true);
const [isScheduleData, setIsScheduleData] = useState(true);
const loginedUserId = useRef("");
useEffect(() => {
fetch("http://localhost:5000/userdata")
.then((response) => response.json())
.then((d) => {
userdata = d;
setIsUserData(false);
})
.then(() => console.log(userdata))
.catch((error) => console.error("Error:", error));
fetch("http://localhost:5000/scheduledata")
.then((response) => response.json())
.then((d) => {
scheduleData = d;
setIsScheduleData(false);
})
.then(() => console.log(scheduleData))
.catch((error) => console.error("Error:", error));
}, []);
if (isUserData || isScheduleData) {
return <div>데이터 로딩중입니다...</div>;
}
|
cs |
그 후 App.jsx(최상위 페이지)에서 기존에 만들었던 유저데이터를 가져오는 useEffect 함수 아래 일정 데이터를 가져오는 코드를 추가해주었다. 그리고 일정 데이터를 가져왔는지 확인하기 위한 isScheduleData라는 state변수를 만들어 데이터를 가져온 후 렌더링되도록 해주었다. 마지막으로 일정 데이터를 다른 페이지 및 컴포넌트에서 사용할 수 있도록 context를 이용하여 데이터를 전달해주었다.

console.log로 가져온 데이터를 확인해보면 일정 데이터를 잘 가져오는 것을 알 수 있다.
3. POST 구현 및 실시간 연동 테스트
3-1. 데이터 입력받기 - React.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
const [input, setInput] = useState({
title: "",
des: "",
startDate: new Date(),
endDate: new Date(),
person: 1,
day: 1,
check: false,
});
const onChangeInput = (e) => {
let name = e.target.name;
let value = e.target.value;
console.log(name, value);
if (name === "startDate" || name === "endDate") {
value = new Date(value);
}
if (name === "check") {
value = !input.check;
}
if (name)
setInput({
...input,
[name]: value,
});
};
|
cs |
먼저 사용자가 입력한 값을 저장하기 위해서 useState를 사용한 input 객체를 생성해주었다. input 객체는 제목, 설명, 시작 날짜, 종료 날짜, 인원, 일수, 기타 설정 등을 저장한다. 그 후 onChangeInput이라는 함수를 만들어 입력한 값에 변화가 생기면 변화된 값으로 input 객체에 저장되도록 해주었다. 날짜 데이터와 기타 설정 데이터는 일반 데이터 형식이 아닌 Date또는 Bool 형식의 데이터이기 때문에 따로 예외 처리를 해주었다.
3-2. POST 요청 구현 - Express.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
app.post("/addschedule", (req, res) => {
const { title, des, startDate, endDate, person, day, userId } = req.body;
let newStartDate = new Date(startDate);
let newEndDate = new Date(endDate);
const formatStartDate = newStartDate
.toISOString()
.slice(0, 19)
.replace("T", " ");
const formatEndDate = newEndDate.toISOString().slice(0, 19).replace("T", " ");
console.log(title, des, formatStartDate, formatEndDate, person, day, userId);
const query =
"INSERT INTO scheduledata (userId, title, des, person, day, startDate, endDate, statusData) VALUES (?,?,?,?,?,?,?,?)";
db.query(
query,
[
userId.toString(),
title,
des,
person,
day,
formatStartDate,
formatEndDate,
"진행중",
],
(err, result) => {
if (err) {
return res.status(500).json({ error: "DB 삽입 실패" });
}
res.json({ message: "유저 데이터 추가 성공!", id: result.insertId });
}
);
});
|
cs |
server.js에서 일정 데이터 POST 요청을 처리해줄 코드를 작성해주었다. 먼저 MySQL에 날짜 데이터를 저장하기 위해서는 YYYY-MM-DD 형식이어야 하기 때문에 이러한 형식으로 변환해주는 코드를 작성해주었다. 그 후 데이터를 추가하는 쿼리문을 이용하여 요청온 데이터를 추가해주는 코드를 작성해주었다.
3-3. POST 요청 보내기 - React.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
const handleSubmit = async (e) => {
e.preventDefault();
console.log(loginedUserId.current);
const response = await fetch("http://localhost:5000/addschedule", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
...input,
userId: loginedUserId.current,
}),
});
const data = await response.json();
if (response.ok) {
alert("일정 추가 성공!");
nav("/Home", { replace: true });
window.location.reload();
} else {
alert("에러: " + data.error);
}
};
|
cs |
AddScheule.jsx에서 handleSubmit 함수를 만들어 Button 컴포넌트를 클릭했을 때 작동하도록 해주었다. 입력받은 데이터에다가 현재 로그인한 유저의 ID를 더해서 데이터를 전송 해주었다. 왜냐하면 일정이 어느 유저의 일정인지 알아야 하기 때문이다. 만약 일정이 잘 추가되었다면, 다시 Home 페이지로 돌아가지도록 navigate를 사용하였고, 추가된 일정이 바로 반영될 수 있도록 창을 새로고침 해주었다. 새로고침을 사용한 이유에 대해서는 저번 일지에 나와있다.


위의 사진과 같이 만들기 버튼을 누르면 자동으로 Home 페이지로 이동되며, 실시간으로 추가된 데이터가 반영되어 일정 목록에 추가되는 것을 알 수 있다.
실습 마무리
오늘은 React를 사용하여 일정 추가 페이지를 만들고, MySQL 데이터베이스를 사용하여 schedule 테이블 설계, 그리고 마지막으로 schedule 테이블을 사용하는 GET, POST 요청 및 실시간 연동까지 구현해보았습니다. 이제 점점 React에 익숙해지고 있어서 머릿속에서 구현한대로 바로 바로 잘 만들어지는 것 같습니다. 이번 개발 일지에서는 크게 막혔던 부분은 없었던 것 같습니다.(데이터베이스에서 foreign key 연결 부분에서 살짝 고생하긴 했지만...) 사실 오늘 개발 일지에 들어가지 않은 내용이 하나 있는데 바로 로그인한 유저에 따라 일정이 다르게 보이도록 설정해주는 부분이 있었습니다. 이 부분을 다음 일지에 간단하게 적어보려고 합니다. 다음 일지도 기대해주시고 오늘도 읽어주셔서 감사합니다!
[EZ_Scheduler] - [React] localStorage를 이용한 유저별 데이터 가져오기
[React] localStorage를 이용한 유저별 데이터 가져오기
[EZ_Scheduler] - [React, Express] 일정 추가 페이지 제작(GET, POST 구현) [React, Express] 일정 추가 페이지 제작(GET, POST 구현)[EZ_Scheduler] - [React, Express] 회원가입 페이지 제작(POST 요청) [React, Express] 회원가입
juyear-coding.tistory.com
다음 개발 일지 읽어보기!
'[Projects] > [EZ_Scheduler]' 카테고리의 다른 글
[React] 일정 세부 페이지와 Modal Popup 만들기 (5) | 2025.02.18 |
---|---|
[React] localStorage를 이용한 유저별 데이터 가져오기 (7) | 2025.02.16 |
[React, Express] 회원가입 페이지 제작(POST 요청) (5) | 2025.02.14 |
[React, Express] 서버 구현 및 데이터베이스 연결하기(REST API) (6) | 2025.02.13 |
[React] 로그인 페이지 스타일링 (1) | 2025.01.30 |