일단 처음에 식사나 배변 횟수 또는 상태 컬럼을 어떻게 나누고 표현할지, 라디오버튼 또는 체크박스를 어떻게 표현할지 많이 고민하고 헤매었다.
개인적으로 이 부분은 사용자가 쉽고 빠르게 작성하며 한 눈에 확인할 수 있는 페이지이길 바랐으며, 댕린이집 웹사이트의 기능들 중에 어쩌면 가장 주요 기능이기도 하다.
먼저 테이블 설계부터 기록해보자.
⭐ Note id: 고유 식별자 (자동 증가) note_date: 알림장의 날짜 meal: 식사 정보 (JSON 형태로 아침식사, 점심식사, 저녁식사, 간식 여부를 저장) poop_frequency: 배변 횟수 (0회, 1회, 2회, 3회 중 하나의 값을 저장) poop_condition: 배변 상태 (건강, 보통, 나쁨, 설사 중 하나의 값을 저장) mood: 컨디션 (좋음, 보통, 피곤, 나쁨 중 하나의 값을 저장) daily: 오늘 하루의 기록 (텍스트 형태로 저장)
훈련사가 특정 강아지를 선택해 해당 강아지의 알림장을 기록할 것이기 때문에 강아지 목록을 저장할 상태 변수
그리고 새로운 알림장 데이터를 저장할 상태변수를 작성해준다.
const [cookies] = useCookies(['accessToken']);
const [puppies, setPuppies] = useState([]); // 강아지 목록을 저장할 상태 변수
const [newNote, setNewNote] = useState({ // 새로운 알림장 데이터를 저장할 상태 변수
noteDate: '', // 날짜
meal: '', // 식사량
poopFrequency: '', // 배변 횟수
poopCondition: '', // 배변 상태
mood: '', // 컨디션
daily: '', // 오늘 하루
puppyId: ''// 강아지 ID
});
consthandleInputChange= (event) => {
const { name, value } = event.target; // 이벤트 타겟에서 name과 value를 추출
setNewNote(prevState => ({ // 새로운 알림장 상태를 업데이트
...prevState, // 이전 상태를 복사하고
[name]: value // 변경된 필드만 업데이트
}));
};
알림장 저장하는 함수
consthandleSubmit= (event) => {
event.preventDefault(); .
axios.post('http://localhost:8082/api/v1/auth/y/saveNote', newNote, {
headers: {
'Authorization': `Bearer ${cookies.accessToken}`
}
})
.then((response) => {
setNewNote({ // 성공 후, 알림장 상태를 초기화
noteDate: '',
meal: '',
poopFrequency: '',
poopCondition: '',
mood: '',
daily: '',
puppyId: ''
});
console.log(response.data);
alert("알림장 등록 완료!");
navigate('/noteList'); // 알림장 목록 페이지로 이동
})
.catch(error => {
console.error('Error: ', error);
});
};
JSX
return (
<><div><Nav2 /></div><h3>📝알림장</h3><formclassName="noteForm"onSubmit={handleSubmit}><divclassName="section"><fieldset><legend>🐶 강아지 선택</legend><selectname="puppyId"value={newNote.puppyId}onChange={handleInputChange}required> {/* 강아지 선택 드롭다운 메뉴 */}
<optionvalue="">강아지를 선택하세요</option>
{puppies.map(puppy => ( // 강아지 목록을 순회하며 옵션을 렌더링
<optionkey={puppy.id}value={puppy.id}>{puppy.puppy_name}</option>
))}
</select></fieldset><br /><fieldset><legend>📅 날짜</legend><inputtype="date"name="noteDate"value={newNote.noteDate}onChange={handleInputChange}required
/> {/* 날짜 입력 필드*/}
</fieldset><br /><fieldset><legend>🦴 식사</legend><label><inputtype="radio"name="meal"value="많이 먹음"onChange={handleInputChange} /> 많이 먹음 </label><label><inputtype="radio"name="meal"value="적당하게 먹음"onChange={handleInputChange} /> 적당하게 먹음 </label><label><inputtype="radio"name="meal"value="조금 먹음"onChange={handleInputChange} /> 조금 먹음 </label><label><inputtype="radio"name="meal"value="안먹음"onChange={handleInputChange} /> 안먹음 </label></fieldset><br /><fieldset><legend>💩 배변</legend><label><inputtype="radio"name="poopFrequency"value="0회"onChange={handleInputChange} /> 0회 </label><label><inputtype="radio"name="poopFrequency"value="1회"onChange={handleInputChange} /> 1회 </label><label><inputtype="radio"name="poopFrequency"value="2회"onChange={handleInputChange} /> 2회 </label><label><inputtype="radio"name="poopFrequency"value="3회"onChange={handleInputChange} /> 3회 </label><br /><label><inputtype="radio"name="poopCondition"value="건강"onChange={handleInputChange} /> 건강 </label><label><inputtype="radio"name="poopCondition"value="보통"onChange={handleInputChange} /> 보통 </label><label><inputtype="radio"name="poopCondition"value="나쁨"onChange={handleInputChange} /> 나쁨 </label><label><inputtype="radio"name="poopCondition"value="설사"onChange={handleInputChange} /> 설사 </label></fieldset><br /><fieldset><legend>🐕 컨디션</legend><label><inputtype="radio"name="mood"value="좋음"onChange={handleInputChange} /> 좋음 </label><label><inputtype="radio"name="mood"value="보통"onChange={handleInputChange} /> 보통 </label><label><inputtype="radio"name="mood"value="피곤"onChange={handleInputChange} /> 피곤 </label><label><inputtype="radio"name="mood"value="나쁨"onChange={handleInputChange} /> 나쁨 </label><br /></fieldset><br /><fieldset><legend>💜 오늘 하루는</legend><textareaid="daily"name="daily"value={newNote.daily}onChange={handleInputChange}></textarea></fieldset></div><buttontype="submit">작성하기</button></form></>
);
};
NoteList.js
로그인 검증: 컴포넌트가 마운트될 때 accessToken을 확인하여 로그인 여부를 검증하고, 로그인되지 않았으면 로그인 페이지로 이동 알림장 목록 가져오기: 로그인된 경우 알림장 목록을 서버에서 가져와 상태 변수에 저장. 알림장은 최신 순으로 정렬됨 페이지네이션: 현재 페이지에 표시할 알림장을 계산하고, 페이지 번호를 클릭할 때 현재 페이지를 업데이트 알림장 상세보기: 알림장을 클릭하면 해당 알림장의 상세보기 페이지로 이동 알림장 등록: 알림장 등록 버튼을 클릭하면 알림장 등록 페이지로 이동
로그인 검증 및 알림장 목록 가져오기
useEffect(() => {
// 토큰으로 로그인 검증if (!cookies.accessToken) {
alert('로그인 해주세요!');
navigate('/login'); // 로그인 페이지로 이동return;
}
// 로그인 O
axios.get('/api/v1/auth/y/note') // 알림장 목록을 가져오는 GET 요청을 보냄
.then(response => {
const sortedNotes = response.data.sort((a, b) => b.id - a.id); // 알림장을 ID 역순으로 정렬setNotes(sortedNotes); // 정렬된 알림장을 상태 변수에 저장
})
.catch(error => {
console.error('Error: ', error);
});
}, [cookies.accessToken, axios, navigate]); // 의존성 배열에 cookies.accessToken, axios, navigate를 포함
페이지네이션
const indexOfLastNote = currentPage * notesPerPage; // 현재 페이지의 마지막 알림장의 인덱스를 계산const indexOfFirstNote = indexOfLastNote - notesPerPage; // 현재 페이지의 첫 번째 알림장의 인덱스를 계산const currentNotes = notes.slice(indexOfFirstNote, indexOfLastNote); // 현재 페이지에 해당하는 알림장을 잘라냄constpaginate = (pageNumber) => setCurrentPage(pageNumber); // 페이지 번호를 설정하는 함수
일단 처음에 식사나 배변 횟수 또는 상태 컬럼을 어떻게 나누고 표현할지, 라디오버튼 또는 체크박스를 어떻게 표현할지 많이 고민하고 헤매었다.
개인적으로 이 부분은 사용자가 쉽고 빠르게 작성하며 한 눈에 확인할 수 있는 페이지이길 바랐으며, 댕린이집 웹사이트의 기능들 중에 어쩌면 가장 주요 기능이기도 하다.
먼저 테이블 설계부터 기록해보자.
⭐ Note id: 고유 식별자 (자동 증가) note_date: 알림장의 날짜 meal: 식사 정보 (JSON 형태로 아침식사, 점심식사, 저녁식사, 간식 여부를 저장) poop_frequency: 배변 횟수 (0회, 1회, 2회, 3회 중 하나의 값을 저장) poop_condition: 배변 상태 (건강, 보통, 나쁨, 설사 중 하나의 값을 저장) mood: 컨디션 (좋음, 보통, 피곤, 나쁨 중 하나의 값을 저장) daily: 오늘 하루의 기록 (텍스트 형태로 저장)
훈련사가 특정 강아지를 선택해 해당 강아지의 알림장을 기록할 것이기 때문에 강아지 목록을 저장할 상태 변수
그리고 새로운 알림장 데이터를 저장할 상태변수를 작성해준다.
const [cookies] = useCookies(['accessToken']);
const [puppies, setPuppies] = useState([]); // 강아지 목록을 저장할 상태 변수
const [newNote, setNewNote] = useState({ // 새로운 알림장 데이터를 저장할 상태 변수
noteDate: '', // 날짜
meal: '', // 식사량
poopFrequency: '', // 배변 횟수
poopCondition: '', // 배변 상태
mood: '', // 컨디션
daily: '', // 오늘 하루
puppyId: ''// 강아지 ID
});
consthandleInputChange= (event) => {
const { name, value } = event.target; // 이벤트 타겟에서 name과 value를 추출
setNewNote(prevState => ({ // 새로운 알림장 상태를 업데이트
...prevState, // 이전 상태를 복사하고
[name]: value // 변경된 필드만 업데이트
}));
};
알림장 저장하는 함수
consthandleSubmit= (event) => {
event.preventDefault(); .
axios.post('http://localhost:8082/api/v1/auth/y/saveNote', newNote, {
headers: {
'Authorization': `Bearer ${cookies.accessToken}`
}
})
.then((response) => {
setNewNote({ // 성공 후, 알림장 상태를 초기화
noteDate: '',
meal: '',
poopFrequency: '',
poopCondition: '',
mood: '',
daily: '',
puppyId: ''
});
console.log(response.data);
alert("알림장 등록 완료!");
navigate('/noteList'); // 알림장 목록 페이지로 이동
})
.catch(error => {
console.error('Error: ', error);
});
};
JSX
return (
<><div><Nav2 /></div><h3>📝알림장</h3><formclassName="noteForm"onSubmit={handleSubmit}><divclassName="section"><fieldset><legend>🐶 강아지 선택</legend><selectname="puppyId"value={newNote.puppyId}onChange={handleInputChange}required> {/* 강아지 선택 드롭다운 메뉴 */}
<optionvalue="">강아지를 선택하세요</option>
{puppies.map(puppy => ( // 강아지 목록을 순회하며 옵션을 렌더링
<optionkey={puppy.id}value={puppy.id}>{puppy.puppy_name}</option>
))}
</select></fieldset><br /><fieldset><legend>📅 날짜</legend><inputtype="date"name="noteDate"value={newNote.noteDate}onChange={handleInputChange}required
/> {/* 날짜 입력 필드*/}
</fieldset><br /><fieldset><legend>🦴 식사</legend><label><inputtype="radio"name="meal"value="많이 먹음"onChange={handleInputChange} /> 많이 먹음 </label><label><inputtype="radio"name="meal"value="적당하게 먹음"onChange={handleInputChange} /> 적당하게 먹음 </label><label><inputtype="radio"name="meal"value="조금 먹음"onChange={handleInputChange} /> 조금 먹음 </label><label><inputtype="radio"name="meal"value="안먹음"onChange={handleInputChange} /> 안먹음 </label></fieldset><br /><fieldset><legend>💩 배변</legend><label><inputtype="radio"name="poopFrequency"value="0회"onChange={handleInputChange} /> 0회 </label><label><inputtype="radio"name="poopFrequency"value="1회"onChange={handleInputChange} /> 1회 </label><label><inputtype="radio"name="poopFrequency"value="2회"onChange={handleInputChange} /> 2회 </label><label><inputtype="radio"name="poopFrequency"value="3회"onChange={handleInputChange} /> 3회 </label><br /><label><inputtype="radio"name="poopCondition"value="건강"onChange={handleInputChange} /> 건강 </label><label><inputtype="radio"name="poopCondition"value="보통"onChange={handleInputChange} /> 보통 </label><label><inputtype="radio"name="poopCondition"value="나쁨"onChange={handleInputChange} /> 나쁨 </label><label><inputtype="radio"name="poopCondition"value="설사"onChange={handleInputChange} /> 설사 </label></fieldset><br /><fieldset><legend>🐕 컨디션</legend><label><inputtype="radio"name="mood"value="좋음"onChange={handleInputChange} /> 좋음 </label><label><inputtype="radio"name="mood"value="보통"onChange={handleInputChange} /> 보통 </label><label><inputtype="radio"name="mood"value="피곤"onChange={handleInputChange} /> 피곤 </label><label><inputtype="radio"name="mood"value="나쁨"onChange={handleInputChange} /> 나쁨 </label><br /></fieldset><br /><fieldset><legend>💜 오늘 하루는</legend><textareaid="daily"name="daily"value={newNote.daily}onChange={handleInputChange}></textarea></fieldset></div><buttontype="submit">작성하기</button></form></>
);
};
NoteList.js
로그인 검증: 컴포넌트가 마운트될 때 accessToken을 확인하여 로그인 여부를 검증하고, 로그인되지 않았으면 로그인 페이지로 이동 알림장 목록 가져오기: 로그인된 경우 알림장 목록을 서버에서 가져와 상태 변수에 저장. 알림장은 최신 순으로 정렬됨 페이지네이션: 현재 페이지에 표시할 알림장을 계산하고, 페이지 번호를 클릭할 때 현재 페이지를 업데이트 알림장 상세보기: 알림장을 클릭하면 해당 알림장의 상세보기 페이지로 이동 알림장 등록: 알림장 등록 버튼을 클릭하면 알림장 등록 페이지로 이동
로그인 검증 및 알림장 목록 가져오기
useEffect(() => {
// 토큰으로 로그인 검증if (!cookies.accessToken) {
alert('로그인 해주세요!');
navigate('/login'); // 로그인 페이지로 이동return;
}
// 로그인 O
axios.get('/api/v1/auth/y/note') // 알림장 목록을 가져오는 GET 요청을 보냄
.then(response => {
const sortedNotes = response.data.sort((a, b) => b.id - a.id); // 알림장을 ID 역순으로 정렬setNotes(sortedNotes); // 정렬된 알림장을 상태 변수에 저장
})
.catch(error => {
console.error('Error: ', error);
});
}, [cookies.accessToken, axios, navigate]); // 의존성 배열에 cookies.accessToken, axios, navigate를 포함
페이지네이션
const indexOfLastNote = currentPage * notesPerPage; // 현재 페이지의 마지막 알림장의 인덱스를 계산const indexOfFirstNote = indexOfLastNote - notesPerPage; // 현재 페이지의 첫 번째 알림장의 인덱스를 계산const currentNotes = notes.slice(indexOfFirstNote, indexOfLastNote); // 현재 페이지에 해당하는 알림장을 잘라냄constpaginate = (pageNumber) => setCurrentPage(pageNumber); // 페이지 번호를 설정하는 함수