- {/* Next.js Image 대신 일반 img 태그 사용 */}
-
-

- handleImageError(e, '모임 스타일에 맞는 모임 개설')
- }
- />
-
-
-
- 모임 공개 여부
-
-
- 비공개
-
+
+
+
-
-
- {typeof isMobile !== 'undefined' &&
- (isMobile ? (
-
- ) : (
-
- ))}
-
- 모임 생성이 완료 되었습니다!
-
-
-
+
+
+
+
+ Copyright @ 2025 DEVING All Rights Reserved
-
-
-
-
-
- {/* 섹션 3: CTA */}
-
-
-
- DEVING에서
-
- 당신의 모임을 바로 시작하세요!
-
-
-
-
-
-
-
-
- {/* 푸터 섹션 - 중요 클래스: section fp-auto-height */}
-
-
+
+
+ );
+ }}
+ />
+
);
}
diff --git a/src/components/common/Footer.tsx b/src/components/common/Footer.tsx
new file mode 100644
index 0000000..6dce498
--- /dev/null
+++ b/src/components/common/Footer.tsx
@@ -0,0 +1,69 @@
+'use client';
+
+import { motion } from 'framer-motion';
+import { usePathname } from 'next/navigation';
+
+import DevingLogo from '../../assets/icon/devingLogo.svg';
+import GithubIcon from '../../assets/icon/github_icon.svg';
+import LandingLogo from '../../assets/icon/landing_logo.svg';
+import NotionIcon from '../../assets/icon/notion_icon.svg';
+
+// 애니메이션 설정
+const animations = {
+ fadeIn: {
+ initial: { opacity: 0 },
+ animate: { opacity: 1 },
+ transition: { duration: 0.5 },
+ },
+};
+
+const Footer = () => {
+ const pathname = usePathname();
+ const isLandingPage = pathname === '/';
+
+ // 랜딩 페이지일 경우 Footer를 렌더링하지 않음
+ if (isLandingPage) {
+ return null;
+ }
+
+ return (
+
+ );
+};
+
+export default Footer;
diff --git a/src/components/landing/BasketballCat.tsx b/src/components/landing/BasketballCat.tsx
new file mode 100644
index 0000000..e2ec10a
--- /dev/null
+++ b/src/components/landing/BasketballCat.tsx
@@ -0,0 +1,113 @@
+'use client';
+
+import { motion } from 'framer-motion';
+
+import { useIsLargeScreen } from '../../hooks/useLargeScreen';
+
+/**
+ * 농구공을 가진 하얀 고양이 컴포넌트
+ * 1920px 이상에서만 애니메이션 작동
+ */
+const BasketballCat = () => {
+ const isLargeScreen = useIsLargeScreen();
+
+ return (
+ <>
+ {/* 하얀 고양이 - 가만히 있는 모습 */}
+
+
+ {/* 헤드셋 - 음악에 맞춰 움직이는 효과 */}
+
+
+ {/* 농구공 - 통통 튀는 효과 */}
+
+ >
+ );
+};
+
+export default BasketballCat;
diff --git a/src/components/landing/BeigeCat.tsx b/src/components/landing/BeigeCat.tsx
new file mode 100644
index 0000000..2de2ad6
--- /dev/null
+++ b/src/components/landing/BeigeCat.tsx
@@ -0,0 +1,63 @@
+'use client';
+
+import { motion } from 'framer-motion';
+
+import { useIsLargeScreen } from '../../hooks/useLargeScreen';
+
+/**
+ * 베이지색 고양이 컴포넌트
+ * 1920px 이상에서만 애니메이션 작동
+ */
+const BeigeCat = () => {
+ const isLargeScreen = useIsLargeScreen();
+
+ return (
+
+ );
+};
+
+export default BeigeCat;
diff --git a/src/components/landing/FrontNotebook.tsx b/src/components/landing/FrontNotebook.tsx
new file mode 100644
index 0000000..e3928dd
--- /dev/null
+++ b/src/components/landing/FrontNotebook.tsx
@@ -0,0 +1,64 @@
+'use client';
+
+import { motion } from 'framer-motion';
+
+import { useIsLargeScreen } from '../../hooks/useLargeScreen';
+
+/**
+ * 앞 노트북 컴포넌트
+ * 1920px 이상에서만 애니메이션 작동
+ */
+const FrontNotebook = () => {
+ const isLargeScreen = useIsLargeScreen();
+
+ return (
+
+ );
+};
+
+export default FrontNotebook;
diff --git a/src/components/landing/GrayCat.tsx b/src/components/landing/GrayCat.tsx
new file mode 100644
index 0000000..b859426
--- /dev/null
+++ b/src/components/landing/GrayCat.tsx
@@ -0,0 +1,182 @@
+'use client';
+
+import { motion, useAnimationControls } from 'framer-motion';
+import { useEffect } from 'react';
+
+import { useIsLargeScreen } from '../../hooks/useLargeScreen';
+
+/**
+ * 회색 고양이 컴포넌트 - 통통 튀는 애니메이션과 고정된 화난 눈 표정
+ * 1920px 이상에서만 애니메이션 작동
+ * 1920px 미만에서는 눈썹이 보이지 않음
+ */
+const GrayCat = () => {
+ // 화면 크기 감지
+ const isLargeScreen = useIsLargeScreen();
+
+ // 모든 애니메이션을 제어할 컨트롤러 생성
+ const controls = useAnimationControls();
+
+ useEffect(() => {
+ // 1920px 이상일 때만 애니메이션 실행
+ if (isLargeScreen) {
+ const startAnimation = async () => {
+ // 초기 진입 애니메이션
+ await controls.start('enter');
+
+ // 반복 애니메이션 시작 (통통 튀는 동작)
+ controls.start('loop');
+ };
+
+ startAnimation();
+ } else {
+ // 작은 화면에서는 정적 상태로 표시
+ controls.start('static');
+ }
+ }, [controls, isLargeScreen]);
+
+ // 고양이 애니메이션에 대한 변형(variants) 정의
+ const catVariants = {
+ initial: {
+ opacity: 0,
+ x: 40,
+ },
+ enter: {
+ opacity: 1,
+ x: 0,
+ transition: {
+ type: 'spring',
+ stiffness: 60,
+ damping: 10,
+ delay: 1.0,
+ },
+ },
+ loop: {
+ y: [0, -8, 0],
+ transition: {
+ y: {
+ duration: 0.5,
+ repeat: Infinity,
+ repeatDelay: 2,
+ ease: 'easeOut',
+ repeatType: 'loop',
+ },
+ },
+ },
+ static: {
+ opacity: 1,
+ x: 0,
+ y: 0,
+ },
+ };
+
+ // 왼쪽 눈에 대한 변형(variants) - 45도 회전 유지
+ const leftEyeVariants = {
+ initial: {
+ opacity: 0,
+ rotate: 45,
+ },
+ enter: {
+ opacity: 1,
+ rotate: 45,
+ transition: {
+ delay: 1.5,
+ duration: 0.2,
+ },
+ },
+ loop: {
+ opacity: 1,
+ rotate: 45,
+ y: [0, -8, 0],
+ transition: {
+ y: {
+ duration: 0.5,
+ repeat: Infinity,
+ repeatDelay: 2,
+ ease: 'easeOut',
+ repeatType: 'loop',
+ },
+ },
+ },
+ static: {
+ opacity: 1,
+ rotate: 45,
+ y: 0,
+ },
+ };
+
+ // 오른쪽 눈에 대한 변형(variants) - -45도 회전 유지
+ const rightEyeVariants = {
+ initial: {
+ opacity: 0,
+ rotate: -45,
+ },
+ enter: {
+ opacity: 1,
+ rotate: -45,
+ transition: {
+ delay: 1.5,
+ duration: 0.2,
+ },
+ },
+ loop: {
+ opacity: 1,
+ rotate: -45,
+ y: [0, -8, 0],
+ transition: {
+ y: {
+ duration: 0.5,
+ repeat: Infinity,
+ repeatDelay: 2,
+ ease: 'easeOut',
+ repeatType: 'loop',
+ },
+ },
+ },
+ static: {
+ opacity: 1,
+ rotate: -45,
+ y: 0,
+ },
+ };
+
+ return (
+ <>
+ {/* 회색 고양이 기본 이미지 - 통통 튀는 애니메이션 */}
+
+
+ {/* 화면 크기가 1920px 이상일 때만 눈썹 렌더링 */}
+ {isLargeScreen && (
+ <>
+ {/* 화난 눈 모양 - 왼쪽 눈 (더 두껍고 아래로) */}
+
+
+ {/* 화난 눈 모양 - 오른쪽 눈 (더 두껍고 아래로) */}
+
+ >
+ )}
+ >
+ );
+};
+
+export default GrayCat;
diff --git a/src/components/landing/HeartNotebookCat.tsx b/src/components/landing/HeartNotebookCat.tsx
new file mode 100644
index 0000000..31c0278
--- /dev/null
+++ b/src/components/landing/HeartNotebookCat.tsx
@@ -0,0 +1,65 @@
+'use client';
+
+import { motion } from 'framer-motion';
+
+import { useIsLargeScreen } from '../../hooks/useLargeScreen';
+
+/**
+ * 하트 노트북 컴포넌트
+ * 1920px 이상에서만 애니메이션 작동
+ */
+const HeartNotebookCat = () => {
+ const isLargeScreen = useIsLargeScreen();
+
+ return (
+
+ );
+};
+
+export default HeartNotebookCat;
diff --git a/src/components/landing/ReadingCat.tsx b/src/components/landing/ReadingCat.tsx
new file mode 100644
index 0000000..c26605b
--- /dev/null
+++ b/src/components/landing/ReadingCat.tsx
@@ -0,0 +1,58 @@
+'use client';
+
+import { motion } from 'framer-motion';
+
+import { useIsLargeScreen } from '../../hooks/useLargeScreen';
+
+const ReadingCat = () => {
+ const isLargeScreen = useIsLargeScreen();
+
+ return (
+
+ );
+};
+
+export default ReadingCat;
diff --git a/src/components/landing/WhiteNotebookCat.tsx b/src/components/landing/WhiteNotebookCat.tsx
new file mode 100644
index 0000000..a91ec52
--- /dev/null
+++ b/src/components/landing/WhiteNotebookCat.tsx
@@ -0,0 +1,62 @@
+'use client';
+
+import { motion } from 'framer-motion';
+
+import { useIsLargeScreen } from '../../hooks/useLargeScreen';
+
+/**
+ * 흰색 노트북 고양이 컴포넌트
+ * 노트북 화면 깜빡임 대신 키보드 타이핑하는 듯한 효과
+ */
+const WhiteNotebookCat = () => {
+ const isLargeScreen = useIsLargeScreen();
+
+ return (
+
+ );
+};
+
+export default WhiteNotebookCat;
diff --git a/src/components/landing/YellowCat.tsx b/src/components/landing/YellowCat.tsx
new file mode 100644
index 0000000..2e54607
--- /dev/null
+++ b/src/components/landing/YellowCat.tsx
@@ -0,0 +1,64 @@
+'use client';
+
+import { motion } from 'framer-motion';
+
+import { useIsLargeScreen } from '../../hooks/useLargeScreen';
+
+/**
+ * 노란 고양이 컴포넌트
+ * 호기심 많은 고양이가 고개를 갸우뚱하는 애니메이션
+ */
+const YellowCat = () => {
+ const isLargeScreen = useIsLargeScreen();
+
+ return (
+
+ );
+};
+
+export default YellowCat;
diff --git a/src/hooks/useLargeScreen.ts b/src/hooks/useLargeScreen.ts
new file mode 100644
index 0000000..b01583f
--- /dev/null
+++ b/src/hooks/useLargeScreen.ts
@@ -0,0 +1,30 @@
+'use client';
+
+import { useEffect, useState } from 'react';
+
+/**
+ * 화면이 1920px 이상인지 감지하는 커스텀 훅
+ * @returns {boolean} 화면이 1920px 이상이면 true, 아니면 false
+ */
+export const useIsLargeScreen = () => {
+ // 초기값은 false로 설정 (SSR 고려)
+ const [isLargeScreen, setIsLargeScreen] = useState(false);
+
+ useEffect(() => {
+ // 화면 크기 확인 함수
+ const checkScreenSize = () => {
+ setIsLargeScreen(window.innerWidth >= 1920);
+ };
+
+ // 초기 실행
+ checkScreenSize();
+
+ // 화면 크기 변경 이벤트 리스너
+ window.addEventListener('resize', checkScreenSize);
+
+ // 클린업
+ return () => window.removeEventListener('resize', checkScreenSize);
+ }, []);
+
+ return isLargeScreen;
+};