Life-Calendar#9 | diary 기능 (part.1)

김하은·2023년 1월 1일
2

Project

목록 보기
10/18
post-thumbnail

diaryPage 기능 추가하기

diaryPage 에 대략적인 퍼블리싱을 만들어 보았다


⭐ 다이어리 입력칸 기능 추가 ⭐

-> 어떤것을 써야 글이 써질까 생각하다가, 게시판에 자주 쓰이는
"textarea" 라는 기능을 쓰기로 정하였다.
🚩textarea는 여러 줄의 긴 문장을 입력할 수 있는 양식이다.
🚩 사용방법: < textarea >< /textarea >

-> textarea 태그를 추가해 주었다. 그후,
입력 type 과 name 값을 입력해 주었다.
📁5Page
L 📁components
L diaryForm.js

		<PostForm>
            <textarea
              type="text"
              placeholder="일기내용을 작성해주세요"
              name='content'
             />
       	</PostForm>

-> css 추가

const PostForm = styled.form`
  width: auto;
  height: auto;
  margin: 0 auto;
  padding: 20px;
  & textarea {
    resize: none;
    width: 95%;
    height: 190px;
    font-family: 'SB 어그로 L';
    border: none;
    padding: 15px 15px;
    ::-webkit-scrollbar {
      width: 8px;
      background-color: red;
      border-radius: 10px;
    }
    ::-webkit-scrollbar-thumb {
      width: 8px;
      background-color: blue;
      border-radius: 10px;
    }
    :focus {
      outline: none;
    }
  }

📍textarea css 에서 신경섰던 부분이 있었는데 입력 text 가 길어질때,
스크롤이 생기면서 아래로 이어져서 써져야 하는것이였다. 따라 css 에
2가지의 기능을 넣어주었다.

  • ::-webkit-scrollbar
    의 기능은 전체 스크롤바. 를 뜻하는 것이다.

  • ::-webkit-scrollbar-thumb 는 드래그 가능한 스크롤 핸들. 을 나타낸다.

이해하기 쉽도록 이미지를 붙여 본다.


⭐다이어리 내용 입력시 내용 변경에 대한 상태값⭐

내용이 입력 될때마다 변경된 내용에 대해 나타나 줘야 하기 때문에 onChange의 이벤트를 추가해주었다.
🚩 onchange 이벤트는 요소의 값이 변경되었을 때 발생한다.
📁5Page
L 📁components
L diaryForm.js


const  DiaryForm = () => {
	const [ViewData, setViewData] = useState({
     id:""
    ,title:""
    ,content:""
    ,date: new Date()
    ,color:""
  })

   const getChangeContent = (e) => {
    const{name, value} = e.target;
    console.log(name, value);
    setViewData({
      ...ViewData,
      [name]:value
    })
    console.log(ViewData);
  };
  
return(
	<>
	<PostForm>
 	 <textarea
  	   type="text"
       placeholder="일기내용을 작성해주세요"
       name='content'
       onChange={getChangeContent}
     />
	</PostForm>
	</>
	);
};

-> useState({}) 안에 들어갈 데이터들을 넣어두었다.
[필요한 기본 데이터들 = 글id, 글제목, 글내용, 등록날짜, 다이어리컬러값]
맨 처음에는 id, title, content, date, color 데이터 값들이 비어있기 때문에 빈 값 "" 으로 넣어두었다.

-> state 를 이용하여 들어갈 data 들을 ViewData, 에 넣어두었고 입력 상태가 바뀔때마다, setViewData에 [name] :value 값을 추가하여 저장된다. console.log(ViewData) 콘솔을 출력해 보면, 입력할때마다 추가된 모습이 보인다.


⭐날짜 기능⭐

🤔 다이어리를 입력 할때의 날짜가 언제인지 날짜표시에 대한 기능을
만들어야 한다. react 날짜표시 기능을 찾아보니, react-datepicker 는 라이브러리가 있었고 이것을 이용해서 날짜를 표시해 보기로 하였다. 설치 방법부터 적어놓으려니 적어질것 같아 따로 포스팅을 해두었다.
datepicker 를 적용하는 방법

📍 datePicker 적용후 전체 코드
📁5Page
L 📁components
L diaryForm.js

import styled from "styled-components";
import DatePicker from "react-datepicker";
import { useState, forwardRef } from 'react';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {  faCircleArrowLeft, faCircleCheck } from "@fortawesome/free-solid-svg-icons"


const  DiaryForm = () => {

  const [ViewData, setViewData] = useState({
     id:""
    ,title:""
    ,content:""
    ,date: new Date()
    ,color:""
  })

  const DatePick = forwardRef(({ value, onClick }, ref) => (
    <Datebutton className='custom-btn'
      onClick={onClick} ref={ref}>
        {value}
    </Datebutton> ));

  const getChangeContent = (e) => {
    const{name, value} = e.target;
    console.log(name, value);
    setViewData({
      ...ViewData,
      [name]:value
    })
    console.log(ViewData);
  };

  return(
    <>
    <AllDiaryBox>
      <DiaryContainer>
        <PostTitle>
          <Datebox>
            <DatePicker
            //value={date}
            dateFormat="yyyy-MM-dd"
            selected={ViewData.date}
            onChange={(date) => setViewData({
            ...ViewData,
            'date': date,
            })}
            customInput={<DatePick/>}
            />
          </Datebox>
          <div className='inputBox'>
            <input
              type="text"
              placeholder="제목을 작성해주세요"
              name='title'
            />
          </div>
          <FontAwesomeIcon
            type='button'
            className="CheckIcon" 
            icon={faCircleCheck} 
          />
          <FontAwesomeIcon
            type='button'
            className="beforeIcon" 
            icon={faCircleArrowLeft}
          />
        </PostTitle>
        <WriteInnerBox>
          <PostForm>
            <textarea
              type="text"
              placeholder="일기내용을 작성해주세요"
              name='content'
              onChange={getChangeContent}
              //value={content}
            />
          </PostForm>
        </WriteInnerBox>
      </DiaryContainer>

        <DiaryInnerBox>
        </DiaryInnerBox>
    </AllDiaryBox>
    </>
  );
};
export default DiaryForm;

const AllDiaryBox = styled.div`
  width: 800px;
  position: relative;
  height: 800px;
  z-index: 10;
