Guide to Integrating App Front with NextPederationPlugin

0

1. Overview of App Front Application

App Front is an application that integrates the previously developed Flight and Hotel modules. This application provides a unified user interface that offers the functionalities from each module. This article explains step-by-step how to set up the app-front application and integrate each module.

2. Configuring the App Front Application

1. `package.json` and Main Scripts

The `package.json` file is configured as follows:

{
  "name": "amu-front",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "rm -rf .next && SET NEXT_PRIVATE_LOCAL_WEBPACK=true && next dev -p 3010",
    "build": "SET NEXT_PRIVATE_LOCAL_WEBPACK=true && next build"
  },
  "dependencies": {
    "@amu/amu-ui-system": "file:../ui-system",
    "axios": "^0.24.0",
    "next": "^12.0.7",
    "react": "^17.0.2",
    "react-dom": "^17.0.2"
  },
  "devDependencies": {
    "typescript": "^4.5.2",
    "webpack": "^5.58.2"
  }
}
  • dev: Runs the development server. Sets the `NEXT_PRIVATE_LOCAL_WEBPACK` environment variable to execute Webpack locally.
  • build: Build command that sets the `NEXT_PRIVATE_LOCAL_WEBPACK` environment variable to perform the build.

2. Configuring the `next.config.js` File and Using NextFederationPlugin

The `next.config.js` file is configured as follows:

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

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  compiler: {
    styledComponents: true,
  },
  eslint: {
    ignoreDuringBuilds: true,
  },
  webpack(config, options) {
    const location = options.isServer ? 'ssr' : 'chunks';
    config.plugins.push(
      new NextFederationPlugin({
        name: 'container',
        filename: 'static/chunks/remoteEntry.js',
        remotes: {
          flight: `flight@http://localhost:3011/_next/static/${location}/remoteEntry.js`,
          hotel: `hotel@http://localhost:3012/_next/static/${location}/remoteEntry.js`,
        },
        exposes: {
          './home': './src/pages/index.tsx',
          './layout': './src/components/Layout/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 },
          axios: { singleton: true },
          '@amu/amu-ui-system': { eager: true, singleton: true },
          'next/image': { eager: true, singleton: true },
        },
      })
    );

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

module.exports = nextConfig;

NextFederationPlugin is configured for module integration.

  • name: The name of the current module.
  • filename: The name of the remote entry file.
  • remotes: URL settings for remote modules.
  • exposes: The paths of the components provided by the current module.
  • shared: Settings for shared modules.

3. Key Components and Page Configuration

`Layout` Component
// src/components/Layout/index.tsx
import React from 'react';
import styled from 'styled-components';

const Layout = ({ children }) => {
  return <Container>{children}</Container>;
};

const Container = styled.div`
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 20px;
`;

export default Layout;
`MetaInfo` Component
// src/components/MetaInfo/index.tsx
import Head from 'next/head';

const DEFAULT_META = {
  title: 'App Front',
  description: 'An application integrating Flight and Hotel modules.',
  keywords: ['flight', 'hotel', 'booking'],
};

type Props = {
  title?: string;
  description?: string;
  keywords?: string[];
  image?: string;
};

const MetaInfo = ({
  title = DEFAULT_META.title,
  description = DEFAULT_META.description,
  keywords = DEFAULT_META.keywords,
  image = 'https://img.png',
}: Props) => {
  return (
    <Head>
      <title>{title}</title>
      <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
      <meta name="description" content={description} />
      <meta name="keywords" content={keywords.join(', ')} />
      <meta property="og:image" content={image} />
      <meta property="og:title" content={title} />
      <meta property="og:description" content={description} />
      <meta property="og:url" content="www.allmyuniverse.com" />
      <meta property="og:type" content="website" />
      <meta property="og:locale" content="ko_KR" />
    </Head>
  );
};

export default MetaInfo;

4. Configuring the Main Page (`home`) and Dynamic Imports

`Home` Page
// src/pages/index.tsx
import dynamic from 'next/dynamic';
import React, { useState } from 'react';
import Layout from 'components/Layout';
import MetaInfo from 'components/MetaInfo';
import { Button } from '@amu/amu-ui-system';
import styled from 'styled-components';

const FlightSearch = dynamic(() => import('flight/flightSearch'), {
  ssr: false,
});

const HotelSearch = dynamic(() => import('hotel/hotelSearch'), {
  ssr: false,
});

const Home = () => {
  const [toggle, setToggle] = useState(false);

  return (
    <Layout>
      <MetaInfo />
      <ButtonWrapper>
        <Button onClick={() => setToggle(!toggle)}>
          {toggle ? 'Flight' : 'Hotel'}
        </Button>
      </ButtonWrapper>
      {toggle ? <FlightSearch /> : <HotelSearch />}
    </Layout>
  );
};

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

export default Home;

5. Global Styles and Font Configuration

`globalStyle.ts` File
// src/styles/globalStyle.ts
import { createGlobalStyle } from 'styled-components';
import PoppinsR from './fonts/poppins-v19-latin-regular.woff';
import NotoSansB from './fonts/NotoSans-Bold.woff';

const GlobalStyle = createGlobalStyle`
  @font-face {
    font-family: 'Poppins';
    src: url(${PoppinsR}) format('woff');
    font-weight: 400;
  }
  @font-face {
    font-family: 'NotoSansKR';
    src: url(${NotoSansB}) format('woff');
    font-weight: 700;
  }
  body {
    font-family: 'Poppins', 'NotoSansKR', sans-serif;
  }
`;

export default GlobalStyle;
6. Configuring the `App` Component
`_app.tsx` File
// src/pages/_app.tsx
import type { AppProps } from 'next/app';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ThemeProvider } from 'styled-components';
import { theme } from '@amu/amu-ui-system';
import GlobalStyle from '../styles/globalStyle';
import MetaInfo from 'components/MetaInfo';
import dynamic from 'next/dynamic';
import React from 'react';

