카테고리 없음

React, Typescript 환경에서 하나의 프로젝트에서 여러 시스템을 사용하고 공유 컴퍼넌트를 사용하고 다른 시스템의 기능을 사용 할 수 있도록 구성

오아름 샘 2025. 4. 24. 02:22
반응형

 

React, Typescript 환경에서 하나의 프로젝트에서 여러 시스템을 사용하고 공유 컴퍼넌트를 사용하고 
다른 시스템의 기능을 사용 할 수 있도록 구성해 주세요.

 

프로젝트 구조 설계

/monorepo-root
  /packages
    /shared-components      # 여러 시스템에서 사용하는 공통 컴포넌트
    /system-a               # 시스템 A
    /system-b               # 시스템 B
  package.json
  tsconfig.json
  ...

 

monorepo 구조로 여러 시스템과 공유 컴포넌트를 하나의 저장소에서 관리
Yarn Workspaces, pnpm workspace, 또는 Lerna 등으로 패키지 관리를 추천

 

Monorepo 및 패키지 관리 설정
Yarn Workspaces 예시
package.json 최상위에 다음 추가

{
  "private": true,
  "workspaces": [
    "packages/*"
  ]
}

 

각 패키지는 독립적인 React + TypeScript 프로젝트

공통 컴포넌트 패키지 설정 (shared-components)
packages/shared-components에 React 컴포넌트 작성
package.json에 React, TypeScript 등 peerDependencies 설정
예: Button.tsx

import React from 'react';

interface ButtonProps {
  label: string;
  onClick?: () => void;
}

export const Button: React.FC<ButtonProps> = ({ label, onClick }) => (
  <button onClick={onClick}>{label}</button>
);

 

각 시스템 패키지 설정 (system-a, system-b)
React 앱으로 구성
shared-components를 의존성으로 추가 (workspace로 연결)
예: system-a/package.jso

{
  "name": "system-a",
  "version": "1.0.0",
  "dependencies": {
    "react": "^18.0.0",
    "react-dom": "^18.0.0",
    "shared-components": "*"
  }
}

system-a에서 shared-components 사용 가능

import React from 'react';
import { Button } from 'shared-components';

export const SystemAComponent = () => (
  <div>
    <h1>System A</h1>
    <Button label="Click me" onClick={() => alert('Clicked!')} />
  </div>
);

시스템 간 기능 공유 방법
1) 공통 유틸 함수, 서비스 공유
shared-utils 같은 별도 패키지로 분리
각 시스템에서 import하여 사용
2) 이벤트 버스 또는 상태 공유
zustand, redux 같은 글로벌 상태 관리 라이브러리를 공유 패키지로 설정 후 시스템 간 상태 공유
또는 React Context를 최상위에서 감싸기
3) 마이크로 프론트엔드 (Micro Frontends)
Webpack Module Federation 활용
각 시스템을 독립 배포하지만 런타임에 컴포넌트/모듈을 주입 가능

 

Webpack Module Federation 기본 예시
system-a의 webpack.config.js

const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');

module.exports = {
  // ...
  plugins: [
    new ModuleFederationPlugin({
      name: 'system_a',
      filename: 'remoteEntry.js',
      exposes: {
        './Button': './src/components/Button', // system-a에서 expose할 컴포넌트
      },
      shared: ['react', 'react-dom'],
    }),
  ],
};

 

요약
Monorepo로 여러 시스템과 공유 컴포넌트 관리
Yarn Workspaces 등으로 패키지 의존성 관리
공통 컴포넌트는 별도 패키지로 분리해 재사용
시스템 간 기능 공유는 공통 패키지 또는 Module Federation 활용
상태 공유가 필요하면 글로벌 상태 관리 도구 사용

 

React + TypeScript 멀티 시스템 아키텍처 구성

하나의 프로젝트에서 여러 시스템을 운영하면서 공통 컴포넌트와 기능을 공유하는 아키텍처를 구성해보겠습니다.

1. 프로젝트 구조 제안

