Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/app/(user-page)/mypage/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export default async function MyPage() {
await prefetchProfileData(queryClient);

return (
<div className="flex flex-col px-[24px] pb-[100px] pt-[80px]">
<div className="flex flex-col px-[24px] pb-[100px]">
<HydrationBoundary state={dehydrate(queryClient)}>
<div className="md:mb-8">
<ProfileImage />
Expand Down
2 changes: 2 additions & 0 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Footer from '@/components/common/Footer';
import Header from '@/components/common/Header';
import { ToastProvider } from '@/components/common/ToastContext';
import ReactQueryProviders from '@/hooks/useReactQuery';
Expand Down Expand Up @@ -31,7 +32,7 @@
return res.data.data;
} catch (error) {
if (error instanceof AxiosError) {
console.log('error: ', error.status);

Check warning on line 35 in src/app/layout.tsx

View workflow job for this annotation

GitHub Actions / check

Unexpected console statement
}
return null;
}
Expand All @@ -51,6 +52,7 @@
<ToastProvider>
<Header userInfo={userInfo} />
<div className="m-auto max-w-[1340px] pt-20">{children}</div>
<Footer />
</ToastProvider>
</ReactQueryProviders>
<div id="modal-root" />
Expand Down
610 changes: 297 additions & 313 deletions src/app/page.tsx

Large diffs are not rendered by default.

69 changes: 69 additions & 0 deletions src/components/common/Footer.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<footer
className="flex h-[300px] flex-col items-center gap-[24px] px-[24px] pt-[60px] md:h-[300px] md:gap-[32px] md:px-[48px] md:pb-[60px] md:pt-[66px] lg:h-[300px] lg:px-[230px]"
role="contentinfo"
>
<motion.div
className="flex w-full justify-center gap-[12px]"
initial={animations.fadeIn.initial}
animate={animations.fadeIn.animate}
transition={animations.fadeIn.transition}
>
<div>
<LandingLogo />
</div>
<div>
<DevingLogo />
</div>
</motion.div>
<div className="text-[13px] text-Cgray500">
Copyright @ 2025 DEVING All Rights Reserved
</div>
<div className="w-full border border-Cgray200 lg:w-[1460px]"></div>
<div className="flex gap-2">
<a
href="https://github.com/your-github"
aria-label="GitHub 계정 방문하기"
>
<GithubIcon />
</a>
<a
href="https://notion.so/your-notion"
aria-label="Notion 페이지 방문하기"
>
<NotionIcon />
</a>
</div>
</footer>
);
};

export default Footer;
113 changes: 113 additions & 0 deletions src/components/landing/BasketballCat.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
'use client';

import { motion } from 'framer-motion';

import { useIsLargeScreen } from '../../hooks/useLargeScreen';

/**
* 농구공을 가진 하얀 고양이 컴포넌트
* 1920px 이상에서만 애니메이션 작동
*/
const BasketballCat = () => {
const isLargeScreen = useIsLargeScreen();

return (
<>
{/* 하얀 고양이 - 가만히 있는 모습 */}
<motion.img
src="/white_cat.png"
alt="고양이"
className="absolute left-[164px] top-[30px] h-[62px] w-[61px] md:left-[342px] md:h-[82px] md:w-[81px] lg:left-[480px] lg:top-[0px] lg:h-[122px] lg:w-[121px]"
initial={isLargeScreen ? { opacity: 0, y: -20 } : { opacity: 1 }}
animate={
isLargeScreen
? {
opacity: 1,
y: 0,
transition: {
duration: 0.6,
ease: 'easeOut',
},
}
: { opacity: 1 }
}
/>

{/* 헤드셋 - 음악에 맞춰 움직이는 효과 */}
<motion.img
src="/headset.png"
alt="헤드셋"
className="absolute left-[164px] top-[32px] h-[30px] w-[57px] md:left-[342px] md:top-[30px] md:h-[40px] md:w-[76px] lg:left-[480px] lg:top-[0px] lg:h-[59px] lg:w-[113px]"
initial={
isLargeScreen ? { opacity: 0, scale: 0.8 } : { opacity: 1, scale: 1 }
}
animate={
isLargeScreen
? {
opacity: 1,
scale: 1,
transition: {
duration: 0.5,
delay: 0.2,
},
}
: { opacity: 1 }
}
whileInView={
isLargeScreen
? {
rotate: [-2, 2, -2, 2, -2],
transition: {
duration: 2.5,
repeat: Infinity,
repeatType: 'loop',
ease: [0.44, 0.56, 0.56, 0.44],
delay: 0.9,
},
}
: {}
}
viewport={{ once: true }}
/>

{/* 농구공 - 통통 튀는 효과 */}
<motion.img
src="/basketball.png"
alt="농구공"
className="absolute left-[165px] top-[72px] h-[18px] w-[18px] md:left-[342px] md:top-[90px] md:h-[24px] md:w-[24px] lg:left-[480px] lg:top-[80px] lg:h-[36px] lg:w-[36px]"
initial={isLargeScreen ? { opacity: 0, y: -50 } : { opacity: 1 }}
animate={
isLargeScreen
? {
opacity: 1,
y: 0,
transition: {
type: 'spring',
stiffness: 300,
damping: 10,
delay: 0.4,
},
}
: { opacity: 1 }
}
whileInView={
isLargeScreen
? {
y: [0, -20, 20, -15, 10, -5, 0],
transition: {
duration: 1.2,
repeat: Infinity,
repeatDelay: 0.5,
ease: 'easeInOut',
delay: 1.2,
},
}
: {}
}
viewport={{ once: true }}
/>
</>
);
};

