LoginSignup
0
0

More than 1 year has passed since last update.

App PORT製作のまとめ

Last updated at Posted at 2022-05-27

URL

テスト用アカウント

email: test@test.com
password: Qwe123123
※自由に投稿していただいて構いません

概要

他の人が製作したアプリの閲覧、
またはユーザー登録することで自分の製作したアプリを投稿できます。

製作理由

私がポートフォリオを作成しようと考えた時、
他の人たちはどのようなサービスを製作したのか知りたかったのですが、
まとまったサイトがなく不便であったため製作しようと考えました。

利用方法

  • All App
    縦スクロールで、それぞれのユーザーが投稿したアプリを閲覧でき、
    横スクロールではそのユーザーが投稿した他のアプリを閲覧できます。
  • Login
    ユーザーの新規登録、またはログインができます。

ログイン後

  • My App
    自分が投稿したアプリを閲覧できます。
    このページでアプリ情報の編集と削除ができます。
  • Add New App
    画像、タイトル、説明、URL を入力し、新しくアプリを投稿できます。

使用技術:フロントエンド

言語

  • HTML&CSS
  • Javascript(Typescript)

フレームワーク&ライブラリ

  • React
  • Redux
  • React Router
  • tailwindCSS

モジュールバンドラー

  • webpack

使用技術:バックエンド

言語

  • Javascript(Typescript)

フレームワーク&ライブラリ

  • Node.js(Express)

データベース

  • MongoDB(ユーザー情報、アプリ情報の保存)

ストレージ

  • AWS S3(画像の保存)

セキュリティ対策

  • helmet(HTTP ヘッダー関係)
  • bcrypt(パスワードをハッシュ化)
  • passport-local / passport-jwt(ユーザー認証)

実装予定

  • Google 認証
  • 登録メールアドレスの URL 認証

製作までの道のり

Webpackで環境構築(React+TypeScript+Babel+ESLint+Prettier+TailwindCSS)

前回webpackでCreateReactAppを使用しないReact環境の構築をしたが、
アプリを制作する上で新たに追加・変更した点があるため再度環境構築しながらもう一度まとめる。

npm 初期設定

mkdir webpack_React
cd webpack_React
npm init

webpack関係 インストール

npm i -D webpack webpack-cli webpack-merge webpack-dev-server
npm i -D html-loader css-loader postcss-loader babel-loader @svgr/webpack
npm i -D html-webpack-plugin css-minimizer-webpack-plugin mini-css-extract-plugin terser-webpack-plugin eslint-webpack-plugin dotenv-webpack

babel関係 インストール

npm i core-js
npm i -D @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript

eslint関係 インストール

npm i -D eslint eslint-plugin-react eslint-plugin-react-hooks eslint-config-prettier @typescript-eslint/eslint-plugin @typescript-eslint/parser

prettier インストール

npm i -D prettier

typescript インストール

npm i @types/react @types/react-dom @types/tailwindcss
npm i -D typescript

tailwindcss インストール

npm i -D postcss autoprefixer tailwindcss

react インストール

npm i react react-dom

ファイル作成

touch {webpack.common.js,webpack.dev.js,webpack.prod.js}
touch babel.config.json
touch {.eslintignore,.eslintrc.js}
touch {.prettierignore,.prettierrc}
touch {postcss.config.js,tailwind.config.js}
touch {.env.dev,.env.production}

webpack 設定

webpack.common.js
const path = require("path");
const ESLintPlugin = require("eslint-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = ({ outputFile, assetFile }) => ({
	entry: path.resolve(__dirname, "src/index.tsx"),
	output: {
		path: path.resolve(__dirname, "build"),
		publicPath: "/",
		filename: `js/${outputFile}.js`,
		chunkFilename: `js/${outputFile}.js`,
		clean: true,
	},
	plugins: [
		new ESLintPlugin({
			extensions: [".ts", ".tsx", ".js"],
			exclude: "node_modules",
		}),
		new MiniCssExtractPlugin({
			filename: `css/${outputFile}.css`,
		}),
	],
	module: {
		rules: [
			{
				test: /\.(ts|tsx)$/i,
				loader: "babel-loader",
				exclude: /node_modules/,
			},
			{
				test: /\.(eot|ttf|woff|woff2|png|jpg|gif|jpeg)$/i,
				type: "asset/resource",
				generator: {
					filename: `asset/${assetFile}[ext]`,
				},
			},
			{
				test: /\.html$/, //HtmlWebpackPluginがないと動作しない
				use: ["html-loader"],
			},
			{
				test: /\.css$/,
				use: [MiniCssExtractPlugin.loader, "css-loader", "postcss-loader"],
			},
			{
				test: /\.svg$/i,
				type: "asset",
				resourceQuery: /url/, // *.svg?url
			},
			{
				test: /\.svg$/i,
				issuer: /\.([jt]sx|js|ts)?$/,
				resourceQuery: { not: [/url/] }, // exclude react component if *.svg?url
				use: ["@svgr/webpack"],
			},
		],
	},
	resolve: {
		modules: [path.resolve(__dirname, "node_modules")],
		extensions: [".tsx", ".ts", ".js"],
	},
	optimization: {
		splitChunks: {
			cacheGroups: {
				vendors: {
					name: "vendors",
					test: /[\\/]node_modules[\\/]/,
					chunks: "all",
					reuseExistingChunk: true,
				},
			},
		},
	},
	stats: {
		children: true,
	},
});
webpack.dev.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const Dotenv = require("dotenv-webpack");
const { merge } = require("webpack-merge");
const common = require("./webpack.common.js");

