はじめに
初めまして!りかと申します。
未経験からのWebエンジニア転職を目指して、日々学習を行っております。
今回、韓国コスメのスキンケア情報サービス「KoreCare(コリケア)」を開発しました。
サービス名
KoreCare
お試し機能を実装しましたので、ログインせずにお試しいただくこともできます。
▼サービスURL
https://korecare.jp/
▼GitHub URL
https://github.com/rk0325/korecare
サービス概要
韓国コスメに特化した、スキンケア情報サービスです。
具体的には、
- ユーザーの肌質やお悩みに合った韓国コスメを提案します。
- お肌の大敵である紫外線や乾燥から、ユーザーのお肌を守るサポートをします。
- スキンケアコスメの使用期限切れを防ぐサポートをします。
サービスを作成した背景
K-POPを好きになったことがきっかけで韓国コスメを使うようになったのですが、比較的安価でありながら質が高いところに魅力を感じ、韓国コスメに馴染みがない方にも興味を持っていただくきっかけを作りたいと思いました。
韓国コスメに関してすでに知識がある方よりは、
「韓国コスメを使ったことはないけれど、何となく興味はある」
「たくさん種類があって、何から手を出したらいいのかわからない」
「どのスキンケアコスメを選んだらいいか迷ってしまう」
といった、韓国コスメ初心者の方や、スキンケアについてお悩みを抱えている方を主なターゲットとし、韓国コスメ用語の解説ページを作成したり、アプリ内で紹介するコスメの種類を絞ることで、気軽に使っていただけるよう工夫しました。
また、UV指数や湿度の情報・使用期限のLINE通知機能を通して、ユーザーの皆さんに自身のスキンケアについてより関心を持っていただき、皆さんの美肌づくりをサポートできたら嬉しいなと思い、開発に至りました。
リリース後には、
「これを機に韓国コスメ使ってみようかな」
「肌質に合わせておすすめしてくれてありがたい」
「何を使ったらいいのか?は男性あるあるなのでありがたい」
「UV、湿度を教えてくれるの嬉しい」
など、たくさんのご感想をいただき、とっても嬉しかったです!
機能一覧
お試し韓国コスメ検索機能 | 韓国コスメ検索機能 |
---|---|
肌質・お悩みを選択すると、それぞれに合った韓国コスメを表示します。 | 肌質・お悩み・形態・金額を選択すると、それぞれに合った韓国コスメを表示します。 |
レコメンド機能(1) | レコメンド機能(2) |
---|---|
ユーザーが検索時に選択したキーワードに関連する商品をレコメンドします。 | マイページにて設定していただいた肌質・お悩みに関連する商品をレコメンドします。 |
お気に入りコスメ登録機能 | お気に入りコスメ一覧機能 |
---|---|
検索したコスメやレコメンドされたコスメをお気に入りに登録できます。 | マイページ内でお気に入りに登録したコスメを確認できます。 |
紫外線/乾燥注意通知機能(設定) | 使用期限通知機能(設定) |
---|---|
マイページにて設定していただいたお住まいのUV指数と湿度の情報を毎朝10時に通知します。 | コスメの製品タイプ、開封日、使用期限を設定していただくと、使用期限の5日前・3日前・前日に通知します。 |
紫外線/乾燥注意通知機能(通知画面) | 使用期限通知機能(通知画面) |
---|---|
実際の通知画面です。 | 実際の通知画面です。 |
レビュー投稿/一覧機能 | レビュー編集/削除機能 |
---|---|
お気に入りに登録したコスメのレビューを投稿できます。 | レビューの編集や削除も可能です。 |
レビュー検索機能 | ログイン/ログアウト機能 |
---|---|
レビュー投稿時に付けられたタグ(肌質、お悩み、年代)により、該当するレビューを検索できます。 | NextAuth.jsを使用したLINEログインが可能です。 |
現在の天気/UV指数/湿度の表示機能 |
---|
OpenWeatherMap APIを使用し、現在の天気、UV指数、湿度の情報を表示しています。 |
お気に入りコスメのXシェア機能 | レビュー投稿のXシェア機能 |
---|---|
お気に入りコスメのXへのシェアができます。 | レビュー投稿のXへのシェアができます。 |
使用技術
カテゴリ | 技術 |
---|---|
フロントエンド | TypeScript 5.3.3 / React 18.2.0 / Next.js 14.0.4 |
バックエンド | Ruby 3.2.2 / Ruby on Rails 7.0.8(API モード) |
データベース | PostgreSQL |
認証 | NextAuth.js |
環境構築 | Docker |
インフラ | Vercel / Render |
Web API | 楽天市場商品検索API / LINE Messaging API / OpenWeatherMap API |
その他 | SWR / shadcn/ui / Sidekiq / Redis / ActiveJob |
インフラ構成
選定理由
開発環境
開発環境には、環境ごとの差異を最小限に抑えられるとともに、docker-compose
を使用することで、各コンテナの起動、停止、再構築をコマンド一つで実行できる点が開発の効率化に繋がると考え、Docker
を採用しました。
▼参考にした記事
フロントエンド
フロントエンドには、昨年チーム開発にてNext.js
を使用した経験を踏まえ、さらに深く掘り下げて学びたいと考え、Next.js
を採用しました。SSRやCSR、SSGなどレンダリング手法を考慮した上でパフォーマンス設計を行い、ユーザビリティを高められるよう意識しました。
バックエンド
バックエンドには、スクールで学んできたRuby on Rails
を採用することで、実装スピードを上げられると考えました。
インフラ
Vercel
は、Next.js
との相性が良いこと、GitHub
との連携によりコードをpushすることで自動でデプロイが行われる点が、開発の効率化に繋がると思い採用しました。
Render
は、過去のアプリ開発での利用経験が豊富であったため、導入コストを抑えられることと、LINE通知機能においてバックエンドで要求されるキューイングシステムの構築に必要なRedis
を提供していることから採用しました。
ER図
画面遷移図
工夫したポイント
ログイン機能
ユーザー登録のハードルを下げたかったことと、セキュリティ面を考慮し、NextAuth.js
を使用したLINEログインを実装しました。
一部コード抜粋
import NextAuth from 'next-auth';
import LineProvider from 'next-auth/providers/line';
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
const handler = NextAuth({
providers: [
LineProvider({
id: "line",
name: "LINE",
clientId: process.env.NEXT_PUBLIC_LINE_CLIENT_ID || '',
clientSecret: process.env.NEXT_PUBLIC_LINE_CLIENT_SECRET || '',
authorization: { params: { scope: "profile openid" } },
token: "https://api.line.me/oauth2/v2.1/token",
userinfo: "https://api.line.me/v2/profile",
profile(profile) {
return {
id: profile.sub,
name: profile.displayName,
image: profile.pictureUrl,
};
},
}),
],
// 中略
});
export { handler as GET, handler as POST };
'use client';
import { SessionProvider } from 'next-auth/react';
import { ReactNode } from 'react';
const NextAuthProvider = ({ children }: { children: ReactNode }) => {
return <SessionProvider>{children}</SessionProvider>;
};
export default NextAuthProvider;
▼参考にした記事
LINE通知機能
この機能では、情報を決まった時間に自動的に送信することが目標でした。そのため、Sidekiq
のスケジューリング機能と、Redis
をバックエンドに使用するActiveJob
を組み合わせて利用しました。さらに、Sidekiq-Cron
を使ってCron
式でジョブのスケジューリングを設定し、ジョブ失敗に備えてSidekiq
の自動リトライ機能を設定しました。
一部コード抜粋
class WeatherNotificationJob < ApplicationJob
queue_as :default
sidekiq_options retry: 1
# Sidekiqはジョブの実行に失敗した場合、デフォルトで25回リトライするため、
# 何度も通知が送られないよう、リトライ回数を1回に指定
sidekiq_retry_in do |_count|
60
end
# ジョブのリトライ間隔を60秒に設定
def perform
User.includes(:profile).find_each do |user|
next unless user.profile
next unless user.receive_notifications_weather
prefecture_name = user.profile.prefecture
next if prefecture_name.blank?
weather_info = WeatherService.fetch_weather_data(prefecture_name)
next if weather_info.blank?
message = WeatherMessageGenerator.generate_message(prefecture_name, weather_info)
LineNotifyService.send_message(user.uid, message) if user.uid.present?
end
end
end
require 'sidekiq'
require 'sidekiq-cron'
Sidekiq.configure_server do |config|
# Sidekiqサーバーの設定(ジョブを実行するサーバー側で適用される)
schedule_file = 'config/schedule.yml'
# 定期的に実行するジョブのスケジュールを記述したYAMLファイルのパスを指定
config.redis = {
url: ENV['REDIS_URL'],
connect_timeout: 5,
read_timeout: 5,
write_timeout: 5
# 接続、読み込み、書き込みそれぞれに5秒のタイムアウトを設定
}
Sidekiq::Cron::Job.load_from_hash YAML.load_file(schedule_file) if File.exist?(schedule_file)
# config/schedule.ymlファイルが存在する場合、そのファイルを読み込み、
# YAMLファイルに記述されたスケジュールに従って定期的なジョブを設定
end
Sidekiq.configure_client do |config|
# Sidekiqクライアントの設定(ジョブをキューに投入する側で適用される)
config.redis = {
url: ENV['REDIS_URL'],
connect_timeout: 5,
read_timeout: 5,
write_timeout: 5
# サーバー側の設定と同様、RedisサーバーのURLとタイムアウトの設定を行う
}
end
weather_notification_job:
cron: "0 10 * * *"
class: "WeatherNotificationJob"
# 毎朝10時に通知を送るよう設定
LINE通知機能の実装については、別の記事にて詳しく書きたいと思います。
説明用のモーダル
画面上にはあまり文章を載せないようにし、補足事項はモーダル内に格納するようにしました。また、ツールチップを使用し、ホバーした際に何の情報が書かれているのかわかるようにしました。
UV指数と湿度の目安 | 肌質の目安 |
---|---|
配色
韓国コスメはカラフルなパッケージのものが多いため、韓国コスメ自体が引き立つよう落ち着いた配色を意識しました。カラーコードはノグチデザインさん(@n_seitan)を参考にさせていただきました。
レスポンシブデザイン
メニューの表示方法をPCとスマートフォンで変更しました。スマートフォンでの使用が多いと想定し、片手で操作することを考え、手の届きやすいところにメニューボタンを配置しました。
PC | スマートフォン |
---|---|
今後の開発について
1.ランキング機能
お気に入りの数が多い順や、レビュー評価の高い順でのランキング機能を実装したいと考えています。
2.テスト
まだまだテストコードが書けていないので、カバレッジ率を上げていきたいです。
おわりに
今まで簡単なミニアプリは作成したことがありますが、0からテーブル設計などを考えて開発するのは初めてでした。わからないことばかりで大変ではありましたが、自分が思い描いたものを形にできてとても楽しかったです!
まだまだ至らない点ばかりですが、今回の経験を糧に今後も楽しみながら学んでいきたいです。
改めて、開発にあたりご協力いただいた皆さまに感謝申し上げます。
最後まで読んでいただきありがとうございました。