`
const DiaryContainer = styled.div`
  max-width: 676px;
  max-height: 400PX;
  margin: 0 auto;
  margin-top: 10px;
  border-radius: 15px;
  background-color: #5800FF;
  justify-content: center;
  padding: 27px;
  `
const PostTitle = styled.div`
  width: 670px;
  height: 50px;
  display: flex;
  position: relative;
  align-items: center;
  justify-content: end;
  margin-bottom: 10px;
  left: 10px;
  & p {
    margin-right: 30px;
    text-align: center;
    font-size: 18px;
    color: white;
    font-family: 'SB 어그로 M';
  }
  & .inputBox{
    width: 300px;
    height: 40px;
    border-radius: 20px;
    background-color: white;
    display: flex;
    position: relative;
    align-items: center;
    padding: 0 20px;
    margin-right: 10px;
    right: 60px;
    & input {
      width: 100%;
      font-family: 'SB 어그로 L';
      border: none;
      :focus {
        outline: none;
      }
    }
  }
  & .CheckIcon {
    width: 50px;
    height: 35px;
    color: white;
    position: relative;
    display: flex;
    right: 50px;
  }
  & .beforeIcon{
    display: flex;
    position: relative;
    width: 35px;
    height: 35px;
    border-radius: 50px;
    color: white;
    right: 40px;
  }
`
const Datebox = styled.div`
  width: 140px;
  height: 40px;
  position: relative;
  display: flex;
  right: 70px;
`
const Datebutton = styled.button`
  width: 110px;
  height: 40px;
  border: none;
  border-radius: 30px;
  background-color: #8D72E1;
  color: white;
  :hover{
    background-color: #8D9EFF;
  }
`

const WriteInnerBox = styled.div`
  width: 100%;
  height: 260px;
  border-radius: 10px;
  margin: 0 auto;
`

const PostForm = styled.form`
  width: auto;
  height: auto;
  margin: 0 auto;
  padding: 20px;
  & textarea {
    resize: none;
    width: 95%;
    height: 190px;
    font-family: 'SB 어그로 L';
    border: none;
    padding: 15px 15px;
    ::-webkit-scrollbar {
      width: 8px;
      background-color: #F4F4F4;
      border-radius: 10px;
    }
    ::-webkit-scrollbar-thumb {
      width: 8px;
      background-color: #c4c4c4;
      border-radius: 10px;
    }
    :focus {
      outline: none;
    }
  }
`
 const DiaryInnerBox = styled.div`
  width: 676px;
  height: 310px;
  margin: 0 auto;
  background-color: #CED0E9;
  overflow: hidden;
  margin-top: 50px;
`
  • 처음엔 데이터의 값이 없기 때문에 초기값을 빈값으로 "" 넣었는데,
    맨처음에 diaryPage 를 들어왔을때 현재 날짜가 보여지길 원해서
    date의 초기값을 현재 날짜를 알수 있는 newDate()로 넣어주었다.

🍀적용 결과🍀

-> 현재 날짜가 보여지는것을 볼수 있다.


⭐diary 배경 color변경 기능⭐

  • 하루의 기분을 색으로 나타낼수 있는 color칸이 있다.
    다이어리 내용을 쓴후 하루의 기분에 맞는 color를 선택하면
    선택한 color 가 diary의 색으로 변경 된다.

🤔 생각해보기
-> 5가지의 color 값이 있는데, 각각 클릭할때마다 배경색이 바뀌어야 한다.

🍀상태관리 라이브러리로 recoil를 사용한 이유🍀
-> 클릭할때마다 상태값이 바뀌어야 하기때문에, 상태관리 라이브러리인 redux 를 사용 하려 했다. 하지만 아직 초보인 나에게는 빠른시간내에 쉽게 적용해볼수 있는 라이브러리가 유용하다 판단하여 recoil 을 사용해 보기로 하였다.


📍color 설정
📁resource
L color.js

export const Color = {
  "설렘": "#FFCCCC",
  "슬픔": "#7A90E2",
  "기쁨": "#FFDF78",
  "화남": "#E5636F",
  "무기력": "#C4C4C4",
};

-> 색상 정보들을 Color 라는 변수 안에다가 넣어주었다.


📍 recoil 사용법

npm install recoil
return (
    <div className="App">
    <>
    <RecoilRoot>
      <Routes>
        <Route path="/" element={
          <PrivateRoute component={<ThirdPage/>} status={<Onepage/>}/>
        }/>
        <Route path="/twopage" element={<TwoPage/>} />

        <Route path="/thirdpage" element={
            <PrivateRoute component={<ThirdPage/>}/>
        }/>
        
        <Route path="/fourpage" element={
            <PrivateRoute component={<Fourpage/>}/>
          }/>
        <Route path="/fivepage" element={
            <PrivateRoute component={<Fivepage/>} />
          }/>
      </Routes>
    </RecoilRoot>
    </>
    </div>
  );

-> recoil을 다운받은후 , App.js 에다가 recoilRoot로 감싸주었다.


📍 상태값 선언
📁recoil
L colorState.js

import { atom } from "recoil";

const initialState = {
  color: "#5800FF"
};

export const recoilColorState = atom({
  key: "recoilColorState",
  default: initialState
});

-> Atoms는 어떤 컴포넌트에서나 읽고 쓸 수 있다. atom의 값을 읽는 컴포넌트들은 암묵적으로 atom을 구독한다. 그래서 atom에 어떤 변화가 있으면 그 atom을 구독하는 모든 컴포넌트들이 재 렌더링 되는 결과가 발생할 것이다.


📁5Page
L 📁components
L colorForm.js

import styled from "styled-components";
import { useState, useEffect } from 'react';
import { Color } from "../../resource/color.js"
import { useRecoilState } from "recoil";
import {recoilColorState} from "../../recoil/colorState"

const ColorForm = () => {

  const [recoilColor, setRecoilColor] = useRecoilState(recoilColorState);
  const defaultColor = { ...recoilColor};

  const colorArray = Object.values(Color);
  const colorNameArray = Object.keys(Color);

  const [colorState, setColorState] = useState(defaultColor.color);

  const onColorChangeHandler = (color) => {
    setColorState(color);
  };

  useEffect(() => {
    const changedColor = {
      color: colorState
    };
    setRecoilColor(changedColor);
  }, [colorState]);

  return (
    <>
      <WhiteContainer>
       <p className="titleText">오늘의 기분은 어떤 색인가요?</p>
          <ColorPallete>
            {colorArray.map((color,index) => (
              <ColorCircle key={color} background={color}>
                <label for={color}></label>
                <input
                  type="radio"
                  id={color}
                  checked={colorState === color}
                  onChange={() => onColorChangeHandler(color, index)}
                />
                <p>{colorNameArray[index]}</p>
              </ColorCircle>
            ))}
          </ColorPallete>
      </WhiteContainer>
    </>
  );
};
export default ColorForm;


const WhiteContainer = styled.div`
  width: 676px;
  height: 106px;
  background-color: #CED0E9;
  display: inline-block;
  margin: 0 auto;
  position: relative;
  left: 50px;
  font-family: 'SB 어그로 L';
  & .titleText {
    text-align: center;
    margin-top: 25px;
    color: white;
    font-size: 17px;
  }
`

const ColorPallete = styled.ul`
  width: 80%;
  margin: 0 auto;
  display: flex;
  gap: 30px;
`;

const ColorCircle = styled.li`
  list-style-type: none;
  display: flex;
  align-items: center;
  gap: 10px;

  & > label {
    display: inline-block;
    background-color: ${(props) => props.background};
    width: 30px;
    height: 30px;
    border-radius: 100%;
    cursor: pointer;
    transition: all 0.25s;
  }

  & > label:hover {
    transform: scale(1.15);
  }

  & > input {
    display: none;
  }
`;

-> state 기본 값으로 useRecoilState(recoilColorState)
colorState 파일에 선언해둔 (recoilColorState) 값을 넣어두었고
defaultColor 라는 변수에 기본 값을 복사해둔 { ...recoilColor} 값을 선언하였다.

-> const colorArray = Object.values(Color);
const colorNameArray = Object.keys(Color);
Object.values() 메서드를 사용해
Color 값 과, Color의 이름을 배열화 하였다.

Object.values()정적 메서드는 주어진 개체 자체의 열거 가능한 문자열 키 속성 값의 배열을 반환한다.

-> colorState 값으로 위에 기본값으로 복사해둔 useState(defaultColor.color) 을 기본값으로 지정해두었다.

-> map()을 사용하여 위에 새로운 colorArray 값을 배열을 만들었다.
<ColorCircle 안에 key 값으로 {color}를 넣어 주었고, background 에도 = {color} 값을 넣어주었다.

-> 각각의 color 값을 label 을 이용해서 나타내 주었다.
"label for" 라는 태그를 써주었는데 ,
input 태그를 제어하여 상태를 변경하도록 도와주는 태그 이다.
즉, 클릭의 유효범위가 달라진다.
사용하기 위해서는 input 태그의 id와 같아야 한다.

-> 따라, 아래와 같이 input 태그 안에 label 과 같은 id값을 넣어 주었고 , color 값이 바뀔때마다 새로운
setColorState(color) 값으로 나타난다.

-> & > label {
background-color: ${(props) => props.background};
label의 color 값을 porps 로 넘겨주었다.

{colorArray.map((color,index) => (
	<ColorCircle key={color} background={color}>
		<label for={color}></label>
		<input
		  type="radio"
		  id={color}
		  checked={colorState === color}
		  onChange={() => onColorChangeHandler(color, index)}
		/>
		<p>{colorNameArray[index]}</p>
	</ColorCircle>
))}

-> useEffect 처음 기본 color값을 : colorState 로 지정해주고, 변경된 값을 setRecoilColor(changedColor) 로 넣어준다.
,그리고 [colorState]가 업데이트 될때마다, 반복 되서 실행된다.


📁5Page
L 📁components
L diaryForm.js

import { useState, forwardRef, useEffect } from 'react';
import { recoilColorState } from "../../recoil/colorState";
import { useRecoilState } from "recoil";
import './diary.css'

const DiaryForm = () => {

  const [recoilColor, setRecoilColor] = useRecoilState(recoilColorState);
  const defaultColor = { ...recoilColor };
  const [colorPeeker, setColorPeeker] = useState(defaultColor.color);
}

  useEffect(() => {
    const tmpColor = { ...recoilColor };
    setColorPeeker(tmpColor.color);
  },[recoilColor]);

return(
      <DiaryContainer background={colorPeeker}>
  )
};
export default DiaryForm;

const DiaryContainer = styled.div`
  max-width: 676px;
  max-height: 400PX;
  margin: 0 auto;
  border-radius: 15px;
  background-color: ${(props) => props.background};
  justify-content: center;
  padding: 27px;
`

-> 위와 똑같이
useRecoilState 에 기본값으로 (recoilColorState) 을 지정해두었고,

defaultColor 변수에 복사된 { ...recoilColor } 값을 넣어두었다.
color의 새 state 값을 지정해두고 기본값으로 (defaultColor.color) 를 지정해두었다.
const [colorPeeker, setColorPeeker] = useState(defaultColor.color);

-> [recoilColor] 값이 업데이트 될때마다,
useEffect 안에 있는 함수들이 반복되서 실행된다.

-> <DiaryContainer 의 배경 색을 state 값 background={colorPeeker} 로 넣어 주었고
css 에 background-color: ${(props) => props.background};
props를 사용하여 선택한 color 값으로 바뀌게 끔 구현하였다.


🍀최종 결과🍀

다이어리 등록은 다음글에 포스팅 하겠다.

profile
꾸준함을 이기는것은 없다

0개의 댓글