| @ブログ

インデックスページをいじって各カテゴリーの最新記事 4 件を配置するようにしてみた。最近の個人サイト復興ブームでみなさんインデックスページを工夫されているのを見ていて真似したくなってやった。

カテゴリーごとに最新記事 4 件を表示

昔ながらのブログだとインデックスページというのは最新の記事 10 件くらいが表示されていて、「次へ」を押すと古い記事が出てくるという構成になっている。以前のこのブログもそうだった。

しかし自分自身が他人のブログで「次へ」を押して次々に記事を読んでいくということをやった記憶がほとんどない。自分のブログでだって何か目的があって特定のキーワードで検索したあとに引っかかった記事を読むという感じなので、時系列に本文とセットで記事が 10 件ずつ表示される UI というのは意味をなしていないと思った。そもそもインデックスという名称なのに最近の記事数件しか表示していないのはおかしい。インデックスというからにはすべての記事の目次になるべきだ。

このブログはカテゴリーがあるので、サイトマップを作るとするとこんな感じになると思う。

+----------+        +------------+        +-----------+
|          |        |            |        |           |
|   Blog   +---+--->+  Category  +------->+   Entry   |
|          |   |    |            |        |           |
+----------+   |    +------------+        +-----------+
               |
               |
               |    +------------+        +-----------+
               |    |            |        |           |
               +--->+  Category  +---+--->+   Entry   |
                    |            |   |    |           |
                    +------------+   |    +-----------+
                                     |
                                     |
                                     |    +-----------+
                                     |    |           |
                                     +--->+   Entry   |
                                     |    |           |
                                     |    +-----------+
                                     |
                                     |
                                     |    +-----------+
                                     |    |           |
                                     +--->+   Entry   |
                                          |           |
                                          +-----------+

第一階層がインデックスページで、第二階層がカテゴリートップ、そして各記事がある。なのでインデックスページは二階層目の一覧ページになっているのが望ましいはずだ。しかし伝統的なブログはカテゴリーという記事をまとめる概念がありつつも、インデックスページから各記事ページへ直接遷移するのが主な導線だった。常に最新の記事が時系列順に並んでいるだけでは味気ないし、常連の読者ではないコンテキストを知らない訪問者には不親切だろう。

しかも SNS の隆盛で個人のブログのインデックスページが参照される機会というのはとんとなくなってしまった。個人が書いたブログ記事は SNS 経由で読まれ、個別記事だけが読まれる。インデックスページやトップページが読まれることはほとんどない。 SNS でシェアされている URL をクリックして個別記事を読んで、それ以上そのブログのほかの記事を読むことなく離脱してしまう。前後のコンテキストは無視して、一つのコンテンツだけがつまみ食いされてしまう。そんな流れにあらがいたいと思った。

これまで関連記事を記事下に表示するなどやってきたが、気に入っていくつか記事を読んで「ホーム」( = インデックスページ)を訪れたユーザーがもう少しブログを深掘りしてみたくなるようにインデックスページを各カテゴリーの最新記事一覧とするようにしてみた。このブログは現在カテゴリーが 13 個あるので、それぞれから 4 件ずつ記事を取得すると 52 記事になる。全カテゴリーからまんべんなく 4 記事ずつ取得して表示するのは簡単なようで結構難しい。普通の SQL ではできない。 OR マッパーではまず無理だろう。

いろいろ調べてみた結果、 MySQL では GROUP_CONCAT というのが使えそうだった。以下のような SQL を書いた。

select entries.id
from entries
inner join (
  select
    category_id,
    GROUP_CONCAT(id order by created_at desc) as entry_ids,
    max(created_at) as last_created_at
  from entries
  where entries.draft = false
  group by category_id
) as grouped_entries
on grouped_entries.category_id = entries.category_id
and FIND_IN_SET(id, entry_ids) between 1 and 4
order by last_created_at desc, entries.id desc;