const poppins = Poppins({
  subsets: ['latin'],
  weight: ['400', '700'],
  display: 'swap',
});

const notosansKr = Noto_Sans_KR({
  subsets: ['latin'],
  weight: ['400', '700'],
  display: 'swap',
});

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retry: 0,
      refetchOnWindowFocus: false,
    },
  },
});

const AlertContainer = dynamic(
  () => import('@amu/amu-ui-system').then((r) => r.AlertContainer),
  { ssr: false }
);

const ConfirmContainer = dynamic(
  () => import('@amu/amu-ui-system').then((r) => r.ConfirmContainer),
  { ssr: false }
);

const MobileAlertContainer = dynamic(
  () => import('@amu/amu-ui-system').then((r) => r.MobileAlertContainer),
  { ssr: false }
);

const ToastContainer = dynamic(
  () => import('@amu/amu-ui-system').then((r) => r.ToastContainer),
  { ssr: false }
);

export default function App({ Component, pageProps }: AppProps) {
  return (
    <ThemeProvider theme={theme}>
      <MetaInfo />
      <GlobalStyle />
      <style jsx global>
        {`
          html {
            font-family: ${poppins.style.fontFamily}, ${notosansKr.style.fontFamily};
          }
          ${pageProps.isMobile &&
          `
            #__next {
              display: initial !important;
            }
          `}
        `}
      </style>
      <QueryClientProvider client={queryClient}>
        <Component {...pageProps} />
        <AlertContainer />
        <ConfirmContainer />
        <ToastContainer />
        <MobileAlertContainer />
      </QueryClientProvider>
    </ThemeProvider>
  );
}

3. Integration Testing and Final Build

1. How to Test Integration with Flight and Hotel Modules

To test if the integrated application is properly connected, follow these steps:

Running Local Servers

Run the development servers for each module.

cd flight
npm run dev
cd hotel
npm run dev
cd app-front
npm run dev
Check in Browser

Open `http://localhost:3010` in your browser to check the integrated application.

2. Troubleshooting During Development and Build

If modules are not loading correctly:
Verify the `remotes` settings in the `next.config.js` file.
Ensure the development servers for each module are running.
If styles are not applied:
Check the configuration for `styled-components`.
Ensure global styles are applied.

3. Final Build and Deployment Process

Final Build
cd app-front
npm run build
Deployment
  • [Vercel:@b A widely used platform for deploying Next.js applications. Connect to Vercel and deploy your project.
  • [Netlify:@b Another popular deployment platform with easy setup.
Example of Deploying on Vercel:
  • Create and log in to a Vercel account.
  • Click the `Import Project` button and select your GitHub repository.
  • Complete the deployment settings and click the `Deploy` button.
Example of Deploying on Netlify:
  • Create and log in to a Netlify account.
  • Click the `New site from Git` button and select your GitHub repository.
  • Complete the deployment settings and click the `Deploy` button.

Conclusion

In this article, we covered how to set up the app-front application and integrate the Flight and Hotel modules. We also discussed how to test the integration, build, and deploy the application. By using NextPederationPlugin to modularize and integrate applications, you can develop projects that are more efficient and easier to maintain.

Leave a Reply