본문 바로가기
Front/React

Day11_react(6)_Am

by roaring90s 2025. 9. 9.

Header부분 기능추가

Home.js

import Button from "../components/Button";
import Header from "../components/Header";
import DiaryList from "../components/DiaryList";
import { useState } from "react";

const Home = () => {
   
    const [pivotDate, setPivotDate] = useState(new Date());

    const onIncreaseMonth = () => {
        setPivotDate(new Date(pivotDate.getFullYear(), pivotDate.getMonth()+1))
    }

    const onDecreaseMonth = () => {
        setPivotDate(new Date(pivotDate.getFullYear(), pivotDate.getMonth()-1))
    }

    return (
        <div>
            <Header
                title={`${pivotDate.getFullYear()}${pivotDate.getMonth()+1}월`}
                leftChild={ <Button text={'<'} onClick={onDecreaseMonth}/> }
                rightChild={<Button text={'>'} onClick={onIncreaseMonth}/>}
            />
            <DiaryList/>

        </div>
    )
};
export default Home;

 


월별로 게시글 보이기

Home.js

import Button from "../components/Button";
import Header from "../components/Header";
import DiaryList from "../components/DiaryList";
import { useState, useContext } from "react";
import { DiaryStateContext } from "../App"

const getMonthlyData = (pivotDate, data) => {
    const beginTime = new Date(
        pivotDate.getFullYear(),
        pivotDate.getMonth(),
        1,
        0,
        0,
        0
    ).getTime();

    const endTime = new Date(
        pivotDate.getFullYear(),
        pivotDate.getMonth()+1,
        0,
        23,
        59,
        59

    ).getTime();

    return data.filter(
        (item) => beginTime <= item.createdDate && item.createdDate <= endTime
    );
}


const Home = () => {
   
    const data = useContext(DiaryStateContext);
    const [pivotDate, setPivotDate] = useState(new Date());

    const monthlyData = getMonthlyData(pivotDate, data);
    console.log(monthlyData);
   
   

    const onIncreaseMonth = () => {
        setPivotDate(new Date(pivotDate.getFullYear(), pivotDate.getMonth()+1))
    }

    const onDecreaseMonth = () => {
        setPivotDate(new Date(pivotDate.getFullYear(), pivotDate.getMonth()-1))
    }

    return (
        <div>
            <Header
                title={`${pivotDate.getFullYear()}${pivotDate.getMonth()+1}월`}
                leftChild={ <Button text={'<'} onClick={onDecreaseMonth}/> }
                rightChild={<Button text={'>'} onClick={onIncreaseMonth}/>}
            />
            <DiaryList data={monthlyData}/>

        </div>
    )
};
export default Home;

 

DiaryList.js

import './DiaryList.css'
import DiaryItem from './DiaryItem'
import Button from './Button'

const DiaryList = ({data}) => {
    return (
        <div className='DiaryList'>
            <div className='menu_bar'>
                <select>
                    <option value={'latest'}>최신순</option>
                    <option value={'oldest'}>오래된순</option>
                </select>
                <Button text={'새 일기 쓰기'} type={'POSITIVE'}/>
            </div>
            <div className='list_warapper'>
                { data.map((item) => (
                    <DiaryItem key={item.id} {...item}/>
                ))}
            </div>
        </div>
    )
}
export default DiaryList;

 

DiaryItem.js

import './DiaryItem.css'
import { getEmotionImage } from '../util/get-emotion-image';
import Button from './Button';

const DiaryItem = ({id, emotionId, createdDate, content}) => {
 
    return (
        <div className='DiaryItem'>
            <div className={`img_section img_section_${emotionId}`}>
                <img src={getEmotionImage(emotionId)}/>
            </div>
            <div className='info_section'>
                <div className='created_date'>
                    { new Date(createdDate).toLocaleDateString()}
                </div>
                <div className='content'>
                    {content}
                </div>
            </div>
            <div className='button_section'>
                <Button text={'수정하기'}/>
            </div>
        </div>
    )
};

export default DiaryItem;

 

 

App.js

