Skip to content

Latest commit

 

History

History
85 lines (57 loc) · 5.13 KB

File metadata and controls

85 lines (57 loc) · 5.13 KB

befit-ai.js 공통 모듈의 null 참조 트러블슈팅 가이드

본 문서는 BeFit 프로젝트의 **AI 분석 모듈(befit-ai.js)**이 특정 DOM 요소가 없는 페이지에서 로드될 때 발생하는 Cannot read properties of null 오류의 원인 및 해결 과정을 정리한 기술 문서입니다.

작성자: 왕택준

작성일: 2025년 6월 27일


1. 문제 현상

  • 에러 메시지: Uncaught TypeError: Cannot read properties of null (reading 'age') (또는 ...reading 'value', ...reading 'addEventListener' 등 다양한 속성으로 발생)

  • 에러 콜 스택:

    initEventListeners @ befit-ai.js:100
    main @ befit-ai.js:152
    (anonymous) @ befit-ai.js:163
    
  • 발생 페이지: index.html을 제외한 모든 페이지 (befit-ai.html, playlist.html, shopping.html)

  • 발생 배경: AI 분석 기능(폼 요소)이 없는 일반 페이지에서 /src/befit-ai/befit-ai.js 모듈이 실행될 때

  • 상세 설명: befit-ai.js는 페이지 로드 시 특정 ID(dietForm-befit-ai)를 가진 폼과 그 하위 요소들이 반드시 존재한다고 가정하고 동작합니다. 콜 스택에서 볼 수 있듯, 메인 로직(main)이 이벤트 리스너를 초기화하는 initEventListeners 함수를 호출할 때, 이 함수 내에서 존재하지 않는 DOM 요소를 참조하여 null이 반환되고, 해당 null 값의 속성(age 등)에 접근하려다 런타임 에러가 발생합니다.


2. 원인 분석

문제의 근본 원인은 모듈의 의존성방어 코드 부재에 있습니다.

  1. DOM 의존성: 모듈 로직 전체가 dietForm-befit-ai 폼과 그 하위 요소들(예: age 입력 필드)의 존재를 전제로 설계되었습니다.
  2. null 참조 발생: initEventListeners 함수 내에서 document.getElementById('age-input')과 같은 코드가 null을 반환합니다.
  3. null에 대한 속성 접근: 이 null 값에 대해 .value.addEventListener 같은 속성/메서드에 접근하면서 TypeError가 발생하고, 스크립트 실행이 즉시 중단됩니다. 에러 메시지의 (reading 'age')는 아마도 form.elements.age와 같이 객체의 속성으로 DOM 요소에 접근하려다 발생한 것으로 추정됩니다.

3. 해결 과정

3-1. 비효율적인 접근 방식

  • 각 함수/이벤트 리스너 내부에 개별적으로 if (element)와 같은 null 체크를 추가하는 방법을 초기에 고려하였으나, 이 방식은 조건문 중복, 코드 가독성 저하, 추후 유지보수 난이도 상승 등의 문제가 있음을 확인하였습니다.

3-2. 최종 해결책: Guard Clause 패턴 적용

// /src/befit-ai/befit-ai.js

function main() {
    // 모듈의 핵심 의존성인 폼 요소를 찾습니다.
    const dietForm = document.getElementById('dietForm-befit-ai');

    // Guard Clause: 모듈 실행에 필수적인 폼 요소가 없으면 실행을 즉시 중단합니다.
    if (!dietForm) {
        // console.log("AI 분석 폼이 없는 페이지이므로 초기화를 건너뜁니다.");
        return;
    }

    // --- 아래 코드는 dietForm이 존재하는 것이 보장된 상태에서만 실행됩니다 ---
    initEventListeners(dietForm); // dietForm을 인자로 전달하여 중복 탐색 방지
    Utils.toggleBMRMode();
    Storage.updateShowSavedResultBtnVisibility();
}

// DOM이 완전히 로드된 후 메인 로직을 실행합니다.
document.addEventListener("DOMContentLoaded", main);
  • 핵심 해결책 (Guard Clause): main 함수의 가장 앞단에서 if (!dietForm) return; 조건을 추가했습니다. 이 한 줄로 인해, AI 분석 폼이 없는 페이지에서는 initEventListeners 함수 자체가 호출되지 않아 오류가 원천 차단됩니다.
  • 안전장치 (DOMContentLoaded): DOM 요소를 참조하기 전에 문서가 준비될 때까지 기다리게 함으로써, 스크립트 실행 시점과 관련된 잠재적 오류를 추가로 방지합니다.

4. 결론 및 배운 점

  • 문제: 특정 페이지의 DOM 요소에 강하게 의존하는 공용 모듈은, 다른 페이지에서 로드될 때 다양한 형태의 null 참조 오류를 일으키기 쉽습니다.
  • 해결: 모듈의 진입점에서 Guard Clause(가드 클로즈) 패턴을 사용하여 자신의 실행 환경을 스스로 검사하고, 조건이 맞지 않으면 조용히 실행을 종료하는 방어적 초기화(Defensive Initialization) 구조를 적용했습니다.
  • 배운 점: 모듈을 설계할 때, "필요한 요소가 언제나 존재할 것"이라고 가정해서는 안 됩니다. 모든 페이지에서 안전하게 임포트될 수 있도록, 자신의 실행 여부를 스스로 결정하는 독립적이고 방어적인 구조를 갖추는 것이 견고한 웹 애플리케이션의 핵심임을 깨달았습니다.

본 문서는 특정 DOM에 의존하는 공용 모듈의 null 참조 오류를 방지하기 위한 구조적 개선 사례를 기록한 기술 문서입니다.