diff --git a/app/data/repositories/ProjectRepositoryImpl.ts b/app/data/repositories/ProjectRepositoryImpl.ts new file mode 100644 index 0000000..e375b2b --- /dev/null +++ b/app/data/repositories/ProjectRepositoryImpl.ts @@ -0,0 +1,264 @@ +import { ProjectItem } from '@/app/domain/entities/project.entity' +import { ProjectType } from '@/app/domain/enums/project.enum' +import { ProjectRepository } from '@/app/domain/repositories/project.repo' + +export class ProjectRepositoryImpl implements ProjectRepository { + private readonly mockData: ProjectItem[] = [ + { + title: '오초이스', + description: '오초이스 설명 입니다', + period: '2024.01 ~ 2024.12', + image: 'https://github.com/jaemin-s/jaemin-s/raw/refs/heads/main/image/portfolio/netflix.webp', + type: ProjectType.WORK, + features: [ + { + title: '주요 기능 이름', + description: '기술 설명입니다.', + }, + ], + company: '(주)홈초이스', + links: { demo: 'https://www.netflix.com/kr/', github: '' }, + stackBadges: ['React', 'TypeScript'], + previewUrls: [ + 'https://github.com/jaemin-s/jaemin-s/raw/refs/heads/main/image/portfolio/netflix.webp', + 'https://github.com/jaemin-s/jaemin-s/raw/refs/heads/main/image/portfolio/tving.webp', + ], + stackCategories: [ + { + title: '상태 관리', + stacks: ['Redux', 'React Query'], + }, + { title: '스타일링', stacks: ['Tailwind CSS'] }, + { title: '테스팅', stacks: ['Jest'] }, + { title: '개발 도구', stacks: ['ESLint', 'Prettier'] }, + { title: '빌드 도구', stacks: ['Next.js'] }, + ], + functions: [ + { + title: '주요 기술 1', + description: ['기술 설명입니다', '도입 이유입니다'], + comment: '추가적인 코멘트 자리입니다', + }, + { + title: '주요 기술 2', + description: ['기술 설명입니다', '도입 이유입니다'], + comment: '추가적인 코멘트 자리입니다', + }, + ], + }, + { + title: '오초이스', + description: '오초이스 설명 입니다', + period: '2024.01 ~ 2024.12', + image: 'https://github.com/jaemin-s/jaemin-s/raw/refs/heads/main/image/portfolio/tving.webp', + type: ProjectType.PERSONAL, + features: [ + { + title: '주요 기능 이름', + description: '기술 설명입니다.', + }, + ], + company: '(주)홈초이스', + links: { demo: 'https://www.netflix.com/kr/', github: '' }, + stackBadges: ['React', 'TypeScript'], + previewUrls: [ + 'https://github.com/jaemin-s/jaemin-s/raw/refs/heads/main/image/portfolio/netflix.webp', + 'https://github.com/jaemin-s/jaemin-s/raw/refs/heads/main/image/portfolio/tving.webp', + ], + stackCategories: [ + { + title: '상태 관리', + stacks: ['Redux', 'React Query'], + }, + { title: '스타일링', stacks: ['Tailwind CSS'] }, + { title: '테스팅', stacks: ['Jest'] }, + { title: '개발 도구', stacks: ['ESLint', 'Prettier'] }, + { title: '빌드 도구', stacks: ['Next.js'] }, + ], + functions: [ + { + title: '주요 기술 1', + description: ['기술 설명입니다', '도입 이유입니다'], + comment: '추가적인 코멘트 자리입니다', + }, + { + title: '주요 기술 2', + description: ['기술 설명입니다', '도입 이유입니다'], + comment: '추가적인 코멘트 자리입니다', + }, + ], + }, + { + title: '오초이스', + description: '오초이스 설명 입니다', + period: '2024.01 ~ 2024.12', + image: 'https://github.com/jaemin-s/jaemin-s/raw/refs/heads/main/image/portfolio/netflix.webp', + type: ProjectType.PERSONAL, + features: [ + { + title: '주요 기능 이름', + description: '기술 설명입니다.', + }, + ], + company: '(주)홈초이스', + links: { demo: 'https://www.netflix.com/kr/', github: '' }, + stackBadges: ['React', 'TypeScript'], + previewUrls: [ + 'https://github.com/jaemin-s/jaemin-s/raw/refs/heads/main/image/portfolio/netflix.webp', + 'https://github.com/jaemin-s/jaemin-s/raw/refs/heads/main/image/portfolio/tving.webp', + ], + stackCategories: [ + { + title: '상태 관리', + stacks: ['Redux', 'React Query'], + }, + { title: '스타일링', stacks: ['Tailwind CSS'] }, + { title: '테스팅', stacks: ['Jest'] }, + { title: '개발 도구', stacks: ['ESLint', 'Prettier'] }, + { title: '빌드 도구', stacks: ['Next.js'] }, + ], + functions: [ + { + title: '주요 기술 1', + description: ['기술 설명입니다', '도입 이유입니다'], + comment: '추가적인 코멘트 자리입니다', + }, + { + title: '주요 기술 2', + description: ['기술 설명입니다', '도입 이유입니다'], + comment: '추가적인 코멘트 자리입니다', + }, + ], + }, + { + title: '오초이스', + description: '오초이스 설명 입니다', + period: '2024.01 ~ 2024.12', + image: 'https://github.com/jaemin-s/jaemin-s/raw/refs/heads/main/image/portfolio/tving.webp', + type: ProjectType.WORK, + features: [ + { + title: '주요 기능 이름', + description: '기술 설명입니다.', + }, + ], + company: '(주)홈초이스', + links: { demo: 'https://www.netflix.com/kr/', github: '' }, + stackBadges: ['React', 'TypeScript'], + previewUrls: [ + 'https://github.com/jaemin-s/jaemin-s/raw/refs/heads/main/image/portfolio/netflix.webp', + 'https://github.com/jaemin-s/jaemin-s/raw/refs/heads/main/image/portfolio/tving.webp', + ], + stackCategories: [ + { + title: '상태 관리', + stacks: ['Redux', 'React Query'], + }, + { title: '스타일링', stacks: ['Tailwind CSS'] }, + { title: '테스팅', stacks: ['Jest'] }, + { title: '개발 도구', stacks: ['ESLint', 'Prettier'] }, + { title: '빌드 도구', stacks: ['Next.js'] }, + ], + functions: [ + { + title: '주요 기술 1', + description: ['기술 설명입니다', '도입 이유입니다'], + comment: '추가적인 코멘트 자리입니다', + }, + { + title: '주요 기술 2', + description: ['기술 설명입니다', '도입 이유입니다'], + comment: '추가적인 코멘트 자리입니다', + }, + ], + }, + { + title: '오초이스', + description: '오초이스 설명 입니다', + period: '2024.01 ~ 2024.12', + image: 'https://github.com/jaemin-s/jaemin-s/raw/refs/heads/main/image/portfolio/netflix.webp', + type: ProjectType.WORK, + features: [ + { + title: '주요 기능 이름', + description: '기술 설명입니다.', + }, + ], + company: '(주)홈초이스', + links: { demo: 'https://www.netflix.com/kr/', github: '' }, + stackBadges: ['React', 'TypeScript'], + previewUrls: [ + 'https://github.com/jaemin-s/jaemin-s/raw/refs/heads/main/image/portfolio/netflix.webp', + 'https://github.com/jaemin-s/jaemin-s/raw/refs/heads/main/image/portfolio/tving.webp', + ], + stackCategories: [ + { + title: '상태 관리', + stacks: ['Redux', 'React Query'], + }, + { title: '스타일링', stacks: ['Tailwind CSS'] }, + { title: '테스팅', stacks: ['Jest'] }, + { title: '개발 도구', stacks: ['ESLint', 'Prettier'] }, + { title: '빌드 도구', stacks: ['Next.js'] }, + ], + functions: [ + { + title: '주요 기술 1', + description: ['기술 설명입니다', '도입 이유입니다'], + comment: '추가적인 코멘트 자리입니다', + }, + { + title: '주요 기술 2', + description: ['기술 설명입니다', '도입 이유입니다'], + comment: '추가적인 코멘트 자리입니다', + }, + ], + }, + { + title: '오초이스', + description: '오초이스 설명 입니다', + period: '2024.01 ~ 2024.12', + image: 'https://github.com/jaemin-s/jaemin-s/raw/refs/heads/main/image/portfolio/netflix.webp', + type: ProjectType.WORK, + features: [ + { + title: '주요 기능 이름', + description: '기술 설명입니다.', + }, + ], + company: '(주)홈초이스', + links: { demo: '', github: '' }, + stackBadges: ['React', 'TypeScript'], + previewUrls: [ + 'https://github.com/jaemin-s/jaemin-s/raw/refs/heads/main/image/portfolio/netflix.webp', + 'https://github.com/jaemin-s/jaemin-s/raw/refs/heads/main/image/portfolio/tving.webp', + ], + stackCategories: [ + { + title: '상태 관리', + stacks: ['Redux', 'React Query'], + }, + { title: '스타일링', stacks: ['Tailwind CSS'] }, + { title: '테스팅', stacks: ['Jest'] }, + { title: '개발 도구', stacks: ['ESLint', 'Prettier'] }, + { title: '빌드 도구', stacks: ['Next.js'] }, + ], + functions: [ + { + title: '주요 기술 1', + description: ['기술 설명입니다', '도입 이유입니다'], + comment: '추가적인 코멘트 자리입니다', + }, + { + title: '주요 기술 2', + description: ['기술 설명입니다', '도입 이유입니다'], + comment: '추가적인 코멘트 자리입니다', + }, + ], + }, + ] + + getProjectList(): ProjectItem[] { + return this.mockData + } +} diff --git a/app/data/repositories/index.ts b/app/data/repositories/index.ts index 4f04544..4c206c1 100644 --- a/app/data/repositories/index.ts +++ b/app/data/repositories/index.ts @@ -1,5 +1,7 @@ import { CareerRepositoryImpl } from './CareerRepositoryImpl' +import { ProjectRepositoryImpl } from './ProjectRepositoryImpl' import { SkillRepositoryImpl } from './SkillRepositoryImpl' export const skillRepository = new SkillRepositoryImpl() export const CareerRepository = new CareerRepositoryImpl() +export const ProjectRepository = new ProjectRepositoryImpl() diff --git a/app/domain/entities/common.entity.ts b/app/domain/entities/common.entity.ts index 8a2a3d9..6feb01e 100644 --- a/app/domain/entities/common.entity.ts +++ b/app/domain/entities/common.entity.ts @@ -4,3 +4,10 @@ export interface ChildrenProps { children: ReactNode className?: '' } + +export interface DialogProps { + isOpen: boolean + children: ReactNode + maxWidth?: string + onClose: () => void +} diff --git a/app/domain/entities/project.entity.ts b/app/domain/entities/project.entity.ts index 1cb2421..e719a41 100644 --- a/app/domain/entities/project.entity.ts +++ b/app/domain/entities/project.entity.ts @@ -3,14 +3,16 @@ import { ProjectType } from '../enums/project.enum' export interface ProjectItem { title: string description: string - simpleStack: string - detailStacks: StackDescriptions[] period: string image: string + links: ProjectLinks + stackBadges: string[] + previewUrls: string[] type: ProjectType - features: string[] + stackCategories: StackCategory[] + features: Feature[] + functions: FunctionDecription[] company: string - links: ProjectLinks } export interface ProjectLinks { @@ -18,7 +20,24 @@ export interface ProjectLinks { demo: string } -export interface StackDescriptions { +export interface ProjectItemProps { + item: ProjectItem + onClickDetailButton: (arg0: ProjectItem) => void +} + +export interface Feature { title: string description: string + comment?: string +} + +export interface FunctionDecription { + title: string + description: string[] + comment?: string +} + +export interface StackCategory { + title: string + stacks: string[] } diff --git a/app/domain/repositories/project.repo.ts b/app/domain/repositories/project.repo.ts new file mode 100644 index 0000000..c99662f --- /dev/null +++ b/app/domain/repositories/project.repo.ts @@ -0,0 +1,5 @@ +import { ProjectItem } from '../entities/project.entity' + +export interface ProjectRepository { + getProjectList(): ProjectItem[] +} diff --git a/app/presentation/component/article/Projects.tsx b/app/presentation/component/article/Projects.tsx index 35f3a63..dc1516b 100644 --- a/app/presentation/component/article/Projects.tsx +++ b/app/presentation/component/article/Projects.tsx @@ -1,83 +1,23 @@ -import React from 'react' +'use client' + +import React, { useState } from 'react' import ArticleHeader from '../text/ArticleHeader' import ProjectItemCard from '../card/ProjectItemCard' -import { ProjectType } from '@/app/domain/enums/project.enum' +import CommonDialog from '../dialog/CommonDialog' +import { ProjectRepository } from '@/app/data/repositories' +import ProjectDialog from '../dialog/ProjectDialog' +import { ProjectItem } from '@/app/domain/entities/project.entity' const Projects = () => { - const projectItemList = [ - { - title: '오초이스', - description: '오초이스 설명 입니다', - simpleStack: 'React JavaScript Tizen WebOS', - detailStacks: [{ title: 'React', description: '리액트 사용 이유' }], - period: '2024.01 ~ 2024.12', - image: 'https://github.com/jaemin-s/jaemin-s/raw/refs/heads/main/image/portfolio/netflix.webp', - type: ProjectType.WORK, - features: [''], - company: '(주)홈초이스', - links: { demo: '', github: '' }, - }, - { - title: '오초이스', - description: '오초이스 설명 입니다', - simpleStack: 'React JavaScript Tizen WebOS', - detailStacks: [{ title: 'React', description: '리액트 사용 이유' }], - period: '2024.01 ~ 2024.12', - image: 'https://github.com/jaemin-s/jaemin-s/raw/refs/heads/main/image/portfolio/tving.webp', - type: ProjectType.PERSONAL, - features: [''], - company: '(주)홈초이스', - links: { demo: 'https://www.tving.com', github: '' }, - }, - { - title: '오초이스', - description: '오초이스 설명 입니다', - simpleStack: 'React JavaScript Tizen WebOS', - detailStacks: [{ title: 'React', description: '리액트 사용 이유' }], - period: '2024.01 ~ 2024.12', - image: 'https://github.com/jaemin-s/jaemin-s/raw/refs/heads/main/image/portfolio/netflix.webp', - type: ProjectType.PERSONAL, - features: [''], - company: '(주)홈초이스', - links: { demo: 'https://www.netflix.com/kr/', github: '' }, - }, - { - title: '오초이스', - description: '오초이스 설명 입니다', - simpleStack: 'React JavaScript Tizen WebOS', - detailStacks: [{ title: 'React', description: '리액트 사용 이유' }], - period: '2024.01 ~ 2024.12', - image: 'https://github.com/jaemin-s/jaemin-s/raw/refs/heads/main/image/portfolio/tving.webp', - type: ProjectType.WORK, - features: [''], - company: '(주)홈초이스', - links: { demo: '', github: '' }, - }, - { - title: '오초이스', - description: '오초이스 설명 입니다', - simpleStack: 'React JavaScript Tizen WebOS', - detailStacks: [{ title: 'React', description: '리액트 사용 이유' }], - period: '2024.01 ~ 2024.12', - image: 'https://github.com/jaemin-s/jaemin-s/raw/refs/heads/main/image/portfolio/netflix.webp', - type: ProjectType.WORK, - features: [''], - company: '(주)홈초이스', - links: { demo: '', github: '' }, - }, - { - title: '오초이스', - description: '오초이스 설명 입니다', - simpleStack: 'React JavaScript Tizen WebOS', - detailStacks: [{ title: 'React', description: '리액트 사용 이유' }], - period: '2024.01 ~ 2024.12', - image: 'https://github.com/jaemin-s/jaemin-s/raw/refs/heads/main/image/portfolio/netflix.webp', - type: ProjectType.WORK, - features: [''], - company: '(주)홈초이스', - links: { demo: '', github: '' }, - }, - ] + const [isOpen, setIsOpen] = useState(false) + const [itemData, setItemData] = useState(null) + + const projectItemList = ProjectRepository.getProjectList() + + const handleDetailButton = (item: ProjectItem) => { + setIsOpen(true) + setItemData(item) + } return (
@@ -85,10 +25,15 @@ const Projects = () => { + {itemData && ( + setIsOpen(false)}> + + + )}
) } diff --git a/app/presentation/component/badge/TechBadge.tsx b/app/presentation/component/badge/TechBadge.tsx new file mode 100644 index 0000000..dc13c3e --- /dev/null +++ b/app/presentation/component/badge/TechBadge.tsx @@ -0,0 +1,14 @@ +import { ChildrenProps } from '@/app/domain/entities/common.entity' +import React from 'react' + +const TechBadge = ({ children, className = '' }: ChildrenProps) => { + return ( +
+ {children} +
+ ) +} + +export default TechBadge diff --git a/app/presentation/component/card/ProjectItemCard.tsx b/app/presentation/component/card/ProjectItemCard.tsx index efef95c..f1c3718 100644 --- a/app/presentation/component/card/ProjectItemCard.tsx +++ b/app/presentation/component/card/ProjectItemCard.tsx @@ -1,25 +1,31 @@ -import { ProjectItem } from '@/app/domain/entities/project.entity' +import { ProjectItemProps } from '@/app/domain/entities/project.entity' import React from 'react' import CommonCard from './CommonCard' import { ProjectType } from '@/app/domain/enums/project.enum' -const ProjectItemCard = (props: ProjectItem) => { +const ProjectItemCard = ({ item, onClickDetailButton }: ProjectItemProps) => { return (
- +
-

{props.title}

-

{props.description}

-

{props.simpleStack}

-

{props.period}

+

{item.title}

+

{item.description}

+

{item.period}

-
- - {props.type === ProjectType.PERSONAL && ( +
+ + {!!item.links.demo && ( diff --git a/app/presentation/component/dialog/CommonDialog.tsx b/app/presentation/component/dialog/CommonDialog.tsx new file mode 100644 index 0000000..dfa8cf9 --- /dev/null +++ b/app/presentation/component/dialog/CommonDialog.tsx @@ -0,0 +1,27 @@ +import { DialogProps } from '@/app/domain/entities/common.entity' +import React, { useEffect } from 'react' + +const CommonDialog = ({ children, isOpen, onClose, maxWidth = 'max-w-3xl' }: DialogProps) => { + useEffect(() => { + const handleEscape = (e: KeyboardEvent) => { + if (e.key === 'Escape') onClose() + } + + document.addEventListener('keydown', handleEscape) + return () => { + document.removeEventListener('keydown', handleEscape) + } + }, [isOpen, onClose]) + + if (!isOpen) return null + + return ( +
+
+
{children}
+
+
+ ) +} + +export default CommonDialog diff --git a/app/presentation/component/dialog/ProjectDialog.tsx b/app/presentation/component/dialog/ProjectDialog.tsx new file mode 100644 index 0000000..c4f1ba5 --- /dev/null +++ b/app/presentation/component/dialog/ProjectDialog.tsx @@ -0,0 +1,92 @@ +import React from 'react' +import TechBadge from '../badge/TechBadge' +import { ProjectItem } from '@/app/domain/entities/project.entity' + +const ProjectDialog = (props: ProjectItem) => { + return ( +
+
+

{props.title}

+
    + {props.stackBadges.map((item, index) => ( +
  • + {item} +
  • + ))} +
+

{props.period}

+
+ {/* 프로젝트 미리보기 */} +
+

프로젝트 미리보기

+
+ {props.previewUrls.map((item, index) => ( + + ))} +
+
+ {/* 주요 기능 */} +
+

주요 기능

+
    + {props.features.map((item, index) => ( +
  • +

    {item.title}

    +

    {item.description}

    +
  • + ))} +
+
+ {/* 기술 스택 섹션 */} +
+

사용 기술

+ {/* 주요 기술 */} +
+

주요 기술

+
    + {props.functions.map((item, index) => ( +
  • +

    {item.title}

    +
    +
    +

    선정 이유:

    +
      + {item.description.map((str, index) => ( +
    • {str}
    • + ))} +
    +
    +

    {item.comment}

    +
    +
  • + ))} +
+
+ {/* 기타 사용 기술 */} +
+

기타 사용 기술

+
+
    + {props.stackCategories.map((item, index) => ( +
  • +

    {item.title}

    +
    + {item.stacks.map((stack, index) => ( + + {stack} + + ))} +
    +
  • + ))} +
+
+
+
+
+ ) +} +export default ProjectDialog