import './App.css';
import Dairy from './pages/Diary';
import New from './pages/New';
import Home from './pages/Home';
import Notfound from './pages/Notfound';
import { Route, Routes } from 'react-router-dom';
import { useReducer, useRef, createContext } from 'react';
import Header from './components/Header';
import Button from './components/Button';



const mockData = [
  {
    id: 1,
    createdDate: new Date('2025-09-09').getTime(),
    emotionId: 1,
    content:'1번 일기 내용'
  },
  {
    id: 2,
    createdDate: new Date('2025-09-03').getTime(),
    emotionId: 2,
    content: '2번 일기 내용'
  },
  {
    id: 3,
    createdDate: new Date('2025-08-22').getTime(),
    emotionId: 4,
    content: '3번 일기 내용'
  }
]

function reducer(state, action){
  console.log(action)
  switch(action.type){
    case 'CREATE':
      return [action.data, ...state]
    case 'UPDATE':
      return state.map((item) =>
        String(item.id) === String(action.data.id) ? action.data : item
      )
    case 'DELETE':
      return state.filter(
        (item) => String(item.id) !== String(action.id)
      );
    default:
      return state;
  }
}


export const DiaryStateContext = createContext();
export const DiaryDispatchContext = createContext();


function App() {

  const [data, dispatch] = useReducer(reducer, mockData);
  const idRef = useRef(4);

  const onCreate = (createdDate, emotionId, content) => {
    dispatch({
      type: 'CREATE',
      data: {
        id: idRef.current++,
        createdDate,
        emotionId,
        content
      }
    })
  }

  const onUpdate = (id, createdDate, emotionId, content) => {
    dispatch({
      type:'UPDATE',
      data: {
        id,
        createdDate,
        emotionId,
        content
      }
    })
  }
 
  const onDelete = (id) => {
    dispatch({
      type:'DELETE',
      id
    })
  }

 
  return (
   
    <>      
      <DiaryStateContext.Provider value={data}>
        <DiaryDispatchContext.Provider value={{ onCreate, onUpdate, onDelete}}>
          <Routes>
            <Route path='/' element={ <Home/> }/>
            <Route path='/new' element={ <New/> }/>
            <Route path='/diary/:id' element={ <Dairy/> }/>
            <Route path='*' element={ <Notfound/> }/>
          </Routes>
        </DiaryDispatchContext.Provider>
      </DiaryStateContext.Provider>
    </>
  );
}

export default App;

 

현재 결과

 


최신순, 오래된순 버튼활성화

DiaryList.js

import './DiaryList.css'
import DiaryItem from './DiaryItem'
import Button from './Button'
import {useState} from 'react'


const DiaryList = ({data}) => {

    const [sortType, setSortType] = useState('latest');

    const onChangeSortType = (e) => {
        setSortType(e.target.value)
    }

    
    return (
        <div className='DiaryList'>
            <div className='menu_bar'>
                <select value={sortType} onChange={onChangeSortType}>
                    <option value={'latest'}>최신순</option>
                    <option value={'oldest'}>오래된순</option>
                </select>
                <Button text={'새 일기 쓰기'} type={'POSITIVE'}/>
            </div>
            <div className='list_warapper'>
                { data.map((item) => (
                    <DiaryItem key={item.id} {...item}/>
                ))}
            </div>
        </div>
    )
}
export default DiaryList;

 

현재결과

 


최신순, 오래된순 정렬

DiaryList.js

import './DiaryList.css'
import DiaryItem from './DiaryItem'
import Button from './Button'
import {useState} from 'react'


const DiaryList = ({data}) => {

    const [sortType, setSortType] = useState('latest');

    const onChangeSortType = (e) => {
        setSortType(e.target.value)
    }

    const getSortedData = () => {
        return data.toSorted((a,b) => {
            if(sortType === 'oldest'){
                return a.createdDate - b.createdDate;
            }
            else{
                return b.createdDate - a.createdDate;
            }
        })

    }

    const sortedData = getSortedData();
   
    return (
        <div className='DiaryList'>
            <div className='menu_bar'>
                <select value={sortType} onChange={onChangeSortType}>
                    <option value={'latest'}>최신순</option>
                    <option value={'oldest'}>오래된순</option>
                </select>
                <Button text={'새 일기 쓰기'} type={'POSITIVE'}/>
            </div>
            <div className='list_warapper'>
                { sortedData.map((item) => (
                    <DiaryItem key={item.id} {...item}/>
                ))}
            </div>
        </div>
    )
}
export default DiaryList;

