npm run build
이 포트폴리오는 React를 기반으로 구축되었으며, 2024년 동안 제가 진행했던 다양한 프로젝트들을 선보이는 공간입니다.
Git: https://github.com/alrang0929/alrang_portfolio/tree/main/portfolio_2024/portfolio_alrang
link : https://alrang-portfolio.vercel.app/
이 포트폴리오는 단순히 프로젝트 결과물만을 나열하는 것이 아니라, 사용자와의 상호 작용을 통해 몰입도를 높이고 제가 가진 기술과 경험을 효과적으로 전달하는 데 중점을 두었습니다.
-
반응형 디자인: 데스크탑, 태블릿, 모바일 등 다양한 기기에서 최적화된 화면을 제공합니다.
-
인터랙션 슬라이드:
Intersection Observer API
를 사용하여 스크롤에 따라 콘텐츠가 부드럽게 나타나는 애니메이션 효과를 적용했습니다. -
마우스 인터랙션:
useState
,useEffect
,useRef Hook
을 사용하여 타이틀 영역에 마우스hover
시 따라 랜덤한 이미지가 마우스 포인터를 따라다니는 애니메이션 구현 -
랜덤 이미지 생성 및 추가(handleMouseEnter)
handleMouseEnter
함수는 마우스가title-wrap
요소에 진입할 때 실행됩니다.Math.random()
을 사용하여 1부터 4까지의 랜덤한 숫자를 생성하고, 이를 이용하여 랜덤 이미지 파일 경로를 생성합니다.setImages([...images, randomImage])
를 사용하여 기존 images 배열에 새로운 랜덤 이미지를 추가합니다.useState Hook
을 통해 images 배열이 업데이트되면 컴포넌트가 다시 렌더링되어 새로운 이미지가 화면에 나타납니다.
const handleMouseEnter = () => {
const randomImage = `/images/random_img/random_0${
Math.floor(Math.random() * 4) + 1
}.jpg`;
setImages([...images, randomImage]);
};
- 2. 마우스 위치 추적 및 이미지 이동
handleMouseMove
함수는 마우스가 title-wrap 요소 내에서 움직일 때마다 실행됩니다.titleWrapRef.current.getBoundingClientRect()
를 사용하여title-wrap
요소의 위치와 크기 정보를 가져옵니다.isWithinTitleWrap
변수를 통해 마우스 좌표가title-wrap
요소 내부에 있는지 확인합니다.- 마우스 좌표가
title-wrap
요소 내부에 있는 경우,imageRef.current.style.top과 imageRef.current.style.left
속성을 업데이트하여 이미지를 마우스 위치에 따라 이동시킵니다. - 마우스 좌표가
title-wrap
요소 외부에 있는 경우,imageRef.current.style.display = "none";
을 사용하여 이미지를 숨깁니다.
3. useEffect에서 이벤트 리스너를 등록 및 제거합니다.
메모리 누수 방지와 불필요한 이벤트 처리를 막기 위하여 useEffect Hook
에서 mousemove
이벤트를 등록하고 컴포넌트가 언마운트 될때 이벤트 리스너를 제거합니다.
const handleMouseMove = (event) => {
if (imageRef.current && titleWrapRef.current) {
const titleWrapRect = titleWrapRef.current.getBoundingClientRect();
// 마우스 좌표가 title-wrap 박스 안에 있는지 확인
const isWithinTitleWrap =
event.clientX > titleWrapRect.left &&
event.clientX < titleWrapRect.right &&
event.clientY > titleWrapRect.top &&
event.clientY < titleWrapRect.bottom;
if (isWithinTitleWrap) {
// title-wrap 박스 안에 있을 때만 이미지 위치 업데이트
imageRef.current.style.top = `${
event.clientY - titleWrapRect.top - imageRef.current.offsetHeight / 2
}px`;
imageRef.current.style.left = `${
event.clientX - titleWrapRect.left - imageRef.current.offsetWidth / 2
}px`;
} else {
// title-wrap 박스 밖에 있을 때 이미지 숨기기 (선택 사항)
imageRef.current.style.display = "none";
}
}
};
useEffect(() => {
window.addEventListener("mousemove", handleMouseMove);
return () => {
window.removeEventListener("mousemove", handleMouseMove);
};
4. 이미지 렌더링 및 Ref 연결
images 배열을 map
함수를 사용하여 순회하며 각 이미지를 렌더링합니다.
ref={index === images.length - 1 ? imageRef : null}
를 사용하여 가장 최근에 추가된 이미지에만 imageRef
를 연결합니다. 이를 통해 마지막 이미지만 마우스를 따라다니도록 구현했습니다.
<div
className="title-wrap"
ref={titleWrapRef}
onMouseEnter={handleMouseEnter}
onMouseMove={handleMouseMove} // title-wrap 내에서도 마우스 이동 추적
>
{images.map((image, index) => (
<div
className="random-img"
key={index}
style={{ zIndex: 9 }}
// 이미지를 절대 위치로 설정
>
<img
src={image}
alt={`Random Image ${index}`}
ref={index === images.length - 1 ? imageRef : null} // 가장 최근 이미지에 ref 연결
/>
</div>
- 프로젝트 상세 페이지: 각 프로젝트의 상세 정보, 기술 스택, GitHub 링크 등을 제공하며, 프로젝트의 핵심 내용을 명확하게 전달합니다.
- React: 성능 최적화와 컴포넌트를 통한 유지보수 최적화를 위해 React를 선택했습니다.
컴포넌트의 재사용성과
useState
,useContext hook
을 사용하여 컴포넌트 상의 상태값을 효율적으로 관리했습니다.
Main컴포넌트에서useState hook
을 사용하여 화면 크기, 호버 상태, 애니메이션 상태등을 관리하고useConstext Hook
을 사용하여WorksDetail
데이터를 공유했습니다. - SCSS:
CSS
의 구조화와 변수,mixin
을 통한 css 관리가 용이하여 해당 기술을 선택했습니다.
- Lighthouse를 통한 퍼포먼스 진단 결과
- 성능 저하의 원인: 불필요한 렌더링 요소와 이미지 관련 속도저하가 주된 원인으로 지적됐습니다.
개선 방법으로는 이미지 용량의 최적화와,
css
와JavaScript
의 분할을 통한 필수 요소만 렌더링을 통하여 해결해나갈 예정입니다.
- 1. TopArea의 home버튼의 이동기능 문제 발생
삼항연산자를 이용하여
isHome
의 상태에 따라Works
와Home
의 링크, 버튼 내 텍스트와 링크 토글기능 구현
그러나 해당 버튼이worksDetail
컴포넌트에 들어갈 경우works
와worksDetail path
를 토글하는 문제 발생
<header id="gnb-area">
<nav>
{!isHome && <Link to="/Works">WORKS</Link>}
<button className="fill-button2" onClick={goLink}>
{isHome ? "Works" : "Home"}
</button>
</nav>
</header>
당시 코드를 다시 보니 중복 라우팅으로 인한 충돌 때문에 해당 문제가 발생했을거라 판단하여
{!isHome && <Link to="/Works">WORKS</Link>}
해당 코드 제거하여 문제 해결
-
2. Intersection Observer API로 제작한 슬라이드 애니메이션의 오류
-
포트폴리오 피드백을 받는 중
Intersection Observer API
를 사용하는 구간에서ios
와android
환경 이용자의 화면이 다른 것을 발견.
구글링을 해봐도 별다른 해결책이 없어props
로 받아오던width
값과height
값을 직접적으로 입력하니 문제가 해결됨
따라서Intersection Observer API
를 사용한 컴포넌트의 기존버전<SlidingText text={"connecting"} font={"ephesis-regular"+" "+"fz-large"} fontsize={12} delay={0} />
에서
props
로 받아오는fontsize
와 컴포넌트 내 사이즈에 관련된 코드를 제거하고, 사이즈는 컴포넌트를 사용하는 곳에서 직접적으로 관리하게 하여 해결함.
src/
├── components/ // 재사용 가능한 UI 컴포넌트
│ ├── data/ // 공통적으로 사용하는 데이터(skill, works등)
│ ├── func/ // 공통함수(스크롤애니 함수, WorksPJList 컴포넌트)
│ ├── Layout/ // 레이아웃 컴포넌트 (Header, Footer 등)
│ ├── Plugin/ // 플러그인 컴포넌트 관리(Swiper 등)
│ └── Page/ // 페이지별 컴포넌트 (Home, Projects 등)
├── CSS/ // 스타일 관련 파일
│ └── common/ // 공통 CSS, 초기화 CSS
│ └── variables/ // 일관성을 위한 변수셋팅
├── index/ // Router 관리 js