2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Render × Vercel で実践するフルスタックアプリ公開ガイド 【GitHub Actions + CI/CD】

Last updated at Posted at 2025-09-29

1. はじめに

  • 本記事の目的:
    Render × Vercel を使い、Spring Boot × React のフルスタックアプリを公開する方法を解説します。
  • 追加要素:
    GitHub Actions(CI/CD) で自動テスト・自動デプロイを行い、実務に近い形を再現しました。
  • 想定読者:
    • フルスタック学習中でデプロイまで経験したい人
    • ポートフォリオを公開したい人
    • CI/CD や Docker を導入してみたい人

2. 全体アーキテクチャと技術スタック

以下でまとめたSpring Boot × React アプリの内容を前提に構築します。
https://qiita.com/MiyaHamu86/items/704056a16e8081cead5f

区分 採用技術 / サービス メモ
バックエンド Spring Boot + Gradle + PostgreSQL
フロントエンド React + Vite + TypeScript
インフラ Render(Backend / DB)+ Vercel(Frontend) RenderにAPI/DB、Vercelに静的フロント
開発フロー GitHub Actions(CI/CD)、Docker(コンテナ化) CIテスト/ビルド自動化、環境の共通化

システム図.jpg

3. GitHub Actions による CI/CD 構築

CI: バックエンドのビルド・テスト(JUnit)を自動化。
CD: Render Deploy Hook / Vercel Hook を GitHub Actions から呼び出して自動デプロイ。

以下、CI/CD用の設定ファイルを準備します。

.github/workflows/backend-ci.yml
name: backend-ci
on:
  push:
    paths:
      - 'todo-backend/**'
      - '.github/workflows/backend-ci.yml'
  pull_request:
    paths:
      - 'todo-backend/**'
jobs:
  build:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: todo-backend
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with:
          distribution: 'temurin'
          java-version: '21'
      - name: Grant execute permission for gradlew
        run: chmod +x ./gradlew
      # テスト込みビルド(-x test を外す)
      - name: Build (with tests)
        run: ./gradlew clean build

      # テスト成功 & main への push のときだけ Render を起動
      - name: Trigger Render Deploy
        if: github.event_name == 'push' && github.ref == 'refs/heads/main' && success()
        run: curl -sSf -X POST "${{ secrets.RENDER_DEPLOY_HOOK }}"
.github/workflows/frontend-ci.yml
name: frontend-ci
on:
  push:
    paths:
      - 'todo-frontend/**'
      - '.github/workflows/frontend-ci.yml'
  pull_request:
    paths:
      - 'todo-frontend/**'
jobs:
  build:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: todo-frontend
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npm ci
      - run: npm run build

4. バックエンド(Spring Boot)を Render にデプロイ

Render の「Native Runtime」でJavaは対象外のため、Web Service を Docker で立てる必要があります。

Dockerfile
#---- Build stage ----
FROM gradle:8.8-jdk21 AS build
WORKDIR /app
COPY build.gradle settings.gradle ./
# 動作確認(任意)
RUN gradle --version
# 残りのソース
COPY src ./src
# ビルド
RUN gradle clean bootJar -x test

# ---- Run stage ----
FROM eclipse-temurin:21-jre
WORKDIR /app
EXPOSE 8081
COPY --from=build /app/build/libs/app.jar /app/app.jar
ENTRYPOINT ["sh","-c","java $JAVA_OPTS -jar /app/app.jar"]

現在は、Render完結のDockerfile構成ですが、
今後の課題として、GitHub Actions で Docker イメージをビルド&レジストリへ push
→ Render はその 出来上がったイメージを pull して起動する運用に挑戦したいと考えています。

■WEBサービス作成手順

Renderにサインインします。
1.Renderダッシュボード右上「+New」押下
2.「WebService」押下
3.「PublicGitRepository」タブにGitリポジトリURLを入力し、「Connect」押下
4.以下入力する。

