[탐나예] 메인 페이지

AnSuebin·2022년 10월 6일
0

[탐나예]프로젝트

목록 보기
2/4

탐나예 메인페이지 기능 및 코드 소개
1. 메인 페이지 간단 기능 소개
2. 코드 소개

00. 들어가기 전에

  • 메인페이지는 처음 프로젝트를 진행하기 위해 가장 먼저 구성한 기능이며, 최대한 한눈에 예약을 구분할 수 있고, 손쉽게 사용할 수 있도록 노력하였습니다.
  • React를 기반으로 제작하였습니다.

1. 메인 페이지 간단 기능 소개

  • 기수에 따른 2, 3층 페이지 구분 구현
  • 회의실 식별 용이를 위한 지도 구현
  • 지도 내에서 버튼 활용
  • 예약 가능 여부에 따른 마감 표기

02. 코드 소개

2-1. 메인 페이지에서 유저별 층수 데이터 전송

  • 메인은 컴포넌트는 기본적으로 2층을 보여주는 페이지, 3층을 보여주는 페이지, 2,3,4층 모두를 보여주는 페이지로 구성 되어있습니다.
  • MainTemplate이라는 페이지를 통해 유저 데이터를 받아, 유저에 따라 필요한 데이터 층수를 구분하여, 층수별 컴포넌트로 props를 활용해 할당해주었습니다.

2-1-1) useState로 각 데이터 선언

  //층수 API 정보 가져오기
  const [floor, setfloor] = useState('');
  const [bookingData, setBookingData] = useState([]);
  const [roomData, setRoomData] = useState([]);

  // 2층 정보
  const [SecondMeetingRoominfo, setSecondMeetingRoominfo] = useState([]);
  const [SecondNaboxinfo, setSecondNaboxinfo] = useState([]);
  const [SinyangID, setSinYangID] = useState('');
  const [SinyangName, setSinYangName] = useState('');

  // 3층 정보
  const [ThirdMeetingStudioinfo, setThirdMeetingStudioinfo] = useState([]);
  const [ThirdNaboxinfo, setThirdNaboxinfo] = useState([]);

  // 4층 정보
  const [FourthFloorinfo, setFourthFloorinfo] = useState([]);

2-1-2) fetch, get을 통해 백에서 데이터를 층수에 맞게 구분 한 후, 필터로 세부 룸에 대한 정보로 나누기

const myUrl = useUrl();
  const url = `http://${myUrl}/api/booking/main`;
  const navigate = useNavigate();
  useEffect(() => {
    fetchGet(url, navigate).then((data) => {
      setfloor(data.floor);
      setBookingData(data.BookingData);
      setRoomData(data.RoomData);

      // 2층 일 때 정보
      if (data.floor === 2) {
        setSecondMeetingRoominfo(
          data.RoomData.filter(
            (rooms) => rooms.roomType === 'meeting' && rooms.roomName !== '신양'
          )
        );
        setSecondNaboxinfo(
          data.RoomData.filter((rooms) => rooms.roomType === 'nabox')
        );
        setSinYangID(
          data.RoomData.filter((rooms) => rooms.roomType === 'official')[0]
            .roomId
        );
        setSinYangName(
          data.RoomData.filter((rooms) => rooms.roomType === 'official')[0]
            .roomName
        );
      }

      // 3층 일 때 정보
      else if (data.floor === 3) {
        setThirdMeetingStudioinfo(
          data.RoomData.filter(
            (rooms) =>
              rooms.roomType === 'meeting' || rooms.roomType === 'studio'
          )
        );
        setThirdNaboxinfo(
          data.RoomData.filter((rooms) => rooms.roomType === 'nabox')
        );
      }

      // 0층일 때 즉 모두 보여줘야 할 때 정보
      else {
        // 2층 정보
        setSecondMeetingRoominfo(
          data.RoomData.filter(
            (rooms) =>
              rooms.floor === 2 &&
              rooms.roomType === 'meeting' &&
              rooms.roomType !== 'official'
          )
        );
        setSecondNaboxinfo(
          data.RoomData.filter(
            (rooms) => rooms.floor === 2 && rooms.roomType === 'nabox'
          )
        );
        setSinYangID(
          data.RoomData.filter((rooms) => rooms.roomType === 'official')[0]
            .roomId
        );
        setSinYangName(
          data.RoomData.filter((rooms) => rooms.roomType === 'official')[0]
            .roomName
        );
        // 3층 정보
        setThirdMeetingStudioinfo(
          data.RoomData.filter(
            (rooms) =>
              (rooms.floor === 3 && rooms.roomType === 'meeting') ||
              rooms.roomType === 'studio'
          )
        );
        setThirdNaboxinfo(
          data.RoomData.filter(
            (rooms) => rooms.floor === 3 && rooms.roomType === 'nabox'
          )
        );
        // 4층 정보
        setFourthFloorinfo(data.RoomData.filter((rooms) => rooms.floor === 4));
      }
    });
  }, [url, myUrl]);