const outputFile = "[name]";
const assetFile = "[name]";

module.exports = merge(common({ outputFile, assetFile }), {
	mode: "development",
	devtool: "inline-source-map",
	devServer: {
		open: true,
		static: {
			directory: path.join(__dirname, "build"),
			watch: {
				ignored: "node_modules",
			},
		},
		compress: true,
		port: 3000,
		historyApiFallback: true /* /home/hoge に直接リンクすると表示されないためfallbackで対策 */,
	},
	plugins: [
		new HtmlWebpackPlugin({
			template: "./src/index.html",
		}),
		new Dotenv({ path: "./.env" }),
	],
});
webpack.prod.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
const Dotenv = require("dotenv-webpack");
const { merge } = require("webpack-merge");
const common = require("./webpack.common.js");

const outputFile = "[name].[chunkhash]";
const assetFile = "[name].[contenthash]";

module.exports = merge(common({ outputFile, assetFile }), {
	mode: "production",
	plugins: [
		new HtmlWebpackPlugin({
			template: "./src/index.html",
			minify: {
				collapseWhitespace: true,
				keepClosingSlash: true,
				removeComments: true,
				removeRedundantAttributes: true,
				removeScriptTypeAttributes: true,
				removeStyleLinkTypeAttributes: true,
				useShortDoctype: true,
			},
		}),
		new Dotenv({ path: "./.env.production" }),
	],
	optimization: {
		minimizer: [new TerserPlugin(), new CssMinimizerPlugin()],
	},
});

babel設定

babel.config.json
{
	"presets": [
		[
			"@babel/preset-env",
			{
				"useBuiltIns": "usage",
				"corejs": 3
			}
		],
		[
			"@babel/preset-react",
			{
				/*options*/
			}
		],
		[
			"@babel/preset-typescript",
			{
				/*options*/
			}
		]
	]
}

eslint設定

.eslintignore
**/node_modules/
**/build/**
webpack.**
.eslintrc.js
module.exports = {
	env: {
		browser: true,
		es2021: true,
		node: true,
	},
	extends: [
		"eslint:recommended",
		"plugin:react/recommended",
		"plugin:@typescript-eslint/recommended",
		"prettier",
	],
	parser: "@typescript-eslint/parser",
	parserOptions: {
		ecmaFeatures: {
			jsx: true,
		},
		ecmaVersion: "latest",
		sourceType: "module",
	},
	plugins: ["react", "react-hooks", "@typescript-eslint"],
	rules: {
		indent: ["error", "tab", { SwitchCase: 1 }],
		"linebreak-style": ["error", "unix"],
		quotes: ["error", "double"],
		semi: ["error", "always"],
	},
	settings: {
		react: {
			version: "detect",
		},
	},
};

prettier設定

.prettierignore
**/node_modules/
**/build/**
.prettierrc
{
	"tabWidth": 2,
	"useTabs": true,
	"semi": true
}

typescript設定

npx tsc --init
tsconfig.json
{
	"compilerOptions": {
		"target": "ESNext",
		"jsx": "react",
		"module": "commonjs",
		"moduleResolution": "node",
		"sourceMap": true,
		"noEmit": true,
		"esModuleInterop": true,
		"forceConsistentCasingInFileNames": true,
		"strict": true,
		"skipLibCheck": true
	},
	"include": ["src/**/*.ts", "src/**/*.tsx"]
}

tailwindcss設定

postcss.config.js
module.exports = {
	plugins: {
		tailwindcss: {},
		autoprefixer: {},
	},
};
tailwind.config.js
module.exports = {
	content: ["./src/**/*.{html,tsx,ts,js}"],
	// theme: {extend: {}},
	plugins: [],
};

html/css/reactのテンプレート作成

mkdir src
cd src
touch {index.html,main.css,index.tsx,App.tsx,custom.d.ts}
index.html
<!DOCTYPE html>
<html lang="ja">
	<head>
		<meta charset="UTF-8" />
		<meta http-equiv="X-UA-Compatible" content="IE=edge" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>Document</title>
	</head>
	<body>
		<div id="root"></div>
	</body>
</html>
main.css
@tailwind base;
@tailwind components;
@tailwind utilities;
App.tsx
import React from "react";

const App = () => {
	return <p>Hello World</p>;
};

export default App;
index.tsx
import React from "react";
import ReactDOM from "react-dom/client";

import App from "./App";
import "./main.css";

const container: HTMLElement | null = document.getElementById("root");
const root = ReactDOM.createRoot(container as HTMLElement);

root.render(<App />);
custom.d.ts
declare module "*.jpg";
declare module "*.png";
declare module "*.jpeg";
declare module "*.gif";
declare module "*.svg";
declare module "*.svg?url";

packege.json 以下を追加

packege.json
{
	"scripts": {
		"start": "webpack-dev-server --config ./webpack.dev.js",
		"dev": " webpack --config ./webpack.dev.js",
		"build": "webpack --config ./webpack.prod.js",
		"lint": "eslint src && prettier --write src",
		"lint:fix": "eslint --fix src && prettier --write src"
	},
	"browserslist": [
		"defaults",
		"not IE 11"
	],
}

※随時更新

0
0
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
0
0