DYO 공부하는 블로그
프론트엔드 개발자 포트폴리오 사이트 만들기 본문
예전부터 개인 포트폴리오 사이트를 웹페이지로 만들고 싶은 욕심이 있었다. 그래서 세달 전에 레포 파고, 배포 하고, 도메인까지 연결해뒀었던 것이 있어 그걸 개선해 사용해보려고 한다.
세 달 전에 처음 레포를 파고, 배포할 때는 두근거리면서 시작했지만 프로젝트 너무 일정이 바빴고, 포트폴리오 결과물이 마음에 안 들었다. 때문에 추후에 시간이 남을 때 리뉴얼을 해보기로 계획했고, 지금에서야 리뉴얼 작업을 하기로 결정했다.
당시에 결과로 만들었던 것은 아래와 같다.


새로 개선해야겠다고 느꼈던 부분

1. 히어로 섹션의 높이가 애매하다. 반응형으로 100vh로 화면을 꽉 채우는 설계로 변경
2. About 섹션에 내 소개가 부족한 것 같아 좌우명이나 개발철학과 같은 것들 짧게 써보기
3. 인터렉티브한 요소가 너무 적어 사이트가 심심함.
4. 오렌지 + 화이트가 당시에는 예쁘다고 생각해 골랐는데 색상 대비가 나쁜 것 같아 수정해야 함.
5. 아무리 썸네일이더라도 프로젝트를 어느 정도 설명해야 하는데 설명 섹션이 너무 작은 것 같음.
6. 프로젝트 디테일 페이지가 없는데, 프로젝트 디테일을 추가해야 함.
그냥 진행해봤다. 그런데,
처음에는 있던 프로젝트에서 수정하는 방향으로 진행하려고 했다. 그래서 작업 첫날에는 기존 구조에서 수정했는데, 폴더 구조도 마음에 안 들었고, 기존에 하던 것에 디자인 문서도 없이 기존 것 위에 덮는 방법으로 작업했더니 진행도 너무 더디고 기대했던 디자인대로 안 나왔다.

그래서 프로젝트 아키텍쳐와 디자인을 엎고 새로 구현하기로 결심했다.
디자인부터 새로 짜자
오렌지색 + 흰색이 가독성이 떨어졌고, 밝은 톤 색 쓰는 게 어려우니 내 디자인 수준에서 쉽게 할 수 있는 단색 조합으로 개선하려고 한다.
애초에 디자인 문서를 안 짜고 작업해서 이런 상황이 발생한 것 같다. 디자인을 잘 하지는 않지만 Figma를 이용해 디자인 문서를 작성해줬다.

(전체를 검은 색으로 구성하니 너무 단순해 보이는 면이 있어 구현하면서 흰색으로 개선한 섹션도 있다.)
파일 구조도 개선하자.
아예 번들링도 TurboPack으로 변경하고 Next 기반 프로젝트로 변경할지도 고민했으나 이미 프로젝트에 배포 설정, 도메인 설정 다 되어 있는 상태인데다가, 프로젝트 크기도 별로 크지 않아 Next로 마이그레이션하기는 품이 너무 커질 것 같아 폴더 구조만 개선했다.

