LoginSignup
12
10

More than 3 years have passed since last update.

素人がNextjsでポートフォリオをデプロイするまでの物語(概要編)

Last updated at Posted at 2019-12-07

はじめに

ずっとポートフォリオを作ろうと思ったまま時だけが過ぎていった私ですが,もう少しで学生が終わってしまう現実にようやく感じ始め,いよいよポートフォリオ作っておいた方がよくね...と思い,やっと手が動きました.
この記事ではその一連のプロセスを概要として記していきます.プロセスそれぞれはすでにたくさんの記事がQiitaに存在しているので,重複していると判断した部分については詳細を書いていません.参考にした記事やサイトは載せるのでそれを参考にしたり調べたりしてください.
気が向いたら別の記事としてピックアップするかもしれません.

投稿者のレベル

大学では計算シミュレーションをしているので,matlabやpython(特にnumpy,scipy),cのような数値計算に利用される言語はよく触っています...が,web系のプログラミングはあまり触っていない(少なくとも作品はない)です.タイトルではweb系においてという意味で素人としています.

やってみて実感しましたが,やっぱり用途が違えば同じプログラミングと言えど感覚が違うと思いました.
要するに,てこずりました.

この記事を書こうと思った背景

Qiita様には日々お世話になっておりますが,細かい作成プロセスが記述されている(要するにやらかした部分を記している)記事が比較的少ないと思っていたので(そもそもこの内容がQiitaのルールに当てはまるのかが微妙なのかもしれない,でも書くが),私がつまづきまくった過程を書きましょう(恥さらしをしよう)と言った具合です.

使用した言語,フレームワーク,ライブラリ

  • TypeScript(プログラミング言語)
  • React(フレームワーク,UI関連)
  • emotion(CSS in JS ライブラリ)
  • Next.js(フレームワーク,Webアプリケーション関連)
  • Storybook(UIコンポーネントのカタログ)
  • material-ui(UIコンポーネントのライブラリ)
  • GitHub pages(ポートフォリオのホスティング先)
  • GitHub Actions(ワークフローの自動化)

使用した理由はこちら
TypeScript
1. 型が欲しかったから
2. 最近のWebサイト作成方法やReactについて調べるたびにtypeScriptが出てくるから

React
候補としてはAngular, React, Vueでしたが,調べた実感としてReact, Vueが多かったと思います,なのでこの2択になりました.ちょっと触ってみてReactの方がしっくりきたのでReactにしました(最初はVueにしようと思ってました.)

emotion
せっかくなら全てJavaScript(TypeScript)にしてしまおうと思ったから.styled-componentsというものもあったが,emotionはいろいろな書き方ができるし,後発なので良いとこどりしてそうだったから.慣れるまでは大変だった.最終的にはstyled記法で書いています(styled-componentsでもよかった感).

Next.js
ReactとWebサイト作成で調べてたらNext.jsがいっぱい出てきたから.正直私が作ったポートフォリオ程度だったら使う必要がなかった感はある.

Storybook
知ったきっかけはQiitaでReactやNext.jsを調べていた時にちょいちょい紹介されていたから.初心者に取っては構築するコストが多少ありますが,自作したコンポーネントの表示の確認をするのにみやすいと思います.なくとも問題はありません.

material-ui
良いUIライブラリあるんだったら自作しないでそれ使いますよね,はい使います.

GitHub pages
GitHubを使ってバージョン管理しているので,そのままGitHubでホスティングできるならばわざわざ別のホスティング先を見つけるのは労力がかかるのでこれにしました,と言っても調べれば様々なサービスが見つかるので好きなもので良いと思います.

GitHub Actions
これを設定すればpushするたびに自動でデプロイまでしてくれます.ただ,正直理解しきれていません.以下の記事を参考にしました.というかほぼ内容通りに進めました. これも必須ではありませんが,ちょうどActionsが話題になっていたので触れてみようということで使用しました.

React Next アプリを GitHub Actions で GitHub Pages にデプロイ

ストーリー

作成物です.先に申し上げますが,現時点で作成されている状態のもので,まだ完成させてません.とりあえずです.ちょっと色々つまづきすぎて疲れました.
スマホだとちょうどよいサイズ感ですが,PCだとサイズ大です.調整しなければ...
ポートフォリオ

GitHub pages

