[TIL] 23.05.15

Minkyu Shin·2023년 5월 15일
0

TIL

목록 보기
29/44
post-thumbnail

오늘의 나는 무엇을 잘했을까?

시간을 잘 맞추어 일어났고, 제 시간에 활동에 참여했다. 또, 새로운 라이브러리를 배우며 나름의 즐거움도 느꼈던 것 같다. 새로운 걸 배우는건 꽤 짜릿하다(물론 이해가 잘 되면).

오늘의 나는 무엇을 배웠을까?

지난 수요일의 프로젝트 kick-off를 시작으로 '보유 가상화폐 가치 계산' 서비스를 제공하는 웹 애플리케이션 제작이 시작되었다. 수~금까지는 회의를 통해 컴포넌트를 어떻게 나눌지 어느 컴포넌트에 어떤 기능이 들어가야 할지를 개략적으로 정하고 토요일에 맡을 컴포넌트를 정한 뒤 구현을 시작하였다.

우리 팀 웹 앱의 구조는 다음과 같다.

App
├─ GNB // 네비게이션 바 컴포넌트
│  ├─ Logo // 로고 및 서비스 이름 컴포넌트
│  ├─ div
│  │  ├─ Button // inputBoard 입력 값 및 계산된 데이터 초기화
│  │  ├─ Select // 화폐 단위 변경
│  │  │  ├─ Option
│  │  │  └─ Option
│  │  └─ Button // 검색 기록 모달 창 띄워주기
│  └─ HistoryModal // 검색 기록 모달 컴포넌트
├─ InputBoard // 사용자의 입력을 받는 컴포넌트
├─ MainBoard // 차트 및 계산 결과를 보여주는 컴포넌트
└─ MarketPriceTable // 가상화폐 시세 테이블 컴포넌트

©generated by Project Tree Generator

나는 이번 프로젝트의 GNB 파트를 담당하게 되었는데, 내가 구현해야 할 기능은 다음과 같다.

  1. 다시 계산하기 버튼을 누르면 inputBoard의 입력값들과 차트, 기타 내용들이 모두 초기화
  2. 화폐 단위를 변경하면 그에 맞추어 데이터들이 ₩ ↔︎ $ 단위로 변경
  3. 검색 기록을 로컬 스토리지에 저장 후 검색 기록 버튼을 누르면 모달 창으로 기록을 보여줌

먼저, 지난 주말 GNB 컴포넌트의 틀을 잡아 놓았다. GNB 내부에는 앞서 프로젝트 트리에서 볼 수 있듯 Logo, Button 컴포넌트를 따로 만들어 삽입하였고, 화폐 단위를 변경하는 select 태그는 재사용이 필요 없을 것 같아 jsx 그대로 넣어 주었다.

어제 자정에 PR을 올려 오늘 팀원들에게 리뷰를 받았고, 함수나 변수명, 구현 방식 등등에 대한 좋은 의견을 들을 수 있었다.

그래서 오늘은, 리뷰 받은 내용을 바탕으로 리팩토링을 진행하고 검색 기록을 띄워줄 모달창 구현을 시작하였다. 모달창을 어떻게 만들 수 있을까 생각을 해보고 자료를 찾아보다 보다 쉽게 제작할 수 있게 해주는 'react-modal' 이라는 라이브러리가 있어서 간단히 공부해 보았다.

react-modal

react-modal - npm package info

모달(Modal)은 기존의 브라우저 페이지 위에 새로운 창이 아닌 레이어가 더해지는 것을 말한다. 부모 창 위로 새로운 자식 창이 레이어 형태로 띄워지며, 자식 창이 닫히기 전까지 부모 창과의 상호작용이 불가능하다는 특징을 가지고 있다.

GNB 컴포넌트에는 검색 기록을 모달 창으로 띄워주는 버튼이 있다. 이 버튼을 클릭하면 로컬 스토리지에 저장되어 있던 현재까지의 검색 기록을 모달창에 정리하여 보여준다. 원래 모달은 index.html의 독립적인 요소에 붙어 메인 프로그램과 병렬적으로 작동해야 하는데, 이를 좀 쉽게 구현하는 것을 도와주는 것이 react-modal이고, 사용 방법을 정리해 보려 한다.

사용법

먼저, 라이브러리를 프로젝트 디렉토리에 설치해 주어야 한다.

$ npm install --save react-modal
$ yarn add react-modal

이후 라이브러리를 호출하고 Modal 컴포넌트를 사용하면 된다.

import Modal from 'react-modal';

function App() {
  return (
    <Modal isOpen={isOpen} onRequestClose={()=> setIsOpen(false)}>
      모달 창 내부 구조 작성
    </Modal>
  )
}

