1-1) 실제 클라이언트와 서버 간의 밀리초 단위의 시간 차가 존재
0.001 ~ 0.005s
Gap 구하는 방법
- 서버시간
- 클라이언트 시간
- gap: 서버 시간 - 클라이언트 시간
- 게임 타이머: 전달받은 타이머 + Gap
1-2) 코드 해석
: 모달창 겹치는 현상이 발생하는 socket Event: "showModal"
부분에서 Gap을 더한 타이머를 해당 ModalProgress의 setState값에 전달
const sockets = {
showModal: (title: string, timer: number, serverTime: any) => {
//클라이언트 시간
const clientTime = new Date();
//gap: 서버 시간- 클라이언트 시간
const timeGap = serverTime - clientTime.getTime();
// gap을 초단위로 변경
const timeGapSeconds = timeGap / 1000;
// 게임 타이머: 전달받은 타이머 + Gap
const gameTime = timer + timeGapSeconds;
//타이머 전달
setTime(gameTime)
}
}
1-3) ModalProgress 컴포넌트 코드 해석
const ModalProgress = () => {
const isModal = useModalIsOpen();
const timer = useModalTimer();
const [count, setCount] = useState(timer * 1000); //ms 단위
const { setIsOpen } = useModalActions();
//NOTE - 타이머 기능
useCountDown(() => setCount((prevCount) => prevCount - 1), 1, isModal);
// 0같거나 작을 경우 -> 모달창 종료
useEffect(() => {
if (count <= 0 && isModal) {
setIsOpen(false);
}
}, [count]);
return(
<progress className={S.progress} value={(timer * 10 - count) * (100 / (timer * 10))} max={90}></progress>
)
};
1-3) useCountDown 컴포넌트 코드 해석
export const useCountDown = (callback: () => void, delay: number, isCount: boolean) => {
const savedCallback = useRef<() => void>(callback);
useEffect(() => {
if (!isCount || !delay) {
return;
}
//현재 저장된 콜백 함수 호출
const tick = () => {
savedCallback.current();
};
const timerId = setInterval(tick, delay);
return () => {
clearInterval(timerId);
};
}, [isCount]);
};
두 번째 방법으로 문제가 해결되지 않은 이유에 대한 생각
setInterval의 delay를 1ms 단위로 countDown 실행 시 정상 작동되지 않는 걸 확인할 수 있었다. MDN 문서를 통해 알게된 "브라우저에서 자체적으로 delay 최소값인 시간 간격을 4ms 적용"
이 원인이었다.
현재 서버와 클라이언트 간의 시간 차이로 인해 발생하는 문제는 단순히 소켓 이벤트 전송 시에만 발생하는 것이 아니라, 서버와 클라이언트에서 각각 실행되는 setInterval 시간의 불일치로 인해 발생한다고 판단
setInterval 함수의 비동기성 및 Web API: setInterval 함수는 비동기적으로 동작하는 Web API 함수입니다. 자바스크립트는 싱글 스레드 환경에서 작동하기 때문에 한 번에 하나의 명령만 처리할 수 있습니다.
이벤트 루프와 태스크 큐: setInterval 함수는 지정된 간격마다 콜백 함수를 태스크 큐에 추가합니다. 이벤트 루프는 콜 스택이 비어 있을 때 태스크 큐에서 작업을 가져와 실행합니다. 따라서 기존 명령이 실행 중인 경우, setInterval로 추가된 콜백 함수는 대기해야 합니다.
정확한 시간 보장 어려움: 이러한 구조로 인해 클라이언트 측의 setInterval 함수는 정확한 시간 간격을 보장하기 어렵습니다. 이벤트 루프가 바쁘거나 다른 작업이 실행 중이면 지연이 발생할 수 있습니다.
클라이언트와 서버 간의 동기화 문제: 클라이언트와 서버에서 각각 setInterval을 사용하면 두 환경의 타이머가 정확하게 일치하지 않을 가능성이 큽니다. 클라이언트와 서버의 작업 부하, 네트워크 지연, 이벤트 루프 상태 등 여러 요인으로 인해 타이머의 동기화가 어렵습니다.