최근에 운영 중인 여러 웹 도구(JavaScript 기반)에서 공통적으로 발생하는 문제가 있어서 이를 해결한 과정을 공유해보려고 함. 문제는 간단히 말하면 초기화 순서 문제와 에러 처리 부족이었는데, 이게 쌓이고 쌓여 사용자 경험(UX)에도 영향을 주고, 개발자 입장에서도 디버깅하느라 시간을 많이 잡아먹는 상황이었음. 그래서 한 번 제대로 손을 봤음.
문제 상황
웹 도구를 사용하다 보니 아래와 같은 문제가 자주 발생했음:
- TypeError: Cannot read properties of undefined (reading 'trim') 오류
- DOM 요소가 준비되기 전에 접근하려다 발생하는 오류로, 특정 기능이 제대로 작동하지 않음.
- 초기화 순서 문제
- Bootstrap 같은 외부 라이브러리가 로드되기도 전에 초기화 코드를 실행하는 경우가 있었음.
- DOMContentLoaded 이벤트만 믿고 초기화를 시도했는데, 외부 의존성은 아직 로드되지 않은 상태라 실패하는 케이스가 많았음.
- 에러 처리 미흡
- 필수 DOM 요소가 없거나 초기화가 실패했을 때 사용자에게 아무런 피드백도 주지 않음.
- 그냥 조용히 작동을 멈추거나 콘솔에만 에러를 뿌리는데, 일반 사용자는 당연히 이걸 알 리가 없으니 답답한 상황.
이 문제가 발생한 주요 도구는 URL 인코더/디코더, Base64 변환기, JSON 포매터, 이미지 정리 도구, 서브넷 계산기 같은 것들이었음. 대부분 간단한 유틸리티인데도 이런 문제가 반복되니 꽤 골치 아팠음.
원인 분석
문제를 하나씩 뜯어봤더니 공통적으로 아래 두 가지가 원인이었음:
1. 초기화 순서 문제
- 모든 도구가 DOMContentLoaded 이벤트에 의존해서 초기화를 진행하고 있었음.
- 문제는 DOM은 준비됐어도 Bootstrap 같은 외부 의존성이 로드되지 않은 경우가 많았다는 거임.
- 비동기로 로드되는 스크립트와 초기화 코드의 실행 순서를 제대로 관리하지 못한 것도 큰 문제였음.
2. 에러 처리 부족
- 필수 DOM 요소가 없으면 그냥 null 값을 반환하는데, 이를 확인하지 않고 바로 메서드를 호출하다 보니 TypeError가 발생함.
- 초기화 실패 시 사용자에게 아무런 안내도 없어서 UX적으로 최악의 상황이었음.
해결 과정
1. 공통 초기화 패턴 만들기
문제를 해결하려면 일단 모든 웹 도구에서 사용할 수 있는 일관된 초기화 패턴부터 만들어야겠다고 생각했음. 그래서 아래와 같은 구조로 코드를 정리했음:
개선된 초기화 코드
// 초기화 함수 분리 function initializeToolName() { // Bootstrap 의존성 체크 if (typeof bootstrap === 'undefined') { console.error('Bootstrap is not loaded'); return; } // DOM 요소 캐싱 const elements = { element1: document.getElementById('element1'), element2: document.getElementById('element2'), // 필요한 요소 추가... }; // 필수 요소 확인 const missingElements = Object.entries(elements) .filter(([key, element]) => !element) .map(([key]) => key); if (missingElements.length > 0) { console.error('Required elements not found:', missingElements.join(', ')); return; } // 이벤트 리스너 등록 등 초기화 작업 console.log('Tool initialized successfully'); } // 의존성 체크 함수 function waitForDependencies(callback, maxAttempts = 10) { let attempts = 0; function checkDependencies() { attempts++; if (typeof bootstrap !== 'undefined') { callback(); } else if (attempts < maxAttempts) { setTimeout(checkDependencies, 100); // 재시도 } else { console.error('Dependencies failed to load'); } } checkDependencies(); } // 초기화 로직 실행 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { waitForDependencies(initializeToolName); }); } else { waitForDependencies(initializeToolName); }
적용한 주요 변경점:
- Bootstrap 의존성 체크
- typeof bootstrap === 'undefined'로 의존성 상태를 확인하고 없으면 바로 종료.
- DOM 요소 누락 확인
- 필수 DOM 요소를 객체로 캐싱하고, 누락된 요소를 한 번에 확인해서 에러 메시지를 출력.
- 비동기 로딩 관리
- waitForDependencies라는 재시도 함수로 외부 의존성이 로드될 때까지 기다림.
- 초기화 실패 시 명확한 로그 제공
- 콘솔에 어떤 요소가 없는지, 왜 실패했는지를 명확히 출력하도록 개선.
2. 템플릿 파일 수정
HTML 템플릿에서도 약간의 수정이 필요했음. 특히 스크립트 로드 순서를 명확히 하기 위해 defer 속성을 적극 활용했음.
수정된 템플릿 예제:
<script> // 스크립트 로딩 상태 추적 window.webtoolsState = window.webtoolsState || {}; window.webtoolsState.toolNameLoaded = false; </script> <!-- Bootstrap 로드 후 실행되도록 defer 추가 --> <script src="{% static 'js/webtools/tool_name.js' %}" defer></script>
개선 결과
이번 작업을 통해 얻은 결과는 다음과 같았음:
1. 안정성 향상
- Bootstrap 의존성을 명확히 체크하면서 초기화 순서를 보장할 수 있었음.
- DOM 요소 누락 시 명확한 에러 메시지를 제공해 디버깅 시간을 단축.
2. 코드 품질 향상
- 모든 웹 도구에 일관된 초기화 패턴을 적용하면서 코드 재사용성과 가독성이 대폭 향상됨.
- 에러 처리가 체계적으로 이루어지면서 유지보수가 훨씬 쉬워짐.
3. 사용자 경험 개선
- 초기화 실패 시 사용자에게 적절한 피드백을 제공하면서 혼란을 줄일 수 있었음.
- 페이지 새로고침 없이도 정상 작동하도록 개선됨.
교훈과 앞으로의 계획
이번 작업을 하면서 느낀 점과 앞으로 개선해야 할 부분은 다음과 같음:
- 의존성 관리의 중요성
- 외부 라이브러리나 프레임워크를 사용할 때는 항상 로딩 상태를 명확히 관리해야 함.
- 일관된 패턴의 필요성
- 모든 코드에서 동일한 구조와 패턴을 유지하면 유지보수와 확장이 훨씬 쉬워짐.
- 자동화된 테스트 추가 필요
- DOM 준비 상태나 의존성 체크 같은 주요 기능에 대한 자동 테스트를 추가할 계획임.
- 사용자 피드백 강화
- 단순히 콘솔 로그만 남기는 게 아니라 UI 상에서도 오류 상황을 안내할 수 있는 방안을 고민 중임.
이번 작업은 비교적 간단한 유틸리티 도구들에서 발생하는 문제였지만, 이런 작은 문제들이 쌓이면 결국 전체 시스템 안정성을 해칠 수 있다는 걸 다시 한 번 느꼈던 계기가 됐음. 앞으로도 이런 기본적인 부분들을 놓치지 않도록 더 꼼꼼히 작업해야겠다는 생각이 들었음!