원티드 프리온보딩에서 진행한 기업과제 토이프로젝트 입니다.
1) 병원 예약 가능 목록
예약 가능한 목록을 확인할 수 있습니다.
병원의 예약 가능 일시를 확인할 수 있습니다.
2) 예약 등록
예약자, 이름, 예약 시간, 예약 종류 (진료, 검진, … 등) 의 데이터를 활용하여 병원 진료를 예약합니다.
중복 예약은 불가합니다.
3) 전체 예약 목록
예약 번호 또는 예약자로 예약 목록 조회가 가능해야 합니다.
4) 예약 변경
신청한 예약 번호를 통해 예약을 변경할 수 있습니다. (환자 이름, 예약 시간, 예약 종류 변경 가능)
선택
1) 예약 등록에 관한 선택 구현 사항
예약을 하면 안내 메일 또는 알림이 가능하도록 구현해주세요. (가산점)
노쇼한 예약자에 한해서 예약이 불가하도록 구현해주세요. (가산점)
탭을 state에 담고 하나의 컴포넌트로 만들어진 콘텐츠에 각 선택된 탭에 따라 보여지는 항목이 달라진다.
function Appointment({
setAppointmentModal, //모달창 닫기에 해당되는 props
appointmentList, //예약list props
setAppointmentList, //새로담을 예약자 list
}) {
const [tab, setTab] = useState(0);
...
return (
<>
...
<ul>
<li onClick={() => { setTab(0); }}
> 진료예약 </li>
<li onClick={() => { setTab(1); }}
> 검진예약 </li>
</ul>
...
</>
);
}
export default Appointment;
function AddWrite({ tab }) {
return (
<ul>
<li>
// onChange이벤트로 e.target.value값을 배열로 받는다
</li>
{tab === 0 ?
<li>
// 내과, 피부과 예약 콘텐츠
</li>
: <li>
// 건강검진, 종합검진 예약 콘텐츠
</li>
)}
</ul>
...
);
}
시현 영상
fetch로 호출한 데이터 배열을 state에 담고 onChange이벤트를 이용하여 새로운 예약정보를 담는 방식으로 예약등록 기능을 구현하였다.
function AddAppointment({ tab, onSendAppointment, lastId }) {
const [formData, setFormData] = useState(clear);
const clear = {
userName: "",
medical: "",
phone: "",
aptNotes: "",
aptDate: "",
};
function formDataPublish() {
const formDataInfo = {
id: lastId + 1,
userName: formData.userName,
medical: formData.medical,
phone: formData.phone,
aptNotes: formData.aptNotes,
aptDate: formData.aptDate + " " + formData.aptTime,
};
onSendAppointment(formDataInfo);
setFormData(clear);
alert("예약이 완료되었습니다.");
}
return (
<AddWrap>
<AddWrite
tab={tab}
formData={formData}
formDataPublish={formDataPublish}
setFormData={setFormData}
/>
</AddWrap>
);
}
export default AddAppointment;
onSendAppointment={
(myAppointment) => setAppointmentList([...appointmentList, myAppointment]) }
function AddWrite({ formData, setFormData, formDataPublish, tab }) {
return (
<>
<ul>
<li>
<label htmlFor="userName"
> 예약자명 </label>
<input type="text" id="userName"
onChange={(e) => {
setFormData({ ...formData, userName: e.target.value });
}} />
</li>
<li>
// 휴대폰 번호
</li>
<li>
// 예약 일자
</li>
<li>
// 예약시간
</li>
{tab === 0 ? (
<li>
<p>
<label htmlFor="medical"
> 내과 </label>
<input type="checkbox" id="medical"
onClick={() => {
setFormData({ ...formData, medical: "내과" });
}} />
<label htmlFor="skincare"
> 피부과 </label>
<input type="checkbox" id="skincare"
onClick={() => {
setFormData({ ...formData, medical: "피부과" });
}} />
<span>※ 진료과를 선택해주세요.</span>
</p>
<p>
<textarea id="userDes" cols="30" rows="10" placeholder="진료 내용"
onChange={(e) => {
setFormData({ ...formData, aptNotes: e.target.value });
}} />
</p>
</li>
) : (
<li>
<p>
<label htmlFor="default"
> 건강검진 </label>
<input type="checkbox" id="default"
onClick={() => {
setFormData({ ...formData, medical: "건강검진" });
}} />
<label htmlFor="full"
> 종합검진 </label>
<input type="checkbox" id="full"
onClick={() => {
setFormData({ ...formData, medical: "종합검진" });
}} />
<span>※ 진료과를 선택해주세요.</span>
</p>
</li>
)}
</ul>
<p>
<input type="submit" value="예약하기"
onClick={formDataPublish}
/>
</p>
</>
);
}
🚧 예약등록후 입력값이 그대로 남아 있다. 이 문제는 추후 리팩토링 할 부분.
e.target.value === data.key && return
조회할수 있는 조건이 뭐가 있을까 고민하다 datad의 키값으로 조회하는 방식으로 구현하였다.
조회를 할때 예약자를 나눌수 있는 방법은 크게 두가지로 나눈다. 이름으로 조회 & 휴대폰 번호로 조회하여 로그인 기능이 없으므로 예약자의 휴대폰 번호로 예약 취소가 될수 있게 구현했다.
먼저 조회부분의 코드이다.
function Search({ setCheck, appointmentList, setAppointmentList }) {
const [tab, setTab] = useState(0);
const closeModal = () => {
setCheck(false);
};
return (
<>
<div>
<h3>
<BsCalendarCheck className="modalIcon" />
예약 확인
</h3>
<Category tab={tab} setTab={setTab} />
{tab === 0 ? (
<SearchAppointment appointmentList={appointmentList} />
) : null}
{tab === 1 ? (
<CancleAppointment
appointmentList={appointmentList}
setAppointmentList={setAppointmentList}
/>
) : null}
<input
type="submit"
value="닫기"
onClick={closeModal}
className="modalCloseBtn"
/>
</div>
</>
);
}
export default Search;
function SearchAppointment({ appointmentList }) {
const [userPhone, setUserPhone] = useState("");
const [userData, setUserData] = useState(false);
const phoneInputHandler = (e) => {
setUserPhone(e.target.value);
};
return (
<>
<h4>예약시 등록하신 휴대폰 번호를 입력해주세요.</h4>
<div>
<input type="text" placeholder="search"
onChange={phoneInputHandler}
/>
<input type="submit" value="검색"
onClick={() => {
setUserData(true);
}} />
</div>
//list
<div>
{appointmentList.map((appointment) => {
return (
<div key={appointment.id}>
{appointment.phone === userPhone && userData === true &&
// list 콘텐츠
)}
</div>
);
})}
</div>
</>
);
}
export default SearchAppointment;
시현 영상
로그인 기능을 따로 구현하지 않았기 때문에 예약자의 휴대폰 번호로 취소가 가능하게 구현하였다.
function CancleModal({ deleteAppointment, appointment, setCancleAppoint }) {
const [phoneInput, setPhoneInput] = useState("");
const delBtn = () => {
if (appointment.phone === phoneInput) {
deleteAppointment(appointment.id);
setCancleAppoint(false);
alert("예약취소가 완료되었습니다.");
} else {
alert("정보를 확인하고 취소해주세요.");
setCancleAppoint(false);
}
};
const enterKey = (e) => {
if (e.key === "Enter") {
setPhoneInput(e.target.value);
setCancleAppoint(false);
delBtn();
}
};
return (
<StyledModal>
<div>
<h4>예약취소</h4>
<p>예약시 입력하신 휴대폰 번호를 입력해주세요.</p>
<input
type="tel"
className="phoneInput"
onChange={(e) => {
setPhoneInput(e.target.value);
}}
onKeyPress={enterKey}
placeholder="' - ' 없이 입력해주세요."
/>
<button onClick={delBtn} className="deleteBtn">
예약취소
</button>
</div>
</StyledModal>
);
}
deleteAppointment={(appointmentId) =>
setAppointmentList(
appointmentList.filter((appointment) =>
appointment.id !==appointmentId ))}
시현 영상
혼자 만들어보는 예약시스템이라 부족한게 너무 많다. 추후 하나하나 리팩토링해야겠다.
ref를 이용해서 value값을 받을 수도 있지만 이전에 프로젝트기간에 ref로 value값이 담기지 않아 곤욕일 치룬경험이 있어서 e.targer.value를 쓴다. 그래도.. ref가 편하긴 한데... 여러개의 입력값을 받을때 e.target.value를 handler변수에 담아서 사용하는것도 좋다. 깨끗한 코드를 짜는 개발자가 되는 그날까지!!