์ด๋ฉด์ ์ผ์ฃผ์ผ์ด ์ด๋ ๊ฒ ์งง์์ง ๋ชฐ๋์ต๋๋ค.. ๐
๊นํ ์๋๋ฐญ์๋ fork ํด์์ ๊ทธ๋ฐ์ง ์ฌ๋ผ๊ฐ์ง ์์ง๋ง, ๋งค์ผ ์ปค๋ฐ๋ ํ๊ณ ์์ด๋๋ค ใ .ใ
๋ญ๊ฐ ์์ฆ ํ๋ฃจ๋ ๊ณต๋ถ + ๋ธ๋ก๊น ์ด๋ฉด ์ ๋ง ๋๋ฑ์ ๋๋น
๊ทผ๋ฐ ์ฐ๋ฆฌ ๊ทธ๋ฐ ๋ง์ด ์์์์. ์ด์ด์น์ด์ด๋ผ๊ณ ใ
์ด ์ํ์์ ์ฝ๋์คํ ์ด์ธ ์คํฐ๋๊น์ง ๋ค์ด๊ฐ์ต๋๋ค :)
์ ๋ง ์ข์ ์คํฐ๋ ์ฅ๋์ด ๊ณ์ ์คํฐ๋์ฌ์ ํธ์ํํ ๋ ธ๋ฆฌ๊ธฐ๋ง ํ๋ค๊ฐ ๋ค์ด์๋ค์ ๐
์ ๊ทธ๋ฆฌ๊ณ ํ๊ณ ๋ก์ ์ฃผ๊ณ ๋ฐ๋ ์๊ฐ์ด ์์๋๋ฐ ์ ๋ธ๋ก๊ทธ๋ฅผ ๋ด์ฃผ์ ๋ค๊ณ ํ์ ๋ถ๋ค
(์ ๊ฐ ์์ธ ์๋จ์๋ผ ๋ง์ ์ ๋ชปํด์..) โฃ๏ธ ๋๋ฆผ๋๋ค.
์์ผ๋ก ๋์ฑ๋ ํฐ์ง๋๋ก ๋ฌ๋ฆด ์์ ์ ๋๋ค. ์ฐ๋ฃ๋ ์ถฉ๋ถํฉ๋๋ค.
์์ฝ์, ์ค๋๋ ํ์ดํ ๐ฅ๐ฅ
๐ ์~ ๊ทธ๋ผ ์ถ๋ฐ~! ๐
์ ๋ฒ์ ์๊ณ ๋ผ์คํ
์ด์ธ ํ๊ณ ๋ฅผ ์์ฑํ์ ๋ 1์ฐจ๋ก ์๋ฃํ ์ฌํญ์ ๋ค์๊ณผ ๊ฐ๋ค.
๊ทธ๋ฆฌ๊ณ ์ด์ด์ ์งํํ 2์ฐจ ์์ ToDO๋ ๋ค์๊ณผ ๊ฐ์๋ค.
๊ฒฐ๋ก ์ ์ผ๋ก๋ ์ผ๋ถ๋ ์ํํ์ง๋ง, ์ผ๋ถ๋ ์ํํ์ง ๋ชปํ๋ค.
๋์ ๋ค๋ฅธ ๊ธฐ๋ฅ์ด ์ถ๊ฐ๋์๋ค.
์ด ๊ธฐ๋ฅ์ ์ ์ ์ด ํ๋ ค์ ์๊ฐ์ด ์กฐ๊ธ ๋ชจ์๋๋ค ๐
๊ตฌํ์ ์๋ฃํ ๋ฆฌ์คํธ๋ฅผ ์์ฑํ์๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
์ด์ ๊ฐ๊ฐ ์ด๋ป๊ฒ ์งํํ๋์ง ๋ฆฌ๋ทฐํด๋ณด๋ฉด์, ๋ฏ์ด๋ณด์ !
1. ๊ธฐ์กด ํด๋ผ์ด์ธํธ ์ฝ๋๋ค์ ์ปดํฌ๋ํธํ์์ผ ๋ฆฌํฉํ ๋ง
์ ์ Html ์ฝ๋๋ฅผ React ์์ผ๋ก ๋ณํํ๋ ์์ ์ ์ฒ์์ด๋ผ ๋ฏธ์ํ๊ธฐ๋ ํ๊ณ , ์๊ฐ์ด ์์ด ๋ถ๋ง์กฑ์ค๋ฌ์ ๋ค.
๊ฐ์ธ์ ์ผ๋ก ๋ฉ์๋๋ค์ ์ถ๊ฐ์์ผ ์ ์ฉ์ํค๋ ๊ฒ์ด ๋ฉ์ธ์ด์๋ค๊ณ ์๊ฐํ๊ธด ํ์ง๋ง,
์ด ์์
๋ํ ๋งค์ฐ ๋์๋๋ ์๊ฐ์ด์๋ค๊ณ ์ค์ค๋ก ์๊ฐํ๋ฉฐ ๋ง์กฑํ๊ณ ์๋ค.
์ผ๋จ ๊ธฐ์กด ํด๋ผ์ด์ธํธ ์ฝ๋ App.js์ ์ปดํฌ๋ํธ 4๊ฐ๋ฅผ ์ถ๊ฐ์์ผ ๋ฆฌํฉํ ๋ง์ ์งํํ๋ค.
๊ธฐ์กด์ ๋นํด, ๋ฉ์๋๊ฐ ์ถ๊ฐ๋์์์๋ ๋ถ๊ตฌํ๊ณ ์ฝ๋๊ฐ ๋งค์ฐ ๊ฐ๊ฒฐํด์ง ๊ฒ์ ์ ์ ์๋ค.
import React, { useState, useEffect } from "react";
import "./App.css";
import axios from "axios";
import { Form, Discussions } from "./components";
const App = () => {
const [discussions, setDiscussions] = useState([]);
const [error, setError] = useState(null);
const addDiscussion = ({ title, author, bodyText }) => {
const newDiscussionData = {
title,
author,
bodyHTML: bodyText,
};
axios
.post("http://localhost:4000/discussions/", newDiscussionData)
.then((response) => {
if (response.status === 201) {
getDiscussion();
}
})
.catch((error) => {
setError("Failed to add discussion.");
});
};
const getDiscussion = () => {
axios
.get("http://localhost:4000/discussions/")
.then((response) => {
setDiscussions(response.data);
})
.catch((error) => {
setError("Failed to fetch discussions.");
});
};
const deleteDiscussion = (id) => {
axios
.delete(`http://localhost:4000/discussions/${id}`)
.then((response) => {
if (response.status === 202 || response.status === 204) {
getDiscussion();
}
})
.catch((error) => {
setError("Failed to delete discussion.");
});
};
useEffect(() => {
getDiscussion();
}, []);
return (
<main>
<div className="form__info">
<h1>My Agora States</h1>
<Form addDiscussion={addDiscussion} />
</div>
<div className="form__active">
{error ? (
<div className="error">{error}</div>
) : (
<Discussions
discussions={discussions}
deleteDiscussion={deleteDiscussion}
/>
)}
</div>
</main>
);
};
export default App;
์ถ๊ฐ๋ ์ปดํฌ๋ํธ๋ค์ ์๊ฐํ๊ฒ ๋ค.
1. Discussion.js
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faTrashAlt } from "@fortawesome/free-solid-svg-icons";
// ๊ธฐ์กด map์ผ๋ก ๋ฟ๋ ค์ฃผ๋ ๋์์ธ ๋ด๋ถ Props ์ญํ ์ ๋์ฒด (DiscussionItem)
// discussion ๊ฐ์ฒด์ deleteDiscussion ํจ์๋ฅผ ๋ฐ์์์ ํ๋ฉด์ ํ์ํ๋ ์ญํ
export const Discussion = ({ discussion, deleteDiscussion }) => {
const { id, url, avatarUrl, title, author, createdAt, answer } = discussion;
return (
<li className="discussion__container">
<div className="discussion__avatar--wrapper">
<img
className="discussion__avatar--image"
src={avatarUrl}
alt="avatar"
/>
</div>
<div className="discussion__content">
<h2 className="discussion__title">
<a href={url}>
{title.length > 53 ? `${title.slice(0, 53)}...` : title}
</a>
</h2>
<div
className="discussion__information"
dangerouslySetInnerHTML={{
__html: `${author} / ${new Date(createdAt).toLocaleTimeString()}`,
}}
></div>
</div>
<div className="discussion__answered">
<p>{answer ? "โ
" : "โ"}</p>
</div>
// ์ญ์ ๋ฒํผ fontawesome package๋ก ์์ด์ฝ์ผ๋ก ๊ตฌํ & ๊ธฐ๋ฅ ์ถ๊ฐ
<button
className="discussion__deleteButton"
onClick={() => deleteDiscussion(id)}
>
<FontAwesomeIcon icon={faTrashAlt} />
</button>
</li>
);
};
2. Discussions.js
import { Discussion } from "./Discussion";
// ๊ธฐ์กด map์ผ๋ก ๋ฟ๋ ค์ฃผ๋ DiscussionItem ์ปดํฌ๋ํธ ์ญํ ๋์ฒด
// discussions ๋ฐฐ์ด์ ๋ฐ์์์ Discussion ์ปดํฌ๋ํธ๋ฅผ ์ฌ๋ฌ ๊ฐ ์์ฑํ์ฌ ํ๋ฉด์ ๋ํ๋ด๋ ์ญํ
export const Discussions = ({ discussions, deleteDiscussion }) => {
return (
<section className="discussion__wrapper">
<ul className="discussions__container">
{discussions.map((discussion) => {
return (
<Discussion
key={discussion.id}
discussion={discussion}
deleteDiscussion={deleteDiscussion}
/>
);
})}
</ul>
</section>
);
};
3. Form.js
export const Form = ({ addDiscussion }) => {
// ์ ์ถ ๋ฒํผ์ ๋๋ฅผ ์, form ์์ฑ ์ ๋ณด๋ฅผ ๋๊ฒจ์ฃผ๋ ์ญํ
const handleSubmit = (event) => {
event.preventDefault();
const author = event.target[0].value;
const title = event.target[1].value;
const bodyText = event.target[2].value;
addDiscussion({ author, title, bodyText });
};
return (
<section className="form__container">
<form action="" method="get" className="form" onSubmit={handleSubmit}>
<div className="form__input--wrapper">
<div className="form__input--name">
<label htmlFor="name">Enter your name: </label>
<input type="text" name="name" id="name" required />
</div>
<div className="form__input--title">
<label htmlFor="title">Enter your title: </label>
<input type="text" name="title" id="title" required />
</div>
<div className="form__textbox">
<label htmlFor="story">Your question: </label>
<textarea
id="story"
name="story"
placeholder="์ง๋ฌธ์ ์์ฑํ์ธ์"
required
></textarea>
</div>
</div>
<div className="form__submit">
<input type="submit" value="submit" />
</div>
</form>
</section>
);
};
4. Index.js
export * from "./Form";
export * from "./Discussions";
export * from "./Discussion";
2. POST, PUT, DELETE ๋ฉ์๋ ์๋ฒ์ ๊ตฌํ
๋จผ์ ์๋ฒ์ ๊ตฌํํ๊ณ ํด๋ผ์ด์ธํธ์ ๊ด๋ จ ๊ธฐ๋ฅ๋ค์ Merge ํ๋ ๊ฒ์ด ๋ง๋ค๊ณ ์๊ฐํ์ฌ, ์๋ฒ๋ฅผ ๋จผ์ ๊ตฌํํ๋ค.
๋ผ์ฐํฐ๋ฅผ ๊ตฌํํ๋ ๊ฒ์ ์ผ๋ ์๋์์ง๋ง... ์ปจํธ๋กค๋ฌ๊ฐ ์ญ์ ๋ณธ์ฒด์๋.. ๋๋ฌด ํ๋ค์๋ค.. ใ
1. server/router/discussions.js
const { discussionsController } = require("../controller");
const { findAll, findById, createOne, updateById, deleteById } =
discussionsController;
const express = require("express");
const router = express.Router();
// TODO: ๋ชจ๋ discussions ๋ชฉ๋ก์ ์กฐํํ๋ ๋ผ์ฐํฐ๋ฅผ ์์ฑํฉ๋๋ค.
router.get("/", findAll);
// TODO: :id์ ๋ง๋ discussion์ ์กฐํํ๋ ๋ผ์ฐํฐ๋ฅผ ์์ฑํฉ๋๋ค.
router.get("/:id", findById);
// โจ TODO: discussion ํ๋๋ฅผ ์์ฑํ๋ ๋ผ์ฐํฐ๋ฅผ ์์ฑํฉ๋๋ค.
router.post("/", createOne);
// โจ TODO: discussion ํ๋๋ฅผ ์์ ํ๋ ๋ผ์ฐํฐ๋ฅผ ์์ฑํฉ๋๋ค.
router.put("/:id", updateById);
// โจ TODO: discussion ํ๋๋ฅผ ์ญ์ ํ๋ ๋ผ์ฐํฐ๋ฅผ ์์ฑํฉ๋๋ค.
router.delete("/:id", deleteById);
module.exports = router;
2. server/controller/index.js
const { agoraStatesDiscussions } = require("../repository/discussions");
const discussionsData = agoraStatesDiscussions;
// ์์ฒญ ๋ณธ๋ฌธ(form ์์ฑ ์ ๋ณด)์ ๊ฒ์ฌํ์ฌ ํ์ํ ๋ฐ์ดํฐ๊ฐ ์๋์ง ์ฌ๋ถ๋ฅผ ํ์ธํ๊ณ ,
// ๋ฐ์ดํฐ๊ฐ ์๊ฑฐ๋ ์๋ชป๋ ๊ฒฝ์ฐ์๋ ์ ์ ํ ์๋ต์ ๋ณด๋ด๋ ์ญํ
const handleRequestBody = (req, res) => {
if (!req.body) return res.status(400).send("no request body");
const { title, author, bodyHTML } = req.body;
if (!title && !author && !bodyHTML)
return res.status(400).send("bad request");
return true;
};
const discussionsController = {
findAll: (req, res) => {
const { author } = req.query;
if (author) {
res.json(discussionsData.filter((item) => item.author === author));
} else {
res.json(discussionsData);
}
},
findById: (req, res) => {
const { id } = req.params;
if (id) {
const discussion = discussionsData.find((item) => item.id === Number(id));
if (discussion) {
res.json(discussion);
} else {
res.status(404).json({ error: "Discussion not found." });
}
}
},
// ํด๋ผ์ด์ธํธ๋ก๋ถํฐ ๋ฐ์ ์์ฒญ์ ๋ณธ๋ฌธ(req.body)์์ title, author, bodyHTML์ ์ถ์ถํ๊ณ ,
// ์ ํจ์ฑ ๊ฒ์ฌ๊ฐ ํต๊ณผ๋๋ฉด ์๋ก์ด ํ ๋ก (discussion) ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ , ์ด๋ฅผ discussionsData ๋ฐฐ์ด์ ๊ฐ์ฅ ์์ ์ถ๊ฐํ๋ ์ญํ
createOne: (req, res) => {
const { title, author, bodyHTML } = req.body;
if (handleRequestBody(req, res) !== true) return;
const id = parseInt(Math.random() * 10000);
const avartarId = parseInt(Math.random() * 100);
const url =
"https://github.com/codestates-seb/agora-states-fe/discussions/" + id;
const newDiscussion = {
id,
createdAt: new Date().toISOString(),
title,
url,
author,
answer: null,
bodyHTML,
avatarUrl: `https://randomuser.me/api/portraits/men/${avartarId}.jpg`,
};
discussionsData.unshift(newDiscussion);
return res.status(201).send("resource created successfully: ID " + id);
},
// ํด๋ผ์ด์ธํธ๋ก๋ถํฐ ๋ฐ์ ์์ฒญ์ ๋ณธ๋ฌธ(req.body)์ ์ด์ฉํ์ฌ ํ ๋ก ์ ์
๋ฐ์ดํธ๋ฅผ ์ํํ๋ ์ญํ
updateById: (req, res) => {
if (handleRequestBody(req, res) !== true) return;
const idx = discussionsData.findIndex(
(d) => d.id === Number(req.params.id)
);
const updated = {
...discussionsData[idx],
...req.body,
updatedAt: new Date().toISOString(),
};
if (idx !== -1) {
discussionsData.splice(idx, 1, updated);
return res.status(200).send("resource updated successfully");
} else {
return res.status(404).send("Not found");
}
},
// ํด๋ผ์ด์ธํธ๋ก๋ถํฐ ๋ฐ์ ์์ฒญ์ URL ํ๋ผ๋ฏธํฐ(req.params.id)๋ฅผ ์ด์ฉํ์ฌ ํ ๋ก ์ ์ญ์ ํ๋ ์ญํ
deleteById: (req, res) => {
const idx = discussionsData.findIndex(
(d) => d.id === Number(req.params.id)
);
if (idx !== -1) {
discussionsData.splice(idx, 1);
return res.status(202).send("resource deleted successfully");
} else {
return res.status(404).send("Not found");
}
},
};
module.exports = {
discussionsController,
};
3. POST, PUT, DELETE ๋ฉ์๋ ํด๋ผ์ด์ธํธ์ ๊ตฌํ
์! ํด๋ผ์ด์ธํธ๋ ๊ตฌํํ๋ค๊ฐ ์๋ฌธ์ ์ด ํ๋ ๋ค์์๋ค.
๋ฐ๋ก ๊ธฐ์กด Html ์์ ๋๋ script.js ์ submit ์, discussion ๋ฐฐ์ด ๊ฐ์ฅ ์์ ์ถ๊ฐํด์ฃผ๋
์ฝ๋๊ฐ ์์๋๋ฐ ์ด๋ฒ์ ๊ตฌํํ๋ฉด์ ์ด ๋ถ๋ถ์ด ์๋ฒ์๋ ์์ผ๋, ํด๋ผ์ด์ธํธ์์ ๋น ์ก๋ ๊ฒ์ด์๋ค.
๊ทธ๋ฌ๋ ค๋ ํ๊ณ ๋๊ธฐ๊ณ ์์๋๋ฐ ํ์คํ๊ฒ ์ง๊ณ ๊ฐ์ผํ๋ค๊ณ ์๊ฐํ๋ค.
๊ฒฐ๋ก ์ '๋น ์ ธ๋ ๋๋ค' ์๊ณ , ์ด์ ๋ ๋ค์๊ณผ ๊ฐ๋ค.
๊ธฐ์กด ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋์์๋ ํด๋ผ์ด์ธํธ ์ธก์์ ์ง์ ๋ฐฐ์ด ์์ ํ ๋ก ์ ์ถ๊ฐํ๊ณ ์์๋ค.
๊ทธ๋ฌ๋ ๋ฆฌ์กํธ๋ก ๋ณ๊ฒฝ๋ ์ฝ๋์์๋ ์ํ ๊ด๋ฆฌ์ UI ๊ฐฑ์ ์ ๋ฆฌ์กํธ๊ฐ ๋ด๋นํ๊ณ ์์ผ๋ฉฐ (use ์๋ฆฌ์ฆ),
ํด๋ผ์ด์ธํธ ์ธก์์ ๋ฐฐ์ด ์์ ํ ๋ก ์ ์ถ๊ฐํ๋ ๋์์ ์ง์ ๊ตฌํ๋์ด ์์ง ์๋ค.
- ์ญํ ๋ณ๊ฒฝ: ์ง์ ๋ฐฐ์ด ์์ ํ ๋ก ์ ์ถ๊ฐ โก๏ธ ์ํ ๊ด๋ฆฌ์ UI ๊ฐฑ์
๋ฐ๋ผ์, ๋ฆฌ์กํธ๋ก ๋ณ๊ฒฝ๋ ํด๋ผ์ด์ธํธ ์ธก์์ ๋ฐฐ์ด ์์ ํ ๋ก ์ ์ถ๊ฐํ๋ ๋์์ ์๋ฒ ์ธก์์ ๊ตฌํ๋์ด์ผ ํ๋ค.
ํด๋ผ์ด์ธํธ๋ ์๋ฒ์ ํ ๋ก ์ถ๊ฐ ์์ฒญ์ ๋ณด๋ด๊ณ ,
์๋ฒ์์ ํด๋น ์์ฒญ์ ์ฒ๋ฆฌํ์ฌ ๋ฐฐ์ด ์์ ํ ๋ก ์ ์ถ๊ฐํ๋ ์์
์ ์ํํด์ผ ํ๋ค.
1. App.js
import React, { useState, useEffect } from "react";
import "./App.css";
import axios from "axios";
import { Form, Discussions } from "./components";
const App = () => {
const [discussions, setDiscussions] = useState([]);
// ์๋ฌ ์ฒ๋ฆฌ๋ฅผ useState๋ก ์ํ ๊ด๋ฆฌ
const [error, setError] = useState(null);
// ์๋ก์ด ํ ๋ก ์ ์ถ๊ฐํ๋ ์ญํ
// title (์ ๋ชฉ), author (์์ฑ์), bodyText (๋ณธ๋ฌธ ํ
์คํธ)๋ฅผ ๋งค๊ฐ๋ณ์๋ก ๋ฐ๋๋ค.
// ์๋ก์ด ํ ๋ก ๋ฐ์ดํฐ๋ฅผ ์์ฑํ๊ณ , ์์ฑ๋ ๋ฐ์ดํฐ๋ฅผ ์๋ฒ๋ก ์ ์กํ๋ค.
// ๋ง์ฝ ์์ฒญ์ด ์ฑ๊ณต์ ์ผ๋ก ์๋ฃ๋๋ฉด getDiscussion ํจ์๋ฅผ ํธ์ถํ์ฌ ์
๋ฐ์ดํธ๋ ํ ๋ก ๋ชฉ๋ก์ ๊ฐ์ ธ์ต๋๋ค.
const addDiscussion = ({ title, author, bodyText }) => {
const newDiscussionData = {
title,
author,
bodyHTML: bodyText,
};
axios
.post("http://localhost:4000/discussions/", newDiscussionData)
.then((response) => {
if (response.status === 201) {
getDiscussion();
}
})
.catch((error) => {
setError("Failed to add discussion.");
});
};
// ๋ชจ๋ ํ ๋ก ๋ชฉ๋ก์ ๊ฐ์ ธ์ค๋ ์ญํ
// ์๋ฒ๋ก๋ถํฐ ํ ๋ก ๋ชฉ๋ก์ ๊ฐ์ ธ์์ response.data์ ์ ์ฅํ๊ณ , ์ด๋ฅผ ์ํ์ ์ค์
const getDiscussion = () => {
axios
.get("http://localhost:4000/discussions/")
.then((response) => {
setDiscussions(response.data);
})
.catch((error) => {
setError("Failed to fetch discussions.");
});
};
// ์ฃผ์ด์ง id์ ํด๋นํ๋ ํ ๋ก ์ ์ญ์ ํ๋ ์ญํ
// ์๋ฒ์ ์ญ์ ์์ฒญ์ ๋ณด๋ด๊ณ , ์์ฒญ์ด ์ฑ๊ณต์ ์ผ๋ก ์๋ฃ๋๋ฉด getDiscussion ํจ์๋ฅผ ํธ์ถํ์ฌ ์
๋ฐ์ดํธ๋ ํ ๋ก ๋ชฉ๋ก์ ๊ฐ์ ธ์จ๋ค.
const deleteDiscussion = (id) => {
axios
.delete(`http://localhost:4000/discussions/${id}`)
.then((response) => {
if (response.status === 202 || response.status === 204) {
getDiscussion();
}
})
.catch((error) => {
setError("Failed to delete discussion.");
});
};
// ๋ ๋๋ง๋ ๋์ ์์กด์ฑ ๋ฐฐ์ด์ด ๋ณ๊ฒฝ๋ ๋๋ง๋ค ์คํ
// ์ด๊ธฐ ๋ ๋๋ง ์์ getDiscussion ํจ์๋ฅผ ํธ์ถํ์ฌ ์ด๊ธฐ ํ ๋ก ๋ชฉ๋ก์ ๊ฐ์ ธ์ค๊ธฐ ์ํด ์ฌ์ฉ
useEffect(() => {
getDiscussion();
}, []);
return (
<main>
<div className="form__info">
<h1>My Agora States</h1>
<Form addDiscussion={addDiscussion} />
</div>
<div className="form__active">
{error ? (
<div className="error">{error}</div>
) : (
<Discussions
discussions={discussions}
deleteDiscussion={deleteDiscussion}
/>
)}
</div>
</main>
);
4. ์๋ฒ ์๋ ์, ์ ์์ ์ผ๋ก ํด๋ผ์ด์ธํธ์ ๋ชจ๋ ๋ฉ์๋๊ฐ ์๋ํ๋์ง ํ์ธ
๐ก ์๋ฒ์ ํด๋ผ์ด์ธํธ์ Mock ๋ฐ์ดํฐ ์ฐจ์ด
๐ก ์๋ฒ์ ๋ํ์ ์ธ ์ญํ ์์
์ฌ์ฉ์ ๋ฑ๋ก: ํด๋ผ์ด์ธํธ์์ ์ฌ์ฉ์๊ฐ ํ์ ๊ฐ์ ์ ์์ฒญํ๋ฉด, ์๋ฒ๋ ํด๋น ์์ฒญ์ ๋ฐ์์ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅํ๊ณ , ์ ์ ํ ์๋ต์ ๋ฐํํ๋ค.
๋ฐ์ดํฐ ์ ์ฅ: ํด๋ผ์ด์ธํธ์์ ์์ฑํ ์๋ก์ด ๋ฐ์ดํฐ๋ฅผ ์๋ฒ๋ก ์ ์กํ๋ฉด, ์๋ฒ๋ ํด๋น ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ํ์ผ ์์คํ ์ ์ ์ฅํ๊ณ , ์ ์ฅ ์๋ฃ ํ ํด๋ผ์ด์ธํธ์๊ฒ ์ ์ ํ ์๋ต์ ๋ฐํํ๋ค.
์ธ์ฆ๊ณผ ๊ถํ ๋ถ์ฌ: ํด๋ผ์ด์ธํธ์์ ๋ก๊ทธ์ธ ์์ฒญ์ ๋ณด๋ด๋ฉด, ์๋ฒ๋ ์ฌ์ฉ์์ ์ธ์ฆ์ ๊ฒ์ฆํ๊ณ , ์ ํจํ ์ธ์ฆ์ด๋ฉด ์ก์ธ์ค ํ ํฐ์ ๋ฐ๊ธํ์ฌ ํด๋ผ์ด์ธํธ์๊ฒ ์ ๊ณตํ๋ค. ๋ํ, ํด๋ผ์ด์ธํธ๊ฐ ๋ณด์ ํ ์ก์ธ์ค ํ ํฐ์ ๊ธฐ๋ฐ์ผ๋ก ์์ฒญ๋ ์์ ์ ๋ํ ๊ถํ์ ๊ฒ์ฌํ๊ณ , ํ์ฉ๋์ง ์์ ์์ ์ ๋ํด์๋ ์ ์ ํ ๊ถํ ์ค๋ฅ ์๋ต์ ๋ฐํํ๋ค.
๋น์ฆ๋์ค ๋ก์ง ์ฒ๋ฆฌ: ํด๋ผ์ด์ธํธ๊ฐ ์์ฒญํ ์์ ์ ๋ํด ๋น์ฆ๋์ค ๋ก์ง์ ์ํํด์ผ ํ ๊ฒฝ์ฐ, ์๋ฒ๋ ํด๋น ๋ก์ง์ ์คํํ๊ณ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ํด๋ผ์ด์ธํธ์๊ฒ ๋ฐํํ๋ค. ์๋ฅผ ๋ค์ด, ์ํ ๊ตฌ๋งค ์์ฒญ์ ๋ํด์๋ ์ฌ๊ณ ์ฒดํฌ, ๊ฒฐ์ ์ฒ๋ฆฌ, ๋ฐฐ์ก ์ ๋ณด ์ ๋ฐ์ดํธ ๋ฑ์ ๋น์ฆ๋์ค ๋ก์ง์ ์ํํ ์ ์๋ค.