Skip to content

Commit

Permalink
feat: projects 페이지 애니메이션 추가 및 디자인 변경
Browse files Browse the repository at this point in the history
  • Loading branch information
2hanbyeol1 committed Feb 17, 2025
1 parent 1d4b739 commit 6bc251a
Show file tree
Hide file tree
Showing 24 changed files with 344 additions and 181 deletions.
Binary file added public/image/skills/css.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
Binary file added public/image/skills/node.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/image/skills/styled.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/image/skills/supabase.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/image/skills/tailwind.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/image/skills/tanstack-query.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/image/skills/typescript.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/image/skills/zustand.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
47 changes: 47 additions & 0 deletions src/app/_components/Checkbox/Checkbox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { ComponentProps } from 'react';

interface CheckboxProps extends Omit<ComponentProps<'input'>, 'size'> {
text: string;
size: 'sm' | 'lg';
}

function Checkbox({
className,
checked,
onChange,
text,
size = 'sm',
}: CheckboxProps) {
const inputVariantBySize = {
sm: '',
lg: '',
};
const inputVariantByChecked = {
checked: 'bg-primary/90',
unchecked: 'bg-primary/25 group-hover:bg-primary/40',
};
const labelVariant = {
sm: 'text-sm',
lg: 'text-lg',
};

return (
<label className={`group flex cursor-pointer items-center ${className}`}>
<input
className={`absolute -z-50 opacity-0`}
type="checkbox"
checked={checked}
onChange={onChange}
/>
<div
className={`relative mr-2 h-5 w-5 rounded-[4px] ${inputVariantBySize[size]} ${checked ? inputVariantByChecked.checked : inputVariantByChecked.unchecked}`}
>
{checked && (
<span className="absolute left-1 top-1 z-10 h-2 w-3 -rotate-45 border-b-[3px] border-l-[3px] border-white"></span>
)}
</div>
<span className={`${labelVariant[size]}`}>{text}</span>
</label>
);
}
export default Checkbox;
4 changes: 2 additions & 2 deletions src/app/_components/SlideInView/SlideInView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ function SlideInView({
opacity: 1,
translateY: 0,
transition: {
duration: 1,
duration: 0.7,
delay,
},
}}
viewport={{ once: true, amount: 0.9 }}
viewport={{ once: true, amount: 0.3 }}
>
{children}
</motion.div>
Expand Down
76 changes: 76 additions & 0 deletions src/app/_data/experience.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
export interface TExperience {
title: string;
subtitle: string;
duration: [Date, Date];
description?: string;
activities: string[];
achievements: string[];
links?: { text: string; url: string }[];
}

export const EXPERIENCE_CONTENTS: TExperience[] = [
{
title: 'SearcHRight AI',
subtitle: '프론트엔드 개발 인턴',
duration: [new Date('2024.09'), new Date('2024.11')],
description:
'서치라이트는 숨어있는 ‘인재’들의 역량을 분석하여 회사에 가장 적합한 후보자를 추천하는 솔루션을 개발하는 스타트업입니다.',
activities: ['<b>랜딩 페이지 개발</b>', '블로그 작성 및 업로드'],
achievements: [
'<b>실무</b> 경험',
'<b>초기 스타트업</b> 경험',
'<b>HR 도메인</b>에 대한 이해',
],
links: [
{
text: '[포트폴리오] 프로젝트 자세히 보기',
url: '/projects/searchright',
},
{
text: '[블로그] 서치라이트 일지',
url: 'https://velog.io/@2hanbyeol1/series/%EC%84%9C%EC%B9%98%EB%9D%BC%EC%9D%B4%ED%8A%B8',
},
],
},
{
title: '내일배움캠프',
subtitle: 'React 5기',
duration: [new Date('2024.04'), new Date('2024.08')],
activities: [
'매일 <b>12시간 집중 학습</b> 및 TIL 작성',
'<b>프로젝트 11개</b> 진행 (개인 5개, 팀 6개, 리더 3회)',
],
achievements: [
'<b>React 주요 개념</b> 습득',
'팀원들을 이끌어 <b>프로젝트를 기한 내에 완성</b>하는 능력 기름',
'<b>디자이너, 개발자와의 협업 및 소통 능력</b> 향상',
],
},
{
title: '오픈소스 컨트리뷰션 아카데미',
subtitle: 'Git 활용 및 React',
duration: [new Date('2023.07'), new Date('2023.08')],
activities: [
'<b>Git, React, React Native 기초 학습</b>',
'오픈소스 <b>`hyper`의 코드와 패키지 구조 분석</b>',
],
achievements: [
'오픈소스에 기여할 수 있는 역량을 키우기 위한 목표 수립',
' → 아카데미에서의 학습을 바탕으로 <b>오픈소스 가상 화이트보드 `excalidraw`에 기여</b>',
],
links: [
{
text: '[블로그] Git 학습 내용',
url: 'https://velog.io/@2hanbyeol1/series/Git',
},
{
text: '[Github] Excalidraw - (CLOSED) 한국어용 손글씨 폰트 추가',
url: 'https://github.com/excalidraw/excalidraw/pull/8531',
},
{
text: '[Github] Excalidraw - (MERGED) PNG로 내보낼 때 모든 파일로 저장되는 버그 수정',
url: 'https://github.com/excalidraw/excalidraw/pull/8946',
},
],
},
];
17 changes: 14 additions & 3 deletions src/data/project.ts → src/app/_data/project.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { TSkillImgFileName } from './skill';

