
NEW
์ผ๋จ ํค๋์์๋ ์ผ์ชฝ ๋ฒํผ์ผ๋ก ๋ค๋ก๊ฐ๊ธฐ ํ๋,
์ค๋ ๋ ์ง๋ฅผ ์ ๋ ์น์
, ๊ฐ์ ์ ์ ํํ๋ ์น์
, ๊ธ์ ์ ๋ ์น์
๊ทธ๋ฆฌ๊ณ ์ ์กํ๋ ๋ฒํผ๋ค๋ก ๋๋์ด ๋ณธ๋ค
New.js
Header
<MyHeader
headtext={"์๋ก์ด ์ผ๊ธฐ ์ฐ๊ธฐ"}
leftchild={
<MyButton text={"< ๋ค๋ก ๊ฐ๊ธฐ"} onClick={() => navigate(-1)} />
}
/>
import ํด์ค๋ ๊ฑฐ๋ navigate ์ง์ ํ๋ ๊ฑด ์๋ตํ๋๋ก ํ๋ค.ใ ใ ๋ด๋ง๋๋ก~

๊ทผ๋ฐ ์ฌ๊ธฐ์, ๋ ์ง ๋ถ๋ถ, ๊ฐ์ ๋ถ๋ถ, ๊ธ ๋ถ๋ถ์ด edit ํ์ด์ง๋ ๋๊ฐ์ด ๋ง๋ค์์ ์ด๋ผ
์ด๋ถ๋ถ์ ์ปดํฌ๋ํธ๋ก ๋ฐ๋ก ๋ถ๋ฆฌ์์ผ์
์๋ก ๋ง๋ค ํ์์์ด ๋๋ค ๊ฐ๋ค ์จ๋ณด๊ธฐ๋ก ํ๋ค.(์ฌ์ค ๋์ค์ ๊นจ๋ซ๊ณ ์ฎ๊นใ
์ฒ์๋ถํฐ ๊ฒฐ์ ํ๋ ์ฒ)
components/DiaryEditor.js
๋ ์ง ์ ๋ ฅ
์นธ ๋ง๋ค๊ธฐ
const [date, setDate] = useState();
...
<div>
<section>
<h4>์ค๋์ ์ธ์ ์ธ๊ฐ์?</h4>
<div className="input-box">
<input
type="date"
value={date}
onChange={(e) => setDate(e.target.value)}
/>
</div>
</section>
</div>
๊น์งํ๋ฉด,

์ด๋ ๊ฒ ๋์ค๊ณ , ๋ componentsํญ์์ ํ์ธํด๋ณด๋ฉด ๋ ์ง๋ฅผ ๋๋ ์ ๋ setDate๋ก ๊ฐ์ด ์ ๋ค์ด๊ฐ๋ ๊ฑธ ํ์ธํ ์ ์๋ค.
์ค๋ ๋ ์ง๋ฅผ ๊ธฐ๋ณธ๊ฐ์ผ๋ก
// ๋ ์ง ๊ฐ์ ๋ฐ๋๋ค
const getStringDate = (date) => {
return date.toISOString().slice(0, 10);
};
console.log(getStringDate(new Date()));

toISOString
๋ฅผ ์ด์ฉํ๋ฉด YYYY-MM-DDTHH:mm:ss.sssZ ๋ผ๊ณ ๋์ค๊ธฐ ๋๋ฌธ์ slice๋ก ์งค๋ผ ์ค ๊ฒ์.
const [date, setDate] = useState(getStringDate(new Date()));
์ด๊ฒ์ input์ ์ด๊น๊ฐ์ผ๋ก ๋ฃ์ด์ฃผ๋ฉด ์์ฑ

๊ฐ์ ์ ํ
const emotionList = [
{
emotion_id: 1,
emotion_img: process.env.PUBLIC_URL + `/assets/emotion1.png`,
emotion_descript: "๋งค์ฐ ์ข์",
},
{
emotion_id: 2,
emotion_img: process.env.PUBLIC_URL + `/assets/emotion2.png`,
emotion_descript: "์ข์",
},
{
emotion_id: 3,
emotion_img: process.env.PUBLIC_URL + `/assets/emotion3.png`,
emotion_descript: "๋ณดํต",
},
{
emotion_id: 4,
emotion_img: process.env.PUBLIC_URL + `/assets/emotion4.png`,
emotion_descript: "๋์จ",
},
{
emotion_id: 5,
emotion_img: process.env.PUBLIC_URL + `/assets/emotion5.png`,
emotion_descript: "๋์ฐํจ",
},
];
<section>
<h4>์ค๋์ ๊ธฐ๋ถ์ ์ด๋ค๊ฐ์?</h4>
<div className="input_box emotion_list_wrap">
{emotionList.map((it) => (
<div key={it.emotion_id}>{it.emotion_descript}</div>
))}
</div>
</section>

