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을 사용하면 두 환경의 타이머가 정확하게 일치하지 않을 가능성이 큽니다. 클라이언트와 서버의 작업 부하, 네트워크 지연, 이벤트 루프 상태 등 여러 요인으로 인해 타이머의 동기화가 어렵습니다.