NextPederationPlugin 활용 가이드 2: 모듈 애플리케이션 통합하기

0

1. Flight 및 Hotel 모듈 개요

Flight 및 Hotel 모듈은 각각 비행 시간 예약과 호텔 예약 기능을 제공하는 독립적인 애플리케이션으로, 이 두 모듈은 NextPederationPlugin을 사용하여 통합 애플리케이션인 app-front에 연동됩니다. 이 글에서는 각 모듈의 구성과 설정 방법을 단계별로 살펴보겠습니다.

2. Flight 모듈 구성

1. `package.json` 및 주요 스크립트

`package.json` 파일은 다음과 같이 구성됩니다:

{
  "name": "amu-front-flight",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "rm -rf .next && SET NEXT_PRIVATE_LOCAL_WEBPACK=true && next dev -p 3011",
    "build": "SET NEXT_PRIVATE_LOCAL_WEBPACK=true && next build"
  },
  "dependencies": {
    "next": "^12.0.7",
    "react": "^17.0.2",
    "react-dom": "^17.0.2"
  },
  "devDependencies": {
    "typescript": "^4.5.2",
    "webpack": "^5.58.2"
  }
}
  • dev: 개발 서버를 실행합니다. `NEXT_PRIVATE_LOCAL_WEBPACK` 환경 변수를 설정하여 Webpack을 로컬에서 실행합니다.
  • build: 빌드 명령어로, `NEXT_PRIVATE_LOCAL_WEBPACK` 환경 변수를 설정하여 빌드를 수행합니다.

2. `next.config.js` 파일 설정 및 NextFederationPlugin 사용법

`next.config.js` 파일은 다음과 같이 구성됩니다:

const NextFederationPlugin = require('@module-federation/nextjs-mf');

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  compiler: {
    styledComponents: true,
  },
  eslint: {
    ignoreDuringBuilds: true,
  },
  webpack(config, options) {
    config.resolve.alias.canvas = false;
    const url = 'http://localhost:3011';
    const location = options.isServer ? 'ssr' : 'chunks';
    config.plugins.push(
      new NextFederationPlugin({
        name: 'flight',
        filename: 'static/chunks/remoteEntry.js',
        remotes: {
          container: `container@${url}/_next/static/${location}/remoteEntry.js`,
        },
        exposes: {
          './home': './src/pages/flight/index.tsx',
          './search': './src/pages/flight/search/index.tsx',
          './flightSearch': './src/components/FlightSearch/index.tsx',
        },
        shared: {
          'styled-components': { eager: true, singleton: true },
          '@tanstack/react-query': { eager: true, singleton: true },
          dayjs: { eager: true, singleton: true },
          '@amu/amu-ui-system': { eager: true, singleton: true },
          'next/image': { eager: true, singleton: true },
        },
      })
    );

    return config;
  },
  images: {
    domains: ['*', 'cdn.domain.net'],
    formats: ['image/webp'],
    dangerouslyAllowSVG: true,
  },
};

module.exports = nextConfig;

NextFederationPlugin 플러그인은 모듈 연동을 위해 설정됩니다.

  • name: 현재 모듈의 이름.
  • filename: 원격 엔트리 파일의 이름.
  • remotes: 원격 모듈의 URL 설정.
  • exposes: 현재 모듈에서 제공하는 컴포넌트 경로.
  • shared: 공유 모듈 설정.

3. Flight 모듈의 파일 및 폴더 구조

/flight
  ├── .next
  ├── node_modules
  ├── public
  └── src
      ├── components
      │   └── FlightSearch
      │       └── index.tsx
      ├── pages
      │   ├── flight
      │   │   ├── search
      │   │   │   └── index.tsx
      │   │   └── index.tsx
      │   ├── _app.tsx
      │   └── _document.tsx
      ├── styles
      │   └── globalStyle.ts
      ├── types
      │   └── common.ts
      ├── utils
      │   └── server.ts
      └── remoteType.d.ts
  ├── env.development
  ├── next.config.js
  ├── package.json
  └── tsconfig.json

4. `FlightSearch` 컴포넌트와 서버 사이드 렌더링 설정**

// src/components/FlightSearch/index.tsx
import React from 'react';

const FlightSearch = () => {
  return <div>Flight Search Component</div>;
};

export default FlightSearch;
// src/pages/flight/index.tsx
import Head from 'next/head';
import { Button } from '@amu/amu-ui-system';
import { useRouter } from 'next/router';
import { PageProps } from 'types/common';
import { makeGetServerSideProps } from 'utils/server';
import FlightSearch from 'components/FlightSearch';
import styled from 'styled-components';

export default function Home({ isMobile }: PageProps) {
  const router = useRouter();

  return (
    <>
      <Head>
        <title>Flight Home</title>
      </Head>
      <ButtonWrapper>
        <Button>Search Flights</Button>
      </ButtonWrapper>
      <FlightSearch />
    </>
  );
}

const ButtonWrapper = styled.div`
  margin-bottom: 20px;
`;

