피파온라인 API는 친절하게도 닉네임을 기반으로 유저의 고유 ID를 반환해주고 있다
이렇게 전달받은 accessId를 기반으로 앞으로 파피온라인 유저의 정보들을 검색할 수 있는 것이다
그렇다면 이걸 내 프로젝트에 활용해야 하는데
처음 로그인을 할 때 , 피파온라인에서 사용하고 있는 실제 ID를 입력받아서 이 웹에서 사용하는 구글 계정과 피파온라인 계정을 연결하고자 했다
이런 과정을 거치는 것이다!
다만 , 이런 과정을 굳이 별도의 페이지로 넘기기엔 UX측면에서 피로감이 쌓일 것이라 생각이 들었고 그래서 모달창으로 구현하는 것이 좋을거라 생각했다
모달창 구현하는 방법은 정말 여러가지가 있다
전역 상태(context , zustand 등)을 이용해서 전역으로 관리하는 모달창을 직접 구현
모달이 필요한 페이지에 하위 컴포넌트로 모달 컴포넌트를 작성하고 css로 처리하기
ReactDOM.createPortal(child, container) 을 사용해서 id=root인 div말고
외부에 모달창 생성하기 (이것도 전역과 비슷해 보인다)
리액트 라이브러리 (react-modal) 사용
부트스트랩 사용
그렇지만 지금 프로젝트는 무엇보다 기능 구현보단 , 코드의 품질에도 신경을 써보고 싶어서
라이브러리의 힘을 빌린다기보다 직접 스스로 구현을 해보기로 했다
그러던 도중 모달창은 언제 어디서 사용될 지 모르니 일일이 필요할 때마다 하위 컴포넌트로 만들어서 구현하는 것보다 ,
전역으로 관리하는게 좋다는 주변 지인의 말에 첫번째 방법을 사용해보게 되었다
모달 창 자체가 사실 상태로써 자주 값이 변경되며 지속적인 관리가 필요한 것은 아닌 ,
단지 일회적인 용도로만 사용된다고 생각이 들어서 굳이 상태관리 라이브러리가 아닌 context로 해결하고자 했다
모달 관련 context를 생성합니다
import React, { createContext, useContext, useState } from 'react';
interface ModalContextValue {
isModalOpen: boolean;
openModal: (children: React.ReactNode) => void;
closeModal: () => void;
}
export const ModalContext = createContext<ModalContextValue | null>(null);
export const ModalProvider = ({ children }: { children: React.ReactNode }) => {
const [isModalOpen, setIsModalOpen] = useState(false);
const [modalContents, setModalContents] = useState((<></>) as React.ReactNode);
const openModal = (children: React.ReactNode) => {
setIsModalOpen(true);
setModalContents(children);
};
const closeModal = () => setIsModalOpen(false);
// 모달 바깥 영역을 클릭하면 모달이 닫힙니다
const onDimmerClick = (event: React.MouseEvent<HTMLDivElement>) => {
if (event.currentTarget !== event.target) return;
closeModal();
};
return (
<ModalContext.Provider value={{ isModalOpen, openModal, closeModal }}>
{children}
{isModalOpen && <div onClick={onDimmerClick}>{modalContents}</div>}
</ModalContext.Provider>
);
};
export function useModal() {
return useContext(ModalContext);
}
여기서 사용된 Provider 부분에 대한 설명은 아래와 같다
<ModalContext.Provider value={{ isModalOpen, openModal, closeModal }}>
{children}
{isModalOpen && <div onClick={onDimmerClick}>{modalContents}</div>}
</ModalContext.Provider>
isModalOpen 이 true라면 div로 된 모달창을 띄운다
이 모달창은 {children}처럼 자식 컴포넌트 어딘가에 존재하는 것이 아니라 Provider로 감싸고 있는 현재 위치에 존재하는 것이 된다 즉, ModalContext.Provider가 있는 최상위에 전역으로 존재하는 컴포넌트인 것이다
자식 컴포넌트 어딘가에서 useModal()로 부터 받은 함수를 사용해서 모달을 생성하게 되면
ModalContext.Provider가 있는 최상위에 컴포넌트에 모달이 생성되는 것이다
contextAPI를 이용하여 해당 컴포넌트에서 모달을 생성한다
import React, { useEffect, useState } from 'react';
import { useModalAPI } from '../../Context/Modal/ModalContext';
import AskNickName from '../../Components/AskNickName';
const ChooseModeAndLogin = () => {
...
const { isModalOpen, openModal } = useModalAPI()!;
const onSocialClick = async (e: React.MouseEvent<HTMLButtonElement>) => {
...
// 모달을 열기
openModal(<AskNickName />);
};
return (
<div>
...
<button name="google" onClick={onSocialClick}>
로그인 하기(Google)
</button>
</div>
);
};
export default ChooseModeAndLogin;