はじめに
こんにちは!!
Ionic 4とFirebaseを用いたモバイルアプリを作成していこうと思います。
Ionic 4はこの記事を書いている時点でBeta版ですが、いち早く最新のIonicに触れようと思います。
(現時点のIonicの最新バージョンは3.9.2です。)
Ionicは 3 と 4 で、見た目はそれほど大きな違いはありません(内部はかなり異なっています)。
じっくり学びたい方は、こちらの本をおすすめします。
Ionicで作る モバイルアプリ制作入門〈Web/iPhone/Android対応〉
自己紹介
- こざけ
- 大阪
- システムアーキテクト
- Java屋(最近はTypeScript成分多め)
- 🍻(´∀`*v)
今から作るもの
チャットアプリです。
こちらのサイトの記事をパク参考にしています。
https://www.djamware.com/post/5a629d9880aca7059c142976/build-ionic-3-angular-5-and-firebase-simple-chat-app
出会えるアプリを目指して頑張りましょう〜!
このアプリの完成イメージはこちらです。
前提条件
今回のハンズオンを進める上での前提条件です。
- Gitがインストールされていること
- Node.jsがインストールされていること
事前準備の詳細はこちらに纏めてあります。
このアプリを作成した時の僕の環境は次のとおりです。
$ node -v
v8.11.1
$ npm -v
6.1
$ ionic --version
4.0.1
エディタは何を使っても構いませんが、特にこだわりがなければ VSCode を推奨しております。
Ionicとは
Ionic はモバイルアプリケーションを開発するためのフレームワークです。
オープンソースで、MIT ライセンスで利用できます。
Ionic は PWAの開発、および、HTML5 の技術を用いたクロスプラットフォームのハイブリットアプリケーションを開発できます。
Firebaseとは
Firebaseとは、Mobile Backend as a Service(MBaaS)と呼ばれるクラウドサービスです。
2011年にアメリカで始まったMBaasで、2014年にGoogleに買収されました。
認証やデータベース、ホスティングなど、モバイルアプリに必要なバックエンドサービスが一通り揃っています。
詳しくはこちらのサイトを参考にしてください。
http://gihyo.jp/dev/serial/01/firebase/0001
Firebaseへの登録
では、Firebaseの利用登録を行いましょう。
Firebaseには無料プランが用意されていますので、今回はその範囲でアプリを開発していきます。
プロジェクトを追加を選択し、次の画像のとおり入力してプロジェクトを作成してください。
各種ツールインストール
では、開発に必要な各種ツールをインストールしていきます。
Firebase CLIのインストール
Firebase CLI をインストールします。
$ npm install -g firebase-tools
インストールが完了したら、Firebaseにログインしましょう。
$ firebase login
「匿名で情報を収集していいか?」と聞かれるので、そこはお好みで選択してください。
無事ログイン出来たら、成功です。
Ionicのインストール
Ionic をインストールします。
$ npm install -g ionic
Gitリポジトリ
出来上がりのソースコードはここにあります。
プロジェクト作成 - Ionic Start
では、プロジェクトを作成していきましょう!
ターミナル、もしくはコマンドプロンプトを起動して、任意のディレクトリで次のコマンドを実行してください。
$ ionic start awesome-chat blank --type=angular
コマンドを実行すると、
「Cordovaを用いてネイティブiOSとAndroidをターゲットにするか」
「Ionic Pro SDK(無料)をインストールしてアプリを接続するか」
を聞かれますので、次のとおり選択してください。
- Integrate your new app with Cordova to target native iOS and Android? (y/N)
N
- Install the free Ionic Pro SDK and connect your app? (Y/n)
N
これでプロジェクトの雛形が出来ました!
ionic start
コマンドで、このように手軽にプロジェクトの雛形を作成することが出来ます。
では、画面を立ち上げて見ましょう。
awesome-chatディレクトリに移って、ionic serve
コマンドを実行してください。
$ cd ./awesome-chat
$ ionic serve
次の画面が立ち上げれば、成功です。
一度に複数のプラットフォームの見た目を表示する、--labオプションを用いることも出来ます。
npm install -D @ionic/lab
コマンドで必要なライブラリをインストール後、
ionic serve --lab
コマンドを実行してください。
$ npm install -D @ionic/lab
$ ionic serve --lab
一度に複数のプラットフォームの見た目が確認できます!便利ですね!
firebaseのインストール
次に、firebaseと接続するライブラリをインストールします。
$ npm install --save firebase
ここまで出来たら、コミットしましょう。
$ git add .
$ git commit -m "install firebase"
Firebaseの設定
アプリにFirebaseの設定をしましょう。次のコードを追加してください。
export const environment = {
production: false,
config: {
apiKey: 'AIzaSyBLcdUiqQdqmARt_tRRvjUhmmZfFMHpNto',
authDomain: 'awesome-chat-f7118.firebaseapp.com',
databaseURL: 'https://awesome-chat-f7118.firebaseio.com',
projectId: 'awesome-chat-f7118',
storageBucket: 'awesome-chat-f7118.appspot.com',
messagingSenderId: '32939361989'
}
};
export const environment = {
production: true,
config: {
apiKey: 'AIzaSyBLcdUiqQdqmARt_tRRvjUhmmZfFMHpNto',
authDomain: 'awesome-chat-f7118.firebaseapp.com',
databaseURL: 'https://awesome-chat-f7118.firebaseio.com',
projectId: 'awesome-chat-f7118',
storageBucket: 'awesome-chat-f7118.appspot.com',
messagingSenderId: '32939361989'
}
};
import * as firebase from 'firebase';
import { environment } from '../environments/environment';
:
initializeApp() {
:
firebase.initializeApp(environment.config);
}
environmentの設定はdev環境で、environment.prod.tsの設定はprod環境(ng build --env = prod
)で使用されます。今回のハンズオンでは、どちらの設定も同じにしています。
各種設定内容は、Firebaseコンソール画面の「ウェブアプリに Firebase を追加」を選択すると、確認することが出来ます。
ionic serve
コマンドで画面を立ち上げて、問題なければコミットしましょう。
$ git add .
$ git commit -m "setting firebase"
認証画面とルーム画面の作成
Ionic CLIのコマンドを用いて、認証画面とルーム画面の雛形を作成します。
$ ionic generate page signin
$ ionic generate page room
ホーム画面をルーム画面に変更します。redirectTo
の箇所をhome
からroom
に変更してください。
また、home画面は必要ないのでコメントアウトしてください。
const routes: Routes = [
{ path: '', redirectTo: 'room', pathMatch: 'full' },
// { path: 'home', loadChildren: './home/home.module#HomePageModule' },
{ path: 'signin', loadChildren: './signin/signin.module#SigninPageModule' },
{ path: 'room', loadChildren: './room/room.module#RoomPageModule' },
];
ここまで出来たら、画面を立ち上げて確認しましょう。
$ ionic serve
ルーム画面の雛形が最初に表示されたら成功です。
ここまで出来たら、コミットしましょう。
$ git add .
$ git commit -m "create signin & room page"
認証画面の実装
では、認証画面を作成していきましょう。
まずは、ルーム画面を訪れた際に、未認証の場合は認証画面を表示するようにします。
import { NavController } from '@ionic/angular';
import * as firebase from 'firebase';
:
constructor(public navCtrl: NavController) { }
:
ngOnInit() {
firebase.auth().onAuthStateChanged((user) => {
if (user) {
} else {
this.navCtrl.goRoot('signin');
}
});
}
次に認証画面を作成します。
<ion-content padding>
<form>
<ion-list>
<ion-item>
<ion-label position="floating">Enter your E-mail</ion-label>
<ion-input type="text" [(ngModel)]="data.email" name="email" required></ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">Password</ion-label>
<ion-input type="password" [(ngModel)]="data.password" name="password" required></ion-input>
</ion-item>
</ion-list>
<p>
<ion-button expand="block" color="primary" type="button" (click)="signIn()">Sign In</ion-button>
</p>
<p>
<ion-button expand="block" fill="clear" color="secondary" type="button" (click)="signUp()">Sign Up</ion-button>
</p>
</form>
</ion-content>
import { NavController, AlertController } from '@ionic/angular';
import * as firebase from 'firebase';
:
export class SigninPage implements OnInit {
data: { email: string, password: string } = { email: '', password: '' };
constructor(
public navCtrl: NavController,
public alertController: AlertController) { }
:
async signIn() {
try {
await firebase
.auth()
.signInWithEmailAndPassword(this.data.email, this.data.password);
this.navCtrl.goRoot('room');
} catch (error) {
const alert = await this.alertController.create({
header: '警告',
message: error.message,
buttons: ['OK']
});
alert.present();
}
}
signUp() {
}
}
ここまで出来たら、画面を立ち上げて確認しましょう。
$ ionic serve
認証画面で適当にサインインしてみてください。
まだユーザがいないので、サインイン出来ません。当然ですね!
ここまで出来たら、コミットしましょう。
$ git add .
$ git commit -m "implemented signin page"
このままでは認証出来ないので、ユーザ登録画面を作成します。
まずは、Firebaseのコンソール画面で「メール / パスワード」による認証を有効にします。
次に、Ionic CLIのコマンドを用いて、ユーザ登録画面の雛形を作成します。
$ ionic generate page signup
コミットしましょう。
$ git add .
$ git commit -m "create signup page"
ユーザ登録画面を作成します。
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button></ion-back-button>
</ion-buttons>
<ion-title>signup</ion-title>
</ion-toolbar>
</ion-header>
<ion-content padding>
<form>
<ion-list>
<ion-item>
<ion-label position="floating">Enter your E-mail</ion-label>
<ion-input type="text" [(ngModel)]="data.email" name="email" required></ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">Password</ion-label>
<ion-input type="password" [(ngModel)]="data.password" name="password" required></ion-input>
</ion-item>
</ion-list>
<p>
<ion-button expand="block" color="primary" color="secondary" type="button" (click)="signUp()">Sign Up</ion-button>
</p>
</form>
</ion-content>
signUp() {
this.navCtrl.goForward('signup');
}
}
import { NavController, AlertController } from '@ionic/angular';
import * as firebase from 'firebase';
:
export class SignupPage implements OnInit {
data: { email: string, password: string } = { email: '', password: '' };
constructor(
public navCtrl: NavController,
public alertController: AlertController) { }
:
async signUp() {
try {
await firebase
.auth()
.createUserWithEmailAndPassword(this.data.email, this.data.password);
this.navCtrl.goRoot('room');
} catch (error) {
const alert = await this.alertController.create({
header: '警告',
message: error.message,
buttons: ['OK']
});
alert.present();
}
}
}
ここまで出来たら、画面を立ち上げて確認しましょう。
$ ionic serve
ユーザ登録画面でユーザ登録してみてください。
room画面が表示出来たら成功です!
無事認証出来たら、コミットしましょう。
$ git add .
$ git commit -m "Implemented signup page"
FirebaseコンソールでRealtime Databaseの作成
ルーム画面を作成する前に、ルームとチャットのメッセージを保存する為のデータベースを作成します。
データベースには、FirebaseコンソールでRealtime Databaseを用います。
今回は、簡単のためにテストモードで開始します。
...と思いましたが、あとでクラウドに公開することを見越して、最低限のルールを適用しましょう。
Databaseのルールを選択し、次のルールを設定してください。
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
これで、Databaseへの読み書きには認証が必要になりました。
ルーム画面の実装
では、ルーム画面を作成していきましょう。
まずは、ルーム画面にサインアウトの機能を実装します。
<ion-menu>
<ion-header>
<ion-toolbar>
<ion-title>Menu</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<ion-menu-toggle>
<ion-item>
<ion-label (click)="signOut()">
Sign Out
</ion-label>
</ion-item>
</ion-menu-toggle>
</ion-list>
</ion-content>
</ion-menu>
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-menu-button></ion-menu-button>
</ion-buttons>
<ion-title>room</ion-title>
</ion-toolbar>
</ion-header>
<!-- ion-contentに main をつけるのを忘れずに -->
<ion-content main padding>
</ion-content>
export class RoomPage implements OnInit {
:
async signOut() {
try {
await firebase.auth().signOut();
this.navCtrl.goRoot('signin');
} catch (error) {}
}
}
画面を立ち上げて、無事サインイン、サインアウトが出来たら、コミットしましょう。
$ git add .
$ git commit -m "Implemented signout"
ルーム一覧を表示する実装を追加します。
<ion-content main padding>
<ion-list>
<ion-item *ngFor="let room of rooms" (click)="joinRoom(room.key)">
{{room.roomname}}
<ion-icon name="chatboxes" slot="end"></ion-icon>
</ion-item>
</ion-list>
</ion-content>
export class RoomPage implements OnInit {
:
rooms = [];
async ngOnInit() {
firebase.auth().onAuthStateChanged((user) => {
if (user) {
firebase.database().ref('chatrooms/').on('value', resp => {
if (resp) {
this.rooms = [];
resp.forEach(childSnapshot => {
const room = childSnapshot.val();
room.key = childSnapshot.key;
this.rooms.push(room);
});
}
});
} else {
this.navCtrl.goRoot('signin');
}
});
}
joinRoom(key) {
this.navCtrl.goRoot('chat/' + key);
}
:
画面を立ち上げて、エラーが出ないことを確認してください。
今はルームがないので、何も表示されませんね。
一旦、ここでコミットしましょう。
$ git add .
$ git commit -m "Implemented room"
では、ルームがないと出会いがありません!
ルーム作成画面を作りましょうー!
まずはルーム作成画面の雛形を作って、
$ ionic generate page add-room
ルーム画面からルーム作成画面に繊維する実装を追加します。
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-menu-button></ion-menu-button>
</ion-buttons>
<ion-title>room</ion-title>
<ion-buttons slot="end">
<ion-button icon-only (click)="addRoom()">
<ion-icon name="add-circle"></ion-icon>
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
addRoom() {
this.navCtrl.goForward('add-room');
}
画面を立ち上げて、ルーム作成画面に繊維できることを確認してください。
ここでコミットしましょう。
$ git add .
$ git commit -m "create add-room"
ルーム作成画面を実装します。
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button></ion-back-button>
</ion-buttons>
<ion-title>add-room</ion-title>
</ion-toolbar>
</ion-header>
<ion-content padding>
<form>
<ion-list>
<ion-item>
<ion-label position="floating">Enter Room Name</ion-label>
<ion-input type="text" [(ngModel)]="data.roomname" name="roomname" required=""></ion-input>
</ion-item>
</ion-list>
<p>
<ion-button expand="block" color="secondary" (click)="addRoom()">Add</ion-button>
</p>
</form>
</ion-content>
import { NavController } from '@ionic/angular';
import * as firebase from 'Firebase';
:
export class AddRoomPage implements OnInit {
data: { roomname: string } = { roomname: '' };
constructor(public navCtrl: NavController) { }
:
addRoom() {
const newData = firebase.database().ref('chatrooms/')
.push({
roomname: this.data.roomname
});
this.navCtrl.goBack('room');
}
}
画面を立ち上げて、ルームが作成できることを確認してください。
ルーム画面で作成したルームが表示出来たら成功です。
コミットしましょう。
$ git add .
$ git commit -m "Implemented add-room"
チャット画面の作成
では、いよいよ出会いのためのチャット画面を作成していきましょう!
次のコマンドでチャット画面の雛形を作成してください。
$ ionic generate page chat
チャット画面では、どのルームかを判別する為に、URLでルームIDを受け取ります。
チャットのRouteパスにKeyの指定を追加してください。
const routes: Routes = [
{
path: ':key',
component: ChatPage
}
];
画面を立ち上げて、ルーム画面からチャット画面に繊維できることを確認してください。
チャット画面が表示出来たら成功です。
コミットしましょう。
$ git add .
$ git commit -m "create chat page"
チャット画面の実装
さて、いよいよチャット画面の実装です。
もうね、コードが長いのでここは全部とっかえをお願いします!
<ion-header>
<ion-toolbar>
<ion-title>chat</ion-title>
<ion-buttons slot="end">
<ion-button icon-only (click)="exitChat()">
<ion-icon name="exit"></ion-icon>
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content padding>
<ion-list class="chat-list">
<ion-item *ngFor="let chat of chats" no-lines>
<div class="chat-status" text-center *ngIf="chat.type==='join'||chat.type==='exit';else message">
<span class="chat-date">{{chat.sendDate | date:'short'}}</span>
<span class="chat-content-center">{{chat.message}}</span>
</div>
<ng-template #message>
<div class="chat-message" text-right *ngIf="chat.user === nickname">
<div class="right-bubble">
<span class="msg-name">Me</span>
<span class="msg-date">{{chat.sendDate | date:'short'}}</span>
<p text-wrap>{{chat.message}}</p>
</div>
</div>
<div class="chat-message" text-left *ngIf="chat.user !== nickname">
<div class="left-bubble">
<span class="msg-name">{{chat.user}}</span>
<span class="msg-date">{{chat.sendDate | date:'short'}}</span>
<p text-wrap>{{chat.message}}</p>
</div>
</div>
</ng-template>
</ion-item>
</ion-list>
</ion-content>
<ion-footer>
<ion-toolbar>
<ion-input type="text" placeholder="Type a message" [(ngModel)]="chatMessage" name="message"></ion-input>
<ion-buttons slot="end">
<ion-button slot="end" color="primary" (click)="sendChatMessage()">
Send
<ion-icon name="send"></ion-icon>
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-footer>
import { Component, OnInit, ViewChild } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { NavController, Content } from '@ionic/angular';
import * as firebase from 'Firebase';
@Component({
selector: 'app-chat',
templateUrl: './chat.page.html',
styleUrls: ['./chat.page.scss'],
})
export class ChatPage implements OnInit {
roomkey: string;
nickname: string;
chatMessage: string;
chats = [];
offStatus = false;
@ViewChild(Content) content: Content;
constructor(public navCtrl: NavController, public route: ActivatedRoute) {
firebase.auth().onAuthStateChanged((user) => {
if (user) {
this.roomkey = this.route.snapshot.paramMap.get('key') as string;
this.nickname = user.email;
this.sendJoinMessage();
this.displayChatMessage();
} else {
this.navCtrl.goRoot('signin');
}
});
}
ngOnInit() {
}
displayChatMessage() {
firebase.database()
.ref('chatrooms/' + this.roomkey + '/chats')
.on('value', resp => {
if (resp) {
this.chats = [];
resp.forEach(childSnapshot => {
const chat = childSnapshot.val();
chat.key = childSnapshot.key;
this.chats.push(chat);
});
setTimeout(async () => {
if (this.offStatus === false) {
// FIX-ME
// V4でコンテンツエリアをスクロールする方法が分からない
const el = await this.content.getScrollElement();
el.scrollToBottom(300);
}
});
}
});
}
exitChat() {
this.sendExitMessage();
this.offStatus = true;
this.navCtrl.goBack('room');
}
sendChatMessage() {
this.sendMessage('message', this.chatMessage);
}
sendJoinMessage() {
this.sendMessage('join', this.nickname + ' has joined this room.');
}
sendExitMessage() {
this.sendMessage('exit', this.nickname + ' has exited this room.');
}
sendMessage(type: string, message: string) {
const newData = firebase.database().ref('chatrooms/' + this.roomkey + '/chats').push();
newData.set({
type: type,
user: this.nickname,
message: message,
sendDate: Date()
});
}
}
画面を立ち上げて、チャット画面で書き込みができることを確認してください。
無事チャット出来たら成功です。
コミットしましょう。
$ git add .
$ git commit -m "Implemented chat page"
チャット画面の装飾
今のままだと、チャット画面が寂しいですね!
これだと話も盛り上がりません。。
チャット画面風に装飾しましょう!
ion-content {
background: url(../../assets/imgs/chat-back.png) no-repeat center center fixed;
background-size: cover;
height: 100%;
overflow: hidden;
.item-md, .item-ios {
background: none;
}
}
ion-footer {
background: white;
ion-icon {
padding: 10px;
font-size: 20px;
color: green;
}
}
.chat-list {
background: none;
}
.chat-status {
min-height: 49px;
width: 100%;
.chat-date {
display: block;
font-size: 10px;
font-style: italic;
color: #fff;
text-shadow: 0px -1px 0px #222, 0px 1px 0px #aaa;
height: 15px;
left: 10%;
right:10%;
}
.chat-content-center {
padding: 5px 10px;
background-color: #e1e1f7;
border-radius: 6px;
font-size: 12px;
color: #555;
height: 34px;
left: 10%;
right:10%;
}
}
.chat-message {
width: 80%;
min-height: 40px;
margin-bottom: 20px;
p {
margin-top: 0px;
margin-bottom: 0px;
}
.right-bubble {
position: relative;
background: #dcf8c6;
border-top-left-radius: .4em;
border-bottom-left-radius: .4em;
border-bottom-right-radius: .4em;
padding: 5px 10px 10px;
left: 15%;
span.msg-name {
font-size: 12px;
font-weight: bold;
color: green;
}
span.msg-date {
font-size: 10px;
}
}
.right-bubble:after {
content: '';
position: absolute;
right: 0;
top: 0;
width: 0;
height: 0;
border: 27px solid transparent;
border-left-color: #dcf8c6;
border-right: 0;
border-top: 0;
margin-top: -13.5px;
margin-right: -27px;
}
.left-bubble {
position: relative;
background: #ffffff;
border-top-right-radius: .4em;
border-bottom-left-radius: .4em;
border-bottom-right-radius: .4em;
padding: 5px 10px 10px;
left: 5%;
span.msg-name {
font-size: 12px;
font-weight: bold;
color: blue;
}
span.msg-date {
font-size: 10px;
}
}
.left-bubble:after {
content: '';
position: absolute;
left: 0;
top: 0;
width: 0;
height: 0;
border: 27px solid transparent;
border-right-color: #ffffff;
border-left: 0;
border-top: 0;
margin-top: -13.5px;
margin-left: -27px;
}
}
src/assets/imgs/chat-back.png
に配置する背景画像はお好みでどうぞ!
完成しました!
ルーム画面
チャット画面
完成した喜びを噛み締めて、コミットしましょう。
$ git add .
$ git commit -m "awesome chat!!"
デプロイ - Firebase Hosting
せっかく作ったこの素晴らしい出会い系Chatアプリを世の中に公開しましょう!
Firebaseは、Webアプリを公開する為のHostingサービスを用意されています。素晴らしいですね!
firebaseのHostingサービスを利用する為に、次のコマンドを実行します。
$ firebase init
どのFirebase CLI機能を設定するか、どのプロジェクトを使うか、パブリックディレクトリはどこか、SPAとして設定するかなどを問われるので、次の通り選択してください。
- Which Firebase CLI features do you want to setup for this folder? Press Space to select features, then Enter to confirm your
choices.Hosting
- Select a default Firebase project for this directory:
awesome-chat
- What do you want to use as your public directory?
www
- Configure as a single-page app?
y
次にプロダクションモードでアプリをビルドしましょう!
次のコマンドを実行してください。
$ ionic build --prod
www
ディレクトリに成果物が出来上がります。
次のコマンドでHostingサービスにデプロイしましょう!
$ firebase deploy
=== Deploying to 'awesome-chat-f7118'...
i deploying hosting
i hosting: preparing www directory for upload...
✔ hosting: 829 files uploaded successfully
✔ Deploy complete!
Project Console: https://console.firebase.google.com/project/awesome-chat-f7118/overview
Hosting URL: https://awesome-chat-f7118.firebaseapp.com
表示されたHosting URL
にアクセスし、画面が表示されたらリリース完了です!
これでハンズオンが完了ですが、如何だったでしょうか?
Ionic 4とFirebaseの組み合わせで、こんなに早くモバイルアプリが世の中にリリース出来ることに驚きませんでしたか?
今回はWebベースのモバイルアプリを作成しましたが、Ionicを用いるとAndroidやiPhoneのネイティブアプリを作成することも可能です。
Web技術を用いて、ネイティブさながらのハイブリッドアプリケションを短時間に作成できるのは、とても魅力的だと考えております。
今回のハンズオンを通して「面白いな!」と感じて頂けたら、どんどんチャレンジして、かっこいいアプリを作成していってくださいね!