tosort 정보를 그대로 가져옴

sortType

오름차순을 할때 a-b 양수

내림차순 b-a 양수

 


 

새일기쓰기 버튼 활성화

DiaryList.js

import './DiaryList.css'
import DiaryItem from './DiaryItem'
import Button from './Button'
import {useState} from 'react'
import { useNavigate } from 'react-router-dom'


const DiaryList = ({data}) => {

    const nav = useNavigate();

    const [sortType, setSortType] = useState('latest');

    const onChangeSortType = (e) => {
        setSortType(e.target.value)
    }

    const getSortedData = () => {
        return data.toSorted((a,b) => {
            if(sortType === 'oldest'){
                return a.createDate - b.createDate;
            }
            else{
                return b.createDate - a.createDate;
            }
        })

    }

    const sortedData = getSortedData();
   
    return (
        <div className='DiaryList'>
            <div className='menu_bar'>
                <select value={sortType} onChange={onChangeSortType}>
                    <option value={'latest'}>최신순</option>
                    <option value={'oldest'}>오래된순</option>
                </select>
                <Button
                    text={'새 일기 쓰기'}
                    type={'POSITIVE'}
                    onClick={()=> nav('/new')}
                />
            </div>
            <div className='list_warapper'>
                { sortedData.map((item) => (
                    <DiaryItem key={item.id} {...item}/>
                ))}
            </div>
        </div>
    )
}
export default DiaryList;

 

 


수정하기 만들기

Edit.js 만들기


const Edit = () => {
    return (
        <div>일기 수정페이지 입니다.</div>
    )
}

export default Edit;

 

App.js

import './App.css';
import Dairy from './pages/Diary';
import New from './pages/New';
import Home from './pages/Home';
import Notfound from './pages/Notfound';
import { Route, Routes } from 'react-router-dom';
import { useReducer, useRef, createContext } from 'react';
import Header from './components/Header';
import Button from './components/Button';
import Edit from './pages/Edit';



const mockData = [
  {
    id: 1,
    createdDate: new Date('2025-09-09').getTime(),
    emotionId: 1,
    content:'1번 일기 내용'
  },
  {
    id: 2,
    createdDate: new Date('2025-09-03').getTime(),
    emotionId: 2,
    content: '2번 일기 내용'
  },
  {
    id: 3,
    createdDate: new Date('2025-08-22').getTime(),
    emotionId: 4,
    content: '3번 일기 내용'
  }
]

function reducer(state, action){
  console.log(action)
  switch(action.type){
    case 'CREATE':
      return [action.data, ...state]
    case 'UPDATE':
      return state.map((item) =>
        String(item.id) === String(action.data.id) ? action.data : item
      )
    case 'DELETE':
      return state.filter(
        (item) => String(item.id) !== String(action.id)
      );
    default:
      return state;
  }
}


export const DiaryStateContext = createContext();
export const DiaryDispatchContext = createContext();


function App() {

  const [data, dispatch] = useReducer(reducer, mockData);
  const idRef = useRef(4);

  const onCreate = (createdDate, emotionId, content) => {
    dispatch({
      type: 'CREATE',
      data: {
        id: idRef.current++,
        createdDate,
        emotionId,
        content
      }
    })
  }

  const onUpdate = (id, createdDate, emotionId, content) => {
    dispatch({
      type:'UPDATE',
      data: {
        id,
        createdDate,
        emotionId,
        content
      }
    })
  }
 
  const onDelete = (id) => {
    dispatch({
      type:'DELETE',
      id
    })
  }

 
  return (
   
    <>      
      <DiaryStateContext.Provider value={data}>
        <DiaryDispatchContext.Provider value={{ onCreate, onUpdate, onDelete}}>
          <Routes>
            <Route path='/' element={ <Home/> }/>
            <Route path='/new' element={ <New/> }/>
            <Route path='/diary/:id' element={ <Dairy/> }/>
            <Route path='/edit/:id' element={ <Edit/> }/>
            <Route path='*' element={ <Notfound/> }/>
          </Routes>
        </DiaryDispatchContext.Provider>
      </DiaryStateContext.Provider>
    </>
  );
}