Modal은 Children으로 내부 템플릿을 전달하는 형식으로 동작한다. Modal 컴포넌트 태그 사이에 템플릿을 작성해주면 Children prop으로 화면에 렌더링 된다. 보통, Modal을 관리하는 컴포넌트를 따로 만들어 사용하는 것이 권장된다고 한다. 이 컴포넌트에서는 Modal의 디자인, 동작을 정의해주면 된다.

Modal 내부에서 사용하는 prop들이 있는데 그 중 중요한 몇가지를 정리해 보면

  1. isOpen : 모달이 화면에 보일지 여부에 대한 속성이다. true 일 경우 화면에 보이고 false 일 경우 화면에 보이지 않는다.
  2. contentLabel : 시각장애인용 스크린리더에서 모달 창을 어떻게 부를지 정의한다.
  3. onRequestClose : ESC키 또는 모달 밖의 공간을 클릭했을 때 창을 종료시키는 함수를 정의한다. 콜백 형식 또는 외부에서 함수를 정의하고 함수 이름을 전달하는 방식으로 사용된다.

추가로, Modal의 setAppElement 메소드를 통해 모달 렌더링의 기준이 되며, 모달 창이 열렸을 때 사용자가 다른 컴포넌트를 보지 않게 하기 위해 숨겨줄 element를 지정해 줘야 한다. 보통 리액트 앱을 렌더링 해주는 root element를 동일하게 지정해 주면 된다.

이 정도로 간단하게 라이브러리에 대해 알아보고 코드에 바로 적용해 보았다.
코드는 아래와 같다.

import { useCallback } from 'react';
import Modal from 'react-modal';
import Button from './Button';

const modalStyle = {
  overlay: {
    top: '76px',
    right: '52px',
    backgroundColor: 'none',
  },
  content: {
    position: 'absolute',
    overflow: 'auto',
    top: 0,
    right: 0,
    left: 'auto',
    bottom: 'auto',
    border: '1px solid #E7E9F0',
    borderRadius: '16px',
    boxShadow: '0px 4px 15px rgba(11, 14, 27, 0.08)',
    zIndex: 10,
    width: '520px',
    height: '590px',
  },
};

function HistoryModal({ isOpen, handleNotClicked, handleModalOpen }) {
  const handleModalClose = useCallback(() => {
    handleModalOpen(false);
    handleNotClicked(false);
  }, []);

  return (
    <Modal
      style={modalStyle}
      isOpen={isOpen}
      onRequestClose={() => handleModalClose()}
    >
      <div
        style={{
          display: 'flex',
          justifyContent: 'space-between',
          alignItems: 'center',
        }}
      >
        <h1 style={{ fontSize: '20px', fontWeight: 700 }}>검색 기록</h1>
        <Button propStyle={{ border: 'none' }} name="기록 모두 지우기" />
      </div>
    </Modal>
  );
}

export default HistoryModal;

Modal.setAppElement('#root');

일단 지금은 스타일 적용을 대충 해놓았기 때문에 인라인으로 지정해 주었다. 스타일 부분은 무시해도 좋을 것 같다.
HistoryModal 컴포넌트가 삽입될 상위 컴포넌트인 GNB에 isOpen 이라는 state를 만들고, true 일 때 화면에 렌더링 되고, false 일 때 화면에서 사라지도록 구현하였다. 따로 창을 닫는 버튼을 만들지는 않았고, 모달 창 외부를 클릭하거나 escape 키를 눌렀을 때 isOpen state를 false 로 설정해주는 함수 handleModalClose 를 정의해 주었다. handleNotClicked(가명) 은 검색 기록 버튼의 디자인을 바꿔주기 위해 만들어본 state notClicked(가명) 의 setter 함수이다.

이렇게 모달의 뼈대를 구성해 두었고, 검색 기록이 로컬스토리지에 저장되는 기능을 구현하면 그 데이터를 배열 형식으로 가져와 모달 안에서 map 메소드를 통해 한 줄씩 보여주면 될 것 같다.

아직 확실하지는 않지만, 해보면 알 지 않을까.... 생각한다.

오늘의 나는 어떤 어려움이 있었을까?

뭔가 어려움이 있으면서 없는 것 같은 기분이다. GNB 컴포넌트가 App 쪽에서 다뤄야 할 state나 context를 사용해야 할 것 같아 그게 정해지기 전까지는 컴포넌트 틀을 만드는 것 이외에는 뭔가 더 할 수가 없는게 좀 아쉽긴 하다. 내일 한번 이야기를 해봐야겠다.

내일의 나는 무엇을 해야할까?

  • 모달 컴포넌트 완성
  • task1 리팩토링 완료
  • MarketPriceTable 컴포넌트 티나와 함께 살펴보기
profile
개발자를 지망하는 경영학도

0개의 댓글