# NEW 컴포넌트 구현
# Header
기존에 만들어놨던 MyHeader 컴포넌트와 MyButton 컴포넌트를 가져와서
필요한 부분을 바꾸면 헤더를 쉽고 빠르게 구현할 수 있다
<MyHeader
headText={"새 일기쓰기"}
leftChild={
<MyButton text={"< 뒤로가기"} onClick={() => navigate(-1)} />
}
/>
뒤로 가는 방법은 navigate 에 -1을 넣어주면 된다
# 날짜 입력받기
input 태그의 type을 date로 지정해서 일기 작성 날짜를 입력 받을 수 있도록 구현해보자
new 컴포넌트 화면이 렌더링되자마자 오늘의 날짜를 받아와서 input 태그에 띄워주어야 한다
현재 날짜가 어떨게 들어오고 있는지 확인해보니
2024-02-27
과 같은 형식으로 들어오고 있다
그렇다면 해당 형식에 맞춰서 날짜를 받아오자!
날짜 형식을 맞춰주는 getStringDate 함수를 생성하자
const getStringDate = (date) => {
return date.toISOString().slice(0, 10);
};
해당 함수를 useState의 초기값으로 설정해주면 오늘 날짜를 기준으로 날짜를 바로 받아오는 것을 확인할 수 있다
지금까지 NEW 컴포넌트에 구현한 내용들은 EDIT 컴포넌트에서도 동일하게 사용한다
즉 별도의 컴포넌트로 독립시키는 것이 효율적이다!
# DiaryEditor 컴포넌트로 분리
DiaryEditor 컴포넌트를 만들어 기존 코드를 옮기자
import React, { useState } from "react";
import { useNavigate } from "react-router-dom";
//import component
import MyHeader from "./MyHeader";
import MyButton from "./MyButton";
const getStringDate = (date) => {
return date.toISOString().slice(0, 10);
};
const DiaryEditor = () => {
const navigate = useNavigate();
const [date, setDate] = useState(getStringDate(new Date()));
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 DiaryEditor;
옮긴 후 DiaryEditor 컴포넌트를 import 하니 이전과 동일하게 잘 렌더링 되고 있다
CSS 스타일링을 마친 후의 모습이다
# 오늘의 감정 선택 section
현재의 감정을 클릭하면 해당 이미지의 색에 맞춰서 박스의 색도 변한다
DiaryEditor에 감정 정보를 담고 있는 배열을 생성한다
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: "끔찍함",
},
];
해당 감정들을 감정 선택 div에 보여주어야 한다
감정 리스트들을 EmotionItem 컴포넌트로 분할하자
import React from "react";
const EmotionItem = ({ emotion_id, emotion_img, emotion_descript }) => {
return (
<div>
<img src={emotion_img} />
<span>{emotion_descript}</span>
</div>
);
};
export default EmotionItem;
EmotionItem 컴포넌트를 DiaryEditor 컴포넌트에 import 해서 렌더링하면?
모든 이미지들이 잘 렌더링되고 있다
css를 적용해서 어떤 감정이 선택되었는지 확인할 수 있도록 해보자
선택된 감정을 저장해 줄 state를 만들어보자
DiaryEditor 컴포넌트에서 state를 선언한다
const [emotion, setEmotion] = useState(3);
기본 값을 그럭저럭인 3번으로 해주자
클릭 시 다른 감정이 선택되도록 하는 함수를 선언한다
const handleClickEmote = (emotion) => {
setEmotion(emotion);
};
해당 함수를 EmotionItem의 props 로 내려주고 emotion_id 값을 onClick 이벤트에 보내주면 현재 클릭된 감정의 id 값을 받아올 수 있다
EmotionItem 컴포넌트에 isSelected 를 추가해서 현재 클릭된 emotion item의 상태를 true로 바꿔주자
클릭되지 않은 경우에는 아래와 같이 false로 뜨는 것을 볼 수 있다
EmotionItem 컴포넌트에서 isSelected 상태에 따라 className 을 다르게 뜨도록 설정해주면 isSelected 상태에 따라 다른 css 효과를 적용할 수 있다
//div className 지정
<div
onClick={() => onClick(emotion_id)}
className={[
"EmotionItem",
isSelected ? `EmotionItem_on_${emotion_id}` : `EmotionItem_off`,
].join(" ")}
>
클릭된 상태와 그렇지 않은 상태에 대한 css 적용이 잘 되었다
# 오늘의 일기 입력 section
DiaryEditor 컴포넌트에서 textatrea section을 추가하자
textarea 태그를 활용해서 구현해주면 된다
css 적용까지 마친 모습은 아래와 같다
# 하단 버튼 section
MyButton 컴포넌트를 사용해서 하단의 취소하기, 작성완료 버튼을 만들자
<section>
<div className="control-box">
<MyButton text={"취소하기"} onClick={() => navigate(-1)} />
<MyButton text={"작성완료"} type={"positive"} onClick={() => {}} />
</div>
</section>
취소하기 버튼을 누르면 useNavigate 를 사용해서 화면을 뒤로 보내자
또한 작성완료 버튼을 누르면 reducer 로 선언해준 onCreate 함수를 호출해서 일기를 등록해야 한다
DiaryEditor 컴포넌트에서 handleSubmit 함수를 선언하고 작성완료 버튼을 클릭했을 때 실행되도록 해주면 완료!
const handleSubmit = () => {
if (content.length < 1) {
contentRef.current.focus();
return;
}
onCreate(date, content, emotion);
navigate("/", { replace: true });
};
저장까지 잘 되는 최종 모습이다
# 오류 해결 기록
1. 일기 작성 후 저장 버튼 눌렀을 때 onCreate is not a function 에러 발생
▶ 원인
DiaryDispatchContex 에서 value를 {( onCreate, onRemove, onEdit )} 으로 보내주고 있었다!
▶ 해결 방법
구조 분해 할당 문법에 의해 {{ ... }} 를 꼭 지켜야 한다
<DiaryDispatchContext.Provider value={{ onCreate, onRemove, onEdit }}>
2. 일기 작성 후 저장 버튼을 눌렀으나 새로운 일기 리스트에 렌더링 되지 않음
▶ 원인 1
아주 허무하게도 data와 date 오타였다...(은근 헷갈린다)
const onCreate = (date, content, emotion) => {
dispatch({
type: "CREATE",
data: {
id: dataId.current,
date: new Date(date).getTime(),
content,
emotion,
},
});
dataId.current += 1;
};
▶ 원인 2
return state 로 기존의 state만 렌더링 되게 했었다
▶ 해결 방법
return newState 로 새로운 배열을 렌더링해주기!
const reducer = (state, action) => {
let newState = [];
switch (action.type) {
case "INIT": {
return action.data;
}
case "CREATE": {
newState = [action.data, ...state];
break;
}
case "REMOVE": {
newState = state.filter((it) => it.id !== action.targetId);
break;
}
case "EDIT": {
newState = state.map((it) =>
it.id === action.data.id ? { ...action.data } : it
);
break;
}
default:
return state;
}
return newState;
};
# EDIT 구현하기
우선 라우팅 설정부터 수정해주어야 한다
edit 컴포넌트의 path variable을 아래와 같이 수정하자
<Route path="/edit/:id" element={<Edit />} />
# Edit 컴포넌트 로직 구현
useParams을 사용해서 현재 일기 데이터의 id 값을 받아오자
useEffect를 사용해서 id 또는 diaryList 에 변화가 일어나면 해당 id를 가진 diary의 정보를 찾도록 한다
useEffect(() => {
if (diaryList.length >= 1) {
const targetDiary = diaryList.find(
(it) => parseInt(it.id) === parseInt(id)
);
}
}, [id, diaryList]);
DiaryEditor 컴포넌트를 그대로 가져와서 사용하되
필요한 부분은 수정해주고 일기 데이터의 내용을 DiaryEditor에 반영해주면 완성이다
# 새로 알게 된 점
자주 사용할 컴포넌트를 독립시키는 것이 나중에 얼마나 편리한지
감정 일기장 강의를 들으며 정말 많이 느꼈다
지금까지는 전체적인 레이아웃 정도만 분리했었는데 버튼, 입력폼, 헤더 등을 분리하여
필요한 곳에 알맞게 수정하여 사용하니 코드도 간결해지고 개발 과정도 매우 빠르고 간단하다
이처럼 자주 사용되는 컴포넌트를 분리하는 습관을 들여야겠다
'TIL > 프로그래머스 데브코스' 카테고리의 다른 글
클라우딩 어플리케이션 엔지니어링 TIL Day 37, 38 - 감정 일기장 만들기 (6~7) (0) | 2024.03.01 |
---|---|
클라우딩 어플리케이션 엔지니어링 TIL Day 36 - 감정 일기장 만들기 (5) (0) | 2024.02.29 |
클라우딩 어플리케이션 엔지니어링 TIL Day 34 - 감정 일기장 만들기 (3) (1) | 2024.02.27 |
클라우딩 어플리케이션 엔지니어링 TIL Day 33 - 감정 일기장 만들기 (2) (0) | 2024.02.23 |
클라우딩 어플리케이션 엔지니어링 TIL Day 32 - 감정 일기장 만들기 (1) (0) | 2024.02.23 |