Nx 모노레포와 타입스크립트 프로젝트 구성: 효율적인 개발 환경 구축하기 🚀
현대 웹 개발 환경에서 프로젝트의 복잡성이 증가함에 따라, 효율적인 코드 관리와 개발 생산성 향상이 더욱 중요해지고 있습니다. 이러한 요구에 부응하기 위해 Nx 모노레포와 타입스크립트를 결합한 프로젝트 구성이 주목받고 있습니다. 이 글에서는 Nx 모노레포의 개념부터 타입스크립트와의 통합, 그리고 실제 프로젝트에서의 적용 방법까지 상세히 알아보겠습니다. 🌟
특히, 재능넷과 같은 복잡한 웹 애플리케이션을 개발할 때 이러한 접근 방식은 매우 유용할 수 있습니다. 다양한 기능과 서비스를 제공하는 플랫폼에서 코드의 재사용성과 유지보수성을 높이는 것은 핵심적인 과제이기 때문입니다.
Nx 모노레포란? 🤔
Nx(Nrwl Extensions)는 모노레포(Monorepo) 아키텍처를 지원하는 강력한 개발 도구 세트입니다. 모노레포란 여러 프로젝트나 패키지를 하나의 저장소에서 관리하는 방식을 말합니다. 이 접근 방식은 코드 공유와 버전 관리를 용이하게 만들어, 대규모 프로젝트나 마이크로서비스 아키텍처에서 특히 유용합니다.
Nx의 주요 특징은 다음과 같습니다:
- 🔄 스마트한 빌드 시스템: 변경된 부분만 빌드하여 시간을 절약합니다.
- 🧩 코드 생성 도구: 보일러플레이트 코드를 쉽게 생성할 수 있습니다.
- 📊 의존성 그래프 시각화: 프로젝트 구조를 쉽게 파악할 수 있습니다.
- 🔧 플러그인 생태계: 다양한 프레임워크와 도구를 지원합니다.
타입스크립트와 Nx의 만남 💑
타입스크립트는 자바스크립트의 슈퍼셋 언어로, 정적 타입 검사와 최신 ECMAScript 기능을 제공합니다. Nx와 타입스크립트를 함께 사용하면 다음과 같은 이점을 얻을 수 있습니다:
- 향상된 개발자 경험: 타입 안정성과 자동 완성 기능으로 생산성이 향상됩니다.
- 버그 감소: 컴파일 시점에 많은 오류를 잡아낼 수 있습니다.
- 코드 품질 향상: 타입 시스템을 통해 더 명확하고 유지보수가 쉬운 코드를 작성할 수 있습니다.
- 리팩토링 용이성: 타입 정보를 활용해 안전한 리팩토링이 가능합니다.
Nx 모노레포 프로젝트 설정하기 🛠️
Nx를 사용하여 모노레포 프로젝트를 설정하는 과정을 단계별로 살펴보겠습니다:
1. Nx 워크스페이스 생성
먼저, Nx CLI를 사용하여 새로운 워크스페이스를 생성합니다:
npx create-nx-workspace@latest my-monorepo
cd my-monorepo
이 명령은 대화형 프롬프트를 통해 프로젝트 설정을 안내합니다. 타입스크립트를 사용할 것이므로, 해당 옵션을 선택해주세요.
2. 애플리케이션 및 라이브러리 추가
Nx 워크스페이스 내에서 여러 애플리케이션과 라이브러리를 생성할 수 있습니다:
nx g @nrwl/react:app my-app
nx g @nrwl/react:lib my-lib
이렇게 생성된 애플리케이션과 라이브러리는 자동으로 타입스크립트 설정이 적용됩니다.
3. 타입스크립트 설정 최적화
프로젝트 루트의 tsconfig.base.json
파일을 수정하여 전체 워크스페이스에 대한 타입스크립트 설정을 최적화할 수 있습니다:
{
"compileOnSave": false,
"compilerOptions": {
"rootDir": ".",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"importHelpers": true,
"target": "es2015",
"module": "esnext",
"lib": ["es2017", "dom"],
"skipLibCheck": true,
"skipDefaultLibCheck": true,
"baseUrl": ".",
"paths": {
"@my-monorepo/*": ["libs/*"]
}
},
"exclude": ["node_modules", "tmp"]
}
이 설정은 모듈 해결, 데코레이터 지원, 그리고 경로 별칭 등을 포함하고 있습니다.
Nx와 타입스크립트의 시너지 효과 🌈
Nx와 타입스크립트를 함께 사용하면 다음과 같은 시너지 효과를 얻을 수 있습니다:
1. 코드 공유의 용이성
타입스크립트의 인터페이스와 타입 정의를 사용하여 프로젝트 간 코드 공유가 더욱 안전하고 명확해집니다. 예를 들어, 공통 모델을 정의할 때:
// libs/shared/src/models/user.model.ts
export interface User {
id: string;
name: string;
email: string;
}
// 다른 프로젝트에서 사용
import { User } from '@my-monorepo/shared';
function processUser(user: User) {
console.log(`Processing user: ${user.name}`);
}
2. 빌드 최적화
Nx의 영향 받은 프로젝트만 빌드하는 기능과 타입스크립트의 증분 컴파일을 결합하여 빌드 시간을 대폭 단축할 수 있습니다:
nx affected:build --parallel
이 명령은 변경된 프로젝트와 그에 영향을 받는 프로젝트만 병렬로 빌드합니다.
3. 테스트 자동화
Nx의 테스트 러너와 타입스크립트의 타입 검사를 결합하여 더욱 강력한 테스트 자동화를 구현할 수 있습니다:
nx affected:test
이 명령은 변경된 프로젝트와 관련된 테스트만 실행하며, 타입스크립트의 타입 검사도 함께 수행합니다.
실제 프로젝트 구성 예시 📚
실제 프로젝트에서 Nx 모노레포와 타입스크립트를 어떻게 구성할 수 있는지 살펴보겠습니다. 예를 들어, 재능넷과 같은 플랫폼을 개발한다고 가정해봅시다:
프로젝트 구조
my-monorepo/
├── apps/
│ ├── web-client/
│ ├── admin-panel/
│ └── api/
├── libs/
│ ├── shared/
│ ├── ui-components/
│ └── data-access/
├── tools/
├── nx.json
├── package.json
└── tsconfig.base.json
주요 컴포넌트 설명
- apps/web-client: 사용자 facing 웹 애플리케이션
- apps/admin-panel: 관리자 대시보드
- apps/api: 백엔드 API 서버
- libs/shared: 공통 유틸리티 및 모델
- libs/ui-components: 재사용 가능한 UI 컴포넌트
- libs/data-access: API 통신 및 상태 관리 로직
코드 예시
1. 공통 모델 정의 (libs/shared/src/models/talent.model.ts):
export interface Talent {
id: string;
title: string;
description: string;
price: number;
seller: {
id: string;
name: string;
};
}
export type TalentStatus = 'active' | 'pending' | 'sold';
2. UI 컴포넌트 (libs/ui-components/src/talent-card/talent-card.tsx):
import React from 'react';
import { Talent } from '@my-monorepo/shared';
interface TalentCardProps {
talent: Talent;
onSelect: (id: string) => void;
}
export const TalentCard: React.FC<talentcardprops> = ({ talent, onSelect }) => {
return (
<div onclick="{()"> onSelect(talent.id)}>
<h3>{talent.title}</h3>
<p>{talent.description}</p>
<span>{talent.price}</span>
</div>
);
};</talentcardprops>
3. API 통신 (libs/data-access/src/talents/talents.api.ts):
import { Talent } from '@my-monorepo/shared';
export async function fetchTalents(): Promise<talent> {
const response = await fetch('/api/talents');
if (!response.ok) {
throw new Error('Failed to fetch talents');
}
return response.json();
}</talent>
4. 웹 클라이언트 사용 예시 (apps/web-client/src/app/talent-list/talent-list.tsx):
import React, { useEffect, useState } from 'react';
import { Talent } from '@my-monorepo/shared';
import { TalentCard } from '@my-monorepo/ui-components';
import { fetchTalents } from '@my-monorepo/data-access';
export const TalentList: React.FC = () => {
const [talents, setTalents] = useState<talent>([]);
useEffect(() => {
fetchTalents().then(setTalents);
}, []);
const handleSelect = (id: string) => {
console.log(`Selected talent: ${id}`);
};
return (
<div>
{talents.map(talent => (
<talentcard key="{talent.id}" talent="{talent}" onselect="{handleSelect}"></talentcard>
))}
</div>
);
};</talent>
Nx 모노레포와 타입스크립트의 고급 기능 활용 🚀
Nx 모노레포와 타입스크립트를 더욱 효과적으로 활용하기 위한 고급 기능들을 살펴보겠습니다.
1. 타입스크립트 경로 별칭 설정
경로 별칭을 사용하면 복잡한 상대 경로 대신 간단한 별칭을 사용할 수 있어 코드의 가독성이 향상됩니다. tsconfig.base.json
파일에서 다음과 같이 설정할 수 있습니다:
{
"compilerOptions": {
...
"paths": {
"@my-monorepo/shared": ["libs/shared/src/index.ts"],
"@my-monorepo/ui-components": ["libs/ui-components/src/index.ts"],
"@my-monorepo/data-access": ["libs/data-access/src/index.ts"]
}
}
}
이제 다음과 같이 간단하게 임포트할 수 있습니다:
import { Talent } from '@my-monorepo/shared';
2. 타입스크립트 프로젝트 참조 활용
타입스크립트의 프로젝트 참조 기능을 사용하면 빌드 성능을 향상시키고 프로젝트 간 의존성을 더 명확하게 관리할 수 있습니다. 각 프로젝트의 tsconfig.json
파일에 다음과 같이 설정합니다:
// apps/web-client/tsconfig.json
{
"extends": "../../tsconfig.base.json",
"references": [
{ "path": "../../libs/shared" },
{ "path": "../../libs/ui-components" },
{ "path": "../../libs/data-access" }
]
}
3. Nx 캐시 최적화
Nx의 캐시 기능을 활용하면 빌드 및 테스트 시간을 크게 단축할 수 있습니다. nx.json
파일에서 다음과 같이 설정합니다:
{
"tasksRunnerOptions": {
"default": {
"runner": "@nrwl/workspace/tasks-runners/default",
"options": {
"cacheableOperations": ["build", "lint", "test", "e2e"]
}
}
}
}
4. 커스텀 ESLint 규칙 설정
프로젝트 전체에 일관된 코드 스타일을 적용하기 위해 커스텀 ESLint 규칙을 설정할 수 있습니다. .eslintrc.json
파일을 다음과 같이 구성합니다:
{
"root": true,
"extends": ["plugin:@nrwl/nx/react", "prettier"],
"rules": {
"@typescript-eslint/explicit-module-boundary-types": "error",
"react-hooks/exhaustive-deps": "warn"
}
}
5. 타입스크립트 strict 모드 활성화
타입스크립트의 strict 모드를 활성화하면 더 엄격한 타입 검사를 통해 잠재적인 버그를 사전에 방지할 수 있습니다. tsconfig.base.json
파일에 다음 옵션을 추가합니다:
{
"compilerOptions": {
...
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true
}
}
성능 최적화 및 모범 사례 🔧
Nx 모노레포와 타입스크립트를 사용할 때 성능을 최적화하고 개발 경험을 향상시키기 위한 몇 가지 모범 사례를 소개합니다.
1. 증분 빌드 활용
Nx의 증분 빌드 기능을 활용하면 변경된 부분만 빌드하여 전체 빌드 시간을 크게 단축할 수 있습니다. 다음 명령어를 사용하세요:
nx affected:build --parallel
2. 코드 생성기 사용
Nx의 코드 생성기를 사용하면 보일러플레이트 코드를 빠르게 생성할 수 있습니다. 예를 들어, 새로운 리액트 컴포넌트를 생성하려면:
nx g @nrwl/react:component my-component --project=ui-components
3. 의존성 그래프 분석
Nx의 의존성 그래프 기능을 사용하여 프로젝트 구조를 시각화하고 분석할 수 있습니다:
nx dep-graph
이 명령어는 프로젝트의 의존성 구조를 브라우저에서 시각적으로 보여줍니다.
4. 타입스크립트 컴파일러 최적화
타입스크립트 컴파일러 옵션을 최적화하여 빌드 성능을 향상시킬 수 있습니다. tsconfig.base.json
에 다음 옵션을 추가하세요:
{
"compilerOptions": {
...
"incremental": true,
"tsBuildInfoFile": "./buildcache/front-end.tsbuildinfo"
}
}
5. 라이브러리 코드 분할
큰 라이브러리를 더 작은 단위로 분할하면 빌드 성능이 향상되고 코드 재사용성이 높아집니다. 예를 들어, UI 컴포넌트 라이브러리를 다음과 같이 구성할 수 있습니다:
libs/
ui-components/
buttons/
forms/
layouts/
modals/
6. 테스트 전략 최적화
효율적인 테스트 전략을 수립하여 개발 생산성을 높일 수 있습니다:
- 단위 테스트는 가능한 한 작은 단위로 작성합니다.
- 통합 테스트는 주요 기능 흐름에 집중합니다.
- E2E 테스트는 핵심 사용자 시나리오에 대해서만 작성합니다.
Nx의 테스트 실행 명령어를 활용하세요:
nx affected:test --parallel
실제 개발 시나리오: 재능넷 플랫폼 구현 🌟
지금까지 학습한 내용을 바탕으로, 재능넷과 같은 실제 플랫폼을 Nx 모노레포와 타입스크립트를 사용하여 구현하는 방법을 살펴보겠습니다.
1. 프로젝트 구조 설계
재능넷 플랫폼을 위한 프로젝트 구조를 다음과 같이 설계할 수 있습니다:
jaenung-net/
├── apps/
│ ├── web-client/ # 사용자 웹 인터페이스
│ ├── admin-panel/ # 관리자 대시보드
│ └── api/ # 백엔드 API 서버
├── libs/
│ ├── shared/
│ │ ├── models/ # 공통 데이터 모델
│ │ └── utils/ # 유틸리티 함수
│ ├── ui-components/ # 재사용 가능한 UI 컴포넌트
│ ├── data-access/ # API 통신 및 상태 관리
│ └── feature-modules/ # 기능별 모듈 (예: 검색, 결제 등)
├── tools/
├── nx.json
├── package.json
└── tsconfig.base.json
2. 공통 모델 정의
재능넷의 핵심 엔티티인 '재능'과 '사용자'에 대한 모델을 정의합니다: