컨텐츠로 건너뛰기

이미지 서비스 API

astro:assets은 모든 이미지 최적화 서비스가 Astro 위에 서비스를 쉽게 구축할 수 있도록 설계되었습니다.

이미지 서비스란 무엇입니까?

섹션 제목: 이미지 서비스란 무엇입니까?

Astro는 로컬과 외부라는 두 가지 유형의 이미지 서비스를 제공합니다.

  • 로컬 서비스는 정적 사이트의 빌드 시 또는 개발 모드와 요청 시 렌더링 모두에서 런타임 시 이미지 변환을 직접 처리합니다. 이는 Sharp, ImageMagick, Squoosh와 같은 라이브러리를 둘러싼 래퍼인 경우가 많습니다. 개발 모드와 요청 시 렌더링된 프로덕션 라우트에서 로컬 서비스는 API 엔드포인트를 사용하여 변환을 수행합니다.
  • 외부 서비스는 URL을 가리키며 Cloudinary, Vercel 또는 모든 RIAPI 호환 서버와 같은 서비스에 대한 지원을 추가할 수 있습니다.

이미지 서비스 API를 사용하여 빌드

섹션 제목: 이미지 서비스 API를 사용하여 빌드

서비스 정의는 다양한 필수 방법 (“후크”)을 사용하여 내보낸 기본 객체의 형태를 취합니다.

외부 서비스는 출력 <img> 태그의 src를 가리키는 getURL()을 제공합니다.

로컬 서비스는 이미지 변환을 수행하는 transform() 메서드와 개발 모드 및 요청 시 렌더링용 엔드포인트를 사용하는 getURL()parseURL() 메서드를 제공합니다.

두 유형의 서비스 모두 getHTMLAttributes()를 제공하여 출력 <img>의 다른 속성을 결정하고 validateOptions()를 제공하여 전달된 옵션을 검증하고 강화할 수 있습니다.

외부 서비스는 최종 <img> 태그의 src 속성으로 사용될 원격 URL을 가리킵니다. 이 원격 URL은 이미지 다운로드, 변환 및 반환을 담당합니다.

import type { ExternalImageService, ImageTransform, AstroConfig } from "astro";
const service: ExternalImageService = {
validateOptions(options: ImageTransform, imageConfig: AstroConfig['image']) {
const serviceConfig = imageConfig.service.config;
// 사용자가 설정한 최대 너비를 적용합니다.
if (options.width > serviceConfig.maxWidth) {
console.warn(`Image width ${options.width} exceeds max width ${serviceConfig.maxWidth}. Falling back to max width.`);
options.width = serviceConfig.maxWidth;
}
return options;
},
getURL(options, imageConfig) {
return `https://mysupercdn.com/${options.src}?q=${options.quality}&w=${options.width}&h=${options.height}`;
},
getHTMLAttributes(options, imageConfig) {
const { src, format, quality, ...attributes } = options;
return {
...attributes,
loading: options.loading ?? 'lazy',
decoding: options.decoding ?? 'async',
};
}
};
export default service;

자신만의 로컬 서비스를 생성하려면 내장된 엔드포인트 (/_image)를 가리키거나 서비스의 메서드를 호출할 수 있는 자체 엔드포인트를 추가로 생성할 수 있습니다.

import type { LocalImageService, AstroConfig } from "astro";
const service: LocalImageService = {
getURL(options: ImageTransform, imageConfig: AstroConfig['image']) {
const searchParams = new URLSearchParams();
searchParams.append('href', typeof options.src === "string" ? options.src : options.src.src);
options.width && searchParams.append('w', options.width.toString());
options.height && searchParams.append('h', options.height.toString());
options.quality && searchParams.append('q', options.quality.toString());
options.format && searchParams.append('f', options.format);
return `/my_custom_endpoint_that_transforms_images?${searchParams}`;
// 또는 내장된 엔드포인트를 사용하여 parsURL 및 변환 함수를 호출합니다.
// 이 함수는 `/_image?${searchParams}`를 반환합니다.
},
parseURL(url: URL, imageConfig) {
return {
src: params.get('href')!,
width: params.has('w') ? parseInt(params.get('w')!) : undefined,
height: params.has('h') ? parseInt(params.get('h')!) : undefined,
format: params.get('f'),
quality: params.get('q'),
};
},
transform(buffer: Uint8Array, options: { src: string, [key: string]: any }, imageConfig): { data: Uint8Array, format: OutputFormat } {
const { buffer } = mySuperLibraryThatEncodesImages(options);
return {
data: buffer,
format: options.format,
};
},
getHTMLAttributes(options, imageConfig) {
let targetWidth = options.width;
let targetHeight = options.height;
if (typeof options.src === "object") {
const aspectRatio = options.src.width / options.src.height;
if (targetHeight && !targetWidth) {
targetWidth = Math.round(targetHeight * aspectRatio);
} else if (targetWidth && !targetHeight) {
targetHeight = Math.round(targetWidth / aspectRatio);
}
}
const { src, width, height, format, quality, ...attributes } = options;
return {
...attributes,
width: targetWidth,
height: targetHeight,
loading: attributes.loading ?? 'lazy',
decoding: attributes.decoding ?? 'async',
};
},
propertiesToHash: ['src', 'width', 'height', 'format', 'quality'],
};
export default service;

