안녕하세요! 오늘 아홉 번째 프로젝트로 도전해 본 주제는 우리에게 너무나 익숙한 ‘자바스크립트 타이핑 속도 측정기’입니다.
사실 처음 기획할 때는 “글자만 비교하면 끝나는 간단한 로직 아닌가?”라고 가볍게 생각했습니다. 하지만 실제 구현 과정에서 마주친 디테일들은 결코 만만치 않았습니다. 특히 웹 환경에서 ‘한글’이라는 언어가 가진 특수성, 그리고 CSS 레이아웃이 1px 차이로 어긋나는 현상을 해결하며 많은 공부가 되었습니다.
이번 포스팅은 결과물 중심의 설명에서 벗어나, 제가 개발 과정에서 겪은 두 가지 결정적인 시행착오와 이를 해결하기 위해 사용한 코드들을 중심으로 깊이 있게 다뤄보겠습니다.

1. 첫 번째 고비: 왜 자꾸 레이아웃이 삐져나갈까? (CSS Box Model)
디자인을 잡던 중 당혹스러운 문제를 만났습니다. textarea의 너비를 부모 컨테이너에 맞춰 100%로 설정했는데, 이상하게 양옆이 툭 튀어나와 전체적인 균형을 깨뜨리고 있었죠.
문제의 원인은 CSS의 기본 박스 모델 방식인 content-box 때문이었습니다. 우리가 흔히 생각하는 ‘너비’에 안쪽 여백(padding)과 테두리(border) 두께가 더해져 실제 크기가 커진 것이었죠. 저는 이 문제를 해결하기 위해 아래와 같은 코드를 적용했습니다.
.quote-input {
width: 100%; /* 부모 너비를 꽉 채움 */
padding: 15px; /* 안쪽 여백 추가 */
border: 2px solid #ddd; /* 테두리 설정 */
/* 핵심 해결책: 패딩과 테두리를 너비 안에 포함시킴 */
box-sizing: border-box;
}
box-sizing: border-box; 단 한 줄을 추가하는 것만으로도 레이아웃은 다시 평화를 되찾았습니다. 초보자분들이 가장 많이 실수하는 부분이자, 기본기의 중요성을 다시금 체감한 순간이었습니다.
2. 두 번째 고비: 한글 입력 시 발생하는 이벤트 버그
레이아웃을 잡고 나니 이번에는 로직에서 문제가 생겼습니다. 한글은 영어와 달리 자음과 모음이 합쳐지는 ‘조합(Composition)’ 과정을 거칩니다. 처음에는 keydown 이벤트를 사용해 입력값을 체크했는데, 한글이 조합되는 과정에서 이벤트가 중복 발생하거나 마지막 글자가 제대로 인식되지 않는 현상이 나타났습니다.
이 문제를 해결하기 위해 저는 input 이벤트를 활용해 최종적으로 입력창에 반영된 값을 실시간으로 비교하는 로직을 짰습니다.
// 한글 조합 이슈를 피하기 위해 input 이벤트를 감시합니다.
quoteInputElement.addEventListener('input', () => {
const arrayQuote = quoteDisplayElement.querySelectorAll('span');
const arrayValue = quoteInputElement.value.split('');
arrayQuote.forEach((characterSpan, index) => {
const character = arrayValue[index];
// 아직 입력하지 않은 상태
if (character == null) {
characterSpan.classList.remove('correct', 'incorrect');
}
// 입력값이 원문과 정확히 일치할 때
else if (character === characterSpan.innerText) {
characterSpan.classList.add('correct');
characterSpan.classList.remove('incorrect');
}
// 오타가 났을 때
else {
characterSpan.classList.remove('correct');
characterSpan.classList.add('incorrect');
}
});
});
위 코드처럼 원문의 각 글자를 태그로 쪼개어 관리함으로써, 사용자가 타이핑하는 즉시 글자 색상이 변하는 시각적 피드백을 완성할 수 있었습니다.
3. 핵심 알고리즘: WPM(분당 타수)은 어떻게 계산할까?
자바스크립트 타이핑 속도 측정기라면 가장 중요한 지표인 WPM(Words Per Minute) 계산 공식도 빼놓을 수 없습니다. 단순히 전체 글자 수를 시간으로 나누는 것이 아니라, 전 세계 표준인 ‘5글자당 1단어’ 기준을 적용해 신뢰도를 높였습니다.
function updateStats(totalCount) {
const currentTime = new Date();
// 경과 시간을 분(minute) 단위로 계산
const timePassed = (currentTime - startTime) / 60000;
// WPM 공식: (입력 글자 수 / 5) / 경과 시간
const wpm = Math.floor((totalCount / 5) / timePassed);
wpmElement.innerText = wpm || 0;
}
이 로직을 통해 사용자는 본인의 타이핑 실력을 객관적인 수치로 확인할 수 있게 됩니다. 계산 과정에서 new Date() 객체를 활용해 정밀한 측정 시작 시점을 잡아내는 것이 포인트였습니다.
4. 경험: 사용자 인터페이스(UI)의 심리학
자바스크립트 타이핑 속도 측정기 개발 도중 제가 직접 테스트를 해보니, 오타가 났을 때 빨간색 밑줄이 그어지는 것만으로도 사용자는 상당한 압박을 느낀다는 것을 알게 되었습니다. 그래서 정확도(Accuracy) 수치를 실시간으로 보여주되, 너무 자극적이지 않은 색상을 선택하는 등 UX적인 배려를 더했습니다.
또한, ‘새 문장 시작’ 버튼을 눌렀을 때 모든 상태값이 초기화되면서 새로운 도전을 유도하는 흐름을 설계하며, 앱의 완성도는 결국 ‘로직’과 ‘사용자의 기분’ 사이의 균형에서 나온다는 것을 배웠습니다.
5. 셀프 Q&A
- Q: 왜 textarea를 사용했나요?
- A: 긴 문장을 입력받을 때는 input보다 textarea가 가독성이 좋고 줄바꿈 대응이 쉽기 때문입니다. 하지만 이번 프로젝트에서는 높이를 고정하고 줄바꿈을 막아 깔끔한 디자인을 유지했습니다.
- A: 긴 문장을 입력받을 때는 input보다 textarea가 가독성이 좋고 줄바꿈 대응이 쉽기 때문입니다. 하지만 이번 프로젝트에서는 높이를 고정하고 줄바꿈을 막아 깔끔한 디자인을 유지했습니다.
- Q: 시간이 실시간으로 흐르게 하려면 어떻게 하나요?
- A: setInterval 함수를 사용하면 됩니다. 1초(1000ms)마다 화면의 숫자를 1씩 올리는 로직을 추가해 긴박감을 더했습니다.
6. 보완점 및 향후 계획
기존에 만들었던 자바스크립트 타이핑 속도 측정기에 더해서 새롭게 추가하고 싶은 기능들은 다음과 같습니다.
- 다양한 언어 지원: 영문 타수 측정 모드 추가.
- 순위표(Leaderboard): LocalStorage를 활용해 역대 최고 타수를 기록하고 저장하는 기능.
- 다크 모드: 장시간 연습하는 사용자들을 위한 눈 보호 테마.
7. 자바스크립트 타이핑 속도 측정기 실행 영상