๊ฐ์ ์ ๋๋ฅด๋ฉด ๋ ์ ํ๋๊ฒ ํด์ผ๋๊ณ ํ๊ธฐ ๋๋ฌธ์ ๋ฐ๋ก ์ปดํฌ๋ํธ๋ฅผ ๋ถ๋ฆฌํด์ค.
๋ค์ด์ด๋ฆฌ ์๋ํฐ์๋ ๊ฐ์ฒด ๋ฐฐ์ด๋ก ์ ๋ฆฌํด๋จ๊ธฐ๋๋ฌธ์
๊ฑ๋ค๋ค์ ํ๋กญ์ผ๋ก ๋ฐ๋๋ก ๋ง๋ค์ด์ฃผ๋ฉด ๋จ
EmotionItem.js
const EmotionItem = ({ emotion_id, emotion_img, emotion_descript }) => {
return (
<div className="EmotionItem">
<img src={emotion_img} />
<span>{emotion_descript}</span>
</div>
);
};
DiaryEditor.js
<div className="input_box emotion_list_wrap">
{emotionList.map((it) => (
<EmotionItem key={it.emotion_id} {...it} />
))}
</div>
App.css
์ฒ์์ผ๋ก ์ ๋ css ์ ๋ณด
.DiaryEditor .emotion_list_wrap {
display: grid;
grid-template-columns: repeat(5, auto);
}
.EmotionItem {
cursor: pointer;
border-radius: 5px;
padding: 20px 0 20px 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}

