150 lines
5.1 KiB
Markdown
150 lines
5.1 KiB
Markdown
# Shared Layer
|
|
|
|
**Shared**는 애플리케이션의 기반을 형성하는 레이어입니다. 외부 세계(백엔드, 서드파티 라이브러리, 환경)와의 연결을 생성하고, 높은 응집력을 가진 자체 라이브러리를 정의하는 곳입니다.
|
|
|
|
## 목적
|
|
|
|
- 외부 시스템 및 라이브러리와의 통합
|
|
- 프로젝트 전체에서 사용되는 공통 코드 제공
|
|
- 비즈니스 로직이 없는 재사용 가능한 유틸리티 및 UI 제공
|
|
- 글로벌 설정 및 환경 변수 관리
|
|
|
|
## 특징
|
|
|
|
- **슬라이스 없음**: Shared 레이어는 비즈니스 도메인이 없으므로 슬라이스로 나뉘지 않습니다
|
|
- **세그먼트 구성**: 기술적 목적에 따라 세그먼트로 직접 구성됩니다
|
|
- **자유로운 import**: Shared 내의 모든 파일은 서로를 참조하고 import할 수 있습니다
|
|
|
|
## 구조
|
|
|
|
```
|
|
shared/
|
|
├── ui/ # 애플리케이션 UI Kit
|
|
│ ├── button/
|
|
│ ├── input/
|
|
│ ├── modal/
|
|
│ └── index.ts
|
|
├── api/ # API 클라이언트 및 백엔드 엔드포인트 함수
|
|
│ ├── client.ts
|
|
│ └── index.ts
|
|
├── lib/ # 내부 라이브러리 모음
|
|
│ ├── dates/ # 날짜 관련 유틸리티
|
|
│ ├── colors/ # 색상 관련 유틸리티
|
|
│ ├── text/ # 텍스트 처리 유틸리티
|
|
│ └── index.ts
|
|
├── config/ # 환경 변수, 글로벌 feature flags
|
|
│ ├── env.ts
|
|
│ └── index.ts
|
|
├── types/ # 공통 타입 정의
|
|
│ └── index.ts
|
|
├── i18n/ # 번역 설정, 글로벌 번역 문자열
|
|
│ └── index.ts
|
|
└── routes/ # 라우트 상수 또는 패턴
|
|
└── index.ts
|
|
```
|
|
|
|
## 세그먼트 설명
|
|
|
|
### 📁 ui
|
|
애플리케이션의 UI Kit로, 비즈니스 로직을 포함하지 않는 재사용 가능한 UI 컴포넌트들입니다.
|
|
|
|
- Button, Input, Modal 같은 기본 컴포넌트
|
|
- 비즈니스 로직은 없지만 UI 로직은 포함 가능 (예: Autocomplete, SearchBar)
|
|
- 비즈니스 테마 컴포넌트도 허용 (예: 회사 로고, 페이지 레이아웃)
|
|
|
|
### 📁 api
|
|
API 클라이언트 및 특정 백엔드 엔드포인트 요청 함수입니다.
|
|
|
|
- HTTP 클라이언트 설정 (Axios, Fetch 등)
|
|
- 인증 인터셉터
|
|
- 공통 에러 핸들링
|
|
|
|
### 📁 lib
|
|
내부 라이브러리 모음으로, 각 라이브러리는 **하나의 관심사**에 집중해야 합니다.
|
|
|
|
> ⚠️ **중요**: `helpers`나 `utils` 폴더처럼 덤프 폴더로 만들지 마세요. 각 라이브러리는 명확한 초점 영역(예: dates, colors, text)을 가져야 하며, README로 문서화해야 합니다.
|
|
|
|
### 📁 config
|
|
환경 변수, 글로벌 feature flags 및 앱 전체 설정입니다.
|
|
|
|
### 📁 types
|
|
프로젝트 전체에서 사용되는 공통 타입 정의입니다.
|
|
|
|
### 📁 i18n
|
|
번역 설정 코드 및 글로벌 번역 문자열입니다.
|
|
|
|
### 📁 routes
|
|
라우트 상수 또는 라우트 매칭 패턴입니다.
|
|
|
|
## 예시
|
|
|
|
```typescript
|
|
// shared/ui/button/button.tsx
|
|
interface ButtonProps {
|
|
children: React.ReactNode;
|
|
variant?: 'primary' | 'secondary';
|
|
onClick?: () => void;
|
|
}
|
|
|
|
export function Button({ children, variant = 'primary', onClick }: ButtonProps) {
|
|
return (
|
|
<button
|
|
className={variant === 'primary' ? 'bg-blue-500' : 'bg-gray-500'}
|
|
onClick={onClick}
|
|
>
|
|
{children}
|
|
</button>
|
|
);
|
|
}
|
|
|
|
// shared/api/client.ts
|
|
import axios from 'axios';
|
|
|
|
export const apiClient = axios.create({
|
|
baseURL: process.env.NEXT_PUBLIC_API_URL,
|
|
timeout: 10000,
|
|
});
|
|
|
|
// shared/lib/dates/format-date.ts
|
|
import dayjs from 'dayjs';
|
|
|
|
export function formatDate(date: Date, format: string = 'YYYY-MM-DD'): string {
|
|
return dayjs(date).format(format);
|
|
}
|
|
|
|
// shared/config/env.ts
|
|
export const ENV = {
|
|
apiUrl: process.env.NEXT_PUBLIC_API_URL!,
|
|
isDev: process.env.NODE_ENV === 'development',
|
|
} as const;
|
|
```
|
|
|
|
## 레이어 의존성 규칙
|
|
|
|
✅ **허용되는 의존성**:
|
|
- 같은 `shared` 레이어 내의 다른 세그먼트 (자유로운 cross-import)
|
|
- 외부 라이브러리 및 패키지
|
|
|
|
❌ **금지된 의존성**:
|
|
- `entities` 레이어
|
|
- `features` 레이어
|
|
- `widgets` 레이어
|
|
- `pages` 레이어
|
|
- `app` 레이어
|
|
|
|
> 💡 **원칙**: Shared는 가장 낮은 레이어이므로 상위 레이어에 의존하지 않습니다.
|
|
|
|
## 세그먼트 명명 규칙
|
|
|
|
세그먼트 이름은 **내용의 목적**을 설명해야 하며, **본질**을 설명하면 안 됩니다.
|
|
|
|
❌ **나쁜 예시**: `components`, `hooks`, `types` (본질을 설명)
|
|
✅ **좋은 예시**: `ui`, `api`, `lib/dates`, `config` (목적을 설명)
|
|
|
|
## 주의사항
|
|
|
|
1. **비즈니스 로직 없음**: Shared는 순수한 기술적 코드만 포함
|
|
2. **높은 재사용성**: 앱 전체에서 사용될 수 있어야 함
|
|
3. **명확한 라이브러리 구조**: `lib` 내부는 관심사별로 명확하게 분리
|
|
4. **문서화**: 각 라이브러리는 README로 사용법과 목적을 문서화
|