Skip to content

Commit 07a9b2a

Browse files
Merge pull request #78 from ShipFriend0516/feature/size
refactor: 블로그 메인페이지 비율 조절 및 잡다한 기능 추가
2 parents 8259d62 + 1da69bd commit 07a9b2a

27 files changed

Lines changed: 327 additions & 267 deletions

app/entities/common/Footer.tsx

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,14 @@ const Footer = () => {
3939
});
4040

4141
if (response.data.success) {
42-
toast.success(
43-
response.data.message || '인증 이메일이 발송되었습니다.'
44-
);
42+
toast.success(response.data.message || '인증 이메일이 발송되었습니다.');
4543
setIsSubmitted(true);
4644
setNickname('');
4745
setEmail('');
4846
}
4947
} catch (error) {
5048
if (axios.isAxiosError(error) && error.response) {
51-
toast.error(
52-
error.response.data.error || '구독 신청에 실패했습니다.'
53-
);
49+
toast.error(error.response.data.error || '구독 신청에 실패했습니다.');
5450
} else {
5551
toast.error('구독 신청 중 오류가 발생했습니다.');
5652
}
@@ -71,8 +67,9 @@ const Footer = () => {
7167
<div className={'footer-col'}>
7268
<b>BLOG</b>
7369
<div className={'text-weak'}>
74-
<p className={'text-left font-serif whitespace-pre-wrap'}>
75-
a developer who never stops growing.
70+
<p className={'text-left text-sm font-serif whitespace-pre-wrap'}>
71+
개발과 기술에 대한 이야기를 공유하는 공간입니다. <br />
72+
문제 해결과 성장의 기록을 만듭니다.
7673
</p>
7774
<p className={'text-left font-serif whitespace-pre-wrap'}></p>
7875
</div>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
interface SectionHeadingProps {
2+
title: string;
3+
}
4+
5+
const SectionHeading = ({ title }: SectionHeadingProps) => {
6+
return (
7+
<div className="space-y-2">
8+
<h2 className="text-xl md:text-2xl font-bold text-gray-900 dark:text-gray-100">
9+
{title}
10+
</h2>
11+
<div className="h-1 w-24 bg-gray-900 dark:bg-gray-100 rounded-full"></div>
12+
</div>
13+
);
14+
};
15+
16+
export default SectionHeading;

app/entities/common/Toast/Toast.tsx

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,42 @@
1-
import { CiCircleCheck, CiCircleRemove } from 'react-icons/ci';
1+
import { CiCircleCheck, CiCircleRemove, CiMail } from 'react-icons/ci';
2+
3+
type ToastType = 'success' | 'error' | 'info';
24

35
interface ToastProps {
46
message: string;
5-
type: 'success' | 'error';
7+
title?: string;
8+
type: ToastType;
69
removeToast: () => void;
710
}
811

9-
const Toast = ({ message, type, removeToast }: ToastProps) => {
10-
const iconRender = (type: 'success' | 'error') => {
11-
if (type === 'success') {
12-
return <CiCircleCheck color={'green'} size={40} />;
13-
} else {
14-
return <CiCircleRemove color={'red'} size={40} />;
15-
}
16-
};
12+
const iconMap: Record<ToastType, JSX.Element> = {
13+
success: <CiCircleCheck color={'green'} size={40} />,
14+
error: <CiCircleRemove color={'red'} size={40} />,
15+
info: <CiMail color={'#3b82f6'} size={40} />,
16+
};
17+
18+
const Toast = ({ message, title, type, removeToast }: ToastProps) => {
1719
return (
1820
<div
1921
onClick={() => removeToast()}
2022
className={`
2123
transform transition-all duration-300 ease-out animate-slideUp
22-
bg-gray-200/90 text-black px-3 py-2 rounded-lg flex items-center gap-3
23-
backdrop-blur-sm w-full max-w-md origin-center cursor-pointer
24+
bg-gray-200/90 text-black px-3 py-2 rounded-lg flex items-center gap-3
25+
backdrop-blur-sm w-full max-w-md origin-center cursor-pointer
2426
hover:bg-gray-300/90 hover:shadow-lg
2527
`}
2628
>
2729
<div className={`flex items-center justify-center rounded-full p-0.5`}>
28-
{iconRender(type)}
30+
{iconMap[type]}
31+
</div>
32+
<div className="flex-1 min-w-0">
33+
{title && (
34+
<p className="text font-semibold text-sm">{title}</p>
35+
)}
36+
<p className={`text whitespace-pre-line ${title ? 'text-xs text-gray-600 mt-0.5' : 'line-clamp-1'}`}>
37+
{message}
38+
</p>
2939
</div>
30-
<p className="text line-clamp-1 whitespace-pre-line flex-1">{message}</p>
3140
</div>
3241
);
3342
};

app/entities/common/Toast/ToastProvider.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import useToastStore from '@/app/stores/useToastStore';
55
interface Toast {
66
id: number;
77
message: string;
8-
type: 'success' | 'error';
8+
title?: string;
9+
type: 'success' | 'error' | 'info';
910
}
1011

1112
const ToastProvider = () => {
@@ -24,6 +25,7 @@ const ToastProvider = () => {
2425
key={toast.id}
2526
removeToast={() => removeToast(toast.id)}
2627
message={toast.message}
28+
title={toast.title}
2729
type={toast.type}
2830
/>
2931
);

app/entities/portfolio/Carousel.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ const Carousel = ({ slides }: CarouselProps) => {
175175
] as ReactElement,
176176
{
177177
hideTags: true,
178-
hoverEffect: false,
178+
179179
}
180180
)}
181181
</div>
@@ -197,7 +197,7 @@ const Carousel = ({ slides }: CarouselProps) => {
197197
] as ReactElement,
198198
{
199199
hideTags: true,
200-
hoverEffect: false,
200+
201201
}
202202
)}
203203
</div>

