Next.js 성능 최적화, AI 에이전트와 함께할 때 컨텍스트 설계가 먼저다

Next.js 성능 최적화, AI 에이전트와 함께할 때 컨텍스트 설계가 먼저다

LCP·CLS·INP를 고치는 패턴은 이미 알려져 있다—AI 에이전트가 그 패턴을 끝까지 완수하지 못하는 이유는 모델 성능이 아니라 세션이 끝난 뒤 사라지는 추론의 맥락이다.

Next.js 성능 최적화 Core Web Vitals AI 에이전트 컨텍스트 LCP CLS INP App Router 에이전트 메모리 컨텍스트 설계
광고

Next.js App Router로 프로덕션 앱을 최적화해본 사람이라면 알 것이다. LCP 이미지에 priority prop 하나 빠뜨리는 것, Suspense 폴백의 높이가 실제 컴포넌트와 10px 어긋나는 것, 이벤트 핸들러 안에서 200ms짜리 동기 연산이 메인 스레드를 막는 것—어느 하나 사소해 보이지만 Core Web Vitals 점수를 한 번에 무너뜨리는 지뢰들이다. dev.to에 공유된 실전 가이드는 이 세 가지 메트릭의 실패 패턴과 해법을 꽤 정밀하게 짚는다. 문제는 이 지식을 '아는 것'과 AI 에이전트가 프로젝트 전반에 걸쳐 '일관되게 적용하는 것' 사이의 간극이다.

성능 패턴은 명확하다—그래서 더 위험하다

App Router 시대의 Core Web Vitals 최적화는 몇 가지 원칙으로 수렴한다. LCP 이미지는 priority prop을 단 하나에만 달고, sizes 속성으로 뷰포트에 맞는 해상도를 내려받게 한다. CDN 도메인엔 preconnect 링크를 미리 걸어 DNS 조회와 TLS 핸드셰이크 시간을 제거한다. CLS는 Suspense 폴백이 핵심인데, <div>Loading...</div> 한 줄짜리 폴백이 200px짜리 카드를 대체하는 순간 레이아웃 시프트는 재앙이 된다. 폴백 컴포넌트는 실제 컴포넌트와 픽셀 단위로 치수가 일치해야 한다. INP는 무거운 Client Component를 dynamic import로 지연 로딩하고, 동기 연산을 setTimeout으로 메인 스레드 밖으로 밀어내는 것만으로도 극적으로 개선된다.

이 패턴들은 명확하게 문서화되어 있다. 그리고 바로 그 명확함이 함정이다. AI 에이전트는 이 패턴을 알고 있고, 처음 80%는 빠르게 적용한다. 나머지 20%에서 무너진다.

에이전트의 80% 문제는 사실 컨텍스트 소멸 문제다

Addy Osmani가 Google의 SDLC 논문을 분석하며 명명한 '80% 문제'를 lizziepika는 dev.to 아티클에서 다른 각도로 재정의한다. 에이전트가 마지막 20%를 완성하지 못하는 이유는 코드가 어려워서가 아니라, 첫 80%를 만들어낸 추론이 세션 종료와 함께 증발하기 때문이라는 것이다.

성능 최적화 시나리오로 옮겨보면 이렇다. 에이전트에게 LCP 이미지 최적화를 맡겼다. 에이전트는 히어로 이미지에 priority를 달고, CDN에 preconnect를 걸고, sizes 속성까지 설정했다. 그런데 한 달 뒤, 새 팀원이 랜딩 페이지에 배너 이미지를 추가하면서 priority를 함께 달았다. Lighthouse 점수는 그대로인데 실제 LCP는 오히려 올라갔다. 왜? priority를 두 이미지에 동시에 달면 브라우저의 프리로드 우선순위가 충돌하기 때문이다. 에이전트가 '왜 하나에만 달아야 하는가'를 추론했던 맥락은 어디에도 없다. 커밋 메시지도, PR 설명도 결론만 남기고 논거를 버렸다.

에이전트 메모리의 구조적 결함

