2318 단어
12 분
개발한 사이트 소스코드 해설

아래는 제가 짠 사이트의 코드 해설입니다. (이 포스트 작성하는데 1시간 걸린듯). https://time.pixelhize.xyz/ 사이트에 해당 코드가 적용되어 있고 소스코드는 https://github.com/PIXELHIZE/time-tool 에 있습니다. 매우 자세하게 설명 해놨으니 참고해서 코드 이해해 보세요.

// ---------------------------------------------
// 변수 선언 및 HTML 요소 가져오기
// ---------------------------------------------

// [스톱워치 관련 변수]
// 스톱워치 시작 시각 (밀리초 단위)
let stopwatchStartTime = 0; 
// 스톱워치가 업데이트 될 때 계산되는 경과 시간
let stopwatchUpdatedTime = 0; 
// 일시정지 후 다시 시작할 때 이전에 경과한 시간을 저장
let stopwatchDifference = 0; 
// setInterval()의 ID를 저장 (스톱워치 업데이트에 사용)
let stopwatchInterval; 
// 스톱워치가 실행 중인지 여부
let isStopwatchRunning = false; 

// [타이머 관련 변수]
// 타이머 시작 시각 (밀리초 단위)
let timerStartTime = 0; 
// 타이머 업데이트를 위한 setInterval()의 ID 저장
let timerInterval; 
// 타이머가 실행 중인지 여부
let isTimerRunning = false; 
// 사용자가 입력한 타이머 총 지속 시간 (밀리초 단위)
let timerDuration = 0; 

// [HTML 요소 참조: 스톱워치]
// 스톱워치 시간을 표시할 요소
const stopwatchDisplay = document.getElementById('stopwatchDisplay');
// 스톱워치 시작/일시정지 버튼
const startPauseStopwatchBtn = document.getElementById('startPauseStopwatchBtn');
// 스톱워치 초기화 버튼
const resetStopwatchBtn = document.getElementById('resetStopwatchBtn');

// [HTML 요소 참조: 타이머]
// 타이머 시간을 표시할 요소
const timerDisplay = document.getElementById('timerDisplay');
// 사용자가 타이머 시간을 입력할 입력창들 (시, 분, 초)
const timerHourInput = document.getElementById('timerHourInput');
const timerMinuteInput = document.getElementById('timerMinuteInput');
const timerSecondInput = document.getElementById('timerSecondInput');
// 타이머 입력 영역 전체를 감싸는 컨테이너 (타이머 실행 시 숨김)
const timerInputContainer = document.getElementById('timerInputContainer');
// 타이머 시작/일시정지 버튼
const startPauseTimerBtn = document.getElementById('startPauseTimerBtn');
// 타이머 초기화 버튼
const resetTimerBtn = document.getElementById('resetTimerBtn');

// [HTML 요소 참조: 시계]
// 현재 시각을 표시할 시계 요소
const clockDisplay = document.getElementById('clockDisplay');

// [탭 및 헤더 관련 요소]
// 각 기능(스톱워치, 타이머, 시계) 화면의 컨테이너
const stopwatchTab = document.getElementById('stopwatch');
const timerTab = document.getElementById('timer');
const clockTab = document.getElementById('clock');
// 페이지 상단 헤더 영역과 헤더 보이기/숨기기 버튼
const header = document.getElementById('header');
const toggleHeaderBtn = document.getElementById('toggleHeaderBtn');


// ---------------------------------------------
// 탭 네비게이션 및 헤더 토글 기능
// ---------------------------------------------

// 사용자가 탭 버튼 클릭 시 해당 탭만 보이도록 설정
document.getElementById('stopwatchBtn').addEventListener('click', () => {
    showTab('stopwatch'); // 스톱워치 탭 표시
});
document.getElementById('timerBtn').addEventListener('click', () => {
    showTab('timer'); // 타이머 탭 표시
});
document.getElementById('clockBtn').addEventListener('click', () => {
    showTab('clock'); // 시계 탭 표시
});

// 헤더 토글 버튼 클릭 시, 헤더 영역을 보이거나 숨김
toggleHeaderBtn.addEventListener('click', () => {
    if (header.style.display === 'none') {
        header.style.display = 'flex';
        toggleHeaderBtn.textContent = 'Hide Header';
    } else {
        header.style.display = 'none';
        toggleHeaderBtn.textContent = 'Show Header';
    }
});

// showTab 함수: 전달된 탭 이름에 따라 모든 탭은 숨기고 선택한 탭만 보여줌
function showTab(tabName) {
    // 모든 탭을 숨김 처리
    stopwatchTab.style.display = 'none';
    timerTab.style.display = 'none';
    clockTab.style.display = 'none';

    // 선택된 탭만 다시 보이도록 설정
    if (tabName === 'stopwatch') {
        stopwatchTab.style.display = 'flex';
    } else if (tabName === 'timer') {
        timerTab.style.display = 'flex';
    } else if (tabName === 'clock') {
        clockTab.style.display = 'flex';
    }
}


