DEVELOP
article thumbnail

인프런 이정한님의 한입크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지 ] 강의 수강 후 강의의 내용을 정리하며 공부한 것을 쓴 게시글입니다.

 

한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지 - 인프런 | 강의

개념부터 독특한 프로젝트까지 함께 다뤄보며 자바스크립트와 리액트를 이 강의로 한 번에 끝내요. 학습은 짧게, 응용은 길게 17시간 분량의 All-in-one 강의!, - 강의 소개 | 인프런...

www.inflearn.com


1. 페이지 구현 - 홈(/)

1.1. Header 세팅하기 

홈 - 헤더

 - Home의 헤더에는 가운데 텍스트로 현재 리스트의 년/월이 있고, 왼쪽에는 이전 월로 이동하는 버튼, 오른쪽에는 이후 월로 이동하는 버튼이 있다.

- 현재 리스트의 날짜 (curDate) 는 state로 관리

<javascript />
const [curDate, setCurDate] = useState(new moment()); const headText = curDate.format("YYYY년 MM월");

- 왼쪽과 오른쪽 버튼을 클릭할 때마다 curDate의 월이 1씩 증가되고 감소해야 함

- moment 객체 특성 상, add 연산시 원래 값이 바뀌어버리기 때문에 clone()을 만들어 add 연산함 

<javascript />
const increaseMonth = () => { setCurDate(curDate.clone().add(1, "months")); }; const decreaseMonth = () => { setCurDate(curDate.clone().add(-1, "months")); };
<javascript />
<MyHeader headText={headText} leftChild={<MyButton text="<" onClick={decreaseMonth} />} rightChild={<MyButton text=">" onClick={increaseMonth} />} />

1.2. 일기 데이터를 날짜에 맞게 불러오기

diaryList 컴포넌트

- 아직 일기 작성 로직을 만들지 않았으므로, 임시로 dummyData 생성하고 진행

- Home 컴포넌트에서 useContext로 불러와서 state로 관리함 (data)

<javascript />
const diaryList = useContext(DiaryStateContext); const [data, setData] = useState([]);

- curDate가 바뀌면 해당 월마다 나타나는 일기 리스트가 다르기 때문에 각각의 일기가 몇월에 작성이 되었는지 구분되어야 함

- curDate의 clone을 만들고, moment객체의 startOf를 이용해 월의 시작일을 format함 (firatDay)

- curDate의 clone을 만들고, moment객체의 endOf를 이용해 월의 마지막일을 format함 (lastDay)

- diaryList의 date를 moment객체로 만들고. firstDay 와 lastDay 사이에 있는지 구분해 setData함 

<javascript />
useEffect(() => { if (diaryList.length > 0) { const firstDay = curDate .clone() .startOf("month") .format("YYYY-MM-DDThh:mm"); const lastDay = curDate.clone().endOf("month").format("YYYY-MM-DDThh:mm"); setData( diaryList.filter((it) => moment(it.date).isBetween(firstDay, lastDay)) ); } }, [diaryList, curDate]);

- DiaryList컴포넌트 생성

<javascript />
const DiaryList = ({ diaryList }) => { return ( <div> {diaryList.map((it) => ( <div key={it.id}>{it.content}</div> ))} </div> ); }; DiaryList.defaultProps = []; export default DiaryList;

일기 출력


1.3. 최신순 / 오래된 순으로 일기 정렬하기

- 최신순 / 오래된 순  select 추가하기

- sortType을 setState로 관리

- select의 value 값이 바뀌면 해당 값으로 setSortType 하도록 함 (lateset or oldest) 

<javascript />
const sortOptionList = [ { value: "latest", name: "최신순" }, { value: "oldest", name: "오래된순" }, ]; const ControlMenu = ({ value, onChange, optionList }) => { return ( <select value={value} onChange={(e) => onChange(e.target.value)}> {optionList.map((it, idx) => ( <option key={idx} value={it.value}> {it.name} </option> ))} </select> ); };
<javascript />
const [sortType, setSortType] = useState("latest"); return ( <div> <ControlMenu value={sortType} onChange={setSortType} optionList={sortOptionList} /> {diaryList.map((it) => ( <div key={it.id}>{it.content}</div> ))} </div> );

select 추가

- 최신순 / 오래된 순 따라 정렬하기

- 원래 배열인 diaryList를 sort하면 원본배열이 바뀌므로 새 배열(copyList)에 깊은 복사하여 그 배열을 sort 

- 깊은 복사 : JSON.parse(JSON.stringify(diaryList))

- sortType에 따라 다르게 sort하도록 compare함수 정의

- moment 객체의 diff 함수를 활용해 compare함수 정의 

<javascript />
const getProcessedDiaryList = () => { const compare = (a, b) => { if (sortType === "oldest") { return moment(a.date).diff(moment(b.date)); } else if (sortType === "latest") { return moment(b.date).diff(moment(a.date)); } }; const copyList = JSON.parse(JSON.stringify(diaryList)); const sortedList = copyList.sort(compare); return sortedList; };