export interface TProject {
id: string;
title: string;
description: string | string[];
duration: [Date, Date];
mainSkills: TSkillImgFileName[];
isMain: boolean; // 주요 프로젝트 여부
bgDark?: boolean;
}
Expand All @@ -13,20 +16,23 @@ export const PROJECT_CONTENTS: TProject[] = [
title: 'Liberty 52 Frame',
description: ['커스텀 스피커, Liberty 52', '쇼핑몰 및 관리자 서비스'],
duration: [new Date('2023.03'), new Date('2023.12')],
mainSkills: ['react', 'javascript', 'styled-component', 'recoil'],
isMain: true,
},
{
id: 'egomogo',
title: 'egomogo',
description: ['명지대 인근 음식점 랜덤 추첨', '모바일 애플리케이션'],
duration: [new Date('2023.07'), new Date('2023.08')],
mainSkills: ['react-native', 'javascript', 'styled-component', 'recoil'],
isMain: true,
},
{
id: 'chat-chat',
title: '챗챗',
description: ['Web Socket 학습용', '채팅 애플리케이션'],
duration: [new Date('2023.10'), new Date('2023.12')],
mainSkills: ['react', 'javascript', 'styled-component', 'node'],
isMain: false,
bgDark: true,
},
Expand All @@ -35,6 +41,7 @@ export const PROJECT_CONTENTS: TProject[] = [
title: '무비무비',
description: '영화 검색 웹페이지',
duration: [new Date('2024.05'), new Date('2024.05')],
mainSkills: ['html', 'css', 'javascript'],
isMain: false,
bgDark: true,
},
Expand All @@ -43,28 +50,32 @@ export const PROJECT_CONTENTS: TProject[] = [
title: '문화열차',
description: '공연 예약 시스템',
duration: [new Date('2024.07'), new Date('2024.07')],
mainSkills: ['next', 'typescript', 'tailwind-css', 'zustand', 'supabase'],
isMain: false,
},
{
id: 'oosie',
title: 'oosie',
description: ['기록, 챌린지를 통한', '헬스 케어 커뮤니티'],
duration: [new Date('2024.07'), new Date('2024.08')],
mainSkills: ['next', 'typescript', 'tailwind-css', 'zustand', 'supabase'],
isMain: true,
bgDark: true,
},
{
id: 'searchright',
title: 'SearcHRight AI 랜딩페이지',
description: ['인재 추천 솔루션,', '서치라이트의 랜딩페이지'],
title: '서치라이트',
description: ['인재 추천 솔루션,', '서치라이트를 소개하는 랜딩페이지'],
duration: [new Date('2024.09'), new Date('2024.11')],
mainSkills: ['next', 'typescript', 'tailwind-css', 'zustand'],
isMain: true,
},
{
id: 'portfolio-2',
title: '포트폴리오',
description: ['프론트개발자 이한별,', '포트폴리오 웹사이트'],
description: ['FE 개발자 이한별,', '포트폴리오 웹사이트'],
duration: [new Date('2025.01'), new Date('2025.02')],
mainSkills: ['next', 'typescript', 'tailwind-css', 'zustand'],
isMain: true,
},
];
82 changes: 82 additions & 0 deletions src/app/_data/skill.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
export type TSkillImgFileName =
| 'html'
| 'css'
| 'javascript'
| 'typescript'
| 'react'
| 'react-native'
| 'next'
| 'aws'
| 'git'
| 'recoil'
| 'zustand'
| 'tanstack-query'
| 'styled-component'
| 'tailwind-css'
| 'node'
| 'supabase';

export interface TSkill {
title: string;
descriptions: string[];
imgFileName: TSkillImgFileName;
}