src/
├── apps/                  # 각 시스템별 앱
│   ├── system-a/          # 시스템 A
│   ├── system-b/          # 시스템 B
│   └── system-c/          # 시스템 C
├── common/                # 공통 코드
│   ├── components/        # 공유 컴포넌트
│   ├── hooks/             # 공통 훅
│   ├── utils/             # 유틸리티 함수
│   ├── services/          # API 서비스
│   ├── types/             # 공통 타입 정의
│   └── contexts/          # 공통 컨텍스트
├── libs/                  # 외부 라이브러리 확장/설정
└── routes/                # 라우팅 설정 (필요시)

2. 공통 컴포넌트 구현 예시

src/common/components/Button.tsx

import React from 'react';

type ButtonVariant = 'primary' | 'secondary' | 'danger';

interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
  variant?: ButtonVariant;
  isLoading?: boolean;
}

const Button: React.FC<ButtonProps> = ({
  variant = 'primary',
  isLoading = false,
  children,
  className = '',
  ...props
}) => {
  const baseClasses = 'px-4 py-2 rounded-md font-medium transition-colors';
  const variantClasses = {
    primary: 'bg-blue-600 text-white hover:bg-blue-700',
    secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300',
    danger: 'bg-red-600 text-white hover:bg-red-700',
  };

  return (
    <button
      className={`${baseClasses} ${variantClasses[variant]} ${className}`}
      disabled={isLoading || props.disabled}
      {...props}
    >
      {isLoading ? 'Loading...' : children}
    </button>
  );
};

export default Button;

3. 시스템 간 기능 공유을 위한 서비스 레이어

src/common/services/api.ts

import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';

class ApiService {
  private static instance: AxiosInstance;

  static getInstance(): AxiosInstance {
    if (!ApiService.instance) {
      ApiService.instance = axios.create({
        baseURL: process.env.REACT_APP_API_BASE_URL,
        timeout: 10000,
      });
    }
    return ApiService.instance;
  }

  static async get<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
    const response = await ApiService.getInstance().get<T>(url, config);
    return response.data;
  }

  static async post<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
    const response = await ApiService.getInstance().post<T>(url, data, config);
    return response.data;
  }

  // 다른 HTTP 메소드들도 추가 가능
}

export default ApiService;

4. 시스템 간 상태 공유를 위한 컨텍스트

src/common/contexts/AuthContext.tsx

import React, { createContext, useContext, useState, useEffect } from 'react';

interface AuthContextType {
  isAuthenticated: boolean;
  user: User | null;
  login: (token: string, userData: User) => void;
  logout: () => void;
}

interface User {
  id: string;
  name: string;
  email: string;
  roles: string[];
}

const AuthContext = createContext<AuthContextType | undefined>(undefined);