- ControlMenu 컴포넌트를 불러올 떄 넘겨주는 diaryList를 getProcessedDiaryList()로 변경함 

<javascript />
<ControlMenu value={sortType} onChange={setSortType} optionList={sortOptionList} />

최신순 / 오래된 순으로 정렬된 모습 


1.4. 좋은 감정만 / 안좋은 감정만 일기 모아보기

- 필터를 state로 관리

- 초기값은 all 

<javascript />
const [filter, setFilter] = useState("all");

- 모두 / 좋은 감정만 / 안좋은 감정만 select 추가하기 

<javascript />
const filterOptionList = [ { value: "all", name: "모두" }, { value: "good", name: "좋은 감정만" }, { value: "bad", name: "안좋은 감정만" }, ];
<javascript />
<ControlMenu value={filter} onChange={setFilter} optionList={filterOptionList} />

- 필터의 value에 따라 일기 아이템을 return할 filterCallback 함수 정의 

- emtion이 3이하이면 좋은 감정이고, 3보다 크면 나쁜 감정으로 구분함  

<javascript />
const filterCallback = (item) => { if (filter === "good") { return parseInt(item.emotion) <= 3; } else if (filter === "bad") { return parseInt(item.emotion) > 3; } };

- getProcessedDiaryList 함수에 감정에 따라 필터링한 filterList 정의 

- 현재 filter가 all 이면 복사한 함수를 그대로 반환하고, all이 아니면 위에서 정의한 filterCallback 함수에  따라 필터링한 아이템들만 filterList에 저장 

- filterList 를 최신순 / 오래된 순으로 정렬하고 리턴함 

<javascript />
const copyList = JSON.parse(JSON.stringify(diaryList)); const filterList = filter === "all" ? copyList : copyList.filter((it) => filterCallback(it)); const sortedList = filterList.sort(compare); return sortedList;

좋은 감정만 / 안좋은 감정만 필터링 


1.5. 새 일기쓰기 버튼 추가

- useNavigate, MyButton import 

<javascript />
import { useNavigate } from "react-router-dom"; import MyButton from "./../components/MyButton";

- Control Menu 아래에 버튼 추가 

   -  onClick함수에 navigate("/new") 추가해 일기 생성 페이지로 이동하도록 함

<javascript />
<div className="right_col"> <MyButton type={"positive"} text={"새 일기쓰기✏️"} onClick={() => navigate("/new")} /> </div>

- 스타일링

<css />
/* DiaryList */ .DiaryList .menu_wrapper { margin-top: 20px; margin-bottom: 20px; display: flex; justify-content: space-between; } .DiaryList .menu_wrapper .right_col { flex-grow: 1; } .DiaryList .menu_wrapper .right_col button { width: 100%; } .DiaryList .ControlMenu { margin-right: 10px; border: none; border-radius: 5px; background-color: #ececec; padding: 10px 20px 10px 20px; cursor: pointer; font-family: "Nanum Myeongjo", serif; font-size: 18px; }


1.6. 일기 리스트 

- DiaryItem 컴포넌트 추가 

<javascript />
{getProcessedDiaryList().map((it) => ( <DiaryItem key={it.id} {...it} /> ))}

 

- 감정 이미지 

<javascript />
<div className={[ "emotion_img_wrapper", "emtion_img_wrapper_" + emotion, ].join(" ")} > <img src={process.env.PUBLIC_URL + `assets/emotion${emotion}.png`} /> </div>

- 감정 이미지 스타일링

<css />
/* DiaryItem */ .DiaryItem { padding: 15px 0px 15px 0px; border-bottom: 1px solid #e2e2e2; display: flex; justify-content: space-between; } .DiaryItem .emotion_img_wrapper { cursor: pointer; min-width: 120px; height: 80px; border-radius: 5px; display: flex; justify-content: center; } .DiaryItem .emotion_img_wrapper_1 { background-color: #64c964; } .DiaryItem .emotion_img_wrapper_2 { background-color: #9dd772; } .DiaryItem .emotion_img_wrapper_3 { background-color: #fdce17; } .DiaryItem .emotion_img_wrapper_4 { background-color: #fd8446; } .DiaryItem .emotion_img_wrapper_5 { background-color: #fd565f; } .DiaryItem .emotion_img_wrapper img { width: 70%; }

- 일기 내용과 수정하기 버튼 

<javascript />
<div onClick={() => navigete("/diary/" + id)} className="info_wrapper"> <div className="diary_date">{strDate}</div> <div className="diary_content">{content}</div> </div> <div className="btn_wrapper"> <MyButton text={"수정하기"} onClick={() => navigete("/edit/" + id)} /> </div>

- 일기 내용과 수정하기 버튼 스타일링

<css />
.DiaryItem .info_wrapper { cursor: pointer; flex-grow: 1; margin-left: 20px; } .DiaryItem .diary_date { font-weight: bold; font-size: 15px; margin-bottom: 5px; } .DiaryItem .diary_content { font-size: 18px; } .DiaryItem .btn_wrapper { min-width: 70px; } .DiaryItem .btn_wrapper button { font-size: 15px; }

 

profile

DEVELOP

@JUNGY00N