export default BasketballCat;
63 changes: 63 additions & 0 deletions src/components/landing/BeigeCat.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
'use client';

import { motion } from 'framer-motion';

import { useIsLargeScreen } from '../../hooks/useLargeScreen';

/**
* 베이지색 고양이 컴포넌트
* 1920px 이상에서만 애니메이션 작동
*/
const BeigeCat = () => {
const isLargeScreen = useIsLargeScreen();

return (
<motion.img
src="/beige_cat.png"
alt="베이지 고양이"
className="absolute left-[32px] top-[175px] h-[66px] w-[54px] md:left-[40px] md:top-[170px] md:h-[88px] md:w-[71px] lg:left-[70px] lg:top-[180px] lg:h-[131px] lg:w-[106px]"
initial={isLargeScreen ? { opacity: 0, x: -30, y: 20 } : { opacity: 1 }}
animate={
isLargeScreen
? {
opacity: 1,
x: 0,
y: 0,
transition: {
type: 'spring',
stiffness: 80,
damping: 15,
delay: 0.6,
},
}
: { opacity: 1 }
}
whileInView={
isLargeScreen
? {
y: [0, -4, 0],
scale: [1, 1.02, 1],
transition: {
y: {
duration: 4.5,
repeat: Infinity,
repeatType: 'reverse',
ease: 'easeInOut',
},
scale: {
duration: 4.5,
repeat: Infinity,
repeatType: 'reverse',
ease: 'easeInOut',
},
delay: 1.5,
},
}
: {}
}
viewport={{ once: true }}
/>
);
};

export default BeigeCat;
64 changes: 64 additions & 0 deletions src/components/landing/FrontNotebook.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
'use client';

import { motion } from 'framer-motion';

import { useIsLargeScreen } from '../../hooks/useLargeScreen';

/**
* 앞 노트북 컴포넌트
* 1920px 이상에서만 애니메이션 작동
*/
const FrontNotebook = () => {
const isLargeScreen = useIsLargeScreen();

return (
<motion.img
src="/front_notebook.png"
alt="앞 노트북"
className="absolute left-[263px] top-[298px] h-[51px] w-[40px] md:left-[456px] md:top-[269px] md:h-[67px] md:w-[53px] lg:left-[598px] lg:top-[280px] lg:h-[100px] lg:w-[79px]"
initial={
isLargeScreen ? { opacity: 0, scale: 0.8 } : { opacity: 1, scale: 1 }
}
animate={
isLargeScreen
? {
opacity: 1,
scale: 1,
transition: {
type: 'spring',
stiffness: 100,
damping: 15,
delay: 0.9,
},
}
: { opacity: 1 }
}
whileInView={
isLargeScreen
? {
filter: ['brightness(1)', 'brightness(1.15)', 'brightness(1)'],
scale: [1, 1.02, 1],
transition: {
filter: {
duration: 2.5,
repeat: Infinity,
repeatType: 'reverse',
ease: 'easeInOut',
},
scale: {
duration: 2.5,
repeat: Infinity,
repeatType: 'reverse',
ease: 'easeInOut',
},
delay: 1.9,
},
}
: {}
}
viewport={{ once: true }}
/>
);
};

export default FrontNotebook;
Loading
Loading