수정페이지 삭제버튼 활성화
Eidit.js
import { useParams, useNavigate } from "react-router-dom";
import Header from "../components/Header";
import Editor from "../components/Editor";
import Button from "../components/Button";
import { DiaryStateContext, DiaryDispatchContext } from "../App";
import { useContext, useEffect, useState } from "react";
const Edit = () => {
const params = useParams();
const nav = useNavigate();
const data = useContext(DiaryStateContext);
const { onUpdate, onDelete } = useContext(DiaryDispatchContext);
const [curDiaryItem, setCurDiaryItem] = useState();
useEffect(() => {
const currentDiaryItem = data.find(
(item) => String(item.id) === String(params.id)
);
if(!currentDiaryItem) {
window.alert('존재하지 않은 일기입니다.');
nav('/', {replace: true})
}
setCurDiaryItem(currentDiaryItem);
console.log(currentDiaryItem);
}, [params.id])
const onClickDelete = () => {
if(window.confirm('일기를 정말로 삭제하시겠습니가? 다시 복구되지 않습니다.')){
onDelete(params.id)
nav('/', {replace: true})
}
}
const onSubmit = (input) => {
if (window.confirm('일기를 정말로 수정하시겠습니까?')){
onUpdate(
params.id,
input.createdDate.getTime(),
input.emotionId,
input.content
)
nav('/', {replac : true});
}
}
return (
<div>
<Header
title={'일기 수정하기'}
leftChild={
<Button onClick = {() => nav(-1)} text={'< 뒤로가기'} />
}
rightChild={
<Button
text={'삭제하기'}
type={'NEGATIVE'}
onClick={onClickDelete}
/>
}
/>
<Editor initData={curDiaryItem} onSubmit={onSubmit}/>
</div>
)
}
export default Edit;
현재 결과

Diary 페이지 구성

다이어리 헤더 버튼 활성화
Diary.js
import { useParams, useNavigate} from "react-router-dom"
import Header from "../components/Header";
import Button from "../components/Button";
import Viewer from "../components/Viewer";
const Diary = () => {
const params = useParams();
const nav = useNavigate();
return (
<div>
<Header
leftChild={
<Button
text={"< 뒤로가기"}
onClick={() => nav(-1)}
/>
}
title={'yyyy년 mm월 ss일 작성'}
rightChild={
<Button
text = "수정하기"
onClick={() => { nav(`/edit/${params.id}`)}}
/>
}
/>
</div>
)
};
export default Diary;


useDiary.js
import { useContext, useState, useEffect } from "react";
import { DiaryStateContext } from "../App";
import { useNavigate } from "react-router-dom";
const useDiary = (id) => {
const nav = useNavigate();
const data = useContext(DiaryStateContext);
const [curDiaryItem, setCurDiaryItem] = useState();
useEffect(() => {
const currentDiaryItem = data.find(
(item) => String(item.id) === String(id)
);
if(!currentDiaryItem) {
window.alert('존재하지 않은 일기입니다.');
nav('/', {replace: true})
}
setCurDiaryItem(currentDiaryItem);
console.log(currentDiaryItem);
}, [id])
return curDiaryItem;
}
export default useDiary;
Edit.js
import { useParams, useNavigate } from "react-router-dom";
import Header from "../components/Header";
import Editor from "../components/Editor";
import Button from "../components/Button";
import { DiaryDispatchContext } from "../App";
import { useContext} from "react";
import useDiary from "../hook/useDiary";
const Edit = () => {
const params = useParams();
const nav = useNavigate();
const { onUpdate, onDelete } = useContext(DiaryDispatchContext);
const curDiaryItem = useDiary(params.id)
const onClickDelete = () => {
if(window.confirm('일기를 정말로 삭제하시겠습니가? 다시 복구되지 않습니다.')){
onDelete(params.id)
nav('/', {replace: true})
}
}
const onSubmit = (input) => {
if (window.confirm('일기를 정말로 수정하시겠습니까?')){
onUpdate(
params.id,
input.createdDate.getTime(),
input.emotionId,
input.content
)
nav('/', {replac : true});
}
}
return (
<div>
<Header
title={'일기 수정하기'}
leftChild={
<Button onClick = {() => nav(-1)} text={'< 뒤로가기'} />
}
rightChild={
<Button
text={'삭제하기'}
type={'NEGATIVE'}
onClick={onClickDelete}
/>
}
/>
<Editor initData={curDiaryItem} onSubmit={onSubmit}/>
</div>
)
}
export default Edit;