export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [user, setUser] = useState<User | null>(null);

  useEffect(() => {
    // 초기 인증 상태 확인 (예: localStorage에서 토큰 확인)
    const token = localStorage.getItem('authToken');
    const userData = localStorage.getItem('userData');
    
    if (token && userData) {
      setIsAuthenticated(true);
      setUser(JSON.parse(userData));
    }
  }, []);

  const login = (token: string, userData: User) => {
    localStorage.setItem('authToken', token);
    localStorage.setItem('userData', JSON.stringify(userData));
    setIsAuthenticated(true);
    setUser(userData);
  };

  const logout = () => {
    localStorage.removeItem('authToken');
    localStorage.removeItem('userData');
    setIsAuthenticated(false);
    setUser(null);
  };

  return (
    <AuthContext.Provider value={{ isAuthenticated, user, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => {
  const context = useContext(AuthContext);
  if (context === undefined) {
    throw new Error('useAuth must be used within an AuthProvider');
  }
  return context;
};

5. 시스템별 앱 구성 예시

src/apps/system-a/App.tsx

import React from 'react';
import { AuthProvider } from '../../common/contexts/AuthContext';
import Button from '../../common/components/Button';
import SystemARoutes from './routes';

const SystemAApp: React.FC = () => {
  return (
    <AuthProvider>
      <div className="system-a">
        <header>
          <h1>System A</h1>
          <Button variant="primary" onClick={() => console.log('System A action')}>
            System A Button
          </Button>
        </header>
        <main>
          <SystemARoutes />
        </main>
      </div>
    </AuthProvider>
  );
};

export default SystemAApp;

6. 모노레포 구성 (선택사항)

여러 시스템을 완전히 분리하고 싶다면 모노레포 구조를 고려할 수 있습니다.

package.json에 workspaces 설정

{
  "name": "my-mono-repo",
  "private": true,
  "workspaces": [
    "packages/*"
  ],
  "scripts": {
    "start:a": "cd packages/system-a && npm start",
    "start:b": "cd packages/system-b && npm start",
    "build:common": "cd packages/common && npm run build",
    "build:a": "npm run build:common && cd packages/system-a && npm run build",
    "build:b": "npm run build:common && cd packages/system-b && npm run build"
  }
}

7. 타입 공유를 위한 중앙 집중식 관리

src/common/types/index.ts

// 공통 응답 타입
export interface ApiResponse<T> {
  data: T;
  message?: string;
  success: boolean;
}

// 사용자 타입
export interface User {
  id: string;
  name: string;
  email: string;
  roles: string[];
}

// 시스템 간 공유할 수 있는 다른 타입들...

8. 시스템 간 컴포넌트 확장 패턴

import React from 'react';
import BaseButton, { ButtonProps as BaseButtonProps } from '../../common/components/Button';

interface ExtendedButtonProps extends BaseButtonProps {
  icon?: React.ReactNode;
}

const SystemAButton: React.FC<ExtendedButtonProps> = ({ icon, children, ...props }) => {
  return (
    <BaseButton {...props}>
      {icon && <span className="mr-2">{icon}</span>}
      {children}
    </BaseButton>
  );
};

export default SystemAButton;

9. 빌드 및 배포 전략

  1. 공통 코드: common 디렉토리는 별도로 빌드하여 패키지로 관리하거나 프로젝트 내에서 직접 참조
  2. 시스템별 앱: 각 시스템은 독립적으로 빌드 가능하도록 구성
  3. 환경 변수: .env 파일을 시스템별로 분리하여 관리
 
.env.system-a
.env.system-b

10. 주의사항

  1. 의존성 관리: 시스템 간에 너무 강한 결합이 생기지 않도록 주의
  2. 버전 관리: 공통 코드 변경 시 모든 시스템에 미치는 영향 고려
  3. 성능: 사용하지 않는 시스템의 코드가 번들에 포함되지 않도록 동적 임포트 고려
  4. 테스트: 공통 코드는 더 엄격한 테스트 필요

이 구조는 시스템 간 코드 공유를 최대화하면서도 각 시스템의 독립성을 유지할 수 있도록 도와줍니다. 프로젝트 규모와 요구사항에 따라 적절히 조정하여 사용하시면 됩니다.

 

 

 

 

React와 TypeScript를 사용하여 하나의 프로젝트에서 여러 시스템을 사용하고 공유 컴포넌트를 활용하며 다른 시스템의 기능을 사용할 수 있도록 구성하는 방법을 설명드리겠습니다. 이 작업을 위해 모노레포(Monorepo) 구조를 사용할 수 있습니다. 모노레포는 여러 패키지나 프로젝트를 하나의 저장소에서 관리할 수 있게 해주는 방식입니다. 이를 통해 공유 컴포넌트와 기능을 쉽게 관리하고 사용할 수 있습니다.

1. 프로젝트 초기 설정

먼저 프로젝트를 초기화하고 필요한 패키지를 설치합니다.
bash
# 프로젝트 디렉토리 생성 및 이동
mkdir my-monorepo
cd my-monorepo

# Yarn Berry (Yarn 2) 설치
yarn set version berry

# 워크스페이스 초기화
yarn init -2
 

2. 워크스페이스 설정

package.json 파일을 열어 워크스페이스를 설정합니다.
{
  "private": true,
  "workspaces": [
    "packages/*"
  ]
}
 

3. 패키지 생성

이제 여러 시스템을 나타내는 패키지를 생성합니다.
# 패키지 디렉토리 생성
mkdir -p packages/system-a
mkdir -p packages/system-b
mkdir -p packages/shared-components

# 각 패키지 초기화
cd packages/system-a
yarn init -y
cd ../system-b
yarn init -y
cd ../shared-components
yarn init -y
 

4. TypeScript 설정

각 패키지에 TypeScript를 설정합니다.
# 패키지 디렉토리 생성
mkdir -p packages/system-a
mkdir -p packages/system-b
mkdir -p packages/shared-components

# 각 패키지 초기화
cd packages/system-a
yarn init -y
cd ../system-b
yarn init -y
cd ../shared-components
yarn init -y

5. React 설정

각 패키지에 React를 설정합니다.
# React 및 타입 설치
yarn workspace system-a add react react-dom @types/react @types/react-dom
yarn workspace system-b add react react-dom @types/react @types/react-dom
yarn workspace shared-components add react react-dom @types/react @types/react-dom
 

6. 공유 컴포넌트 작성

shared-components 패키지에 공유 컴포넌트를 작성합니다.
# src 디렉토리 생성
mkdir -p packages/shared-components/src

# Button 컴포넌트 작성
echo 'import React from "react";

interface ButtonProps {
  label: string;
  onClick: () => void;
}

const Button: React.FC<ButtonProps> = ({ label, onClick }) => {
  return <button onClick={onClick}>{label}</button>;
};

export default Button;' > packages/shared-components/src/Button.tsx
 

7. 시스템에서 공유 컴포넌트 사용

system-a와 system-b에서 공유 컴포넌트를 사용합니다.
# src 디렉토리 생성
mkdir -p packages/system-a/src
mkdir -p packages/system-b/src

# App.tsx 파일 작성
echo 'import React from "react";
import Button from "@shared/Button";

const App: React.FC = () => {
  return (
    <div>
      <h1>System A</h1>
      <Button label="Click Me" onClick={() => alert("Button clicked!")} />
    </div>
  );
};

export default App;' > packages/system-a/src/App.tsx

cp packages/system-a/src/App.tsx packages/system-b/src/App.tsx
 

8. 시스템 빌드 및 실행

각 시스템을 빌드하고 실행합니다. 예를 들어 Webpack을 사용하여 빌드할 수 있습니다.
# Webpack 및 관련 패키지 설치
yarn workspace system-a add webpack webpack-cli ts-loader html-webpack-plugin
yarn workspace system-b add webpack webpack-cli ts-loader html-webpack-plugin

# Webpack 설정 파일 작성
echo 'const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  entry: "./src/App.tsx",
  output: {
    filename: "bundle.js",
    path: path.resolve(__dirname, "dist"),
  },
  resolve: {
    extensions: [".ts", ".tsx", ".js"],
  },
  module: {
    rules: [
      {
        test: /.tsx?$/,
        use: "ts-loader",
        exclude: /node_modules/,
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "./src/index.html",
    }),
  ],
};' > packages/system-a/webpack.config.js

cp packages/system-a/webpack.config.js packages/system-b/webpack.config.js

# index.html 파일 작성
echo '<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>System A</title>
</head>
<body>
  <div id="root"></div>
</body>
</html>' > packages/system-a/src/index.html

cp packages/system-a/src/index.html packages/system-b/src/index.html

# 빌드 및 실행
yarn workspace system-a webpack serve
yarn workspace system-b webpack serve
 
이제 system-a와 system-b에서 shared-components의 Button 컴포넌트를 사용할 수 있습니다.
모노레포 구조를 사용하면 여러 시스템에서 공유 컴포넌트를 쉽게 관리하고 사용할 수 있습니다.
 
react 17.0.2 버전 기준
 

 

1. 프로젝트 초기 설정

먼저 프로젝트 디렉토리를 생성하고 Yarn 워크스페이스를 설정합니다.
# 프로젝트 디렉토리 생성 및 이동
mkdir my-monorepo
cd my-monorepo

# Yarn 초기화
yarn init -y

# 워크스페이스 설정
echo '{
  "private": true,
  "workspaces": [
    "packages/*"
  ]
}' > package.json

 

2. 패키지 생성

여러 시스템을 나타내는 패키지를 생성합니다.
# 패키지 디렉토리 생성
mkdir -p packages/system-a
mkdir -p packages/system-b
mkdir -p packages/shared-components

# 각 패키지 초기화
cd packages/system-a
yarn init -y
cd ../system-b
yarn init -y
cd ../shared-components
yarn init -y
 

3. TypeScript 설정

각 패키지에 TypeScript를 설정합니다.
# TypeScript 설치
yarn workspace system-a add typescript
yarn workspace system-b add typescript
yarn workspace shared-components add typescript

# tsconfig.json 파일 생성 및 설정
echo '{
  "compilerOptions": {
    "target": "ES6",
    "module": "commonjs",
    "jsx": "react",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "baseUrl": "./",
    "paths": {
      "@shared/*": ["../shared-components/src/*"]
    }
  },
  "include": ["src"]
}' > packages/system-a/tsconfig.json

