JSP + Spring FE 트러블슈팅 정리

요즘 취직하고 이래저래 할 것이 많아서 제대로 된 글을 작성하지 못했습니다.

회사에서 내어준 과제를 진행하며 겪은 문제를 정리했습니다.

트러블 슈팅

1. Overlay 문제

  • 문제 : 요소가 다른 요소의 Overlay 순서가 꼬이는 문제
  • 원인 : z-index 값이 잘못 들어가있음
  • 해결 : z-index의 값을 요소 별로 적절히 배분

2. 메인 화면 슬라이드 전환간 Button의 “선택 상태”

  • 문제
    • radio처럼 기본적으로 한 개가 선택되어 있어야 함
    • 화면 전환 시 그에 맞는 버튼으로 자동으로 선택되어야 함
    • 버튼을 클릭하면 그에 맞는 화면으로 전환되어야 함
  • 원인
    • is-active가 아닌 active 옵션 사용 및 EventListener 미활용
  • 해결
    • is-active 옵션 추가
      <button type="button" class="home-toggle-btn is-active" id="alarmONButton">
          알림<br>ON
      </button>
      <button type="button" class="home-toggle-btn" id="searchONButton">
          검색<br>ON
      </button>
    • for-each문을 활용해 버튼을 순회하며 상태 관리
      buttons.forEach(btn => {
      btn.addEventListener('click', () => {
          buttons.forEach(b => b.classList.remove('is-active'));
              btn.classList.add('is-active');
          });
      });
    • JS를 활용해 화면 전환 구현(기존 코드 재활용)
      alarmONButton.addEventListener('click', () => {
          window.moveToSlide(0);
      });
      
      searchONButton.addEventListener('click', () => {
          window.moveToSlide(1);
      });
    • 추가로 궁금한 점
      1. 왜 radio를 사용하지 않고 button을 사용할까?
        • radio라는 편한 기능이 있는데, for-each 등을 활용해 button으로 구현하는 이유가 궁금해 찾아보았다.
        • 디자인 제약
          • 브라우저마다 모양이 다르고, 크기·색상·애니메이션 적용이 거의 불가능
          • border-radius와 같은 CSS 직접 적용 불가
        • 상호작용 제어가 제한적 : 클릭 영역이 작고, “눌렀다”는 피드백을 주기 어려움
        • 아이콘 + 텍스트, 설명 문구, 카드 형태의 선택 UI 등 복잡한 상태 표현이 힘듦
      2. window가 무엇인가?
        • 브라우저 전체를 대표하는 최상위 객체로 탭 하나, 그 안의 페이지, 화면, 스크롤, 위치, 전역 변수 등 모든 것을 감쌈
        • 버튼을 클릭하면 window 객체에서 moveToSlide 함수를 호출하여 인자를 전달
        • JS에서 전역으로 선언한 함수는 자동으로 window의 속성이 됨
        • 단순히 moveToSlide로 작성해도 되지만 아래 이유로 window를 명시
          1. 전역 함수라는 것을 명확히 하기 위해
          2. 이름 충돌 방지 및 의도 표현
          3. 추후 구조 변경이 용이
      3. 어떻게 바로 실행되는가?
        (() => {
          // 코드
        })();
        • 마지막 ()를 통해 JS 파일이 로드될 때 바로 호출

기타 궁금한 점 등

FE의 동작 조건

  1. HTML이 요소(노드)를 생성 : DOM 생성
    • 브라우저가 HTML을 읽으면 <div> 같은 요소들이 DOM(Documented Object Model) 트리로 만들어짐
    • JS는 DOM을 통해 요소를 제어할 수 있음
  2. CSS가 어떻게 보여줄지 정의
    • 레이아웃, 스타일, 애니메이션 등 결정
  3. JS가 이벤트를 결정
    • 이벤트가 발생했을 때 DOM/CSS 값을 바꿔서 화면 상태를 갱신
  4. JS 실행 타이밍 조건
    • JS가 DOM 요소를 잡으려면, 그 요소가 DOM에 이미 존재해야 함
    • 스크립트가 너무 위에서 실행되거나, 요소 이름이 바뀌거나, 페이지마다 구조가 다르면 동작하지 않음
  • JS는 어떻게 이벤트를 받아 처리하고 화면을 구성할까?
    • JS는 이벤트를 만드는 주체가 아닌, 이벤트를 기다리다 발생하면 처리하는 역할
    • 이벤트 발생 -> 이벤트 객체 생성 -> 리스너 실행 -> 상태 변경 -> DOM/CSS 갱신 -> 브라우저가 다시 그림