정적 사이트와 사전 렌더링된 경로 빌드 시 <Image />getImage(options)는 모두 transform() 함수를 호출합니다. 각각 컴포넌트의 속성이나 options 인수를 통해 옵션을 전달합니다. 변환된 이미지는 dist/_astro 폴더에 빌드됩니다. 파일 이름에는 propertiesToHash에 전달된 속성의 해시가 포함됩니다. 이 속성은 선택 사항이며 기본값은 ['src', 'width', 'height', 'format', 'quality']입니다. 사용자 정의 이미지 서비스에 생성된 이미지를 변경하는 추가 옵션이 있는 경우 배열에 추가합니다.

개발 모드와 요청 시 렌더링을 위해 어댑터를 사용할 때, Astro는 어떤 이미지를 최적화해야 하는지 미리 알 수 없습니다. Astro는 GET 엔드포인트 (기본적으로 /_image)를 사용하여 런타임에 이미지를 처리합니다. <Image />getImage()는 해당 옵션을 getURL()에 전달하여 엔드포인트 URL을 반환합니다. 그런 다음 엔드포인트는 parseURL()을 호출하고 결과 속성을 transform()에 전달합니다.

getConfiguredImageService & imageConfig

섹션 제목: getConfiguredImageService &amp; imageConfig

자체 엔드포인트를 Astro 엔드포인트로 구현하는 경우 getConfiguredImageServiceimageConfig를 사용하여 서비스의 parseURLtransform 메서드를 호출하고 이미지 구성을 제공할 수 있습니다.

이미지 서비스 구성 (image.service.config)에 액세스하려면 imageConfig.service.config를 사용할 수 있습니다.

src/api/my_custom_endpoint_that_transforms_images.ts
import type { APIRoute } from "astro";
import { getConfiguredImageService, imageConfig } from 'astro:assets';
export const GET: APIRoute = async ({ request }) => {
const imageService = await getConfiguredImageService();
const imageTransform = imageService.parseURL(new URL(request.url), imageConfig);
// ... imageTransform.src에서 이미지를 가져와 inputBuffer에 저장합니다.
const { data, format } = await imageService.transform(inputBuffer, imageTransform, imageConfig);
return new Response(data, {
status: 200,
headers: {
'Content-Type': mime.getType(format) || ''
}
}
);
}

전체 예시는 내장 엔드포인트를 참조하세요.

로컬 및 외부 서비스에 필요함

getURL(options: ImageTransform, imageConfig: AstroConfig['image']): string

로컬 서비스의 경우 이 후크는 이미지를 생성하는 엔드포인트의 URL을 반환합니다 (요청 시 렌더링 및 개발 모드에서). 빌드 중에는 사용되지 않습니다. getURL()이 가리키는 로컬 엔드포인트는 parseURL()transform()을 모두 호출할 수 있습니다.

외부 서비스의 경우 이 후크는 이미지의 최종 URL을 반환합니다.

두 서비스 유형 모두 options는 사용자가 <Image /> 컴포넌트의 속성 또는 getImage()에 대한 옵션으로 전달한 속성입니다. 그들은 다음과 같은 타입입니다:

export type ImageTransform = {
// 이미지를 가져오는 ESM | 원격/public 이미지 경로
src: ImageMetadata | string;
width?: number;
height?: number;
widths?: number[] | undefined;
densities?: (number | `${number}x`)[] | undefined;
quality?: ImageQuality;
format?: OutputFormat;
alt?: string;
[key: string]: any;
};

로컬 서비스에 필요합니다. 외부 서비스에 사용할 수 없음

parseURL(url: URL, imageConfig: AstroConfig['image']): { src: string, [key: string]: any}

이 후크는 getURL()에 의해 생성된 URL을 transform (요청 시 렌더링 및 개발 모드에서)에 사용되는 다른 속성을 가진 객체로 다시 구문 분석합니다. 빌드 중에는 사용되지 않습니다.