export const getServerSideProps = makeGetServerSideProps();

3. Hotel 모듈 구성

1. `package.json` 및 주요 스크립트

`package.json` 파일은 `flight` 모듈과 유사하게 구성됩니다. 다만, `name`과 포트 번호가 다릅니다:

{
  "name": "amu-front-hotel",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "rm -rf .next && SET NEXT_PRIVATE_LOCAL_WEBPACK=true && next dev -p 3012",
    "build": "SET NEXT_PRIVATE_LOCAL_WEBPACK=true && next build"
  },
  "dependencies": {
    "next": "^12.0.7",
    "react": "^17.0.2",
    "react-dom": "^17.0.2"
  },
  "devDependencies": {
    "typescript": "^4.5.2",
    "webpack": "^5.58.2"
  }
}

2. `next.config.js` 파일 설정

`next.config.js` 파일은 `flight` 모듈과 거의 동일합니다. 주요 차이점은 `exposes` 부분입니다:

const NextFederationPlugin = require('@module-federation/nextjs-mf');

const nextConfig = {
  reactStrictMode: true,
  compiler: {
    styledComponents: true,
  },
  eslint: {
    ignoreDuringBuilds: true,
  },
  webpack(config, options) {
    config.resolve.alias.canvas = false;
    const url = 'http://localhost:3012';
    const location = options.isServer ? 'ssr' : 'chunks';
    config.plugins.push(
      new NextFederationPlugin({
        name: 'hotel',
        filename: 'static/chunks/remoteEntry.js',
        remotes: {
          container: `container@${url}/_next/static/${location}/remoteEntry.js`,
        },
        exposes: {
          './home': './src/pages/hotel/index.tsx',
          './search': './src/pages/hotel/search/index.tsx',
          './hotelSearch': './src/components/HotelSearch/index.tsx',
        },
        shared: {
          'styled-components': { eager: true, singleton: true },
          '@tanstack/react-query': { eager: true, singleton: true },
          dayjs: { eager: true, singleton: true },
          '@amu/amu-ui-system': { eager: true, singleton: true },
          'next/image': { eager: true, singleton: true },
        },
      })
    );

    return config;
  },
  images: {
    domains: ['*', 'cdn.domain.net'],
    formats: ['image/webp'],
    dangerouslyAllowSVG: true,
  },
};

module.exports = nextConfig;

3. Hotel 모듈의 파일 및 폴더 구조

`flight` 모듈과 유사한 구조를 따릅니다.

4. `HotelSearch` 컴포넌트와 서버 사이드 렌더링 설정

// src/components/HotelSearch/index.tsx
import React from 'react';

const HotelSearch = () => {
  return <div>Hotel Search Component</div>;
};

export default HotelSearch;
// src/pages/hotel/index.tsx
import Head from 'next/head';
import { Button } from '@amu/amu-ui-system';
import { useRouter } from 'next/router';
import { PageProps } from 'types/common';
import { makeGetServerSideProps } from 'utils/server';
import HotelSearch from 'components/HotelSearch';
import styled from 'styled-components';

export default function Home({ isMobile }: PageProps) {
  const router = useRouter();

  return (
    <>
      <Head>
        <title>Hotel Home</title>
      </Head>
      <ButtonWrapper>
        <Button>Search Hotels</Button>
      </ButtonWrapper>
      <HotelSearch />
    </>
  );
}

const ButtonWrapper = styled.div`
  margin-bottom: 20px;
`;

export const getServerSideProps = makeGetServerSideProps();

5. 모바일 디바이스 대응 방법

서버 사이드 렌더링 시 모바일 디바이스를 감지하여 적절한 UI를 제공할 수 있습니다. 이를 위해 `server.ts` 파일에 다음과 같은 함수를 정의합니다:

// src/utils/server.ts
import { GetServerSidePropsContext } from 'next';

export const checkMobileDevice = (context: GetServerSidePropsContext) => {
  return Boolean(context?.req?.headers['user-agent']?.includes('Mobi'));
};

export const makeGetServerSideProps = () => (context: GetServerSidePropsContext) => {
  const isMobile = checkMobileDevice(context);

  return {
    props: {
      isMobile,
    },
  };
};

이 함수는 `getServerSideProps`를 사용하여 서버 사이드에서 모바일 디바이스 여부를 확인하고, 이를 컴포넌트에 전달합니다.

결론

이 글에서는 Flight 및 Hotel 모듈 애플리케이션을 구성하고 설정하는 방법을 다루었습니다. 각 모듈은 독립적으로 개발되어, NextPederationPlugin을 통해 연동됩니다. 다음 글에서는 이 두 개의 애플리케이션과 지난 시간에 만든 UI 시스템 패키지를 app-front 애플리케이셔너에 구성하고, 이 모듈들을 통합하는 방법을 알아보겠습니다. 이를 통해 최종적으로 완성된 통합 애플리케이션을 구축해 보겠습니다.

Leave a Reply