export default App;

 

DiaryItem.js

import './DiaryItem.css'
import { getEmotionImage } from '../util/get-emotion-image';
import Button from './Button';
import { useNavigate } from 'react-router-dom';


const DiaryItem = ({id, emotionId, createdDate, content}) => {
 
    const nav = useNavigate();

    const goEditPage = () => {
        nav(`/edit/${id}`);
    }
   
    return (
        <div className='DiaryItem'>
            <div className={`img_section img_section_${emotionId}`}>
                <img src={getEmotionImage(emotionId)}/>
            </div>
            <div className='info_section'>
                <div className='created_date'>
                    { new Date(createdDate).toLocaleDateString()}
                </div>
                <div className='content'>
                    {content}
                </div>
            </div>
            <div className='button_section'>
                <Button text={'수정하기'} onClick={goEditPage}/>
            </div>
        </div>
    )
};

export default DiaryItem;

 

Edit.js 추가작성

import { useParams } from "react-router-dom";


const Edit = () => {
    const params = useParams();

    return (
        <div>{params.id}번 일기 수정페이지 입니다.</div>
    )
}

export default Edit;

 


 

다이어리 페이지로 넘어가기

Diary.js

import { useParams } from "react-router-dom"

const Diary = () => {
    const params = useParams();
    console.log(params)
    return (
        <div>
            <h1>{params.id}번째 다이어리</h1>
        </div>
    )
};
export default Diary;

 

DiaryItem.js

import './DiaryItem.css'
import { getEmotionImage } from '../util/get-emotion-image';
import Button from './Button';
import { useNavigate } from 'react-router-dom';


const DiaryItem = ({id, emotionId, createdDate, content}) => {
 
    const nav = useNavigate();

    const goEditPage = () => {
        nav(`/edit/${id}`);
    }

    const goDiaryPage = () => {
        nav(`/diary/${id}`)
    }
   
    return (
        <div className='DiaryItem'>
            <div onClick={goDiaryPage} className={`img_section img_section_${emotionId}`}>
                <img src={getEmotionImage(emotionId)}/>
            </div>
            <div onClick={goDiaryPage} className='info_section'>
                <div className='created_date'>
                    { new Date(createdDate).toLocaleDateString()}
                </div>
                <div className='content'>
                    {content}
                </div>
            </div>
            <div className='button_section'>
                <Button text={'수정하기'} onClick={goEditPage}/>
            </div>
        </div>
    )
};

export default DiaryItem;

 


 

New 페이지 만들기

New.js

import Header from "../components/Header";
import Button from "../components/Button";
import Editor from "../components/Editor";


const New = () => {
    return (
        <div>
            <Header
                title='새 일기 쓰기'
                leftChild={ <Button text={'< 뒤로 가기'}/>}
            />
           
            <Editor/>
        </div>
    )
}
export default New;

 

Editor.js

import './Editor.css'

const Editor = () => {
    return (
        <div className='Editor'>
           <section className='date_section'>
            <h4>오늘의 날짜</h4>
            <input type='date'/>
           </section>
           <section>
            <h4>오늘의 감정</h4>
           </section>
        </div>
    )
}
export default Editor;

 

Editor.js

.Editor > section {
    margin-bottom: 40px;
}

.Editor > section > h4 {
    font-size: 22px;
    font-weight: bold;
}

.Editor > section > input{
    background-color: rgb(236, 236, 236);
    border : none;
    border-radius: 5px;
    font-size: 20px;
   
    padding: 10px 20px ;
}

 

현재 결과

 

 


 

EmotionItem.js / .css 추가

 

EmotionItem.js

import './EmotionItem.css'

const EmotionItem = () => {
    return(
        <div>e</div>
    )
};
export default EmotionItem;

 

Editor.js

import './Editor.css'
import EmotionItem from './EmotionItem';
import Button from './Button';

const emotionList = [{
    emotionId: 1,
    emotionName: '매우 좋음'
    },
    {
    emotionId:2,
    emotionName: '좋음'
    },
    {
    emotionId:3,
    emotionName: '보통'
    },
    {
    emotionId:4,
    emotionName: '나쁨'
    },
    {
    emotionId:5,
    emotionName: '매우나쁨'
    },
]

const Editor = () => {
    return (
        <div className='Editor'>
           <section className='date_section'>
            <h4>오늘의 날짜</h4>
            <input type='date'/>
           </section>
           <section className='emotion_section'>
            <h4>오늘의 감정</h4>
            <div className='emotion_list_wrapper'>
                {emotionList.map((item) => (
                    <EmotionItem
                        key={item.emotionId}
                        {...item}
                        />
                    ))}
            </div>
           </section>
        </div>
    )
}
export default Editor;

 

EmotionItem.js

import './EmotionItem.css'
import { getEmotionImage } from '../util/get-emotion-image';

const EmotionItem = ({ emotionId, emotionName}) => {
    return(
        <div className='EmotionItem'>
            <img className='emotion_img' src={getEmotionImage(emotionId)}/>
            <div className='emotion_name'>{emotionName}</div>

        </div>
    )
};
export default EmotionItem;

 

Editor.css

.Editor > section {
    margin-bottom: 40px;
}

.Editor > section > h4 {
    font-size: 22px;
    font-weight: bold;
}

.Editor > section > input{
    background-color: rgb(236, 236, 236);
    border : none;
    border-radius: 5px;
    font-size: 20px;
   
    padding: 10px 20px ;
}

.Editor .emotion_section .emotion_list_wrapper{
    display: flex;
    justify-content: space-around;
    gap: 2%;
}

 

EmotionItem.css

.EmotionItem {
    background-color: rgb(236,236,236);
    padding: 20px;
    border-radius: 5px;
    cursor: pointer;
    text-align: center;
}

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

 

현재 결과

 


 

Editor.js

import './Editor.css'
import EmotionItem from './EmotionItem';
import Button from './Button';

const emotionList = [{
    emotionId: 1,
    emotionName: '매우 좋음'
    },
    {
    emotionId:2,
    emotionName: '좋음'
    },
    {
    emotionId:3,
    emotionName: '보통'
    },
    {
    emotionId:4,
    emotionName: '나쁨'
    },
    {
    emotionId:5,
    emotionName: '매우나쁨'
    },
]

const Editor = () => {
    const emotionId = 5;

    return (
        <div className='Editor'>
           <section className='date_section'>
            <h4>오늘의 날짜</h4>
            <input type='date'/>
           </section>
           <section className='emotion_section'>
            <h4>오늘의 감정</h4>
            <div className='emotion_list_wrapper'>
                {emotionList.map((item) => (
                    <EmotionItem
                        key={item.emotionId}
                        {...item}
                        isSelected={item.emotionId === emotionId}
                        />
                    ))}
            </div>
           </section>
        </div>
    )
}
export default Editor;

 

EmotionItem.js

import './EmotionItem.css'
import { getEmotionImage } from '../util/get-emotion-image';

const EmotionItem = ({ emotionId, emotionName, isSelected}) => {
    return(
        <div className={`EmotionItem ${isSelected ? `EmotionItem_on_${emotionId}` : ""}`}>
            <img className='emotion_img' src={getEmotionImage(emotionId)}/>
            <div className='emotion_name'>{emotionName}</div>

        </div>
    )
};
export default EmotionItem;

 

EmotionItem.css

.EmotionItem {
    background-color: rgb(236,236,236);
    padding: 20px;
    border-radius: 5px;
    cursor: pointer;
    text-align: center;
}

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

.EmotionItem_on_1 {
    color: white;
    background-color: rgb(100,201,100);
}
.EmotionItem_on_2 {
    color: white;
    background-color: rgb(157,215,114);
}
.EmotionItem_on_3 {
    color: white;
    background-color: rgb(253,206,23);
}
.EmotionItem_on_4 {
    color: white;
    background-color: rgb(253,132,70);
}
.EmotionItem_on_5 {
    color: white;
    background-color: rgb(253,86,95);
}

 

현재 결과

 


 

텍스트창 만들기

Editor.js

import './Editor.css'
import EmotionItem from './EmotionItem';
import Button from './Button';

