반응형
250x250
Notice
Recent Posts
Recent Comments
Link
관리 메뉴

짧은코딩

일기 쓰기 구현 본문

인프런, 유데미/한입 크기로 잘라 먹는 리액트

일기 쓰기 구현

5_hyun 2022. 6. 22. 22:18

헤더 및 오늘 날짜

-new.js

import { useState } from "react";
import { useNavigate } from "react-router-dom";

import MyHeader from "./../components/MyHeader";
import MyButton from "./../components/MyButton";

// 처음부터 오늘의 날짜가 오게 해주기 위한 기능
const getStringDate = (date) => {
  // 0~9까지 잘라준다.
  return date.toISOString().slice(0, 10);
};

const New = () => {
  const [date, setDate] = useState(getStringDate(new Date()));

  const navigate = useNavigate();

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

여기까지 이렇게 구현했는데 이 부분은 일기 쓰기 부분이지만 일기 수정할 때도 유사하게 페이지를 사용한다. 따라서 컴포넌트화 시켜준다.

 

-DiaryEditor.js

따라서 DiaryEditor.js를 만들고 New.js에서 옮기는 작업을 한다. 이때 import부터 옮기는 것이 좋다. 그리고 경로을 잘 수정해야한다.

import { useState } from "react";
import { useNavigate } from "react-router-dom";

import MyHeader from "./MyHeader";
import MyButton from "./MyButton";

// 처음부터 오늘의 날짜가 오게 해주기 위한 기능
const getStringDate = (date) => {
  // 0~9까지 잘라준다.
  return date.toISOString().slice(0, 10);
};

const DiaryEditor = () => {
  const [date, setDate] = useState(getStringDate(new Date()));

  const navigate = useNavigate();

  return (
    <div className="DiaryEditor">
      {/* 헤더 */}
      <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;

따라서 이렇게 옮겨주고 New.js는 비워준다.

 

-New.js

import DiaryEditor from "../components/DiaryEditor";

const New = () => {
  return (
    <div>
      <DiaryEditor />
    </div>
  );
};

export default New;

이렇게 만들어주면 다시 위 사진과 똑같은 화면이 나오게된다.

 

-App.css

.DiaryEditor section {
  margin-bottom: 40px;
}

.DiaryEditor h4 {
  font-size: 22px;
  font-weight: bold;
}

.DiaryEditor .input_date {
  border: none;
  border-radius: 5px;
  background-color: #ececec;

  padding: 10px 20px 10px 20px;

  cursor: pointer;
  font-family: "Nanum Pen Script";
  font-size: 20px;
}

이렇게 완성이 된다.


오늘의 감정

-감정 리스트

// 감정 리스트
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: "끔찍함",
  },
];

우선 감정 리스트를 만들어준다.

 

그리고 클릭하면 어떤 감정을 선택했는지 저장해야되는 컴포넌트 EmotionItem.js를 만들어준다.

-EmotionItem.js

const EmotionItem = ({ emotion_id, emotion_img, emotion_descript }) => {
  return (
    <div className="EmotionItem">
      <img src={emotion_img} />
      <span>{emotion_descript}</span>
    </div>
  );
};

export default EmotionItem;

 

-DiaryEditor.js

{/* 오늘의 감정 */}
        <section>
          <h4>오늘의 감정</h4>
          <div className="input_box emotion_list_wrapper">
            {emotionList.map((it) => (
              <EmotionItem key={it.emotion_id} {...it} />
            ))}
          </div>
        </section>

 

-App.css

.DiaryEditor .emotion_list_wrapper {
  /* grid는 격자를 만드는 것 */
  display: grid;
  /* 5개를 둘건데 크기는 작아지든 커지든 알아서 놓으라는 의미 */
  grid-template-columns: repeat(5, auto);
  /* gap는 아이템 사이의 거리 */
  gap: 2%;
}

/* EMOTION ITEM */

.EmotionItem {
  cursor: pointer;

  border-radius: 5px;
  padding-top: 20px;
  padding-bottom: 20px;

  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

.EmotionItem img {
  width: 50%;
  margin-bottom: 10px;
}

.EmotionItem sapn {
  font-size: 18px;
}

이렇게 나오게 된다. 이제는 감정을 누르기 전에 회색 배경으로 있고 선택된 감정은 배경 색을 다르게 해줘야 한다.

 

-감정 선택

-DiaryEditor.js

  // 어떤 감정이 선택되었는지 체크해주는 state
  const [emotion, setEmotion] = useState(3);

  //감정 클릭시 발생하는 기능
  const handleClickEmote = (emotion) => {
    setEmotion(emotion);
  };

이렇게 감정을 저장해줄 state를 만들고 기본 감정은 3으로 해준다. 그리고 감정 클릭시 감정의 번호를 전달해준다.

<section>
          <h4>오늘의 감정</h4>
          <div className="input_box emotion_list_wrapper">
            {emotionList.map((it) => (
              <EmotionItem
                key={it.emotion_id}
                {...it}
                onClick={handleClickEmote}
                // 자신이 선택된 감정인지 아닌지 알기 위한 prop
                // 선택된 emotion의 값과 같다면 true 전달
                isSelected={it.emotion_id === emotion}
              />
            ))}
          </div>
        </section>

그리고 onClick에서 handleClickEmote로 전달해준다. isSelected는 선택된 감정이면 True, 아니면 False 전달

 

-EmotionItem.js

const EmotionItem = ({
  emotion_id,
  emotion_img,
  emotion_descript,
  onClick,
}) => {
  return (
    <div onClick={() => onClick(emotion_id)} className="EmotionItem">
      <img src={emotion_img} />
      <span>{emotion_descript}</span>
    </div>
  );
};

export default EmotionItem;

EmotionEditor에서 onClick로 감정을 전달 받으면 된다.

 

그리고 개발자 도구 DiaryEditor에서 감정들을 눌러보면 state 번호가 바뀌는 것을 알 수 있다.

게다가 선택된 2번 감정은 isSelected가 true로 나온다.

 

이제 감정이 선택되었냐 안되었냐에 따라 다르게 나오도록 해야한다.

-EmotionItem.js

const EmotionItem = ({
  emotion_id,
  emotion_img,
  emotion_descript,
  onClick,
  isSelected,
}) => {
  return (
    <div
      onClick={() => onClick(emotion_id)}
      className={[
        "EmotionItem",
        isSelected ? `EmotionItem_on_${emotion_id}` : `EmotionItem_off`,
      ].join(" ")}
    >
      <img src={emotion_img} />
      <span>{emotion_descript}</span>
    </div>
  );
};

export default EmotionItem;

isSelected를 받고 True이면 EmotionItem_on이고 아니면 EmotionItem_off로 클래스 네임을 갖게 했다.

 

-App.css

.EmotionItem_off {
  background-color: #ececec;
}

.EmotionItem_on_1 {
  background-color: #64c964;
  color: white;
}

.EmotionItem_on_2 {
  background-color: #9dd772;
  color: white;
}

.EmotionItem_on_3 {
  background-color: #fdce17;
  color: white;
}

.EmotionItem_on_4 {
  background-color: #fd8446;
  color: white;
}

.EmotionItem_on_5 {
  background-color: #fd565f;
  color: white;
}

off와 on으로 차이를 주고 on은 각각 색에 맞춰서 바꿔주면 완성이다.

 


오늘의 일기

-DiaryEditor.js

// 오늘의 일기 state
  const [content, setContent] = useState("");
  // 오늘의 일기에서 focus 기능에 사용 할 useRef
  const contentRef = useRef();

일기 내용에 사용할 state 생성하고 useRef도 만든다.

      <section>
          <h4>오늘의 일기</h4>
          <div className="input_box text_wrapper">
            <textarea
              placeholder="오늘은 어땠나요"
              ref={contentRef}
              value={content}
              onChange={(e) => setContent(e.target.value)}
            />
          </div>
        </section>

그리고 textarea를 통해 만들어준다.

 

-App.css

.DiaryEditor textarea {
  font-family: "Nanum Pen Script";
  font-size: 20px;

  box-sizing: border-box;
  width: 100%;
  min-height: 200px;
  /* vertical로 안주면 가로로 움직인다. 이걸 주면 세로로만 움직인다. */
  resize: vertical;

  border: none;
  border-radius: 5px;
  background-color: #ececec;

  padding: 20px;
}

오늘의 일기까지 완성되었다.


취소하기, 완료하기 버튼

-DiaryEditor.js

  // onCreate 가져오기
  const { onCreate } = useContext(DiaryDispatchContext);

  // 작성 완료 기능
  const handleSubmit = () => {
    if (content.length < 1) {
      contentRef.current.focus();
      return;
    }

    onCreate(date, content, emotion);
    // 일기 데이터 저장하면 홈으로 돌아간다.
    // 그래서 일기 작성하는 페이지를 뒤로가게해서 못도록하게 한다.
    navigate("/", { replace: true });
  };

App.js에서 만든 onCreate를 가져오고 작성 완료 버튼을 처리 해줄 수 있는 기능을 만든다.

 

        <section>
          <div className="control_box">
            <MyButton text={"취소하기"} onClick={() => navigate(-1)} />
            <MyButton text={"작성완료"} type={"positive"} onClick={() => {}} />
          </div>
        </section>

취소하기, 작성완료 버튼을 만들었다. 취소하기 버튼은 뒤로 갈 수 있도록 했고 작성완료 버튼은 일기를 저장하고 다시 홈으로 돌아간다.

 

-App.css

.DiaryEditor .control_box {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

이렇게 새로운 일기를 작성할 수 있다.

728x90
반응형

'인프런, 유데미 > 한입 크기로 잘라 먹는 리액트' 카테고리의 다른 글

DAIRY 구현  (0) 2022.06.23
EDIT 구현  (0) 2022.06.23
HOME 구현하기  (0) 2022.06.21
프로젝트 기초 공사 2  (0) 2022.06.20
프로젝트 기초 공사 1  (0) 2022.06.19
Comments