Files
frontend/apps/_template/features
2026-02-26 11:55:09 +09:00
..
2026-02-26 11:55:09 +09:00

Features Layer

Features는 앱의 주요 사용자 상호작용과 기능을 구현하는 레이어입니다. 사용자가 실제로 수행하는 작업들로, 주로 비즈니스 엔티티와 관련이 있습니다.

목적

  • 사용자가 중요하게 생각하는 주요 상호작용 구현
  • 재사용 가능한 비즈니스 기능 제공
  • 여러 페이지에서 사용되는 기능의 중복 제거

Feature로 만들어야 하는 경우

⚠️ 중요: 모든 것을 feature로 만들 필요는 없습니다.

Feature로 만드는 것이 좋은 지표:

  • 여러 페이지에서 재사용됨: 예를 들어, 여러 에디터가 있고 모두 댓글 기능이 있다면, 댓글은 재사용 가능한 feature입니다
  • 중요한 비즈니스 기능: 신규 프로젝트 참여자가 Features 폴더를 보고 앱의 주요 기능을 빠르게 파악할 수 있어야 합니다

Feature가 너무 많으면 중요한 기능이 묻히므로, 신규 참여자의 경험을 최적화하여 결정하세요.

구조

각 feature 슬라이스는 다음 세그먼트로 구성됩니다:

features/
└── [feature-name]/
    ├── ui/            # 상호작용을 수행하는 UI (폼, 버튼 등)
    ├── api/           # 액션 수행에 필요한 API 호출 (선택적)
    ├── model/         # 검증 및 내부 상태 관리 (선택적)
    ├── lib/           # 유틸리티 함수 (선택적)
    ├── config/        # feature flags (선택적)
    └── index.ts       # Public API

세그먼트 설명

  • ui: 사용자 상호작용을 위한 UI 컴포넌트 (폼, 모달, 버튼 등)
  • api: feature 실행에 필요한 API 요청 함수
  • model: 폼 검증, 내부 상태, 비즈니스 로직
  • lib: feature 내부에서만 사용되는 유틸리티
  • config: feature flags, feature별 설정값
  • index.ts: 슬라이스의 Public API (외부에서 사용할 수 있는 것만 export)

예시

// features/auth-login/ui/login-form.tsx
import { useState } from 'react';
import { loginUser } from '../api/login-user';
import type { LoginCredentials } from '../model/types';

export function LoginForm() {
  const [credentials, setCredentials] = useState<LoginCredentials>({
    email: '',
    password: ''
  });

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    await loginUser(credentials);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={credentials.email}
        onChange={(e) => setCredentials({ ...credentials, email: e.target.value })}
      />
      <input
        type="password"
        value={credentials.password}
        onChange={(e) => setCredentials({ ...credentials, password: e.target.value })}
      />
      <button type="submit">로그인</button>
    </form>
  );
}

// features/auth-login/api/login-user.ts
import type { LoginCredentials } from '../model/types';

export async function loginUser(credentials: LoginCredentials) {
  const response = await fetch('/api/auth/login', {
    method: 'POST',
    body: JSON.stringify(credentials),
  });
  return response.json();
}

// features/auth-login/model/types.ts
export interface LoginCredentials {
  email: string;
  password: string;
}

// features/auth-login/index.ts
export { LoginForm } from './ui/login-form';
export { loginUser } from './api/login-user';
export type { LoginCredentials } from './model/types';

레이어 의존성 규칙

✅ 허용되는 의존성:

  • shared 레이어의 모듈
  • entities 레이어의 모듈
  • 같은 feature 슬라이스 내의 다른 세그먼트

❌ 금지된 의존성:

  • 다른 features 슬라이스 (feature 간 직접 의존 금지)
  • widgets 레이어
  • pages 레이어
  • app 레이어

💡 팁: 여러 features가 공통 기능을 필요로 한다면, 그 기능을 shared 레이어로 이동시키거나 entities 레이어에서 관리하세요.

명명 규칙

  • 소문자와 대시 사용: auth-login, post-create, comment-edit
  • 동사 형태 권장: 사용자 액션을 나타내는 이름 사용
  • 명확하고 구체적으로: 기능의 목적이 명확하게 드러나도록

주의사항

  1. 하나의 기능에만 집중: 각 feature는 단일 사용자 시나리오를 구현해야 합니다
  2. 재사용성 고려: 여러 페이지에서 사용될 수 있도록 설계
  3. 독립성 유지: 다른 features에 의존하지 않고 독립적으로 작동
  4. 적절한 추상화: 너무 많은 feature를 만들지 말고, 중요한 기능 위주로 구성