인프런 워밍업 클럽 스터디 1기 FE 과제(9번 과제)
[9번 과제(Day10) - 디즈니 플러스 앱]
따라하며 배우는 리액트 A-Z
학습 범위: Section 4 ~ 5
https://github.com/helloleesul/inflearn-warmup-club-study/tree/main/disney-plus-app
과제 이미지
- 구글 로그인, swiper, 모달
- 유튜브 플레이, 검색, 상세페이지, 로그아웃
고민한 것
MovieModal
mount될 때에는 애니메이션이 되고, unmount될 때에는 애니메이션 없이 툭 사라져버린다.내가 원하는 것은 모달이 꺼질때에도 애니메이션으로 사라지게 한 후, unmount되게 하는 것
기존 코드
// Row 컴포넌트 modalOpen && <MovieModal {...movieSelection} setModalOpen={setModalOpen} />
이전 코드에서는
modalOpen
상태가 참일 때만MovieModal
이 렌더링되었지만, 변경된 코드에서는MovieModal
컴포넌트 내부에서 직접적으로modalOpen
상태를 조작할 수 있도록 했다.변경 코드
// Row 컴포넌트 <MovieModal {...movieSelection} modalOpen={modalOpen} setModalOpen={setModalOpen} /> // MovieModal 컴포넌트 const MovieModal = ({ ... modalOpen, setModalOpen, }) => { const ref = useRef(); const [showModal, setShowModal] = useState(false); const [animation, setAnimation] = useState(""); useOnClickOutside(ref, () => { // 2. 닫기 이벤트 setModalOpen(false); }); useEffect(() => { if (!modalOpen) { // 4. 사라지는 애니메이션으로 설정 setAnimation("animate-fade-down animate-reverse"); } else { setShowModal(true); setTimeout(() => { // 1. 나타나는 애니메이션으로 설정 setAnimation("animate-fade-up"); }, 10); } }, [modalOpen]); return ( showModal && ( <div className="animate-fade"> <article className={`${animation}`} ref={ref} onAnimationEnd={() => { // 3. 사라지는 애니메이션 끝이나면 showModal false if (!modalOpen) setShowModal(false); }} ></article> </div> ) ) };
MovieModal
컴포넌트 내부에서는modalOpen
상태에 따라 모달의 나타남과 사라짐을 제어하는 useEffect와 useState를 사용했다.modalOpen
상태가 변경될 때마다 useEffect가 실행되어 애니메이션 클래스를 설정하고, 애니메이션 효과를 주는 CSS 클래스를 적용시킨다.onAnimationEnd 이벤트 핸들러를 사용하여 애니메이션 종료 후에
showModal
상태를 변경하여 모달이 화면에서 완전히 사라지도록 처리했다.
추가한 것
tailwind css + 스크롤바 숨기는 플러그인, 애니메이션 플러그인
/** @type {import('tailwindcss').Config} */ module.exports = { content: ["./src/**/*.{js,jsx,ts,tsx}"], theme: { extend: {}, }, plugins: [ require("tailwind-scrollbar-hide"), require("tailwindcss-animated"), ], };
회원만 접근 가능한 parent 컴포넌트 생성
const UserGuard = () => { const navigate = useNavigate(); useEffect(() => { const profilePictureUrl = localStorage.getItem("profilePictureUrl"); // 로그인 시 저장한 구글 유저 프로필이미지가 없다면 LandingPage로 이동 if (!profilePictureUrl) { navigate("/"); } }, [navigate]); return <Outlet />; }; export default UserGuard; function App() { return ( <div className="App"> <Routes> <Route path="/" element={<Layout />}> <Route index element={<LandingPage />} /> 👇 <Route element={<UserGuard />}> <Route path="main" element={<MainPage />} /> <Route path=":movieId" element={<DetailPage />} /> <Route path="search" element={<SearchPage />} /> </Route> </Route> </Routes> </div> ); } export default App;
로그인 상태일 때 랜딩 페이지 접근 불가
const LandingPage = () => { const navigate = useNavigate(); useEffect(() => { const profilePictureUrl = localStorage.getItem("profilePictureUrl"); // 로그인 시 저장한 구글 유저 프로필이미지가 있다면 MainPage로 이동 if (profilePictureUrl) { navigate("/main"); } }, [navigate]); return (...) };
댓글을 작성해보세요.