✨ 한 줄 요약:
React의 렌더링 타이밍과 브라우저 쿠키 반영 시점은 완전히 같지 않다.
AuthProvider, Router, Axios, 그리고 TTS/STT의 동작 순서를 의식적으로 설계해야
세션 기반 인증이 안정적으로 유지된다.
useAuth() 훅에서 상태를 유지하지 못하고 null로 초기화됨 useState)는 새로고침 시 메모리에서 사라짐withCredentials 누락1) 전역 AuthProvider 개선
// src/api/loginstate.jsx
useEffect(() => {
const fetchUser = async () => {
try {
const res = await api.get("/members/info", { withCredentials: true });
if (res.data.success) setUser(res.data.data);
} catch {
setUser(null);
}
};
fetchUser();
}, []);
2) Axios 인스턴스에 쿠키 옵션 상시 적용
// src/api/axiosInstance.js
const api = axios.create({
baseURL: "http://localhost:8080",
withCredentials: true,
});
로그인 후 /mypage로 이동했는데, user가 null로 남아있음
로그인 성공 직후 navigate("/mypage") 실행 시, 아직 user 상태가 세팅되지 않아
페이지가 다시 리다이렉트됨 (ProtectedRoute에서 user === null로 판단)
AuthProvider.login()은 비동기적으로 상태를 갱신하므로
navigate()가 먼저 실행되면 아직 user가 업데이트되지 않은 상태임
// LoginForm.jsx
const handleLogin = async (e) => {
e.preventDefault();
try {
const response = await api.post("/members/login", { mid, password_hash: password }, { withCredentials: true });
const member = response.data.data;
if (member) {
login(member); // 상태 먼저 반영
setTimeout(() => navigate("/mypage"), 100); // navigate 약간 지연
}
} catch {
alert("로그인 실패");
}
};
상태 업데이트(setState)는 즉시 반영되지 않는다.
로그인 직후 라우팅 시에는 약간의 딜레이 또는 useEffect 기반 감지를 사용하자.
AuthProvider가 user를 아직 가져오기 전인데
ProtectedRoute에서 user === null로 판정되어 /login으로 강제 이동
// ProtectedRoute.jsx
export default function ProtectedRoute({ element }) {
const { user } = useAuth();
const location = useLocation();
if (user === undefined) return null; // 아직 fetchUser 중일 때 로딩 차단
if (user === null) return <Navigate to="/login" state={{ from: location }} replace />;
return element;
}
undefined 상태를 별도로 두면 초기 fecth 시점의 '로딩' 상태와
진짜 로그아웃 상태(null)를 명확히 구분할 수 있다.
페이지 이동 시 이전 음성이 끊기지 않고 겹쳐서 출력
버튼 클릭 시마다 speechSynthesis.speak()가 중첩 실행됨
SpeechSynthesisUtterance 인스턴스가 여러 번 생성됨
언마운트 시점에 speechSynthesis.cancel()이 호출되지 않아 누적됨
// TTS 유틸 함수
const speak = (text) => {
window.speechSynthesis.cancel(); // 이전 음성 중단
const utter = new SpeechSynthesisUtterance(text);
utter.lang = "ko-KR";
utter.rate = 0.9;
utter.pitch = 1.0;
utter.volume = 1.0;
window.speechSynthesis.speak(utter);
};
자동 예매 화면(SeniorSeatAuto.jsx)에서
SpeechRecognition이 중복 실행되어 “매수를 선택해주세요”가 반복 출력됨.
Chrome에서 not-allowed 에러 발생 (SpeechRecognitionErrorEvent)
컴포넌트 리렌더링 시 recognition.start()가 중복 호출됨
브라우저 마이크 권한이 즉시 허용되지 않아 초기화 실패
const sttRestarting = useRef(false);
useEffect(() => {
if (!recognition) {
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
const recog = new SpeechRecognition();
recog.lang = "ko-KR";
recog.onresult = handleResult;
recog.onerror = (e) => console.error("음성 인식 오류:", e);
setRecognition(recog);
}
}, []);
const startListening = () => {
if (!sttRestarting.current) {
sttRestarting.current = true;
recognition.start();
setTimeout(() => (sttRestarting.current = false), 1500); // 재시작 간격 제한
}
};
참고 : window.SpeechRecognition은 Chrome 보안 정책상 HTTPS 환경 또는 localhost에서만 동작한다.
useEffect 내부의 TTS 또는 Axios 호출이 무한 루프로 실행됨
“한 번만 실행되어야 하는” 코드가 여러 번 호출되어 브라우저 과부하
useEffect의 의존성 배열 누락 또는 잘못된 상태 포함
상태 업데이트(setState)가 트리거되어 다시 useEffect 호출됨
// 잘못된 예시
useEffect(() => {
speak("안내 음성입니다.");
setCount(count + 1); // 상태 변경 → 재렌더 → 무한 루프
}, [count]);
// 수정된 예시
useEffect(() => {
speak("안내 음성입니다.");
// 빈 배열 → 최초 렌더링 시 1회만 실행
}, []);