LULULAB 예약시스템

Miog Yang·2022년 11월 3일
0
post-thumbnail

Pre onboarding

원티드 프리온보딩에서 진행한 기업과제 토이프로젝트 입니다.

1. 구현 사항

1) 병원 예약 가능 목록

예약 가능한 목록을 확인할 수 있습니다.
병원의 예약 가능 일시를 확인할 수 있습니다.

2) 예약 등록

예약자, 이름, 예약 시간, 예약 종류 (진료, 검진, … 등) 의 데이터를 활용하여 병원 진료를 예약합니다.
중복 예약은 불가합니다.

3) 전체 예약 목록

예약 번호 또는 예약자로 예약 목록 조회가 가능해야 합니다.

4) 예약 변경

신청한 예약 번호를 통해 예약을 변경할 수 있습니다. (환자 이름, 예약 시간, 예약 종류 변경 가능)
선택

1) 예약 등록에 관한 선택 구현 사항

예약을 하면 안내 메일 또는 알림이 가능하도록 구현해주세요. (가산점)

노쇼한 예약자에 한해서 예약이 불가하도록 구현해주세요. (가산점)




2. 구현한 기능

  • 예약 등록 : 예약자, 이름, 예약 시간, 예약 종류 (진료, 검진, … 등) 의 데이터를 활용하여 병원 진료를 예약합니다.
  • 예약 조회 : 예약자 이름 또는 예약시 등록한 휴대폰 번호로 조회를 합니다.
  • 예약 취소 : 로그인 대신 휴대폰 번호로 예약자를 확인하여 취소를 합니다.



3. 프로젝트 구조

  • index js : fetch를 사용하여 데이터를 호출하여 이를 예약하기와 예약확인 각각 넘겨준다.

4. 기능 살펴보기

1) state를 이용한 탭메뉴

탭을 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;
  • 예약하기 Appointment.js에 state로 탭을 관리하여 각 index를 지정해주었다.

function AddWrite({ tab }) {
  return (
      <ul>
        <li>
    			// onChange이벤트로 e.target.value값을 배열로 받는다
        </li>
    
        {tab === 0 ? 
    		<li>
    				// 내과, 피부과 예약 콘텐츠
          </li> 
          :  <li>
    				// 건강검진, 종합검진 예약 콘텐츠
          </li>
        )}
      </ul>

			...
            
  );
}
  • tab을 props로 받아 삼항연산자를 적용하여 각 탭에 따른 콘텐츠가 보여지게 하였다.

시현 영상

2) onChange를 이용한 예약등록

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;
  • clear : 빈값을 담은 배열을 준비한다.
  • 데이터를 담을 폼을 state로 관리하여 준비한 clear를 초기값으로 담는다.
  • 데이터에 담긴 키값으로 데이터 body에 들어갈 value를 지정해준다.
  • Appointment js에서 넘겨준 새로운 배열을 담는 onSendAppointment에 body를 담는다.
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>
    </>
  );
}
  • AddWrite js에 새로운 데이터 body를 만든 폼을 가져오고, 각 input에 state변경값으로 담아 스프레드 연산자를 이용하여 새로운 데이터객체를 담는다.
    예약하기 버튼에 onClick이벤트로 데이터추가를 한다.

🚧 예약등록후 입력값이 그대로 남아 있다. 이 문제는 추후 리팩토링 할 부분.

3) 삼항연산자를 이용한 예약 조회

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;
  • Search js에는 두가지의 탭메뉴에 따라 컴포넌트를 나누었다.
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;
  • input에 입력하는 value값과 props로 받아온 키값이 같을 경우 반환하는 방식으로 구현하였다.
    여기서 검색을 클릭할때 리스트가 보이도록 state지정후 불리언값으로 구현하였다.

시현 영상

4) 휴대폰 번호로 예약 취소하기

로그인 기능을 따로 구현하지 않았기 때문에 예약자의 휴대폰 번호로 취소가 가능하게 구현하였다.

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 ))}
  • 삭제 버튼을 누르면 deleteAppointment이 실행되는데, api로 서버에 등록되고 삭제되는 것이 아니라서 리스트만 보이지 않게 구현하였다.

시현 영상

✏️ 마치며

혼자 만들어보는 예약시스템이라 부족한게 너무 많다. 추후 하나하나 리팩토링해야겠다.
ref를 이용해서 value값을 받을 수도 있지만 이전에 프로젝트기간에 ref로 value값이 담기지 않아 곤욕일 치룬경험이 있어서 e.targer.value를 쓴다. 그래도.. ref가 편하긴 한데... 여러개의 입력값을 받을때 e.target.value를 handler변수에 담아서 사용하는것도 좋다. 깨끗한 코드를 짜는 개발자가 되는 그날까지!!

profile
주니어 개발사전 & 프론트엔드 도전기

0개의 댓글