get-stringed-date.js
export const getStringDate = (targetDate) => {
// yyyy-mm-dd
let year = targetDate.getFullYear();
let month = targetDate.getMonth() +1
let date = targetDate.getDate();
if (month < 10){
month = `0${month}`;
}
if (date < 10){
date = `0${date}`;
}
return `${year}-${month}-${date}`;
};
Editor.js 에서 잘라오기
Editor.js
import './Editor.css'
import EmotionItem from './EmotionItem';
import Button from './Button';
import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { emotionList } from '../util/constants';
import { getStringDate } from '../util/get-stringed-date';
const Editor = ({initData, onSubmit}) => {
const nav = useNavigate();
const [input, setInput] = useState({
createdDate: new Date(),
emotionId: 3,
content: ""
})
useEffect(()=> {
if(initData){
setInput({
...initData,
createdDate: new Date(Number(initData.createdDate))
})
}
}, [initData])
const onChangeInput = (e) => {
let name= e.target.name;
let value= e.target.value;
if(name ==='createdDate'){
value = new Date(value);
}
setInput({
...input,
[name]: value
})
}
//const emotionId = 5;
const onSubmitButtonClick = () => {
onSubmit(input);
}
return (
<div className='Editor'>
<section className='date_section'>
<h4>오늘의 날짜</h4>
<input
type='date'
name='createdDate'
onChange={onChangeInput}
value={getStringDate(input.createdDate)}
/>
</section>
<section className='emotion_section'>
<h4>오늘의 감정</h4>
<div className='emotion_list_wrapper'>
{emotionList.map((item) => (
<EmotionItem
onClick={() =>
onChangeInput({
target: {
name: 'emotionId',
value: item.emotionId
}}
)
}
key={item.emotionId}
{...item}
isSelected={item.emotionId === input.emotionId}
/>
))}
</div>
</section>
<section className='content_section'>
<h4>오늘의 일기</h4>
<textarea
placeholder='오늘은 어땠나요?'
name='content'
onChange={onChangeInput}
value={input.content}
/>
</section>
<section className='button_section'>
<Button text={'취소하기'} onClick={() => nav(-1)}/>
<Button
text={'작성완료'}
type='POSITIVE'
onClick={onSubmitButtonClick}
/>
</section>
</div>
)
}
export default Editor;
Diary.js
import { useParams, useNavigate} from "react-router-dom"
import Header from "../components/Header";
import Button from "../components/Button";
import Viewer from "../components/Viewer";
import useDiary from "../hook/useDiary";
import { getStringDate } from "../util/get-stringed-date";
const Diary = () => {
const params = useParams();
const nav = useNavigate();
const curDiaryItem = useDiary(params.id);
if(!curDiaryItem){
return <div>데이터 로딩중...</div>
}
const { createdDate, emotionId, content } = curDiaryItem;
const title = getStringDate(new Date(createdDate))
return (
<div>
<Header
leftChild={
<Button
text={"< 뒤로가기"}
onClick={() => nav(-1)}
/>
}
title={`${title} 기록`}
rightChild={
<Button
text = "수정하기"
onClick={() => { nav(`/edit/${params.id}`)}}
/>
}
/>
<Viewer emotionId={emotionId} content={content} />
</div>
)
};
export default Diary;
현재 결과

Viewer.js
import './Viewer.css'
import { getEmotionImage } from '../util/get-emotion-image';
const Viewer = ({ emotionId, content}) => {
return (
<div className='Viewer'>
<section className='img_section'>
<h4>오늘의 감정</h4>
<div>
<img src={getEmotionImage(emotionId)}/>
<div>이모션 텍스트</div>
</div>
</section>
<section className='content_section'>
<h4>오늘의 일기</h4>
<div className='content_wrapper'>
<p>{content}</p>
</div>
</section>
</div>
)
};
export default Viewer;
현재 결과