GitHub pagesを使うので今回のプロジェクト用のリポジトリ(私の場合はリポジトリ名:"faluna.github.io")を作成する.
GitHub pagesには主にプロジェクト,ユーザそしてOrganizationと呼ばれる3種類のサイトが存在するようです.今回はポートフォリオということでユーザサイトを選びました.ユーザサイトはリポジトリ名を以下のようにして作成すればよいと記されています.

ユーザおよび Organization サイトは、常に .github.io または .github.io と名付けられたリポジトリから公開されます。 カスタムドメインを設定していない場合、ユーザおよび Organization サイトは http(s)://<username>.github.io または http(s)://<organization>.github.io から利用できます。

From https://help.github.com/ja/github/working-with-github-pages/about-github-pages

参考

Nextjsのセットアップ

参考欄に載せているNexjsのチュートリアルやDocsに記されているSetup項の通りにコマンドを打つだけです.TypeScriptを使用したので,その導入だけ追加の作業があります.ただ,その辺りの記事はたくさんあるので調べればたくさんヒットするので大丈夫だと思います.私は以下の記事を参考にしました.
Next v9 + TypeScript の環境構築

参考

CSS in JS ライブラリ emotionの導入

私はstyled記法で記述していたので@emotion/core@emotion/sytledの2つをyarnを使ってパッケージをインストールしました.あと,必要なのかどうかはわかりませんでしたが,babel-plugin-emotionもインストールしました.

よくわからない点

  • babel-plugin-emotionが必要なのかどうかがわからない
    追加の機能?が使えるようになるみたいですがバージョン8以降は不要みたいなことが書いてもあるのでいらないかもです.詳しくは参考のページを読んでください(私は力つきました)

参考

Storybook導入

これは作ったコンポーネントをカタログみたいな形式で表示できるものなので,よくわからなかったりコンポーネントの表示確認がいらなければ導入しなくとも良いと思います.私は気になったので導入してみました.
のちに書きますが,これのおかげでかなりつまづきました.そのくせあまり使いこなせなかったと自覚してます(というか,コンポーネントをあまり自作してない).
余裕があれば導入しても良いと思います.ただ,js界隈に慣れていない人は分からなくなるような気がします.(最終的には使えるところまで進みましたが,かなりの労力と時間を費やしました)

つまづいた点

  • TypeScriptを使っている人はどうすれば?
    Qiitaの記事を参考にするか,複数の方法が出てきたら,とりあえず公式のTypeScript configの通りに進めたら導入はできました,導入はね

参考

Material-UI導入

yarn addコマンドで@material-ui/coreをインストールしてくださいiconも使用するのであれば@material-ui/iconsも.

参考

GitHub Actionsの設定

これはデプロイの自動化のためなのでやらなくとも問題ないと思います.
個人的に触れてみたかったので,この機会に触ってみました.
気になる方は@peaceirisさんの以下の記事を読まれると良いと思います.
私はこの記事に沿ってActionsを使いました.
React Next アプリを GitHub Actions で GitHub Pages にデプロイ

つまづいた点

  • ".github"ディレクトリと".git"ディレクトリを間違えた
    しっかり読んだり調べたりすればわかることなのですが,".github"ディレクトリを作成し,その中に"workflow/[filename].yml"を配置します.しかし,私は   $git initなどで作成される".git"ディレクトリの中にそれを作成してデプロイに失敗しました. しっかり読みましょう,調べましょう.

参考

ディレクトリ構成

基本的にはNextjs Docsを読めば書いてあるので,それを元に配置しているだけです.

  • srcディレクトリを作成し,それ以下にpagesディレクトリなどを配置.
  • pngやsvgのような画像はpublicディレクトリに配置.

加えて,Atmic DesignというUIデザイン手法というものを取り入れたのでsrcディレクトリ以下のようないくつかのディレクトリを作成しました.

src/
- atoms
- molecules
- organisms
- templates
- pages

Atomic Designは以下を参考にしながら取り入れました.
Atomic Design について調べて見た
https://blog.spacemarket.com/code/atomic-designを使ってreactコンポーネントを再設計した話/

ちなみに,書いてる途中で見つけたのですが,Yahooも似たような構成(React, TypeScript, Atomic Design)でトップページを作り変えたようです.
https://techblog.yahoo.co.jp/entry/20191203785540/

コンポーネント作成