// ---------------------------------------------
// 스톱워치 기능
// ---------------------------------------------

// startPauseStopwatch 함수: 스톱워치를 시작하거나 일시정지하는 기능
function startPauseStopwatch() {
    if (!isStopwatchRunning) {
        // 스톱워치가 실행 중이 아니면 시작 혹은 재시작
        // 이전 일시정지 상태의 경과 시간(timerDifference)을 반영하여 시작 시간을 보정
        stopwatchStartTime = new Date().getTime() - stopwatchDifference;
        // 10밀리초 간격으로 updateStopwatch 함수 호출하여 화면 업데이트
        stopwatchInterval = setInterval(updateStopwatch, 10); 
        startPauseStopwatchBtn.textContent = 'Pause'; // 버튼 텍스트 변경
        isStopwatchRunning = true; // 실행 상태 업데이트
    } else {
        // 스톱워치가 실행 중이면 일시정지
        clearInterval(stopwatchInterval); // 업데이트 멈춤
        // 현재까지 경과된 시간을 저장하여 이후 재시작 시 사용
        stopwatchDifference = new Date().getTime() - stopwatchStartTime;
        startPauseStopwatchBtn.textContent = 'Start';
        isStopwatchRunning = false;
    }
}

// resetStopwatch 함수: 스톱워치 초기화(시간 리셋 및 인터벌 정리)
function resetStopwatch() {
    clearInterval(stopwatchInterval); // 실행중인 타이머 정지
    stopwatchStartTime = 0;
    stopwatchUpdatedTime = 0;
    stopwatchDifference = 0;
    isStopwatchRunning = false;
    // 화면에 초기 시간(00:00:00.000) 표시
    stopwatchDisplay.textContent = '00:00:00.000';
    startPauseStopwatchBtn.textContent = 'Start';
}

// updateStopwatch 함수: 스톱워치가 실행 중일 때 주기적으로 호출되어 경과 시간을 계산 후 갱신
function updateStopwatch() {
    // 현재 시간과 시작 시간의 차이를 계산하여 경과 시간 업데이트
    stopwatchUpdatedTime = new Date().getTime() - stopwatchStartTime;

    // 경과 시간을 밀리초, 초, 분, 시 단위로 계산
    let milliseconds = Number.parseInt((stopwatchUpdatedTime % 1000) / 10); // 10ms 단위
    let seconds = Math.floor((stopwatchUpdatedTime / 1000) % 60);
    let minutes = Math.floor((stopwatchUpdatedTime / (1000 * 60)) % 60);
    let hours = Math.floor((stopwatchUpdatedTime / (1000 * 60 * 60)) % 24);

    // 두 자리 숫자 형식으로 맞추기 위해 앞에 0 추가 (예: 5 → 05)
    milliseconds = milliseconds < 10 ? `0${milliseconds}` : milliseconds;
    seconds = seconds < 10 ? `0${seconds}` : seconds;
    minutes = minutes < 10 ? `0${minutes}` : minutes;
    hours = hours < 10 ? `0${hours}` : hours;

    // 스톱워치 디스플레이에 시간 업데이트 (형식: HH:MM:SS.ms)
    stopwatchDisplay.textContent = `${hours}:${minutes}:${seconds}.${milliseconds}`;
}

/* [예시: 스톱워치 사용]
   - 사용자가 'Start' 버튼을 누르면 startPauseStopwatch()가 호출되어 스톱워치가 시작됩니다.
   - 시간이 흐르며 stopwatchDisplay에 실시간으로 경과 시간이 "00:00:05.23"와 같이 업데이트됩니다.
   - 'Pause' 버튼을 누르면 스톱워치가 일시정지되고, 다시 'Start'를 누르면 이전 경과 시간부터 재개됩니다.
   - 'Reset' 버튼을 누르면 모든 값이 초기화되어 "00:00:00.000"이 표시됩니다.
*/


// ---------------------------------------------
// 타이머 기능
// ---------------------------------------------

// startPauseTimer 함수: 타이머를 시작하거나 일시정지하는 기능
function startPauseTimer() {
    if (!isTimerRunning) {
        // 타이머 시작 전, 사용자가 입력한 시, 분, 초 값을 읽어옴 (잘못된 입력은 0으로 처리)
        const hours = Number.parseInt(timerHourInput.value) || 0;
        const minutes = Number.parseInt(timerMinuteInput.value) || 0;
        const seconds = Number.parseInt(timerSecondInput.value) || 0;

        // 입력값을 밀리초로 변환하여 timerDuration 계산
        timerDuration = (hours * 3600 + minutes * 60 + seconds) * 1000;
        timerStartTime = new Date().getTime();

        if (timerDuration > 0) {
            // 유효한 타이머 시간일 경우, 10밀리초 간격으로 updateTimer 호출
            timerInterval = setInterval(updateTimer, 10);
            startPauseTimerBtn.textContent = 'Pause';
            // 타이머 실행 중에는 입력창을 숨겨 사용자의 값 변경을 방지
            timerInputContainer.style.display = 'none';
            isTimerRunning = true;
        }
    } else {
        // 타이머가 실행 중일 경우 일시정지: 남은 시간을 재계산 후 업데이트
        clearInterval(timerInterval);
        timerDuration -= (new Date().getTime() - timerStartTime);
        startPauseTimerBtn.textContent = 'Start';
        isTimerRunning = false;
    }
}