GROUP_CONCATFIND_IN_SET という関数を組み合わせることで、各カテゴリーから作成日の降順に記事を 4 件ずつ取得できた。このクエリでは記事 id のみ取得して、もう一回 DB に記事を取得するクエリを ActiveRecord で投げる。 ActiveRecord でクエリを組み立てるときは N+1 が起こらないように関連テーブルを JOIN する。

query = <<~SQL
  select entries.id
  from entries
  inner join (
    select
      category_id,
      group_concat(id order by created_at desc) as entry_ids,
      max(created_at) as last_created_at
    from entries
    where entries.draft = false
    group by category_id
  ) as grouped_entries
    on grouped_entries.category_id = entries.category_id
    and find_in_set(id, entry_ids) between 1 and 4
  inner join categories on categories.id = entries.category_id
  order by last_created_at desc, entries.id desc;
SQL
entry_ids = ActiveRecord::Base.connection.select_all(query).rows.flatten
entries = Entry.includes(:category, :user, :tags, :comments).where(id: entry_ids)

PostgreSQL のときに最初に取得した entry_ids の並び順通りに結果が受け取れるかは怪しいが、 MySQL の場合は一回目のクエリで取得した id 順に各レコードが ActiveRecord のクエリ結果として取得できた。あとはこれをカテゴリーごとにグルーピングして View でよろしくやれば良い。

なお各カテゴリーはカテゴリー内の最新の記事の作成日で降順ソートするようにしている。例えば現在のインデックスページの最下部には音楽カテゴリーがあるが、これは音楽についての記事を最後に書いたのが一年以上前だからだ。もしいま音楽の記事を書けば音楽カテゴリーがトップに浮上するようになっている。

見た目に関しては各カテゴリーの記事最新一件は大きなサイズで表示している。最も最近書かれた記事なのでより多く人の目に付いた方がよいだろうという考えだ。またすべての記事にサムネイルというか、アイキャッチ画像を表示するようにした。画像がない記事に関してはデフォルトのサイトアイコン画像を表示するようにしている。やっぱり視覚的に情報を捉えられるのは重要だ。画像がごちゃごちゃ表示されるのを嫌う人もいるかもしれないが、テキストだけでは人間の認知というのはどうしても追いつかない。

あわせて今回、インデックスページの冒頭部にこのサイトについての説明文を載せることにした。ながしまきょうさんr7kamura さんがやっているののパクりだ。伝統的なブログのインデックスページは初めて訪れた人のことを無視しすぎていたと思う。そのブログ自体について説明するページがあるブログは少ない。最初の記事でブログを始めた経緯みたいなことが書かれたきり、そのブログは何なのか、誰が書いているのかが書かれることは希だ。きちんとブログについての説明ページと著者についての説明ページがあっても、左右のサイドバーやトップのナビゲーションの端っこに押し込まれて見られることはない。これではいけないだろう。というわけでインデックスページの一番目立つ位置にブログと自分自身の簡単な紹介を入れた。

インデックスページトップに紹介文を表示

ブログはなぜ衰退したかを考えてみると、 Facebook や Twitter の隆盛はあるにせよ、ブログ自体に初めて訪れた人に読まれるための工夫が欠けていたのだと思う。誰も自分のブログの継続率を計測したりコホート分析したりはしない。読者が前後の記事を読んでいることを前提に書かれた記事やサイト構成では初めて訪れた人はどうやっても離脱してしまう。特に書き手が芸能人でもない一般人の場合はなおさらだ。誰も RSS フィードを購読していないし、ほとんどの読者は初めて訪れる人なのだから、そういう人たちが読んでブログのテーマや著者の人となりが分かる構成にしていかなければならないのだと思う。でないと SNS でたまにバズったときだけ読んでもらえる、ソーシャルメディアの肥やしにしかならない。

この新しいインデックスページが正解なのかどうかは分からないが、ブログ衰退の流れにあらがっていきたい。

| @ブログ

blog.8-p.info の過去記事ページの真似をして、 Archive ページにタグを表示するようにしてみた。

Archive ページにタグを表示

タグはあまり使っていなかったのだけど、一覧で記事タイトルだけ並んだときその記事にどんな内容が書いてあるのかを把握するためにはタグが便利だなと思い直し、タグを表示させてみることにした。いくつか過去のタグが付いていない記事にタグを振ってもみた。