기술선정
React - 익숙하고 편하니 선택. constants 기반 정적 데이터 관리하는 데 선언적으로 아이템 관리하기도 쉽고, 에코시스템도 잘 되어 있어 js 바닐라로 하는 것보다 훨씬 나았다.
TailwindCSS - 모듈 CSS로 번들링해 관리하는 것보다 React 코드 내에서 관리하는 것이 명확하고 가독성도 좋아 선택
Framer Motion - Framer를 계속 써보고 싶었는데, 이상하게 연이 없었다. GSAP보다 선언적인 방식으로 애니메이션 구현을 위해 사용.
react-router-dom - 라우터를 직접 구현하기보다 간편하게 관리할 수 있는 react-router-dom을 이용해 라우팅 관리
드디어 작업 재시작, 완료
기존에 작성했던 부분에 공통으로 이용할 수 있는 것 같은 부분은 그대로 가져다 쓰고, 디자인 자체를 변경해야 했다. 기존 삽질하던게 2일 정도, 코드 작성하고 디테일 페이지 작업까지 마치는 데는 5일 정도 걸렸다. 미리 이력서를 작성해둬서, 소스는 그쪽에서 가져오면 돼서 얼마 안 걸렸다.
제일 어려웠던 부분은 이벤트가 없고 백엔드 서버가 없는 정적인 코드로 작성하려고 constants에 모든 프로젝트 정보를 담아 아이템으로 뿌려줬었는데, 이 데이터 관리하는 게 좀 피곤했다.
constant.ts
export const PROJECTS: ProjectDetail[] = [
{
thumbnail: relifeThumb,
title: "Re:Life",
desc: "AI 평행우주 시나리오 조회 플랫폼",
period: "2025-09-15 ~ 2025-10-16 (4주)",
teammate: "프론트엔드팀 3명-백엔드팀 5명 (팀장)",
background:
'인생의 중요한 선택에서 "만약 그때 다른 선택을 했다면?" 이라는 궁금증에서 출발한 프로젝트입니다. 사용자의 성향과 그 시점의 선택에 따라 평행우주 인생 시나리오를 시뮬레이션하는 서비스입니다.',
architecture: relifeArchitecture,
teamFlag: [
"백엔드팀과 원팀으로 협업해 서로 윈윈하기",
"결정은 단순히 이야기로 끝나지 않고, 문서를 남겨 명확히 정의하기",
"예의는 지키되, 의견 제시를 망설이지 않기",
],
skills: [
"Typescript",
"React",
"Next.js",
"TailwindCSS",
"GSAP",
"react-hook-form",
"zod",
"axios",
"Tanstack Query",
],
skillReason: [
{
skill: "Typescript",
reason:
"코드 작성 단계에서 타입을 걸러줘 안정적인 코드 작성이 가능한 장점이 있고, 사용하는 라이브러리들도 모두 Type에 강점이 있는 라이브러리였기 때문에 연계성이 좋다고 판단했다.",
},
{
skill: "React",
reason:
"팀원 모두 팀 활동을 거치면서 컴포넌트 기반의 작업의 재사용성과 작업 분리에 대해서 잘 이해하고 있었기 때문에 좋은 선택지라고 생각했다.",
},
{
skill: "Next.js",
reason:
"백엔드에서 많은 데이터를 받아오고 많은 페이지를 상정했기 때문에 서버 컴포넌트, 최적화 기능, 라우팅 구조, SEO에서 큰 장점이 있다고 생각했다. 때문에 Next를 사용하기로 결정하게 되었다.",
},
{
skill: "TailwindCSS",
reason:
"CSS 프레임워크로 빠른 스타일링과 일관된 디자인 시스템 구축을 위해 선택했다.",
},
{
skill: "GSAP",
reason:
"팀원들이 익숙한 라이브러리라 온보딩이 쉽다고 판단했고, 라이브러리의 다양한 기능이 무료로 풀렸기 때문에 강력한 기능들을 사용해보고 싶은 욕심이 있었다. 스크롤 제어에서의 정밀함이 장점이라고 생각해 선택했다.",
},
{
skill: "react-hook-form",
reason:
"유저 입력이 많은 서비스였기 때문에 유효성 검증에 react-hook-form을 이용하면 유저 입력 검증을 쉽게 구현할 수 있다고 생각해 선택했다.",
},
{
skill: "Zod",
reason:
"백엔드 협업에서 데이터 구조는 빈번하게 있을 수 있다고 판단해 런타임 타입 검증이 필요하다고 생각해 Zod를 이용하려고 했다.",
},
{
skill: "axios",
reason:
"간단하게 사용할 수 있는 fetch, 공용 인스턴스 구현 기능이 백엔드 협업에서 헤더 설정과 인터셉터를 이용한 전처리에 큰 장점이 있다고 느껴 선택했다.",
},
{
skill: "Tanstack Query",
reason:
"클라이언트에서의 캐시 관리에 큰 장점이 있는 데 더해 RSC에서도 dehydrate 패턴으로 데이터를 받아오는 데 큰 장점이 있다고 느껴 선택했다.",
},
],
charged: [
{
title: "프론트엔드 아키텍처 설계 및 팀 리드",
desc: "Feature-based 디렉터리 구조로 코드 응집도 향상, 팀원 온보딩을 위한 기술 문서 작성으로 코드 작성 일관성 개선",
},
{
title: "Next.js App Router + Server Action 기반 인증 시스템 구현",
desc: "로그인 / 회원가입 로직을 숨기기 위해 Server Action으로 처리, Next.js Middleware를 이용한 로그인 상태별 라우팅 그룹 접근 제어",
},
{
title: "CSRF 처리를 위한 서버, 클라이언트 공용 fetcher 구현",
desc: "헤더 설정과 CSRF 인증 실패 재인증 로직을 거치는 공용 fetcher 구현으로 팀 로직 일관 처리",
},
{
title: "Zod + React-Hook-Form 타입 안전 폼 시스템",
desc: "스키마 기반 런타임 유효성 검증 + ts 타입 추론, setValue / control를 이용한 중첩 폼 상태 관리",
},
{
title: "SSR + Tanstack Query 캐시",
desc: "dehydrate 패턴으로 서버/클라이언트 캐시 일관성 유지",
},
{
title: "커뮤니티 페이지 구현",
desc: "게시글 CRUD, 좋아요 구현, 댓글 CRUD, 좋아요 구현, 디테일 페이지 동적 메타데이터 SEO 설정, SSR-CSR 구분 처리",
},
],
achievements: [
"DDD 개념에서 파생된 Feature-based 디렉터리 구조로 코드 탐색 시간 20% 단축",
"팀원 온보딩 지원으로 프로젝트의 모든 클라이언트 요청을 Tanstack Query로 일관성있게 처리",
"헤더 설정과 CSRF 인증 실패 재인증 로직을 거치는 공용 fetcher 구현으로 팀 코드 구현 복잡도 감소",
"커뮤니티 페이지 메타데이터 설정으로 SEO 평가 점수 60점에서 100점으로 상승",
"커뮤니티 페이지 캐싱 적용으로 요청수 40%이상 감소",
],
deployStatus: "백엔드 배포 중단",
deployUrl: "https://relife.kr",
youtubeUrl: "https://youtu.be/9T7L8-4rH9M",
tistoryUrl: "https://yun-engene.tistory.com/106",
},
// .....
이런 형식으로 모든 정적 데이터를 관리했다.
구현 결과
히어로 섹션

원래는 검은색 단색이었다가, 그라데이션으로 개선하고도 페이지가 좀 심심한가 싶었다.
개발자 포트폴리오 사이트의 히어로 페이지는 대부분 애니메이션이 있었던 생각이 나서 기존 작업한 내용을 편집해 그라데이션 scale, translate로 전환되는 애니메이션과 영상 재생을 넣어줬다. 영상 재생 때문에 페이지가 조금 무거워지긴 했는데 작업 내용을 바로 보여줄 수 있는 게 마음에 들었다.
최적화를 위해 영상은 480p로 일괄 처리하고, 배속을 걸고 압축해 영상 시간을 최대한 줄인 뒤에 lazy loading으로 최적화하도록 했다. 백그라운드 위에 상위에 블러를 배치하고 나니 화질 낮은 게 티가 덜 나고, 영상의 동적인 느낌은 그대로 살아 블러 처리했다.
자기소개 섹션

자기소개 섹션은 소개 단 3개가 애니메이션으로 들어오도록 구현했다. 빈공간이 있긴 한데, 나쁘지 않은 것 같아서 남겨 뒀다.
지금 다시 보니까 반응형 작업한다고 grid를 조금 만졌더니 좌우 여백이 삭제된 것 같은데, 수정해야겠다... 자식 요소인데 왜 padding이 빠진 거지?
피어리뷰 섹션

부트캠프 진행하며 받았던 피어리뷰를 아래로 천천히 넘어가는 영화 크레딧처럼 구현했다. 마우스가 올라가면 멈추고, 드래그해서 이전과 이후 데이터를 볼 수 있도록 하고 반복되도록 구현했다.
솔직히 말하면 '내 칭찬'을 적는 게 너무 낯부끄러워서 제일 구현하기 어려웠다.
포트폴리오에 리더십 관련한 내용이 들어갔으니 설득력 있는 내용을 넣기 위해 피어리뷰 원본 내용을 넣고 Modal로 열어서 볼 수 있도록 구현했다.
스킬 섹션

일반적인 포트폴리오의 스킬 섹션을 가져왔다.
다른 사람들에게 리뷰 받았을 때, 필요 없는 것이 너무 많아 보여서 어느정도 쓸 줄 아는지 보였으면 좋겠다고 이야기를 들었다. 그런데 숙련도를 %로 표시하기에는 고민이 너무 많아서. '렉시컬 환경, 실행 컨텍스트 환경을 이해했다'라고 하더라도 JS를 80%로 적는 게 맞는건가? 싶어서 어느 정도 개념을 알고 있는지 텍스트로 덧붙였다.
프로젝트 섹션

프로젝트 섹션은 기본적인 정보에서 hover했을 때 프로젝트 개요와 기술 스택, 내가 구현한 핵심 내용을 볼 수 있도록 애니메이션으로 구현했고, 클릭하면 Detail 페이지로 갈 수 있도록 라우팅 설정을 해 뒀다.
기존에 회고글과 정리글을 열심히 써 둬서 작성하는 것 자체는 어렵지 않았는데, 아무래도 분량이 좀 많다 보니까 그걸 작성하고 정리하는 게 오래 걸렸다.
컨텍트 섹션

마무리로 컨텍트 섹션에 간단한 메세지와 버튼으로 깃허브, 티스토리, 이메일로 연결될 수 있도록 구현했다.
반응형 작업


반응형 화면도 당연히 구현했다. 짧은 기간 목표를 잡긴 했어도, 할 줄 아는 것들은 담아야 한다고 생각해 반응형 작업도 완료해뒀다.
마무리
삽질하고 설계부터 구현까지 9일정도 걸렸던 것 같다. 프로젝트 상세정보에 트러블슈팅도 담으면 참 좋을 것 같은데. 트러블슈팅을 넣으면 분량이 너무 길어져서 안 넣고 있는데, 블로그 글 작성하면서 다시 열어본김에. 하루이틀 작업 해서 작업해서 넣어야겠다.
포트폴리오를 웹페이지로 만들고 PPT는 따로 안 만드려고 했는데, 둘다 있는 게 좋다고 해서 PPT도 만들었다. (한번 할 거 두번했다.) PPT도 기회가 되면 포스팅해봐야겠다.
포폴 페이지를 공유할까 생각했는데, 내 얼굴이 있어서... 링크를 올리지는 못하겠다. 부끄럽다.