AEM開発者ブログ by YAMATO

アドビ社のデリバリーパートナー大和株式会社のAEM開発者ブログです。

すべてはノードという話

AEM開発者の皆様、お疲れ様です。
大和株式会社の狩野です。

今回は、ノードというものについて深堀りしようと思います。
AEMで開発や調査をしているときに便利な考え方と思いますので、お役に立てれば幸いです。

この記事では、自分がしばらくAEM開発に携わってきて身についた考え方を話したいと思います。
ノードとはどういうものでどうやって確認するかというところから、なぜそういう考えをするに至ったかという部分をお話できればと思います。

ノードとは何か

単純に言葉の意味でいうと「結び目」「集合点」「節」という意味です。
……正直書いてて自分でもピンと来てないです。

AEMにおける「ノード」は何かというと、「AEMのページを構成する最小単位」です。
あくまで、これはAdobe公式ドキュメントのどこかにあったりしたものではなく、この記事を書いている自分の解釈ではありますが、大きく的を外した解釈ではないと自負しております。

また、鋭い人は、以前の【AEMバックエンド開発】使うべきはJCRかSlingかAEMかという記事に書いた

結論から言うと、開発時のAPIにはAEMを使うのが望ましいです。 package名でいうとcom.day.cq.wcm.apiにあるクラスから使用を検討しましょう。

対して、Nodeクラスを提供しているpackageであるjavax.jcrパッケージはJCRデータを扱うための低レベルのAPIを提供しています。

この記事内でいうJCRのNodeクラスも同じ「ノード」なので、「先の記事にあった『ノードを使うべきではない』という主張と今回の記事は矛盾しているのでは?」と感じたかもしれませんが、それとはまた別の話です。
先の記事は、AEMのバックエンド開発で使うべきClassの話でしたが、今回はAEMのページがどんなもので構成されているか、問題が起きたときに見るべき場所を特定するにはどうすればいいか、という話をします。

AEMにおけるノードとは

ページはすべてノードから作られています。

AEMのページに表示されているあらゆる要素の情報はノードを辿れば理解することが可能となります。 直接ノードに書いていなかったとしても、参照する先が書かれていたりなどで、その先を辿れば必ず情報が見つかります。

ページを開いたときに何か問題があったとして、その原因がどこにあるかを探るためにまずはノードを辿るという方法が有用となります。

ノードの確認方法

CRXDEから確認します。

http://{host}:{port}/crx/de/index.jsp

CRXDEの画面
今回の記事では、この /content/we-retail/us/en に置いてあるタイトルコンポーネントの実体と、ソースコードがどこにあるかをノードを辿って確認していきます。

実際にノードを辿ってみる

バニラのAEMローカルインスタンスに入っているwe-retailを使って、タイトルコンポーネントの実体を確認しましょう。

we-retailのページ
http://localhost:4502/crx/de/index.jsp を開きます
CRXDEのトップ画面
② CRXDEのアドレスバーに/content/we-retail/us/en/と入力する 今回は「タイトルコンポーネントの実体」を確認するので深堀りはしませんが、この選んだ /content/we-retail/us/en ノードの下にある jcr:content ノードを確認すると、ページプロパティが格納されています。
ノードツリー
③ 左のノードツリーで /content/we-retail/us/en のノードが選択されているので、順に jcr:content/root/ と辿っていくと、ページに配置されているコンポーネントが並びます
/content/we-retail/us/enを選択
④ /content/we-retail/us/en/jcr:content/root/responsivegrid/section_title ノードを選択してみると、そのコンポーネントのプロパティが並んでいます
プロパティ
section_title のノードに sling:resourceType というプロパティがあると思います。 この中身が恐らく weretail/components/content/title になってると思うので、頭に /apps/ を付与して /apps/weretail/components/content/title として、CRXDEのアドレスバーに入力してください

sling:resourceType プロパティに格納されているパスに、最初から /apps/ が付与されていないのは、 AEMのオーバーレイ という拡張性を高める機能によるもので、 /apps/ 配下にノードが存在しない場合は、 /libs/ を付与して、参照先を探します。
 このオーバーレイという機能についての言及は本題から逸れるので割愛しますが、AEMにおける大事な機能の1つです。

/apps/追加
/apps/weretail/components/content/title これが、weretailのタイトルコンポーネントの中身となりますが、左のノードツリーを見ると、clientlibsフォルダしか存在してません。「実体はどこにあるんだ?」と疑問に思うかも知れません。
そこで、 /apps/weretail/components/content/title ノードのプロパティを確認すると sling:resourceSuperType というプロパティがあり、その中身が core/wcm/components/title/v2/title のような値になってます。
we-retailのtitleコンポーネント
 これも、先頭に /apps/ を追加して、 /apps/core/wcm/components/title/v2/title とCRXDEのアドレスバーで検索します。
/apps/core/wcm/components/title/v2/title
⑦ この /apps/core/wcm/components/title/v2/title の中に、 /content/we-retail/us/en に置かれたタイトルコンポーネントがどういう動作をしているかを確認することができます

これまでの手順で、実際に呼び出されているコンポーネントがどこにあるかを、ページ及びページを構成しているノードにあった情報だけで知ることができました。

