AEM의 GraphQL API를 사용하는 React 앱 빌드
이 장에서는 AEM의 GraphQL API가 외부 애플리케이션에서 경험을 유도하는 방법을 살펴봅니다.
간단한 React 앱을 사용하여 AEM의 GraphQL API로 노출된 팀 및 개인 콘텐츠를 쿼리하고 표시합니다. React의 사용은 대부분 중요하지 않으며 소비하는 외부 애플리케이션은 모든 플랫폼의 프레임워크에 작성될 수 있습니다.
사전 요구 사항
이 다중 파트 자습서의 이전 부분에 설명된 단계가 완료되었거나 basic-tutorial-solution.content.zip이(가) AEM as a Cloud Service 작성자 및 Publish 서비스에 설치되어 있다고 가정합니다.
이 장의 IDE 스크린샷은 Visual Studio 코드 에서 가져옵니다.
다음 소프트웨어를 설치해야 합니다.
목표
방법 알아보기:
- 예제 React 앱을 다운로드하여 시작합니다.
- AEM Headless JS SDK를 사용하여 AEM의 GraphQL 끝점을 쿼리합니다
- 팀 및 참조된 멤버 목록을 AEM에 쿼리
- 팀 구성원의 세부 사항에 대해 AEM 쿼리
샘플 React 앱 가져오기
이 장에서는 AEM의 GraphQL API와 상호 작용하고 팀원 및 팀원으로부터 얻은 개인 데이터를 표시하는 데 필요한 코드로 스터브아웃 샘플 React 앱을 구현합니다.
샘플 React 앱 소스 코드는 Github.com (https://github.com/adobe/aem-guides-wknd-graphql/tree/main/basic-tutorial)에서 사용할 수 있습니다.
React 앱을 다운로드하려면:
-
Github.com에서 샘플 WKND GraphQL React 앱을 복제합니다.
code language-shell $ cd ~/Code $ git clone git@github.com:adobe/aem-guides-wknd-graphql.git
-
basic-tutorial
폴더로 이동하여 IDE에서 엽니다.code language-shell $ cd ~/Code/aem-guides-wknd-graphql/basic-tutorial $ code .
-
AEM as a Cloud Service Publish 서비스에 연결하려면
.env.development
을(를) 업데이트하십시오.REACT_APP_HOST_URI
의 값을 AEM as a Cloud Service의 Publish URL로 설정합니다(예:REACT_APP_HOST_URI=https://publish-p123-e456.adobeaemcloud.com
) 및REACT_APP_AUTH_METHOD
의 값을none
에 변환
note note NOTE 프로젝트 구성, 콘텐츠 조각 모델, 작성된 콘텐츠 조각, GraphQL 끝점 및 이전 단계의 지속 쿼리를 게시했는지 확인합니다. 로컬 AEM 작성자 SDK에서 위의 단계를 수행한 경우 http://localhost:4502
및REACT_APP_AUTH_METHOD
의 값을basic
로 지정할 수 있습니다. -
명령줄에서
aem-guides-wknd-graphql/basic-tutorial
폴더로 이동합니다. -
React 앱 시작
code language-shell $ cd ~/Code/aem-guides-wknd-graphql/basic-tutorial $ npm install $ npm start
-
React 앱이 http://localhost:3000/에 개발 모드에서 시작됩니다. 자습서 전체에서 React 앱에 대한 변경 사항이 즉시 반영됩니다.
React 앱 구조
샘플 React 앱에는 다음 세 가지 주요 부분이 있습니다.
-
src/api
폴더에 AEM에 GraphQL 쿼리를 만드는 데 사용되는 파일이 있습니다.src/api/aemHeadlessClient.js
은(는) AEM과의 통신에 사용되는 AEM Headless 클라이언트를 초기화하고 내보냅니다.src/api/usePersistedQueries.js
이(가) 사용자 지정 React 후크를 구현함 AEM GraphQL에서Teams.js
및Person.js
보기 구성 요소로 데이터를 반환합니다.
-
src/components/Teams.js
파일에는 목록 쿼리를 사용하여 팀 및 해당 멤버의 목록이 표시됩니다. -
src/components/Person.js
파일은 매개 변수가 있는 단일 결과 쿼리를 사용하여 한 사람의 세부 정보를 표시합니다.
AEMHeadless 개체 검토
aemHeadlessClient.js
파일에서 AEM과의 통신에 사용되는 AEMHeadless
개체를 만드는 방법을 검토하십시오.
-
src/api/aemHeadlessClient.js
열기 -
1-40행을 검토합니다.
-
JavaScript용 AEM Headless 클라이언트의 11행
AEMHeadless
선언을 가져옵니다. -
.env.development
, 14-22행 및 화살표 함수 식setAuthorization
, 31-40행에 정의된 변수를 기반으로 한 권한 부여의 구성입니다. -
포함된 개발 프록시 구성에 대한
serviceUrl
설정, 27행.
-
-
줄 42-49는
AEMHeadless
클라이언트를 인스턴스화하고 React 앱 전체에서 사용하도록 내보낼 때 가장 중요합니다.
// Initialize the AEM Headless Client and export it for other files to use
const aemHeadlessClient = new AEMHeadless({
serviceURL: serviceURL,
endpoint: REACT_APP_GRAPHQL_ENDPOINT,
auth: setAuthorization(),
});
export default aemHeadlessClient;
AEM GraphQL 지속 쿼리를 실행하도록 구현
AEM GraphQL 지속 쿼리를 실행하기 위해 제네릭 fetchPersistedQuery(..)
함수를 구현하려면 usePersistedQueries.js
파일을 여십시오. fetchPersistedQuery(..)
함수는 aemHeadlessClient
개체의 runPersistedQuery()
함수를 사용하여 쿼리를 비동기적으로 약속 기반 동작을 실행합니다.
나중에 사용자 지정 React useEffect
후크가 이 함수를 호출하여 AEM에서 특정 데이터를 검색합니다.
src/api/usePersistedQueries.js
업데이트fetchPersistedQuery(..)
의 35행, 아래 코드.
/**
* Private, shared function that invokes the AEM Headless client.
*
* @param {String} persistedQueryName the fully qualified name of the persisted query
* @param {*} queryParameters an optional JavaScript object containing query parameters
* @returns the GraphQL data or an error message
*/
async function fetchPersistedQuery(persistedQueryName, queryParameters) {
let data;
let err;
try {
// AEM GraphQL queries are asynchronous, either await their return or use Promise-based syntax
const response = await aemHeadlessClient.runPersistedQuery(
persistedQueryName,
queryParameters
);
// The GraphQL data is stored on the response's data field
data = response?.data;
} catch (e) {
// An error occurred, return the error messages
err = e
.toJSON()
?.map((error) => error.message)
?.join(", ");
console.error(e.toJSON());
}
// Return the GraphQL and any errors
return { data, err };
}
팀 기능 구현
그런 다음 React 앱의 기본 보기에서 팀과 팀원을 표시하는 기능을 빌드합니다. 이 기능을 사용하려면 다음 조건을 충족해야 합니다.
my-project/all-teams
지속 쿼리를 호출하여 AEM의 팀 콘텐츠 조각 목록을 반환하는src/api/usePersistedQueries.js
의 새 사용자 지정 React useEffect 후크입니다.- 새 사용자 지정 React
useEffect
후크를 호출하고 팀 데이터를 렌더링하는src/components/Teams.js
의 React 구성 요소입니다.
완료되면 앱의 기본 보기가 AEM의 팀 데이터로 채워집니다.
단계
-
src/api/usePersistedQueries.js
열기 -
useAllTeams()
함수를 찾습니다. -
fetchPersistedQuery(..)
을(를) 통해 지속 쿼리my-project/all-teams
을(를) 호출하는useEffect
후크를 만들려면 다음 코드를 추가하십시오. 또한 후크는data?.teamList?.items
에 있는 AEM GraphQL 응답의 관련 데이터만 반환하여 React 보기 구성 요소가 상위 JSON 구조를 인식하지 못하도록 합니다.code language-javascript /** * Custom hook that calls the 'my-project/all-teams' persisted query. * * @returns an array of Team JSON objects, and array of errors */ export function useAllTeams() { const [teams, setTeams] = useState(null); const [error, setError] = useState(null); // Use React useEffect to manage state changes useEffect(() => { async function fetchData() { // Call the AEM GraphQL persisted query named "my-project/all-teams" const { data, err } = await fetchPersistedQuery( "my-project/all-teams" ); // Sets the teams variable to the list of team JSON objects setTeams(data?.teamList?.items); // Set any errors setError(err); } // Call the internal fetchData() as per React best practices fetchData(); }, []); // Returns the teams and errors return { teams, error }; }
-
src/components/Teams.js
열기 -
Teams
React 구성 요소에서useAllTeams()
후크를 사용하여 AEM에서 팀 목록을 가져옵니다.code language-javascript import { useAllTeams } from "../api/usePersistedQueries"; ... function Teams() { // Get the Teams data from AEM using the useAllTeams const { teams, error } = useAllTeams(); ... }
-
반환된 데이터를 기반으로 오류 메시지를 표시하거나 표시기를 로드하는 보기 기반 데이터 유효성 검사를 수행합니다.
code language-javascript function Teams() { const { teams, error } = useAllTeams(); // Handle error and loading conditions if (error) { // If an error ocurred while executing the GraphQL query, display an error message return <Error errorMessage={error} />; } else if (!teams) { // While the GraphQL request is executing, show the Loading indicator return <Loading />; } ... }
-
마지막으로 팀 데이터를 렌더링합니다. GraphQL 쿼리에서 반환된 각 팀은 제공된
Team
React 하위 구성 요소를 사용하여 렌더링됩니다.code language-javascript import React from "react"; import { Link } from "react-router-dom"; import { useAllTeams } from "../api/usePersistedQueries"; import Error from "./Error"; import Loading from "./Loading"; import "./Teams.scss"; function Teams() { const { teams, error } = useAllTeams(); // Handle error and loading conditions if (error) { return <Error errorMessage={error} />; } else if (!teams) { return <Loading />; } // Teams have been populated by AEM GraphQL query. Display the teams. return ( <div className="teams"> {teams.map((team, index) => { return <Team key={index} {...team} />; })} </div> ); } // Render single Team function Team({ title, shortName, description, teamMembers }) { // Must have title, shortName and at least 1 team member if (!title || !shortName || !teamMembers) { return null; } return ( <div className="team"> <h2 className="team__title">{title}</h2> <p className="team__description">{description.plaintext}</p> <div> <h4 className="team__members-title">Members</h4> <ul className="team__members"> {/* Render the referenced Person models associated with the team */} {teamMembers.map((teamMember, index) => { return ( <li key={index} className="team__member"> <Link to={`/person/${teamMember.fullName}`}> {teamMember.fullName} </Link> </li> ); })} </ul> </div> </div> ); } export default Teams;
사용자 기능 구현
팀 기능을 완료하면 팀 구성원 또는 개인 세부 정보에 대한 표시를 처리하는 기능을 구현해 보겠습니다.
이 기능을 사용하려면 다음 조건을 충족해야 합니다.
-
매개 변수가 있는
my-project/person-by-name
지속 쿼리를 호출하고 단일 개인 레코드를 반환하는src/api/usePersistedQueries.js
의 새 사용자 지정 React useEffect 후크입니다. -
src/components/Person.js
에서 개인의 전체 이름을 쿼리 매개 변수로 사용하고, 새로운 사용자 지정 ReactuseEffect
후크를 호출하고, 개인 데이터를 렌더링하는 React 구성 요소입니다.
완료되면 팀 보기에서 사용자 이름을 선택하면 사용자 보기가 렌더링됩니다.
-
src/api/usePersistedQueries.js
열기 -
usePersonByName(fullName)
함수를 찾습니다. -
fetchPersistedQuery(..)
을(를) 통해 지속 쿼리my-project/all-teams
을(를) 호출하는useEffect
후크를 만들려면 다음 코드를 추가하십시오. 또한 후크는data?.teamList?.items
에 있는 AEM GraphQL 응답의 관련 데이터만 반환하여 React 보기 구성 요소가 상위 JSON 구조를 인식하지 못하도록 합니다.code language-javascript /** * Calls the 'my-project/person-by-name' and provided the {fullName} as the persisted query's `name` parameter. * * @param {String!} fullName the full * @returns a JSON object representing the person */ export function usePersonByName(fullName) { const [person, setPerson] = useState(null); const [errors, setErrors] = useState(null); useEffect(() => { async function fetchData() { // The key is the variable name as defined in the persisted query, and may not match the model's field name const queryParameters = { name: fullName }; // Invoke the persisted query, and pass in the queryParameters object as the 2nd parameter const { data, err } = await fetchPersistedQuery( "my-project/person-by-name", queryParameters ); if (err) { // Capture errors from the HTTP request setErrors(err); } else if (data?.personList?.items?.length === 1) { // Set the person data after data validation setPerson(data.personList.items[0]); } else { // Set an error if no person could be found setErrors(`Cannot find person with name: ${fullName}`); } } fetchData(); }, [fullName]); return { person, errors }; }
-
src/components/Person.js
열기 -
Person
React 구성 요소에서fullName
경로 매개 변수를 구문 분석하고usePersonByName(fullName)
후크를 사용하여 AEM에서 개인 데이터를 가져옵니다.code language-javascript import { useParams } from "react-router-dom"; import { usePersonByName } from "../api/usePersistedQueries"; ... function Person() { // Read the person's `fullName` which is the parameter used to query for the person's details const { fullName } = useParams(); // Query AEM for the Person's details, using the `fullName` as the filtering parameter const { person, error } = usePersonByName(fullName); ... }
-
반환된 데이터를 기반으로 오류 메시지를 표시하거나 표시기를 로드하는 보기 기반 데이터 유효성 검사를 수행합니다.
code language-javascript function Person() { // Read the person's `fullName` which is the parameter used to query for the person's details const { fullName } = useParams(); // Query AEM for the Person's details, using the `fullName` as the filtering parameter const { person, error } = usePersonByName(fullName); // Handle error and loading conditions if (error) { return <Error errorMessage={error} />; } else if (!person) { return <Loading />; } ... }
-
마지막으로 개인 데이터를 렌더링합니다.
code language-javascript import React from "react"; import { useParams } from "react-router-dom"; import { usePersonByName } from "../api/usePersistedQueries"; import { mapJsonRichText } from "../utils/renderRichText"; import Error from "./Error"; import Loading from "./Loading"; import "./Person.scss"; function Person() { // Read the person's `fullName` which is the parameter used to query for the person's details const { fullName } = useParams(); // Query AEM for the Person's details, using the `fullName` as the filtering parameter const { person, error } = usePersonByName(fullName); // Handle error and loading conditions if (error) { return <Error errorMessage={error} />; } else if (!person) { return <Loading />; } // Render the person data return ( <div className="person"> <img className="person__image" src={process.env.REACT_APP_HOST_URI+person.profilePicture._path} alt={person.fullName} /> <div className="person__occupations"> {person.occupation.map((occupation, index) => { return ( <span key={index} className="person__occupation"> {occupation} </span> ); })} </div> <div className="person__content"> <h1 className="person__full-name">{person.fullName}</h1> <div className="person__biography"> {/* Use this utility to transform multi-line text JSON into HTML */} {mapJsonRichText(person.biographyText.json)} </div> </div> </div> ); } export default Person;
앱 사용
http://localhost:3000/ 앱을 검토하고 구성원 링크를 클릭하세요. 또한 AEM에서 컨텐츠 조각을 추가하여 팀 Alpha에 더 많은 팀 및/또는 구성원을 추가할 수 있습니다.
언더더 후드
all-teams
요청에 대해 브라우저의 개발자 도구 > 네트워크 및 필터 를 엽니다. GraphQL API 요청 /graphql/execute.json/my-project/all-teams
이(가) REACT_APP_HOST_URI
의 값(예: <https://publish-pxxx-exxx.adobeaemcloud.com
)에 대해 http://localhost:3000
및 NOT 에 대해 수행됩니다. http-proxy-middleware
모듈을 사용하여 프록시 설정을(를) 사용하도록 설정했기 때문에 React 앱의 도메인에 대해 요청이 수행됩니다.
기본 ../setupProxy.js
파일을 검토하고 ../proxy/setupProxy.auth.**.js
파일 내에서 /content
및 /graphql
경로가 프록시화되는 방식을 확인하고 정적 자산이 아님을 나타냅니다.
module.exports = function(app) {
app.use(
['/content', '/graphql'],
...
로컬 프록시를 사용하는 것은 프로덕션 배포에 적합하지 않습니다. 자세한 내용은 프로덕션 배포 섹션에서 확인할 수 있습니다.
축하합니다! congratulations
축하합니다! AEM의 GraphQL API의 데이터를 기본 자습서의 일부로 소비하고 표시할 React 앱을 만들었습니다!