로컬 서비스에만 필요합니다. 외부 서비스에 사용할 수 없음

transform(buffer: Uint8Array, options: { src: string, [key: string]: any }, imageConfig: AstroConfig['image']): { data: Uint8Array, format: OutputFormat }

이 후크는 이미지를 변환하고 반환하며 빌드 중에 호출되어 최종 자산 파일을 생성합니다.

요청 시 렌더링 및 개발 모드에서 사용자에게 적절한 MIME 유형이 제공되도록 하려면 format을 반환해야 합니다.

로컬 및 외부 서비스 모두 선택 사항

getHTMLAttributes(options: ImageTransform, imageConfig: AstroConfig['image']): Record<string, any>

이 후크는 사용자가 전달한 매개변수 (options)를 기반으로 이미지를 HTML로 렌더링하는 데 사용되는 모든 추가 속성을 반환합니다.

추가된 버전: astro@3.3.0

로컬 및 외부 서비스 모두 선택 사항입니다.

getSrcSet?: (options: ImageTransform, imageConfig: AstroConfig['image']): SrcSetValue[] | Promise<SrcSetValue[]>;

이 후크는 <img> 또는 <picture>sourcesrcset 속성을 생성하기 위해 지정된 이미지의 여러 변형을 생성합니다.

이 후크는 다음 속성을 가진 객체 배열을 반환합니다.

export type SrcSetValue = {
transform: ImageTransform;
descriptor?: string;
attributes?: Record<string, any>;
};

로컬 및 외부 서비스 모두 선택 사항

validateOptions(options: ImageTransform, imageConfig: AstroConfig['image']): ImageTransform

이 후크를 사용하면 사용자가 전달한 옵션을 검증하고 강화할 수 있습니다. 이는 기본 옵션을 설정하거나 사용자에게 매개변수가 필요함을 알리는 데 유용합니다.

Astro 내장 서비스에서 validateOptions()가 어떻게 사용되는지 확인하세요.

astro.config.mjs 파일에서 사용할 이미지 서비스를 구성합니다. 구성은 다음 형식을 취합니다.

astro.config.mjs
import { defineConfig } from "astro/config";
export default defineConfig({
image: {
service: {
entrypoint: "your-entrypoint", // 'astro/assets/services/sharp' | string,
config: {
// ... 서비스별 구성. 선택적.
}
}
},
});

Astro는 사용자 정의 이미지 서비스를 개발하는 데 사용할 수 있는 여러 도우미 함수를 제공합니다. 이러한 유틸리티는 astro/assets/utils에서 가져올 수 있습니다.

import {
isRemoteAllowed,
matchHostname,
matchPathname,
matchPattern,
matchPort,
matchProtocol
} from "astro/assets/utils";

타입: (src: string, { domains, remotePatterns }: {domains: string[], remotePatterns: RemotePattern[] }): boolean

추가된 버전: astro@4.0.0

제공된 도메인 및 원격 패턴을 기반으로, 소스 URL로 식별되는 지정된 원격 리소스가 허용되는지 여부를 결정합니다.

import { isRemoteAllowed } from 'astro/assets/utils';
const testImageURL = 'https://example.com/images/test.jpg';
const domains = ['example.com', 'anotherdomain.com'];
const remotePatterns = [
{ protocol: 'https', hostname: 'images.example.com', pathname: '/**' }, // 이 호스트 이름 아래의 모든 경로를 허용합니다.
];
const url = new URL(testImageURL);
const isAllowed = isRemoteAllowed(url.href, { domains, remotePatterns });
console.log(`Is the remote image allowed? ${isAllowed}`);

타입: (url: URL, hostname?: string, allowWildcard = false): boolean

추가된 버전: astro@4.0.0

주어진 URL의 호스트 이름이 지정된 호스트 이름과 일치하는지 확인하며, 선택적으로 와일드카드 패턴을 지원합니다.

import { matchHostname } from 'astro/assets/utils';
const testURL = new URL('https://sub.example.com/path/to/resource');
// matchHostname의 사용 예시입니다.
const hostnameToMatch = 'example.com';
// 와일드카드 없이 비교합니다.
const isMatchWithoutWildcard = matchHostname(testURL, hostnameToMatch);
console.log(`Does the hostname match without wildcard? ${isMatchWithoutWildcard}`); // 출력: false
// 와일드카드를 사용하여 비교합니다.
const isMatchWithWildcard = matchHostname(testURL, hostnameToMatch, true);
console.log(`Does the hostname match with wildcard? ${isMatchWithWildcard}`); // 출력: true

