'기능이 완성됐다'는 말은 생각보다 훨씬 좁은 의미다. 버튼이 클릭되고, 페이지가 이동하고, 데이터가 표시된다. 하지만 그 버튼이 스크린 리더 사용자에게도 눌리는지, 애니메이션이 느린 기기에서도 올바르게 동작하는지, SEO 메타 태그가 실제로 유효한 절대 경로를 가리키는지—이런 것들은 '기능 완성' 이후의 영역으로 밀려나기 쉽다. 그리고 대부분의 경우, 그 영역에서 제품의 진짜 품질이 결정된다.
최근 두 편의 글이 이 문제를 각기 다른 각도에서 정확하게 짚어냈다. 하나는 Astro + GSAP + Lenis 조합의 모션 디자인 랜딩 페이지에서 반복적으로 발생하는 프로덕션 버그를 정리한 사례(dev.to)이고, 다른 하나는 접근성 감사(audit) 결과를 체크리스트처럼 처리하는 관행의 구조적 한계를 지적하는 엔지니어링 관점의 글(dev.to)이다. 두 글의 표면적 주제는 다르지만, 가리키는 방향은 같다. 품질 문제는 대부분 개별 버그가 아니라 시스템 설계의 빈틈에서 발생한다.
모션 버그는 왜 로컬에서 안 보이나
Astro + GSAP + Lenis 스택은 최근 퍼포먼스와 비주얼 완성도를 동시에 잡으려는 랜딩 페이지에서 자주 선택되는 조합이다. 문제는 이 세 라이브러리가 각자의 영역에서 독립적으로 설계되어 있다는 점이다. Lenis는 네이티브 스크롤을 가상의 부드러운 스크롤로 대체하지만, GSAP의 ScrollTrigger는 그 사실을 모른다. 네이티브 스크롤 이벤트를 기다리고 있는데, Lenis가 그 이벤트를 가로채 버린 것이다. 결과는 콘솔 에러 없이 조용히 멈춰버리는 스크롤 트리거 애니메이션이다.
이런 버그가 까다로운 이유는 에러 메시지가 없기 때문이다. 요소는 정상적으로 렌더링됐고, 스크립트도 오류 없이 실행됐다. 단지 이벤트 연결이 누락됐을 뿐인데, 그 누락이 눈에 보이지 않는다. 해결책은 한 줄짜리 연결이다—`lenis.on("scroll