Skip to content

Commit b4f0964

Browse files
committed
refactor: 오늘의 문제 리팩토링 및 메인 모바일버전 리팩토링
1 parent 7b3a62f commit b4f0964

File tree

11 files changed

+690
-221
lines changed

11 files changed

+690
-221
lines changed

src/components/common/Container.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ interface ContainerProps {
77

88
const Container: React.FC<ContainerProps> = ({ children, className = '' }) => {
99
return (
10-
<div className={`max-w-4xl mx-auto px-5 ${className}`}>
10+
<div className={`max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 ${className}`}>
1111
{children}
1212
</div>
1313
);

src/components/common/EmailTemplate.tsx

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -9,67 +9,67 @@ interface EmailTemplateProps {
99
const EmailTemplate: React.FC<EmailTemplateProps> = ({ toEmail, quizLink }) => {
1010
const navigate = useNavigate();
1111
return (
12-
<div className="font-pretendard p-10">
13-
<div className="max-w-2xl mx-auto bg-white rounded-2xl shadow-lg overflow-hidden">
12+
<div className="font-pretendard p-4 sm:p-6 lg:p-10">
13+
<div className="max-w-2xl mx-auto bg-white rounded-xl sm:rounded-2xl shadow-lg overflow-hidden">
1414
{/* Header with Logo */}
15-
<div className="bg-gradient-to-r from-brand-500 to-brand-600 p-8 text-center">
16-
<div className="flex items-center justify-center space-x-2 mb-4">
17-
<span className="text-3xl font-bold text-white">CS</span>
18-
<span className="text-3xl font-bold text-blue-100">25</span>
15+
<div className="bg-gradient-to-r from-brand-500 to-brand-600 p-6 sm:p-8 text-center">
16+
<div className="flex items-center justify-center space-x-2 mb-3 sm:mb-4">
17+
<span className="text-2xl sm:text-3xl font-bold text-white">CS</span>
18+
<span className="text-2xl sm:text-3xl font-bold text-blue-100">25</span>
1919
</div>
20-
<h1 className="text-2xl font-bold text-white">오늘의 CS 문제</h1>
21-
<p className="text-brand-100 mt-2">AI가 준비한 맞춤형 문제를 확인하세요</p>
20+
<h1 className="text-xl sm:text-2xl font-bold text-white">오늘의 CS 문제</h1>
21+
<p className="text-sm sm:text-base text-brand-100 mt-2">AI가 준비한 맞춤형 문제를 확인하세요</p>
2222
</div>
2323

2424
{/* Content */}
25-
<div className="p-8">
26-
<div className="text-center mb-8">
27-
<div className="w-20 h-20 bg-brand-100 rounded-full flex items-center justify-center mx-auto mb-4">
28-
<svg className="w-10 h-10 text-brand-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
25+
<div className="p-6 sm:p-8">
26+
<div className="text-center mb-6 sm:mb-8">
27+
<div className="w-16 h-16 sm:w-20 sm:h-20 bg-brand-100 rounded-full flex items-center justify-center mx-auto mb-3 sm:mb-4">
28+
<svg className="w-8 h-8 sm:w-10 sm:h-10 text-brand-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
2929
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
3030
</svg>
3131
</div>
32-
<h2 className="text-2xl font-bold text-gray-900 mb-4">오늘의 문제를 풀어보세요!</h2>
33-
<p className="text-gray-600 leading-relaxed mb-6">
34-
안녕하세요! CS25에서 오늘의 맞춤형 CS 문제를 보내드립니다.<br />
35-
AI가 생성한 문제와 상세한 해설로 CS 지식을 향상시켜보세요.
32+
<h2 className="text-xl sm:text-2xl font-bold text-gray-900 mb-3 sm:mb-4">오늘의 문제를 풀어보세요!</h2>
33+
<p className="text-sm sm:text-base text-gray-600 leading-relaxed mb-4 sm:mb-6 px-2 sm:px-0">
34+
안녕하세요! CS25에서 오늘의 맞춤형 CS 문제를 보내드립니다.<br className="hidden sm:block" />
35+
<span className="block sm:inline">AI가 생성한 문제와 상세한 해설로 CS 지식을 향상시켜보세요.</span>
3636
</p>
3737
</div>
3838

3939
{/* Action Button */}
40-
<div className="text-center mb-8">
40+
<div className="text-center mb-6 sm:mb-8">
4141
<button
4242
onClick={() => navigate('/quiz')}
43-
className="inline-block bg-gradient-to-r from-brand-500 to-brand-600 text-white px-8 py-4 rounded-full font-semibold text-lg transition-all duration-300 hover:from-brand-600 hover:to-brand-700 hover:shadow-lg"
43+
className="inline-block bg-gradient-to-r from-brand-500 to-brand-600 text-white px-6 sm:px-8 py-3 sm:py-4 rounded-full font-semibold text-base sm:text-lg transition-all duration-300 hover:from-brand-600 hover:to-brand-700 hover:shadow-lg w-full sm:w-auto max-w-xs"
4444
>
4545
🧠 연습문제 풀기
4646
</button>
4747
</div>
4848

4949
{/* Today's Topic Preview */}
50-
<div className="bg-gray-50 rounded-xl p-6 mb-8">
51-
<h3 className="text-lg font-bold text-gray-900 mb-2">오늘의 주제</h3>
52-
<div className="flex flex-wrap gap-2">
53-
<span className="bg-brand-100 text-brand-700 px-3 py-1 rounded-full text-sm font-medium">알고리즘</span>
54-
<span className="bg-navy-100 text-navy-700 px-3 py-1 rounded-full text-sm font-medium">시간복잡도</span>
50+
<div className="bg-gray-50 rounded-lg sm:rounded-xl p-4 sm:p-6 mb-6 sm:mb-8">
51+
<h3 className="text-base sm:text-lg font-bold text-gray-900 mb-2">오늘의 주제</h3>
52+
<div className="flex flex-wrap gap-2 justify-center sm:justify-start">
53+
<span className="bg-brand-100 text-brand-700 px-3 py-1 rounded-full text-xs sm:text-sm font-medium">알고리즘</span>
54+
<span className="bg-navy-100 text-navy-700 px-3 py-1 rounded-full text-xs sm:text-sm font-medium">시간복잡도</span>
5555
</div>
5656
</div>
5757

5858
{/* Subscription Settings */}
59-
<div className="text-center mb-8">
59+
<div className="text-center mb-6 sm:mb-8">
6060
<button
6161
disabled
62-
className="inline-flex items-center space-x-2 bg-gray-100 hover:bg-gray-200 text-gray-700 px-4 py-2 rounded-lg font-medium text-sm border border-gray-200 transition-all duration-200 hover:shadow-sm"
62+
className="inline-flex items-center space-x-2 bg-gray-100 hover:bg-gray-200 text-gray-700 px-3 sm:px-4 py-2 rounded-lg font-medium text-xs sm:text-sm border border-gray-200 transition-all duration-200 hover:shadow-sm"
6363
>
64-
<span className="text-base">⚙️</span>
64+
<span className="text-sm sm:text-base">⚙️</span>
6565
<span>구독 설정</span>
6666
</button>
6767
</div>
6868

6969
{/* Footer */}
70-
<div className="border-t border-gray-200 pt-6">
71-
<div className="text-center text-gray-500 text-sm space-y-2">
72-
<p>
70+
<div className="border-t border-gray-200 pt-4 sm:pt-6">
71+
<div className="text-center text-gray-500 text-xs sm:text-sm space-y-2">
72+
<p className="px-2 sm:px-0">
7373
이 메일은 <span className="font-medium text-gray-700">{toEmail}</span> 계정으로 발송되었습니다.
7474
</p>
7575
<p>

src/components/common/Section.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const Section: React.FC<SectionProps> = ({
2323
};
2424

2525
return (
26-
<section className={`py-20 font-pretendard ${getBgColor()} ${className}`}>
26+
<section className={`py-12 sm:py-16 lg:py-20 font-pretendard ${getBgColor()} ${className}`}>
2727
{children}
2828
</section>
2929
);

src/components/common/SubscriptionModal.tsx

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, { useState } from 'react';
22
import Modal from './Modal';
33
import { useQuizCategories } from '../../hooks/useQuiz';
44
import { useRequestEmailVerification, useVerifyCode, useCreateSubscription, useCheckEmail } from '../../hooks';
5+
import { getCategoryLabel } from '../../utils/categoryUtils';
56

67
interface SubscriptionModalProps {
78
isOpen: boolean;
@@ -20,6 +21,7 @@ interface FormErrors {
2021
weekdays?: string;
2122
period?: string;
2223
verification?: string;
24+
subscription?: string;
2325
}
2426

2527
type Step = 'form' | 'verification' | 'success';
@@ -44,18 +46,6 @@ const SubscriptionModal: React.FC<SubscriptionModalProps> = ({ isOpen, onClose }
4446
const verifyCodeMutation = useVerifyCode();
4547
const createSubscriptionMutation = useCreateSubscription();
4648

47-
const getCategoryLabel = (category: string) => {
48-
switch (category.toLowerCase()) {
49-
case 'frontend':
50-
return '프론트엔드';
51-
case 'backend':
52-
return '백엔드';
53-
case 'certification':
54-
return '자격증';
55-
default:
56-
return category;
57-
}
58-
};
5949

6050
// API 응답이 {data: []} 형태인지 확인하고 처리
6151
const categoryList = (categoriesData && typeof categoriesData === 'object' && 'data' in categoriesData)
@@ -209,21 +199,38 @@ const SubscriptionModal: React.FC<SubscriptionModalProps> = ({ isOpen, onClose }
209199
{ email: formData.email, code: verificationCode },
210200
{
211201
onSuccess: () => {
202+
// period 문자열을 월 단위 숫자로 변환
203+
const getMonthsFromPeriod = (period: string): number => {
204+
switch (period) {
205+
case 'ONE_MONTH':
206+
return 1;
207+
case 'THREE_MONTHS':
208+
return 3;
209+
case 'SIX_MONTHS':
210+
return 6;
211+
case 'ONE_YEAR':
212+
return 12;
213+
default:
214+
return 0;
215+
}
216+
};
217+
212218
// 인증 성공 후 구독 생성
213219
createSubscriptionMutation.mutate(
214220
{
215221
email: formData.email,
216222
category: formData.categories[0], // 첫 번째 선택된 카테고리만 전송
217223
days: formData.weekdays,
218-
period: formData.period,
224+
period: getMonthsFromPeriod(formData.period), // 숫자로 변환해서 전송
219225
},
220226
{
221227
onSuccess: () => {
222228
setStep('success');
223229
},
224230
onError: (error) => {
225231
console.error('구독 생성 실패:', error);
226-
setFormErrors({ verification: '구독 생성에 실패했습니다. 다시 시도해주세요.' });
232+
const errorMessage = error?.message || '구독 생성에 실패했습니다. 다시 시도해주세요.';
233+
setFormErrors({ subscription: errorMessage });
227234
},
228235
}
229236
);
@@ -505,6 +512,18 @@ const SubscriptionModal: React.FC<SubscriptionModalProps> = ({ isOpen, onClose }
505512
>
506513
{verifyCodeMutation.isPending || createSubscriptionMutation.isPending ? '처리 중...' : '인증 완료'}
507514
</button>
515+
516+
{/* 구독 생성 에러 메시지 */}
517+
{formErrors.subscription && (
518+
<div className="mt-4 p-3 bg-red-50 border border-red-200 rounded-lg">
519+
<p className="text-red-700 text-sm flex items-center">
520+
<svg className="w-4 h-4 mr-2 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
521+
<path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clipRule="evenodd" />
522+
</svg>
523+
{formErrors.subscription}
524+
</p>
525+
</div>
526+
)}
508527
</form>
509528

510529
<button

src/components/sections/FeaturesSection.tsx

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,36 +43,36 @@ const FeaturesSection: React.FC = () => {
4343
];
4444

4545
return (
46-
<Section className="py-20">
46+
<Section className="py-12 sm:py-16 lg:py-20">
4747
<Container>
48-
<div className="text-center max-w-3xl mx-auto mb-16">
49-
<h2 className="text-4xl md:text-5xl font-bold mb-6 text-gray-900">
48+
<div className="text-center max-w-3xl mx-auto mb-12 sm:mb-16 px-4 sm:px-0">
49+
<h2 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-bold mb-4 sm:mb-6 text-gray-900">
5050
AI 기반 CS 학습의 혁신
5151
</h2>
52-
<p className="text-xl text-gray-600 leading-relaxed">
52+
<p className="text-base sm:text-lg md:text-xl text-gray-600 leading-relaxed">
5353
AI가 생성하고 해설하는 개인화된 CS 지식 학습 경험
5454
</p>
5555
</div>
5656

57-
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
57+
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 sm:gap-8 px-4 sm:px-0">
5858
{features.map((feature, index) => (
5959
<div
6060
key={index}
61-
className="bg-white rounded-2xl p-8 shadow-sm border border-gray-100 hover:shadow-lg transition-all duration-300 hover:-translate-y-1 group"
61+
className="bg-white rounded-xl sm:rounded-2xl p-6 sm:p-8 shadow-sm border border-gray-100 hover:shadow-lg transition-all duration-300 hover:-translate-y-1 group"
6262
>
63-
<div className="mb-6">
63+
<div className="mb-4 sm:mb-6">
6464
{feature.icon}
6565
</div>
6666

67-
<h3 className="text-xl font-bold text-gray-900 mb-4 group-hover:text-navy-700 transition-colors">
67+
<h3 className="text-lg sm:text-xl font-bold text-gray-900 mb-3 sm:mb-4 group-hover:text-navy-700 transition-colors">
6868
{feature.title}
6969
</h3>
7070

71-
<p className="text-gray-600 mb-3 leading-relaxed">
71+
<p className="text-sm sm:text-base text-gray-600 mb-2 sm:mb-3 leading-relaxed">
7272
{feature.description}
7373
</p>
7474

75-
<p className="text-sm text-gray-500">
75+
<p className="text-xs sm:text-sm text-gray-500">
7676
{feature.details}
7777
</p>
7878
</div>

src/components/sections/HeroSection.tsx

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,46 +8,46 @@ interface HeroSectionProps {
88

99
const HeroSection: React.FC<HeroSectionProps> = ({ onSubscribeClick }) => {
1010
return (
11-
<Section className="text-center pt-20 pb-32 bg-gradient-to-br from-gray-50 via-blue-50 to-brand-50">
11+
<Section className="text-center pt-16 sm:pt-20 pb-20 sm:pb-32 bg-gradient-to-br from-gray-50 via-blue-50 to-brand-50">
1212
<Container>
1313
<div className="max-w-4xl mx-auto">
14-
<div className="inline-flex items-center bg-brand-100 rounded-full px-6 py-2 mb-8">
15-
<span className="text-sm font-medium text-brand-700">🤖 AI가 생성하고 해설하는 CS 지식</span>
14+
<div className="inline-flex items-center bg-brand-100 rounded-full px-4 sm:px-6 py-2 mb-6 sm:mb-8">
15+
<span className="text-xs sm:text-sm font-medium text-brand-700">🤖 AI가 생성하고 해설하는 CS 지식</span>
1616
</div>
1717

18-
<h1 className="text-5xl md:text-6xl font-bold mb-6 leading-tight tracking-tight text-gray-900">
18+
<h1 className="text-3xl sm:text-4xl md:text-5xl lg:text-6xl font-bold mb-4 sm:mb-6 leading-tight tracking-tight text-gray-900">
1919
AI가 전하는<br />
2020
<span className="bg-gradient-to-r from-brand-600 to-navy-600 bg-clip-text text-transparent">
2121
데일리 CS 지식
2222
</span><br />
2323
메일로 만나보세요!
2424
</h1>
2525

26-
<p className="text-xl md:text-2xl mb-12 text-gray-700 max-w-2xl mx-auto leading-relaxed">
27-
AI가 매일 새로운 CS 문제를 생성하고 상세히 해설<br />
28-
<span className="text-lg text-gray-600">개인 수준에 맞는 알고리즘, 자료구조, 운영체제 등</span>
26+
<p className="text-base sm:text-lg md:text-xl lg:text-2xl mb-8 sm:mb-12 text-gray-700 max-w-2xl mx-auto leading-relaxed px-4 sm:px-0">
27+
AI가 매일 새로운 CS 문제를 생성하고 상세히 해설<br className="hidden sm:block" />
28+
<span className="block sm:inline text-sm sm:text-base md:text-lg text-gray-600">개인 수준에 맞는 알고리즘, 자료구조, 운영체제 등</span>
2929
</p>
3030

31-
<div className="flex flex-col sm:flex-row gap-4 justify-center items-center">
31+
<div className="flex flex-col sm:flex-row gap-4 sm:gap-6 justify-center items-center">
3232
<button
3333
onClick={onSubscribeClick}
34-
className="bg-gradient-to-r from-brand-500 to-brand-600 text-white px-8 py-4 rounded-full font-semibold text-lg transition-all duration-300 hover:from-brand-600 hover:to-brand-700 hover:scale-105 hover:shadow-xl group"
34+
className="bg-gradient-to-r from-brand-500 to-brand-600 text-white px-6 sm:px-8 py-3 sm:py-4 rounded-full font-semibold text-base sm:text-lg transition-all duration-300 hover:from-brand-600 hover:to-brand-700 hover:scale-105 hover:shadow-xl group w-full sm:w-auto max-w-xs"
3535
>
36-
<span className="flex items-center">
36+
<span className="flex items-center justify-center">
3737
무료로 구독하기
38-
<svg className="w-5 h-5 ml-2 group-hover:translate-x-1 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
38+
<svg className="w-4 h-4 sm:w-5 sm:h-5 ml-2 group-hover:translate-x-1 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
3939
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
4040
</svg>
4141
</span>
4242
</button>
4343

44-
<div className="flex items-center text-gray-600 text-sm">
45-
<div className="flex -space-x-2 mr-3">
46-
<div className="w-8 h-8 bg-gradient-to-r from-brand-400 to-brand-500 rounded-full border-2 border-white"></div>
47-
<div className="w-8 h-8 bg-gradient-to-r from-navy-400 to-navy-500 rounded-full border-2 border-white"></div>
48-
<div className="w-8 h-8 bg-gradient-to-r from-brand-500 to-navy-500 rounded-full border-2 border-white"></div>
44+
<div className="flex items-center text-gray-600 text-xs sm:text-sm">
45+
<div className="flex -space-x-1 sm:-space-x-2 mr-2 sm:mr-3">
46+
<div className="w-6 h-6 sm:w-8 sm:h-8 bg-gradient-to-r from-brand-400 to-brand-500 rounded-full border-2 border-white"></div>
47+
<div className="w-6 h-6 sm:w-8 sm:h-8 bg-gradient-to-r from-navy-400 to-navy-500 rounded-full border-2 border-white"></div>
48+
<div className="w-6 h-6 sm:w-8 sm:h-8 bg-gradient-to-r from-brand-500 to-navy-500 rounded-full border-2 border-white"></div>
4949
</div>
50-
<span>이미 <strong className="text-gray-800">1,000+</strong> 취준생이 사용 중</span>
50+
<span className="text-center sm:text-left">이미 <strong className="text-gray-800">1,000+</strong> 취준생이 사용 중</span>
5151
</div>
5252
</div>
5353
</div>

src/components/sections/TodayEmailFormSection.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,24 @@ const TodayEmailFormSection: React.FC = () => {
1010
const navigate = useNavigate();
1111

1212
return (
13-
<Section className="py-20">
13+
<Section className="py-12 sm:py-16 lg:py-20">
1414
<Container>
15-
<div className="text-center max-w-3xl mx-auto mb-12">
16-
<div className="inline-flex items-center bg-brand-100 rounded-full px-6 py-2 mb-8">
17-
<span className="text-sm font-medium text-brand-700">📧 메일 미리보기</span>
15+
<div className="text-center max-w-3xl mx-auto mb-8 sm:mb-12 px-4 sm:px-0">
16+
<div className="inline-flex items-center bg-brand-100 rounded-full px-4 sm:px-6 py-2 mb-6 sm:mb-8">
17+
<span className="text-xs sm:text-sm font-medium text-brand-700">📧 메일 미리보기</span>
1818
</div>
1919

20-
<h2 className="text-3xl md:text-4xl font-bold mb-6 text-gray-900">
20+
<h2 className="text-2xl sm:text-3xl md:text-4xl font-bold mb-4 sm:mb-6 text-gray-900">
2121
매일 받게 될 <span className="bg-gradient-to-r from-brand-600 to-navy-600 bg-clip-text text-transparent">CS 지식</span>
2222
</h2>
2323

24-
<p className="text-lg text-gray-600 leading-relaxed mb-8">
24+
<p className="text-base sm:text-lg text-gray-600 leading-relaxed mb-6 sm:mb-8">
2525
아래와 같은 형태의 맞춤형 메일을 받아보실 수 있습니다.
2626
</p>
2727
</div>
2828

2929
{/* Email Template Preview */}
30-
<div className="max-w-4xl mx-auto">
30+
<div className="max-w-4xl mx-auto px-4 sm:px-0">
3131
<EmailTemplate
3232
toEmail="your-email@example.com"
3333
quizLink="/todayQuiz"

0 commit comments

Comments
 (0)