Location.jsx 컴포넌트 개발하기

KHW·2021년 12월 8일
0

프레임워크

목록 보기
40/43
post-custom-banner

목표

야놀자와 비슷하게 시도를 치면 구체적인 지역이 나와 그것을 클릭하면
해당 시도별 지역의 내용을 얻을 수 있게 만드는 것

첫번째 시도

기존에 MUI를 이용해서 해당 모달과 버튼등을 쉽게 사용하려고 시도 했지만
MUI가 갖고있는 특성이 서로 충돌이 나면서 내가 의도한 결과의
레이아웃이 안짜졌다. 오히려 더 복잡해졌다.

두번째 시도

그냥 처음부터 만들자는 생각에 MUI를 사용하지 않고 직접 modal창과 전반적인 틀을 다시 짰다.
Grid를 통해 짜고 Grid Container는 display flex로 중앙에 위치시켜
모바일 화면에서도 중앙에 배치되도록 만들었다.

참고한 modal 창

<html lang="ko"><head>
    <meta charset="UTF-8">
    <title>Modal</title>

    <style>
      .modal {
        position: absolute;
        top: 0;
        left: 0;

        width: 100%;
        height: 100%;

        display: none;

        background-color: rgba(0, 0, 0, 0.4);
      }

      .modal.show {
        display: block;
      }

      .modal_body {
        position: absolute;
        top: 50%;
        left: 50%;

        width: 400px;
        height: 600px;

        padding: 40px;

        text-align: center;

        background-color: rgb(255, 255, 255);
        border-radius: 10px;
        box-shadow: 0 2px 3px 0 rgba(34, 36, 38, 0.15);

        transform: translateX(-50%) translateY(-50%);
      }
    </style>
  </head>
  <body cz-shortcut-listen="true" style="overflow: auto;">
    <div class="modal">
      <div class="modal_body">Modal</div>
    </div>
    <button class="btn-open-popup">Modal 띄우기</button>
    </div>
    <script>
      const body = document.querySelector('body');
      const modal = document.querySelector('.modal');
      const btnOpenPopup = document.querySelector('.btn-open-popup');

      btnOpenPopup.addEventListener('click', () => {
        modal.classList.toggle('show');

        if (modal.classList.contains('show')) {
          body.style.overflow = 'hidden';
        }
      });

      modal.addEventListener('click', (event) => {
        if (event.target === modal) {
          modal.classList.toggle('show');

          if (!modal.classList.contains('show')) {
            body.style.overflow = 'auto';
          }
        }
      });
    </script>
  

</body></html>

해당블로그를 참고하여 이를 응용하기로 했다.

React로 작성한 Location.jsx

import styled from "styled-components";
import { useState } from "react";

const CustomModal = styled.div`
  position: absolute;
  top: 0;
  left: 0;

  width: 100%;
  height: 100%;

  display: block;

  background-color: rgba(0, 0, 0, 0.4);
`;

const CustomModalBody = styled.div`
  position: absolute;
  top: 50%;
  left: 50%;

  width: 300px;

  padding: 40px;

  text-align: center;

  background-color: rgb(255, 255, 255);
  border-radius: 10px;
  box-shadow: 0 2px 3px 0 rgba(34, 36, 38, 0.15);

  transform: translateX(-50%) translateY(-50%);
`;

const ModalGridArea = styled.div`
  display: grid;
  grid-template-rows: repeat(3, 1fr);
  grid-template-columns: repeat(3, 1fr);
  justify-content: center;
  align-items: center;
  justify-items: center;
  align-items: center;
`;

const ModalGridAreaItem = styled.button`
  background-color: #fd9f28;
  width: 100px;
  height: 50px;
  border-radius: 10px;
  text-align: center;
  margin: 10px;
  display: table-cell;
  color: white;
`;

const GridGroup = styled.div`
  display: flex;
  justify-content: center;
  align-items: center;
`;

const GridArea = styled.div`
  width: 300px;
  border: 4px solid;
  display: grid;
  grid-template-rows: repeat(2, 1fr);
  grid-template-columns: repeat(2, 1fr);
  justify-content: center;
  align-items: center;
  justify-items: center;
  align-items: center;
`;

