文系未経験がベンチャー就職後のリアル

これは「Happiness Chain Advent Calendar 2024」の15日目の記事です。

はじめに

今年も残りわずかとなりました。この時期になると、自然と1年を振り返り、どれだけ成長できたかを考える時間が増えますね。

この記事では、文系未経験からIT業界に飛び込んだ私が、受託兼SESのベンチャー企業に入社して1年半を過ごしてきたリアルを振り返ります。

これからIT業界を目指す方や同じような道を歩んでいる方々の参考になれば嬉しいです。

文系未経験からSEへ

入社当時の自分は、Webアプリ開発の経験はゼロ。もちろんチーム開発の経験等もありません。

フレームワークやいろんな技術を駆使してアプリを作り上げる力は、全くと言っていいほどありません。

GitもDockerも聞いたことあるな〜というレベルです。

趣味程度に競技プログラミング(競プロ)問題に熱中しつつJavaの基本文法やアルゴリズムには一定の自信がついた程度でした。

漠然と「とりあえず言語自体すらすら書けるようになっとけばなんとかなるだろう」と本気で思っていました。

初めてのプロジェクト(入社1ヶ月目)

入社後、新規開発案件に配属されることに。

技術スタックは TypeScript,Vue, Nuxt, Spring, Docker, Vuetify など。

当時の自分の技術力はほぼゼロ。

Javaをゼロから書くスキルはある程度身についていましたが、フロントエンド担当になったため、こちらの案件ではほとんど出番はなかったと言えます。

驚いたのは、競プロのようなアルゴリズムや高速化の工夫が実務ではほとんど登場せず、「if文とfor文で大体の処理が実現できる」ことでした。

当初、コーディングに対してとても難しい作業であるという先入観を持っていたためかなり驚きました。

Webアプリを作るには、コーディングスキル以上に「複数の技術を組み合わせ、ゼロから形にする力」が必要だということを学んだ瞬間でした

特に業務フロー理解からコードに書き起こす過程は個人学習では到底経験できていなかったため、とても難しく感じていました。

苦難の日々が続く(入社2〜4ヶ月目)

プロジェクトのチームは3人で、そのうち1人はマネージャーだったため、実質2人体制で開発を進める形でした。

私はフロントエンドを担当しましたが、それまでJavaしか触ったことがない状態で TypeScript や Vue やNuxt を扱うことになり完全に混乱しました。

脳が完全にJS系のソースを拒絶しているなと感じました。

今でさえ案件や個人開発を通してJavaの他に様々言語を触りましたが、当時はJava1本。

VueやJavaScriptの基礎知識がないため、動作は確認できても、何がどう動いているのかあまり理解できない状態。

少し複雑な修正が必要になった際は度々苦戦しまくりな状態でした。

同チームの先輩に何度も質問を繰り返し、大幅に時間を割かせていたことを今でも申し訳なく思っています。

この経験を通して得た教訓は、「ベンチャー企業の教育体制には期待してはいけない」 ということ。

そして、というかそもそも論として「ベンチャーに行くのであれば、未経験であればインターンやスクールなどで開発経験を積み、自分でプロダクトを形にする力を一通り身につけた上で入らなければ活躍は厳しい」 ということでした。

逆を言えばある程度一人でできる方であれば、大手とは違い研修はそこそこに、早い段階からガンガン経験を積めると言えます。

案件の技術のキャッチアップのほか、資格取得も推奨されていたため同時並行でJavaSilverや基本情報の勉強など行なっていました。

あまり資格に意味はないというものの、右も左も分からない新参者のため一応上記2資格は取得しました。

ただ、今思えば案件の技術キャッチアップを最優先で行い本当に余裕のある時に資格勉強に時間を割けばよかったなと今でも思っています。

資格はいつでも取れますし、結局転職の際にも見られるのは実務経験なので。

社内の期待に応えるため両立できればと思っていましたが、完全に優先順位をミスっていたと思います。

危機感と焦燥感(入社5ヶ月〜1年ちょい)

その後、別の案件に異動し、3件程プロジェクトを転々としました。

どの案件でも使用されていた技術は想像以上に「レガシー」と言われるものでした。(StrutsjQueryなどなど)

新人の私には十分学びの多い内容でしたが、正直かなり焦りもありました。

「このまま枯れた技術を使い続けるのはまずいのでは。」という危機感です。

生成AIで単純作業が代替されている時流もあり尚更です。

いわゆるエクセルスクショなどもひたすらやっていた期間もあり、より現状に対する不安が募っていきました。

また、独学で学習は続けていたものの、キャリアアップという意味では全く最短経路ではなくむしろ遠回りをしている気がして不安で不安で仕方ありませんでした。

もうこれ転職した方がいいのだろうか?

とはいえ今のスキルでより良いところに行けるはずもない。。。

正直詰んだな自分と思っていました。

これらの焦燥感から、スクールや学習サービスを探していた中で偶然Xで出会ったのが HappinessChain でした。

HappinessChainに入会

実は、HappinessChainに入る前は月額制のオンラインスクールを利用していました。

しかし、そのスクールでは成果物へのフィードバックが基本1回、ざっくりとした回答のみであったため、物足りなさを感じて退会。

その点、HappinessChainでは機能ごとにプルリクエストを提出し、都度細かく指摘やコメントをもらえる仕組みがあり、非常に実践的だと感じました。このフィードバックがあるおかげで、改善点が明確になり、技術力の向上に繋がっています。



HappinessChainbに入会していなければおそらく



Macも買わない



Dockerも全然知らない



Gitも解像度ぼんやり



自分のレベルに不釣り合いな書籍やUdemyに時間を浪費し、うろうろうろうろ彷徨い



とりあえず資格勉強にすがっていただろうと思うので



入会5ヶ月目の現時点でもう既に入会してよかったなと



心の底から思います。

周囲との差と悔しさ

現在、入社1年半が経過しましたが、業務は決して順調とは言えません。むしろ真逆です。

大学時代の同期や幼馴染と飲みに行くと、名のある企業に就職し、プライベートも充実している彼らの華やかな話を聞くたびに、自分の現状を嫌というほど思い知らされます。

平日は残業続き、土日は勉強漬けの生活。そんな自分との乖離に悔しさがこみ上げ、大学や地元の友人との飲み会の後に泣きながら帰った日もありました。

ただ、この悔しさこそが私の原動力でもあります。

「絶対に見返す」という反骨心を燃料に、モチベーションに変えています。

しかし、人と自分を比べすぎなところはあるので、過去の自分と比べる意識は忘れないようにしたいものです。

この悔しい経験を通して、さらに成長していきたいと思っています。



終わりに

この1年半は苦労の連続でしたが、その分得られたものも多くありました。

いつか自分自身の理想を叶えられるよう淡々と努力を継続していきます。

今の私と同じように絶賛勉強漬けの方、これからITの世界に入る方、自分の選択が正解と思えるまで、続けましょう!



それでは!

11月振り返り

はじめに

HappinessChainに入会して5ヶ月が経過し、12月に入りました。 今年も残りわずかとなりました。年末は何かと忙しい時期ではありますが、この1年の総仕上げとして学習も気を抜かずに取り組みたいと思います。 それでは、11月の学習を振り返っていきます!

今月の学習時間(期間 : 11/1~11/30)

11月中の学習時間はトータルで 93.5時間 でした。
先月に引き続き、月の学習目標である100時間には届かず、大反省です。

進捗状況

11月中に実施した課題は以下の通りです。

Django

先月に引き続きDjangoコースに取り組んでいます。 11月は主に商品管理機能、カート機能、チェックアウト機能、プロモーション機能をゴリゴリ実装しました!

JavaScript

カート機能やプロモーション機能を実装する中でJavascriptを書く場面が多々あったため、JavaScriptコースの学習も並行して進めました。 カレンダーやtodoリストも一旦実装し、jsでのDOM操作に関してはかなり慣れたかと思います。 引き続き「ガチで学びたい人のためのJavaScriptカニズム」や「JavaScript Primer」をインプットし、jsの理解を深めていきたいと思います。

記事執筆時点(12/8)では ECサイトのプロモーション機能の実装に取り組んでいます。 商品管理機能、カート機能、チェックアウト機能はLGTMをいただきました。

今月の悪かった点

  • 無理が祟りコロナに罹患

    • 11月は先月に続き残業の多い月で、帰宅時間が21:00〜22:00になる日が多くありました。その中で睡眠時間を削りながら学習時間を確保していましたが、11月末にコロナを発症し、1週間ほど会社を休むことになりました。睡眠不足が一因だったのではないかと深く反省しています。また、社会人になってから運動する機会が減り、体力がかなり落ちていることも原因の一つだと感じています。 これまで体調不良で仕事を休むことは全くありませんでしたが、今回は健康の大切さを痛感させられました。今後は学習や仕事だけでなく、健康管理にも意識を向けたいと思います。

今月の良かった点

  • 月の学習時間が100hを切ってはいるが、学習を継続できたこと。
  • 最低限学習を行った日はgithubへのプッシュは継続し行ったこと。