타입: (url: URL, pathname?: string, allowWildcard = false): boolean

추가된 버전: astro@4.0.0

주어진 URL의 경로 이름이 지정된 패턴과 일치하는지 확인하며, 선택적으로 와일드카드를 지원합니다.

import { matchPathname } from 'astro/assets/utils';
const testURL = new URL('https://example.com/images/photo.jpg');
// 경로 이름 일치 예시입니다.
const pathnameToMatch = '/images/photo.jpg';
// 와일드카드 없이 비교합니다.
const isMatchWithoutWildcard = matchPathname(testURL, pathnameToMatch);
console.log(`Does the pathname match without wildcard? ${isMatchWithoutWildcard}`); // 출력: true
// 와일드카드를 사용하여 비교합니다.
const wildcardPathname = '/images/*';
const isMatchWithWildcard = matchPathname(testURL, wildcardPathname, true);
console.log(`Does the pathname match with wildcard? ${isMatchWithWildcard}`); // 출력: true

타입: (url: URL, remotePattern: RemotePattern): boolean

추가된 버전: astro@4.0.0

주어진 URL이 프로토콜, 호스트 이름, 포트 및 경로 이름을 기반으로 지정된 원격 패턴과 일치하는지 평가합니다.

import { matchPattern } from 'astro/assets/utils';
const testURL = new URL('https://images.example.com/photos/test.jpg');
// URL을 일치시키는 원격 패턴을 정의합니다.
const remotePattern = {
protocol: 'https',
hostname: 'images.example.com',
pathname: '/photos/**', // `/photos/` 아래의 모든 파일을 허용하는 와일드카드입니다.
port: '', // 선택 사항: 모든 포트와 일치시키거나 기본값으로 사용하기 위해 비워둡니다.
};
// URL이 원격 패턴과 일치하는지 확인합니다.
const isPatternMatched = matchPattern(testURL, remotePattern);
console.log(`Does the URL match the remote pattern? ${isPatternMatched}`); // 출력: true

타입: (url: URL, port?: string): boolean

추가된 버전: astro@4.0.0

주어진 URL의 포트가 지정된 포트와 일치하는지 확인합니다. 포트가 제공되지 않으면 true를 반환합니다.

import { matchPort } from 'astro/assets/utils';
const testURL1 = new URL('https://example.com:8080/resource');
const testURL2 = new URL('https://example.com/resource');
// matchPort의 사용 예시입니다.
const portToMatch = '8080';
// URL을 지정된 포트와 비교합니다.
const isPortMatch1 = matchPort(testURL1, portToMatch);
console.log(`Does the port match? ${isPortMatch1}`); // 출력: true
// 지정된 포트 없이 URL을 비교합니다. (기본 포트를 사용한다고 가정합니다.)
const isPortMatch2 = matchPort(testURL2, portToMatch);
console.log(`Does the port match? ${isPortMatch2}`); // 출력: false
// 명시적으로 제공된 포트 없이 URL을 확인합니다. (포트가 정의되지 않은 경우 기본적으로 true입니다.)
const isPortMatch3 = matchPort(testURL1);
console.log(`Does the port match (no port specified)? ${isPortMatch3}`); // 출력: true

타입: (url: URL, protocol?: string): boolean

추가된 버전: astro@4.0.0

제공된 URL의 프로토콜을 지정된 프로토콜과 비교합니다.

import { matchProtocol } from 'astro/assets/utils';
const testURL1 = new URL('https://example.com/resource');
const testURL2 = new URL('http://example.com/resource');
// matchProtocol의 사용 예시입니다.
const protocolToMatch = 'https';
// 정확한 프로토콜을 사용한 URL과 비교합니다.
const isProtocolMatch1 = matchProtocol(testURL1, protocolToMatch);
console.log(`Does the protocol match for testURL1? ${isProtocolMatch1}`); // 출력: true
// 정확하지 않은 프로토콜을 사용한 URL과 비교합니다.
const isProtocolMatch2 = matchProtocol(testURL2, protocolToMatch);
console.log(`Does the protocol match for testURL2? ${isProtocolMatch2}`); // 출력: false
// 명시적으로 제공된 프로토콜 없이 URL을 확인합니다. (프로토콜이 정의되지 않은 경우 기본적으로 true입니다.)
const isProtocolMatch3 = matchProtocol(testURL1);
console.log(`Does the protocol match (no protocol specified)? ${isProtocolMatch3}`); // 출력: true
기여하기 커뮤니티 후원하기
京ICP备15031610号-99