DEVELOP
article thumbnail

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

 

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

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

www.inflearn.com


페이지 구현 - 홈(/)

Header 세팅하기 

홈 - 헤더

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

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

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

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

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

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

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

diaryList 컴포넌트

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

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

  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함 

  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컴포넌트 생성

const DiaryList = ({ diaryList }) => {
  return (
    <div>
      {diaryList.map((it) => (
        <div key={it.id}>{it.content}</div>
      ))}
    </div>
  );
};

DiaryList.defaultProps = [];
export default DiaryList;

일기 출력


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

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

- sortType을 setState로 관리

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

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>
  );
};
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함수 정의 

  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()로 변경함 

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

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


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

- 필터를 state로 관리

- 초기값은 all 

  const [filter, setFilter] = useState("all");

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

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

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

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

    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 를 최신순 / 오래된 순으로 정렬하고 리턴함 

    const copyList = JSON.parse(JSON.stringify(diaryList));
    const filterList =
      filter === "all" ? copyList : copyList.filter((it) => filterCallback(it));

    const sortedList = filterList.sort(compare);

    return sortedList;

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


새 일기쓰기 버튼 추가

- useNavigate, MyButton import 

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

- Control Menu 아래에 버튼 추가 

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

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

- 스타일링

/* 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;
}


일기 리스트 

- DiaryItem 컴포넌트 추가 

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

 

- 감정 이미지 

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

- 감정 이미지 스타일링

/* 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%;
}

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

      <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>

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

.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