๐Ÿฌ ๊ธดํ˜ธํก/๊ณ ์Šค๋ฝ ํ‹ฐ์ผ“

[2.0] ๋ชจ๋‹ฌ ์ „์—ญ์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ธฐ with TypeScript

ํ•œ๊ทœ์ง„ 2022. 8. 10. 23:22

์•ฑ์˜ ๋‹ค์–‘ํ•œ ํŽ˜์ด์ง€์—์„œ ๋ชจ๋‹ฌ์„ ์‚ฌ์šฉํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋ฏธ๋ฆฌ ์ง€์ •๋˜์ง€ ์•Š์€ ์˜ค๋ฅ˜์— ๋Œ€ํ•ด์„œ๋„ ์บ์น˜ํ•ด์„œ ๋ชจ๋‹ฌ์„ ๋„์šฐ๋„๋ก ๋˜์–ด์žˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ๋ชจ๋‹ฌ์„ ์ „์—ญ์œผ๋กœ ๊ด€๋ฆฌํ•ด์„œ ์‚ฌ์šฉํ•ด๋ณด๊ธฐ๋กœ. ๋ฑ…ํ‚ค์ฆˆ์™€ ๋น„์Šทํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜์ง€๋งŒ ์กฐ๊ธˆ์€ ๋‹ค๋ฅธ ๋ฐฉ์‹์ด๋‹ค. ์ฐธ๊ณ 

 


 

6๊ฐœ์›” ์ „, 1์ฐจ ํ”„๋กœ์ ํŠธ ์ค‘์— ์งฐ๋˜ ์ฝ”๋“œ์ด๋‹ค.

import React from 'react';

const ModalComponent = ({ children }, ref) => {
  return (
    <div className="modal hidden" ref={ref}>
      <div
        className="modal-overlay"
        onClick={() => {
          ref.current.classList.add('hidden');
        }}
      ></div>
      <div className="modal-content">{children}</div>
    </div>
  );
};

export default React.forwardRef(ModalComponent);

์ด๋ ‡๊ฒŒ ๋ชจ๋‹ฌ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋†“๊ณ ,

 

function TicketCodePage({ ...props }) {

  const modalRef = useRef();
  const onClick={() => {
    modalRef.current.classList.remove('hidden');
  }}
// ...
  return (
    <TicketWrapContainer>
    // ...
        <ModalComponent ref={modalRef}>
          <ModalBox
            onClickClose={() => {
              modalRef.current.classList.add('hidden');
            }}
          />
        </ModalComponent>
    </TicketWrapContainer>
  );
}

export default TicketCodePage;

๋ชจ๋‹ฌ์ด ํ•„์š”ํ•œ ํŽ˜์ด์ง€๋งˆ๋‹ค ๋ชจ๋‹ฌ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‹ฌ์•„์ค€๋‹ค. ref๋กœ div์š”์†Œ๋ฅผ ๊ฐ€์ ธ์™€์„œ ์ง์ ‘ class๋ฅผ ๋ฐ”๊ฟ”๊ฐ€๋ฉฐ display ์†์„ฑ์„ ๋ฐ”๊ฟ”์ฃผ๋Š” ํ˜•์‹์ด์—ˆ๋‹ค. ๋ฌด์—‡๋ณด๋‹ค ์ด๊ฒŒ ์ œ์ผ ๋ง˜์— ์•ˆ๋“ค์—ˆ์Œ. ๋˜ ๋ชจ๋‹ฌ์„ ์‚ฌ์šฉํ•˜๋Š” ํƒ€์ž…๋งˆ๋‹ค ๋˜‘๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ์—ฌ๋Ÿฌ๋ฒˆ ์ž‘์„ฑํ•ด์•ผ ํ•˜๋Š”๋ฐ, ์ด ๋ถ€๋ถ„๋„ ์ง€์ •๋œ ํƒ€์ž…์œผ๋กœ ๊ด€๋ฆฌ๋ฅผ ํ•˜๊ณ  ์‹ถ์—ˆ๋‹ค. ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํƒ€์ž…๋“ค์ด ์žˆ๋‹ค.

 

  • ์‘์›ํ†ก์„ ๋‚จ๊ธฐ๊ธฐ ์ „ ์ด๋ฆ„๊ณผ ๋‚ด์šฉ์„ ํ™•์ธํ•˜๊ณ  ์ „์†ก
  • ์†Œ์ผ“ ์‘๋‹ต์„ ๋ฐ›๊ณ  ๋‚œ ํ›„ ์—๋Ÿฌ ์ฒ˜๋ฆฌ
  • API ์š”์ฒญ์˜ ์‘๋‹ต์„ ๋ฐ›๊ณ  ๋‚œ ํ›„ ์—๋Ÿฌ ์ฒ˜๋ฆฌ
  • ๊ณต์—ฐ์žฅ ์ •๋ณด, ๊ฐœ๋ฐœ์ž ์†Œ๊ฐœ ๋“ฑ์˜ ๋ณ„๋„ ํŽ˜์ด์ง€๋กœ ๋นผ์ง€ ์•Š๋Š” ์ •๋ณด

 