app/entities/portfolio/PortfolioPreview.tsx

Lines changed: 49 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -1,178 +1,67 @@
11
'use client';
22
import Image from 'next/image';
33
import Link from 'next/link';
4-
import { useState } from 'react';
54
import { FaGithub, FaGlobe } from 'react-icons/fa';
5+
import { MdArrowForward } from 'react-icons/md';
66
import { Project } from '@/app/types/Portfolio';
77

88
interface PortfolioPreviewProps {
99
project: Project;
1010
hideTags?: boolean;
11-
hoverEffect?: boolean;
1211
}
1312

14-
const PortfolioPreview = ({
15-
project,
16-
hideTags,
17-
hoverEffect = true,
18-
}: PortfolioPreviewProps) => {
19-
const [isHovered, setIsHovered] = useState(false);
20-
const [isTouched, setIsTouched] = useState(false);
21-
22-
const handleTouchStart = () => {
23-
if (hoverEffect) {
24-
setIsTouched(true);
25-
}
26-
};
27-
28-
const handleTouchEnd = () => {
29-
if (hoverEffect) {
30-
setTimeout(() => setIsTouched(false), 3000);
31-
}
32-
};
33-
34-
const showOverlay = hoverEffect && (isHovered || isTouched);
35-
13+
const PortfolioPreview = ({ project, hideTags }: PortfolioPreviewProps) => {
3614
return (
37-
<div
38-
className="group bg-gradient-to-br from-gray-50 to-gray-100 dark:from-primary-rich rounded-2xl overflow-hidden shadow transition-all duration-300 border border-gray-200 dark:border-gray-700"
39-
onMouseEnter={() => setIsHovered(true)}
40-
onMouseLeave={() => setIsHovered(false)}
41-
>
42-
<div
43-
className="relative overflow-hidden"
44-
onTouchStart={handleTouchStart}
45-
onTouchEnd={handleTouchEnd}
46-
>
47-
<Image
48-
width={500}
49-
height={400}
50-
src={project.image}
51-
alt={`${project.title} 프로젝트 이미지`}
52-
className={`w-full aspect-video object-cover transition-transform duration-500 ${
53-
showOverlay ? 'scale-110' : 'scale-100'
54-
}`}
55-
/>
56-
57-
{/* 데스크톱 호버 오버레이 */}
58-
<div
59-
className={`absolute inset-0 bg-black bg-opacity-50 hidden md:flex items-center justify-center transition-opacity duration-300 ${
60-
isHovered ? 'opacity-100' : 'opacity-0'
61-
}`}
62-
>
63-
<div className="flex gap-3 ">
64-
{project.demoUrl && (
65-
<Link
66-
href={project.demoUrl}
67-
target="_blank"
68-
rel="noopener noreferrer"
69-
>
70-
<button className="inline-flex items-center gap-2 bg-neutral-500 hover:bg-neutral-600 text-white px-4 py-2 rounded-md transition-colors text-sm ">
71-
배포 <FaGlobe size={14} />
72-
</button>
73-
</Link>
74-
)}
75-
{project.githubUrl && (
76-
<Link
77-
href={project.githubUrl}
78-
target="_blank"
79-
rel="noopener noreferrer"
80-
className={'h-full'}
81-
>
82-
<button className="inline-flex items-center gap-2 bg-gray-700 hover:bg-gray-800 text-white px-4 py-2 rounded-md transition-colors text-sm ">
83-
<FaGithub size={14} /> Github
84-
</button>
85-
</Link>
86-
)}
87-
{project.slug && (
88-
<Link
89-
href={`/portfolio/${project.slug}`}
90-
rel="noopener noreferrer"
91-
>
92-
<button className="bg-neutral-700 hover:bg-neutral-800 text-white px-4 py-2 rounded-md transition-colors text-sm">
93-
세부 정보
94-
</button>
95-
</Link>
96-
)}
97-
</div>
98-
</div>
99-
100-
{/* 모바일 터치 오버레이 */}
101-
<div
102-
className={`absolute inset-0 bg-black bg-opacity-50 flex md:hidden items-center justify-center transition-opacity duration-300 ${
103-
isTouched ? 'opacity-100' : 'opacity-0'
104-
}`}
105-
>
106-
<div className="flex flex-col gap-2 px-4 ">
107-
{project.demoUrl && (
108-
<Link
109-
href={project.demoUrl}
110-
target="_blank"
111-
rel="noopener noreferrer"
112-
>
113-
<button className="inline-flex items-center justify-center gap-2 w-full bg-neutral-500 hover:bg-neutral-600 text-white px-4 py-3 rounded-md transition-colors text-sm">
114-
배포 보기 <FaGlobe size={14} />
115-
</button>
116-
</Link>
117-
)}
118-
{project.githubUrl && (
119-
<Link
120-
href={project.githubUrl}
121-
target="_blank"
122-
rel="noopener noreferrer"
123-
>
124-
<button className="inline-flex items-center justify-center gap-2 w-full bg-gray-700 hover:bg-gray-800 text-white px-4 py-3 rounded-md transition-colors text-sm ">
125-
GitHub <FaGithub size={14} />
126-
</button>
127-
</Link>
128-
)}
129-
{project.slug && (
130-
<Link
131-
href={`/portfolio/${project.slug}`}
132-
rel="noopener noreferrer"
133-
>
134-
<button className="w-full bg-neutral-700 hover:bg-neutral-800 text-white px-4 py-3 rounded-md transition-colors text-sm">
135-
세부 정보
136-
</button>
137-
</Link>
138-
)}
139-
</div>
140-
</div>
141-
</div>
142-
143-
<div className="p-6 md:p-8">
15+
<div className="flex flex-col md:flex-row group bg-gradient-to-br from-gray-50 to-gray-100 dark:from-primary-rich rounded-2xl overflow-hidden shadow transition-all duration-300 border border-gray-200 dark:border-gray-700">
16+
{/* 이미지 영역 */}
17+
<div className="relative overflow-hidden w-full md:w-1/3 shrink-0">
14418
<Link href={project.slug ? `/portfolio/${project.slug}` : '#'}>
145-
<h3 className="text-xl md:text-2xl font-bold mb-3 text-gray-900 dark:text-gray-100 hover:text-gray-600 dark:hover:text-gray-400 transition-colors line-clamp-2">
146-
{project.title}
147-
</h3>
19+
<Image
20+
width={500}
21+
height={400}
22+
src={project.image}
23+
alt={`${project.title} 프로젝트 이미지`}
24+
className="w-full h-48 md:h-full object-cover transition-transform duration-500 group-hover:scale-105"
25+
/>
14826
</Link>
149-
<p className="text-sm md:text-base text-gray-600 dark:text-gray-300 mb-4 md:mb-5 line-clamp-3 leading-relaxed">
150-
{project.description}
151-
</p>
27+
</div>
15228

153-
{!hideTags && project.tags && project.tags.length > 0 && (
154-
<div className="flex flex-wrap gap-2 mt-4">
155-
{project.tags.map((tag, index) => (
156-
<span
157-
key={index}
158-
className="text-xs px-3 py-1.5 bg-gray-200 dark:bg-gray-700 rounded-full text-gray-700 dark:text-gray-300 whitespace-nowrap font-medium"
159-
>
160-
{tag}
161-
</span>
162-
))}
163-
</div>
164-
)}
29+
{/* 정보 + 버튼 영역 */}
30+
<div className="flex flex-1 flex-col md:flex-row">
31+
{/* 텍스트 정보 */}
32+
<div className="flex-1 p-5 md:p-6">
33+
<Link href={project.slug ? `/portfolio/${project.slug}` : '#'}>
34+
<h3 className="text-lg md:text-xl font-bold mb-2 text-gray-900 dark:text-gray-100 hover:text-gray-600 dark:hover:text-gray-400 transition-colors line-clamp-2">
35+
{project.title}
36+
</h3>
37+
</Link>
38+
<p className="text-sm text-gray-600 dark:text-gray-300 mb-3 line-clamp-3 leading-relaxed">
39+
{project.description}
40+
</p>
16541

166-
{/* 모바일 하단 액션 버튼들 */}
167-
<div className="flex md:hidden gap-2 mt-5 pt-4 border-t border-gray-200 dark:border-gray-700">
42+
{!hideTags && project.tags && project.tags.length > 0 && (
43+
<div className="flex flex-wrap gap-1.5">
44+
{project.tags.map((tag, index) => (
45+
<span
46+
key={index}
47+
className="text-xs px-2.5 py-1 bg-gray-200 dark:bg-gray-700 rounded-full text-gray-700 dark:text-gray-300 whitespace-nowrap font-medium"
48+
>
49+
{tag}
50+
</span>
51+
))}
52+
</div>
53+
)}
54+
</div>
55+
<div className="flex md:flex-col gap-2 p-4 md:p-5 border-t md:border-t-0 md:border-l border-gray-200 dark:border-gray-700 justify-center items-stretch">
16856
{project.demoUrl && (
16957
<Link
17058
href={project.demoUrl}
17159
target="_blank"
17260
rel="noopener noreferrer"
173-
className="flex-1"
61+
className="flex-1 md:flex-none"
62+
title="배포 링크"
17463
>
175-
<button className="inline-flex items-center justify-center gap-1.5 w-full bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 text-gray-800 dark:text-gray-200 px-3 py-2.5 rounded-lg transition-colors text-xs font-medium">
64+
<button className="inline-flex items-center justify-center gap-1.5 w-full bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 text-gray-800 dark:text-gray-200 px-3 py-2 rounded-lg transition-colors text-xs font-medium">
17665
<FaGlobe size={12} />
17766
배포
17867
</button>
@@ -183,9 +72,10 @@ const PortfolioPreview = ({
18372
href={project.githubUrl}
18473
target="_blank"
18574
rel="noopener noreferrer"
186-
className="flex-1"
75+
className="flex-1 md:flex-none"
76+
title="GitHub 코드 보기"
18777
>
188-
<button className="inline-flex items-center justify-center gap-1.5 w-full bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 text-gray-800 dark:text-gray-200 px-3 py-2.5 rounded-lg transition-colors text-xs font-medium">
78+
<button className="inline-flex items-center justify-center gap-1.5 w-full bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 text-gray-800 dark:text-gray-200 px-3 py-2 rounded-lg transition-colors text-xs font-medium">
18979
<FaGithub size={12} />
19080
코드
19181
</button>
@@ -195,9 +85,11 @@ const PortfolioPreview = ({
19585
<Link
19686
href={`/portfolio/${project.slug}`}
19787
rel="noopener noreferrer"
198-
className="flex-1"
88+
className="flex-1 md:flex-none"
89+
title="자세히 보기"
19990
>
200-
<button className="w-full bg-gray-800 dark:bg-gray-200 hover:bg-gray-700 dark:hover:bg-gray-300 text-gray-100 dark:text-gray-900 px-3 py-2.5 rounded-lg transition-colors text-xs font-medium">
91+
<button className="inline-flex items-center justify-center gap-1.5 w-full bg-gray-800 dark:bg-gray-200 hover:bg-gray-700 dark:hover:bg-gray-300 text-gray-100 dark:text-gray-900 px-3 py-2 rounded-lg transition-colors text-xs font-medium">
92+
<MdArrowForward size={12} />
20193
자세히
20294
</button>
20395
</Link>

0 commit comments

Comments
 (0)