2-1-3) props를 통해 층수 별 component에 정보 할당하기

{/* classes 활용 */}
        {floor === 0 && [
          <SecondFloor
            key="2"
            className={styles.secondFloor}
            ablebtn={ablebtn}
            BookingConfirm={BookingConfirm}
            SecondMeetingRoominfo={SecondMeetingRoominfo}
            SecondNaboxinfo={SecondNaboxinfo}
            bookingData={bookingData}
            roomData={roomData}
            SinyangID={SinyangID}
            SinyangName={SinyangName}
            floor={floor}
          />,
          <ThirdFloor
            key="3"
            className={styles.thirdFloor}
            ablebtn={ablebtn}
            BookingConfirm={BookingConfirm}
            ThirdMeetingStudioinfo={ThirdMeetingStudioinfo}
            ThirdNaboxinfo={ThirdNaboxinfo}
            bookingData={bookingData}
            roomData={roomData}
          />,
          <FourthFloor
            key="4"
            className={styles.fourthFloor}
            ablebtn={ablebtn}
            BookingConfirm={BookingConfirm}
            bookingData={bookingData}
            roomData={roomData}
            FourthFloorinfo={FourthFloorinfo}
          />,
        ]}
        {floor === 2 && (
          <SecondFloor
            key="2"
            className={styles.secondFloor}
            ablebtn={ablebtn}
            BookingConfirm={BookingConfirm}
            SecondMeetingRoominfo={SecondMeetingRoominfo}
            SecondNaboxinfo={SecondNaboxinfo}
            bookingData={bookingData}
            roomData={roomData}
            SinyangID={SinyangID}
            SinyangName={SinyangName}
            floor={floor}
          />
        )}
        {floor === 3 && (
          <ThirdFloor
            key="3"
            className={styles.thirdFloor}
            ablebtn={ablebtn}
            BookingConfirm={BookingConfirm}
            ThirdMeetingStudioinfo={ThirdMeetingStudioinfo}
            ThirdNaboxinfo={ThirdNaboxinfo}
            bookingData={bookingData}
            roomData={roomData}
          />
        )}

2-2. 층수별로 받은 데이터 활용하여, 맵, 회의실 버튼, 나박스 버튼 component로 각각 할당

  • 메인페이지의 구성을 3개로 쪼개서 component를 구현하였습니다.

2-2-1) 3층 컴포넌트 예시

ThirdFloorMeetingRoom.js';
import ThirdFloorNaRoom from './ThirdFloorNaRoom.js';
import styles from './ThirdFloor.module.css';

const ThirdFloor = ({
  ablebtn,
  BookingConfirm,
  ThirdMeetingStudioinfo,
  ThirdNaboxinfo,
  bookingData,
  roomData,
}) => {
  return (
    <div className={styles.MainThirdFloor}>
      <div className={styles.ThirdFloor}>
        <h2>3 Floor</h2>
        <div className={styles.ThirdFloorcontainer}>
          <ThirdFloorMap
            className={styles.ThirdFloorMap}
            ablebtn={ablebtn}
            BookingConfirm={BookingConfirm}
            ThirdMeetingStudioinfo={ThirdMeetingStudioinfo}
            ThirdNaboxinfo={ThirdNaboxinfo}
            bookingData={bookingData}
            roomData={roomData}
          />
          <div className={styles.RoomContainer}>
            <ThirdFloorMeetingRoom
              className={styles.meetingRoom}
              ablebtn={ablebtn}
              BookingConfirm={BookingConfirm}
              ThirdMeetingStudioinfo={ThirdMeetingStudioinfo}
              bookingData={bookingData}
              roomData={roomData}
            />
            <ThirdFloorNaRoom
              className={styles.naRoom}
              ablebtn={ablebtn}
              BookingConfirm={BookingConfirm}
              ThirdNaboxinfo={ThirdNaboxinfo}
              bookingData={bookingData}
              roomData={roomData}
            />
          </div>
        </div>
      </div>
    </div>
  );
};

export default ThirdFloor;

2-3. 맵 화면 구현

  • 알고리즘적으로, 화면 구성적으로 가장 고민을 많이했던 파트입니다.
  • 예약시간, 지난 시간, 예약 상황을 모두 고려한 알고리즘을 구성하였습니다. 이를 통해 회의실이 모두 찼는지 예약 전에 확인 할 수 있도록 표현하였습니다.
  • 회의실과 나박스 모두 <div>태그를 사용하여, 버튼으로 작동하도록 구현하였습니다.

2-3-1) roomFull 함수 설정

  • 회의실이 꽉찼는지 확인할 수 있는 알고리즘을 제작하였습니다.
    1) 현재 시간 받아오기
    2) 오후 9시 즉, 21시와 현재 시를 빼서, 예약가능 시간 중 남은 시간 구하기
  const Now = new Date();
  const NowHour = Now.getHours();
  const RemainTime = 21 - NowHour;

