icon안동민 개발노트

프로젝트 구조 설계


 효과적인 프로젝트 구조는 대규모 TypeScript 애플리케이션의 유지보수성과 확장성을 크게 향상시킵니다.

 이 절에서는 프로젝트 구조 설계의 다양한 측면을 살펴봅니다.

디렉토리 구조와 파일 조직

 효과적인 디렉토리 구조 예시

src/
  ├── api/
  ├── components/
  ├── hooks/
  ├── pages/
  ├── services/
  ├── types/
  ├── utils/
  └── index.ts
tests/
config/
  ├── webpack.config.js
  └── tsconfig.json
package.json
README.md

 장점

  • 관심사의 분리
  • 코드 탐색 용이성
  • 모듈화 및 재사용성 증가

 단점

  • 초기 설정의 복잡성
  • 과도한 구조화로 인한 유연성 감소 가능성

모노레포 아키텍처

 Lerna를 사용한 모노레포 설정

  1. Lerna 설치
npm install --global lerna
  1. 프로젝트 초기화
lerna init
  1. 패키지 추가
lerna create @myorg/package-a
lerna create @myorg/package-b
  1. 의존성 관리
lerna add @myorg/package-a --scope=@myorg/package-b

 이점

  • 코드 공유 및 재사용 용이
  • 통합 테스트 및 배포 프로세스 간소화
  • 버전 관리 일원화

코드 분할 및 Lazy Loading

 Dynamic Import를 사용한 코드 분할

const AdminDashboard = React.lazy(() => import('./AdminDashboard'));
 
function App() {
  return (
    <React.Suspense fallback={<div>Loading...</div>}>
      <AdminDashboard />
    </React.Suspense>
  );
}

 성능 최적화

  • 초기 로딩 시간 감소
  • 필요한 코드만 로드하여 리소스 효율성 증가

공유 타입 정의 관리

 공유 타입을 위한 별도의 패키지 생성

// @myorg/shared-types/index.ts
export interface User {
  id: string;
  name: string;
  email: string;
}
 
export type AuthStatus = 'authenticated' | 'unauthenticated' | 'loading';

 사용

import { User, AuthStatus } from '@myorg/shared-types';

 이점

  • 타입 정의의 중앙화된 관리
  • 프로젝트 전반의 일관성 유지
  • 재사용성 및 유지보수성 향상

환경 설정 관리

 dotenv와 TypeScript의 통합

  1. 설치
npm install dotenv @types/dotenv
  1. 환경 변수 타입 정의
// src/types/env.d.ts
declare global {
  namespace NodeJS {
    interface ProcessEnv {
      NODE_ENV: 'development' | 'production' | 'test';
      API_URL: string;
      // 기타 환경 변수
    }
  }
}
  1. 환경 변수 사용
import dotenv from 'dotenv';
dotenv.config();
 
const apiUrl = process.env.API_URL;

의존성 관리 전략

 패키지 매니저 선택 고려사항

  • npm : 광범위한 지원, 기본 도구
  • yarn : 빠른 설치, 더 나은 캐싱
  • pnpm : 디스크 공간 효율성, 모노레포 지원

 pnpm 사용 예

pnpm add -D typescript @types/node

TypeScript 컴파일러 설정 최적화

 tsconfig.json 예시

{
  "compilerOptions": {
    "target": "es2018",
    "module": "esnext",
    "moduleResolution": "node",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "baseUrl": "./src",
    "paths": {
      "@/*": ["*"]
    }
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "**/*.spec.ts"]
}

 주요 설정

  • strict : 엄격한 타입 체크 활성화
  • baseUrlpaths : 절대 경로 임포트 지원
  • includeexclude : 컴파일 대상 파일 지정

코드 품질 유지 도구

 ESLint 설정 (.eslintrc.js)

module.exports = {
  parser: '@typescript-eslint/parser',
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    'prettier'
  ],
  plugins: ['@typescript-eslint'],
  rules: {
    // 커스텀 규칙
  }
};

 Prettier 설정 (.prettierrc)

{
  "singleQuote": true,
  "trailingComma": "es5",
  "printWidth": 100
}

 Git Hooks 통합 (package.json)

{
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "*.{ts,tsx}": [
      "eslint --fix",
      "prettier --write"
    ]
  }
}

종합적인 가이드라인 및 주의사항

 1. 모듈화 원칙 준수

  • 단일 책임 원칙을 따라 모듈 설계
  • 순환 의존성 방지

 2. 일관된 네이밍 컨벤션

  • 파일명, 폴더명, 변수명 등에 일관된 규칙 적용
  • 예 : PascalCase for components, camelCase for functions

 3. 문서화

  • README.md 파일로 각 모듈/패키지 설명
  • JSDoc 주석 활용

 4. 테스트 구조

  • 소스 코드와 유사한 디렉토리 구조로 테스트 파일 조직
  • 단위 테스트, 통합 테스트, E2E 테스트 분리

 5. 버전 관리 전략

  • Semantic Versioning 준수
  • CHANGELOG.md 파일 유지

 6. 성능 고려

  • 빌드 시간 최적화를 위한 증분 컴파일 사용
  • 번들 크기 모니터링 및 최적화

 7. 확장성

  • 향후 요구사항 변화를 고려한 유연한 구조 설계
  • 플러그인 아키텍처 고려

 8. 보안

  • 환경 변수를 통한 민감 정보 관리
  • 의존성 정기 업데이트 및 취약점 스캔

 9. 국제화(i18n) 고려

  • 텍스트 리소스 분리
  • RTL 지원 준비

 10. 접근성

  • 컴포넌트 레벨에서 접근성 고려
  • ARIA 속성 활용

 프로젝트의 확장성과 유지보수성을 고려한 구조 설계는 장기적인 프로젝트 성공에 핵심적입니다.

 모듈화, 일관된 네이밍, 충분한 문서화, 그리고 미래 요구사항에 대한 고려 등이 중요한 요소입니다.

 또한 성능, 보안, 국제화, 접근성 등 다양한 측면을 초기 설계 단계에서부터 고려해야 합니다.