Skip to Content
기타AWSAmplify 캐싱15. 관측: CloudWatch 로그와 헤더 종합 판독

이번 편의 결과물: 임의의 페이지 요청 하나를 두고 어느 캐시 계층(CDN, 데이터 캐시, 라우트 캐시, 캐시 없음)이 응답을 만들었는지 헤더만으로 판정하는 절차를, notice-board의 모든 라우트에 적용해 봅니다. · 다루는 개념: x-cache 값별 의미, CloudWatch Hosting compute logs 확인 절차, 캐시 계층 판별 우선순위

05~13편에서 각 캐시 계층을 하나씩 다뤘습니다. 이 편은 그 지식을 합쳐, 응답 하나를 보고 “이건 어느 계층이 만든 응답인가”를 순서대로 판단하는 절차를 만듭니다.

이 편에서 만드는 파일

notice-board/ └── scripts/ └── inspect-cache.mjs + (여러 라우트에 요청을 보내 헤더를 표로 출력)

개념 정리

헤더 세 개의 역할

헤더누가 붙이는가의미
x-cacheCloudFront이 요청을 CDN 캐시에서 바로 줬는지, 오리진(컴퓨트)까지 다녀왔는지
ageCloudFront지금 응답이 캐시에 저장된 뒤 몇 초가 지났는지
cache-controlNext.js/컴퓨트이 응답을 얼마나 오래, 어떤 조건으로 캐시해도 되는지에 대한 원본의 지시

x-cache 값 읽는 법

의미
Hit from cloudfront유효한 캐시 사본을 엣지에서 바로 반환. 컴퓨트는 실행되지 않음
RefreshHit from cloudfront캐시가 오래됐지만 오리진에 재검증해 그대로 써도 된다고 확인받은 뒤 반환
Miss from cloudfront엣지에 캐시가 없어 오리진(컴퓨트)까지 요청이 도달함
Error from cloudfront오리진 또는 CloudFront 자체에서 오류가 발생함

판별 순서

  1. x-cacheHit/RefreshHit이면 컴퓨트는 이번 요청에서 실행되지 않았다고 볼 수 있습니다. CloudWatch에도 이 요청에 대응하는 실행 로그가 없어야 정상입니다.
  2. x-cacheMiss면 컴퓨트가 실행됐다는 뜻이므로, cache-control 값으로 그 안에서 어떤 Next.js 캐시 정책이 적용됐는지 봅니다. s-maxage가 있으면 ISR(07편), private, no-cache면 동적 렌더링입니다.
  3. 페이지 안에서 여러 fetch를 부르는 경우, CDN·컴퓨트 캐시와는 별개로 요청 메모이제이션(08편)이 있으므로 서버 콘솔 로그로 같은 렌더 패스 안에서 fetch 호출 횟수를 따로 확인합니다.
  4. Miss인데도 응답이 빠르다면 데이터 캐시(06편)나 DynamoDB cacheHandler(13편)가 저장해 둔 값을 읽었을 가능성이 큽니다. CloudWatch 로그의 실행 시간으로 실제 렌더링이 일어났는지 구분합니다.

CloudWatch 로그 확인 절차

Amplify 콘솔 → 앱 선택 → 왼쪽 탐색 창 MonitoringHosting compute logs로 이동해, 확인하려는 브랜치의 CloudWatch 로그 그룹을 선택합니다. 이 화면에서 방금 요청이 컴퓨트까지 도달했는지, 실행 중 오류가 있었는지를 확인합니다.

실습

1. 헤더 점검 스크립트 작성

매번 curl -I로 여러 경로를 확인하는 대신, 한 번에 표로 보여주는 스크립트를 만듭니다.

// notice-board/scripts/inspect-cache.mjs const baseUrl = process.argv[2] const paths = ['/', '/notices/1', '/api/notices', '/api/notices/1'] if (!baseUrl) { console.error('사용법: node scripts/inspect-cache.mjs https://배포주소') process.exit(1) } async function inspectPath(path) { const response = await fetch(`${baseUrl}${path}`, { cache: 'no-store' }) return { path, status: response.status, xCache: response.headers.get('x-cache') ?? '(없음)', age: response.headers.get('age') ?? '(없음)', cacheControl: response.headers.get('cache-control') ?? '(없음)', } } const results = await Promise.all(paths.map(inspectPath)) console.table(results)

