AEM Edge 기능 개발
목표는 서드파티 API에서 동적 데이터를 가져오기 위해 AEM Edge 함수를 호출하는 동적 Edge Delivery Services 블록을 빌드하는 것입니다.
첫 번째 단계는 AEM Edge 함수를 개발하는 것입니다. 이 함수는 두 개의 업스트림 API 호출을 하나의 응답으로 결합하는 끝점을 노출하고 로컬 개발을 위해 CORS를 활성화합니다. 요청 및 응답 계약, 끝점 일치 및 아웃바운드 fetch() 기본 사항에 대해서는 Edge 함수를 사용하여 API 끝점 만들기를 참조하십시오. 이 자습서와 관련된 부분만 여기에서 다룹니다.
보일러판에서 시작
AEM Edge 함수 프로젝트(Edge Delivery Services에서 AEM Edge 함수 설정에서 복제됨)는 두 개의 샘플 경로 /hello-world 및 /weather과(와) 함께 제공되므로 첫 날에 테스트할 수 있는 작업이 있습니다. 둘 다 실제 프로젝트에 속하지 않으므로 먼저 보일러판을 검토하고 필요하지 않은 항목을 제거하십시오.
API 계약 정의
핸들러를 작성하기 전에 요청 및 응답 셰이프를 정의하면 구현을 읽지 않고도 Edge Delivery Services 블록 또는 다른 호출자를 계약에 따라 작성할 수 있습니다.
끝점: GET /api/frescopa/estimated-delivery
GET /api/frescopa/estimated-delivery?sku=house-blend-medium-roast&postcode=90210
postcode은(는) 필수입니다. sku은(는) 선택 사항이며 기본값은 house-blend-medium-roast입니다.
응답 본문:
성공적인 응답은 다음 형태를 반환합니다.
// 200 OK
{
"sku": "house-blend-medium-roast",
"productName": "House Blend - Medium Roast",
"inventoryStatus": "in-stock", // or "low-stock", "out-of-stock"
"qtyLeft": 12,
"fulfillmentRegion": "West Coast Fulfillment",
"deliveryEta": "tomorrow",
"cutoffMessage": "Order by 2:00 PM for same-day dispatch.",
"message": "Arrives tomorrow in West Coast Fulfillment"
}
오류 응답은 각 실패 이유에 대해 다른 code을(를) 사용하여 이 셰이프를 공유합니다.
// 400 missing postcode, 404 unknown sku, 405 wrong method: same shape, different code
{
"error": "Missing required parameter",
"code": "MISSING_POSTCODE", // or "UNKNOWN_SKU", "METHOD_NOT_ALLOWED"
"message": "The postcode query parameter is required"
}
모든 오류 본문에는 code 필드가 포함되어 있으므로 호출자는 message을(를) 구문 분석하는 대신 실패 이유를 분기할 수 있습니다. OPTIONS 요청은 아래 로컬 개발용 CORS 활성화에서 다루는 CORS Preflight에 대한 별도의 응답을 받습니다.
비즈니스 로직 구현
index.js을(를) 라우팅만 제한합니다. 비즈니스 논리는 src/handlers/ 아래에 있으며 끝점당 하나의 폴더입니다.
추가할 파일
src/
├── index.js # routes to a handler, nothing else
├── handlers/estimated-delivery/
│ ├── handler.js # orchestrates the two upstream calls
│ ├── responses.js # builds the success and error JSON payloads
│ └── constants.js # route path and query-param defaults
├── lib/
│ ├── api-client.js # shared per-API token loading
│ └── cors.js # origin allow-list, applied to every response
└── mocks/ # tutorial stand-ins for the two upstream APIs
├── catalog/product-api.js
└── delivery/delivery-api.js
핸들러 및 업스트림 호출
handler.js의 처리기가 sku 및 postcode을(를) 가져와서 두 업스트림 서비스를 호출하고 결과를 하나의 응답으로 병합합니다.
// src/handlers/estimated-delivery/handler.js
async function estimatedDeliveryBySkuAndPostcodeHandler(req) {
const url = new URL(req.url);
const sku = (url.searchParams.get("sku") ?? DEFAULT_SKU).trim();
const postcode = (url.searchParams.get("postcode") ?? "").trim();
if (!postcode) {
return missingPostcodeError(sku);
}
// API 1: catalog lookup
const product = await getProductBySku(sku);
if (!product) {
return unknownSkuError(sku, postcode);
}
// API 2: fulfillment lookup
const fulfillment = await getEstimatedDeliveryByPostcode(postcode);
return json(buildSuccessResponse(product, fulfillment));
}
responses.js은(는) 오류 및 성공 JSON 셰이프를 핸들러에서 유지하고 constants.js은(는) 경로 경로 경로 및 기본 SKU를 유지하므로 handler.js은(는) 두 호출 시퀀스에 초점을 맞추고 있습니다. src/mocks/의 각 모형은 데이터를 반환하기 전에 자체 토큰을 로드하며, 실제 API 클라이언트가 사용하는 것과 동일한 형태입니다.
// src/mocks/catalog/product-api.js
export async function getProductBySku(sku) {
await getApiToken(SECRETS.CATALOG);
// Real API: uncomment and replace with your catalog endpoint.
// const response = await authorizedFetch(
// `https://api.example.com/products?sku=${encodeURIComponent(sku)}`,
// SECRETS.CATALOG,
// );
// return response.ok ? response.json() : null;
return PRODUCTS[sku] || null;
}
getApiToken() 및 authorizedFetch()이(가) lib/api-client.js에 있습니다. product-api.js 및 delivery-api.js은(는) 모두 이 도우미를 공유하므로 각 업스트림 API는 인증 논리를 복제하지 않고 자체 비밀 저장소 키(CATALOG_API_TOKEN, DELIVERY_API_TOKEN)를 유지합니다. 시뮬레이션된 호출을 실제 API로 바꿀 준비가 되면 배포된 사이트에서 암호 설정을 위해 구성 및 암호를 Edge 함수와 함께 사용을 수행한 다음 위의 블록의 주석을 제거하십시오.
각 플랫폼은 단일 호출을 32개의 아웃바운드 가져오기 호출로 제한하므로 여기에 두 개의 호출이 있으면 여유 공간이 충분합니다.
로컬 개발용 CORS 활성화
Edge Delivery Services 블록의 로컬 개발 서버가 http://localhost:3000에서 실행됩니다. AEM Edge 함수의 로컬 개발 서버가 http://127.0.0.1:7676에서 실행됩니다. 이러한 요청은 두 개의 서로 다른 출처이므로 CORS 헤더가 없으면 브라우저에서 코드가 요청을 보기 전에 차단합니다.
cors.js의 허용 목록에 대해 요청 원본을 확인하고 브라우저에서 먼저 보내는 OPTIONS Preflight 요청에 동일한 검사를 적용합니다.
// src/lib/cors.js
const ALLOWED_ORIGIN_EXACT = new Set([
"http://localhost:3000",
"http://127.0.0.1:3000",
]);
function isAllowedOrigin(origin) {
return Boolean(origin) && ALLOWED_ORIGIN_EXACT.has(origin);
}
function applyCors(request, response) {
const origin = request.headers.get("Origin");
if (!isAllowedOrigin(origin)) {
return response;
}
const headers = new Headers(response.headers);
headers.set("access-control-allow-origin", origin);
headers.set("vary", "Origin");
return new Response(response.body, { status: response.status, headers });
}
function corsPreflightResponse(request) {
const origin = request.headers.get("Origin");
if (!isAllowedOrigin(origin)) {
return new Response(null, { status: 403 });
}
return new Response(null, {
status: 204,
headers: {
"access-control-allow-origin": origin,
"access-control-allow-methods": "GET, OPTIONS",
"access-control-allow-headers": "Content-Type",
vary: "Origin",
},
});
}
export { applyCors, corsPreflightResponse };
// src/index.js
if (url.pathname === "/api/frescopa/estimated-delivery") {
if (req.method === "OPTIONS") {
finalResponse = corsPreflightResponse(req);
} else if (req.method === "GET") {
finalResponse = await estimatedDeliveryBySkuAndPostcodeHandler(req);
}
}
finalResponse = applyCors(req, finalResponse);
참조 구현은 CDN을 통해 배포된 개발, 스테이지 및 프로덕션 도메인에 대한 요청이 동일한 원본이고 기술적으로 CORS 헤더가 필요하지 않으므로 해당 도메인에 대한 정규식을 사용하여 ALLOWED_ORIGIN_EXACT을(를) 확장합니다. 하지만 CORS 헤더가 명시적으로 일치하면 허용 목록이 자동으로 문서화됩니다. 배포되면 브라우저는 동일 원본 요청에 대한 CORS 검사를 보내지 않으므로 추가 헤더는 문제가 없습니다. CORS 도우미를 그대로 둘 수 있습니다. 요청의 원본이 허용 목록과 일치하는 경우에만 헤더를 추가합니다.
fastly.toml에서 로컬 개발 서버 구성
fastly.toml은(는) aio aem edge-functions serve에 의해 시작된 로컬 개발 서버를 구성합니다. 이 자습서의 경우 해당 [local_server.secret_stores] 블록은 각 업스트림 API에 로컬 토큰을 제공하므로 핸들러가 실제 자격 증명 없이 SecretStoreManager.getSecret()을(를) 호출할 수 있습니다.
# fastly.toml
[local_server.secret_stores]
[[local_server.secret_stores.secret_default]]
key = "CATALOG_API_TOKEN"
data = "catalog-tutorial-token"
[[local_server.secret_stores.secret_default]]
key = "DELIVERY_API_TOKEN"
data = "delivery-tutorial-token"
또한 보일러플레이트는 날씨 샘플의 Open-Meteo API에 대한 [local_server.backends] 항목도 선언합니다. 이 자습서에서는 실제 호스트를 호출하는 대신 업스트림 호출을 모두 시뮬레이트하므로 위치를 다시 지정하지 말고 해당 항목을 제거하십시오.
[local_server] 옵션의 전체 집합을 보려면 Fastly.toml 참조를 참조하십시오.
로컬에서 끝점 테스트
이 시점에서는 API 계약만 테스트합니다. 나중에 호출되는 Edge Delivery Services 블록과 관계없이 끝점의 요청 및 응답 셰이프입니다.
Edge Delivery Services에서 AEM Edge 기능 설정에서 로컬 개발 서버를 시작합니다.
$ aio aem edge-functions serve
curl(으)로 행복한 경로를 호출하거나 브라우저에서 URL을 엽니다.
$ curl "http://127.0.0.1:7676/api/frescopa/estimated-delivery?sku=house-blend-medium-roast&postcode=90210"
응답이 두 업스트림 호출의 데이터를 병합하는지 확인합니다(예: 동일한 JSON 본문에 있는 카탈로그 조회의 제품 이름과 이행 조회의 게재 예상).
그런 다음 오류 경로 responses.js 빌드를 확인합니다.
# missing postcode
$ curl "http://127.0.0.1:7676/api/frescopa/estimated-delivery?sku=house-blend-medium-roast"
# unknown SKU
$ curl "http://127.0.0.1:7676/api/frescopa/estimated-delivery?sku=not-a-real-product&postcode=90210"
각 JSON은 제네릭 500이 아닌 code 필드(MISSING_POSTCODE, UNKNOWN_SKU)를 사용하여 JSON 오류 본문을 반환해야 하며, 처리기가 업스트림 호출에 도달하기 전에 입력의 유효성을 검사하는지 확인합니다.
끝점 추가
두 구성 파일이 API 끝점 빌드의 패턴을 따라 경로를 선언합니다.
config/edgeFunctions.yaml은(는) AEM Edge 함수 자체를 선언합니다. 표준 접시에서 변경되지 않습니다.
# config/edgeFunctions.yaml
kind: "EdgeFunctions"
version: "1"
data:
functions:
- name: my-edge-function
config/cdn.yaml은(는) 이 자습서의 경로를 해당 함수에 전달하는 원본 선택기 규칙을 추가합니다.
# config/cdn.yaml
- name: route-estimated-delivery-to-edge-function
when: { reqProperty: path, equals: "/api/frescopa/estimated-delivery" }
action:
type: selectAemOrigin
originName: edgefunction-my-edge-function
skipCache: true
참조 구현에서 전체 파일을 확인합니다. config/edgeFunctions.yaml 및 config/cdn.yaml.
다음 단계
Edge Delivery Services 블록 개발에서 이 끝점을 호출하는 블록을 스캐폴드한 다음 유니버설 편집기에서 작성합니다.