実際にノードを辿ってみて得られたこと

実際にCRXDEを使ってページのノードを辿ってみて、何が嬉しいのかというと

  • コアコンポーネントについて知らない状態でも、コアコンポーネントにたどり着くことができた
    • → コアコンポーネントに限らず、自分以外の誰かが開発したコンポーネントにもたどり着くことができる
  • /apps/weretail/components 配下に置かれたtitleコンポーネントは、拡張部分のみが実装されていて、大部分は /apps/core/wcm/components 配下に置かれていることがわかった
    • → ページで使われているコンポーネントの実装の大部分がプロジェクトで開発したコンポーネントではなく、コアコンポーネント のように元からある部品を使っていると知ることができた
      同じように /content/we-retail/us/en/jcr:content/root 配下にはページに配置された他のコンポーネントの情報もあるので、同じような手順で辿ってみると、ページを構成しているものがノードであるという感覚がつかめるかもしれません。

ページで読み込まれているCSS/Javascriptファイルを特定してみる①

先の手順では、コンポーネントの実装を辿ることができましたが、フロントエンド部分も辿れるのか試してみましょう。

/content/we-retail/us/en/jcr:content ノードを左のノードツリーから選択し、 cq:template プロパティを確認する

templateプロパティ
cq:template プロパティの中身が /conf/we-retail/settings/wcm/templates/hero-page となっているので、そのノードをCRXDEのアドレスバーに入力して中身を確認する
hero-pageノード
/conf/we-retail/settings/wcm/templates/hero-page から、 policies/jcr:content とノードを辿ると cq:policy というプロパティが確認できると思います。
 クライアントライブラリはページポリシーで読み込んでいくはずなので、policiesについてのノードを辿りました。
cq:policyプロパティ
④ この cq:policy プロパティの中身である weretail/components/structure/page/we-retail-page ですが、恐らく、先の手順で書いたように頭に /apps//libs/ を付与してもノードが存在しないと思います。
 では何を付与するかというと、 /conf/we-retail/settings/wcm/policies/ というノードまで一旦戻ってから、 weretail/components/structure/page/we-retail-page を付与し、 /conf/we-retail/settings/wcm/policies/weretail/components/structure/page/we-retail-page というパスにするとノードが見つかると思います。
⑤ しかし、多くの実装ではこの /conf/we-retail/settings/wcm/policies/weretail/components/structure/page/we-retail-page ノードに clientlibs プロパティがあり、ここにclientlibsのカテゴリが格納されているはずなんですが、格納されていません。
 なので、アプローチを変えます。

↓(参考)clientlibsプロパティが無い we-retail we-retail

we-retailのページポリシー
↓(参考)clientlibsプロパティがある wknd
wkndのページポリシー
/conf/we-retail/settings/wcm/templates/hero-page/structure/jcr:content ノードに戻って、そのノードの sling:resourceType を確認してみましょう。
  そうしたら、そのページで呼び出されているコンポーネントがわかります。今回の場合は、 /apps/weretail/components/structure/page ですね。
ページコンポーネント
⑦ このコンポーネント内のファイルを見ると、 customheaderlibs.html というファイルがあります。このHTMLファイルの中に

<sly data-sly-call="${clientlib.css @ categories='we-retail.base'}"></sly>

という記述があります。この記述によって、 we-retail.base というカテゴリのクライアントライブラリが読み込まれてます。

customheaderlibs.html
⑧ 次はJavascriptファイルですが、こちらは customfooterlibs.html というHTMLファイルの中にあります。   この中の

<sly data-sly-call="${clientlib.js @ categories='we-retail.base'}"/>

この記述でCSSと同様に we-retail.base というカテゴリのクライアントライブラリが読み込まれているのを確認できました。

customfooterlibs.html

ページで読み込まれているCSS/Javascriptファイルを特定してみる②

先の手順では、ページに読み込まれているコンポーネントにJS/CSS読み込みが埋め込まれている場合でしたが、次はwkndの実装を確認してみましょう。

/content/wknd/us/en/jcr:content ノードを開くと、 cq:template プロパティに /conf/wknd/settings/wcm/templates/landing-page-template とあるので、このノードを探しに行きます。

wknd/us/en
/conf/wknd/settings/wcm/templates/landing-page-template のノードを辿っていくと、 policies/jcr:content というノードがあるので、そのノードの cq:policy というプロパティを確認します。
landing page templateのPolicy
wknd/components/page/policy_1570140199406 と値が入っているので、先の手順と同じように /conf/wknd/settings/wcm/policies/wknd/components/page/policy_1570140199406 ノードを確認すると、 clientlibs プロパティに wknd.site , wknd.base とあるので、この2つのカテゴリのクライアントライブラリが呼び出されていることがわかります。
clientlibsプロパティ

最後に

実際にノードを辿ってみるという手順を踏んでみて、いかがでしたか? 「まだよくわからない」とか「なるほど!わかった!」など様々な意見もあるかと思いますが、わからない部分があればまずノードから原因を特定してみるのを覚えると、一気にAEMのことがわかるはずです。