이벤트

  • 이벤트란?
    • 브라우저에서 발생하는 신호·사건
    • 사용자 입력, 마우스, 화면·브라우저, 로딩 등
    • 이벤트가 발생하면 브라우저는 Event 객체를 생성해 핸들러에게 전달
  • 이벤트 핸들러란?
    • 특정 이벤트가 발생했을 때 실행할 함수를 등록해두는 장치
    • 구조
      btnRight.addEventListener("click", () => { ... });
      • bntRight : 이벤트를 받을 대상
      • click : 이벤트 종류
      • () => { ... } : 이벤트 발생 시 실행할 콜백 함수
  • 리스너만 있으면 이벤트를 다 받을 수 있을까?
    • 아니다. 어디에 붙였는지와 전파에 따라 다름
    • 이벤트는 타겟 요소에서 발생
      • click은 실제 클릭한 요소에서 발생
      • 그 요소에 리스너를 붙이면 확실히 받음
    • 이벤트는 전파될 수 있음
      • 대부분의 DOM 이벤트는 부모 방향으로 전파(Bubbling)되므로 자식에서 발생한 이벤트는 부모에서 받을 수도 있음
    • 안받아지는 경우
      • DOM 요소가 존재하기 전에 리스너를 단 경우
      • display: none인 요소에 이벤트를 기대하는 경우
      • 이벤트가 bubbling되지 않는 타입인 경우
        • focus, blur, mouseenter, mouseleave
      • stopPropagation()으로 중간에 끊긴 경우
      • 다른 요소가 위에서 덮은 경우
      • CustomEvent를 Dispatch하지 않은 경우
  • 리스너는 다중 구조를 가질 수 있음
    • 이벤트 전파(Event Propagation)
      • 이벤트는 DOM 트리를 타고 이동하는데 순서가 있음
      1. Capturing : 위(문서) -> 아래(타겟)로 내려감
      2. Target : 실제 이벤트가 발생한 요소에서 처리
      3. Bubbling : 아래(타겟) -> 위(문서)로 올라감
      • 기본적으로 addEventListener는 Bubbling 단계에서 동작
    • 이벤트 위임(Event Delegation)
      • 자식이 많을 때 자식마다 리스너를 다는 것이 아니라, 부모 하나만 달아서 처리하는 패턴
      • 자식에서 click 이벤트 발생 -> 이벤트가 부모로 Bubbling -> 부모 리스너가 e.target을 보고 어떤 자식이 눌렀는지 판별

Dispatch

  • Listener는 듣고만 있고 dispatchEvent()는 이벤트가 발생했다고 브라우저에 알리는 행위
  • addEventListener는 마이크를 켜둔 것이고, dispatchEvent는 실제로 말을 하는 것

index.jsp에서 슬라이드가 동작하는 과정

  1. 초기화 후 즉시 실행
  2. state 만들기
    let index = 0;
    const maxIndex = slides.length - 1;
  3. render()를 통해 화면 그리기
    const render = () => {
      track.style.transform = `translateX(-${index * 50}%)`;
      btnLeft.classList.toggle("is-disabled", index === 0);
      btnRight.classList.toggle("is-disabled", index === maxIndex);
    };
    • 트랙 위치 갱신(transform)
    • 버튼 UI 상태 갱신
  4. 버튼 클릭 시 상태를 바꾸고 render()
    btnRight.addEventListener("click", () => {
      index = Math.min(maxIndex, index + 1);
      render();
    });
  5. 외부에서도 슬라이드를 이동할 수 있도록 공개