const GridAreaItem = styled.button`
  background-color: #fd9f28;
  width: 100px;
  height: 50px;
  border-radius: 10px;
  text-align: center;
  margin: 10px;
  display: table-cell;
  color: white;
`;

const Location = () => {
  const areaArr = Object.keys(area);
  const [isModalOpen, setIsModalOpen] = useState(false);
  const [mainArea, setMainArea] = useState([]);
  const [subArea, setSubArea] = useState([]);

  const clickHandler = (mainArea) => {
    setSubArea(area[mainArea]);
    setMainArea(mainArea);
    setIsModalOpen(true);
  };

  const clickModalHandler = () => {
    setIsModalOpen(false);
  };

  const clickModalBodyHandler = (subArea) => {
    setIsModalOpen(false);

    // modal 클릭시 필요한 ~시 ~구 확인 가능
    console.log(`${mainArea} ${subArea}`); //서울시 강남구
  };

  if (isModalOpen)
    return (
      <CustomModal
        className="modal"
        onClick={(e) => {
          clickModalHandler(e);
        }}
      >
        <CustomModalBody>
          <ModalGridArea>
            {subArea.map((t, i) => (
              <ModalGridAreaItem
                key={i}
                onClick={(e) => {
                  clickModalBodyHandler(e.target.textContent);
                }}
              >
                <p style={{ verticalAlign: "center" }}>{t}</p>
              </ModalGridAreaItem>
            ))}
          </ModalGridArea>
        </CustomModalBody>
      </CustomModal>
    );

  return (
    <>
      <GridGroup>
        <GridArea>
          {areaArr.map((t, i) => (
            <GridAreaItem
              key={i}
              onClick={(e) => {
                clickHandler(e.target.textContent);
              }}
            >
              <p style={{ verticalAlign: "center" }}>{t}</p>
            </GridAreaItem>
          ))}
        </GridArea>
      </GridGroup>
    </>
  );
};

export default Location;

isModalOpen을 통해 모달창의 진행 여부를 판단하고
모달의 item을 누르거나 모달 밖을 클릭하면 모달이 사라지는 기능을 구현했다.

주소 관련데이터

const area = {
  서울특별시: [
    "강남구",
    "강동구",
    "강북구",
    "강서구",
    "관악구",
    "광진구",
    "구로구",
    "금천구",
    "노원구",
    "도봉구",
    "동대문구",
    "동작구",
    "마포구",
    "서대문구",
    "서초구",
    "성동구",
    "성북구",
    "송파구",
    "양천구",
    "영등포구",
    "용산구",
    "은평구",
    "종로구",
    "중구",
    "중랑구",
  ],
  인천광역시: [
    "계양구",
    "남구",
    "남동구",
    "동구",
    "부평구",
    "서구",
    "연수구",
    "중구",
    "강화군",
    "옹진군",
  ],
  대전광역시: ["대덕구", "동구", "서구", "유성구", "중구"],
  ....
  
    ],
  제주도: ["서귀포시", "제주시", "남제주군", "북제주군"],
};

해당 데이터는 예시로 이와같다.

결과 확인하기

1. 첫화면

시도별 선택지를 중앙정렬된 상태로 확인할 수 있다.

2. 시/도 클릭시

가장 도시가 많은 경기도를 클릭시 이와같은 레이아웃이 나타난다.

3. 도시 클릭시

해당 모달창이 빠져나오고 콘솔로 그때의 시/도와 도시를 확인할 수 있다.

text 세로 정렬

text의 세로 정렬을 시도하려하니 처음에 제대로 작동하지 않았다.
그 이유는 vertical-aligninline에서만 동작하기때문에
div로 묶인 text들이 제대로 동작하지않았다.
이를 p태그로 묶고 처리하니 정상 작동되었다.

느낀점

MUI와 같은 라이브러리도 좋지만 기본적으로 스스로 만들줄 아는 습관을 들여야 좀 더 복잡한 컴포넌트를 스스로 처리할 수 있는 것을 느꼈다.

코드 링크

github branch

profile
나의 하루를 가능한 기억하고 즐기고 후회하지말자
post-custom-banner

0개의 댓글