設定項目 設定値 メモ
Instance Type Free
Environment Variables 後ほど設定 必要な環境変数は別セクションで定義・投入
Auto-Deploy Off GitHub ActionsでCI/CD実行後に手動/自動デプロイを実施

5.「Deploy Web Service」押下

■PostgreSQL サービス作成手順

1.Renderダッシュボード右上「+New」押下
2.「Postgres」を押下し、以下を設定
Name:任意
Plan Options
Instance Type : Free
3.「Create DataBase」押下

■Deploy Hook設定

GithubAction(CI)でのテスト合格後、Renderでデプロイさせる設定です。

.github/workflows/backend-ci.yml
      # テスト成功 & main への push のときだけ Render を起動
      - name: Trigger Render Deploy
        if: github.event_name == 'push' && github.ref == 'refs/heads/main' && success()
        run: curl -sSf -X POST "${{ secrets.RENDER_DEPLOY_HOOK }}"

1) Render で Deploy Hook URL を取得
1.WEB SERVICE画面で Settings 押下。
2.Build & Deploy Deploy Hooks → Create Hook押下
3.表示された URL をコピー

2) GitHub Secrets に保存
1.GitHub → リポジトリ → Settings → Secrets and variables → Actions → New repository secret
2.以下を入力
Name: RENDER_DEPLOY_HOOK
Value: さきほどの URL
3.Add Secret押下

5. フロントエンド(React/Vite)を Vercel にデプロイ

Vercel のリポジトリ連携とビルド設定
Vercelにサインイン
1.ホーム画面「▲Start Deploying」押下
2.Import Git Repositoryにて、GitリポジトリURL指定
3.New Projectにて、以下を設定

設定項目 設定値 メモ
Root Directory todo-frontend フロント用のディレクトリ
Install Command npm install 依存関係をインストール
Build Command npm run build Viteビルド(出力先は通常 dist)

■Environment Variables

環境変数 Key Value  メモ
VITE_API_BASE_URL / 相対ベースURL。呼び出しは /api/... を使用(Vercel rewrites / Vite proxy)

4.Deploy押下

Vercelのrewritesで /api をRenderへプロキシ

vercel.json の rewrites 設定で /api でバックエンドAPI を透過的に呼び出します。
API ベースURLを設定します。

todo-frontend/vercel.json
{
  "rewrites": [
    { "source": "/api/(.*)", "destination": "https://todo-java-react.onrender.com/$1" },
    { "source": "/(.*)", "destination": "/" }
  ]
}

RenderのWebサービス画面上部に、バックエンドAPIのURLがあります。
image.png

Vercelの自動デプロイ

VercelはGit連携した場合、デフォルトでプッシュ時に自動デプロイされます。
以下の設定で、指定したフロント用のディレクトリに変更があった場合のみ、自動デプロイ設定が可能です。
image.png
また、以下で、ビルドする条件を細かく設定して、自動デプロイの制御が可能です。
Project → Settings → Git → Ignored Build Step

6.Render側へ環境変数設定

1.WEB SERVICE画面で Environment 押下。
2.Environment Variablesに以下を設定。

環境変数 Key Value メモ
CORS_ALLOWED_ORIGINS https://todo-java-react.vercel.app/

→Vercelのサービス画面の以下です。
image.png

環境変数 Key Value メモ
DATABASE_URL jdbc:postgresql://dpg-d3381u6mcj7s73a56p30-a.oregon-postgres.render.com/tododb_nl4r?sslmode=require JDBC接続URL(SSL必須)
DB tododb_nl4r データベース名
DB_USERNAME tododb_nl4r_user DBユーザー名
DB_PASSWORD ******** DBパスワード
HOST dpg-d3381u6mcj7s73a56p30-a DBホスト名

→RenderのDBサービス画面、以下にあります。
image.png

環境変数 Key Value メモ
PORT 5432
JWT_SECRET ********

JWT_SECRET→Base64 でエンコードしたランダム文字列を使います。
以下で作成できます。