このブログは技術情報からポエム、日々の日記まで何でもありのごった煮ブログなので、カテゴリーによる情報分類には限界がある。現在 13 個のカテゴリーがあるが、記事数にバラツキがあり、情報分類としてあまり機能していない。カテゴリーの粒度をもっと荒くして緩い分類に変更し、そこから先はタグによって超細かくラベリングすると情報の分類としてはまともになるのではないかと思った。

いま、カテゴリーの内訳がこんな感じ。

- "雑談":303
- "技術/プログラミング":272
- "映画/ドラマ/テレビ":150
- "Mac/iPhone":134
- "WWW":113
- "散財":95
- "旅行/ハイキング":70
- "ブログ":69
- "音楽":63
- "読書":34
- "写真":32
- "料理/食事":31
- "労働":27

もっと緩い分類にして以下みたいな感じにするとよさそう。

- 雑記
- パソコン・インターネット
- 見た・読んだ・聞いた
- 出かけた・撮った・食べた

カテゴリーとタグの使い分けは 10 年以上前から悩んでいる気がする。

情報分類の手法でありつつコンテンツの内容そのものを指し示すものでもあるからだろう。インスタグラムで #ラーメン #からの #うどん とかやってる投稿を見るととても嫌な気持ちになるのだけど、そういうことがされるくらいにタグというものは不安定なもので、正しく使おうとか気負わず、もっと緩く使えばいいのかもしれない。

もう廃れてしまったが、フォークソノミーが勢いを取り戻して、情報の発信者ではなく受け取り側がコンテンツにタグ付けできるような世の中になるとおもしろいのかもしれない。

| @労働

瑞梅寺川の桜

リモートワークが長くなってきた。正直どのくらい自宅で仕事しているか定かではない。ほとんど外出しないので時間の感覚がおかしくなってきている。職場がリモートワークを許可するようになってもしばらくは出社していたが、二週間ほどで自分の具合が悪くなり会社に行くのを控えるようになった。その後本格的に具合が悪くなって家庭内隔離状態になり、同時に会社もリモートワークを推奨から出社禁止という状態に変わった。約 3 年ぶりのリモートワークで色々思うことがあったので雑に書きます。

リモート会議

リモートのミーティングでは前職の頃から Zoom をよく使っていた。いまの職場でも Zoom を多用している。リモートミーティングの Tips みたいなのは最近どこかで流れてきたのを読んだ気がするけど自分も思うところがあるのでまとめておく。

まず第一にはミュートだ。話さない人はとにかくミュート。 Zoom は音が鳴っているマイクの音を拾って音声ストリームに載せる。そして音声ストリームでは主として話している(音を出している)人の音声が採用されて、その他の人の音声はカットされてしまうようだ。図にするとこんなイメージ。

オフラインの会話

Zoom の会話 1

Zoom の会話 2

Zoom の会話 3

イヤフォン・ヘッドフォンを使う

自宅だとイヤフォンを付けずにスピーカーから音を出しがちだ。 Zoom のミーティングにもイヤフォンを付けずに参加してしまうかもしれないが、ハウリングしてしまったりするのでなるべくイヤフォンを付けてパソコンのスピーカーからは音を出さずに参加したい。

またイヤフォンを付けずに参加するとパソコンのマイクを使うことになって、会話と会話の切れ目が聞き取りにくくなる。 Zoom はハウリングが起こらないよう、ストリームで流している音と同じ音がパソコンのスピーカーから出ていたらそれを拾わないようにしているはずで、パソコンのマイクで話し始めるときに切り替え処理が必要になり、会話の始まりのあたりが拾われなかったりする。マイク付きのイヤフォンを使うことでこの問題は回避できる。

ちなみに以前も書いているけど Apple の iPhone 用イヤフォンはよくできていると思う。 2800 円くらいなのにマイクの音が良好で、相手の声もよく聞こえる。 10 倍以上の値段がする AirPods Pro を使っている人の声より、 2800 円の iPhone 用イヤフォンを使っている人の声の方が聞き取りやすい。 Lightning 端子ではなく 3.5mm ジャックのやつじゃないとパソコンにつなげないので注意。