次月の目標

来月は以下の学習時間を目標にします。

  • 平日 : 1~3時間/日、通勤時の英語学習、朝学習 1h
  • 休日 : 10時間/日

また、以下コースを完了目標にします - Django( ECサイトTwitterクローン着手)

終わりに

では、また来月!


10月振り返り

はじめに

HappinessChainに入会して4ヶ月が経過し、11月に入りました。 気温もぐっと下がり、布団でぬくぬくするのが心地よい季節です。 今年もあと8週間ほどで終わると考えると、20代があっという間に過ぎ去りそうな気がします。1日1日を後悔のないように生きたいものです。 とはいえ、時にはモチベーションが落ちる日もあるもの。そんなとき、私はダルビッシュ有投手のマインドセットを見返しています。

ろくに練習しなかったいせいで大した選手になれないまま40代を迎えてしまったことを激しく後悔し、神様に必死で頼み込んで20歳年巻き戻してもらった

この設定で20歳からは生きてるらしいのです。 それでは、10月の学習を振り返っていきます!

今月の学習時間(期間 : 10/1~10/31)

10月中の学習時間はトータルで 81.5時間 でした。
先月に引き続き、月の学習目標である100時間には届かず、大反省。 今月は下旬から現場のプロジェクトが落ち着き始め、定時上がりが増え学習時間も確保できました。 私は管理者側のアプリを担当していましたが、前任者が先月退場し管理者側アプリは実装は私一人で行うことになり、入場して4ヶ月ほどの私にとっては中々精神的なプレッシャーを感じる月となりました。 勤怠の残業時間自体は45hギリギリでしたが、朝の始業前やお昼休憩なども仕様理解やソース解析等行っていたため、実質の残業時間は60時間に達している気がします。 精神的には大変な月でしたが、Happiness Chainの学習によって、確実に自分が前進していると感じられたことが精神的な支えになっています。

進捗状況

10月中に完了した課題は以下の通りです。

Django

先月に引き続きDjangoコースに取り組んでいます。 10月前半は主にDjangoのUdemy動画と公式チュートリアルの復習を行いました。 教材を1周しただけではやはり理解仕切れておらず、2周したことで知識が整理され、理解が深まったように感じました。

記事執筆時点(11/4)では ECサイトの実装に取り組んでいます。 商品一覧と、商品詳細画面の機能はLGTMをいただきました。続いて商品管理機能をプルリク提出中です。 並行してカート機能の実装も進めています。 また、今回初めて本格的にheorkuを用いデプロイを実施しました。Djangoでもそうですが、環境周りのエラー解消にかなり苦戦しました。

今月の悪かった点

  • 日報やdaily goalが疎かに
    • 帰宅時間が21:00~22:00,就寝が23:00ということから、日報やdaily goal、SNSの発信などが億劫になり、しばらく停滞していました。
    • Githubへのpushもせず就寝してしまうことも多々あったため、今後は自宅での勉強時間が確保できない日も、最低限GitHubのコミットだけは続けていこうと思いました。

今月の良かった点

  • 月の学習時間が100hを切ってはいるが、学習を継続できたこと。

次月の目標

来月は以下の学習時間を目標にします。

  • 平日 : 1~4時間/日、通勤時の英語学習、朝学習 1h
  • 休日 : 10時間/日

また、以下コースを完了目標にします

終わりに

HappinessChainに入会し4ヶ月。現場に入り4ヶ月。慣れてはきたものの、10月には気が緩む瞬間もあり、良くない慣れが見られた月でした。 学び続ける意欲を失わず、毎日少しずつでも成長していきたいと思います。

11月は、自分のペースを大切にしながらも、計画的に学習時間を確保し、目標達成に向けて前進したいです。

では、また来月!


Django公式チュートリアルをやってみる

はじめに

今回は、Djangoの公式チュートリアルを通して、Djangoアプリケーション作成に挑戦しました。 Django公式チュートリアルの項目に沿って、基本的な使い方や開発の流れを紹介しながら、自分なりに学んだことや気づきをまとめました。 はじめてDjangoに触れる方でも、この記事を通じて参考にしてもらえると嬉しいです。

はじめての Django アプリ作成、その 1

mkdir mysite
cd mysite
  • プロジェクトを作成する
python3 -m django startproject mysite .
  • 以下のコマンドでサーバを起動(manage.pyのあるディレクトリで実行すること)
python3 manage.py runserver
  • 以下のURLにブラウザからアクセスする。Djangoのウェルカムページが表示される。
http://localhost:8000/
  • 「polls」というアプリケーションを作成する
k_tanaka@tanakaknyanoAir mysite % python3 manage.py startapp polls

k_tanaka@tanakaknyanoAir mysite % ls
db.sqlite3      manage.py       mysite          polls
  • polls/views.pyに以下のソースを記載
from django.http import HttpResponse

def index(request):
    return HttpResponse("Hello, world. You're at the polls index.")
polls/
    __init__.py
    admin.py
    apps.py
    migrations/
        __init__.py
    models.py
    tests.py
    urls.py
    views.py
  • urls.pyに以下のソースを記載
from django.urls import path

from . import views

urlpatterns = [
    path("", views.index, name="index"),
]
  • プロジェクト(mysite)配下のurls.pyに以下に修正
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path("polls/", include("polls.urls")),
    path("admin/", admin.site.urls),
]

はじめての Django アプリ作成、その2

  • migrate コマンドは INSTALLED_APPS の設定を参照するとともに、 mysite/settings.py ファイルのデータベース設定に従って必要なすべてのデータベースのテーブルを作成します。
k_tanaka@tanakaknyanoAir mysite % python3 manage.py migrate
  • polls/models.pyに以下の内容を記載
polls/models.py¶
from django.db import models


class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField("date published")


class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)
  • mysite/setting.pyに以下の記載を修正
INSTALLED_APPS = [
    "polls.apps.PollsConfig",
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
  • makemigrations を実行することで、Djangoにモデルに変更があったこと(この場合、新しいものを作成しました)を伝え、そして変更を マイグレーション の形で保存することができました。
python manage.py makemigrations polls
  • migrate を再度実行し、 モデルのテーブルをデータベースに作成
$ python3 manage.py migrate
  • shellで遊んでみる
k_tanaka@tanakaknyanoAir mysite % python3 manage.py shell
Python 3.9.6 (default, Feb  3 2024, 15:58:27) 
[Clang 15.0.0 (clang-1500.3.9.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from polls.models import Choice, Question
>>> Question.objects.all()
<QuerySet []>
>>> from django.utils import timezone
>>>  q = Question(question_text="What's new?", pub_date=timezone.now())
  File "<console>", line 1
    q = Question(question_text="What's new?", pub_date=timezone.now())
IndentationError: unexpected indent
>>> q = Question(question_text="What's new?", pub_date=timezone.now())
>>> q.save()
>>> q.id
1
>>> q.question_text
"What's new?"
>>> q.pub_date
datetime.datetime(2024, 10, 15, 12, 24, 7, 807229, tzinfo=datetime.timezone.utc)
>>> q.question_text = "What's up?"
>>> q.save()
>>> q.question_text
"What's up?"
>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>]>
>>> q = Question(question_text="testtest", pub_date=timezone.now())
>>> q.save()
>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>, <Question: Question object (2)>]>
>>> 
  • polls/models.pyに以下の内容を記載
from django.db import models


class Question(models.Model):
    # ...
    def __str__(self):
        return self.question_text


class Choice(models.Model):
    # ...
    def __str__(self):
        return self.choice_text
  • 以下コマンドで管理サイト用のユーザを作成する
 python manage.py createsuperuser
  • サーバを起動
$ python manage.py runserver
from .models import Question
admin.site.register(Question)
  • 再度管理側サイトを覗くと、model.pyに記載したテーブルが表示される

はじめての Django アプリ作成、その 3

  • polls/views.pyに以下のビューを追加
def detail(request, question_id):
    return HttpResponse("You're looking at question %s." % question_id)


def results(request, question_id):
    response = "You're looking at the results of question %s."
    return HttpResponse(response % question_id)


def vote(request, question_id):
    return HttpResponse("You're voting on question %s." % question_id)
  • polls.urls.pyに以下のpathを追加
from django.urls import path

from . import views

urlpatterns = [
    # ex: /polls/
    path("", views.index, name="index"),
    # ex: /polls/5/
    path("<int:question_id>/", views.detail, name="detail"),
    # ex: /polls/5/results/
    path("<int:question_id>/results/", views.results, name="results"),
    # ex: /polls/5/vote/
    path("<int:question_id>/vote/", views.vote, name="vote"),
]
def index(request):
    latest_question_list = Question.objects.order_by("-pub_date")[:5]
    template = loader.get_template("polls/index.html")
    context = {
        "latest_question_list": latest_question_list,
    }
    return HttpResponse(template.render(context, request))
  • polls/templates/polls/index.htmlを作成
{% if latest_question_list %}
    <ul>
    {% for question in latest_question_list %}
        <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
    {% endfor %}
    </ul>
{% else %}
    <p>No polls are available.</p>
{% endif %}
from django.shortcuts import render

from .models import Question


def index(request):
    latest_question_list = Question.objects.order_by("-pub_date")[:5]
    context = {"latest_question_list": latest_question_list}
    return render(request, "polls/index.html", context)
  • detailのビューを以下 get() を実行し、オブジェクトが存在しない場合には Http404 を送出することは非常によく使われるイディオムです。
def detail(request, question_id):
    try:
        question = Question.objects.get(pk=question_id)
    except Question.DoesNotExist:
        raise Http404("Question does not exist")
    return render(request, "polls/detail.html", {"question": question})
  • 上記はget_object_or_404()を使って省力して記述できる。
def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, "polls/detail.html", {"question": question})
  • polls/templates/polls/detail.htmlを以下のように修正。
    • choice_set()はそのquestionに紐づくchoiceが取得される。
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>
  • polls/index.htmlを以下のように修正
    • 以下のように書くことでurls.pyのnameの値がdetailのpath()を呼び出せる。
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
  • pollsのurls.pyにて名前空間を設定し、他のアプリのurls.py特別できるようにしてあげる