export const SKILL_CONTENTS: TSkill[] = [
{
title: 'HTML & CSS',
descriptions: [
'<b>다양한 디바이스 환경에 최적화된 레이아웃과 CSS 애니메이션</b>을 구현합니다.',
'시맨틱 태그를 사용하여 <b>접근성과 SEO 최적화를 고려</b>한 마크업을 작성합니다.',
],
imgFileName: 'html',
},
{
title: 'JS & TS',
descriptions: [
'<b>ES6 문법과 최신 자바스크립트</b> 기능들을 활용하여 효율적인 코드를 작성합니다.',
'타입 안전성을 확보하기 위해 <b>Typescript를 적극 활용</b>합니다.',
'코드의 <b>가독성과 유지보수성</b>을 높입니다.',
],
imgFileName: 'javascript',
},
{
title: 'React',
descriptions: [
'<b>재사용 가능한 컴포넌트 및 모듈화</b>를 통해 유지보수성이 뛰어난 애플리케이션을 구현합니다.',
'<b>다양한 React Hook을 활용</b>하여 기능을 개발할 수 있습니다.',
'<b>React Native</b>를 이용한 크로스 플랫폼 앱 개발 프로젝트를 진행한 적이 있습니다.',
],
imgFileName: 'react',
},
{
title: 'Next.js',
descriptions: [
'<b>CSR, SSR, SSG, ISR의 동작원리에 대해 이해</b>하고 있습니다.',
'Next.js의 라우팅, API 라우트, 이미지 최적화 등 다양한 기능을 활용하여 <b>성능 및 SEO에 최적화된 웹 애플리케이션을 개발</b>합니다.',
],
imgFileName: 'next',
},
{
title: 'Deploy',
descriptions: [
'<b>AWS S3와 CloudFront로 정적 페이지를 배포</b>한 경험이 있습니다.',
'GitHub Actions를 통해 <b>CI/CD 파이프라인을 구축하여 배포 프로세스를 자동화</b>한 경험이 있습니다.',
],
imgFileName: 'aws',
},
{
title: 'Git',
descriptions: [
'Git과 GitHub를 활용한 <b>코드 버전 관리 및 협업 경험</b>을 바탕으로, 효과적인 팀 커뮤니케이션과 프로젝트 관리를 수행합니다.',
],
imgFileName: 'git',
},
{
title: '전역 상태 관리',
descriptions: [
'상태 관리 라이브러리로 <b>Recoil, Zustand, Tanstack Query</b> 등을 사용한 경험이 있습니다.',
],
imgFileName: 'recoil',
},
];
56 changes: 33 additions & 23 deletions src/app/_sections/ExperiencesSection/Experience.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TExperience } from '@/data/experience';
import { TExperience } from '@/app/_data/experience';
import { parseDateToYYYYMM } from '@/utils/util';
import Image from 'next/image';
import Link from 'next/link';
Expand All @@ -17,54 +17,64 @@ function Experience({
experience: TExperience;
}) {
return (
<div className="flex flex-col gap-10">
<div className="flex flex-col gap-12">
<div>
<div className="mb-2.5 text-base text-dark2">
<div className="mb-2 text-xl font-medium [&>span]:text-dark2">
<span>{parseDateToYYYYMM(duration[0])}</span>
<span> - </span>
<span>{parseDateToYYYYMM(duration[1])}</span>
</div>
<div className="relative mb-5">
<div className="relative">
<Image
width={20}
height={20}
width={32}
height={32}
src="/image/star.png"
alt="별"
className="absolute right-0 -z-10 tablet:-left-10 tablet:top-1"
className="absolute right-0 -z-10 tablet:-left-14 tablet:top-1"
/>
<h3 className="mb-1 text-[1.7rem] font-bold">{title}</h3>
<h4 className="text-lg font-medium">{subtitle}</h4>
<h3 className="mb-1.5 text-3xl font-bold">{title}</h3>
<h4 className="text-2xl font-medium">{subtitle}</h4>
</div>
<p className="leading-normal">{description}</p>
{description ? (
<p className="mt-4 text-lg leading-normal">&gt; {description}</p>
) : (
<></>
)}
</div>
<div>
<h5 className="mb-2 font-semibold">주요 활동</h5>
<h5 className="mb-2 text-xl font-bold">주요 활동</h5>
<ul>
{activities.map((activity, idx) => (
<li key={`act-${title}-${idx}`} className="leading-relaxed">
{activity}
</li>
<li
key={`act-${title}-${idx}`}
className="text-lg [&>b]:font-semibold"
dangerouslySetInnerHTML={{ __html: activity }}
/>
))}
</ul>
</div>
<div>
<h5 className="mb-2 font-semibold">성과</h5>
<h5 className="mb-2 text-xl font-bold">성과</h5>
<ul>
{achievements.map((achievement, idx) => (
<li key={`ach-${title}-${idx}`} className="leading-relaxed">
{achievement}
</li>
<li
key={`ach-${title}-${idx}`}
className="text-lg [&>b]:font-bold [&>b]:text-primary/80"
dangerouslySetInnerHTML={{ __html: achievement }}
/>
))}
</ul>
</div>
{links && links.length > 0 ? (
<div>
<ul>
<h5 className="mb-2 text-xl font-bold">참조</h5>
<ul className="flex flex-col gap-1">
{links.map((link, idx) => (
<li key={`lin-${title}-${idx}`}>
<Link href={link.url} className="text-primary underline">
{link.text}
</Link>
<li
key={`lin-${title}-${idx}`}
className="text-lg text-dark2 underline"
>
<Link href={link.url}>{link.text}</Link>
</li>
))}
</ul>
Expand Down
Loading

0 comments on commit 6bc251a

Please sign in to comment.