やっとるわ感

先週、 sudoken さん( Kaizen Platform の CEO )が三戸正和さんという人と対談してる YouTube Live を見た。ほとんど三戸さんという方の話で sudoken さんの話はあまり聞けなかったが、「リモートワークではプロセスでの評価ができないので、アウトプットを意識的に行い、自分の成果をアピールすることまでが仕事のうち」という話が面白かった。リモートワークでは結果による評価に移行せざるを得ないということだ。

エンジニア業を止めてからつくづく感じるのが、人はしつこいほど言わないとわかってくれないということだ。伝えたいことは社内ブログに書いていたらみんな読んでくれるわけではなく、目立つような場所に掲示したり、目立つ方法で喧伝したり、何度もしつこく見せたりしないとなかなか見聞きしてもらえない。リモートワークをやっていて自分はそこそこ頑張って仕事してるつもりでも、その成果をちょっと過剰なんじゃないのと思えるくらいにアピールしないと、組織や同僚からは仕事してる風には受け取ってもらえないだろうな、と思った。リモートワークではやっとるわ感を出すところまでが仕事の範疇に入ることになる。

増えた自由に使える時間を有効に

リモートワークに移行して、毎日通勤にかかっていた 2 時間を自由に使えるようになった。前職時代は朝仕事を始めるギリギリまで寝ていたけど、いまは 7 時くらいに起きて仕事を始める 9 時半くらいまでブログを書いたり散歩したり食器を洗ったりしてる。最近、ブログの記事が増えているのはそういうわけだったりする。

コロナウイルスのせいで恐らく世界大恐慌に陥るだろうと思う。 1 ヶ月も 2 ヶ月も世界中の人が外出しなかったら経済回らなくなる。この時期にゲームしたり Netflix 見たりするだけでなく、何かしら一つ生産的なことをして自分に投資しておかないと数ヶ月後に泣きを見ることになるだろうなと思う。いまはインターネットの会社はコロナウイルスの影響をあまり受けていないか、あるいは Zoom や Slack 、そのほか EC をやっているところは逆に業績好調かもしれない。ただ、世の中が全体的に不景気になると必ずその余波が及ぶわけで、そのときをどう迎えるかはソフトウェア企業に勤める人々にも重要なテーマとなるだろう。

| @ブログ

75 件ほどあった tech.portalshit.net の記事を取り込んだ。実家に住んでいた 10 年前に始めた技術ブログで、最初は Rails 製の Mephisto 、その次に Jekyll で構築した。まだ GitHub Pages の仕組みが存在する前で、自前で用意したさくら VPS に git push すると自動でビルドして記事が公開されるような仕組みを作ったりしてた。

職業プログラマーになろうとしてもがいてた頃にやってたブログで、いま読み返すと「頑張ってたんだな」感があっていなたい記事が多い。

だいぶ放置していて、いまは S3 で静的サイトとして公開していたのでそのまま放置でもよかったが、 10 年前と違って何でも一カ所にまとめて書いておきたいという気持ちが強くなって取り込むことにした。ブログはトピックを混ぜずに一つのトピックにフォーカスした方がよいと 10 年前は考えていたのだけど、最近の世の中のブログ記事の読まれ方は変わってきていて、一人の人のブログをフィードリーダーに登録して読むというより、 SNS をだら見していて流れてきた記事を適当に消費するというスタイルに変わってきているので、一つのブログに一つのテーマという書き分けは不要になったと感じる。

tech.portalshit.net を取り込んだおかげで Archive ページのグラフに占める技術記事の割合が増えた。

Tech category Bar extension

ちなみに取り込みは以下のようなコードを書いて SQL の INSERT 文に変換した。

require 'yaml'
require 'pathname'