๋ชจ๋‹ฌ์„ ์ „์—ญ์œผ๋กœ ๊ด€๋ฆฌํ•˜๋ฉด ๊ฐ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋งค๋ฒˆ ๋ชจ๋‹ฌ์„ ์ž„ํฌํŠธํ•ด์„œ ๋„ฃ์–ด์ค„ ํ•„์š”๊ฐ€ ์—†์–ด์ง„๋‹ค.

 


 

GlobalModal.tsx

export const MODAL_TYPES = {
  Notice: 'Notice',
  CheckBeforeSend: 'CheckBeforeSend',
  Location: 'Location',
  Developers: 'Developers',
} as const;

const ModalComponents: any = {
  [MODAL_TYPES.Notice]: Notice,
  [MODAL_TYPES.CheckBeforeSend]: CheckBeforeSend,
  [MODAL_TYPES.Location]: Location,
  [MODAL_TYPES.Developers]: Developers,
};

const GlobalModal = () => {
  const [modal, setModal] = useRecoilState(modalState);
  const { modalType, modalProps } = modal || {};
  const renderComponent = () => {
    if (!modalType) {
      return null;
    }
    const ModalComponent = ModalComponents[modalType];

    return (
      <Modal>
        <Overlay onClick={() => setModal(null)} />
        <Container>
          <ModalComponent {...modalProps} />
        </Container>
      </Modal>
    );
  };
  return <>{renderComponent()}</>;
};

export default GlobalModal;

๋จผ์ € GlobalModal ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค์–ด ์ตœ์ƒ์œ„(๋‚ด ๊ฒฝ์šฐ์—” index.tsx)์— ๋„ฃ์–ด๋‘”๋‹ค. styled-component๋ฅผ ์‚ฌ์šฉํ•œ ์ฝ”๋“œ์ด๋‹ค. ์ค‘์š”ํ•˜๊ฒŒ ๋ด์•ผํ•  ๊ณณ์€ ๋ฆฌ์ฝ”์ผ์— ๋ชจ๋‹ฌ ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜๋Š” ๊ฒƒ๊ณผ, GobalModal์—์„œ ๋ณด์—ฌ์ค„ ๋ชจ๋‹ฌ์˜ ์ข…๋ฅ˜๋“ค์„ ์ฝ”๋“œ์™€ ๊ฐ™์ด ModalTypes ๊ฐ์ฒด๋กœ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ - ๋‘ ๊ตฐ๋ฐ์ด๋‹ค. ModalTypes ์—์„œ๋Š” ๊ฐ ๋ชจ๋‹ฌ์˜ ํƒ€์ž…์„ ๋ฌธ์ž์—ด๋กœ ๊ฐ–๊ณ  ์žˆ๋‹ค๊ฐ€ ModalCompoents์—์„œ ๊ฐ ํƒ€์ž…์˜ ๊ฐ’์„ ํ‚ค๋กœ ํ•˜๋Š” ๊ฐ์ฒด๋กœ ๋ชจ๋‹ฌ ์ปดํฌ๋„ŒํŠธ ์ž์ฒด๋ฅผ ๊ด€๋ฆฌํ•œ๋‹ค. ๋ฆฌ์ฝ”์ผ ์ƒํƒœ์— ์ €์žฅํ•ด๋‘” ๊ฐ’์ด ์žˆ์„ ๋• ํ•ด๋‹น ๋ชจ๋‹ฌ์„ ๋ Œ๋”๋งํ•˜๊ณ , ์ƒํƒœ๋ฅผ null๋กœ ๋ฐ”๊พธ๋ฉด renderComponent()์—์„œ null์„ ๋ฆฌํ„ดํ•˜๋ฏ€๋กœ ์•„๋ฌด๊ฒƒ๋„ ๋ Œ๋”๋งํ•˜์ง€ ์•Š๋Š”๋‹ค!

 

