
안녕하세요!
그동안 우리는 텍스트 기반의 데이터와 간단한 로직들을 위주로 다뤄왔습니다. 하지만 웹 개발의 세계는 정적인 텍스트를 넘어 역동적인 시각적 결과물을 만들어낼 때 그 진가가 드러나곤 하죠. 이번에 제가 도전한 주제는 바로 ‘자바스크립트 아날로그 시계’입니다.
단순히 현재 시간을 숫자로 보여주는 디지털시계는 누구나 쉽게 만들 수 있습니다. 하지만 시계바늘이 중심점을 기준으로 회전하고, 초침의 움직임에 따라 분침과 시침이 미세하게 각도를 바꾸는 아날로그 시계는 조금 다른 차원의 접근이 필요합니다. 이번 포스팅에서는 라이브러리 없이 순수 자바스크립트(Vanilla JS)와 CSS 변수만을 이용해 다크 모드 환경에 최적화된 정교한 시계를 구현하는 과정을 아주 상세히 공유해 보려 합니다.
핵심 설계의 시작: 삼각함수와 각도
자바스크립트 아날로그 시계 구현의 가장 큰 기술적 장벽은 ‘시간을 어떻게 각도로 변환할 것인가’입니다. 원은 360도이고, 시계는 이를 12시간, 60분, 60초로 나눕니다. 산술적으로 계산하면 1초가 흐를 때 초침은 360 / 60 = 6°씩 이동해야 합니다.
하지만 여기서 ‘디테일’의 차이가 발생합니다. 초침이 정직하게 6도씩 툭툭 끊겨 움직이면 시계가 매우 투박해 보입니다. 저는 실제 고급 아날로그 시계처럼 ‘연속성’을 살리고 싶었습니다. 그래서 분침을 계산할 때 단순히 분 정보만 쓰는 것이 아니라, 현재 흐르고 있는 ‘초’의 값도 계산에 포함했습니다.
예를 들어, 30초가 지났다면 분침은 단순히 그 자리에 있는 것이 아니라 다음 분을 향해 3°(6°의 절반)만큼 더 기울어져 있어야 합니다. 시침 역시 마찬가지입니다. 45분이 지났다면 시침은 숫자와 숫자 사이의 75% 지점에 도달해 있어야 하죠.
// 시침 각도 계산의 핵심 디테일
// 시간당 30도(360/12) + 분에 따른 추가 각도(30도 * 분/60)
const hourDegrees = (hours / 12) * 360 + (mins / 60) * 30;
이러한 세밀한 수식 처리는 사용자가 시계를 보았을 때 “정말 정교하게 만들어졌구나”라는 느낌을 받게 만드는 결정적인 요소가 됩니다.
UI 디자인: CSS 변수와 다크 테마
이번 자바스크립트 아날로그 시계의 디자인 컨셉은 ‘미드나잇 블루(Midnight Blue)’입니다. 15번째 프로젝트에서 다루었던 다크 모드 감성을 한층 더 끌어올리기 위해 #1e293b 색상을 베이스로 사용했습니다.
특히 이번에 강조하고 싶은 기술은 CSS 변수(–variable)의 활용입니다. 이전에는 스타일 시트에 고정된 값을 적었다면, 이번에는 색상 코드와 바늘의 굵기 등을 모두 변수화했습니다.
:root {
--bg-color: #0f172a;
--clock-bg: #1e293b;
--accent-blue: #3b82f6;
--sec-color: #ef4444; /* 초침 포인트 컬러 */
}
왜 이렇게 번거롭게 변수를 쓸까요? 이는 나중에 테마 변경 기능을 추가하거나, 특정 기념일에 시계 색상을 바꾸고 싶을 때 자바스크립트 로직을 전혀 건드리지 않고 CSS 변수값만 동적으로 교체하면 되기 때문입니다. 이는 유지보수 효율성을 극대화하는 개발 방식입니다.
또한 시계바늘의 움직임에는 transition 속성을 적용했습니다. 하지만 일반적인 linear 방식은 기계적인 느낌이 강합니다. 그래서 cubic-bezier 값을 조정하여 초침이 이동한 후 아주 미세하게 탄력을 받으며 안착하는 듯한 느낌을 주었습니다. 이는 실제 태엽 시계의 물리적인 움직임을 웹 환경에 재현하려는 시도였습니다.
트러블슈팅: 360도의 함정
자바스크립트 아날로그 시계를 만들면서 가장 당황스러웠던 순간은 초침이 59초에서 0초로 넘어가는 찰나였습니다. 각도가 354°에서 0°로 갑자기 리셋되면, 브라우저의 transition 효과는 이를 “뒤로 한 바퀴 빠르게 회전하라”는 명령으로 오해합니다. 결과적으로 초침이 시계 반대 방향으로 360도를 휘감으며 돌아가는 버그가 발생했죠.
이를 해결하기 위해 여러 방법을 고민했습니다.
- 첫 번째 시도: 0초가 되는 순간 transition을 잠시 끄는 방법. (코드가 지저분해졌습니다.)
- 두 번째 시도: 각도를 360도에서 멈추지 않고 계속 누적시키는 방법. (숫자가 무한히 커지는 리스크가 있었습니다.)
결국 저는 자바스크립트의 시간 객체를 매 초 새로 호출하되, 시각적인 튀김 현상을 최소화할 수 있는 최적의 로직을 선택했습니다. 이 과정을 통해 단순히 화면에 보여주는 것을 넘어, 브라우저가 애니메이션을 처리하는 매커니즘을 깊이 이해할 수 있었습니다.
완성된 자바스크립트 아날로그 시계
보완점 및 발전 방향
애니메이션의 정밀도: requestAnimationFrame 도입
현재 우리가 구현한 시계는 setInterval을 통해 1,000ms(1초)마다 화면을 갱신합니다. 하지만 자바스크립트의 타이머 함수는 브라우저의 다른 작업 부하에 따라 미세하게 지연될 수 있다는 단점이 있습니다. 이를 보완하기 위해 브라우저의 주사율에 맞춰 부드러운 애니메이션을 보장하는 requestAnimationFrame() 인터페이스를 도입해 볼 수 있습니다. 이를 활용하면 초침이 딱딱 끊기며 움직이는 것이 아니라, 실제 고급 기계식 시계(무브먼트)처럼 물 흐르듯 부드럽게 회전하는 효과를 줄 수 있어 시각적인 완성도가 높아질 것입니다.
CSS 변수를 활용한 다이나믹 테마 확장
이번 프로젝트에서 우리는 :root 가상 선택자에 CSS 변수를 정의했습니다. 이는 단순히 코드를 깔끔하게 만드는 것을 넘어, 확장성을 위한 포석입니다. 자바스크립트의 document.documentElement.style.setProperty() 메서드를 활용하면 사용자가 버튼 하나로 시계의 테마를 실시간으로 바꿀 수 있는 기능을 구현할 수 있습니다. 예를 들어 낮에는 밝은 ‘라이트 모드’, 밤에는 눈이 편안한 ‘다크 모드’, 혹은 강렬한 ‘네온 모드’ 등으로 시계 색상을 즉시 변경하는 인터랙션을 추가한다면 훨씬 더 풍성한 프로젝트가 될 것입니다.
오디오 API를 활용한 청각적 경험(Tick-Tock)
시각적인 즐거움에 더해 청각적인 요소를 추가하는 것도 좋을 것 같습니다. 웹 오디오 API를 사용하여 1초마다 아주 짧고 깔끔한 ‘째깍’ 소리(Ticking Sound) 추가하면 사용자가 화면을 보지 않아도 시간의 흐름을 느낄 수 있게 하는 훌륭한 장치가 될 것입니다.
이번 자바스크립트 아날로그 시계를 만들면서 삼각함수를 활용한 좌표 계산과 CSS 변수의 유연함이 만났을 때, 우리는 거창한 라이브러리 없이도 충분히 아름답고 기능적인 인터페이스를 구축할 수 있다는 점을 알았습니다. 내 블로그의 한 구석에서 묵묵히 돌아가는 이 시계처럼, 저와 여러분의 코딩 실력도 매 초 조금씩, 하지만 멈추지 않고 성장하고 있습니다.
시곗바늘이 매 초마다 회전할 때 각도를 계산하는 로직에서 자바스크립트의 Math 객체와 Date 객체를 적절히 조합하는 것이 관건이었습니다. 특히 CSS의 transform-origin 속성을 이해하지 못해 바늘이 엉뚱한 곳을 중심으로 도는 버그를 잡느라 고생했는데, 이를 통해 자바스크립트와 CSS가 어떻게 긴밀하게 상호작용하는지 확실히 배울 수 있었습니다.
시간 데이터를 다루는 또 다른 방식이 궁금하시다면, 밀리초 단위까지 계산하는 [자바스크립트 스톱워치] 포스팅도 함께 확인해 보세요.
단순한 ‘시계 만들기’ 예제는 많지만, 그 속에 담긴 고민의 깊이는 만드는 사람에 따라 천차만별입니다. 이번 프로젝트가 여러분의 블로그에 방문하는 다른 개발자들에게도 좋은 영감이 되기를 바랍니다. 다음 17번째 프로젝트는 더 놀라운 주제로 찾아오겠습니다!