이모션 가져오기
Viewer.js
import './Viewer.css'
import { getEmotionImage } from '../util/get-emotion-image';
import { emotionList } from '../util/constants';
const Viewer = ({ emotionId, content}) => {
const emotionItem = emotionList.find(
(item) => String(item.emotionId) === String(emotionId)
)
return (
<div className='Viewer'>
<section className='img_section'>
<h4>오늘의 감정</h4>
<div className={`emotion_img_wrapper emotion_img_wrapper_${emotionId}`}>
<img src={getEmotionImage(emotionId)}/>
<div>{emotionItem.emotionName}</div>
</div>
</section>
<section className='content_section'>
<h4>오늘의 일기</h4>
<div className='content_wrapper'>
<p>{content}</p>
</div>
</section>
</div>
)
};
export default Viewer;
Viewer.css
.Viewer > section {
width: 100%;
margin-bottom: 100px;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
}
.Viewer > section > h4 {
font-size: 22px;
font-weight: bold;
}
.Viewer .emotion_img_wrapper {
width: 250px;
height: 250px;
border-radius: 5px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-around;
color: white;
font-size: 25px;
}
.Viewer .emotion_img_wrapper_1 {
background-color: rgb(100,201,100);
}
.Viewer .emotion_img_wrapper_2 {
background-color: rgb(157,215,114);
}
.Viewer .emotion_img_wrapper_3 {
background-color: rgb(253,206,23);
}
.Viewer .emotion_img_wrapper_4 {
background-color: rgb(253,132,70);
}
.Viewer .emotion_img_wrapper_5 {
background-color: rgb(253,86,95);
}
.Viewer .content_wrapper {
width: 100%;
background-color: rgb(236,236,236);
border-radius: 5px;
word-break: keep-all;
overflow-wrap: break-word;
}
.Viewer .content_wrapper > p {
padding: 20px;
text-align: left;
font-size: 20px;
font-weight: 400;
line-height: 2.5;
}
현재 결과

아이피에 데이터 저장하기
App.js
import './App.css';
import Diary 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);
localStorage.setItem('test', 'hello local storage!!!')
// localStorage.setItem('person', {name:'lims'})
localStorage.setItem('person', JSON.stringify({name:'lims'}))
console.log(localStorage.getItem('test'));
console.log(localStorage.getItem('person'));
console.log(JSON.parse(localStorage.getItem('person')));
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={ <Diary/> }/>
<Route path='/edit/:id' element={ <Edit/> }/>
<Route path='*' element={ <Notfound/> }/>
</Routes>
</DiaryDispatchContext.Provider>
</DiaryStateContext.Provider>
</>
);
}
export default App;

App.js
import './App.css';
import Diary 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, useState, useEffect } 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)
let nextState;
switch(action.type){
case 'INIT':
return action.data
case 'CREATE':
nextState = [action.data, ...state]
break;
case 'UPDATE':
nextState = state.map((item) =>
String(item.id) === String(action.data.id) ? action.data : item
)
break;
case 'DELETE':
nextState = state.filter(
(item) => String(item.id) !== String(action.id)
);
break;
default:
return state;
}
localStorage.setItem('diary', JSON.stringify(nextState));
return nextState;
}
export const DiaryStateContext = createContext();
export const DiaryDispatchContext = createContext();
function App() {
const [isLoading, setIsLoading] = useState(true);
const [data, dispatch] = useReducer(reducer, []);
const idRef = useRef(0);
useEffect(() => {
const storagedData = localStorage.getItem('diary');
if(!storagedData){
setIsLoading(false);
return;
}
const parsedData = JSON.parse(storagedData)
if(!Array.isArray(parsedData)){
setIsLoading(false);
return;
}
let maxId = 0;
parsedData.forEach((item) => {
if(Number(item.id) > maxId){
maxId = item.id
}
})
idRef.current = maxId +1;
dispatch({
type: 'INIT',
data: parsedData
});
setIsLoading(false);
}, [])
// localStorage.setItem('test', 'hello local storage!!!')
// // localStorage.setItem('person', {name:'lims'})
// localStorage.setItem('person', JSON.stringify({name:'lims'}))
// console.log(localStorage.getItem('test'));
// console.log(localStorage.getItem('person'));
// console.log(JSON.parse(localStorage.getItem('person')));
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
})
}
if(isLoading){
return <div>데이터 로딩중 입니다...</div>
};
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={ <Diary/> }/>
<Route path='/edit/:id' element={ <Edit/> }/>
<Route path='*' element={ <Notfound/> }/>
</Routes>
</DiaryDispatchContext.Provider>
</DiaryStateContext.Provider>
</>
);
}
export default App;
'Front > React' 카테고리의 다른 글
| Day13_react(7)_Pm (0) | 2025.09.12 |
|---|---|
| Day11_react(6)_Pm (0) | 2025.09.09 |
| Day11_react(6)_Am (0) | 2025.09.09 |
| Day10_react(5)_Pm (0) | 2025.09.08 |
| Day10_react(5)_Am (0) | 2025.09.08 |