# polls/urls.py
from django.urls import path

from . import views

app_name = "polls"
urlpatterns = [
    path("", views.index, name="index"),
    path("<int:question_id>/", views.detail, name="detail"),
    path("<int:question_id>/results/", views.results, name="results"),
    path("<int:question_id>/vote/", views.vote, name="vote"),
]
  • 今回はアプリが1つだけなので、上記の書き方で、pollsのurls.pyが呼び出されるが、アプリが複数の場合、どのアプリのurls.pyかを指定する必要がある。それがいか。
# polls/templates/polls/index.html
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>

はじめての Django アプリ作成、その 4

  • polls/templates/polls/detail.htmlを以下のように修正
    • legend
      • fieldsetタグでグループ化されたフォームの入力項目に対して、タイトルを付けるためのタグ。
    • forloop.counter は、 for タグのループが何度実行されたかを表す値。
<form action="{% url 'polls:vote' question.id %}" method="post">
{% csrf_token %}
<fieldset>
    <legend><h1>{{ question.question_text }}</h1></legend>
    {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
    {% for choice in question.choice_set.all %}
        <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}">
        <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br>
    {% endfor %}
</fieldset>

<input type="submit" value="Vote">
</form>
  • polls/views.pyのvote関数を以下のように修正
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse

from .models import Choice, Question


