NextPederationPlugin 활용 가이드 3: App Front 애플리케이션 구성 및 연동하기

0

1. App Front 애플리케이션 개요

App Front는 앞서 만들어 본 Flight 및 Hotel 모듈을 통합하는 애플리케이션입니다. 이 애플리케이션은 각 모듈에서 제공하는 기능을 하나의 통합된 사용자 인터페이스로 제공합니다. 이 글에서는 app-front 애플리케이션을 구성하고, 각 모듈을 연동하는 방법을 단계별로 설명합니다.

2. App Front 애플리케이션 구성하기

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

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

{
  "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: 개발 서버를 실행합니다. `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) {
    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 플러그인은 모듈 연동을 위해 설정됩니다.

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

3. 주요 컴포넌트 및 페이지 구성

`Layout` 컴포넌트
// 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` 컴포넌트
// 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. 메인 페이지 (`home`) 구성 및 동적 임포트

`Home` 페이지
// 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. 글로벌 스타일과 폰트 설정

`globalStyle.ts` 파일
// 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. `App` 컴포넌트 구성

`_app.tsx` 파일
// 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. 연동 테스트 및 최종 빌드

1. Flight 및 Hotel 모듈과의 연동 테스트 방법

통합 애플리케이션이 제대로 연동되었는지 테스트하기 위해 다음 단계를 따릅니다:

로컬 서버 실행

각 모듈의 개발 서버를 실행합니다.

cd flight
npm run dev
cd hotel
npm run dev
cd app-front
npm run dev
브라우저에서 확인

브라우저에서 `http://localhost:3010`을 열어 통합 애플리케이션을 확인합니다.

2. 개발 및 빌드 시 발생할 수 있는 문제 해결 방법

모듈이 제대로 로드되지 않는 경우:
`next.config.js` 파일의 `remotes` 설정이 올바른지 확인합니다.
각 모듈의 개발 서버가 실행 중인지 확인합니다.
스타일이 적용되지 않는 경우:
`styled-components` 설정이 올바른지 확인합니다.
글로벌 스타일이 적용되는지 확인합니다.

3. 최종 빌드 및 배포 과정

최종 빌드
cd app-front
npm run build
배포
  • [Vercel:@b Next.js 애플리케이션을 배포하는데 널리 사용되는 플랫폼입니다. Vercel에 연결하고 프로젝트를 배포할 수 있습니다.
  • [Netlify:@b 또 다른 인기 있는 배포 플랫폼으로, 설정이 간편합니다.
Vercel 배포 예시:
  • Vercel 계정 생성 후 로그인.
  • `Import Project` 버튼을 클릭하여 GitHub 저장소를 선택합니다.
  • 배포 설정을 완료하고 `Deploy` 버튼을 클릭합니다.
Netlify 배포 예시:
  • Netlify 계정 생성 후 로그인.
  • `New site from Git` 버튼을 클릭하여 GitHub 저장소를 선택합니다.
  • 배포 설정을 완료하고 `Deploy` 버튼을 클릭합니다.

결론

이 글에서는 app-front 애플리케이션을 구성하고, Flight 및 Hotel 모듈을 통합하는 방법을 다루었으며, 또한, 연동 테스트 방법과 빌드 및 배포 과정에 대해 알아보았습니다. NextPederationPlugin을 활용하여 모듈화된 애플리케이션을 성공적으로 구성하고 통합하면 이를 통해 더욱 효율적이고 유지보수가 용이한 프로젝트를 개발할 수 있습니다.

Leave a Reply