cp packages/system-a/tsconfig.json packages/system-b/tsconfig.json
cp packages/system-a/tsconfig.json packages/shared-components/tsconfig.json
 

4. React 설정

React 17.0.2 및 관련 타입 패키지를 설치합니다.
# React 및 타입 설치
yarn workspace system-a add react@17.0.2 react-dom@17.0.2 @types/react@17.0.2 @types/react-dom@17.0.2
yarn workspace system-b add react@17.0.2 react-dom@17.0.2 @types/react@17.0.2 @types/react-dom@17.0.2
yarn workspace shared-components add react@17.0.2 react-dom@17.0.2 @types/react@17.0.2 @types/react-dom@17.0.2
 

5. 공유 컴포넌트 작성

shared-components 패키지에 공유 컴포넌트를 작성합니다.
# src 디렉토리 생성
mkdir -p packages/shared-components/src

# Button 컴포넌트 작성
echo 'import React from "react";

interface ButtonProps {
  label: string;
  onClick: () => void;
}

const Button: React.FC<ButtonProps> = ({ label, onClick }) => {
  return <button onClick={onClick}>{label}</button>;
};

export default Button;' > packages/shared-components/src/Button.tsx
 

6. 시스템에서 공유 컴포넌트 사용

system-a와 system-b에서 공유 컴포넌트를 사용합니다.
# src 디렉토리 생성
mkdir -p packages/system-a/src
mkdir -p packages/system-b/src