# ...
def vote(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    try:
        selected_choice = question.choice_set.get(pk=request.POST["choice"])
    except (KeyError, Choice.DoesNotExist):
        # Redisplay the question voting form.
        return render(
            request,
            "polls/detail.html",
            {
                "question": question,
                "error_message": "You didn't select a choice.",
            },
        )
    else:
        selected_choice.votes += 1
        selected_choice.save()
        # Always return an HttpResponseRedirect after successfully dealing
        # with POST data. This prevents data from being posted twice if a
        # user hits the Back button.
        return HttpResponseRedirect(reverse("polls:results", args=(question.id,)))
  • polls/view.pyに以下の関数を追加
def results(request, question_id):
    # response = "You're looking at the results of question %s."
    # return HttpResponse(response % question_id)
    question = get_object_or_404(Question, pk=question_id)
    return render(request, "polls/results.html", {"question": question})
  • polls/templates/polls/results.htmlを作成
<h1>{{ question.question_text }}</h1>

<ul>
{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
{% endfor %}
</ul>

<a href="{% url 'polls:detail' question.id %}">Vote again?</a>
  • polls/urls.pyを以下のように修正( <question_id> から に変更)
from django.urls import path

from . import views

app_name = "polls"
urlpatterns = [
    path("", views.IndexView.as_view(), name="index"),
    path("<int:pk>/", views.DetailView.as_view(), name="detail"),
    path("<int:pk>/results/", views.ResultsView.as_view(), name="results"),
    path("<int:question_id>/vote/", views.vote, name="vote"),
]

はじめての Django アプリ作成、その 5

  • polls/tests.py
    • ssertIs(a, b) というメソッドは、「a is b」となるかどうかのテストを実行しており、was_published_recently() の出力が False となっていれば、テストは成功となる
    import datetime

    from django.test import TestCase
    from django.utils import timezone

    from .models import Question


    class QuestionModelTests(TestCase):
        def test_was_published_recently_with_future_question(self):
            """
            was_published_recently() returns False for questions whose pub_date
            is in the future.
            """
            # 現在日時から30日後の日時を取得
            time = timezone.now() + datetime.timedelta(days=30)

            # 上記の日時を使ってQuestionインスタンスを作成
            future_question = Question(pub_date=time)

            # 上記のQuestionインスタンスをつかってwas_published_recently()を呼び出し真偽値を確認
            self.assertIs(future_question.was_published_recently(), False)

            # modelsのwas_published_recentlyに定義した通り、pub_dateが1日前から現在日時までの間であればtrueが返される

            # そのwas_published_recentlyの返り血がFalseなら上記テスト自体はTrueとはんていする仕様

はじめての Django アプリ作成、その 6

li a {
    color: green;
}
  • style.cssをhtmlで読み込む
{% load static %}

<link rel="stylesheet" href="{% static 'polls/style.css' %}">
body {
    background: white url("images/background.png") no-repeat;
}
  • polls/static/polls/ ディレクトリの中に images サブディレクトリを作成 polls/static/polls/images/background.png となるようにする。 polls/static/polls/style.cssに以下の内容を追記
body {
    background: white url("images/background.png") no-repeat;
}

終わりに

今回のDjangoチュートリアルを通して、Djangoの基本的な使い方やアプリケーション作成の流れを体験し、理解が深まりました。 今後はより複雑なプロジェクトや、実際の業務でDjangoを活用できるようスキルを磨いていきたいと思います。 それでは、また次回の記事でお会いしましょう!

9月学習振り返り

はじめに

HappinessChainに入会し、早くも3ヶ月が経過しました。 9月も残暑が続きましたが、10月に入りやっと涼しくなってきたように感じます。 急激に気温が下がった日に何故か頭痛が起きるのは私だけでしょうか。あれどうにかならないんですかね。。
季節の変わり目、体調管理には気をつけたいところですね。 それでは、9月の学習を振り返っていきます!

今月の学習時間(期間 : 9/1~9/30)

9月中の学習時間はトータルで 71.5時間 でした。
今月は中旬頃から残業が増え始め、平日はほとんど学習できませんでした(泣)
プロジェクトメンバーが他プロジェクトのサポートに回ったことで、私一人にタスクが集中してしまったためです(泣)。
二週間ずっと22時過ぎに帰宅する日々が続きましたが、驚いたことに、その反動で浪費も増えてしまいました。業務のストレスからか、食費や休日の娯楽費が爆増しています。
しかし、この不満をエネルギーに変えて、日々頑張っていこうと思います!

進捗状況

9月中に完了した課題は以下の通りです。

SQL

インプットとしては「スッキリわかるSQL入門」「達人に学ぶDB設計徹底指南書」をこなしました。
👇学習に使用した書籍についてレビュー記事を作成しました。

kenya6111.hatenablog.com 1冊目の書籍(スッキリわかるSQL入門)に関しては、主にSQL文法を中心に学びました。 実務ではSQLを書くことはあまりなかった為、がっつりSQL文法を学んでこなかった私にとっては、知らないことを多く知れた書籍になります。
入門書とはいえ、ボリュームがあり、こなすのに少し時間がかかりました。

kenya6111.hatenablog.com

2冊目の書籍(達人に学ぶDB設計徹底指南書)に関しては、主にDBの設計手法について学びました。 スッキリわかるSQL入門でもサラッとDB設計に触れられていましたが、こちらではより具体的に学ぶことができ理解が深まりました。

以上のインプットの後、アウトプットとして某SNSアプリのDBモデリング課題に取り組みました。 上記2冊を読み返しつつ設計しました。

REST

「REST」に関しては、ネット上でいくらか聞いたことはあるものの、何を意味するのかははっきり理解できていませんでした。 コーディングはせず、概念の理解に取り組むというのは中々大変ではありましたが、
RESTとは何か、その詳細を理解できました。

👇学習に使用した教材についてレビュー記事を作成しました。
kenya6111.hatenablog.com

Django

REST課題も完了し、Djangoコースに現在取り組んでいます。
9月末時点ではDjangoのUdemy動画まで完了しました。 こちらは移動時間や隙間時間にできるものではなく、しっかり机と向き合い実装をする必要があるため、残業の多い最近では進捗はイマイチ。 ですが、焦っても仕方がないので、理解の深さを優先し落ち着いて進めようと思います。

次月の目標

来月も、というより11月末まではおそらく残業が多くなりそうです。 できるだけ効率的にタスクをこなし、学習時間を確保したいです。 来月は以下の学習時間を目標にします。

  • 平日 : 1~2時間/日、通勤時の英語学習
  • 休日 : 10時間/日

また、以下コースを完了目標にします

終わりに

先月に引き続き、プログラミングで脳がオーバーヒートするたびにサウナに駆け込んでいます。
今月は残業フィーバーだったため、休日はどこかリラックスできる場所に行きたい。。都心で良さげな場所を探したところ、ありました。
その名も「テルマー湯 新宿店」
こちら、伊豆から天然温泉を毎日運搬し提供しているようです。
新宿という土地柄、施設はそこまで広くないだろうと予想していましたが、新宿という土地に似つかわないほど広々とした空間で驚き。
施設周辺も歌舞伎町から少し外れた通りにあるせいか、人通りも少なくとても静かでした。
コワーキングスペースもあり、リラックスしながら作業も捗りました。また時間を見つけて訪れたいと思います! ではまた来月!!!


よく耳にする「REST」とは何なのか

はじめに

今回、RESTfulなURL設計をしっかり理解するために、RESTについて学びました。 この記事では、その学びを簡単にまとめ、自分なりの言葉でRESTの概念を整理しています。

参考にした教材は以下になります:

REST WebAPI サービス 設計

REST APIについて自分の言葉でまとめてみる

  • RESTとは、Web上のリソースを、HTTPメソッドを使ってシンプルに操作する設計ルール。 「representational state transfer」の略。2000年にRoy Foieldingというアメリカの学者が発表したRESTに関する論文が元となっている。
  • REST APIの設計原則としては以下のようなものがある。
    • クライアント-サーバー構造:クライアントとサーバーで関心事を分離している。
    • リソース指向:全情報をリソースとして扱い、一意のURIでそれぞれのリソースを識別する。
    • ステートレス:各リクエストは、以前のリクエストとは独立している(依存していない)。
    • 統一インターフェース:HTTPメソッド(GET, POST, PUT, DELETE等)を使用して、リソースに対する操作を定義する。
    • キャッシュ可能:レスポンスにキャッシュ情報を含めることで、クライアント側でのキャッシュが可能。
    • 階層化システム:システムを階層構造(3層アーキテクチャ)にする。ここの層は独立して設計される。

movieをリソースとして CRUD操作のURI、HTTPメソッドを定義してみる

URI HTTP method
/movies GET
/movies POST
/movies/{id} GET
/movies/{id} PUT
/movies/{id} DELETE

メモ

  • Web
    • HTTPなどのインターネット関連技術を利用してメッセージの送受信を行う技術、またはそれらの技術を利用して展開されたサービス
  • API(application programming interface)
    • 機能やデータを外部から呼び出して利用できるよう定めた規約
  • WebAPI
    • Webサービスで提供している 機能やデータを外からプログラムが読み取りやすい形で利用できるよう定めた規約規約またはその実装(第三者が情報を利用して新たな機能を開発すること)
    • そのAPI独自の機能を公開する。一般的などこでもできる機能は公開しても価値がないので後悔しない
CRUD 操作 HTTPメソッド
CREATE 作成 POST
CREATE 作成 PUT
READ 読み取り GET
UPDATE 更新 PUT
DELETE 削除 DELETE
  • CREATEのPOSTとPUTはリソース名が未定ならばPOST、リソース名が決まっていればPUTをする

  • WEB API公開によるリスクと対策

    リスク 原因 対策
    ユーザが減る 他サービスが優れている、作り込む機能を優先順を間違えている ユーザ獲得している他さーびすの機能を取り込む
    ユーザが減る 他サービスが劣っている、自サービスが落とされている 該当サービスに対してWEBAPIキーの提供を停止
    リソースが圧迫される API化することで機械的にデータが取りやすくなっている 該当サービスに対してWEBAPIの利用制限をかける
  • 副作用

    • リソース(データ)が改変されること
    • 副作用がある=DBテーブルの店舗名を書き換える更新処理(データが書き換わっているもの)
    • 副作用のない=GETのようなデータ自体は改変されないもの
  • 安全
    • 副作用がないこと。
    • つまりリソースの状態を変化させない読み取り専用の操作(GET,HEADなど)
  • 冪等
    • 何度実行しても同じ結果が得られる。副作用の有無は問わない
    • 副作用のある操作で冪等な操作は更新処理で、毎回No1の店舗を指定して更新をかけるというやり方だと冪等。(PUTやDELETE)
    • 副作用のない冪等はデータの取得(GET、HEAD)
  • 安全でも冪等でもない操作は、「データ登録」(POST)

  • section3

    • RESTFUL(representational state transfer)(分散型システムにおける設計原則群、つまり設計ルールのこと)
      • RESTで求められる原則に従っている
      • representational state transfer(分散型システムにおける設計原則群、つまり設計ルールのこと)
      • Roy fieldingというアメリカの学者がRESTNo設計ルールを考えた。
    • REST原則6種類
      • クライアントサーバ
        • クライアントとサーバ側で関心ごとを分離する
        • クライアント側がトリガー、サーバ側が受け身
      • 階層化システム
        • 多層アーキテクチャシステム
        • WEB,AP,DB(これらが蜘蛛の巣状にネットわーっ具を作ってる)
        • 階層化システムのメリット
          • 各システム(コンポーネント)に役割を決めて独立させることで進化と再利用ができる
          • マイクロサーボスのような概念
        • デメリット
          • データ処理にオーバーヘッドが増加
          • ユーザから見ると応答が悪くなる
      • コードオンデマンド

        • クライアントコードをダウンロードして実行できる
        • リリース後もサーバ側の処理を修正してクライアントに反映できる
        • 「on demand」の意味・翻訳・日本語 - 要求に応じて、要求があり次第
        • メリット
          • リリース後リリース後のくらいあんとに対して機能追加ができる
          • サーバの負荷が下がる(クライアントに処理を委譲できる)
          • 評価が複雑になる(多数のブラウザとか)
      • ステートレス

        • ステートフルは以前の状態を覚えてくれている(前の会話を前の会話の状態を記憶して次の会話をする)
        • ステートレス(前の状態を保存しない。それぞれの会話が独立している、単独で成り立つ)
        • このステートレスを制約としていうと、「サーバはリクエストだけでコンテキストを理解できる」
        • つまりサーバの保存されたコンテキスト情報(サー婆セッション)は使わずに、状態はクライアント上に保存され、各単独のリクエストで情報が成り立っているというのが制約
        • メリット
          • 単一のリクエスト以外見る必要がないので監視が容易
          • 障害の発生したリクエストだけ回復すればいいので復旧が容易
          • リクエスト全体でサーバリースを共有する必要がないのでスケールが容易
        • デメリット
          • 単一のリクエストで完結させるため、リクエストデータに重複がある
      • キャッシュ制御
        • クライアント側でレスポンスがキャッシュできるようにしてくれよなってこと
        • レスポンスは明示的、暗黙的に キャッシュ可能
        • 適切にキャッシュすることでクライアント・サーバ間の通信が排除されユーザ体験の向上、リソーすの向上、拡張性の向上が見込まれる
        • 適切にキャッシュすれば必要が情報だけリクエストすれば良くなる→効率上がる
        • メリット上記。
        • 古いデータを戻してしまうとデータの不整合が起こり、画面で正しくない情報が表示される。
      • 統一インタフェース
        • 中身は以下の4つの制約で成り立っている
        • リソースの識別
          • リソースとは名前がつけられるあらゆるもの。サーバ側に保持される全データ。(ドキュメント、人、物、画像、サービス、状態)
          • リソースの識別==URIを用いてサーバに保存されたデータを識別するということ。普通のURLでサーバのデータを撮りに行くこと
          • Web上にあるあらゆるファイルを認識するための識別子の総称
          • URIはリソースを識別するもの。URIではリソースでパスを指定する
        • 表現を用いたリソース操作
          • 表現=リソース(サーバに保持された)のある断面(クライアントクライアントへ返されるレスポンスやサーバへポストするデータのこと)←この断面のデーtを受け取ったり渡したりしている。断面情報を利用してサーバのデータを操作する
          • あるタイミングにおける断面を受け取ったり渡したりしているから、これを表現と言っている(断面情報を利用してサーバ上のデータを操作する)(Authorizationも送信する)
        • 自己記述メッセージ
          • 自己記述=データ自身がデータの中身を説明している
          • メッセージ=サーバへリクエストするデータ、クライアントへレスポンスするデータ
          • つまりメッセージ内容がなんであるかヘッダに書いてあればOKっていうこと
          • ContentTypeをヘッダーに記述している、これが自己記述メッセージ
        • アプリケーション状態エンジンとしてのハイパーメディア
          • HATEAOS(hypermedia as the engine of application state)
          • つまりはレスポンスに現在の状態を踏まえて関連するハイパーメディアを含めてねってこと(検索結果ページにおける次ページのリンクとか)
        • メリット
          • システムアーキテクチャ全体が簡素化されわかりやすくなる
          • 提供するサービスに集中でき独自の進化ができる
          • 取り決めが決まっているので異なるブラウザでも同じ画面を表示できる
        • デメリット
          • 標準化によって効率性が犠牲になる
    • RESTAPI設計レベル(どれだけRESTに準拠しているか)
      • LEVEL0,HTTPを使っている
        • RESTAPIの基本レベル。RPC(remote pocedure call)スタイルのXML通信のようなものを定義している。RPC=remote procedure call(ネットワーク越しに別コンピュータ上のプログラムを実行する仕組み)
      • LEVEL1 リソースの概念を導入
        • リソースごとにURLが分割されている
        • リソースに対して複数のURLが定義されている
      • LEVEL2 HTTPの動詞を導入

        • リソースに対してHTTPメソッドを使ったCRUD操作が行われている
      • LEVEL3 HATEOAS導入している

        • レスポンスにリソース間のつながりが含まれる(レスポンスに現在の状態に関連するハイパーリンクが含まれる)
    • URI設計(URIを設計すると、より良いAPIになるか)
    • HTTPメソッドの適用(HTTPメソッドとURLをどう組み合わせて使うか)
      • GET /v1/users/123 HTTP/1.1 Host: api.example.com
      • リソースが「/v1/users/123」,HTTPメソッドはリソースに対する操作(上で言うとGET)
      メソッド名 説明
      GET リソースの取得
      POST リソースの新規登録(データ作成時にリソース名が決まっていない)
      PUT 既存リソースの更新、 リソースの新規登録(データ作成時にリソース名が決まっている)
      DELETE リソースの削除
      操作 API実装例 補足
      ユーザ情報の一覧取得 GET http://api.example.com/users ユーザ情報の一覧を取得するというAPIであればGETで一覧を取得できる(usersと複数形なので一覧nの取得になる)
      ユーザの新規登録 POST http://api.example.com/users ↑と同じURIに対してPOSTを指定するとユーザの新規登録という意味になる
      特定ユーザの取得 GET http://api.example.com/users/12345 (12345とある通りユーザを特定している場合)GETをつければ特定してユーザの情報を取得
      ユーザの更新 UPDATE http://api.example.com/users/12345 (12345とある通りユーザを特定している場合)PUTを指定するとそのユーザの更新
      ユーザの削除 DELETE http://api.example.com/users/12345 (12345とある通りユーザを特定している場合)そのユーザの削除
      • URIは同じでも、HTTPメソッドによって意味合い、リソースへの操作を切り替えられる。
    • クエリとパスの使い分け

      種類 概要
      クエリパラメータ URLの末尾の?に続くキーバリューのこと GET http://api.example.com/users?page=3
      パスパラメータ URlの中に埋め込みをするパラメータ GET http://api.example.com/users/123
      • どう使い分けるかの基準2つ
        • 一意のリソースを表すのに必要かどうか
          • この必要がある場合はおあすパラメータを使う
        • 省略可能かどうか
          • 可能な場合、クエリパラメータを使う
        • パスは対象のリソースまでの道のり、クエリは検索の際の条件って意味合いがある

        • 一意のリソースがパスパラメータになる理由は、パスがリソースそのものを示す「場所」だからです。

          • リソースの場所を明確に示す: パスはリソースの位置を指し示すため、一意のリソースを特定する際にそのリソースの場所(IDなど)をURLの一部として表現します。例えば、/users/123は「ユーザーID 123というリソースがここにある」と示します。

          • 意味的に正確: クエリパラメータは条件やフィルタリングのために使うべきで、リソースそのものを表すパスパラメータとは役割が異なります。/users/123が特定のユーザーを指すのに対し、/users?id=123は条件に近い使い方になります。

        このように、一意のリソースを指す時はそのリソースの「場所」を表すためにパスパラメータを使います。

    • ステータスコード
      • 1✖︎✖︎
        • 情報
        • 100 continue サーバがリクエストの最初の部分を受け取り、まだサーバから拒否されていないこと
        • 101 プロトコル
      • 2✖︎✖︎
        • 成功
      • 3✖︎✖︎
        • リダイレクト
      • 4✖︎✖︎
        • クライアントサイドに起因するエラー
      • 5✖︎✖︎
        • サーバーサイドに起因するエラー
      • 1✖︎✖︎
    • データフォーマット
      • XML
        • タグで記述
        • タグには属性つけられる
      • JSON
        • 波括弧で囲う
        • XMKに比べてデータ量が減らせる
        • jsを元にしたフォーマット
      • JSONP
    • データの内部構造
      • エンベロープ→レスポンスボディ内のメタ情報。(レスポンスのヘッダーとかぶっている)
      • オブジェクトはできるだけフラットにする。(JSON内部のネストを減らすようにする)
      • ページネーションをサポートする情報を返す
    • APIバージョンの表現
      • APIにバージョンを含めると特定バージョン指定でアクセスできるので、クライアント側で突然エラーにならない
      • 複数バージョンを並列稼働させるので、ソースやDBの管理が複雑になる
        • APIのURLにバージョンを含めるかどうかは、世間一般に公  開するサービスかどうかになる。
      • バージョンはパス、クエリ、ヘッダーに入れる3パターンある。
      • パスに入れるのが一番多いパターん
      • バージョンは1、2、3の3パターン
        • 「メジャー: 後方互換しない修正を行った時にバージョンが上がる」「マイナー: 後方互換する機能追加した時にバージョン上がる」「パッチ:単純なバグ修正した時に上がる 」
    • OAuthとOpenID Connect
      • 認証は本人特定すること。
      • 認可はアクセス制御すること
      • 最初にユーザがサーバへアクセスしてきた時に、誰であるかの本人特定が認証。(ログインすると言うこと)
      • そしてログイン後にリソースに対してアクセスして良いかどうかを判断制御するのが認可
      • OAuthとOpenID Connectの違い
        • どちらも認可の仕組み(認可なのでアクセス制御の仕組み)
        • OpenID ConnectはOAuth(認可)+本人情報取得を加えた仕組み
      • OAuth
        • Authorization code フロー
          • サードパーティーのアプリに対して連携設定に依頼する(facebook,line,yahoo!連携的な)
          • 画面がリダイレクトされ、facebookならfacebookのログイン画面が表示される
          • ユーザはログインし、サードパーティに対する処理の許可を与える
          • 許可を与えるとサービス側からサードパーティアプリに対して、「認可コード」が転送される
          • 受け取った認可コードを使ってサードパーティは再度サービス(facebook等)にアクセスする
          • サービス側からアクセストークンのキー情報が返却される
          • このトークンはユーザを特定しかつサービスに対する認可を持たせたキー情報になる。
          • このアクセストークンを用いると、あるユーザがあるサードパーティにアクセスした時、そのユーザのアクセストークンを使ってサービスにたいしてAPIwo利用することができる
      • OpenID connect
    • JSON WEB TOKEN(JWT) ジョットと呼ぶ。
      • 主に3つの特徴がある
        • 署名による改ざんチェック
        • URL-safeなデータ
        • データの中身はJSON形式
      • 主な用途
        • 認証結果をサーバサイドで保存せずクライアントサイドで保持する
      • 基本構造
        • 中身はピリオド2つで括られた3要素
        • ヘッダー、ペイロード、署名
      • ヘッダー(署名で使うアルゴリズムなどを定義する)
        • typ項目→JWTで固定
        • alg項目→署名に使うアルゴリズム(HS256,ES256などなど)
      • {
          "type":"JWT",
          "alg":"ES256"
        }
        
      • ペイロード(保存するデータをなんでも入れちゃおうってとこ)

        終わりに

        今回の学習を通じて、REST APIの設計原則や、その具体的な実装方法について深く理解することができました。

また、CRUD操作の具体例やHTTPメソッドの使い分け、URI設計のポイントについても、知識を身につけることができました。
今後は、この知識を実際のプロジェクトに活かし、より良いAPI設計に挑戦していきたいと思います。 それでは!!!

達人に学ぶDB設計徹底指南書〜初級者で終わりたくないあなたへ〜 感想&メモ書き

良かったところ

『スッキリわかるSQL入門』で学んだ基礎をさらに深め、より深くなデータベース設計を学べた点が大きな収穫でした。特に、エンティティの生成から正規化、そしてER図の作成までのプロセスを体系的に理解できたことで、これまで何となく行っていたテーブル設計が、明確な理論に基づいたものに進化した点が大きな収穫かと思います。 また、SQLの文法をすでに学んでいたおかげで、例示されたSQL文もすらすらと理解でき、学習がスムーズに進みました。 また、私自身、DB設計の正規化について、「第何正規化」など意識せずとも、自然と正規化できるものでは?と思っていたのですが、理論家クリスデイトの以下の引用の記載があり勉強になりました。

前述のように、正規化理論は基本的に常識にすぎないという理由で批判されてきた。......
優秀な設計者であれば、たとえBCNFの予備知識がなかったとしても、この関係変数を射影SPとSSに「自然に」分解するだろう。
だが、「自然に」とはどういう意味なのか。設計者は「自然な」設計を選ぶうえで、どのような「原理」を用いているのだろうか?
答えは、正規化の原理とまったく同じである。つまり、優秀な設計者は、そうした原理を正式に学んだことがなくても、その名前を言い当てることができなくても、そうした原理がすでに頭の中にある。
したがって、原理は確かに常識だが、それらは正規化された常識なのである。正規化を批判する人は、そういった点を見逃している。
それらの概念が実は常識すぎることを(実際はもっともらしく)主張するが、結して、常識の意味を正確かつ形所に述べることができない。

今回学んだ理論は非常に学びになったと感じましたが、実際に業務で設計を行うことで、知識が定着し、実践的なスキルとして身につくのかなと感じました。

学んだこと

1章

  • スキーマ(schema) もともとは「枠組みをもった計画」という意味のギリシア語が語源
    • 古代ギリシャ語の「σχημα」から来ている。意味は「形」や「見かけ」で、現代英語では、構想やモデルを表す用語としても使われています。
    • ある特定の事柄や概念に関する知識や情報の「枠組み」や「構造」を指す。これは、過去の経験や知識を基にして、新しい情報を処理したり、問題を解決したりする際に使う。例えば、スキーマを持っている人は、情報を理解しやすくなったり、問題を迅速に解決できる可能性が高くなる。
    • スキーマには3種類存在する
    • 外部スキーマ
      • データベースの構造(スキーマ)のうち、ユーザー側やアプリケーション側から見た構造のこと
      • データベースから必要なデータを抽出した結果
      • 外部スキーマを「ビュー」と呼ぶ。MVCモデルの概念における「ビュー」はユーザーが操作する見た目の画面だが、SQLのビューはユーザーが見やすいようにデータベースから抽出したデータ。
      • ユーザーがデータベースと対話する際のインタフェースを提供する部分で、ビュー層とも呼ばれています。
      • 実際にアプリを使うユーザが見る多くの画面の中で、どの画面にDB上のどのデータが必要でどのデータが不要かを設計すること。
    • 概念スキーマ
      • 開発者から見たデータベースのこと。主にER図のこと。
      • 概念スキーマの設計を「論理設計」ともいう
      • 概念スキーマの具体的な要素として「エンティティ」「属性」「リレーションシップ」「製約」がある。
        • エンティティ
          • ERで書くあの矢印で繋がってる四角箱。
          • 基本的にDBテーブル単位
          • 「顧客」「注文」「製品」などなど
        • 属性
          • エンティティが持つプロパティのこと。言い換えるとDBテーブルが持つカラムのこと
          • 「顧客ID」「名前」「住所」などなど
        • リレーションシップ
          • ER図でいうところの1体1とか1体多とかの話のこと
        • 制約
          • DBテーブルのDDL描くときのあの制約のこと。
          • 「主キー」「not null」「check」「unique」などなど
    • 内部スキーマ
      • データを一元管理する層。データベースサーバのこと。「物理設計」ともいう
      • 正直あんまピンときてない。
    • エンジニアの端くれになろうと思うのなら、マニュアルを熟読することを面倒に思ってはいけない。

      2章

  • 概念スキーマ=論理設計。論理=論理手して筋が通っているという意味ではなく、物理層の制約にとらわれないということ。DBの実装レベルの話が無関係であること

    • 物理層の設計とは、DBサーバーのCPUパフォーマンス、ストレージ(一般的にはハードディスク)のデータ格納場所、使っているDBMSのデータ型やSQL構文などの具体的かつ実装的なレベルの条件のこと。
  • 論理設計のステップは→エンティティの抽出→エンティティの定義→正規化→ER図の作成

    • エンティティとは
      • 現実世界におけるデータの集合体。「顧客」「社員」「店舗」など実態を伴うものと、「税」「注文履歴」「操作履歴」など、物理的な実態を伴わない概念としてのエンティティがある。
    • エンティティの抽出
      • 作るシステムにどのようなエンティティ(=データ)が必要かを考える工程。どういうデータを扱う必要があるか。なので顧客やシステム利用者と要件を詰めていく必要がある。
    • エンティティの定義
      • 各エンティティ(=テーブル)が実際にどのような値(カラム)を持つのかを定義すること。
    • 正規化
      • エンティティについてシステムの利用がスムーズになるようにすること
      • 論理設計の結果を受けて、データを格納するための具体的な格納方法や領域を決めること。
  • 物理設計のステップは「テーブル定義」「インデックス定義」「ハードウェアのサイジング」「ストレージでの冗長構成決定」「ファイルの物理配置決定」
    • テーブル定義
      • 論理設計で作った概念スキーマ(=ER図)をもとにDBMS内に格納するテーブルの単位に変換していくこと。
      • 論理設計で作ったものを論理モデルといい、物理設計でのモデルを物理モデルという。
    • インデックス定義
      • ほんの索引で探した方がより早く探せるようにインデックスを定義する
    • ハードウェアのサイジング
      • システムで使うデータサイズを見積り、それに十分な容量のストレージを選定すること(キャパシティ)。サービス終了時のデータ量を見積もってストレージの容量を選定 これには2つのアプローチがあり、安全に余裕を持ったサイジングをするか、もしくは容量が足りなくなっても簡単に容量を追加できる構成にしておく方法。 後者のような容量が足りなくなった時など、負荷のかかった時に柔軟にシステムを増強できる性質を拡張性という
      • またシステムが十分に動くだけのCPUやメモリのスペックの選定など(パフォーマンス) -性能要件→システムなどが期待通りに動作するための必要なパフォーマンスの条件や基準のこと。これには主に2つの指標がある
          - 処理時間(何秒以内に処理が終了すること)=どれだけ早いか
          - スループット(単位時間あたりにどれだけの量の処理をこなせるか)=どれだけ多いか
        
    • ストレージでの冗長構成決定
      • 冗長=何か障害があった時のために余裕を持たせておくこと。
      • 可能な限り高い障害耐性を持つシステムを構築する必要がある
      • ここで出てくるのがRAID
        • RAID→redundant array of independent disks(独立したディスクの冗長配列)
        • 基本の考え方は複数のディスクにデータを書き込むようにして、一方のディスクが壊れても残りのディスクが生きていればデータを失わないように保全するというもの
          • RAID0→データを複数のディスクにバラバラに保存すること データがいろんなディスクに分散してるので、こっちのディスクのデータを処理する間に同時にこっちのディスクに入ってるデータを処理したりできる→同時に処理が進む。→処理速度アップする。ただディスクがどれか1つでも壊れると復元できない。冗長性ゼロ。 逆にもし1つのディスクし火なくてそこに全部データ入ってたら、どのデータを処理するにしてもその1つのディスクに行くので、順番に処理を待つ必要がある。
          • RAID1 特徴はミラーリング。同じデータをもう一つのディスクにまるまるコピーする。なのでかたほうのディスクが壊れてもデータを復元できる。譲渡ゆ性が2倍。しかし書き込みの際にディスクを2つとも毎秋更新しないといけないので処理速度は微妙になる
          • RAID5→最低ディスク3本で構成する。ディスクが壊れても残ったディスクとパリティがあれば復元できる。
          • RAID10 RAID0,RAID1のいいとこどり。
  • 可用性とは、システムやネットワーク、データなどが障害なく継続的に稼働し、必要なときにいつでも利用できる状態であることを指します可用性を示す指標として稼働率(%)が用いられ、システムが稼働している時間と停止している時間の割合で算出されます。たとえば、あるシステムが10時間稼働し、そのうち7時間が稼働していた場合、稼働率は70%となる。

  • クラスタシステムとは、複数のサーバをネットワークで接続して1つのシステムとして運用するシステム のこと。サーバの冗長システム構築することをクラスタリングと言う。 クラスタシステムでは、1台のサーバで障害が発生しても、別のサーバで通信を継続して処理できるのでシステム全体が停止することなく、障害復旧の交換作業中などの間もサービスを提供することができる。 クライアントからは1代のマシンに見えるようにする。クラスタの前にロードバランさを挟み、クラスタ内のノードらに分散させて処理する。クラスタリングは、水平方向のスケーリングとも呼ばれる。需要が増加した場合、クラスタにサーバを追加するだけで、ロード バランサがより大きなサーバ グループにリクエストを分散する。対照的に、垂直方向のスケーリングではサーバを、1秒あたり大量のリクエストを処理できる、より強力なサーバに置き換える必要があり、通常は高価なアプローチになる。

    3章

  • テーブル名は英語ならば複数形、複数名詞でかける。
  • 外部キーは人間の親子関係と同じ。外部キーを持つテーブルが子。参照先が親。親のテーブルはこの状態を機にすることなく変更可能。
  • 第一正規形(=1セルにつき値は1つ)

    • 1つのセルには1つのデータしか含まない(スカラ値という。)
    • 1テーブルには1エンティティ分の情報を入れるべき。
    • なぜRDBではスカラ値しか許されないか。
      • 主キーを定義しても各列を一意に特定できないため。
    • 関数従属性
      • ある値が決まれば他の値も決まること。y=axのようなこと
    • 以下のような非正規形から
    顧客名 商品名 購入日
    田中 本, ノート, ペン 2024-09-10
    鈴木 パソコン 2024-09-11
    佐藤 ノート,ぺん 2024-09-12
    • 以下のような第一正規形に変えることで、「田中さん」「佐藤さん」の行が分割されて、それぞれの商品が個別の行として特定できる
    顧客名 商品名 購入日
    田中 2024-09-10
    田中 ノート 2024-09-10
    田中 ペン 2024-09-10
    鈴木 パソコン 2024-09-11
    佐藤 ノート 2024-09-12
    佐藤 ペン 2024-09-12
  • 第二正規形(=部分従属を解消し完全従属のみのテーブルを作る)

    • 複合キーに対してそれ以外の列が片方のキーにしか従属しない場合は部分従属になってしまうのでそれらを完全従属に修正する
    • 部分従属の解消にはテーブルの分割
    • 言い換えると異なるエンティティはテーブルレベルでもちゃんと分離すること
    • 第2正規化のメリット
      • 部分従属が残ったままだと、修正箇所が増える(=データ変更時に修正箇所が多くなる&情報のご登録が発生)点を防げる
    • 正規化の逆操作は結合処理

    • 第二正規形に準拠していないテーブルの例

      • このテーブルでは、主キーである「顧客名+商品名」に対して、「顧客住所」が「顧客名」にのみ依存しており、部分関数従属が発生しています。
    顧客名 商品名 購入日 顧客住所
    田中 2024-09-10 東京都渋谷区
    田中 ノート 2024-09-10 東京都渋谷区
    田中 ペン 2024-09-10 東京都渋谷区
    鈴木 パソコン 2024-09-11 東京都新宿区
    佐藤 ノート 2024-09-12 東京都品川区
    佐藤 ペン 2024-09-12 東京都品川区
    • 2NFに準拠させたテーブル

    • 顧客情報テーブル

    顧客名 顧客住所
    田中 東京都渋谷区
    鈴木 東京都新宿区
    佐藤 東京都品川区
    1. 購入記録テーブル
    顧客名 商品名 購入日
    田中 2024-09-10
    田中 ノート 2024-09-10
    田中 ペン 2024-09-10
    鈴木 パソコン 2024-09-11
    佐藤 ノート 2024-09-12
    佐藤 ペン 2024-09-12

    部分従属を排除し、非キー属性が主キー全体に完全に依存するようにしました。!

  • 第三正規形

    • 推移的関数従属
      • 2段階の関数従属があること

    ### 第三正規形に準拠していないテーブルの例

    顧客名 顧客住所 郵便番号
    田中 東京都渋谷区 150-0001
    鈴木 東京都新宿区 160-0022
    佐藤 東京都品川区 140-0014

    このテーブルでは、「郵便番号」が「顧客住所」に依存していますが、「顧客住所」は主キーではありません。これが推移的従属の例です。

    ### 第3正規形に準拠させたテーブル

    1. 顧客情報テーブル
    顧客名 顧客住所
    田中 東京都渋谷区
    鈴木 東京都新宿区
    佐藤 東京都品川区
    1. 住所情報テーブル
    顧客住所 郵便番号
    東京都渋谷区 150-0001
    東京都新宿区 160-0022
    東京都品川区 140-0014

    非キー属性が他の非キー属性に依存しないように、住所情報を分離。

4章

  • テーブル(エンティティ)の数が増えていくと、テーブル同士の関係がわかりにくくなり、設計に支障をきたす。この問題を解決するために、テーブル同士の関係を記述する道具がER図
  • 「多対多」を「1対多」に分解するときに必要になるエンティティを「関連実態」と呼ぶ

5章

  • 正規化の功罪

    • 正規化=情報を複数のテーブルに分割させる行為なので、単独のテーブルだけでは必要な情報を取れることが少なくなる
    • よってSQL結合処理を行うのだが、結合は非常にコストの高い操作。結合するテーブル数、およびテーブルのレコード数が増えるほど処理時間がかかる。正規化によるパフォーマンスの劣化の多くがこの結合によるもの。
    • 正規化とパフォーマンスは強いトレードオフの関係にあるとおいうこと。つまり厳しく正規化すればパフォーマンスが悪化し、パフォーマンスを求め非正規化すれば、データの不整合が起きやすくなる。
    • 非正規化はあくまでも最後の手段。つまり十分に正規化された設計を諦めて良いのはパフォーマンスを向上させるためその他全ての戦略が要件を見たさない時だけ。
    • 逆に言うと最初は必ず正規するべきと言うこと。正規化の字数は高ければ高いほどよい。そして、他手段でのパフォーマンス改善が図れないかを検討し、最後の手段として正規化は行う。
  • 正規化以外の冗長性排除よるパフォーマンス劣化

    • サマリデータの冗長性排除によるパターン

      • 受注(ver1.0)

        受注ID 受注日 注文者名義
        0001 2012-01-05 岡野 徹
        0002 2012-01-05 浜田 健一
        0003 2012-01-06 石井 慶子
        0004 2012-01-07 若山 みどり
        0005 2012-01-07 庄野 弘一
        0006 2012-01-11 若山 みどり
        0007 2012-01-12 岡野 徹
      • 受注明細(ver1.0)

      受注ID 受注明細連番 商品名
      0001 1 カロン
      0001 2 紅茶
      0001 3 チョコ詰め合わせ
      0002 1 日本茶
      0002 2 ティーポット
      0002 3
      0003 1
      0004 1 アイロン
      0004 2 ネクタイ
      0005 1 チョコ詰め合わせ
      0005 2 紅茶
      0005 3 クッキーセット
      0006 1 牛肉
      0006 2 鍋セット
      0007 1
      • 上記のテーブルで、「受注日ごとの注文された商品数」を取得したいとき以下のSQLになる sql select 受注.受注日 COUNT(*) AS 注文数 FROM 受注 INNER JOIN 受注明細 ON 受注.受注ID = 受注明細.受注ID GROUP BY 受注.受注日
      • こちら結合をしているためパフォーマンス悪化。実際の運用では注文明細テーブルのレコード数は受注件数✖️注文1件あたりの平均商品数分のレコードができる。ので現実的には非常に大きなテーブルを結合吸うことになりパフォーマンスに影響が出る
      • そこで、受注テーブルに商品数(日毎の注文商品数をsumしたもの)
      • 受注(ver1.1)

        受注ID 受注日 注文者名義 商品数
        0001 2012-01-05 岡野 徹 7
        0002 2012-01-05 浜田 健一 7
        0003 2012-01-06 石井 慶子 1
        0004 2012-01-07 若山 みどり 5
        0005 2012-01-07 庄野 弘一 5
        0006 2012-01-11 若山 みどり 2
        0007 2012-01-12 岡野 徹 1
      • 商品数というサマリデータカラムを追加することで冗長性を持たせることで結合処理は必要なくない理、パフォーマンスが向上する。(推移的関数従属性ができてしまうので第3正規系ではなくなる。受注ID→受注日→商品数)

    • 選択条件の冗長性排除によるパターン

      • 上記の「受注(ver1.0)」と「受注明細」テーブルで受注日が2012-01-06 ~ 2012-01-07の期間に注文された商品の一覧を出力する場合 sql select 受注.受注ID 商品明細.商品名 FROM 受注 INNER JOIN 受注明細 ON 受注.受注ID = 受注明細.受注ID WHERE 受注.受注日 BETWEEN '2012-01-06' AND '2012-01-07';
      • こちらのSQL文は問題の解としては問題ないがやはり結合しているためコストが高い。
      • 受注明細(ver1.1)

      受注ID 受注明細連番 商品名 受注日
      0001 1 カロン 2012-01-05
      0001 2 紅茶 2012-01-05
      0001 3 チョコ詰め合わせ 2012-01-05
      0002 1 日本茶 2012-01-05
      0002 2 ティーポット 2012-01-05
      0002 3 2012-01-05
      0003 1 2012-01-06
      0004 1 アイロン 2012-01-07
      0004 2 ネクタイ 2012-01-07
      0005 1 チョコ詰め合わせ 2012-01-07
      0005 2 紅茶 2012-01-07
      0005 3 クッキーセット 2012-01-07
      0006 1 牛肉 2012-01-11
      0006 2 鍋セット 2012-01-11
      0007 1 2012-01-12
      • 上記のテーブルに変更することで、先ほどのSQLから結合は必要なくなる。
      • ただ、受注ID→受注日という部分従属性が生まれてしまうため第2正規形ではなくなってしまう。
      • このように非正規化をすることで検索のパフォーマンスの向上が図れる
      • ただ、大原則として正規化は可能な限り高次元にしておくものであり、非正規化によるパフォーマンス向上は最後の手段である。
    • 上記の非正規化によるリスクを見ていく

      • 更新時のデータ不整合はご存知だろう
      • 更新時のパフォーマンス
        • 受注テーブルに商品数のカラムを追加したため、受注テーブルに新規レコードを毎登録する際は毎回商品数カラムの値を集計して登録する必要がある。
        • また、受注したとしても、ユーザが注文キャンセルした場合のことも考えると、受注テーブルへの定期的な更新が必要。こうした更新処理の負荷も考慮する必要がある。

          6章

  • インデックス

    • インデックスを使うと、本の索引のように欲しいデータに直接アクセスできる
    • SQLには「欲しいデータ」を書き「データ取得の道筋」は書かない。(whatを書きhowは書かない)
    • そういう意味で、SQLはカーナビと同じである。(「ここにいきたい」を入力するだけで、どのようにいくかはお任せ。)
    • SQLではデータの取得経路は自動で決定してくれる
    • このようのその存在をユーザが意識しなくても良い、でもそれがないとダメな「空気」のような性質を「透過性」という。
  • B-tree

    • 頻繁に利用するインデックスの1種
    • B-treeの長所は平均点の高さ
    • インデックスを付与するテーブルは大規模なものにする(1万レコードが目安)
    • インデックスを作成するときはカーディなりの高い列に対して作成する
      • カーディナリ = その列に入る値の種類(性別列なら「男」「女」「その他」があルのでカーディなりティは3。日にちであれば1年で365のカーディなりティがある)
      • その列を指定したときに5%未満に絞れるカーディなリティの列にインデックス付与
      • ただし値が平均的に分散していることが前提
  • オプティマイザと実行計画
    • テーブルにアクセスする流れは以下の通り
      • ユーザがSQL実行 → パーサ → オプティマイザ →カタログマネージャ→ オプティマイザ →テーブル
      • パーサ(parser)とはSQLが適切な構文であるかどうかチェックする
      • オプティマイザとはSQLのアクセスパス、すなわち実行計画を決めるもの
        • 実行計画を決める際に必要になるのが統計情報。オプティマイザはカタログマネージというモジュールに統計情報の照介をかける。統計情報を受け取ったのち、たくさんの経路の中から最適な最短経路を選択する
  • ビットマップインデックスとハッシュマップインデックス

7章

  • 論理設計のやってはいけない(真似してはいけない設計のパターン)
    • プログラミングというのは設計をプログラミング言語に翻訳する作業であって、プログラミング自身がシステムの品質を左右するすることは相対的に稀
    • 戦略の失敗を戦術で取り返すことはできない
    • 配列方による非スカラ値
      • 以下、配列型の値のあるINSERT文 sql INSERT INTO 〇〇 VALUES('0001', '田中', '{太郎,花子,梅子}')
      • 配列型は利用しない。第一正規形を守る
    • ダブルミーニング

      • 1つの列に複数の意味の値を入れてしまうこと | 年度 | 社員名 | 列1 |列2| |--------|--------------|------------------|---------| | 2001 | 田中 | 178 |67 | 2001 | 石井 | 167 |48 | 2001 | 大塚 | 166 |52 | 2001 | 八神 | 182 |21 | 2001 | 江部洲本 | 158 |33 | 2001 | 泊 | 155 |22
      • 列1は大体「身長」の列だとわかる。第2の列は体重列、、と思いきや違う。年齢?? といった感じで途中で意味が変わっている
    • 単一参照

      • 単一参照テーブル(雑多なコード体系の寄せ集め)

      コードタイプ コード値 コード内容
      comp_cd C0001 A 商事
      comp_cd C0002 B 化学
      comp_cd C0003 C 建設
      comp_cd C9999 Z 興業
      pref_cd 01 北海道
      pref_cd 02 青森
      ... ... ...
      pref_cd 47 沖縄
      sex_cd 0 不明
      sex_cd 1
      sex_cd 2
      sex_cd 9 適用不能
      • 同じ構造のマスタテーブルを一緒のテーブルにまとめちゃえばいいじゃん!な発想から生まれたバッドノウハウ
      • 上記のテーブルは3つの同じ構図のマスタ(どのマスタも「ID」+「名称」の構造)を寄せ集めたもの
      • なんのマスタかを判別するのに「コードタイプ」列を追加している。
      • メリット
        • マスタが減るのでER図やスキーマがシンプルに。
        • コード検索のSQLの共通化が可能
      • デメリット
        • 各マスタのデータ構造(「ID」+「名称」)は同じではあるが、値の文字列の長さマスタによって当たり前だが変わるので、余裕を持ってかなり大きめの可変長文字列の長さを指定する必要がある
        • ER図はシンプルにはなるが、可読性は下がる
        • 1つのテーブルに集約するので種類と数の多さによっては検索パフォーマンスが下がる
    • テーブル分割
      • 水平分割(レコード単位でテーブルを分割すること) ### 売上げ

        年度 会社コード 売上げ (億円)
        2001 C0001 50
        2001 C0002 52
        2001 C0003 55
        2001 C0004 46
        2002 C0001 52
        2002 C0002 55
        2002 C0003 60
        2002 C0004 47
        2003 C0001 46
        2003 C0002 52
        2003 C0003 44
        2003 C0004 60
        • 上記のテーブルを以下のように分割すること

        ### 売上げ(2001) | 年度 | 会社コード | 売上げ (億円) | |-------|------------|---------------| | 2001 | C0001 | 50 | | 2001 | C0002 | 52 | | 2001 | C0003 | 55 |

        ### 売上げ(2002) | 年度 | 会社コード | 売上げ (億円) | |-------|------------|---------------| | 2002 | C0001 | 52 | | 2002 | C0002 | 55 | | 2002 | C0003 | 60 | | 2002 | C0004 | 47 |

        ### 売上げ(2003) | 年度 | 会社コード | 売上げ (億円) | |-------|------------|---------------| | 2003 | C0001 | 46 | | 2003 | C0002 | 52 | | 2003 | C0003 | 44 | | 2003 | C0004 | 60 |

        • SQLが常に1年毎にしか「売り上げ」テーブルにアクセスしないのであれば分割することでパフォーマンスの改善になる
        • 欠点
          • テーブルのスキーマを変更する必要がある場合、全ての年ごとのテーブルに対して更新する必要がある
          • 各年のテーブルに対して同じクエリを書くので、コードが冗長になる(同じようなクエリを複数回書く必要がある)、メンテナンス性が低下(クエリに修正が入る場合に多くの箇所を修正しないといけなくなる)
          • 毎年テーブルを新たに作る必要がある

            8章

  • グレーノウハウ
    • 主キーが決められない、または主キーとして不十分な場合
      • そもそも主キーになるような一意になるキーがない
      • 一意になるキーはあるが、サイクリック
      • 一意キーはあるが、途中で対象が変わる
    • サロゲートキー
      • surrogate · 代理人{だいりにん}、代行人{だいこう にん}
      • 実際のデータから導出されるものではなく、システムが人工的に生成する主キー。例えば、オートインクリメントの整数やUUIDなど
      • 一般的な原則としては極力代理キーの使用は避け、自然キーによる解決を図るべき。 それは代理キーは論理的に不要なキーであるため。
      • オートナンバーを主キーに使う=データモデルを書いている証拠
    • 自然キーにより解決するためには、「タイムスタンプ」「インターバル」を使って複合キーにするものがある。
    • オートナンバリングについて
      • シーケンスとID列ではシーケンスの方が柔軟で拡張性が高い。
      • 自動採番をアプリケーション側で実装するのは「車輪の再発明」なのでNG。
    • ビューを使用する際には、ビューの背後にデーブルが存在することを意識すること。この意識が薄いと、多段ビューによりパフォーマンス悪化につながる。
    • 多段ビューのような過度に複雑な作りはシステムをダメにする。「KISSの原則」に従うべき。
      • KISSの原則;Keep It Simple, Stupid.(単純にしておけ、馬鹿者)
    • データクレンジングの重要性
      • 一意キーの存在しないデータは不適切なキーを生むので、データクレンジングが必要。
      • 名寄せをサボると、ダブルマスタを産むのでデータクレンジングが必要

9章

  • 隣接リストモデル

    • 同じテーブルの違うカラムの値を自己参照しているもの。(上司カラムにその社員の社員コードを設定している)

      組織図

      社員 上司
      アダム なし
      イブ アダム
      セト アダム
      カイン セト
      アベル セト
      ノア セト
      ヨブ カイン
  • 入れ子集合モデル

    • 各ノードを円とみなす。子の円は親の円の中に存在する。
    • ルートノード一番外側の円なので(left_valueが最小かつright_valueが最大のレコードがルートリーフ)
    • リーフノード=自身の円の中に円が存在しない。

      家電テーブル

      id name left_value right_value
      1 家電 1 10
      2 テレビ 2 5
      3 スマートテレビ 3 4
      4 冷蔵庫 6 7
      5 洗濯機 8 9
    • ルートを求める sql select * from 家電テーブル where left_value=1;

    • リーフを求める ```sql

      ```

難しかったこと

  • 正規化について 理論としては理解できたものの、どこまで細かく分割するかの判断は難しいと感じました。特に、正規化によってデータの冗長性は排除できるものの、パフォーマンスや可読性とのバランスをアプリケーションの要件を元に加減する必要のある点が難しいかなと感じました。

  • ある程度業務で実際にやってみないとイメージがつきにくい部分があると思うので、業務経験を積む&都度繰り返し読むようにするといいなと感じました