내 역할
내가 혼자 다함.
팀
이의찬
타임라인
1주일, 2024년 9월 시작
사용 기술
Next.js, TypeScript, Figma, Tailwindcss, Redux, middleware, i18n, Framer
개요
마이사이트는 포트폴리오 웹사이트입니다. Next.js를 통해서 구현하였고, 모든 디자인을 Tailwindcss로 구현하였고 반응형을 고려하여 디자인되었습니다. 컴포넌트 움직임들을 Framer Motion을 사용해서 구현하였습니다.
Redux를 활용해 다크모드 상태를 전역으로 관리합니다. middleware를 통한 동적 라우팅과 함께 i18n 라이브러리를 활용해 한영 전환 기능을 구현하였습니다.
HIGHLIGHTS
개인 포트폴리오 웹사이트로 프로젝트의 설명과 개인 이력서를 제공합니다.



DESIGN
디자인할 때 고려한 요소들.
컴포넌트화
디자인을 시작할 때부터, 재사용성을 고려하여 컴포넌트화를 적용하였습니다. 컴포넌트화를 통해 개발과정에서 최적화를 추구하였습니다.
반응형 디자인
화면 크기에 따라 변하는 디자인을 적용하였습니다. 코드에서는 Tailwindcss을 사용하여 디자인을 적용하였습니다.
디자인에 따라서 애니메이션이 바뀌는 부분은 window.addEventListener를 통해 감지해주었습니다.
useEffect(() => {
const checkScreenSize = () => {
setIsSmallScreen(window.innerWidth < 640); // 640px is the 'sm' breakpoint in Tailwind
};
checkScreenSize();
window.addEventListener('resize', checkScreenSize);
return () => window.removeEventListener('resize', checkScreenSize);
}, []);컴포넌트 같은 경우는 마우스를 올리면 설명이 표시됩니다. 하지만 핸드폰에서는 마우스를 올릴 수가 없기 때문에 다른 애니메이션을 적용해주기 위해. 사이즈를 체크해주었습니다.
TableOfContents.tsx
ProjectBox.tsx
LANGUAGE
한영 전환은 어떻게 해야할까?
middleware
동적라우팅을 사용하여 locale에 따라 ko, en을 감지합니다. langmodeswitch가 눌리면 새로운 경로로 설정하고, middleware는 그 신호를 가로채 알맞는 경로로 바꾸어줍니다.
i18n
언어 변환 등 국제화처리를 가능하게 해주는 i18n 라이브러리를 사용하였습니다. locale 설정에 따라 다른 message 파일을 불러옵니다.
import createMiddleware from 'next-intl/middleware';
export default createMiddleware({
locales: ['en', 'ko'],
defaultLocale: 'en'
});
export const config = {
matcher: ['/((?!api|_next|.*\..*).*)']
};middleware를 통해 locale의 값을 감지합니다.
middleware.ts
i18n.ts
layout.tsx
HomePage.tsx
TABLECONTENTS
수많은 페이지에서 목차를 찾아가는 방법.
Section
IntersectionObserver를 사용해 각 section을 감지하였습니다. 클릭하면 scrollIntoView를 사용해 각 section으로 이동합니다.
useEffect(() => {
const observerOptions = {
root: null,
rootMargin: '-20% 0px -20% 0px',
threshold: 0.1
};
const observerCallback = (entries: IntersectionObserverEntry[]) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
setActiveSection(entry.target.id);
}
});
};
const observer = new IntersectionObserver(observerCallback, observerOptions);
document.querySelectorAll('section[id]').forEach((section) => {
observer.observe(section);
});
const handleScroll = () => {
//...
};
window.addEventListener('scroll', handleScroll);
return () => {
observer.disconnect();
window.removeEventListener('scroll', handleScroll);
};
}, [sections]);각 section의 위치를 확인하고, 현재 화면에 보이는 section이 무엇인지 확인합니다.
TableOfContents.tsx
scroll
STATE
페이지에서 Redux를 사용해 상태관리를 하는 방법.
다크모드
darkModeSwitch를 누르면 dispatch를 사용해 redux에 값을 저장합니다. provider를 통해 locale의 값을 확인하고, dark 모드 값을 변경합니다.
export default function DarkModeSwitch() {
const dispatch = useDispatch();
const isDarkMode = useSelector((state: RootState) => state.theme.isDarkMode);
const handleToggle = () => {
dispatch(toggleTheme());
};
return (
<motion.button
onClick={handleToggle}
className="w-14 h-8 p-1 bg-white/5 rounded-full shadow border-2 border-white/30 backdrop-blur-[15px] relative flex items-center"
transition={{ duration: 0.3 }}
>
//...
</motion.div>
</motion.button>
);
}다크모드 버튼을 누르면 dispatch를 통해 값을 업데이트합니다.
DarkModeSwitch.tsx
ANIMATION
부드러운 사용자 경험성 증가.
Framer
마우스를 올리면 컴포넌트가 살짝 올라오는 것, Nav 바 이동 popop 컴포넌트 표시 등을 Framer를 사용해 움직임을 부드럽게 구현했습니다.
<motion.div
initial={{ opacity: 1, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.9 }}
transition={{ duration: 0.3 }}
className='fixed inset-0 z-50 flex items-center justify-center p-4'
onClick={onClose}
>
//...
</motion.div>컴포넌트가 나타나고 사라지는 애니메이션을 적용하였습니다.
ProjectBox.tsx
