[Vite] 한 번에 여러 프로덕트를 개발하는 방법

kyle·2025년 4월 17일
0

Vite

목록 보기
2/2


Vite Middleware를 활용해 한 레포지토리에서 여러 프로덕트를 개발하는 방법에 대해 알아보겠습니다.
이 방법을 활용하면 개발 환경에서 경로 기반으로 원하는 파일을 서빙할 수 있습니다. 마치 웹서버에서 경로 기반으로 파일을 서빙 해주는 것처럼요.

이 방법의 장점은 아래와 같습니다.

  1. 하나의 개발서버로 여러 프로덕트 개발(경로 기반)
  2. 메인 <-> 서브 프로덕트간 의존성, 모듈 공유
  3. Multi Entry Points 기반으로 빌드 및 배포시 동일한 동작 보장
    Multi Entry Points로 빌드하기

사용 방법은 단순합니다.
각 단계에 주석을 작성해두었습니다.

  1. 레포지토리에 server.js를 생성합니다.
  2. 아래 코드를 작성합니다.

예제 코드

import fs from "node:fs/promises";
import express from "express";

const port = process.env.PORT || 5173;

// HTTP 서버 생성
const app = express();

// 미들웨어 모드로 Vite 서버를 생성하고 애플리케이션의 타입을 'custom'으로 설정합니다.
// 이는 Vite의 자체 HTML 제공 로직을 비활성화하고,
// 상위 서버에서 이를 제어할 수 있도록 합니다.
let vite;
const { createServer } = await import("vite");
vite = await createServer({
  server: { middlewareMode: true }, //SSR 사용시 'ssr'
  appType: "custom", // HTML 제공 로직을 커스텀할 것이므로 custom을 넣어야합니다. 기본 설정은 spa입니다.
});
app.use(vite.middlewares);

// 루트(/)에서 a 경로로 이동시 캐치
app.use("/a", async (req, res) => {
  try {
    console.log(req.originalUrl, "서브 프로덕트 요청");
    // 1. 서빙할 파일을 불러옵니다.
    let template = await fs.readFile("./src/a/index.html", "utf-8");
    
    // 2. Vite의 HTML 변환 작업을 통해 Vite HMR 클라이언트를 주입하고,
    //    Vite 플러그인의 HTML 변환도 적용합니다.
    //    (예: @vitejs/plugin-react의 전역 초기화 코드)
    template = await vite.transformIndexHtml(req.originalUrl, template);

    // 클라이언트에게 html을 서빙합니다.
    res.status(200).set({ "Content-Type": "text/html" }).send(template);
    return;
  } catch (e) {
    console.log(e.stack);
    res.status(500).end(e.stack);
  }
});

// 루트 경로 접근시 캐치
app.use("/", async (req, res) => {
  try {
    console.log(req.originalUrl, "메인 프로덕트 요청");
    
    let template = await fs.readFile("./index.html", "utf-8");
    template = await vite.transformIndexHtml(req.originalUrl, template);

    res.status(200).set({ "Content-Type": "text/html" }).send(template);
    return;
  } catch (e) {
    console.log(e.stack);
    res.status(500).end(e.stack);
  }
});

// Start http server
app.listen(port, () => {
  console.log(`Server started at http://localhost:${port}`);
});

서버 실행

미들웨어를 작성했으면, 이제 서버를 실행시켜주면 됩니다.

	"scripts": {
      "dev": "node ./server/server.js",
    }

마무리하며

얼마 전 회사에서 메인/서브 프로덕트를 동시에 개발할 일이 생겼습니다.
서브 프로덕트를 별개의 레포지토리로 만들어서 개발 할 수도 있었지만, 메인/서브 프로덕트는 기획상 논리적으로 얽힌 관계였기에 한 레포지토리에서 관리하는게 좋을 것 같았습니다. 또한, 별개 레포지토리로 관리한다면 공통 모듈 또한 사용할 수 없으며 배포도 두 번 해야하기 때문에 비효율적이죠.

서비스의 크기를 고려해 모노레포는 고려하지않았고 다른 방법을 찾기 시작했습니다.

모노레포? 도입하기엔 서비스가 작습니다.
React Router? 각 프로덕트들은 기획상 논리적으로 얽혀있지만 독립적인 프로덕트여야 했습니다. 따라서 리액트 라우터도 고려하지않았습니다.

기본적으로 Vite는 하나의 SPA 프로덕트를 실행하기때문에 서브 프로덕트를 나눌 방법이 없습니다. 하지만 Vite Middleware를 커스텀하면 가능해집니다.

참고한 레퍼런스 : VITE SSR

위 글은 Vite Middleware를 사용해 SSR 처리를 하는 방법을 다루고 있습니다.
Vite Middleware를 사용해 Express 서버 내부에서
custom 모드로 Vite 서버를 생성 및 실행하고, 경로 기반으로 프로덕트들을 구분해 서빙해주었습니다.

읽어주셔서 감사합니다.

profile
DX를 사랑하는 개발자

0개의 댓글