たいして作成していません.atomsやmoleculesに当たるコンポーネントはMaterial-UIで提供されているUIを使えばいいので,Atomsはロゴのようなsvgのコンポーネント化したfileしか作成してません.MoleculesもMaterial-UIをラッパー化したようなコンポーネントしかありません.一部ですが,AtomsやMoleculesにあたるコードを以下に載せておきます.

src/atoms/LogoIcon.tsx
import React from 'react';
import ReactSVG from 'react-svg';
import CircularProgress from '@material-ui/core/CircularProgress';
import styled from '@emotion/styled';

const StyledReactSVG = styled(ReactSVG)((props: ILogoSvgIconProps) => ({
  width: props.width,
  height: props.height,
  margin: props.margin,
  padding: 'auto'
}));

export type ILogoSvgIconProps = {
  src: string;
  className?: string;
  width: string;
  height: string;
  margin: string;
};

export default function LogoSvgIcon(props: ILogoSvgIconProps) {
  const { src, className, ...other } = props;
  return (
    <StyledReactSVG
      src={src}
      fallback={() => <div>Error!</div>}
      loading={() => (
        <div>
          <CircularProgress />
        </div>
      )}
      className={className}
      {...other}
    />
  );
}

Reactでsvgを使うためにreact-svgを使用してます.widthheight,marginはpropsで指定するようにしてます.

src/molecules/GitHubIconButton.tsx
import React from 'react';
import GitHubIcon from '@material-ui/icons/GitHub';
import Link from '@material-ui/core/Link';
import IconButton from '@material-ui/core/IconButton';
import styled from '@emotion/styled';

const StyledIconButton = styled(IconButton)`
  width: 100%;
  height: 100%;
`;

export default function GitHubIconButton() {
  return (
    <div>
      <Link href="https://github.com/faluna">
        <StyledIconButton size="medium">
          <GitHubIcon style={{fontSize: '2.5rem'}} />
        </StyledIconButton>
      </Link>
    </div>
  );
}

Atomic Design手法的にはこれもpropsでwidthheightを指定できるようにした方がよいと思います.テスト時のここを仮確定させた状態から更新するのを忘れてました.

さて気を取り直し,私の場合,Organismsからしっかりコンポーネントを組みながら上位コンポーネントを作成していきました.Organismsの一例です.基本的にはAtomsとMoleculesで作ったコンポーネントを使い作成していきます.

src/organisms/IconGridList.tsx
import React from 'react';
import GitHubIconButton from '../../molecules/GitHubIconButton';
import TwitterIconButton from '../../molecules/TwitterIconButton';
import Grid from '@material-ui/core/Grid';
import styled from '@emotion/styled';

const StyledGridContainer = styled(Grid)((props: IIconGridListProps) => ({
  width: props.width,
  height: props.height,
  margin: props.margin,
  padding: 'auto'
}));

const StyledGridItem = styled(Grid)({
  padding: '0',
  height: '100%'
});

type IIconList = typeof GitHubIconButton[];

export type IIconGridListProps = {
  width: string;
  height: string;
  margin: string;
  className?: string;
};

const iconList: IIconList = [GitHubIconButton, TwitterIconButton];

export default function IconGridList(props: IIconGridListProps) {
  return (
    <StyledGridContainer container justify="center" spacing={4} {...props}>
      {iconList.map((Value, index) => (
        <StyledGridItem key={index} item>
          <Value />
        </StyledGridItem>
      ))}
    </StyledGridContainer>
  );
}

Templatesではサイトページの枠組み(サイズも含めて)を作っていきました.ただ,正直Templatesの作り方がよく分からなく無理矢理にサイズの変数(props)はここで決めて,画像のような要素をpropsでPagesから注入させるようにしています.正しい運用していないかもしれません.だって分からないものは分からない.

上記のYahooのブログを読むとどうやらHeaderFooterのようなPagesで使い回すようなコンポーネントを配置するようです.ただ,YahooではTemplatesを採用していないと記載されています.

src/templates/TemplateIndex.tsx
import React from 'react';
import LogoSvgIcon from '../../atoms/LogoSvgIcon';
import IconGridList from '../../organisms/IconGridList';
import Footer from '../../organisms/Footer';
import Header from '../../organisms/Header';
import NameList from '../../organisms/NameList';
import styled from '@emotion/styled';

const StyledDiv = styled.div({
  width: '90%',
  height: '100%',
  margin: 'auto',
  textAlign: 'center'
});

