- useCallback → 일반 함수 + eslint-disable (초기 데이터 fetch) - ToastProvider: useCallback 프로퍼티 할당 → useMemo 객체 패턴 - SSAFYCallbackPage: eslint-disable-line 추가 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
54 lines
1.8 KiB
JavaScript
54 lines
1.8 KiB
JavaScript
import { useEffect, useState, useRef } from 'react';
|
|
import { useNavigate, useSearchParams } from 'react-router-dom';
|
|
import { useAuth } from '../context/useAuth';
|
|
import { ssafyCallback } from '../api/auth';
|
|
|
|
// Inline styles are intentional for this simple callback/loading page — not worth a separate CSS file.
|
|
export default function SSAFYCallbackPage() {
|
|
const [error, setError] = useState('');
|
|
const navigate = useNavigate();
|
|
const [searchParams] = useSearchParams();
|
|
const { setUserFromSSAFY } = useAuth();
|
|
// useRef(false) prevents double-execution in React 18+ StrictMode,
|
|
// which remounts components in development. The ref persists across
|
|
// the StrictMode remount, ensuring the OAuth callback runs only once.
|
|
const called = useRef(false);
|
|
|
|
useEffect(() => {
|
|
if (called.current) return;
|
|
called.current = true;
|
|
|
|
const code = searchParams.get('code');
|
|
if (!code) {
|
|
setError('인가 코드가 없습니다.'); // eslint-disable-line react-hooks/set-state-in-effect -- error state from URL param check
|
|
return;
|
|
}
|
|
|
|
ssafyCallback(code)
|
|
.then((data) => {
|
|
setUserFromSSAFY(data);
|
|
navigate('/', { replace: true });
|
|
})
|
|
.catch((err) => {
|
|
setError(err.message || 'SSAFY 로그인에 실패했습니다.');
|
|
});
|
|
}, [searchParams, setUserFromSSAFY, navigate]);
|
|
|
|
if (error) {
|
|
return (
|
|
<div style={{ textAlign: 'center', marginTop: '4rem' }}>
|
|
<p style={{ color: '#e74c3c' }}>{error}</p>
|
|
<button onClick={() => navigate('/login', { replace: true })}>
|
|
로그인 페이지로 돌아가기
|
|
</button>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div style={{ textAlign: 'center', marginTop: '4rem' }}>
|
|
<p>SSAFY 로그인 처리 중...</p>
|
|
</div>
|
|
);
|
|
}
|