밍비
프로그램의 편린
밍비
전체 방문자
오늘
어제
  • 분류 전체보기 (64)
    • Spring (2)
    • TIL (23)
    • 프로그래머스 (12)
    • Udemy (16)
    • Typescript (2)
    • MERN (1)
    • AWS (7)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 데이터 수정
  • react
  • Edge Locations
  • AWS Regions
  • 서비스아키텍처
  • useNavigate
  • state 끌어올리기
  • useState
  • 한입크기로잘라먹는리액트
  • 리액트
  • 네이버커넥트
  • 리액트 reducer
  • Points of Presence
  • 분산저장소
  • 리액트 프로젝트 만들기
  • useParams
  • 컴포넌트트리
  • state 관리
  • 리스트 조회
  • Availability Zones
  • useRef
  • 리액트 생애주기
  • DOM
  • 함수형 update
  • API 호출
  • State 합치기
  • Page Moving
  • 한입 크기로 잘라먹는 리액트
  • overflow-wrap
  • 수평 스케일링

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
밍비

프로그램의 편린

Udemy

[React] 일기장 프로젝트 2. 페이지 구현 - 수정, 상세화면

2023. 5. 26. 03:31
728x90

이 블로그는 유데미 '한입 크기로 잘라먹는 리액트' 강의를 듣고 복습하고자 작성되었습니다.

목차

1. 수정페이지

2. 상세페이지

 

1. 수정페이지(/edit)

 

수정페이지는 일기 작성페이지와 거의 유사해서 만드는 과정도 간단합니다.

 

1. id 정보와 일기 데이터 가져오기

id는 App.js의 route에서 path variable로 설정한 후 useParams를 이용해 id 객체로 불러올 것입니다.

또 일기 데이터는 일기 작성화면에서 구현했듯 useContext를 이용해 가져올 것입니다.

 

1-1. Edit 컴포넌트 만들기

import { useNavigate } from 'react-router-dom';
const Edit = () => {
  const navigate = useNavigate();
  return <div></div>;
};
 
export default Edit;

1-2. App.js에서 edit 페이지에서 id별로 보여주도록 path 고치기

<Route path='/edit/:id' element={<Edit />} />

1-3. useParams를 이용해 path variable을 Edit에서 받아오기

import { useNavigate, useParams } from 'react-router-dom';
const { id } = useParams();

이때 콘솔로 id를 찍어 잘 들어왔는지 확인해봅시다.

1-4. useContext로 데이터가 있는 DiaryStateContext를 불러오기

import { useContext } from 'react';
const diaryList = useContext(DiaryStateContext);

 

2. diaryList에서 원하는 id의 일기만 뽑기

 

2-1. useEffect로 일기 뽑은 후 렌더

일기를 뽑아서 렌더하는 것은, 페이지가 마운트되자 마자 이뤄져야 하는 일들입니다.

따라서 useEffect를 사용해 렌더합시다.

import { useContext, useEffect, useState } from 'react';
useEffect(() => {
  if (diaryList.length >= 1) {
    const targetDiary = diaryList.find(
      (it) => parseInt(it.id) === parseInt(id),
    );
    console.log(targetDiary);
  }
}, [id, diaryList]);

여기서 id나 diaryList에 변경이 있을 경우 추가로 리렌더가 필요하므로, useEffect의 두 번째 인자로 [id, diaryList] 배열을 주었습니다.

 

2-2. 존재하지 않는 id의 페이지를 접근할 경우, 홈으로 돌아가게 하기

useEffect(() => {
  if (diaryList.length >= 1) {
    const targetDiary = diaryList.find(
      (it) => parseInt(it.id) === parseInt(id),
    );
    console.log(targetDiary);
 
    if (targetDiary) { } else {
      navigate("/", {replace:true})
    }
  }
}, [id, diaryList]);

아까전 코드에서 조건문을 추가, targetDiary가 없을 경우 홈화면으로 돌아가도록 합니다.

{ replace: true }는 홈 화면으로 돌아간 후 뒤로가기가 되지 않도록 하는 것입니다.

 