display : grid๋ ๊ทธ๋ฆฌ๋๋ฅผ ๋ง๋๋ ์์ฑ์ด๋ค
grid-template-columns: repeat(5, auto) ๋,
5๊ฐ๋ฅผ ๋์ด์ ํ ๊ฑฐ๊ณ ์ฌ์ด์ฆ๋ auto๋ก ์ค์ ํ๋ค๋ ๋ง์ด๋ค.
๊ทธ๋ฌ๋ ์ฌ๊ธฐ์ ์ด๋ฏธ์ง ํฌ๊ธฐ๋ก ์ฌ์ด์ฆ๊ฐ ๋จ.
์ฌ๊ธฐ์ gap: x%๋ฅผ ์ถ๊ฐํด์ฃผ๋ฉด ๊ฐ๊ฒฉ์ด ๋ฒ์ด์ง๋ค
๋๋ 2ํผ์ผํธ๋ก ํจ!
์ด์ ์ ํ๋ ๊ฐ์ ์ ๋ฐ์๋ณด์
์ผ๋จ DiaryEditor์์ ํจ์๋ฅผ ๋ง๋ค๊ณ state๋ ๋ง๋ ๋ค.
๊ทธ๋์ EmotionItem์์ ๋ฐ์์ค๋ฉด ๋จ
DiaryEditor.js
const DiaryEditor = () => {
const [emotion, setEmotion] = useState(3);
const handleClickEmote = (emotion) => {
setEmotion(emotion);
};
...
<section>
<h4>์ค๋์ ๊ธฐ๋ถ์ ์ด๋ค๊ฐ์?</h4>
<div className="input_box emotion_list_wrap">
{emotionList.map((it) => (
<EmotionItem
key={it.emotion_id}
{...it}
onClick={handleClickEmote}
/>
))}
</div>
</section>
์ผ๋จ ๊ธฐ๋ณธ ์ํ๋ 3์ผ๋ก ๋๊ณ ,
emotion์ ๋ฐ์์ ๋๋ฅด๋ฉด ์ํ๊ฐ ๋ฐ๋๊ฒ ๋ง๋ค์ด์ค๋ค.
๊ทธ๋ฆฌ๊ณ onClick์ด๋ฒคํธ๋ฅผ ๊ฐ ์ด๋ชจ์
์์ดํ
์ ๋ฌ์์ค.
EmotionItem.js
<div className="EmotionItem" onClick={() => onClick(emotion_id)}>
<img src={emotion_img} />
<span>{emotion_descript}</span>
</div>
๊ทธ๋ฆฌ๊ณ EmotionItem์์ props๋ก onClick๋ ๋ฐ์์,
๊ฐ div๋ฅผ ๋๋ ์ ๋ emotion_id๋ฅผ ๋ฐ์์ค๋๋ก ํด์ค๋ค.
๊ทธ๋์ ๊ทธ ๋ฐ์์จ id์ handleClickEmoteํจ์๊ฐ ์คํ๋๋ฉด์ ๊ฐ์ ์ํ๋ฅผ ๋ฐ๊พธ์ด ์ค.

3์ด ๊ธฐ๋ณธ๊ฐ์ด๊ณ ๋๋ฅผ๋๋ง๋ค ๋ฐ๋๋ ๊ฑฐ ๋ณผ ์ ์๋ค ์ข ๋๋ฆฌ์ง๋ง,,
๊ทธ๋ฆฌ๊ณ DiaryEditor์์ map ๋ถ๋ถ์ isSelected={it.emotion_id === emotion}๋ฅผ ์ถ๊ฐํด์ฃผ๋ฉด, ์ ํ๋ id๊ฐ ํ์ฌ ์ํ์ ๊ฐ์ ๊ณผ ๊ฐ์ ๊ฒฝ์ฐ true๋ฅผ ์ถ๋ ฅํด์ฃผ๊ฒ ๋๋ค

์ด ์ํ์์ 3๋ฒ ๊ทธ๋ฆผ์ ๋๋ฅด๋ฉด isSelected๊ฐ true๋ก ๋ฐ๋
๊ทธ๋์ ์์ ์ด ์ ํ๋์๋์ง ์ ํ๋์ง ์์๋์ง ์ ์๊ฐ ์๊ฒ ๋๋ค.
๊ทธ๋ฆฌ๊ณ EmotionItem.js ๋ก ๋์์์, ์ ํ๋ ๊ทธ๋ฆผ๋ง ๋ฐ๋ก ๋ชจ์์ ์ฃผ๊ธฐ ์ํด
className์ ๋ฐฐ์ด๋ก ๋ฐ๊ฟ์ค๋ค.
const EmotionItem = ({
emotion_id,
emotion_img,
emotion_descript,
onClick,
isSelected,
}) => {
return (
<div
className={[
"EmotionItem",
isSelected ? `EmotionItem_on_${emotion_id}` : `EmotionItem_off`,
].join(" ")}
onClick={() => onClick(emotion_id)}
>
...
์ผ๋จ ์ด๋ฆ์ EmotionItem ์ธ๋ฐ, ๋ง์ฝ์ ์ ํ๋์๋ค๋ฉด ~on๊ณผ id ์ซ์๊ฐ ๋ถ๊ณ ์๋๋ผ๋ฉด off๋ฅผ ๋ถ์ธ๋ค
๊ทธ๋ฆฌ๊ณ ์ ๋ฒ์ ํ๋ ๊ฒ์ฒ๋ผ ๋ฐฐ์ด์ด๊ธฐ ๋๋ฌธ์ join(' ')์ ํด์ฃผ์ด์ผ ํจ.
๊ทธ๋ ๊ฒ ๋๋ฉด ์์์์ ์ด๋ ๊ฒ ๋๋ ๊ฑธ ํ์ธํ ์ ์๋ค.

2๋ฒ์ ๋๋ฅด๋ฉด 2๋ฒ๋ง _on_2๊ฐ ๋ถ๊ณ ๋๋จธ์ง๋ off๊ฐ ๋ ๊ฑธ ๋ณผ ์ ์์.

๊ทธ๊ฑธ ๋ฐํ์ผ๋ก css ์ค์ ์ ํด์ฃผ์ด์ ์์์ ๋ฐ๊พธ์ด ๋ณด์๋ค.
์ผ๊ธฐ ์์ฑ
DiaryEditor.js
const DiaryEditor = () => {
//๋น์ด์์ ์ ์๋์ผ๋ก ํฌ์ปค์คํ๋ ๊ธฐ๋ฅ์ ์ํ
const contentRef = useRef(0);
const [content, setContent] = useState("");
...
<section>
<h4>์ค๋์ ์ผ๊ธฐ</h4>
<div className="input_box text_wrap">
<textarea
placeholder="์ค๋ ํ๋ฃจ๋ฅผ ๊ธฐ๋กํด๋ณด์ธ์!"
ref={contentRef}
value={content}
onChange={(e) => setContent(e.target.value)}
/>
</div>
</section>
App.css
.DiaryEditor textarea {
font-family: "Humanbumsuk";
font-size: 16px;
box-sizing: border-box;
width: 100%;
min-height: 200px;
resize: vertical;
/* ๊ฐ๋ก๋ก ์ฌ์ด์ฆ ์กฐ์ ์ ํ ์ ์๊ฒ ํจ */
border: none;
border-radius: 5px;
background-color: #ececec;
padding: 20px;
}
์ฌ๊ธฐ์ resize: vertical์ ๊ฐ๋ก๋ก ์ฌ์ด์ฆ ์กฐ์ ์ ํ ์ ์๊ฒ ํ๋ค.
textarea ํน์ฑ์ ํฌ๊ธฐ๋ฅผ ์์ ์์ฌ๋ก ์ค์ ํ ์ ์๋๋ฐ, ๊ฐ๋ก๋ก ์์ง์ด๋ ๊ฑด ์๋ฌด๋๋ ๋ณ๋ก๋ผ
์์ฃผ ์ข์ ์ ๋ณด์๋ ๊ฒ ๊ฐ์!

์ ์ก ๋ฒํผ
DiaryEditor.js
const contentRef = useRef(0);
const { onCreate } = useContext(DiaryDispatchContext);
const handleSubmit = () => {
//
if (content.length < 1) {
contentRef.current.focus();
return;
}
onCreate(date, content, emotion);
navigate("/", { replace: true });
// ์ผ๊ธฐ ์์ฑํ๋ ํ์ด์ง๋ฅผ ๋ค๋ก๊ฐ๊ธฐ๋ก ๋ชป ์ค๊ฒ ๋ง๋๋ ์์
};
...
<section className="control_box">
<MyButton text={"์ทจ์ํ๊ธฐ"} onClick={() => navigate(-1)} />
<MyButton
text={"์์ฑ์๋ฃ"}
type={"positive"}
onClick={handleSubmit}
/>
</section>
๋ด์ฉ์ด ํ๋๋ ์์ ๊ฒฝ์ฐ ๋ค์ textarea๋ก focusํด์ฃผ๋ ์์
์ ์ํด
useRef๋ฅผ ์ด์ฉํ๋ค.
๊ทธ๋ฆฌ๊ณ contentRef.current.focus() ๋ฃ์ด์ฃผ๋ฉด ๋จ.
๋ ์๊ทธ์ App.js ์์ ์ ์ฅํด๋์๋ onCreate๋ฅผ ๋ถ๋ฌ์จ ๋ค,
handleSubmit์ ๊ทธ ๋ ํ์๋กํ๋ props์ ํจ๊ป ๋ฃ์ด์ค๋ค.
๊ทธ๋ฆฌ๊ณ ๋ง์ง๋ง์ผ๋ก ์ด๋ฒคํธ ํธ๋ค๋ฌ์ ์ถ๊ฐํด์ค.
import { DiaryDispatchContext } from "../App";
const { onCreate } = useContext(DiaryDispatchContext);
๋!
์์ฑ ํ navigate๋ฅผ ์ด์ฉํด์ ํ์ผ๋ก ๋ค์ ์ค๋๋ฐ,
๊ทธ๋ฅ ์ค์ง ์๊ณ ์ผ๊ธฐ ์์ฑํ๋ ํ์ด์ง๋ฅผ ๋ค๋ก๊ฐ๊ธฐ๋ก ๋ชป ์ค๊ฒ ๋ง๋๋ ์์
๊น์ง ์ถ๊ฐํด์ค๋ค.
navigate("/", { replace: true })

๊ทธ๋ฌ๋ฉด ์ด์ ์์ฑํ๋ ๊ฑด ์๋ฃ!

โ
์! ๊ทธ๋ฆฌ๊ณ ์ด์ ํ๋ ค๋ค๊ฐ ์๊ฐ ์์ด์ ๋ชปํ๋
๊ธธ์ด๊ฐ ๋ ๊ธธ๋ฉด ์ง๋ฅด๊ณ '...' ๊น์ง ์ถ๊ฐํด์ฃผ๋ ์์
์ ํ๋ค

<div className="diary_content_prev">
{content.length <= 30 ? content : `${content.slice(0, 30)}...`}
</div>
'Project > Toy-project' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
๊ฐ์ ์ผ๊ธฐ์ฅ ๋ง๋ค๊ธฐ 3 - ์ค๋ฅ ์์ & Local Storage (0) | 2023.05.19 |
---|---|
๊ฐ์ ์ผ๊ธฐ์ฅ ๋ง๋ค๊ธฐ 2 - CRUD ๊ธฐ๋ฅ ๊ตฌํ (4) ์์ธ (0) | 2023.05.19 |
๊ฐ์ ์ผ๊ธฐ์ฅ ๋ง๋ค๊ธฐ 2 - CRUD ๊ธฐ๋ฅ ๊ตฌํ (3) ์์ (0) | 2023.05.19 |
๊ฐ์ ์ผ๊ธฐ์ฅ ๋ง๋ค๊ธฐ 2 - CRUD ๊ธฐ๋ฅ ๊ตฌํ (1) HOME (0) | 2023.05.19 |
๊ฐ์ ์ผ๊ธฐ์ฅ ๋ง๋ค๊ธฐ 1 - ๊ธฐ์ด ์์ (0) | 2023.05.19 |