Hugoの一般的な使用テクニック:理想的な静的ブログフレームワーク
James Reed
Infrastructure Engineer · Leapcell

Hugoの一般的な使用テクニックの紹介
Hugoは、オープンソースのgoldmarkをMarkdownパーサーとして使用しており、GitHubと互換性があります。
紹介
Hugoは、Golangで記述された静的ウェブサイトのページ生成ツールであり、その効率はRubyで記述されたJekyllよりもはるかに高いです。Githubからバイナリパッケージを直接ダウンロードし、解凍後、PATH環境変数に追加して使用できます。
SCSS機能をサポートし、テーマ開発に適しているhugo - extendedバージョンをインストールすることをお勧めします。さらに、多くの拡張機能がgoldmarkを通じて実装されており、最も一般的に使用されるのはタスクリストです。詳細なリストについては、MarkdownGuideの紹介を参照してください。Hugoは高度にカスタマイズ可能であり、設計時にMarkdown構文との互換性を試みます。
基本的な使用法
- テーマを通じてページをカスタマイズする: 多くのテーマから選択でき、themes.gohugo.ioで閲覧できます。hugo - geekdocを例にとると、ダウンロード後、プロジェクトのthemesディレクトリに直接配置します。
- インストールが成功したか確認する:
hugo version
- 現在のディレクトリに新しいプロジェクトを作成する:
hugo new site hugo
- テーマテンプレートをthemesディレクトリに追加し、構成ファイルconfig.tomlにtheme="doks"を追加するか、--theme hydeパラメーターで起動します:
git clone --depth 1 --recursive https://github.com/h - enk/doks.git themes
- 記事を作成する:
hugo --verbose new post/create - hugo - blog.md
- サービスを開始する:
hugo server --buildDrafts --bind="0.0.0.0" --port=38787
- 静的ファイル(ドラフトを含む)を生成し、buildDirディレクトリに保存します。デフォルトではpublicです:
hugo -D
構成ファイル
デフォルトでは、ルートディレクトリにあるhugo.toml、hugo.yaml、hugo.jsonなどの構成ファイルが使用されます。--config a.toml,b.tomlの方法で構成ファイルを指定することもできます。ただし、ディレクトリを使用することをお勧めします。これはデフォルトでconfigディレクトリであり、構成ファイルのconfigDir構成項目を使用して変更できます。
hugo.toml
config/
|-_default/
| |-hugo.toml
| |-languages.toml
| |-menus.en.toml
| |-menus.cn.toml
| `-params.toml
|-develop/
| |-hugo.toml
| `-params.toml
`-production/
`-hugo.toml
ディレクトリの最初のレイヤーは環境であり、_defaultデフォルト構成が含まれ、productionディレクトリも一般的に構成されます。たとえば、--environment productionで起動すると、_defaultの構成がproductionの構成とマージされ、テンプレートで次の方法で確認できます。
{{ if eq hugo.Environment "production" }} <div> Production Mode </div> {{ else }} <div> Development Mode </div> {{ end }}
2番目のレイヤーは、構成項目の最上位に対応しています。一般的に、[Params]はparams.tomlに対応し、[Menu]はmenu.tomlに対応し、異なる言語が含まれます。
基本的な概念
セクション
コンテンツディレクトリ構造に基づいて定義されたページのコレクション。このディレクトリの最初のレベルのサブディレクトリはすべてセクションです。サブディレクトリをセクションにする場合は、ディレクトリに_index.mdファイルを定義する必要があります。このようにして、すべてのセクションがセクションツリーを形成します。
content/
`-blog/ <-- セクション、コンテンツのサブディレクトリであるため
|-funny - cats/
| |-mypost.md
| `-kittens/ <-- セクション、_index.mdが含まれているため
| `-_index.md
`-tech/ <-- セクション、上記と同じ
`-_index.md
セクションを通じてコンテンツを分類することに加えて、Typeのカスタマイズも許可されています。ページにTypeがない場合、デフォルトでセクションに対応する値が使用されます。使用するときは、次の検索順序を参照できます。テンプレートTypeはセクションよりも優先度が高く、より良いカスタマイズが可能であることがわかります。.Kind変数であるpage、section、term、home、taxonomyなど、さまざまなページタイプを処理することもできます。詳細については、公式Methods Kindの紹介を参照してください。
テンプレート
Hugoは、GoLangのhtml/templateライブラリをテンプレートエンジンとして使用しており、3種類のテンプレートに分かれています。
- single: 単一のページをレンダリングするために使用されます。
- list: ディレクトリ内のすべてのコンテンツなど、関連コンテンツのセットをレンダリングします。
- partial: 他のテンプレートから参照でき、ページヘッダーやページフッターなど、テンプレートレベルのコンポーネントとして機能します。
中でも、baseof.htmlは異なるセクションのルートテンプレートとして機能し、テンプレート検索メカニズムがあります。コンテンツに完全に一致するテンプレートが見つからない場合は、1レベル上に移動してそこから検索します。基本テンプレートbaseof.htmlの検索ルールは次のとおりです。
- /layouts/section/<TYPE>-baseof.html
- /themes/<THEME>/layouts/section/<TYPE>-baseof.html <--- 異なるTypesのテンプレート
- /layouts/<TYPE>/baseof.html
- /themes/<THEME>/layouts/<TYPE>/baseof.html
- /layouts/section/baseof.html
- /themes/<THEME>/layouts/section/baseof.html <--- 異なるセクションのテンプレート
- /layouts/_default/<TYPE>-baseof.html
- /themes/<THEME>/layouts/_default/<TYPE>-baseof.html
- /layouts/_default/baseof.html
- /themes/<THEME>/layouts/_default/baseof.html <--- デフォルト値
テンプレートでは、Partialsモジュールは{{ partial "xx" . }}
を通じて導入され、モジュール内の対応するページ情報は{{ . }}
を通じて表示できます。さらに、{{ partial "header.html" (dict "current" . "param1" "1" "param2" "2" ) }}
のようにパラメーターを追加できます。
上記のPartialsの導入方法に加えて、{{ define "main" }} ... {{ end }}
を通じて異なるタイプ(single、listなど)でモジュールをカスタマイズし、{{ block "main" . }} ... {{ end }}
のように使用することもできます。
変数参照
テンプレートでは、変数は{{ xxx }}
のように参照され、変数の使い方は次のとおりです。
- グローバル構成: たとえば、.Site.Params.titileはhugo.tomlの[Params]またはconfig/_default/params.tomlの構成に対応します。
- ページパラメーター: 先頭で指定し、
.Params.xxx
のように参照できます。 - その他のパラメーター: 一部の組み込みパラメーターが含まれており、
.Title
、.Section
、.Content
、.Page
など、直接使用できます。 - ローカリゼーションパラメーター: 一般的に、
i18n "global - identifier"
で指定するか、i18n .Site.Params.xxx
で変換します。 - 関数の使用: たとえば、
hugo.Environment
、hugo.IsExtended
など。その他については、Functionsの内容を参照してください。
上記の変数に加えて、json、yaml、toml、xmlなどの形式のデータファイルもdataディレクトリに保存し、.Site.Data.xxx
のようにテンプレートで参照できます。
基本的な構文
テンプレートは{{ }}
でラップされ、内部のコンテンツはアクション(Action)と呼ばれ、一般的に2つのタイプが含まれます。
- データ評価: 変数を直接使用するなど、テンプレートに直接出力されます。
- 制御構造: 条件、ループ、関数などが含まれます。
さらに、各行をラップするかどうかを制御できます。これは-
によって制御されます。たとえば、{{- -}}
は、先頭も末尾もラップしないことを意味しますが、末尾で圧縮することもできます。
----- コメント {{/* comment */}} ----- 既存の変数、カスタム変数へのアクセス {{ .Title }} {{ $var }} {{ $var := "Value" }} {{ $var := `Hello World` }}
スライス VS. マップ
スライスは配列に対応し、一般的な操作は次のとおりです。
{{ $fruit := slice "Apple" "Banana" }} {{ $fruit = append "Cherry" $fruit }} {{ $fruit = append $fruit (slice "Pear") }} {{ $fruit = append (slice "Cherry" "Peach") $fruit }} {{ $fruit = uniq $fruit }} {{ range $fruit }} I love {{ . }} {{ end }} {{ range $index, $value := $fruit }} {{ $value }} or {{ . }} is at index {{ $index }} {{ end }} {{ $first := first 2 $fruit }} {{ $last := last 3 $fruit }} {{ $third := first 3 $fruit | last 1 }} {{ $third := index $fruit 2 }}
辞書の使用法は次のとおりです。
{{ $hero := dict "firstame" "John" "lastname" "Lennon" }} {{ $hero = merge $hero (dict "birth" "1940") }} {{ $hero.firstame }} {{ $firstname := index $hero "firstname" }} {{ range $key, $value := $hero }} {{ $key }}: {{ $value }} {{ end }} {{ $basis := "lastname" }} {{ if eq $relation "friend" }} {{ $basis = "firstname" }} {{ end }} Hello {{ index $hero $basis }}! {{ range slice "firstname" "lastname" }} {{ . }}: {{ index $hero . }} {{ end }}
使用するときは、{{ if reflect.IsSlice $value }}
またはIsMap
を通じて特定のタイプを判断できます。
論理判断
if
ステートメントはある値の真偽を判断するために使用されますが、with
を使用することをお勧めします。このとき、コンテキストはスコープ内でリバウンドされます。
{{ if .IsHome }} xxx {{ end }} // IsHomeがtrueの場合に呼び出されます。それ以外の場合はnotを使用します {{ if eq .Title "Home" }} xxx {{ end }} // 変数が等しいかどうかを判断します。等しくない場合はneを使用します {{ if and .IsHome .Params.show }} xxx {{ end }} // 複数の条件が同時に満たされている場合、またはいずれかの条件が満たされている場合 {{ if strings.Contains "hugo" "go" }} xxx {{end}} // 指定された文字列が含まれているかどうかを判断します // 次の2つの方法は同等であり、空の場合はスキップされます {{ if isset .Params "title" }} <h4>{{ index .Params "title" }}</h4> {{ end }} {{ with .Params.title }} <h4>{{ . }}</h4> {{ end }} // ただし、else ifステートメントを使用できます {{ if (isset .Params "description") }} {{ index .Params "description" }} {{ else if (isset .Params "summary") }} {{ index .Params "summary" }} {{ else }} {{ .Summary }} {{ end }} // 次は少し複雑なロジックです {{ if (and (or (isset .Params "title") (isset .Params "caption")) (isset .Params "attr")) }} <div class="caption {{ index .Params "attr" }}"> {{ if (isset .Params "title") }} <h4>{{ index .Params "title" }}</h4> {{ end }} {{ if (isset .Params "caption") }} <p>{{ index .Params "caption" }}</p> {{ end }} </div> {{ end }}
Paramにdescription属性が設定されている場合、Paramの説明コンテンツが出力され、それ以外の場合はSummaryのコンテンツが出力されます。
{{ with .Param "description" }} {{ . }} {{ else }} {{ .Summary }} {{ end }}
反復
辞書データの場合、{{ range $idx, $var := .Site.Data.xxx }}
を通じてトラバースでき、配列の場合、{{ range $arr }}
のようにトラバースできます。上記のDataを例にとると、次の方法でデータをソート、フィルタリング、および取得できます。
// ここで、コンテキストは配列要素にアクセスします。グローバルコンテキストにアクセスするには、$.を使用してアクセスする必要があります {{ range $array }} {{ . }} {{ end }} // 変数と要素インデックスを宣言できます {{ range $val := $array }} {{ $val }} {{ end }} {{ range $idx, $val := $array }} {{ $idx }} -- {{ $val }} {{ end }} // マップ要素のインデックスと値を宣言する変数 {{ range $key, $val := $map }} {{ $key }} -- {{ $val }} {{ end }} // 渡されたパラメーターが空の場合、elseステートメントが実行されます {{ range $array }} {{ . }} {{else}} // $arrayが空の場合にのみ実行されます {{ end }}
さらに、次の方法も使用できます。
<ul> {{ range sort .Site.Data.books.fiction "title" }} <li>{{ .title }} ({{ .author }})</li> {{ end }} </ul> {{ range where .Site.Data.books.fiction "isbn" "978 - 0140443530" }} <li>{{ .title }} ({{ .author }})</li> {{ end }} {{ index .Site.Data.books "historical - fiction" }}
このようにして、さまざまな変数に従ってフィルタリングでき、.Site.Pages
などの組み込み変数も同様です。
次は、条件付きフィルタリングの処理です。
{{- if and (isset .Params "math") (eq .Params.math true) }} {{- end -}}
ページのフィルタリング
次は、現在のセクションのデータを最初にフィルタリングすることです。ページは、class: "page"オプションを設定する必要があります。最初に、年ごとに集計し、次に日付順に逆順にソートし、日付と対応するタイトル情報を表示します。
{{ $blogs := where (where .Site.Pages "Section" .Section) "Params.Class" "==" "page" -}} {{ range $blogs.GroupByDate "2006" "desc" }} <h1>{{ .Key }}</h1> <ul> {{ range .Pages.ByDate.Reverse }} <li><span>{{ .Date.Format "2006 - 01 - 02" }}</span> <a href="{{ .RelPermalink }}">{{ .Title }}</a></li> {{ end }} </ul> {{ end }}
ページで利用可能な変数は、Page Variablesを通じて表示でき、{{ . }}
を通じて直接印刷すると、ファイルパスが表示されます。
その他の一般的な使用例は次のとおりです。
{{ range .Data.Pages }} // Data.Pagesをトラバースします {{ range where .Data.Pages "Section" "blog" }} // Data.Pagesをトラバースし、Sectionがblogのデータをフィルタリングします {{ range first 10 .Data.Pages }} // Data.Pagesをトラバースし、最初の10個のデータを取得します {{ range last 10 .Data.Pages }} // Data.Pagesをトラバースし、最後の10個のデータを取得します {{ range after 10 .Data.Pages }} // Data.Pagesをトラバースし、10番目のデータの後のデータを取得します {{ range until 10 .Data.Pages }} // Data.Pagesをトラバースし、10番目のデータの前のデータを取得します
ショートコード
ShortCodesは、Markdownで表現するのが不便な一部の処理ロジックを処理するために主に使用され、元のhtmlコードの記述を省略します。公式では、一部のビデオウェブサイトへのリンク、relrefなど、いくつかのデフォルトの実装が提供されており、ソースコードはGithubで参照できます。
foobar ShortCodeがあるとすると、次の方法で使用できます。パラメーターのラップ方法に注意してください。現在、レンダリングを防ぐためのより良い方法は見つかっていません。
{{ foobar "foo" "bar" }} Some short codes {{ /foobar }}
ShortCodeテンプレートで上記のパラメーターを取得する方法はいくつかあります。with .Get 0
を通じて取得する方法が最も簡単で直接的です。または、index .Params 0
を通じて取得する方法もあります。内部のコンテンツは.Inner
メソッドを通じて読み取ることができ、呼び出す必要があります。さらに、Hugo ShortCodesのいくつかの例を参照することもできます。
一般的に使用される関数
テンプレートでは、{{ with .Site.Data.Resume . }} {{ .SomeData }} {{ end }}
のように参照できます。
高度な使用法
静的ファイル
画像、CSS、JavaScriptなどを含み、通常はサードパーティライブラリBootstrap、FontAwesomeなどの既存のファイルです。参照するときは、staticディレクトリに配置する必要があり、テンプレートでは、対応するディレクトリに配置する必要があります。自動的にコピーされます。
{{ "css/bootstrap.min.css" | absURL }}
のように参照できます。このとき、http://foobar.com/css/bootstrap.min.cssにアクセスすると、static/css/bootstrap.min.cssファイルにマッピングされます。
マウント
npmを通じてサードパーティJSパッケージを管理できますが、このとき、構成ファイルのmodule.mountsを通じて構成する必要があります。
[module] [[module.mounts]] source = "node_modules/katex/dist" target = "static/katex"
次に、テンプレートでは、次の方法で使用できます。
<script type="text/javascript" src="{{ "katex/katex.min.js" | absURL }}"></script>` <link rel="stylesheet" href="{{ "katex/katex.min.css" | absURL }}"/>
CSS
バージョン0.43以降、HugoはSASSのコンパイルをサポートしています。ただし、ソースファイルは/assets/scss/または/themes/<NAME>/assets/scss/ディレクトリにのみ配置できます。次に、次の方法で導入できます。
{{ $opts := dict "transpiler" "libsass" "targetPath" "css/style.css" }} {{ with resources.Get "sass/main.scss" | toCSS $opts | minify | fingerprint }} <link rel="stylesheet" href="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous"> {{ end }}
resources.Getを使用してSCSSファイルの内容を取得し、パイプラインを使用してコンパイル、圧縮、およびフィンガープリントを生成します。これにより、生成されたファイルにハッシュファイル名を追加できるため、キャッシュされた古いファイルではなく、最新のCSSをCDNからプルできます。さらに、CSSをコンパイルするための上記の構成オプションに加えて、以下も参照できます。
{{ $opts := (dict "outputStyle" "compressed" "enableSourceMap" true "includePaths" (slice "node_modules")) -}}
次のコマンドを使用してコードのハイライトを生成でき、例はStyle LongerおよびStyleで参照できます。
hugo gen chromastyles --style=monokai > syntax.css
構成パラメーターcodeFences=trueを設定する必要があることに注意してください。そうしないと、行番号などの情報がテーブルの形式で表示され、表示が異常になります。
JavaScript
CSSと同様に、JSスクリプトは次の方法で導入できます。
{{ $params := dict "api" "https://example.org/api" }} {{ with resources.Get "js/main.js" }} {{ if hugo.IsDevelopment }} {{ with . | js.Build }} <script src="{{ .RelPermalink }}"></script> {{ end }} {{ else }} {{ $opts := dict "minify" true "params" $params }} {{ with . | js.Build $opts | fingerprint }} <script src="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous"></script> {{ end }} {{ end }} {{ end }}
スクリプトでは、import * as params from '@params';
のようにパラメーターを参照でき、ReactコードでさえShimsメソッドを通じて導入できます。詳細については、Hugo JSを参照してください。
画像レンダリング
Hugoで画像形式をカスタマイズするのは少し面倒です。{: width="420"}
のような方法はサポートされていません。htmlを直接使用することをサポートしているため、次の方法で構成できます。
<img src="picture.png" alt="some message" width="50%" />
cssでimgの配置をカスタマイズできますが、このとき、1つの配置のみを使用できます。さらに、公式ではfigure
のショートコードが提供されており、使用できますが、さまざまなプラットフォームで互換性の問題が発生します。
別の方法があります。layouts/_default/_markup/render - image.html
で画像レンダリング方法をカスタマイズし、
のように使用しますが、サポートされるパラメーターは上記のファイルで構成する必要があります。
その他のレンダリングフックの詳細については、Render Hooksのコンテンツを参照してください。
データ転送
データはScratchを通じて転送できます。これは、PageとShortCodesの間でデータを転送するために使用されます。テンプレートで一時的なデータ転送を使用する場合は、newScratch
を通じて新しいものを作成できます。以下に、Hugoによって自動的に生成されたScratchを例にとります。.Page.Scratch
と.Scratch
は同じ機能を持っています。
{{ .Scratch.Set "hey" "Hello" }} # 3または(slice "Hello" "World")にすることもできます {{ .Scratch.Get "hey" }} # Get {{ .Scratch.Add "hey" "Welcome" }} # Go言語と同様の加算を行います。文字列の連結、数値の加算、配列の連結 {{ .Scratch.GetSortedMapValues "hey" }} # Getを通じてマップを取得するだけでなく、キーでソートされた値を返すこともできます {{ .Scratch.Delete "hey" }} # 削除 {{ .Scratch.SetInMap "hey" "Hello" "World" }} # マップを設定します。key:valueはHello:Worldに対応します {{ .Scratch.SetInMap "hey" "Hey" "Andy" }} {{ .Scratch.Get "hey" }} # map[Hello:World Hey:Andy]
テンプレートのデバッグ
{{ printf "%#v" .Permalink }}
を通じて現在の変数情報を印刷できます。さらに、ページレイアウトをデバッグする場合は、表示しやすいように`
undefined
関連のある記事
Hugoは、デフォルトで関連のある記事のRelated Content構成を提供しており、キーワード、日付、タグを通じて関連性をデフォルトで一致させます。
ベストプラクティス
トップレベルディレクトリには、archetypes
、assets
、content
、data
、i18n
、static
、layouts
のいくつかのディレクトリが含まれています。
- archetypes/:
new
サブコマンドを通じて新しい記事を作成するときのテンプレート。 - config/: デフォルトでは、
hugo.[toml|yaml|json]
が構成として使用され、次のディレクトリ方法を使用できます。- _default/: デフォルト構成。
- production/: グローバル構成。
- i18n/: ローカリゼーション。
- themes/:
- halo/: 対応するテンプレート名。
- assets/: テンプレート内のリソースファイル。
- images/
- js/: JavaScript関連のスクリプト。詳細については、
footer/script - footer.html
を参照してください。 - scss/: SCSSファイル。
- app.scss: 最上位のSCSSファイル。他のディレクトリのファイルが含まれます。詳細については、
head/head.html
を参照してください。
- app.scss: 最上位のSCSSファイル。他のディレクトリのファイルが含まれます。詳細については、
- static/: 静的ファイル。
- syntax.css: 上記の
hugo gen chromastyles
コマンドによって生成されたCSSファイル。詳細については、head/head.html
を参照してください。
- syntax.css: 上記の
- layouts/: レイアウトテンプレート。
_default
はデフォルトであり、partials
は異なるテンプレートの参照です。- _default/
- blog/: blogセクションテンプレートに対応。
- docs/: docsセクションテンプレートに対応。
- partials/: 異なるテンプレートでの参照。
- resume/: resumeセクションテンプレートに対応。
- baseof.html: レンダリングのルートページ。
- shortcodes/: ショートコード。
- slide/: slideセクションテンプレートに対応。
- 404.html: 404ページを生成します。
- assets/: テンプレート内のリソースファイル。
- halo/: 対応するテンプレート名。
CSS関連のコンテンツは、次のファイルに依存しています。
- syntax.css: 構文の強調表示、圧縮するだけで済みます。
- main.css: コアカスタム構成。bootstrap変数を使用するために、scssテンプレートでも参照されます。したがって、
bootstrap.min.css
を別途導入する必要はありません。 - fontawesome.min.css: 使用されるアイコン。Iconsのコンテンツを参照できます。
さらに、Bootstrapの関連変数はbootstrap/scss/_variables.scss
に保存されています。
タグ
デフォルトのtags
およびkeywords
構成を通じて記事を関連付けます。中でも、keywords
は記事のタイプに応じて追加され、tags
には次の一般的な分類が含まれます。
- topic: 特別なトピックの記事。いくつか
- language: プログラミングに関連し、
c/cpp
、lua
、bash
、rust
、java
、python
、golang
、web
、css
、html
などの特定の言語に細分化されます。 - database: データベースに関連し、
mysql
などに細分化されます。 - linux: オペレーティングシステムに関連し、
kvm
、network
、command
、vim
、ebpf
などに細分化されます。 - security: セキュリティに関連し、
ssh
、tls/ssl
などに細分化されます。 - container: コンテナの関連コンテンツ。
docker
、k8s
などに細分化されます。 - warehouse: ビッグデータの関連コンテンツ。
hudi
などに細分化されます。 - devops: 運用と開発に関連するツール。
git
などに細分化されます。 - algorithm structure: アルゴリズムデータ構造に関連します。
- example: 関連するサンプルコード、一般的に使用されるコマンドラインを整理するための
cheatsheet
、およびsoftware
はコマンドラインよりも体系的です。
各記事のタイトルの下にあるタグリストは、ジャンプに使用できます。このとき、テンプレートのlayouts/_default/list.html
を通じてレンダリングされます。
Leapcell: ウェブホスティング、非同期タスク、および Redis 用の次世代サーバーレスプラットフォーム
最後に、Golang のデプロイに最適なプラットフォームを推奨します: Leapcell
1. 多言語サポート
- JavaScript、Python、Go、または Rust で開発。
2. 無制限のプロジェクトを無料でデプロイ
- 使用量に対してのみ支払い - リクエスト、チャージは発生しません。
3. 無敵のコスト効率
- アイドルチャージなしの従量課金制。
- 例: $25 で、平均応答時間 60 ミリ秒で 694 万件のリクエストをサポート。
4. 合理化された開発者体験
- 簡単なセットアップのための直感的な UI。
- 完全に自動化された CI/CD パイプラインと GitOps 統合。
- 実用的な洞察のためのリアルタイムのメトリクスとロギング。
5. 簡単なスケーラビリティと高パフォーマンス
- 簡単な高並行性を処理するための自動スケーリング。
- 運用上のオーバーヘッドゼロ - 構築に集中するだけです。
Leapcell Twitter: https://x.com/LeapcellHQ