Featured image of post 第4回-短くするには? — URL短縮サポーターを作ってみよう

第4回-短くするには? — URL短縮サポーターを作ってみよう

長いURLを短いコードに変換する方法を学びます。Digest::SHAを使ったハッシュ生成で、URLから一意の短縮コードを作成しましょう。

@nqounetです。

連載「URL短縮サポーターを作ってみよう」の第4回です。

前回の振り返り

第3回では、フォームから送信されたURLをサーバー側で受け取る処理を実装しました。

前回学んだ内容を簡単に振り返ります。

  • post '/shorten' => sub ($c) { ... };でPOSTルーティングを定義した
  • $c->param('url')でフォームから送信されたパラメータを取得した
  • 受け取ったURLを画面に表示して動作確認した

今回は、受け取ったURLから「短縮コード」を生成する仕組みを学びます。

今回のゴール

第4回では、以下を達成することを目標とします。

  • ハッシュ関数の仕組みを理解する
  • Digest::SHAを使ってURLから短縮コードを生成する
  • メモリ上で仮実装し、動作確認する

どうやって短いコードを作る?

タカシさんの疑問

前回、タカシさんが入力したURLを受け取れるようになりました。タカシさんは画面を見ながらこう尋ねます。

「受け取ったURLを、どうやって短いコードに変換するの?」

良い質問です。長いURLから短い識別子を作るには、いくつかの方法が考えられます。

  • 連番(1, 2, 3…)を発行する
  • ランダムな文字列を生成する
  • URLをハッシュ化する

今回は、3番目の「ハッシュ化」を採用します。

ハッシュ関数とは

ハッシュ関数とは、任意の長さのデータから固定長の文字列(ハッシュ値)を生成する関数です。以下の特徴があります。

  • 同じ入力からは常に同じハッシュ値が生成される
  • 異なる入力からは異なるハッシュ値が生成される(衝突の可能性は非常に低い)
  • ハッシュ値から元のデータを復元することはできない

ハッシュ関数を使えば、どんなに長いURLでも短い固定長の文字列に変換できます。また、同じURLを2回登録しようとしたときに、同じ短縮コードが生成されるため、重複チェックにも活用できます。

なぜDigest::SHAを使うのか

PerlでハッシュといえばMD5を思い浮かべる方もいるかもしれません。しかし、MD5はセキュリティ上の理由から現在は推奨されていません。

今回使うDigest::SHAは、SHA(Secure Hash Algorithm)というハッシュ関数を提供するモジュールです。SHA-1はMD5より安全性が高く、URLの識別子を生成する用途には十分です。

Digest::SHAはPerl 5.10以降のコアモジュールなので、追加インストール不要で使えます。

Digest::SHAを使ってみよう

モジュールを読み込む

まず、Digest::SHAモジュールを使う準備をします。以下のようにuseで読み込みます。

1
2
3
4
#!/usr/bin/env perl
# Perl: 5.10以上(Digest::SHAがコアモジュール)
# 依存: なし(コアモジュール)
use Digest::SHA qw(sha1_hex);

sha1_hexは、入力データのSHA-1ハッシュ値を16進数の文字列として返す関数です。qwで明示的にインポートしています。

短縮コードを生成する

sha1_hexを使うと、任意の文字列から40文字の16進数ハッシュ値が得られます。ただ、40文字は短縮URLとしては長すぎるので、先頭の6文字だけを使います。

1
2
3
# Perl: 5.10以上
# 依存: なし(Digest::SHAはコアモジュール)
my $code = substr(sha1_hex($url), 0, 6);

substrは文字列の一部を切り出す組み込み関数です。この例では、sha1_hexで生成した40文字のハッシュ値から先頭6文字を取り出しています。

6文字の16進数は約1677万通り(16^6)の組み合わせがあるため、一般的な個人利用では衝突の心配はほとんどありません。

アプリに組み込んでみよう

コードを追加する

前回作成したapp.plを以下のように修正します。Digest::SHAを読み込み、POSTハンドラで短縮コードを生成するようにしました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#!/usr/bin/env perl
# app.pl
# Perl: 5.20以上(サブルーチンシグネチャ使用)
# 依存: Mojolicious, Digest::SHA(コアモジュール)
use Mojolicious::Lite -signatures;
use Digest::SHA qw(sha1_hex);

# 仮のURL保存用ハッシュ(メモリ上のみ)
my %urls;

get '/' => 'index';

post '/shorten' => sub ($c) {
    my $url = $c->param('url');

    # 短縮コードを生成
    my $code = substr(sha1_hex($url), 0, 6);

    # メモリ上に保存(仮実装)
    $urls{$code} = $url;

    $c->render(text => "短縮コード: $code\n元のURL: $url");
};

app->start;

__DATA__

@@ index.html.ep
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>URL短縮サポーター</title>
</head>
<body>
    <h1>URL短縮サポーター</h1>
    <p>短縮したいURLを入力してください</p>
    <form method="post" action="/shorten">
        <input type="url" name="url" placeholder="https://example.com/very-long-url..." required>
        <button type="submit">短縮</button>
    </form>
</body>
</html>

コードの各部分を解説します。

Digest::SHAの読み込み

1
use Digest::SHA qw(sha1_hex);

sha1_hex関数を使うために、Digest::SHAモジュールを読み込みます。

仮のURL保存用ハッシュ

1
my %urls;

今回は動作確認のため、Perlのハッシュ変数%urlsを使ってメモリ上にURLを保存します。これはサーバーを再起動すると消えてしまうため、次回以降でデータベースに置き換えます。

短縮コードの生成と保存

1
2
my $code = substr(sha1_hex($url), 0, 6);
$urls{$code} = $url;

sha1_hexでハッシュ値を生成し、先頭6文字を短縮コードとして使用します。そのコードをキーにして、元のURLをハッシュ変数に保存しています。

動作確認

morboで起動する

ファイルを保存したら、morboで起動しているサーバーが自動的にリロードされます。もしサーバーを停止していた場合は、再度以下のコマンドを実行してください。

1
morbo app.pl

ブラウザで確認する

  1. http://localhost:3000にアクセスします
  2. テキストボックスにURL(例: https://example.com/very-long-url?param=value)を入力します
  3. 「短縮!」ボタンをクリックします

画面に「短縮コード: a1b2c3」のような6文字のコードと元のURLが表示されれば成功です。

試しに同じURLを再度入力してみてください。ハッシュ関数の特性により、同じURLからは常に同じ短縮コードが生成されることを確認できます。

現状の制限

今回の仮実装には以下の制限があります。

  • サーバーを再起動するとデータが消える
  • 短縮URLにアクセスしてもリダイレクトされない

これらの課題は、次回以降でデータベースを導入し、動的ルーティングを実装することで解決していきます。

まとめ

今回学んだこと

第4回では、以下のことを学びました。

  • ハッシュ関数の仕組みと、URLから一意のコードを生成する方法
  • use Digest::SHA qw(sha1_hex);でSHA-1ハッシュ関数を使う方法
  • substr(sha1_hex($url), 0, 6)で先頭6文字の短縮コードを生成する方法

次回予告

次回は「忘れないように保存しよう — データベースの準備」をテーマに、SQLiteとDBIを使ってデータを永続化する方法を学びます。サーバーを再起動してもデータが消えない、本格的なURL短縮サービスへの第一歩です。お楽しみに。

comments powered by Disqus
Hugo で構築されています。
テーマ StackJimmy によって設計されています。