const emotionList = [{
    emotionId: 1,
    emotionName: '매우 좋음'
    },
    {
    emotionId:2,
    emotionName: '좋음'
    },
    {
    emotionId:3,
    emotionName: '보통'
    },
    {
    emotionId:4,
    emotionName: '나쁨'
    },
    {
    emotionId:5,
    emotionName: '매우나쁨'
    },
]

const Editor = () => {
    const emotionId = 5;

    return (
        <div className='Editor'>
           <section className='date_section'>
            <h4>오늘의 날짜</h4>
            <input type='date'/>
           </section>
           <section className='emotion_section'>
            <h4>오늘의 감정</h4>
            <div className='emotion_list_wrapper'>
                {emotionList.map((item) => (
                    <EmotionItem
                        key={item.emotionId}
                        {...item}
                        isSelected={item.emotionId === emotionId}
                        />
                    ))}
            </div>
           </section>
           <section className='content_section'>
                    <h4>오늘의 일기</h4>
                    <textarea placeholder='오늘은 어땠나요?'/>
           </section>
         
        </div>
    )
}
export default Editor;

 

Editor.css

.Editor > section {
    margin-bottom: 40px;
}

.Editor > section > h4 {
    font-size: 22px;
    font-weight: bold;
}

.Editor > section > input, textarea{
    background-color: rgb(236, 236, 236);
    border : none;
    border-radius: 5px;
    font-size: 20px;
   
    padding: 10px 20px ;
}

.Editor > section > textarea{
    padding: 20px;
    width: 100%;
    min-height: 200px;
    resize: vertical;
    box-sizing: border-box;
}

.Editor .emotion_section .emotion_list_wrapper{
    display: flex;
    justify-content: space-around;
    gap: 2%;
}

 


 

아래 버튼만들기

Editor.js

import './Editor.css'
import EmotionItem from './EmotionItem';
import Button from './Button';

const emotionList = [{
    emotionId: 1,
    emotionName: '매우 좋음'
    },
    {
    emotionId:2,
    emotionName: '좋음'
    },
    {
    emotionId:3,
    emotionName: '보통'
    },
    {
    emotionId:4,
    emotionName: '나쁨'
    },
    {
    emotionId:5,
    emotionName: '매우나쁨'
    },
]

const Editor = () => {
    const emotionId = 5;

    return (
        <div className='Editor'>
           <section className='date_section'>
            <h4>오늘의 날짜</h4>
            <input type='date'/>
           </section>
           <section className='emotion_section'>
            <h4>오늘의 감정</h4>
            <div className='emotion_list_wrapper'>
                {emotionList.map((item) => (
                    <EmotionItem
                        key={item.emotionId}
                        {...item}
                        isSelected={item.emotionId === emotionId}
                        />
                    ))}
            </div>
           </section>
           <section className='content_section'>
                    <h4>오늘의 일기</h4>
                    <textarea placeholder='오늘은 어땠나요?'/>
           </section>
           <section className='button_section'>
                    <Button text={'취소하기'}/>
                    <Button text={'작성완료'} type='POSITIVE'/>
           </section>
        </div>
    )
}
export default Editor;

 

Editor.css

 

.Editor > section {
    margin-bottom: 40px;
}

.Editor > section > h4 {
    font-size: 22px;
    font-weight: bold;
}

.Editor > section > input, textarea{
    background-color: rgb(236, 236, 236);
    border : none;
    border-radius: 5px;
    font-size: 20px;
   
    padding: 10px 20px ;
}

.Editor > section > textarea{
    padding: 20px;
    width: 100%;
    min-height: 200px;
    resize: vertical;
    box-sizing: border-box;
}

.Editor .emotion_section .emotion_list_wrapper{
    display: flex;
    justify-content: space-around;
    gap: 2%;
}

.Editor .button_section{
    display: flex;
    justify-content: space-between;
}

 

현재 결과


 

'Front > React' 카테고리의 다른 글

Day11_react(6)_Am  (0) 2025.09.10
Day11_react(6)_Pm  (0) 2025.09.09
Day10_react(5)_Pm  (0) 2025.09.08
Day10_react(5)_Am  (0) 2025.09.08
Day9_react(4)_Pm  (0) 2025.09.05