// resetTimer 함수: 타이머 초기화 (시간 리셋, 인터벌 중지, 입력창 복원)
function resetTimer() {
    clearInterval(timerInterval);
    isTimerRunning = false;
    // 타이머 디스플레이를 초기값으로 재설정
    timerDisplay.textContent = '00:00:00.00';
    startPauseTimerBtn.textContent = 'Start';
    // 타이머 입력 영역을 다시 보이게 함
    timerInputContainer.style.display = 'block';
}

// updateTimer 함수: 타이머가 실행 중일 때 남은 시간을 계산하여 갱신
function updateTimer() {
    // 남은 시간을 계산: 설정된 시간에서 경과한 시간을 뺌
    const remainingTime = timerDuration - (new Date().getTime() - timerStartTime);

    if (remainingTime <= 0) {
        // 남은 시간이 0 이하가 되면 타이머 종료 처리
        clearInterval(timerInterval);
        timerDisplay.textContent = '00:00:00.00';
        isTimerRunning = false;
        startPauseTimerBtn.textContent = 'Start';
        timerInputContainer.style.display = 'block';
        alert('Time is up!'); // 타이머 종료 알림
        return;
    }

    // 남은 시간을 밀리초, 초, 분, 시 단위로 분해
    let milliseconds = Number.parseInt((remainingTime % 1000) / 10);
    let seconds = Math.floor((remainingTime / 1000) % 60);
    let minutes = Math.floor((remainingTime / (1000 * 60)) % 60);
    let hours = Math.floor((remainingTime / (1000 * 60 * 60)) % 24);

    // 두 자리 형식 맞추기
    milliseconds = milliseconds < 10 ? `0${milliseconds}` : milliseconds;
    seconds = seconds < 10 ? `0${seconds}` : seconds;
    minutes = minutes < 10 ? `0${minutes}` : minutes;
    hours = hours < 10 ? `0${hours}` : hours;

    // 타이머 디스플레이 업데이트 (형식: HH:MM:SS.ms)
    timerDisplay.textContent = `${hours}:${minutes}:${seconds}.${milliseconds}`;
}

/* [예시: 타이머 사용]
   - 사용자가 시, 분, 초 입력창에 예를 들어 "0", "1", "30"을 입력하면 타이머는 1분 30초로 설정됩니다.
   - 'Start' 버튼을 누르면 타이머가 시작되어 남은 시간이 "00:01:30.00"에서 카운트 다운됩니다.
   - 중간에 'Pause'를 누르면 일시정지되고, 다시 'Start'를 누르면 일시정지한 시점부터 계속 카운트 다운됩니다.
   - 시간이 모두 경과하면 alert 창이 뜨고, 타이머는 초기 상태로 복원됩니다.
*/


// ---------------------------------------------
// 시계 기능
// ---------------------------------------------

// updateClock 함수: 현재 시간을 가져와 시계 디스플레이에 갱신
function updateClock() {
    const now = new Date();

    let seconds = now.getSeconds();
    let minutes = now.getMinutes();
    let hours = now.getHours();

    // 1자리 수일 경우 앞에 0을 붙여 두자리 형식 유지
    seconds = seconds < 10 ? `0${seconds}` : seconds;
    minutes = minutes < 10 ? `0${minutes}` : minutes;
    hours = hours < 10 ? `0${hours}` : hours;

    // 시계 디스플레이에 현재 시각 표시 (형식: HH:MM:SS)
    clockDisplay.textContent = `${hours}:${minutes}:${seconds}`;
}

// 시계는 1초마다 updateClock 함수를 호출해 현재 시각을 업데이트
setInterval(updateClock, 1000);

/* [예시: 시계 사용]
   - 페이지에 접속하면 시계 탭에 현재 시간이 "08:07:05"와 같이 표시됩니다.
   - 매 초마다 시간이 갱신되어 정확한 현재 시간을 보여줍니다.
*/


// ---------------------------------------------
// 이벤트 리스너 등록 및 초기 탭 설정
// ---------------------------------------------

// 각 버튼에 대해 클릭 이벤트를 등록하여 해당 기능을 수행하도록 함
startPauseStopwatchBtn.addEventListener('click', startPauseStopwatch);
resetStopwatchBtn.addEventListener('click', resetStopwatch);

startPauseTimerBtn.addEventListener('click', startPauseTimer);
resetTimerBtn.addEventListener('click', resetTimer);

// 페이지 로드 시 기본적으로 스톱워치 탭을 보여줌
showTab('stopwatch');