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 今回の記事では、この /content/we-retail/us/en に置いてあるタイトルコンポーネントの実体と、ソースコードがどこにあるかをノードを辿って確認していきます。
実際にノードを辿ってみる
バニラのAEMローカルインスタンスに入っているwe-retailを使って、タイトルコンポーネントの実体を確認しましょう。
① http://localhost:4502/crx/de/index.jsp を開きます
② 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/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/weretail/components/content/title
これが、weretailのタイトルコンポーネントの中身となりますが、左のノードツリーを見ると、clientlibsフォルダしか存在してません。「実体はどこにあるんだ?」と疑問に思うかも知れません。
そこで、 /apps/weretail/components/content/title
ノードのプロパティを確認すると sling:resourceSuperType
というプロパティがあり、その中身が core/wcm/components/title/v2/title
のような値になってます。
これも、先頭に /apps/
を追加して、 /apps/core/wcm/components/title/v2/title
とCRXDEのアドレスバーで検索します。
⑦ この /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
プロパティを確認する
② cq:template
プロパティの中身が /conf/we-retail/settings/wcm/templates/hero-page
となっているので、そのノードをCRXDEのアドレスバーに入力して中身を確認する
③ /conf/we-retail/settings/wcm/templates/hero-page
から、 policies/jcr:content
とノードを辿ると cq:policy
というプロパティが確認できると思います。
クライアントライブラリはページポリシーで読み込んでいくはずなので、policiesについてのノードを辿りました。
④ この 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
↓(参考)clientlibsプロパティがある 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
というカテゴリのクライアントライブラリが読み込まれてます。
⑧ 次はJavascriptファイルですが、こちらは customfooterlibs.html
というHTMLファイルの中にあります。
この中の
<sly data-sly-call="${clientlib.js @ categories='we-retail.base'}"/>
この記述でCSSと同様に we-retail.base
というカテゴリのクライアントライブラリが読み込まれているのを確認できました。
ページで読み込まれているCSS/Javascriptファイルを特定してみる②
先の手順では、ページに読み込まれているコンポーネントにJS/CSS読み込みが埋め込まれている場合でしたが、次はwkndの実装を確認してみましょう。
① /content/wknd/us/en/jcr:content
ノードを開くと、 cq:template
プロパティに /conf/wknd/settings/wcm/templates/landing-page-template
とあるので、このノードを探しに行きます。
② /conf/wknd/settings/wcm/templates/landing-page-template
のノードを辿っていくと、 policies/jcr:content
というノードがあるので、そのノードの cq:policy
というプロパティを確認します。
③ wknd/components/page/policy_1570140199406
と値が入っているので、先の手順と同じように /conf/wknd/settings/wcm/policies/wknd/components/page/policy_1570140199406
ノードを確認すると、 clientlibs
プロパティに wknd.site
, wknd.base
とあるので、この2つのカテゴリのクライアントライブラリが呼び出されていることがわかります。
最後に
実際にノードを辿ってみるという手順を踏んでみて、いかがでしたか? 「まだよくわからない」とか「なるほど!わかった!」など様々な意見もあるかと思いますが、わからない部分があればまずノードから原因を特定してみるのを覚えると、一気にAEMのことがわかるはずです。