stores/modal.ts

import { atom } from 'recoil';
import { MODAL_TYPES } from '../components/modal/GlobalModal';

export type TNoticeModal = {
  modalType: typeof MODAL_TYPES.Notice;
  modalProps: any;
};

export type TCheckBeforeSendModal = {
  modalType: typeof MODAL_TYPES.CheckBeforeSend;
  modalProps: any;
};

export type TLocationModal = {
  modalType: typeof MODAL_TYPES.Location;
  modalProps: any;
};

export type TDevelopersModal = {
  modalType: typeof MODAL_TYPES.Developers;
  modalProps: any;
};

export type ModalType =
  | TNoticeModal
  | TCheckBeforeSendModal
  | TLocationModal
  | TDevelopersModal;
export const modalState = atom<ModalType | null>({
  key: 'modalState',
  default: null,
});

ModalState์—๋Š” ๋‘๊ฐ€์ง€ ์ •๋ณด๊ฐ€ ๋“ค์–ด๊ฐ„๋‹ค. ์–ด๋–ค ์ข…๋ฅ˜์˜ ๋ชจ๋‹ฌ์ด ๋“ค์–ด๊ฐˆ์ง€, ๊ทธ๋ฆฌ๊ณ  ํ•ด๋‹น ๋ชจ๋‹ฌ ์ปดํฌ๋„ŒํŠธ์— ๋“ค์–ด๊ฐˆ props. ์ปดํฌ๋„ŒํŠธ ์ž์ฒด๋ฅผ ๋ฐ”๋กœ ์“ฐ์ง€ ์•Š๊ณ  MODAL_TYPES๋ฅผ ์ค‘๊ฐ„์— ๋‘์–ด ๋ Œ๋”๋งํ•˜๋Š”๋ฐ์˜ ์ด์œ ๊ฐ€ ์—ฌ๊ธฐ์— ์žˆ๋‹ค. ๋ฆฌ์ฝ”์ผ ์ƒํƒœ์—๋Š” JSX ์š”์†Œ๋ฅผ ๋‹ด์„ ์ˆ˜ ์—†๋‹ค. ๋ฑ…ํ‚ค์ฆˆ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ฆฌ๋•์Šค๋„ ๋˜‘๊ฐ™์€ ์ด์œ  ๋•Œ๋ฌธ์— ๋”ฐ๋กœ context api๋ฅผ ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ, ์ด ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋ฉด ๋์—ˆ๋‹ค!!

 

useModal.tsx

const useModal = () => {
  const [modal, setModal] = useRecoilState(modalState);

  const openModal = ({ modalType, modalProps }: ModalType) => {
    setModal({ modalType, modalProps });
  };

  const closeModal = () => {
    setModal(null);
  };

  return { openModal, closeModal };
};

export default useModal;

๋ชจ๋‹ฌ์„ ์—ด๊ณ  ๋‹ซ๋Š” ํ•จ์ˆ˜๋ฅผ ์ปค์Šคํ…€ํ›…์„ ํ†ตํ•ด ์ถ”์ƒํ™”ํ•ด์„œ ์“ฐ๊ณ  ์žˆ๋‹ค.

 


 

openModal({
          modalType: 'CheckBeforeSend',
          modalProps: {
            onClick: () => {
              mutate({ nickName, content });
              closeModal();
              console.log('asdf');
            },
            closeModal,
            content,
            nickName,
          },
        });

๋ชจ๋‹ฌ์ด ํ•„์š”ํ•œ ๊ณณ์—์„œ ์ด๋ ‡๊ฒŒ openModal ํ•จ์ˆ˜๋ฅผ ์“ธ ์ˆ˜ ์žˆ๋‹ค.