# App.tsx 파일 작성
echo 'import React from "react";
import Button from "@shared/Button";

const App: React.FC = () => {
  return (
    <div>
      <h1>System A</h1>
      <Button label="Click Me" onClick={() => alert("Button clicked!")} />
    </div>
  );
};

export default App;' > packages/system-a/src/App.tsx

cp packages/system-a/src/App.tsx packages/system-b/src/App.tsx
 

7. Webpack 설정

각 시스템을 빌드하고 실행하기 위해 Webpack을 설정합니다.
# Webpack 및 관련 패키지 설치
yarn workspace system-a add webpack webpack-cli ts-loader html-webpack-plugin webpack-dev-server
yarn workspace system-b add webpack webpack-cli ts-loader html-webpack-plugin webpack-dev-server

# Webpack 설정 파일 작성
echo 'const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  entry: "./src/App.tsx",
  output: {
    filename: "bundle.js",
    path: path.resolve(__dirname, "dist"),
  },
  resolve: {
    extensions: [".ts", ".tsx", ".js"],
  },
  module: {
    rules: [
      {
        test: /.tsx?$/,
        use: "ts-loader",
        exclude: /node_modules/,
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "./src/index.html",
    }),
  ],
  devServer: {
    contentBase: path.join(__dirname, "dist"),
    compress: true,
    port: 9000,
  },
};' > packages/system-a/webpack.config.js

cp packages/system-a/webpack.config.js packages/system-b/webpack.config.js

# index.html 파일 작성
echo '<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>System A</title>
</head>
<body>
  <div id="root"></div>
</body>
</html>' > packages/system-a/src/index.html

cp packages/system-a/src/index.html packages/system-b/src/index.html
 
 

8. 빌드 및 실행

각 시스템을 빌드하고 실행합니다.
# 빌드 및 실행
cd packages/system-a
yarn webpack serve

cd ../system-b
yarn webpack serve
 

이제 system-a system-b에서 shared-components Button 컴포넌트를 사용할 수 있습니다. 모노레포 구조를 사용하면 여러 시스템에서 공유 컴포넌트를 쉽게 관리하고 사용할 수 있습니다

 
 
 
 
 
 
 
 

 

 

 

 

반응형