fetchcache: 'no-store'를 준 것은 스크립트를 실행하는 Node.js 쪽에서 결과를 캐시하지 않기 위해서입니다. 실제 응답에 담긴 CDN 캐시 상태는 헤더로 그대로 확인할 수 있습니다.

2. 배포된 주소로 실행

node scripts/inspect-cache.mjs https://main.xxxxxxxxxxxx.amplifyapp.com
┌─────────┬────────────────┬────────┬─────────────────────────┬─────┬───────────────────────────┐ │ (index) │ path │ status │ xCache │ age │ cacheControl │ ├─────────┼────────────────┼────────┼─────────────────────────┼─────┼───────────────────────────┤ │ 0 │ '/' │ 200 │ 'Hit from cloudfront' │ '12'│ 'public, s-maxage=60' │ │ 1 │ '/notices/1' │ 200 │ 'Miss from cloudfront' │ '0' │ 'private, no-cache' │ └─────────┴────────────────┴────────┴─────────────────────────┴─────┴───────────────────────────┘

표의 숫자는 실습 환경마다 다르게 나옵니다. 중요한 건 각 라우트의 xCachecacheControl 조합이 개념 정리의 판별 순서와 맞는지 직접 대조하는 것입니다.

3. CloudWatch 로그와 대조

같은 시각에 x-cacheMiss였던 라우트에 한해, Amplify 콘솔의 Hosting compute logs를 열어 해당 요청 로그가 실제로 남아있는지 확인합니다. Hit였던 라우트는 로그가 남지 않아야 정상입니다.

4. notice-board 전체 라우트에 적용

inspect-cache.mjspaths 배열에 있는 네 라우트 각각에 대해, 아래 표를 채워 어느 계층이 응답을 만들었는지 결론을 적어 봅니다.

라우트x-cachecache-control결론
/(기록)(기록)(CDN 히트 / ISR 재생성 / 동적)
/notices/1(기록)(기록)(기록)
/api/notices(기록)(기록)(기록)
/api/notices/1(기록)(기록)(기록)

직접 해보기

  1. 요청 경로 끝에 의미 없는 쿼리스트링(?debug=1)을 붙여 다시 요청해 보고, x-cacheMiss로 바뀌는지 확인해 보세요. CloudFront는 쿼리스트링을 포함한 URL 전체를 캐시 키로 쓰기 때문입니다.
  2. 브라우저 개발자 도구 Network 탭에서 같은 요청을 두 번 보내고, 두 번째 요청이 브라우저 자체 캐시(디스크 캐시)에서 온 것인지 CDN에서 온 것인지 x-cache 유무로 구분해 보세요.

정답 보기

CloudFront 기본 설정에서는 쿼리스트링이 다르면 다른 캐시 키로 취급되므로 ?debug=1을 붙이면 처음 보는 요청이 되어 Miss from cloudfront가 나옵니다. 브라우저 디스크 캐시로 응답이 온 경우 요청 자체가 서버로 나가지 않으므로 응답 헤더 목록에 x-cache가 아예 잡히지 않고, Network 탭에 (disk cache) 같은 표시가 붙습니다.

자주 하는 실수

증상원인고치는 법
브라우저에서 항상 캐시된 것처럼 보임개발자 도구의 Disable cache 옵션을 꺼둠Network 탭에서 Disable cache를 체크한 뒤 다시 확인한다
age가 항상 (없음)으로 나옴애초에 캐시되지 않는 응답(동적 라우트)을 확인 중cache-controlprivate인 라우트는 age가 없는 것이 정상이다
CloudWatch에 로그 그룹이 안 보임서비스 역할에 CloudWatch Logs 권한이 없음Amplify가 자동 생성한 역할을 쓰고 있는지 확인하거나 필요한 logs:* 권한을 추가한다
매번 x-cacheMiss만 나옴매 요청에 쿼리스트링이나 캐시 방지 헤더가 섞여 들어감스크립트의 요청 경로와 옵션에 불필요한 값이 없는지 확인한다

확인 문제

문제 14지선다
x-cache 값이 RefreshHit from cloudfront일 때 실제로 일어난 일은
문제 24지선다
x-cache가 Miss from cloudfront인 요청에서 응답이 유독 빠를 때 의심할 수 있는 것은
문제 34지선다
CloudWatch에서 Amplify SSR 로그를 확인하는 콘솔 경로는
문제 44지선다
같은 페이지 안에서 fetch가 여러 번 호출되는지 확인하려면 어떤 방법이 알맞은가

참고 자료

Last updated on