3) 현재 시간이 껴있지 않은 지금보다 뒤의 예약 시간 계산하는 알고리즘

  • 현재 시간이 껴있지 않은 이후 예약 현황의 예약 시작과 끝 시간 추출
const middleRoomState = bookingData.filter(
      (room) =>
        room.roomId === roomid &&
        Number(TimeToString(room.startTime)) < Number(NowHour) &&
        Number(TimeToString(room.endTime)) > Number(NowHour)
    );
  • 추출된 끝 시간과 시작 시간 뺀 리스트 제작
const roomBookingState = roomState.map(
      (room) =>
        TimeToString(room.endTime) - Number(TimeToString(room.startTime))
    );
  • 추출된 시간의 합
const sum = roomBookingState.reduce(function add(sum, currValue) {
      return sum + currValue;
    }, 0);

4) 현재 시간이 껴있는 예약 현황 시간 추출 (처음에는 고려하지 못한 부분)

  • 현재 시간이 껴있는 예약의 예약 시작과 끝 시간 추출
const middleRoomState = bookingData.filter(
      (room) =>
        room.roomId === roomid &&
        Number(TimeToString(room.startTime)) < Number(NowHour) &&
        Number(TimeToString(room.endTime)) > Number(NowHour)
    );
  • 추출된 끝 시간과 시작 시간 뺀 리스트 제작
 const middleBookingState = middleRoomState.map(
      (room) => Number(TimeToString(room.endTime)) - Number(NowHour)
    );
  • 추출된 시간의 합
const middleSum = middleBookingState.reduce(function add(sum, currValue) {
      return sum + currValue;
    }, 0);

5) 전체 추출 시간들의 합과 남은 시간 불린으로 결과값 제출

  return sum + middleSum < RemainTime;

2-3-2) 상황 별 회의실 기능 분리

  • 우선적으로 회의실과 나박스 분리
  • 위의 예약실이 꽉 찼는지 알아보는 함수를 통해, id 혹은 class를 다르게 줘 형태 변화
{ThirdMeetingStudioinfo.map((rooms) => (
          <Link
            to={`/booking/${rooms.roomId}`}
            key={rooms.roomId}
            className={styles[rooms.roomName]}
            id={
              notroomFull(rooms.roomId) && ablebtn
                ? [styles.MeetingRoom]
                : [styles.full]
            }
            onClick={BookingConfirm}
          >
            <div>
              {notroomFull(rooms.roomId) && ablebtn
                ? rooms.roomName
                : `${rooms.roomName}\n마감`}
            </div>
          </Link>
        ))}

2-3-3) useTimeAlert 훅 제작, 시간에 따라 예약 가능여부 맵에 표현

  • 예약 시간을 전체적으로 한번에 관리할 수 있도록 커스텀 훅을 제작하여 사용하였습니다.

  • 버튼 활성화 관련 상태값 useState로 지정

 const [ablebtn, setAblebtn] = useState(true)
  • 현재 시간 가져오기
  const Now = new Date() 
  const NowHour = Now.getHours()
  const NowMins = Now.getMinutes()
  • 시간 형식 변경해주기 : 예) '0811'
function pluszero(times) {
    let time = times.toString() //시간을 숫자에서 문자로 변환
    if (time.length < 2) {
      time = '0' + time //숫자 앞에 0을 붙여줌
      return time
    } else {
      return time
    }
  }
  const nowHour = pluszero(NowHour)
  const nowMins = pluszero(NowMins)
  const nowTime = nowHour + nowMins
  • 시작 시간, 끝 시간 설정해주기
  const startTime = '0830'
  const endTime = '2100'
  • 지금이 시작 시간보다 이르거나 끝 시간이 시작 시간보다 늦을 경우, false로 하는 if 구문 설정
 useEffect(() => {
    if (startTime > nowTime || endTime < nowTime) {
      setAblebtn(false)
    } else {
      setAblebtn(true)
    }
  }, [])
  • 클릭 시 false라면, alert창 설정
const BookingConfirm = () => {
    if (startTime > nowTime || endTime < nowTime) {
      alert(
        '예약할 수 없는 시간입니다!\n오전08:30부터 오후21:00까지 예약이 가능합니다.'
      )
    }
  }
  • ablebtnBookingConfirm 리스트로 내보내기
  return [ablebtn, BookingConfirm]

2-4. 버튼도 모두 roomFull 함수 적용

{ThirdMeetingStudioinfo.map((room) => (
          <Link to={`/booking/${room.roomId}`} key={room.roomId}>
            <button
              className={
                notroomFull(room.roomId) && ablebtn
                  ? [styles.notfull]
                  : [styles.full]
              }
              onClick={BookingConfirm}
            >
              {room.roomName}
            </button>
          </Link>
        ))}
profile
고객에게 명료한 의미를 전달하고, 명료한 코드를 통해 생산성 향상에 기여하고자 노력합니다.

0개의 댓글