Next.js 앱에서 클릭이 한 박자 느리게 느껴질 때 살펴본 것들
서버가 멀면 응답이 느린 건 어쩔 수 없습니다. 제 경우 오리진 응답 지연에 CDN 우회 경로까지 겹쳐 페이지 응답에 1초 가까이 걸리는 상황이었고, 네트워크 쪽은 당장 손댈 수 없었습니다. 그래서 응답 속도 대신 "눌렀을 때 반응하는 속도"를 올리는 쪽으로 방향을 잡았습니다. 느린 것과 느리게 느껴지는 것은 생각보다 다른 문제였습니다.
처음에는 버튼 컴포넌트를 의심했습니다. 그런데 디자인시스템 버튼에는 이미 active:scale-95가 들어 있었고, 서버 액션을 부르는 버튼들도 누르는 즉시 스피너가 뜨게 돼 있었습니다. 버튼 탓이 아니었습니다. 한참 눌러보다가 찾은 구멍은 세 군데였습니다.
클릭하고 아무 일도 안 일어나는 페이지
loading.tsx가 없는 라우트가 세 개 있었습니다. Next.js 앱 라우터는 라우트에 loading.tsx가 있으면 이동하는 순간 스켈레톤을 먼저 그려주는데, 이게 없는 페이지는 클릭 후 서버 렌더가 끝날 때까지 화면에 아무 변화가 없습니다. 하필 그중 하나가 three.js를 불러오는 3D 페이지라 청크만 876KB였습니다. 클릭, 1초 정지, 화면 전환. "버튼이 늦게 눌린다"는 느낌의 절반은 여기서 나왔던 것 같습니다.
스켈레톤 파일 세 개를 추가하는 게 이번 작업에서 가성비가 제일 좋았습니다. 늦게 뜨는 건 그대로인데, 클릭한 순간 화면이 반응하니까 체감이 다릅니다.
눌렀다는 확인이 없는 링크
사이드바 링크에는 hover 색 전환(transition-colors)만 있었습니다. 누른 순간에는 아무 변화가 없다가, 이동이 끝나야 활성 하이라이트가 옮겨갑니다. 응답이 1초 걸리는 환경에서는 이 1초 동안 "눌린 건가?" 하게 됩니다.
링크에 active:scale와 100ms 트랜지션을 넣어서 누르는 순간 살짝 눌리는 느낌을 줬습니다. CSS 한 줄이지만, 눌렀다는 확인이 즉시 오는 것과 안 오는 것의 차이가 큽니다.
응답을 기다리게 만드는 부수 작업
서버 액션의 응답 경로에 부수 작업이 끼어 있었습니다. 얼마 전 감사 로그와 알림 기능을 넣으면서 권한 검사를 통과할 때 로그를 남기고, 상태가 바뀔 때 알림을 적재하게 해뒀는데, 이게 전부 액션 응답을 기다리게 하는 위치에 있었습니다. 버튼 하나 누를 때마다 본 작업에 DB insert가 두 번 더 순차로 붙는 셈입니다.
Next.js의 after()로 옮겨서 응답을 보낸 뒤에 실행되게 했습니다. 문서를 보니 after 콜백은 액션이 에러를 던져도 실행된다고 해서, 권한 거부 기록이 유실될 걱정도 없었습니다. 로깅이나 알림처럼 응답에 꼭 필요하지 않은 쓰기는 처음부터 여기에 두는 게 맞았습니다.
남은 것
이 세 가지를 고쳐도 네트워크 지연 자체는 1초 그대로입니다. 그래도 클릭하는 순간 반응이 오고 스켈레톤이 뜨는 것만으로 "느리다"는 인상은 꽤 줄었습니다. 응답 시간을 줄일 수 없을 때 남는 선택지는 첫 반응까지의 시간을 줄이는 것이고, 그 첫 반응은 대부분 서버가 아니라 CSS와 파일 하나에서 나옵니다.
같은 작업에서 겪은 다른 문제:
함께 읽기
- 클릭했는데 화면이 늦게 바뀌는 느낌을 줄이는 방법사용자가 버튼이나 링크를 눌렀는데 아무 반응이 없는 것처럼 보이면, 실제 로딩 시간이 길지 않아도 서비스가 느리다고 느낍니다. 이 문제는 단순한 성능 문제가 아니라 체감 성능과 피드백의 문제입니다.
- Next.js 랜딩 페이지의 Lighthouse 점수가 폰트 하나로 해결되지 않는 이유성능 최적화에서 가장 당황스러운 순간은 개발 환경에서는 빠른데 배포 후 측정값만 크게 느릴 때입니다. 이번 랜딩 페이지가 그랬습니다. 개발 환경에서 관찰한 LCP는 약 1.68초였지만 PageSpeed Insights의 첫 측정은 5.5초까지 늘어났습니다. 이후 배포본을 다시 측정했을 때도 성능 점수 72점, LCP 4…
- Next.js는 운영에서 멀쩡한데 로컬 개발 서버에서만 UI가 먹통이 되는 이유Next.js 애플리케이션은 운영 빌드가 안정적이면 개발 서버도 같은 방식으로 움직일 것 같지만, 둘은 클라이언트 코드의 수명이 다릅니다. 제가 확인한 환경은 Next.js 16.2.7과 Turbopack이었고, 운영 사이트에서는 잘 눌리던 상단 메뉴가 로컬 개발 서버에서만 간헐적으로 먹통이 됐습니다.
- Next.js에서 링크를 눌렀는데 로딩 뒤 이전 화면이 다시 나타나는 이유페이지 이동을 빠르게 보이게 만드는 장치는 대개 눈에 잘 띄지 않습니다. 미리 다음 화면을 받아 두거나, 서버 응답을 기다리는 동안 스켈레톤을 보여주는 식입니다. 이번에는 그 두 가지가 한꺼번에 작동하면서 오히려 이동을 늦추고 화면 상태까지 헷갈리게 만들었습니다.
- Google 번역을 켰더니 React 앱이 오류 화면으로 바뀐 이유한국어, 영어, 일본어, 중국어를 직접 제공하는 React 랜딩 페이지에서 예상하지 못한 문제가 생겼습니다. Chrome이 띄운 "이 페이지를 번역하시겠습니까?" 제안을 수락하면 번역이 시작되는 듯하다가, 잠시 뒤 사이트의 "일시적인 오류가 발생했습니다" 화면으로 바뀌었습니다.