files = Dir.glob(File.join(__dir__, '_posts', '*.markdown'))
files.each do |file|
  content = File.read(file)
  _, header, body = content.split('---')
  header_yml = YAML.load(header)
  title = header_yml['title']
  tags = header_yml['category']
  tags = tags.is_a?(Array) ? tags.map(&:downcase) : [tags&.downcase]
  slug = Pathname.new(file).basename.to_s.sub(/\d{4}\-\d{2}\-\d{2}\-(.+?)\.markdown/, '\1').gsub('_', '-')
  body = body.strip.gsub(/\n/, "\\n").gsub('\'', '\'\'')
  created_at = File.birthtime(file).to_s.sub(' +0900', '')
  updated_at = File.mtime(file).to_s.sub(' +0900', '')
  puts <<~EOS
    INSERT INTO entries(title, user_id, category_id, slug, markup, type, draft, body, frozen_tag_list, created_at, updated_at) VALUES('#{title}', 1, 6, '#{slug}', 'redcarpet', 'Post', 0, '#{body}', '#{tags.join(',')}', '#{created_at}', '#{updated_at}');
  EOS
end

| @技術/プログラミング

Chart

Rechars という React のチャートライブラリを利用して、 Archive ページにカテゴリーごとに記事を集計してグラフ化する機能を作った。

グラフの Bar にカーソルを載せると Tooltip が表示されて、具体的な件数がわかる。

Chart Tooltip

カテゴリごとに表示・非表示を切り替えることも可能。グラフ下のカテゴリー名( Legend )をクリックして切り替えられる。

Chart Show-Hide Toggle

ただし残念なことに Bar を非表示にしたときに Legend の表示を変化させるのが難しくてできていない。

仕事で使ってる Looker とか Redash であれば Legend をクリックして表示・非表示を切り替えることができ、それに連動して Legend の色をトーンダウンさせたりする機能が付属しているが、利用した Recharts にはその機能がなかった。 Bar の表示・非表示切り替えも標準サポートされていなかったので、 GitHub の Issue の情報を頼りに無理矢理実装した。

