Skip to content

Commit ad638ea

Browse files
jammy0903claude
andcommitted
fix: fuzz test fixes and auth simplification
- Fix problems/:id returning 500 on non-UUID IDs (now validates UUID format, returns 400) - Remove GitHub and Kakao OAuth login (only Google remains) - Merge /signup into /login (identical functionality, single route) - Add fuzz test script (scripts/fuzz-test.ts) covering all API endpoints Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 261e958 commit ad638ea

5 files changed

Lines changed: 483 additions & 128 deletions

File tree

packages/backend/src/modules/problems/routes.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,17 @@ export const problemRoutes: FastifyPluginAsync = async (fastify) => {
4343
},
4444
},
4545
}, async (request, reply) => {
46+
const { id } = request.params;
47+
48+
// Validate UUID format to avoid Prisma throwing on invalid UUID
49+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
50+
if (!uuidRegex.test(id)) {
51+
return reply.status(400).send({ error: 'Invalid problem ID format' });
52+
}
53+
4654
try {
4755
const problem = await prisma.problem.findUnique({
48-
where: { id: request.params.id }
56+
where: { id }
4957
});
5058

5159
if (!problem) {

packages/frontend/src/features/auth/AuthPage.tsx

Lines changed: 18 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,41 @@
11
/**
2-
* AuthPage - 통합 로그인/회원가입 페이지
3-
* Firebase 소셜 로그인 (Google, GitHub, Kakao)
2+
* AuthPage - 로그인 페이지
3+
* Firebase 소셜 로그인 (Google)
44
*
5-
* WHY: LoginPage와 SignupPage가 100% 동일한 기능 수행
6-
* TRADEOFF: 단일 컴포넌트로 통합 > 중복 코드 145줄 제거
5+
* WHY: Google OAuth 하나로 로그인/회원가입 자동 처리
6+
* 신규 유저는 initializeAuthListener에서 자동 등록됨
77
*/
88

99
import { useState } from 'react';
10-
import { useNavigate, useLocation } from 'react-router-dom';
10+
import { useNavigate } from 'react-router-dom';
1111
import { useTranslation } from 'react-i18next';
1212
import { motion } from 'framer-motion';
13-
import { loginWithGoogle, loginWithGithub, loginWithKakao } from '@/services/firebase';
14-
import { Github } from 'lucide-react';
13+
import { loginWithGoogle } from '@/services/firebase';
1514
import { logger } from '@/utils/logger';
1615
import { useStore } from '@/stores/store';
1716

1817
export default function AuthPage() {
1918
const { t } = useTranslation();
2019
const navigate = useNavigate();
21-
const location = useLocation();
2220
const setSidebarOpen = useStore((s) => s.setSidebarOpen);
2321
const [error, setError] = useState<string | null>(null);
2422
const [loading, setLoading] = useState(false);
2523

26-
// URL 경로로 로그인/회원가입 구분
27-
const isSignup = location.pathname === '/signup';
28-
const pageTitle = isSignup ? t('auth.signup') : t('auth.login');
29-
const errorPrefix = isSignup ? t('auth.signup') : t('auth.login');
30-
const linkPath = isSignup ? '/login' : '/signup';
31-
const linkText = isSignup ? t('auth.go_login') : t('auth.go_signup');
32-
const linkPrompt = isSignup ? t('auth.have_account') : t('auth.no_account');
33-
3424
const handleGoogleLogin = async () => {
3525
try {
3626
setError(null);
3727
setLoading(true);
3828
await loginWithGoogle();
39-
setSidebarOpen(false); // 로그인 성공 시 사이드바 닫기
29+
setSidebarOpen(false);
4030
navigate('/courses');
4131
} catch (err) {
42-
setError(t('auth.google_failed', { action: errorPrefix }));
32+
setError(t('auth.google_failed', { action: t('auth.login') }));
4333
logger.error('Google login failed:', err);
4434
} finally {
4535
setLoading(false);
4636
}
4737
};
4838

49-
const handleGithubLogin = async () => {
50-
try {
51-
setError(null);
52-
setLoading(true);
53-
await loginWithGithub();
54-
setSidebarOpen(false); // 로그인 성공 시 사이드바 닫기
55-
navigate('/courses');
56-
} catch (err) {
57-
setError(t('auth.github_failed', { action: errorPrefix }));
58-
logger.error('GitHub login failed:', err);
59-
} finally {
60-
setLoading(false);
61-
}
62-
};
63-
64-
const handleKakaoLogin = async () => {
65-
try {
66-
setError(null);
67-
setLoading(true);
68-
await loginWithKakao();
69-
setSidebarOpen(false); // 로그인 성공 시 사이드바 닫기
70-
navigate('/courses');
71-
} catch (err) {
72-
setError(t('auth.kakao_failed', { action: errorPrefix }));
73-
logger.error('Kakao login failed:', err);
74-
} finally {
75-
setLoading(false);
76-
}
77-
};
78-
7939
return (
8040
<div className="min-h-screen flex items-center justify-center px-6">
8141
<motion.div
@@ -87,56 +47,32 @@ export default function AuthPage() {
8747
{/* 타이틀 */}
8848
<div className="text-center mb-12">
8949
<h1 className="text-4xl font-bold text-text mb-4">
90-
{pageTitle}
50+
{t('auth.login')}
9151
</h1>
9252
<p className="text-text-secondary">
9353
{t('auth.social_subtitle')}
9454
</p>
9555
</div>
9656

97-
{/* 소셜 로그인 버튼들 */}
98-
<div className="flex justify-center gap-6">
99-
{/* Google */}
57+
{/* Google 로그인 버튼 */}
58+
<div className="flex justify-center">
10059
<motion.button
101-
whileHover={{ scale: 1.1 }}
60+
whileHover={{ scale: 1.05 }}
10261
whileTap={{ scale: 0.95 }}
10362
onClick={handleGoogleLogin}
10463
disabled={loading}
105-
className="w-16 h-16 flex items-center justify-center bg-white border-2 border-border rounded-full shadow-md hover:shadow-lg hover:border-primary transition-all disabled:opacity-50"
106-
aria-label={t('auth.google_auth', { action: pageTitle })}
64+
className="flex items-center gap-3 px-8 py-4 bg-white border-2 border-border rounded-2xl shadow-md hover:shadow-lg hover:border-primary transition-all disabled:opacity-50"
65+
aria-label={t('auth.google_auth', { action: t('auth.login') })}
10766
>
108-
<svg className="w-7 h-7" viewBox="0 0 24 24">
67+
<svg className="w-6 h-6" viewBox="0 0 24 24">
10968
<path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/>
11069
<path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/>
11170
<path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/>
11271
<path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/>
11372
</svg>
114-
</motion.button>
115-
116-
{/* GitHub */}
117-
<motion.button
118-
whileHover={{ scale: 1.1 }}
119-
whileTap={{ scale: 0.95 }}
120-
onClick={handleGithubLogin}
121-
disabled={loading}
122-
className="w-16 h-16 flex items-center justify-center bg-[#24292e] border-2 border-[#24292e] rounded-full shadow-md hover:shadow-lg hover:bg-[#1a1e22] transition-all disabled:opacity-50"
123-
aria-label={t('auth.github_auth', { action: pageTitle })}
124-
>
125-
<Github className="w-7 h-7 text-white" />
126-
</motion.button>
127-
128-
{/* Kakao */}
129-
<motion.button
130-
whileHover={{ scale: 1.1 }}
131-
whileTap={{ scale: 0.95 }}
132-
onClick={handleKakaoLogin}
133-
disabled={loading}
134-
className="w-16 h-16 flex items-center justify-center bg-[#FEE500] border-2 border-[#FEE500] rounded-full shadow-md hover:shadow-lg hover:bg-[#FFEB3B] transition-all disabled:opacity-50"
135-
aria-label={t('auth.kakao_auth', { action: pageTitle })}
136-
>
137-
<svg className="w-7 h-7 text-[#3C1E1E]" viewBox="0 0 24 24" fill="currentColor">
138-
<path d="M12 3C6.477 3 2 6.477 2 10.75c0 2.745 1.79 5.155 4.5 6.645l-1.125 4.125c-.075.27.21.495.45.345l5.25-3.495c.315.03.63.045.945.045 5.523 0 9.98-3.477 9.98-7.75S17.523 3 12 3z"/>
139-
</svg>
73+
<span className="text-base font-medium text-gray-700">
74+
{t('auth.google_auth', { action: t('auth.login') })}
75+
</span>
14076
</motion.button>
14177
</div>
14278

@@ -150,14 +86,6 @@ export default function AuthPage() {
15086
{error}
15187
</motion.div>
15288
)}
153-
154-
{/* 하단 텍스트 */}
155-
<p className="mt-8 text-center text-sm text-text-tertiary">
156-
{linkPrompt}{' '}
157-
<a href={linkPath} className="text-primary hover:underline font-medium">
158-
{linkText}
159-
</a>
160-
</p>
16189
</motion.div>
16290
</div>
16391
);

packages/frontend/src/layouts/Sidebar.tsx

Lines changed: 1 addition & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { X, Home, BookOpen, Play, Shield, LogOut, UserPlus, FileQuestion, BarCha
1515
import { Link, useLocation } from 'react-router-dom';
1616
import { useTranslation } from 'react-i18next';
1717
import { useStore } from '@/stores/store';
18-
import { logout, loginWithGoogle, loginWithKakao } from '@/services/firebase';
18+
import { logout, loginWithGoogle } from '@/services/firebase';
1919
import { PixelAvatar } from '@/components/PixelAvatar';
2020
import { ReportModal } from '@/components/ReportModal';
2121
import { logger } from '@/utils/logger';
@@ -65,13 +65,6 @@ export function Sidebar() {
6565
}
6666
};
6767

68-
const handleKakaoLogin = async () => {
69-
try {
70-
await loginWithKakao();
71-
} catch (error) {
72-
logger.error('Kakao login failed:', error);
73-
}
74-
};
7568

7669
return (
7770
<AnimatePresence>
@@ -402,34 +395,6 @@ export function Sidebar() {
402395
{t('auth.login_google')}
403396
</motion.button>
404397

405-
{/* 카카오 로그인 */}
406-
<motion.button
407-
onClick={handleKakaoLogin}
408-
whileHover={{ scale: 1.02, y: -2 }}
409-
whileTap={{ scale: 0.98 }}
410-
transition={{ type: 'spring', stiffness: 400, damping: 20 }}
411-
className="w-full flex items-center justify-center gap-2 px-4 py-3 text-sm font-semibold border rounded-lg transition-colors"
412-
style={{
413-
color: 'var(--theme-sidebar-kakao-btn-text)',
414-
backgroundColor: 'var(--theme-sidebar-kakao-btn-bg)',
415-
borderColor: 'var(--theme-sidebar-kakao-btn-border)'
416-
}}
417-
onMouseEnter={(e) => {
418-
e.currentTarget.style.backgroundColor = 'var(--theme-sidebar-kakao-btn-hover-bg)';
419-
}}
420-
onMouseLeave={(e) => {
421-
e.currentTarget.style.backgroundColor = 'var(--theme-sidebar-kakao-btn-bg)';
422-
}}
423-
>
424-
<svg className="w-5 h-5" viewBox="0 0 24 24">
425-
<path
426-
fill="var(--theme-sidebar-kakao-btn-text)"
427-
d="M12 3C6.477 3 2 6.463 2 10.691c0 2.693 1.775 5.063 4.445 6.412-.144.523-.926 3.369-.963 3.592 0 0-.02.166.088.229.108.063.235.015.235.015.31-.043 3.593-2.363 4.159-2.771.339.047.686.071 1.036.071 5.523 0 10-3.463 10-7.548C22 6.463 17.523 3 12 3z"
428-
/>
429-
</svg>
430-
{t('auth.login_kakao')}
431-
</motion.button>
432-
433398
<p className="text-xs text-center pt-1" style={{ color: 'var(--theme-sidebar-copyright-text)' }}>
434399
© 2026 CodeInsight
435400
</p>

packages/frontend/src/router.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ export const router = createBrowserRouter([
2828
children: [
2929
{ index: true, element: <HomePage /> },
3030
{ path: 'login', element: <AuthPage /> },
31-
{ path: 'signup', element: <AuthPage /> },
3231
{
3332
path: 'courses',
3433
element: <CoursesPage />,

0 commit comments

Comments
 (0)