이번 타이핑 속도 측정기 프로젝트는 저에게도 참 많은 영감을 주었습니다. 단순히 텍스트를 입력받는 기능을 넘어, 한글 조합의 특수성을 이해하고 CSS의 박스 모델을 다시금 정립하는 계기가 되었기 때문입니다.
타이핑 속도를 계산할 때 단순 글자 수가 아니라 ‘단어 수(WPM)’를 기준으로 로직을 짜는 과정이 꽤 흥미로웠습니다. 특히 한글 타이핑의 경우 초성, 중성, 종성이 분리되는 특성 때문에 오타 체크 로직에서 예외 상황이 많았는데, 이를 정규표현식과 이벤트 리스너의 조합으로 해결하며 자바스크립트의 문자열 처리 능력을 한 단계 높일 수 있었습니다.
이 프로젝트에서 익힌 문자열 비교 로직은 나중에 제작한 [자바스크립트 타자 게임]의 핵심 엔진으로 그대로 활용되었습니다.
개발을 하다 보면 오늘 제가 겪은 레이아웃 어긋남이나 이벤트 중복 발생 같은 ‘예상치 못한 버그’를 무수히 마주하게 됩니다. 그때마다 당황하기보다는 “왜 이런 현상이 생겼을까?”를 고민하며 코드 한 줄의 의미를 파헤치는 과정이 숙련된 개발자로 성장시키는 자양분이 된다고 믿습니다.