コードはこんな感じ。結構汚い Hack で、 Bar の表示・非表示を、表示用のキー文字列に空白を追加するかしないかで切り替えている。

  // クリックされたアイテムが `this.state.disabled` 配列の中にすでに存在していれば除外し、
  // 存在してなければ追加する
  selectBar(event) {
    let dataKey = event.dataKey.trim()
    if (this.state.disabled.includes(dataKey)) {
      this.setState({ disabled: this.state.disabled.filter(item => item !== dataKey) })
    } else {
      this.setState({ disabled: this.state.disabled.concat([dataKey]) })
    }
  }

  render() {
    return (
      <ResponsiveContainer height={500}>
        <BarChart
          data={this.state.data}
          margin={{
            top: 20, right: 20, left: 0, bottom: 20,
          }}
          style={{ fontSize: '14px' }}
        >
          <CartesianGrid strokeDasharray="3 3" />
          <XAxis dataKey="year" />
          <YAxis />
          <Tooltip labelStyle={{ color: '#000', fontWeight: 'bold' }} itemStyle={{ margin: '0 2px 0 4px', padding: '0' }} />
          {// Legend クリック時のコールバックに `this.selectBar` を指定する }
          <Legend onClick={this.selectBar} />
          {/*
            `this.state.categories` 配列と `this.state.disabled` 配列の内容を比較し、
            `this.state.disabled` に追加済のカテゴリーは dataKey に空白を追加することで非表示に
          */}
          {this.state.categories.map((category, index) => {
            let dataKey = this.state.disabled.includes(category) ? category + " " : category
            let color = this.colors[index % this.colors.length]
            return(<Bar key={index} dataKey={dataKey} stackId="a" fill={color} />)
          })}
        </BarChart>
      </ResponsiveContainer>
    );
  }

| @技術/プログラミング

react-select-before-after.jpg

一個前の記事のキャプチャにあるように、従来テキストリンクだった年とカテゴリーの選択をセレクトボックスにした。 git log -S したところ去年( 2019 年)の 11 月頃に変更を行ったみたいだ。もうそろそろ 2020 年になろうとしていて、 2005 年からやっていて年の数が 16 個になろうとしていてさすがに多すぎると思ったので整理のためにセレクトボックス化してコンパクトにした。

利用したのは React Select というパッケージで、色々カスタマイズできるみたいだけど面倒だったので素のまま使ってる。

Archives ページのリファクタリング のときのように、子コンポーネントのイベントをトリガーに親コンポーネントの setState() を呼び出すような作りになっている。作るときは結構難儀したけどおかげでスマートフォンで見たときも年やカテゴリーだけでファーストビューが埋まるということがなくなった。

react-select-smartphone-view.jpg

現在のコードをまるっと貼り付けるとこんな感じ↓。

子コンポーネント(年のセレクトボックス)

import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import Select from 'react-select'
import PropTypes from 'prop-types'
import { withRouter } from 'react-router-dom'

import history from './history'

class YearSelect extends Component {
  constructor(props) {
    super(props)
    this.state = {
      data: [],
      selectedOption: null
    }
    this.handleChange = this.handleChange.bind(this)
  }

  static propTypes = {
    match: PropTypes.object.isRequired,
    location: PropTypes.object.isRequired,
    history: PropTypes.object.isRequired
  }

  async loadYearSelectFromServer() {
    const request = await fetch('/archives/years.json')
    const response = await request.json()
    this.setState({ data: response })
  }

  componentDidMount() {
    this.loadYearSelectFromServer()
  }

  handleChange(selectedOption) {
    const year = selectedOption ? selectedOption.value : null
    if (year) {
      this.props.history.push(`/archives/${year}`)
    } else {
      this.props.history.push("/archives")
    }
    this.setState(
      { selectedOption },
      () => { this.props.update(year) }
    )
  }

  render() {
    const options = this.state.data.map(year => {
      return { value: year, label: year }
    })
    return (
      <div className="year-list">
        <Select
          value={this.state.selectedOption}
          onChange={this.handleChange}
          options={options}
          placeholder="Year"
          isClearable
        />
      </div>
    )
  }
}

const YearList = withRouter(YearSelect)

export default YearList

親コンポーネント( App.js )

import React, { Component } from 'react'
import { BrowserRouter as Router, Switch, Route, Redirect } from 'react-router-dom'

import YearList from './YearList'
import CategoryList from './CategoryList'
import Archives from './Archives'

class App extends Component {
  constructor(props) {
    super(props)
    this.state = {
      category: null,
      year: null,
      length: 0
    }
    this.updateCategory = this.updateCategory.bind(this)
    this.updateYear = this.updateYear.bind(this)
    this.setLength = this.setLength.bind(this)
  }

  updateCategory(category) {
    this.setState({ category })
  }

  updateYear(year) {
    this.setState({ year })
  }

  setLength(length) {
    this.setState({ length })
  }

  render() {
    return(
      <Router history={history}>
        <div className="archive-filter">
          <YearList update={this.updateYear} />
          <CategoryList update={this.updateCategory} activeCategory={this.state.category} />
          <div className="entry-length"><p>{this.state.length} entries</p></div>
        </div>
        <Switch>
          <Route
            exact path="/archives"
            render={(props) =>
              <Archives
                category={this.state.category}
                setLength={this.setLength}
                {...props}
              />
            }
          />
          <Route
            path="/archives/:year(\d{4})"
            render={(props) =>
              <Archives
                category={this.state.category}
                setLength={this.setLength}
                year={this.state.year}
                {...props}
              />
            }
          />
        </Switch>
      </Router>
    )
  }
}

export default App

| @旅行/散歩

クヒオビーチ

マイルを貯めるのは低所得の自分には無理だという記事を書いた。

しかしこの記事を書いたあともセブンイレブンで 100 円のコーヒーを買うときもクレジットカードで払ったり、トイレットペーパーを LOHACO で買うときもポイントサイトを経由したり、通りすがりの自販機の釣り銭受けをまさぐって取り忘れの小銭をあさる中学生のようにちまちまマイルを貯め続け、なんとか家族三人そろって特典航空券(エコノミー)でホノルルまで行ける程度のマイルを貯めることができた。金持ちだと毎年ハワイにビジネスクラスで行けるくらいマイルが貯まるみたいだけど、自分の場合は 3 年かかってエコノミー(ローシーズン、 3 人分で 105,000 マイル)が限界だった。

なぜハワイなのか

2013 年に買った POPEYE がハワイ特集号で、「ハワイには若いうちに行こう」と書いてあった。自分はそのとき 32 歳で、コテコテのリゾート地に興味なかったのだけど POPEYE の特集を読んでみると「確かに良いな」と思えたので、なるべく年を取る前にハワイに行っておきたいなと思っていた。

ここ一年半くらい、身を粉にしながら働くものの大した成果を残すことができず、完全に人間として腐り始めていた。このままでは玄界灘で魚の餌になるくらいしか社会貢献の方法がないと思ったので、休みを取って旅行に行ってみることにした。

旅の準備

福岡から成田へ移動

特典航空券界の常識をわかってなくて、マイルが貯まったらすぐに特典航空券を申し込めるのかと思っていたけどそういうわけではなさそうだった。マイルが貯まったので「来月旅行にでも行くか」とほくほくしながら特典航空券を申し込もうと ANA のサイトを覗いたら全然席が空いてない。ハワイのような人気路線の特典航空券は申し込めるようになった瞬間全部埋まってしまうみたいだった。 ANA の国際線の特典航空券の予約は 355 日前から可能になり、マイレージクラブのランクが高い人(= @t32k さんなどの金持ち)ほど申込みが優遇されるシステムなので、ランクの低いマイレージクラブ会員(=貧民)が特典航空券を申し込もうとしたらほぼ一年後の日付にするしかない。手持ちの一番古いマイルの有効期限が 2019 年の 2 月だったので、この時期に本当に旅行に行けるかどうかもわからないまま 2019 年の 2 月末に 2020 年 2 月のチケットを取った。申し込んだとき、どこに行くかとか何をするかとかどこに泊まるかとか何も考えられていなかった。やっと旅の予定を決め始めたのは 2020 年の正月だった。その後コロナウイルスの話が出てきて一旦は旅行を取りやめようかとも思ったが、先延ばしにすると逆に騒動が広がりそうだと思って予定通り 2 月中旬に出発した。

成田で暇つぶし

ハワイに行ってどうだったか

よかった。

カジュアル

ヨーロッパを旅行するときは結構緊張したが(人種差別とかマナーに気をつけたりで気をつかう)、ハワイは気楽でとてもよかった。

アメリカ人に対する良くないイメージが払拭できたのもよかった。少なくともハワイに住んでたりハワイに旅行に来てるアメリカ人からは、ヨーロッパの安宿で見かけるような声がでかくて何でもアメリカ流を押し通そうとする感じの悪さがなくて、嫌な気持ちになることなく旅行できた。

快適な移動

レンタカーや Uber 、 Lyft で移動したので移動で荷物を抱えて大変な目に遭ったり、タクシーにぼったくられないか心配したりしなくて済んだのが良かった。海外で車を運転するのは初めてだったが、ハワイの道は広くてとても運転しやすかった。

スーパークレジットカード社会

何でもカードで払えるので両替する必要がない。現金はチップを払うときか空港の自販機で飲み物を買うときくらいしか使う機会がない。 ABC ストア(ワイキキに 50m 間隔である旅行者向けのコンビニ)で現金で金払ってるのは日本人旅行者くらいだった。

eSIM 便利

今回は自分の iPhone 11 も嫁さんの iPhone XR も eSIM に対応していたので、香港の 3 というキャリアの eSIM を利用してハワイでも日本にいるとき同様に携帯が使えたのが良かった。 5 年前にギリシャに行ったときはちまちま Pocket WiFi をオンにしてインターネットにつないでいたので不自由極まりなかった。

ホテルの取り方

宿は Hotels.com と HIS で取った。 Hotels.com が最安かというとそういうわけではなく、マリオットなど結構良いホテルのチェーンだと HIS の方が料金安い&朝食付きだったりしてお得だった。

旅程

旅程 宿泊
Day 1 家 🚕 福岡 🛩 成田 ✈️ ホノルル 🛩 ヒロ Hilo Hawaiian Hotel
Day 2 ヒロ 🚙 アカカの滝 🚙 MKVIS Hilo Hawaiian Hotel
Day 3 ヒロ 🚙 ワイピオ渓谷 🚙 コナ King Kamehameha's Kona Beach Hotel
Day 4 コナ 🛩 ホノルル Queen Kapiolani Hotel
Day 5 ホノルル 🌴 Queen Kapiolani Hotel
Day 6 ホノルル 🚙 ノースショア Queen Kapiolani Hotel
Day 7 ホノルル ✈️ 成田 🛩 福岡

ハワイ島とオアフ島に三泊ずつした。福岡空港から成田へ行き、成田で 5 時間時間を潰してホノルルへ。ホノルルからハワイアン航空に乗り換えてハワイ島のヒロへ。ヒロに二泊してコナへ移動し、コナで一泊してからコナ空港からまたハワイアン航空でホノルルへ。なおコナではホテルにパスポートを忘れて大騒ぎとなったが、ハワイアン航空の職員の人がめっちゃ親切でスーパー助かった。ギリシャでエーゲ航空に散々な目に合わせられたのとは大違いだった。ホノルルに三泊して成田経由で福岡に戻った。 6 泊 8 日の旅(一泊は飛行機の中)。

ハワイ島

ホテルの部屋から見るマウナケア

ハワイ島ではアカカの滝、マウナケア(中腹まで)、ワイピオ渓谷、コナブリュワリーの工場レストランなどを訪れた。主にレンタカーでドライブしていて、ハワイ島の北半分をぐるっと回った。

アカカの滝

マウナケア

ワイピオ渓谷

ハワイ島はハワイ諸島の中では一番新しい島でまだ火山の荒々しい感じが残ってる。マウナケアは 4200m もある高い山だが、裾野が広がっていて草原が広がり、景色が生まれ故郷の阿蘇に似ていて海外旅行に来てるのに帰省してるみたいな感じで不思議な感覚だった。

ハワイ島の形式

オアフ島

オアフ島ではワイキキに滞在しつつ、ビーチで遊んだり、街をうろついたりした。

ワイキキビーチ

オアフ島でも一日は車で遠出して、ノースショアまで行ってパタゴニアで環境に配慮しながら家族で爆買いしたりした。

ノースショアハレイワ

patagonia で爆買い

ダイアモンドヘッドが見える宿に泊まっていて、できれば登りに行ってみたいと思っていたけどうだうだ無為に時間を過ごしてしまってついぞ登ることができなかった。残念。

ホテルの部屋から見るダイアモンドヘッド

ワイキキは福岡でいうと西通りといった趣でひたすらショッピングするのが好きな人にはよさそうだが少々退屈だった。ライドシェアの Lyft で、カカアコという昔は治安が悪かったけど最近は小洒落た店が増えているエリアに行ってオシャンティなレストランでラム肉を食べたりした。

ラムチョップ

POPEYE で紹介されていた Ray's Cafe という店が安くて巨大なリブアイやロブスターが食べられるようだったが、治安の悪いエリアにある地元民向けのレストランのようで閉店時間が早く、今回は行くことができなかった。

旅行中のツイート


正直、ハワイは一回行けば充分だろうと思っていたが、旅行から帰ってきてからは「またハワイに行きたい」という気持ちが日々募っていく一方だ。酒のやまやに行ってコナビールやフラ印のポテトチップスを買ったり、中公新書のハワイの歴史についての本を読んだり、Amazon Prime ビデオで HAWAII FIVE-0 というドラマを見たりしている。

HAWAII FIVE-0 ではよく主人公が山登りに行っている。ハワイといえば海というイメージあるけど、ダイヤモンドヘッド以外でも登り甲斐のありそうな山がたくさんある。今度は山に登ったり、地元民向けの治安が悪いエリアの店にも乱入してみたりしたい。

ワイキキの夕景

それにつけても金の欲しさよ。