클라우딩 어플리케이션 엔지니어링 TIL Day 34 - 감정 일기장 만들기 (3)

2024. 2. 27. 10:34· TIL/프로그래머스 데브코스
목차
  1. # 프로젝트 기초 공사 2
  2. # 상태 관리 세팅하기
  3. # dispatch 함수 생성
  4. # Context 생성
  5. # HOME 구현하기
  6. # Header
  7. # DiayList 컴포넌트 구현
  8. # DiaryList 정렬 기능
  9. # DiaryItem 컴포넌트 생성
  10. # 새로 알게 된 점

# 프로젝트 기초 공사 2

전체 프로젝트 구조를 살펴보자

각각의 컴포넌트는 다른 state가 필요하다

우선 App 컴포넌트에서 일기 데이터를 관리할 수 있는 state 를 만들어보자

 

# 상태 관리 세팅하기

useReducer 를 사용해서 App 컴포넌트에서 상태 관리를 세팅하자

const reducer = (state, action) => {
  let newState = [];
  switch (action.type) {
    case "INIT": {
      return action.data;
    }
    case "CREATE": {
      const newItem = {
        ...action.data,
      };
      newState = [newItem, ...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 state;
};

reducer 를 생성해준다

각각의 case 별로 어떤 작업을 수행할지 정리한다

 

# dispatch 함수 생성

만들어준 reducer에 맞춰서 dispatch 함수를 만들어준다

data의 id 값은 useRef 를 사용해서 받는다

  const dataId = useRef(0);

 

# CREATE 함수

  const onCreate = (date, content, emotion) => {
    dispatch({
      type: "CREATE",
      data: {
        id: dataId.current,
        data: new Date(date).getTime(),
        content,
        emotion,
      },
    });
    dataId.current += 1;
  };

# REMOVE 함수

  const onRemove = (targetId) => {
    dispatch({ type: "REMOVE", targetId });
  };

# EDIT 함수

  const onEdit = (targetId, date, content, emotion) => {
    dispatch({
      type: "EDIT",
      data: {
        id: targetId,
        date: new Date(date).getTime(),
        content,
        emotion,
      },
    });
  };

이렇게 작성한 상태 관리 로직의 context를 만들어서 data state를 컴포넌트 트리 전역에 공급해보자

 

# Context 생성

App 컴포넌트 상단에 context 컴포넌트를 생성하고 return 문의 모든 컴포넌트를

DiaryStateContext.Provider 컴포넌트로 감싼다

그리고 value 값으로는 data를 보내주면 전역에서 사용가능하다

export const DiaryStateContext = React.createContext();

//return
    <DiaryStateContext.Provider value={data}>
      <BrowserRouter>
        <div className="App">
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/new" element={<New />} />
            <Route path="/edit" element={<Edit />} />
            <Route path="/diary/:id" element={<Diary />} />
          </Routes>
        </div>
      </BrowserRouter>
    </DiaryStateContext.Provider>

onCreate, onRemove, onEdit 함수를 관리하는 DiaryDispatchContext 컴포넌트도 생성해서

DiaryStateContext 컴포넌트 태그의 하위에 넣어주고 value 값으로 각 함수를 보내주자!


# HOME 구현하기

# Header

화면이 마운트됨과 동시에 상단 헤더에는 현재의 년, 월이 표시되어야 한다

또한 이전달, 다음달로 이동할 수 있는 버튼이 필요하다

1. 년 월 띄우기

날짜를 생성하는 state를 생성해서 MyHeader 컴포넌트에 전달하자

getMonth() 는 1월을 0월로 반환하는 것에 주의해야 한다

  const [curDate, setCurDate] = useState(new Date());
  const headText = `${curDate.getFullYear()}년 ${curDate.getMonth() + 1}월`;

  return (
    <div>
      <MyHeader headText={headText} />
    </div>
  );
};

완성된 모습

 

2. 이전달 다음달 이동 버튼

왼쪽 버튼을 누르면 이전달로, 오른쪽 버튼을 누르면 다음달로 이동할 수 있는 함수를 구현하여

각각의 버튼 컴포넌트에 연결하자

  const increaseMonth = () => {
    setCurDate(
      new Date(curDate.getFullYear(), curDate.getMonth() + 1, curDate.getDate())
    );
  };

  const decreaseMonth = () => {
    setCurDate(
      new Date(curDate.getFullYear(), curDate.getMonth() - 1, curDate.getDate())
    );
  };

해당 함수들을 이전에 만들어놓았던 MyButton 컴포넌트에 연결하니

빠르고 쉽게 구현 가능하다

 

# DiayList 컴포넌트 구현

우선 화면이 렌더링될 때 해당 년, 월에 해당하는 일기 리스트들만 보이도록 구현해야 한다

전체적인 구현과정을 정리해보겠다

1. useContext 사용해서 더미 데이터를 diaryList 에 담아오기

2. DiaryList 컴포넌트 생성 후 diaryList props 로 보내주기

3. DiaryList 컴포넌트에서 해당 diaryList data 리스트 형태로 뿌려주기

 

1. useContext 사용해서 더미 데이터를 diaryList 에 담아오기

DiaryList 컴포넌트를 렌더링하기 위해 App 컴포넌트에 dummyData를 생성하고 해당 값들을 data에 넣어준다

Home 컴포넌트에서 useContext 를 사용해서 해당 더미값을 받아온다

더미 값을 제대로 받아오고 있다!

화면이 렌더링될 때 현재 년, 월에 해당하는 일기 데이터만 가져와야 한다

따라서 useEffect 사용해서 curDate 가 변하면 리스트가 바뀌도록 로직을 작성해보자

일단 매 달의 첫번째 날짜와 마지막 날짜 구하자

첫번째 날짜 ~ 마지막 날짜 사이의 일기 데이터만 보이도록 하면 된다

  useEffect(() => {
    const firstDay = new Date(
      curDate.getFullYear(),
      curDate.getMonth(),
      1
    ).getTime();
    const lastDay = new Date(
      curDate.getFullYear(),
      curDate.getMonth() + 1,
      0
    ).getTime();

    setData(
      diaryList.filter((it) => firstDay <= it.date && it.date <= lastDay)
    );
  }, [diaryList, curDate]);

 

2. DiaryList 컴포넌트 생성 후 diaryList props 로 보내주기

3. DiaryList 컴포넌트에서 해당 diaryList data 리스트 형태로 뿌려주기

 

DiaryList 컴포넌트를 생성해서 diaryList를 props 로 받아 map으로 뿌리도록 로직을 작성한다

import React from "react";

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

DiaryList.defaultProps = {
  diaryList: [],
};

export default DiaryList;

 

# DiaryList 정렬 기능

# 최신순, 오래된순 필터

ControlMemu 컴포넌트를 추가한다

const ControlMenu = ({ value, onChange, optionList }) => {
  return (
    <select value={value} onChange={(e) => e.target.value}>
      {optionList.map((it, idx) => (
        <option key={idx} value={it.value}>
          {it.name}
        </option>
      ))}
    </select>
  );
};

 

최신순, 오래된 순 고를 수 있는 옵션 리스트를 추가한다

const sortOptionList = [
  { value: "lastest", name: "최신순" },
  { value: "oldest", name: "오래된 순" },
];

기존의 diaryList 배열 중 가장 오래된 순서 또는 최신 순서로 보여주어야 한다

기존 배열은 건들지 않아야 하기 때문에 깊은 복사가 필요하다

 

가장 쉬운 깊은 복사 방법

JSON.parse(JSON.stringify ) 를 사용하자!!

const copyList = JSON.parse(JSON.stringify(diaryList));

날짜를 비교하는 compare 함수를 만들어서 해당 함수를 기준으로 sort 하자

  const getProcessedDiaryList = () => {
    const compare = (a, b) => {
      if (sortType === "lastest") {
        return parseInt(b.date) - parseInt(a.date);
      } else {
        return parseInt(a.date) - parseInt(b.date);
      }
    };
    const copyList = JSON.parse(JSON.stringify(diaryList));
    const sortedList = copyList.sort(compare);
    return sortedList;
  };

이제 만들어준 함수로 map을 수행시키면 원하는대로 날짜 기준으로 정렬이 된다

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

ControlMemu 컴포넌트를 추가해서 위의 로직에 맞추어 props 를 전달하자

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

 

최신순으로 정렬했을 때

오래된 순으로 정렬했을 때

정렬이 잘 되고 있다

 

2. 감정 점수 필터

시간순 필터링과 동일한 방식으로 구현한다

useState를 사용해서 현재의 상태를 받아오고 select 태그를 사용해서 화면에 띄워질 수 있도록 감정 필터 옵션 리스트를 만들자

const filterOptionList = [
  { value: "all", name: "전부다" },
  { value: "good", name: "좋은 감정만" },
  { value: "bad", name: "안좋은 감정만" },
];

해당 리스트를 사용하는 감정 필터를 생성한다

      <ControlMenu
        value={filter}
        onChange={setFilter}
        optionList={filterOptionList}
      />

기존에 시간 순으로 정렬했던 리스트에 다시 한번 감정 점수를 기준으로 정렬하는 것이 필요하다

따라서 이전의 getProcessedDiaryList 함수 내에서 수정하자

  const getProcessedDiaryList = () => {
    const filterCallBack = (item) => {
      if (filter === "good") {
        return parseInt(item.emotion) <= 3;
      } else {
        return parseInt(item.emotion) > 3;
      }
    };

    const compare = (a, b) => {
      if (sortType === "lastest") {
        return parseInt(b.date) - parseInt(a.date);
      } else {
        return parseInt(a.date) - parseInt(b.date);
      }
    };
    const copyList = JSON.parse(JSON.stringify(diaryList));

    const filteredList =
      filter === "all" ? copyList : copyList.filter((it) => filterCallBack(it));

    const sortedList = filteredList.sort(compare);
    return sortedList;
  };

감정 상태를 기준으로 filter를 한 뒤에 해당 값으로 다시 한번 compare 함수를 적용한다

어렵다..! 여러번 복습하자!

 

# 새 일기 쓰기 기능 구현

MyButton 컴포넌트를 사용해서 새로운 일기 쓰기 페이지로 이동하는 버튼을 추가하자

이때 useNavigate 가 사용된다

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

잘 완성된 모습이다

css 적용 후의 모습은 아래와 같다

 

# DiaryItem 컴포넌트 생성

일기 리스트 각각을 보여줄 컴포넌트를 생성해보자

총 3개의 div로 분할하여

1. 감정에 따른 이미지 보여줄 div

2. 간략한 일기 내용 보여줄 div

3. 수정하기 버튼 div

로 구성한다

 

1. 감정에 따른 이미지 div

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

각각의 emotion 이미지에 맞춰서 css 스타일링을 적용해주면 아래와 같이 잘 띄워지는 것을 확인할 수 있다

 

2. 간략한 일기 내용 보여줄 div

      <div className="info_wrapper" onClick={goRetail}>
        <div className="diary_date">{strDate}</div>
        <div className="diary_content_preview">{content.slice(0, 25)}</div>
      </div>

 

3. 수정하기 버튼 div

      <div className="btn_wrapper">
        <MyButton text={"수정하기"} onClick={goEdit} />
      </div>

 

해당 영역 눌렀을 때 페이지 이동까지 할 수 있도록 useNavigate 사용하자

  const goRetail = () => {
    navigate(`/diary/${id}`);
  };

  const goEdit = () => {
    navigate(`/edit/${id}`);
  };

최종적으로 완성된 모습이다!


# 새로 알게 된 점

이번 강의를 통해서 정렬 필터를 만드는 방법을 새로 알게 되었다

필터를 만들기 위해서는 어떤 식으로 데이터를 받아오고 있는지 확실하게 파악해야 한다

또한 그 데이터를 활용해서 어떻게 로직을 작성할 것인지 알아야 한다

compare 함수를 통해서 원하는 식으로 정렬하는 방법도 새로 알게 되었는데

이 방법은 다양한 상황에 적용할 수 있을 것 같다

유용한 내용이었지만 처음 구현하는 것이라 헷갈리기 때문에 리팩토링하면서 다시 한번 복습해야겠다

저작자표시 비영리 변경금지 (새창열림)

'TIL > 프로그래머스 데브코스' 카테고리의 다른 글

클라우딩 어플리케이션 엔지니어링 TIL Day 36 - 감정 일기장 만들기 (5)  (0) 2024.02.29
클라우딩 어플리케이션 엔지니어링 TIL Day 35 - 감정 일기장 만들기 (4)  (0) 2024.02.27
클라우딩 어플리케이션 엔지니어링 TIL Day 33 - 감정 일기장 만들기 (2)  (0) 2024.02.23
클라우딩 어플리케이션 엔지니어링 TIL Day 32 - 감정 일기장 만들기 (1)  (0) 2024.02.23
클라우딩 어플리케이션 엔지니어링 TIL Day 31 - 리액트 기본 간단한 일기장 만들기 (4)  (2) 2024.02.23
  1. # 프로젝트 기초 공사 2
  2. # 상태 관리 세팅하기
  3. # dispatch 함수 생성
  4. # Context 생성
  5. # HOME 구현하기
  6. # Header
  7. # DiayList 컴포넌트 구현
  8. # DiaryList 정렬 기능
  9. # DiaryItem 컴포넌트 생성
  10. # 새로 알게 된 점
'TIL/프로그래머스 데브코스' 카테고리의 다른 글
  • 클라우딩 어플리케이션 엔지니어링 TIL Day 36 - 감정 일기장 만들기 (5)
  • 클라우딩 어플리케이션 엔지니어링 TIL Day 35 - 감정 일기장 만들기 (4)
  • 클라우딩 어플리케이션 엔지니어링 TIL Day 33 - 감정 일기장 만들기 (2)
  • 클라우딩 어플리케이션 엔지니어링 TIL Day 32 - 감정 일기장 만들기 (1)
개발자 정지은
개발자 정지은
프로그래밍 공부 기록
개발자 정지은
PROGRAMMING DIARY
개발자 정지은
전체
오늘
어제
  • 분류 전체보기 (107)
    • 알고리즘 (49)
      • BeakJoon (27)
      • SWEA (9)
      • Inflearn (2)
      • CodeSignal (1)
      • Programmers (10)
    • FE (0)
      • Javascript (0)
      • React (0)
    • BE (0)
    • CS공부 (13)
      • Database (7)
      • IT기본지식 (6)
    • TIL (45)
      • 프로그래머스 데브코스 (45)
    • Project (0)
      • DreamHi (0)
      • 인사관리시스템 (0)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 코딩부트캠프
  • 자바스크립트
  • javascript
  • figma
  • 리액트
  • React.JS
  • 피그마
  • ReactNative
  • 프로그래머스 데브코스
  • 알고리즘
  • 프론트엔드
  • 국비지원교육

최근 댓글

최근 글

hELLO · Designed By 정상우.v4.2.2
개발자 정지은
클라우딩 어플리케이션 엔지니어링 TIL Day 34 - 감정 일기장 만들기 (3)
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.