shudiptotrafder가 CoALA 논문을 기반으로 정리한 에이전트 메모리 분류는 이 문제를 구조적으로 설명한다. 대부분의 팀이 구축한 메모리는 두 가지다. 대화 히스토리(Working Memory)와 벡터 DB 기반 검색(RAG). 그런데 이 둘은 에이전트를 시간이 갈수록 똑똑하게 만들지 않는다. 세션이 끝나면 Working Memory는 사라지고, RAG는 검색만 할 뿐 학습하지 않는다.

성능 최적화 작업에서 진짜 필요한 것은 Episodic Memory다. '이 프로젝트에서 priority를 두 이미지에 달았다가 LCP가 오히려 악화됐고, 그래서 히어로 이미지 하나에만 제한했다'는 사건 기록. 그리고 이 패턴이 반복되면 Semantic Memory로 승격되어 '이 프로젝트의 규칙: priority는 LCP 이미지 하나에만'이라는 사실로 굳어져야 한다. 하지만 대부분의 팀은 이 루프를 구현하지 않았다.

컨텍스트 설계의 실천: 지금 당장 할 수 있는 것

거창한 메모리 아키텍처를 구축하기 전에, AI 에이전트와 함께 Next.js 성능 최적화를 할 때 컨텍스트를 보존하는 방법은 생각보다 단순하다.

의사결정 로그를 코드 옆에 붙인다. PERFORMANCE.md 파일 하나에 '왜 이 컴포넌트는 Client Component인가', '왜 이 이미지에만 priority가 있는가'를 기록한다. 에이전트가 다음 세션에서 이 파일을 컨텍스트로 읽으면, 이전 추론을 재구성할 필요가 없다.

측정 기준을 세션 외부에 고정한다. Lighthouse가 아니라 Search Console의 필드 데이터를 기준으로 삼는다는 원칙을 프로젝트 설정 파일에 명시한다. 에이전트는 매 세션마다 이 기준을 다시 협의하지 않아도 된다.

Suspense 폴백 치수를 디자인 시스템 토큰으로 고정한다. 치수를 하드코딩하지 않고 컴포넌트 스펙으로 문서화하면, 에이전트가 새 폴백을 만들 때 참조할 수 있는 단일 진실 공급원이 생긴다.

출력 평가가 아니라 경로 평가가 필요하다

lizziepika의 표현을 빌리면, 우리에게 필요한 것은 '박스 스코어'가 아니라 '경기 필름'이다. Lighthouse 90점은 박스 스코어다. 그 점수가 어떤 결정의 연쇄를 통해 나왔는지, 어떤 트레이드오프가 있었는지는 필름에 있다. 그런데 현재 대부분의 PR 리뷰 도구는 박스 스코어만 보여준다.

Next.js 성능 최적화에서 이것은 더 직접적인 실패로 이어진다. Core Web Vitals는 상호작용한다. LCP를 위해 이미지를 eager load하면 다른 리소스의 우선순위가 밀려 INP에 영향을 줄 수 있다. Suspense 경계를 늘리면 CLS 리스크가 분산되지만 서버 비용이 달라진다. 이 트레이드오프의 추론을 저장하지 않으면, 에이전트는 다음 세션에서 같은 고민을 처음부터 다시 한다—혹은 하지 않는다.

전망: 성능 최적화의 단위가 바뀐다

가까운 미래에 Next.js 성능 최적화의 작업 단위는 PR이 아니라 '의도-결정-검증'의 아크 전체가 될 것이다. priority prop 하나를 다는 것이 아니라, '왜 이 이미지가 LCP 요소인가', '이 결정을 언제 재검토해야 하는가'가 함께 기록되는 방식으로.

LangMem, Mem0 같은 도구들이 에이전트의 과거 실행에서 패턴을 추출해 지속적인 규칙으로 승격하는 방향으로 발전하고 있는 것도 이 맥락이다. 에이전트가 빨라지는 것은 이미 기정사실이다. 41%의 신규 코드가 AI로 생성된다는 수치는 지금도 빠르게 올라가고 있다. 다음 과제는 에이전트가 남긴 성능 결정들을 팀이 이해하고, 이어가고, 신뢰할 수 있게 만드는 것이다.

Core Web Vitals 체크리스트는 이미 있다. AI 에이전트도 있다. 지금 없는 것은 그 둘 사이에서 추론이 살아남을 수 있는 구조다.

출처

더 많은 AI 트렌드를 Seedora 앱에서 확인하세요