3. 뽑은 targetDiary를 저장할 state 만들기, 페이지를 잘 찾아간 경우 set state하기

import { useContext, useEffect, useState } from 'react';
const [originData, setOriginData] = useState();
if (targetDiary) {
  setOriginData(targetDiary);
} else {
  navigate('/', { replace: true });
}

2-2의 조건문을 수정해 targetDiary가 truthy할 경우 originData의 내용을 targetDiary로 바꿉니다.

 

4. return 작성

return (
  <div>
    {originData && <DiaryEditor isEdit={true} originData={originData} />}
  </div>
);

originData가 있을 경우 이전에 만들어둔 diaryEditor 컴포넌트를 띄웁니다.

새 일기 작성이 아닌 수정 모드이고(모드에 따라 헤더 텍스트를 바꿀 예정), originData를 띄워놓은 상태에서 수정해야 하므로

해당 정보를 props로 전달합니다.

 

5. DiaryEditor 컴포넌트에서 props 받아 처리하기

 

5-1. prop 받아오기

const DiaryEditor = ({ isEdit, originData }) => {

DiaryEditor 컴포넌트의 props 부분을 수정합니다.

 

5-2. useEffect로 originData 띄우기

useEffect(() => {
  if (isEdit) {
    setDate(getStringDate(new Date(parseInt(originData.date))));
    setEmotion(originData.emotion);
    setContents(originData.emotion);
  }
}, [isEdit, originData]);

수정 시 originData를 띄우는 건 마운트 하자마자 처리해야 하므로, useEffect를 사용합니다.

isEdit:true를 받았을 때, 즉 수정 화면에서 에디터에 진입했을 때만 이 로직이 실행됩니다.

 

 

5-3. 헤더의 headText도 isEdit 값에 따라 '새 일기쓰기'가 아니라 '수정하기'로 고치기

headText={isEdit ? '일기 수정하기' : '새 일기쓰기'}

5-4. 작성완료 버튼을 눌렀을 때, handleSubmit에서 onCreate가 아니라 수정이 되도록 하기

const { onCreate, onEdit } = useContext(DiaryDispatchContext);

먼저 useContext에서 onEdit을 불러온 후

if (
  window.confirm(
    isEdit ? '일기를 수정하시겠습니까?' : '새로운 일기를 작성하시겠습니까?',
  )
) {
  if (!isEdit) {
    onCreate(date, contents, emotion);
  } else {
    onEdit(originData.id, date, contents, emotion);
  }
}
navigate('/', { replace: true });

isEdit 상태에 따라 다른 확인메시지를 올린 후, onCreate와 onEdit 둘중 하나를 수행합니다.

 

2. 상세페이지(/diary)

이제 상세페이지를 구현해봅시다.

 

1. edit처럼 useContext로 일기 데이터를 가져와 find로 찾아 렌더

import { useContext, useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { DiaryStateContext } from '../App';
 
const Diary = () => {
  const { id } = useParams();
  const diaryList = useContext(DiaryStateContext);
  const navigate = useNavigate();
  const [data, setData] = useState();
  useEffect(() => {
    if (diaryList.length >= 1) {
      const targetDiary = diaryList.find(
        (it) => parseInt(it.id) === parseInt(id),
      );
 
      if (targetDiary) {
        //일기가 존재할 때
        setData(targetDiary);
      } else {
        //일기가 없을 때
        alert('없는 일기입니다.');
        navigate('/', { replace: true });
      }
    }
  }, [id, diaryList]);
 
  return (
    <div>
      <h1>Diary</h1>
      <p>이곳은 일기 상세페이지입니다.</p>
    </div>
  );
};
export default Diary;

2. 일기 state를 덜 받아왔을 때 핸들링

if (!data) {
  return <div className='DiaryPage'>로딩중입니다...</div>;
}

3. 바로 전 글(작성화면)에서 getStringDate를 가져와 headText 쓰기

 

작성화면에서 사용한 getStringDate 함수를 사용해야 하는데, 함수를 통째로 복사해오면 중복이 생겨 유지보수가 어려워집니다.

그래서 util 폴더를 만든 후 그 안에 함수를 써놓고, DiaryEditor와 Edit에서 함수를 import 해옵시다.

const getStringDate = (date) => {
  const tzoffset = new Date().getTimezoneOffset() * 60000;
  return new Date(Date.now() - tzoffset).toISOString().slice(0, 10);
};

이렇게 함수를 따로 써놓은 후

import { getStringDate } from './../util/date.js';

이렇게 불러오는 것입니다.

 

이제 return을 수정해봅시다.

if (!data) {
  return <div className='DiaryPage'>로딩중입니다...</div>;
} else {
  return (
    <div>
      <MyHeader
        headText={`${getStringDate(new Date(data.date))}`}
        leftChild={
          <MyButton text={'< 뒤로가기'} onClick={() => navigate(-1)} />
        }
        rightChild={
          <MyButton
            text={'수정하기'}
            onClick={() => {
              navigate(`/edit/${id}`);
            }}
          />
        }
      />
    </div>
  );
}

getStringDate를 사용해줍니다.

 

4. 이모티콘 렌더를 위해 emotionList 불러오기

 

4-1. 아까처럼 util 폴더에 emotionList 배열을 작성한 후 임포트

import { emotionList } from '../util/emotion.js';

DiaryEditor.js, Diary.js에서 불러옵니다.

 

4-2. return 작성

if (!data) {
  return <div className='DiaryPage'>로딩중입니다...</div>;
} else {
  const curEmotionData = emotionList.find(
    (it) => parseInt(it.emotion_id) === parseInt(data.emotion),
  );
  return (
    <div className='DiaryPage'>
      <MyHeader
        headText={`${getStringDate(new Date(data.date))}`}
        leftChild={
          <MyButton text={'< 뒤로가기'} onClick={() => navigate(-1)} />
        }
        rightChild={
          <MyButton
            text={'수정하기'}
            onClick={() => {
              navigate(`/edit/${id}`);
            }}
          />
        }
      />
      <article>
        <section>
          <h4>오늘의 감정</h4>
          <div className={['diary_img_wrapper',`diary_img_wrapper_${data.emotion}`].join(' ')}>
            <img src={curEmotionData.emotion_img} />
            <div className='emotion_descript'>
              {curEmotionData.emotion_descript}
            </div>
          </div>
        </section>
      </article>
    </div>
  );
}

여기서 curEmotionData가 find를 이용해 항목을 찾는 것이고, 

return에 article이라는 시맨틱 태그를 추가해 curEmotionData의 이미지, 설명을 가져왔습니다.

 

5. 일기 내용부분 렌더

5-1. 섹션 추가

<section>
  <h4>오늘의 일기</h4>
  <div className='diary_contents_wrapper'>
    <p>{data.contents}</p>
  </div>
</section>

5-2. CSS작업

일부만 가져와봤습니다.

.DiaryPage .diary_contents_wrapper {
  width: 100%;
  background-color: #ececec;
  border-radius: 5px;
  word-break: keep-all;
  overflow-wrap: break-word;
}

width 100%를 해야 자식 요소(p태그)의 text-align도 왼쪽정렬이 잘 됩니다.

또 word-break는 글 길이가 한줄을 넘어갈 때 한 단어를 쪼개지 말라는 뜻이고,

overflow-wrap은 텍스트가 한 줄을 넘어갈 경우 줄바꿈을 강제하는 것입니다.

 

 

728x90

'Udemy' 카테고리의 다른 글

[React] 프로젝트 최적화  (1) 2023.05.27
[React] 일기장 프로젝트 3. 페이지 구현 - 작성화면  (0) 2023.05.17
[React] 일기장 프로젝트 - 후기  (1) 2023.04.30
[React] 일기장 프로젝트 2. 페이지 구현 - 홈화면  (0) 2023.04.25
[React] 일기장 프로젝트 1. 기초 작업  (0) 2023.04.25
    밍비
    밍비

    티스토리툴바