powershell
[Convert]::ToBase64String((1..64 | ForEach-Object {Get-Random -Maximum 256}))

7. テスト自動化の実践

バックエンド:JUnit テストを Actions 上で実行。
Beanの依存関係の解決に苦戦したため、SpringBootを起動せず、依存するモックを定義して
簡単なAPIレスポンスのテストを行いました。

com.example.demo/PublicApiSmokeTest.java
class PublicApiSmokeTest {

  private MockMvc mvc;

  // 依存はすべて Mockito のモック
  private JwtService jwt;
  private UserMapper userMapper;
  private PasswordEncoder passwordEncoder;
  private AuthenticationManagerBuilder amb;

  @BeforeEach
  void setup() {
    jwt = mock(JwtService.class);
    userMapper = mock(UserMapper.class);
    passwordEncoder = mock(PasswordEncoder.class);
    amb = mock(AuthenticationManagerBuilder.class);

    AuthController controller = new AuthController(amb, jwt, userMapper, passwordEncoder);

    mvc = MockMvcBuilders.standaloneSetup(controller)
          .build();
  }

  /** ボディなしで /auth/login を叩いたら 400 を返す(存在確認+バリデーション) */
  @Test
  void login_requires_body_returns_400() throws Exception {
    mvc.perform(post("/auth/login").contentType(MediaType.APPLICATION_JSON))
       .andExpect(status().isBadRequest());
  }

  /** /auth/register に正しいJSONを投げると、パスワードをエンコードして UserMapper.insert が呼ばれる */
  @Test
  void register_encodes_password_and_inserts_user() throws Exception {
    when(passwordEncoder.encode("pass")).thenReturn("ENC(pass)");

    mvc.perform(
        post("/auth/register")
          .contentType(MediaType.APPLICATION_JSON)
          .content("{\"username\":\"testuser\",\"password\":\"pass\",\"roles\":\"1\"}")
    ).andExpect(status().isOk());

    //コントローラ内で new された User はこちらから参照できないので、キャプチャして中身をチェックする
    ArgumentCaptor<User> captor = ArgumentCaptor.forClass(User.class);
    //userMapper.insert(...) が 1回だけ呼ばれたことを検証
    verify(userMapper, times(1)).insert(captor.capture());

    User saved = captor.getValue();
    //testuserがJSONでPOSTされたことの確認
    assertEquals("testuser", saved.getUsername());
    //passwordEncoder.encode("pass")が呼ばれたことの確認
    assertEquals("ENC(pass)", saved.getPassword());
  }
}

フロントエンド:(省略)
今回は割愛しますが、Reactでは以下のようなCIテストができるそうです。

  • 静的チェック:ESLint + TypeScript(型崩れ/書き方ミス)
  • 単体/コンポーネントテスト:Vitest + React Testing Library
  • E2E:Playwright(実際のブラウザを自動操作して確認)

CI 上でテストが落ちるとデプロイされない仕組み。
1.GitHub Actions のジョブにて、./gradlew clean buildの中でテストが走る。
2.JUnit が@Testのついたクラスを実行
3.Testクラスのアサーション(処理結果が期待値通りかチェック)が失敗
4.Gradleタスクがエラー終了
5.GitHub Actions のジョブ失敗(RenderへのDeployHookが実行されない)

8. まとめ

  • Render × Vercel × GitHub Actions × Docker を組み合わせることで、実務に近いフルスタック開発環境が体験できる。
  • CI/CD + テスト自動化で品質と効率を両立できる。

以下、実際に触れるデモ環境とソースコードです。

🔗 デモURL:https://todo-java-react.vercel.app
※バックエンドAPI初回接続時、Renderサーバがスリープモードから起動するため2分程度かかります。15分動作がないと再度スリープモードへ移ります。

💻 GitHub:https://github.com/miyagawa-git/todo-java-react

ご意見・ご指摘などありましたらぜひコメントでいただけると嬉しいです。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?