Drupal & Astro
Drupal은 오픈소스 콘텐츠 관리 도구입니다.
전제 조건
시작하려면 다음이 필요합니다:
Astro 프로젝트 - 아직 Astro 프로젝트가 없는 경우, 설치 가이드를 참조하면 금방 시작할 수 있습니다.
Drupal 사이트 - Drupal 사이트를 설정하지 않은 경우 공식 가이드라인 Drupal 설치하기를 따르세요.
Drupal과 Astro 통합
JSON:API Drupal 모듈 설치하기
Drupal에서 콘텐츠를 가져오려면 Drupal JSON:API 모듈을 활성화해야 합니다.
- Manage administrative 메뉴를 통해 Extend 페이지
admin/modules로 이동합니다. - JSON:API 모듈을 찾아 옆의 확인란을 선택합니다.
- Install을 클릭하여 새 모듈을 설치합니다.
이제 JSON:API를 통해 Drupal 애플리케이션에 GET 요청을 할 수 있습니다.
.env에 Drupal URL 추가하기
Astro에 Drupal URL을 추가하려면 프로젝트의 루트에 .env 파일을 만들고 (아직 없는 경우) 다음 변수를 추가하세요:
DRUPAL_BASE_URL="https://drupal.ddev.site/"개발 서버를 재시작하여 Astro 프로젝트에서 이 환경 변수를 사용하세요.
자격 증명 설정
기본적으로 외부 데이터 가져오기 요청에 대해 인증 없이도 Drupal JSON:API 엔드포인트에 액세스할 수 있습니다. 이를 통해 자격 증명 없이도 Astro 프로젝트에서 데이터를 가져올 수 있지만 사용자가 데이터나 사이트 설정을 수정할 수는 없습니다.
그러나 액세스를 제한하고 인증이 필요한 경우 Drupal은 다음과 같은 여러 인증 방법을 제공합니다:
.env 파일에 자격 증명을 추가할 수 있습니다.
DRUPAL_BASIC_USERNAME="editor"
DRUPAL_BASIC_PASSWORD="editor"
DRUPAL_JWT_TOKEN="abc123"
....env 파일에 대해 자세히 알아보세요.
이제 루트 디렉터리에 이 새 파일들이 포함되어야 합니다:
- .env
- astro.config.mjs
- package.json
종속성 설치
JSON:API 요청과 응답은 종종 복잡하고 깊게 중첩될 수 있습니다. 작업을 간소화하기 위해 요청과 응답 처리를 모두 간소화하는 두 가지 npm 패키지를 사용할 수 있습니다:
JSONA: 서버와 브라우저에서 사용하기 위해 JSON API v1.0 사양을 직렬화 및 역직렬화 할 수 있습니다.Drupal JSON-API Params: 이 모듈은 필요한 쿼리를 생성하는 헬퍼 클래스를 제공합니다. 이 과정에서 가능한 경우 짧은 형식을 사용하여 쿼리를 최적화하려고 시도합니다.
npm install jsona drupal-jsonapi-params
pnpm add jsona drupal-jsonapi-params
yarn add jsona drupal-jsonapi-params
Drupal에서 데이터 가져오기
콘텐츠는 JSON:API URL에서 가져옵니다.
Drupal JSON:API URL 구조
기본 URL 구조: /jsonapi/{entity_type_id}/{bundle_id}
URL 앞에는 항상 jsonapi가 붙습니다.
entity_type_id는 node, block, user 등의 엔티티 타입을 나타냅니다.bundle_id는 엔티티 번들을 나타냅니다. Node 엔티티 타입의 경우 번들은 article이 될 수 있습니다.- 이 경우 모든 articles 목록을 가져오기 위해 URL은
[DRUPAL_BASE_URL]/jsonapi/node/article이 됩니다.
개별 엔티티를 검색하기 위한 URL 구조는 /jsonapi/{entity_type_id}/{bundle_id}/{uuid}이며, 여기서 uuid는 엔티티의 UUID입니다. 예를 들어 특정 article을 가져오는 URL은 /jsonapi/node/article/2ee9f0ef-1b25-4bbe-a00f-8649c68b1f7e 형태가 됩니다.
특정 필드만 검색
쿼리 문자열 필드를 요청에 추가하여 특정 필드만 검색합니다.
GET: /jsonapi/{entity_type_id}/{bundle_id}?field[entity_type]=field_list
예시:
/jsonapi/node/article?fields[node--article]=title,created/jsonapi/node/article/2ee9f0ef-1b25-4bbe-a00f-8649c68b1f7e?fields[node--article]=title,created,body
필터링
쿼리 문자열 필터를 추가하여 요청에 필터를 추가합니다.
가장 간단하고 일반적인 필터는 키-값 필터입니다:
GET: /jsonapi/{entity_type_id}/{bundle_id}?filter[field_name]=value&filter[field_other]=value
예시:
/jsonapi/node/article?filter[title]=Testing JSON:API&filter[status]=1/jsonapi/node/article/2ee9f0ef-1b25-4bbe-a00f-8649c68b1f7e?fields[node--article]=title&filter[title]=Testing JSON:API
더 많은 쿼리 옵션은 JSON:API 문서에서 확인할 수 있습니다.
Drupal 쿼리 작성하기
Astro 컴포넌트는 drupal-jsonapi-params 패키지로 쿼리를 작성하여 Drupal 사이트에서 데이터를 가져올 수 있습니다.
다음 예시는 "article" 콘텐츠 타입을 쿼리하는 컴포넌트를 보여줍니다. 이 콘텐츠 타입은 제목용 일반 텍스트 필드와 본문용 서식 있는 텍스트 필드를 포함하고 있습니다.
---
import {Jsona} from "jsona";
import {DrupalJsonApiParams} from "drupal-jsonapi-params";
import type {TJsonApiBody} from "jsona/lib/JsonaTypes";
// Drupal 기본 URL 가져오기
export const baseUrl: string = import.meta.env.DRUPAL_BASE_URL;
// JSON:API 쿼리를 생성합니다. 게시된 articles에서 모든 제목과 본문을 가져옵니다.
const params: DrupalJsonApiParams = new DrupalJsonApiParams();
params.addFields("node--article", [
"title",
"body",
])
.addFilter("status", "1");
// 쿼리 문자열 생성
const path: string = params.getQueryString();
const url: string = baseUrl + '/jsonapi/node/article?' + path;
// articles 가져오기
const request: Response = await fetch(url);
const json: string | TJsonApiBody = await request.json();
// Jsona 초기화
const dataFormatter: Jsona = new Jsona();
// 응답 역직렬화
const articles = dataFormatter.deserialize(json);
---
<body>
{articles?.length ? articles.map((article: any) => (
<section>
<h2>{article.title}</h2>
<article set:html={article.body.value}></article>
</section>
)): <div><h1>No Content found</h1></div> }
</body>더 많은 쿼리 옵션은 Drupal JSON:API 문서에서 확인할 수 있습니다.
Astro 및 Drupal로 블로그 만들기
위의 설정으로 이제 Drupal을 CMS로 사용하는 블로그를 만들 수 있습니다.
전제 조건
JSONA및Drupal JSON-API Params가 설치된 Astro 프로젝트항목이 하나 이상 있는 Drupal 사이트 - 이 튜토리얼에서는 표준 설치로 새 Drupal 사이트를 시작하는 것을 권장합니다.
Drupal 사이트의 Content 섹션에서 Add 버튼을 클릭하여 새 항목을 만듭니다. 그런 다음 Article을 선택하고 필드를 채웁니다:
- Title:
My first article for Astro! - Alias:
/articles/first-article-for astro - Description:
This is my first Astro article! Let's see what it will look like!
Save를 클릭하여 첫 번째 Article을 작성합니다. 원하는 만큼 자유롭게 articles를 추가할 수 있습니다.
- Title:
articles 목록 표시
아직 존재하지 않는 경우
src/types.ts를 생성하고 다음 코드를 사용하여DrupalNode및Path라는 두 개의 새 인터페이스를 추가합니다. 이 인터페이스는 Drupal의 article 콘텐츠 타입 필드 및 Path 필드와 일치합니다. 이를 사용하여 article 항목 응답에 대한 타입을 정의합니다.export interface Path { alias: string pid: number langcode: string } export interface DrupalNode extends Record<string, any> { id: string type: string langcode: string status: boolean drupal_internal__nid: number drupal_internal__vid: number changed: string created: string title: string default_langcode: boolean sticky: boolean path: Path }이제 src 디렉터리에 새 파일이 포함되어야 합니다:
- .env
- astro.config.mjs
- package.json
디렉터리src/
- types.ts
src/api에drupal.ts라는 새 파일을 만들고 다음 코드를 추가합니다:import {Jsona} from "jsona"; import {DrupalJsonApiParams} from "drupal-jsonapi-params"; import type {DrupalNode} from "../types.ts"; import type {TJsonApiBody} from "jsona/lib/JsonaTypes"; // Drupal 기본 Url을 가져옵니다. export const baseUrl: string = import.meta.env.DRUPAL_BASE_URL;이렇게 하면 응답을 역직렬화하기 위한
Jsona, 요청 URL 및 Node와 Jsona 타입의 형식을 지정하는DrupalJsonApiParams와 같은 필수 라이브러리를 가져옵니다.또한.env파일에서baseUrl을 가져옵니다.이제 src/api 디렉터리에 새 파일이 포함되어야 합니다:
- .env
- astro.config.mjs
- package.json
디렉터리src/
디렉터리api/
- drupal.ts
- types.ts
같은 파일에서
fetchUrl함수를 생성하여 가져오기 요청을 하고 응답을 역직렬화합니다.import {Jsona} from "jsona"; import {DrupalJsonApiParams} from "drupal-jsonapi-params"; import type {DrupalNode} from "../types.ts"; import type {TJsonApiBody} from "jsona/lib/JsonaTypes"; // Drupal 기본 Url 가져오기 export const baseUrl: string = import.meta.env.DRUPAL_BASE_URL; /** * Fetch url from Drupal. * * @param url * * @return Promise<TJsonaModel | TJsonaModel[]> as Promise<any> */ export const fetchUrl = async (url: string): Promise<any> => { const request: Response = await fetch(url); const json: string | TJsonApiBody = await request.json(); const dataFormatter: Jsona = new Jsona(); return dataFormatter.deserialize(json); }게시된 모든 articles를 가져오는
getArticles()함수를 만듭니다.import {Jsona} from "jsona"; import {DrupalJsonApiParams} from "drupal-jsonapi-params"; import type {DrupalNode} from "../types.ts"; import type {TJsonApiBody} from "jsona/lib/JsonaTypes"; // Drupal 기본 Url 가져오기 export const baseUrl: string = import.meta.env.DRUPAL_BASE_URL; /** * Fetch url from Drupal. * * @param url * * @return Promise<TJsonaModel | TJsonaModel[]> as Promise<any> */ export const fetchUrl = async (url: string): Promise<any> => { const request: Response = await fetch(url); const json: string | TJsonApiBody = await request.json(); const dataFormatter: Jsona = new Jsona(); return dataFormatter.deserialize(json); } /** * Get all published articles. * * @return Promise<DrupalNode[]> */ export const getArticles = async (): Promise<DrupalNode[]> => { const params: DrupalJsonApiParams = new DrupalJsonApiParams(); params .addFields("node--article", [ "title", "path", "body", "created", ]) .addFilter("status", "1"); const path: string = params.getQueryString(); return await fetchUrl(baseUrl + '/jsonapi/node/article?' + path); }이제
.astro컴포넌트에서getArticles()함수를 사용하여 각 제목, 본문, 경로, 작성 날짜에 대한 데이터가 포함된 게시된 모든 articles를 가져올 수 있습니다.Drupal에서 데이터를 가져올 Astro 페이지로 이동합니다. 다음 예시는
src/pages/articles/index.astro에 articles 랜딩 페이지를 만듭니다.필요한 종속성을 가져오세요. 그리고
getArticles()함수를 사용하여 Drupal에서article콘텐츠 타입의 모든 항목을 가져오세요. 이때 응답의 타입을 지정하기 위해DrupalNode인터페이스를 함수에 전달하세요.--- import {Jsona} from "jsona"; import {DrupalJsonApiParams} from "drupal-jsonapi-params"; import type {TJsonApiBody} from "jsona/lib/JsonaTypes"; import type { DrupalNode } from "../../types"; import {getArticles} from "../../api/drupal"; // 게시된 모든 articles 가져오기 const articles = await getArticles(); ---getArticles()를 사용한 이 가져오기 호출은 페이지 템플릿에서 사용할 수 있는 항목의 타입이 있는 배열을 반환합니다.
동일한 페이지 파일을 사용했다면 이제
src/pages/디렉터리에 새 파일이 포함되어야 합니다:- .env
- astro.config.mjs
- package.json
디렉터리src/
디렉터리api/
- drupal.ts
디렉터리pages/
디렉터리articles/
- index.astro
- types.ts
제목과 같은 콘텐츠를 페이지에 추가합니다.
articles.map()을 사용하여 Drupal 항목을 목록의 줄 항목으로 표시합니다.--- import {Jsona} from "jsona"; import {DrupalJsonApiParams} from "drupal-jsonapi-params"; import type {TJsonApiBody} from "jsona/lib/JsonaTypes"; import type { DrupalNode } from "../types"; import {getArticles} from "../api/drupal"; // 게시된 모든 articles 가져오기 const articles = await getArticles(); --- <html lang="en"> <head> <title>My news site</title> </head> <body> <h1>My news site</h1> <ul> {articles.map((article: DrupalNode) => ( <li> <a href={article.path.alias.replace("internal:en/", "")}> <h2>{article.title}</h2> <p>Published on {article.created}</p> </a> </li> ))} </ul> </body> </html>
개별 블로그 게시물 생성
위와 동일한 방법을 사용하여 Drupal에서 데이터를 가져오되 이번에는 각 article에 대해 고유한 페이지 경로를 만드는 페이지에서 데이터를 가져옵니다.
이 예시는 Astro의 기본 정적 모드를 사용하고 getStaticPaths() 함수를 사용하여 동적 라우팅 페이지 파일을 생성합니다. 이 함수는 빌드 시 호출되어 페이지가 되는 경로 목록을 생성합니다.
새 파일
src/pages/articles/[path].astro를 만들고src/api/drupal.ts에서DrupalNode인터페이스와getArticle()을 가져옵니다.getStaticPaths()함수에서 데이터를 가져와 블로그의 경로를 만듭니다.--- import {Jsona} from "jsona"; import {DrupalJsonApiParams} from "drupal-jsonapi-params"; import type {TJsonApiBody} from "jsona/lib/JsonaTypes"; import type { DrupalNode } from "../../types"; import {getArticles} from "../../api/drupal"; // 게시된 모든 articles 가져오기 export async function getStaticPaths() { const articles = await getArticles(); } ---이제 src/pages/articles 디렉터리에 새 파일이 포함되어야 합니다:
- .env
- astro.config.mjs
- package.json
디렉터리src/
디렉터리api/
- drupal.ts
디렉터리pages/
디렉터리articles/
- index.astro
- [path].astro
- types.ts
같은 파일에서 각 Drupal 항목을
params및props속성을 가진 객체에 매핑합니다.params속성은 페이지의 URL을 생성하는 데 사용되며props값은 페이지 컴포넌트에 props로 전달됩니다.--- import {Jsona} from "jsona"; import {DrupalJsonApiParams} from "drupal-jsonapi-params"; import type {TJsonApiBody} from "jsona/lib/JsonaTypes"; import type { DrupalNode } from "../../types"; import {getArticles} from "../../api/drupal"; // 게시된 모든 articles 가져오기 export async function getStaticPaths() { const articles = await getArticles(); return articles.map((article: DrupalNode) => { return { params: { // `path`를 `[path]` 라우팅 값과 일치하도록 선택 path: article.path.alias.split('/')[2] }, props: { title: article.title, body: article.body, date: new Date(article.created).toLocaleDateString('en-EN', { day: "numeric", month: "long", year: "numeric" }) } } }); } ---params내부의 속성은 동적 경로의 이름과 일치해야 합니다. 파일 이름이[path].astro이므로params에 전달되는 속성 이름은path여야 합니다.예시에서
props객체는 페이지에 세 가지 속성을 전달합니다:title: 글의 제목을 나타내는 문자열입니다.body: 글의 내용을 나타내는 문자열입니다.date: 파일 작성 날짜를 기준으로 한 타임스탬프입니다.
페이지
props를 사용하여 블로그 글을 표시합니다.--- import {Jsona} from "jsona"; import {DrupalJsonApiParams} from "drupal-jsonapi-params"; import type {TJsonApiBody} from "jsona/lib/JsonaTypes"; import type { DrupalNode } from "../../types"; import {getArticles} from "../../api/drupal"; // 게시된 모든 articles 가져오기 export async function getStaticPaths() { const articles = await getArticles(); return articles.map((article: DrupalNode) => { return { params: { path: article.path.alias.split('/')[2] }, props: { title: article.title, body: article.body, date: new Date(article.created).toLocaleDateString('en-EN', { day: "numeric", month: "long", year: "numeric" }) } } }); } const {title, date, body} = Astro.props; --- <html lang="en"> <head> <title>{title}</title> </head> <body> <h1>{title}</h1> <time>{date}</time> <article set:html={body.value} /> </body> </html>개발 서버 미리 보기로 이동하여 게시물 중 하나를 클릭하여 동적 경로가 작동하는지 확인합니다.
사이트 게시
웹사이트를 배포하려면 배포 가이드를 방문하여 선호하는 호스팅 제공업체의 지침을 따르세요.
커뮤니티 리소스
:::note[공유할 리소스가 있나요?]Astro와 Drupal을 함께 사용하는 방법에 대한 유용한 비디오나 블로그 게시물을 찾았거나 직접 만들었다면, 이 목록에 추가해 주세요!:::
더 많은 CMS 가이드
주요 CMS 파트너
-
CloudCannon
속도, 보안, 그리고 번거로움 없는 사용을 위해 만들어진 Git 기반 CMS입니다.