2022. 8. 17. 17:49ㆍReact/한입 크기로 잘라 먹는 리액트(React.js)
자 먼저 새 일기쓰기 버튼을 클릭해서 나오게되는 New페이지를 한번 제작해보자.
먼저 상단의 헤더를 아래의 코드와 같이 구현해보자.
import { useNavigate } from "react-router-dom";
import MyHeader from "./../components/MyHeader";
import MyButton from "./../components/MyButton";
const New = () => {
const navigate = useNavigate();
return (
<div>
<MyHeader
headText={"새 일기쓰기"}
leftChild={
<MyButton text={"< 뒤로가기"} onClick={() => navigate(-1)} />
}
/>
</div>
);
};
export default New;
그럼 다음으로 오늘은 언제인가요의 부분을 제작해보자.
먼저 날짜를 들고오기위해 아래를 알아보자.
Date.prototype.toISOString()
toISOString() 메서드는 단순화한 확장 ISO 형식(ISO 8601)의 문자열을 반환합니다. 반환값은 언제나 24글자 또는 27글자(각각 YYYY-MM-DDTHH:mm:ss.sssZ 또는 ±YYYYYY-MM-DDTHH:mm:ss.sssZ)입니다. 시간대는 언제나 UTC이며 접미어 "Z"로 표현합니다.
const event = new Date('05 October 2011 14:48 UTC');
console.log(event.toString());
// expected output: Wed Oct 05 2011 16:48:00 GMT+0200 (CEST)
// (note: your timezone may vary)
console.log(event.toISOString());
// expected output: 2011-10-05T14:48:00.000Z
출처 : https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString
그래서 우리는 0부터 9까지 사용하기 위해 slice를 이용하여 날짜를 들고온다.
const getStringDate = (date) => {
return date.toISOString().slice(0, 10);
};
그래서 날짜를 불러오는 useState에 현날짜를 toISOString로 잘라서 넣으면 되는 것이다.
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import MyHeader from "./../components/MyHeader";
import MyButton from "./../components/MyButton";
const getStringDate = (date) => {
return date.toISOString().slice(0, 10);
};
const New = () => {
// 초기 값을 toISOString로 현 date를 잘라서 넣어주면 된다.
const [date, setDate] = useState(getStringDate(new Date()));
const navigate = useNavigate();
return (
<div>
<MyHeader
headText={"새 일기쓰기"}
leftChild={
<MyButton text={"< 뒤로가기"} onClick={() => navigate(-1)} />
}
/>
<div>
<section>
<h4>오늘은 언제인가요?</h4>
<div className="input-box">
<input
className="input-date"
value={date}
onChange={(e) => setDate(e.target.value)}
type="date"
/>
</div>
</section>
</div>
</div>
);
};
export default New;
근데 둘러보면 각 내용만 다를 뿐 다른 프레임들은 동등한것을 우리는 알 수 있는데 그래서 내용물을 component로 따로 분류하여 프레임과 내용물을 한번 분리시켜 보자.
이때 옮길때 경로처리는 주의해야한다!
components/DiaryEditor.js
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import MyHeader from "./MyHeader";
import MyButton from "./MyButton";
const getStringDate = (date) => {
return date.toISOString().slice(0, 10);
};
const DiaryEditor = () => {
// 초기 값을 toISOString로 현 date를 잘라서 넣어주면 된다.
const [date, setDate] = useState(getStringDate(new Date()));
const navigate = useNavigate();
return (
<div className="DiaryEditor">
<MyHeader
headText={"새 일기쓰기"}
leftChild={
<MyButton text={"< 뒤로가기"} onClick={() => navigate(-1)} />
}
/>
<div>
<section>
<h4>오늘은 언제인가요?</h4>
<div className="input_box">
<input
className="input_date"
value={date}
onChange={(e) => setDate(e.target.value)}
type="date"
/>
</div>
</section>
</div>
</div>
);
};
export default DiaryEditor;
pages/New.js
import DiaryEditor from "../components/DiaryEditor";
const getStringDate = (date) => {
return date.toISOString().slice(0, 10);
};
const New = () => {
return (
<div>
<DiaryEditor />
</div>
);
};
export default New;
App.css 추가
/* DiaryEditor */
.DiaryEditor {
}
.DiaryEditor section {
margin-bottom: 40px;
}
.DiaryEditor h4 {
font-size: 22px;
font-weight: bold;
}
.DiaryEditor .input_date {
border: none;
border-radius: 5px;
background-color: #ececec;
padding-top: 10px;
padding-bottom: 10px;
padding-left: 20px;
padding-right: 20px;
cursor: pointer;
font-family: 'Nanum Pen Script';
font-size: 20px;
}
그럼 두번째 섹션이 다음과 같이 출력된다.
다음은 오늘의 감정 부분을 제작해 보자
먼저 감정에 관한 배열의 데이터를 만들어보자.
<section>
<h4>오늘의 감정</h4>
<div className="input_box emotion_list_wrapper">
{emotionList.map((it) => (
<div key={it.emotion_id}>{it.emotion_descript}</div>
))}
</div>
</section>
그럼 아이템들을 또 다른 컴포넌트로 빼서 다시 작성해보자.
component/EmotionItem.js
const EmotionItem = ({ emotion_id, emotion_img, emotion_descript }) => {
return (
<div className="EmotionItem">
<img src={emotion_img} />
<span>{emotion_descript}</span>
</div>
);
};
export default EmotionItem;
component/DiaryEditor.js
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import MyHeader from "./MyHeader";
import MyButton from "./MyButton";
import EmotionItem from "./EmotionItem";
const env = process.env;
env.PUBLIC_URL = env.PUBLIC_URL || "";
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: "끔찍함",
},
];
const getStringDate = (date) => {
return date.toISOString().slice(0, 10);
};
const DiaryEditor = () => {
// 초기 값을 toISOString로 현 date를 잘라서 넣어주면 된다.
const [date, setDate] = useState(getStringDate(new Date()));
const navigate = useNavigate();
return (
<div className="DiaryEditor">
<MyHeader
headText={"새 일기쓰기"}
leftChild={
<MyButton text={"< 뒤로가기"} onClick={() => navigate(-1)} />
}
/>
<div>
<section>
<h4>오늘은 언제인가요?</h4>
<div className="input_box">
<input
className="input_date"
value={date}
onChange={(e) => setDate(e.target.value)}
type="date"
/>
</div>
</section>
<section>
<h4>오늘의 감정</h4>
<div className="input_box emotion_list_wrapper">
{emotionList.map((it) => (
<EmotionItem key={it.emotion_id} {...it} />
))}
</div>
</section>
</div>
</div>
);
};
export default DiaryEditor;
App.css
.DiaryEditor .emotion_list_wrapper {
display: grid;
grid-template-columns: repeat(5, auto);
gap: 2%;
}
/* EmotionItem */
.EmotionItem {
cursor: pointer;
border-radius: 5px;
padding-top: 20px;
padding-bottom: 20px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.EmotionItem img {
width: 50%;
margin-bottom: 10px;
}
.EmotionItem span {
font-size: 18px;
}
그럼 이 감정들을 클릭할 때에 배경색이 변하도록 이벤트를 주어보자.
먼저 아래와 같이 handleClickEmote함수를 생성하여 오늘의 감정 section에서 handleClickEmote를 전달하자.
그러면 각 감정을 클릭할 때에 다음과 같이 state가 변하는 것을 확인할 수 있다.
그럼 다음과 같이 새로운 prop을 전달하여 다음과 같이 선택된 감정만 true로 반환하는 것을 확인할 수 있다.
그러면 다음과 같이 배경색도 적용해보자.
DiaryEditor.js
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import MyHeader from "./MyHeader";
import MyButton from "./MyButton";
import EmotionItem from "./EmotionItem";
const env = process.env;
env.PUBLIC_URL = env.PUBLIC_URL || "";
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: "끔찍함",
},
];
const getStringDate = (date) => {
return date.toISOString().slice(0, 10);
};
const DiaryEditor = () => {
const [emotion, setEmotion] = useState(3);
// 초기 값을 toISOString로 현 date를 잘라서 넣어주면 된다.
const [date, setDate] = useState(getStringDate(new Date()));
const handleClickEmote = (emotion) => {
setEmotion(emotion);
};
const navigate = useNavigate();
return (
<div className="DiaryEditor">
<MyHeader
headText={"새 일기쓰기"}
leftChild={
<MyButton text={"< 뒤로가기"} onClick={() => navigate(-1)} />
}
/>
<div>
<section>
<h4>오늘은 언제인가요?</h4>
<div className="input_box">
<input
className="input_date"
value={date}
onChange={(e) => setDate(e.target.value)}
type="date"
/>
</div>
</section>
<section>
<h4>오늘의 감정</h4>
<div className="input_box emotion_list_wrapper">
{emotionList.map((it) => (
<EmotionItem
key={it.emotion_id}
{...it}
onClick={handleClickEmote}
isSelected={it.emotion_id === emotion}
/>
))}
</div>
</section>
</div>
</div>
);
};
export default DiaryEditor;
EmotionItem.js
const EmotionItem = ({
emotion_id,
emotion_img,
emotion_descript,
onClick,
isSelected,
}) => {
return (
<div
onClick={() => onClick(emotion_id)}
className={[
"EmotionItem",
isSelected ? `EmotionItem_on_${emotion_id}` : `EmotionItem_off`,
].join(" ")}
>
<img src={emotion_img} />
<span>{emotion_descript}</span>
</div>
);
};
export default EmotionItem;
App.css
.DiaryEditor .emotion_list_wrapper {
display: grid;
grid-template-columns: repeat(5, auto);
gap: 2%;
}
/* EmotionItem */
.EmotionItem {
cursor: pointer;
border-radius: 5px;
padding-top: 20px;
padding-bottom: 20px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.EmotionItem img {
width: 50%;
margin-bottom: 10px;
}
.EmotionItem span {
font-size: 18px;
}
.EmotionItem_off {
background-color: #ececec;
}
.EmotionItem_on_1 {
background-color: #64c964;
color: white
}
.EmotionItem_on_2 {
background-color: #9dd772;
color: white
}
.EmotionItem_on_3 {
background-color: #fdce17;
color: white
}
.EmotionItem_on_4 {
background-color: #fd8446;
color: white
}
.EmotionItem_on_5 {
background-color: #fd565f;
color: white
}
자 그럼 이제 마지막 섹션을 작성해 보자.
App.css
.DiaryEditor textarea {
font-family: 'Nanum Pen Script';
font-size: 20px;
box-sizing: border-box;
width: 100%;
min-height: 200px;
resize: vertical;
border: none;
border-radius: 5px;
background-color: #ececec;
padding: 20px;
}
DiaryEditor.js
import { useState, useRef } from "react";
import { useNavigate } from "react-router-dom";
import MyHeader from "./MyHeader";
import MyButton from "./MyButton";
import EmotionItem from "./EmotionItem";
const env = process.env;
env.PUBLIC_URL = env.PUBLIC_URL || "";
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: "끔찍함",
},
];
const getStringDate = (date) => {
return date.toISOString().slice(0, 10);
};
const DiaryEditor = () => {
const contentRef = useRef();
const [content, setContent] = useState("");
const [emotion, setEmotion] = useState(3);
// 초기 값을 toISOString로 현 date를 잘라서 넣어주면 된다.
const [date, setDate] = useState(getStringDate(new Date()));
const handleClickEmote = (emotion) => {
setEmotion(emotion);
};
const navigate = useNavigate();
return (
<div className="DiaryEditor">
<MyHeader
headText={"새 일기쓰기"}
leftChild={
<MyButton text={"< 뒤로가기"} onClick={() => navigate(-1)} />
}
/>
<div>
<section>
<h4>오늘은 언제인가요?</h4>
<div className="input_box">
<input
className="input_date"
value={date}
onChange={(e) => setDate(e.target.value)}
type="date"
/>
</div>
</section>
<section>
<h4>오늘의 감정</h4>
<div className="input_box emotion_list_wrapper">
{emotionList.map((it) => (
<EmotionItem
key={it.emotion_id}
{...it}
onClick={handleClickEmote}
isSelected={it.emotion_id === emotion}
/>
))}
</div>
</section>
<section>
<h4>오늘의 일기</h4>
<div className="input_box text_wrapper">
<textarea
placeholder="오늘은 어땠나요?"
ref={contentRef}
value={content}
onChange={(e) => setContent(e.target.value)}
/>
</div>
</section>
</div>
</div>
);
};
export default DiaryEditor;
그리고 취소하기와 작성완료 버튼까지 한번 만들어보자.
DiaryEditor.js
import { useState, useRef, useContext } from "react";
import { useNavigate } from "react-router-dom";
import { DiaryDispatchContext } from "./../App";
import MyHeader from "./MyHeader";
import MyButton from "./MyButton";
import EmotionItem from "./EmotionItem";
const env = process.env;
env.PUBLIC_URL = env.PUBLIC_URL || "";
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: "끔찍함",
},
];
const getStringDate = (date) => {
return date.toISOString().slice(0, 10);
};
const DiaryEditor = () => {
const contentRef = useRef();
const [content, setContent] = useState("");
const [emotion, setEmotion] = useState(3);
// 초기 값을 toISOString로 현 date를 잘라서 넣어주면 된다.
const [date, setDate] = useState(getStringDate(new Date()));
const { onCreate } = useContext(DiaryDispatchContext);
const handleClickEmote = (emotion) => {
setEmotion(emotion);
};
const navigate = useNavigate();
const handleSubmit = () => {
if (content.length < 1) {
contentRef.current.focus();
return;
}
onCreate(date, content, emotion);
navigate("/", { replace: true });
};
return (
<div className="DiaryEditor">
<MyHeader
headText={"새 일기쓰기"}
leftChild={
<MyButton text={"< 뒤로가기"} onClick={() => navigate(-1)} />
}
/>
<div>
<section>
<h4>오늘은 언제인가요?</h4>
<div className="input_box">
<input
className="input_date"
value={date}
onChange={(e) => setDate(e.target.value)}
type="date"
/>
</div>
</section>
<section>
<h4>오늘의 감정</h4>
<div className="input_box emotion_list_wrapper">
{emotionList.map((it) => (
<EmotionItem
key={it.emotion_id}
{...it}
onClick={handleClickEmote}
isSelected={it.emotion_id === emotion}
/>
))}
</div>
</section>
<section>
<h4>오늘의 일기</h4>
<div className="input_box text_wrapper">
<textarea
placeholder="오늘은 어땠나요?"
ref={contentRef}
value={content}
onChange={(e) => setContent(e.target.value)}
/>
</div>
</section>
<section>
<div className="control_box">
<MyButton text={"취소하기"} onClick={() => navigate(-1)} />
<MyButton
text={"작성완료"}
type={"positive"}
onClick={handleSubmit}
/>
</div>
</section>
</div>
</div>
);
};
export default DiaryEditor;
App.css
.DiaryEditor .control_box {
display: flex;
justify-content: space-between;
align-items: center;
}
위와 같이 새로운 글이 업로드 된것을 확인할 수 있다.
'React > 한입 크기로 잘라 먹는 리액트(React.js)' 카테고리의 다른 글
7-9. DIARY 구현하기 (/DIARY) (0) | 2022.08.24 |
---|---|
7-8. EDIT 구현하기 (/EDIT) (0) | 2022.08.23 |
7-6. HOME 구현하기 (0) | 2022.08.11 |
7-5. 프로젝트 기초공사 2 (프로젝트 상태관리 기초 공사하기) (0) | 2022.08.10 |
7-4. 프로젝트 기초공사 1 (폰트, 공용 레이아웃, 이미지 에셋, 공용 컴포넌트 제작하기), Flex설명, vh, vw, em, rem 등 (0) | 2022.08.01 |