const overrideHeaderStyle = {
  width: '100%',
  height: '30%',
  margin: 'auto'
};

const overrideSvgIconStyle = {
  width: 'auto',
  height: '20%',
  margin: 'auto'
};

const overrideNameListStyle = {
  width: '100%',
  height: '10%',
  fontSize: '1.5rem',
  margin: 'auto'
};

const overrideIconGridList = {
  width: '100%',
  height: '30%',
  margin: 'auto'
};

const overrideFooterStyle = {
  width: '100%',
  height: '10%',
  margin: 'auto'
};

export type IIndexTemplateProps = {
  src: string;
  className?: string;
};

export default function IndexTemplate(props: IIndexTemplateProps) {
  const { src, className } = props;
  return (
    <StyledDiv className={className}>
      <Header {...overrideHeaderStyle} />
      <LogoSvgIcon src={src} {...overrideSvgIconStyle} />
      <NameList {...overrideNameListStyle} />
      <IconGridList {...overrideIconGridList} />
      <Footer {...overrideFooterStyle} />
    </StyledDiv>
  );
}

最後に,Pagesです.これはNextjsでも決まっているPagesディレクトリに対応するもので,ここに配置されているtsxファイルがサイトのページに直接対応するようです.なので,Atomic DesignとNextjsは相性がよいと書かれている記事もあったような気がします.どの記事か忘れました.
と言っても,私の場合はTemplatesで作ったものに画像ファイルなどをpropsに渡すだけです.

src/pages/index.tsx
import React from 'react';
import IndexTemplate from '../templates/IndexTemplate';

export default function Index() {
  return <IndexTemplate src="/logo.svg" />;
}

つまづいた点

  • publicディレクトリに配置されているファイルのパス名が"/logo.svg"か"../../public/logo.svg"か?
    どちらでも動きます.(public下に配置していれば)
    Nextjsにおいてpublicディレクトリは特別扱いのようでルート指定"/"(ルートでよいのか?)でもしっかり見つけてくれるようです. https://nextjs.org/blog/next-9-1

  • インポートした自作コンポーネントをstyled記法でCSSを上書きしても反応ないんですが...
    最初はpropsで子コンポーネントのmarginなどを指定するのではなく,インポートしたコンポーネントをemotionのstyled記法でCSSを上書き指定していたのですが(この自由にインポート先で上書きするやり方はよろしくない気がする)何度確認しても反映されている気配がしませんでした.
    原因はclassNameが被インポート側のコンポーネントにpropsで渡されていないからでした.CSSはclassNameに一致する(というかCSSはタグやid,class,その他諸々...でスライルを指定する)ものをスタイルするということをなぁなぁで理解していた私の責任でした.JSで書いているとあまり意識しないというか忘れる.本来はCSSの基本だから常識なんでしょうけど...
    上記のコードを見るとわかりますが,classNameをpropsに含めています.はい,そうです,このようなことがあったからとりあえず渡すようにしています.
    結局propsでmarginなどを渡すようにしたのでなくともよいですが

参考

完成(と言っておきながら完成してない)

もう一度ポートフォリオ先を載せておきます.
ポートフォリオ
私の場合だと,GitHub Actionsですがリモートリポジトリにpushしてもすぐにデプロイされるわけではないので,気長に待つとよいです.Actions欄でdeployされたか確認できます.
それと,Actionsの方がデプロイされた後,URL先にアクセスしてもページが更新されていない場合もありました.コードを間違えたかとも思いましたが,そうではなくPagesの方の反映も少し時間がかかるみたいでした.
早く確認したい場合はホスト先のURLに続けてコミット番号(7桁のやつ)?をつけてアクセスすれば見れるみたいです.

最後に

本当はこんなもんじゃないくらいのエラーの連続でした.書きたかったんですけど,さらに読みにくくなりそうだし書き疲れたので断念しました.Storybookについては導入部分しか触れてないし
気が向いたら別記事にして書こうと思っています.書きたい気持ちはあります.この記事のタイトルに概要編と付けたくらいですから.

本当の最後にこの記事を読んでくださりありがとうございました.
どちらかというと,アルゴリズムやシミュレーションのような数学を駆使する設計の方が得意なのでそっちの方も書いていくかもしれないです.ちなみにシステムプログラミング(やシミュレーション)用途ではRustを使用しています.
ではまたの機会に

参考文献

12
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
10