Copilot으로 코드 구성
잘 구성된 코드 구조는 장기적인 유지 관리에 매우 중요합니다. 이 기사에서는 Copilot을 사용하여 정의하는 방법을 살펴보겠습니다. 폴더 구조, 명명 규칙, 구성 관리 전자 깨끗하고 전문적인 코드베이스를 위한 패턴입니다.
🗂️ 코드 구성 원칙
- 응집력: 관련 파일은 서로 가까이 유지됩니다.
- 예측 가능성: 모든 것이 어디에 있는지 쉽게 찾을 수 있습니다.
- 확장성: 프로젝트가 성장함에 따라 구조도 작동합니다.
- 일관성: 동일한 규칙이 모든 곳에 적용됩니다.
- 검색 가능성: 새로운 개발자는 자신이 원하는 것을 즉시 찾습니다.
시리즈 개요
| # | 기준 치수 | 상태 |
|---|---|---|
| 1 | 재단 – 파트너로서의 부조종사 | ✅ 완료됨 |
| 2 | 개념 및 요구사항 | ✅ 완료됨 |
| 3 | 백엔드 및 프런트엔드 아키텍처 | ✅ 완료됨 |
| 4 | 강령 및 조직의 구조 | 📍 당신은 여기에 있습니다 |
| 5 | 프롬프트 엔지니어링 및 MCP 에이전트 | ⏳ 다음 |
| 6 | 테스트 및 품질 | ⏳ |
| 7 | 선적 서류 비치 | ⏳ |
| 8 | 배포 및 DevOps | ⏳ |
| 9 | 진화와 유지 | ⏳ |
폴더 구조 백엔드 완료
Create a detailed folder structure for a Node.js + Express + TypeScript backend.
PROJECT: TaskFlow (time tracking + task management)
ARCHITECTURE: Layered (Controller → Service → Repository)
Requirements:
- Clean Architecture layers clearly separated
- Modular organization by feature/domain
- Clear separation of concerns
- Support for unit, integration, and e2e testing
- Environment-based configuration
- Database migrations and seeds
- Shared utilities and helpers
- API documentation structure
Show the complete tree with explanations for each folder/file.
권장 백엔드 구조
backend/
├── src/
│ ├── config/ # 🔧 Application configuration
│ │ ├── index.ts # Main config export (loads from env)
│ │ ├── database.config.ts # Database connection settings
│ │ ├── auth.config.ts # JWT, OAuth settings
│ │ ├── cors.config.ts # CORS policy
│ │ └── rate-limit.config.ts # Rate limiting rules
│ │
│ ├── modules/ # 📦 Feature modules (domain-driven)
│ │ ├── users/
│ │ │ ├── users.controller.ts # HTTP handling
│ │ │ ├── users.service.ts # Business logic
│ │ │ ├── users.repository.ts # Data access
│ │ │ ├── users.routes.ts # Route definitions
│ │ │ ├── dto/ # Data Transfer Objects
│ │ │ │ ├── create-user.dto.ts
│ │ │ │ ├── update-user.dto.ts
│ │ │ │ └── user-response.dto.ts
│ │ │ ├── entities/ # Domain entities
│ │ │ │ └── user.entity.ts
│ │ │ └── __tests__/ # Module-specific tests
│ │ │ ├── users.service.spec.ts
│ │ │ └── users.controller.spec.ts
│ │ │
│ │ ├── auth/
│ │ │ ├── auth.controller.ts
│ │ │ ├── auth.service.ts
│ │ │ ├── strategies/ # Passport strategies
│ │ │ │ ├── jwt.strategy.ts
│ │ │ │ └── local.strategy.ts
│ │ │ ├── dto/
│ │ │ └── auth.routes.ts
│ │ │
│ │ ├── tasks/
│ │ │ ├── tasks.controller.ts
│ │ │ ├── tasks.service.ts
│ │ │ ├── tasks.repository.ts
│ │ │ ├── tasks.routes.ts
│ │ │ ├── dto/
│ │ │ ├── entities/
│ │ │ └── __tests__/
│ │ │
│ │ └── time-entries/
│ │ └── ... (same structure)
│ │
│ ├── shared/ # 🔄 Shared across modules
│ │ ├── middleware/
│ │ │ ├── auth.middleware.ts
│ │ │ ├── validation.middleware.ts
│ │ │ ├── rate-limit.middleware.ts
│ │ │ └── request-logger.middleware.ts
│ │ ├── guards/
│ │ │ ├── roles.guard.ts
│ │ │ └── ownership.guard.ts
│ │ ├── decorators/
│ │ │ ├── current-user.decorator.ts
│ │ │ └── roles.decorator.ts
│ │ ├── errors/
│ │ │ ├── app-error.ts
│ │ │ ├── validation.error.ts
│ │ │ ├── not-found.error.ts
│ │ │ └── unauthorized.error.ts
│ │ ├── utils/
│ │ │ ├── hash.util.ts
│ │ │ ├── jwt.util.ts
│ │ │ ├── pagination.util.ts
│ │ │ └── date.util.ts
│ │ └── types/
│ │ ├── express.d.ts # Express type extensions
│ │ └── common.types.ts
│ │
│ ├── database/ # 💾 Database setup
│ │ ├── connection.ts # DB connection pool
│ │ ├── migrations/ # Schema migrations
│ │ │ ├── 001_create_users.ts
│ │ │ ├── 002_create_tasks.ts
│ │ │ └── 003_create_time_entries.ts
│ │ ├── seeds/ # Test/dev data
│ │ │ ├── users.seed.ts
│ │ │ └── tasks.seed.ts
│ │ └── queries/ # Complex SQL queries
│ │ └── reports.queries.ts
│ │
│ ├── api/ # 🌐 API versioning
│ │ ├── v1/
│ │ │ └── index.ts # v1 router aggregator
│ │ └── index.ts # Main API router
│ │
│ ├── app.ts # Express app setup
│ └── server.ts # Entry point (starts server)
│
├── tests/ # 🧪 Test files (by type)
│ ├── unit/ # Isolated unit tests
│ ├── integration/ # API integration tests
│ │ └── users.integration.spec.ts
│ ├── e2e/ # End-to-end tests
│ ├── fixtures/ # Test data
│ │ └── users.fixture.ts
│ └── helpers/ # Test utilities
│ ├── db.helper.ts # Test DB setup/teardown
│ └── auth.helper.ts # Get test tokens
│
├── scripts/ # 📜 Utility scripts
│ ├── migrate.ts # Run migrations
│ ├── seed.ts # Seed database
│ └── generate-types.ts # Generate TS from DB
│
├── docs/ # 📚 Documentation
│ ├── api/ # OpenAPI specs
│ │ └── openapi.yaml
│ └── adr/ # Architecture Decision Records
│ └── 001-database-choice.md
│
├── .env.example # Environment template
├── .env.test # Test environment
├── tsconfig.json
├── jest.config.js
├── package.json
└── README.md
폴더 구조 프런트엔드 완료
frontend/
├── src/
│ ├── app/
│ │ ├── core/ # 🔧 Singleton services (providedIn: 'root')
│ │ │ ├── services/
│ │ │ │ ├── api.service.ts # HTTP client wrapper
│ │ │ │ ├── auth.service.ts # Auth state management
│ │ │ │ ├── storage.service.ts # LocalStorage/SessionStorage
│ │ │ │ ├── toast.service.ts # Notification service
│ │ │ │ └── theme.service.ts # Dark/light mode
│ │ │ ├── guards/
│ │ │ │ ├── auth.guard.ts # Protect routes
│ │ │ │ ├── guest.guard.ts # Redirect if logged in
│ │ │ │ └── unsaved-changes.guard.ts # Prevent accidental navigation
│ │ │ ├── interceptors/
│ │ │ │ ├── auth.interceptor.ts # Add JWT to requests
│ │ │ │ ├── error.interceptor.ts # Global error handling
│ │ │ │ ├── loading.interceptor.ts # Show/hide loading
│ │ │ │ └── retry.interceptor.ts # Retry failed requests
│ │ │ └── models/
│ │ │ ├── api-response.model.ts
│ │ │ └── user.model.ts
│ │ │
│ │ ├── shared/ # 🔄 Reusable components (import in features)
│ │ │ ├── components/
│ │ │ │ ├── ui/ # Design system primitives
│ │ │ │ │ ├── button/
│ │ │ │ │ │ ├── button.component.ts
│ │ │ │ │ │ ├── button.component.html
│ │ │ │ │ │ ├── button.component.scss
│ │ │ │ │ │ └── button.component.spec.ts
│ │ │ │ │ ├── input/
│ │ │ │ │ ├── select/
│ │ │ │ │ ├── checkbox/
│ │ │ │ │ ├── modal/
│ │ │ │ │ ├── tooltip/
│ │ │ │ │ └── dropdown/
│ │ │ │ ├── layout/ # Layout components
│ │ │ │ │ ├── card/
│ │ │ │ │ ├── page-header/
│ │ │ │ │ └── empty-state/
│ │ │ │ └── feedback/ # Feedback components
│ │ │ │ ├── loading-spinner/
│ │ │ │ ├── skeleton/
│ │ │ │ ├── error-message/
│ │ │ │ └── toast/
│ │ │ ├── directives/
│ │ │ │ ├── click-outside.directive.ts
│ │ │ │ ├── autofocus.directive.ts
│ │ │ │ ├── debounce-click.directive.ts
│ │ │ │ └── tooltip.directive.ts
│ │ │ ├── pipes/
│ │ │ │ ├── time-ago.pipe.ts
│ │ │ │ ├── duration.pipe.ts
│ │ │ │ ├── truncate.pipe.ts
│ │ │ │ └── highlight.pipe.ts
│ │ │ └── index.ts # Barrel export
│ │ │
│ │ ├── features/ # 📦 Feature modules (lazy loaded)
│ │ │ ├── auth/
│ │ │ │ ├── components/
│ │ │ │ │ ├── login-form/
│ │ │ │ │ ├── register-form/
│ │ │ │ │ └── forgot-password-form/
│ │ │ │ ├── pages/
│ │ │ │ │ ├── login.page.ts
│ │ │ │ │ ├── register.page.ts
│ │ │ │ │ └── forgot-password.page.ts
│ │ │ │ ├── services/
│ │ │ │ │ └── auth-api.service.ts
│ │ │ │ ├── models/
│ │ │ │ │ └── auth.model.ts
│ │ │ │ └── auth.routes.ts
│ │ │ │
│ │ │ ├── tasks/
│ │ │ │ ├── components/
│ │ │ │ │ ├── task-list/
│ │ │ │ │ ├── task-item/
│ │ │ │ │ ├── task-form/
│ │ │ │ │ ├── task-timer/
│ │ │ │ │ └── task-filters/
│ │ │ │ ├── pages/
│ │ │ │ │ ├── tasks-list.page.ts
│ │ │ │ │ └── task-detail.page.ts
│ │ │ │ ├── services/
│ │ │ │ │ └── tasks-api.service.ts
│ │ │ │ ├── state/ # Feature-specific state
│ │ │ │ │ └── tasks.store.ts # Signals-based store
│ │ │ │ ├── models/
│ │ │ │ │ └── task.model.ts
│ │ │ │ └── tasks.routes.ts
│ │ │ │
│ │ │ ├── dashboard/
│ │ │ │ ├── components/
│ │ │ │ │ ├── stats-card/
│ │ │ │ │ ├── recent-tasks/
│ │ │ │ │ ├── time-summary/
│ │ │ │ │ └── quick-actions/
│ │ │ │ ├── pages/
│ │ │ │ │ └── dashboard.page.ts
│ │ │ │ └── dashboard.routes.ts
│ │ │ │
│ │ │ └── settings/
│ │ │ ├── components/
│ │ │ ├── pages/
│ │ │ └── settings.routes.ts
│ │ │
│ │ ├── layouts/ # 🖼️ Layout components
│ │ │ ├── main-layout/
│ │ │ │ ├── main-layout.component.ts
│ │ │ │ ├── components/
│ │ │ │ │ ├── header/
│ │ │ │ │ ├── sidebar/
│ │ │ │ │ └── footer/
│ │ │ └── auth-layout/
│ │ │ └── auth-layout.component.ts
│ │ │
│ │ ├── app.component.ts
│ │ ├── app.routes.ts # Root routes
│ │ └── app.config.ts # App providers
│ │
│ ├── assets/
│ │ ├── images/
│ │ ├── icons/
│ │ │ └── svg/ # SVG icons
│ │ └── fonts/
│ │
│ ├── styles/
│ │ ├── _variables.scss # Design tokens
│ │ ├── _mixins.scss # SCSS mixins
│ │ ├── _typography.scss # Typography rules
│ │ ├── _utilities.scss # Utility classes
│ │ └── global.scss # Global styles
│ │
│ └── environments/
│ ├── environment.ts
│ └── environment.prod.ts
│
├── angular.json
├── tsconfig.json
└── package.json
명명 규칙
일관된 이름 지정은 Copilot이 일관된 코드를 생성하고 코드베이스를 만드는 데 도움이 됩니다. 팀의 모든 사람이 더 쉽게 읽을 수 있습니다(6개월 후 본인 포함).
📝 명명 규칙
| 유형 | 협약 | Esempio |
|---|---|---|
| 파일(일반) | 케밥집 | user-profile.comComponent.ts |
| 수업 | 파스칼 케이스 | 사용자 프로필 구성 요소 |
| 변수/함수 | 낙타 케이스 | getUserProfile() |
| 끊임없는 | 비명_SNAKE_CASE | MAX_RETRY_COUNT |
| 인터페이스 | PascalCase(접두사 없음) | 사용자 프로필 |
| 유형 | 파스칼 케이스 | 작업상태 |
| 열거형 | 파스칼 케이스 | 사용자 역할 |
| 열거형 멤버 | 비명_SNAKE_CASE | 사용자역할.ADMIN |
| 부울 변수 | is/has/can 접두사 | isLoading, hasError |
| 이벤트 핸들러 | on/handle 접두사 | onSubmit, 핸들클릭 |
| 주목할 만한 | $ 접미사 | 사용자$, isLoading$ |
| 신호 | 접미사 없음(Angular 18+) | 사용자, isLoading |
파일에 권장되는 접미사
백엔드
// Controllers
user.controller.ts → UserController
// Services
user.service.ts → UserService
// Repositories
user.repository.ts → UserRepository
// Entities
user.entity.ts → User
// DTOs
create-user.dto.ts → CreateUserDto
update-user.dto.ts → UpdateUserDto
// Middleware
auth.middleware.ts → authMiddleware
// Guards
roles.guard.ts → RolesGuard
// Routes
user.routes.ts → userRoutes
프런트엔드(Angular)
// Components
user-list.component.ts → UserListComponent
// Pages (Smart Components)
users-list.page.ts → UsersListPage
// Services
user.service.ts → UserService
user-api.service.ts → UserApiService
// Models
user.model.ts → User (interface)
// Guards
auth.guard.ts → authGuard (function)
// Interceptors
auth.interceptor.ts → authInterceptor
// Pipes
time-ago.pipe.ts → TimeAgoPipe
// Directives
tooltip.directive.ts → TooltipDirective
우려사항의 분리
각 파일/클래스에는 하나의 명확한 책임이 있어야 합니다. 이렇게 하면 코드를 더욱 테스트 가능하고 재사용 가능하며 유지 관리 가능하게 됩니다.
❌ 틀림: God Controller
// user.controller.ts - TUTTO insieme
class UserController {{ '{' }}
async create(req, res) {{ '{' }}
// 1. Validation (dovrebbe essere in middleware)
if (!req.body.email) {{ '{' }}
return res.status(400).json(...);
{{ '}' }}
if (!/^[^\s@]+@[^\s@]+$/.test(req.body.email)) {{ '{' }}
return res.status(400).json(...);
{{ '}' }}
// 2. Business logic (dovrebbe essere in service)
const exists = await db.query('SELECT...');
if (exists) throw new Error('...');
const hashedPassword = await bcrypt.hash(...);
// 3. Database (dovrebbe essere in repository)
await db.query('INSERT INTO users...');
// 4. Send email (dovrebbe essere in service)
await sendgrid.send({{ '{' }}
to: req.body.email,
subject: 'Welcome!',
...
{{ '}' }});
res.json({{ '{' }} success: true {{ '}' }});
{{ '}' }}
{{ '}' }}
✅ 올바른 예: 별도의 책임
// Controller: SOLO HTTP handling
async create(req, res, next) {{ '{' }}
try {{ '{' }}
const user = await this.userService
.createUser(req.body);
res.status(201).json({{ '{' }} data: user {{ '}' }});
{{ '}' }} catch (err) {{ '{' }}
next(err);
{{ '}' }}
{{ '}' }}
// Service: SOLO business logic
async createUser(dto: CreateUserDto) {{ '{' }}
await this.validateUniqueEmail(dto.email);
const user = new User(dto);
const saved = await this.userRepo.save(user);
await this.emailService.sendWelcome(saved);
return saved;
{{ '}' }}
// Repository: SOLO data access
async save(user: User): Promise<User> {{ '{' }}
return this.db.users.create(user);
{{ '}' }}
// EmailService: SOLO email
async sendWelcome(user: User) {{ '{' }}
await this.mailer.send({{ '{' }}...{{ '}' }});
{{ '}' }}
구성 관리
검증 및 유형 안전성을 통해 환경별로 구성을 중앙 집중화합니다.
// config/index.ts
import dotenv from 'dotenv';
import {{ '{' }} z {{ '}' }} from 'zod';
// Load .env file
dotenv.config();
// Define schema with validation
const configSchema = z.object({{ '{' }}
// Environment
NODE_ENV: z.enum(['development', 'test', 'production']).default('development'),
PORT: z.string().transform(Number).default('3000'),
// Database
DATABASE_URL: z.string().url(),
DATABASE_POOL_MIN: z.string().transform(Number).default('2'),
DATABASE_POOL_MAX: z.string().transform(Number).default('10'),
// Authentication
JWT_SECRET: z.string().min(32),
JWT_EXPIRES_IN: z.string().default('7d'),
BCRYPT_ROUNDS: z.string().transform(Number).default('12'),
// CORS
CORS_ORIGIN: z.string().default('http://localhost:4200'),
// External Services (optional in dev)
SENDGRID_API_KEY: z.string().optional(),
SENTRY_DSN: z.string().optional(),
{{ '}' }});
// Parse and validate
const parsed = configSchema.safeParse(process.env);
if (!parsed.success) {{ '{' }}
console.error('❌ Invalid environment variables:');
console.error(parsed.error.format());
process.exit(1);
{{ '}' }}
// Export typed config
export const config = {{ '{' }}
env: parsed.data.NODE_ENV,
port: parsed.data.PORT,
isProduction: parsed.data.NODE_ENV === 'production',
isDevelopment: parsed.data.NODE_ENV === 'development',
database: {{ '{' }}
url: parsed.data.DATABASE_URL,
pool: {{ '{' }}
min: parsed.data.DATABASE_POOL_MIN,
max: parsed.data.DATABASE_POOL_MAX,
{{ '}' }},
{{ '}' }},
jwt: {{ '{' }}
secret: parsed.data.JWT_SECRET,
expiresIn: parsed.data.JWT_EXPIRES_IN,
{{ '}' }},
bcrypt: {{ '{' }}
rounds: parsed.data.BCRYPT_ROUNDS,
{{ '}' }},
cors: {{ '{' }}
origin: parsed.data.CORS_ORIGIN.split(','),
{{ '}' }},
sendgrid: {{ '{' }}
apiKey: parsed.data.SENDGRID_API_KEY,
{{ '}' }},
sentry: {{ '{' }}
dsn: parsed.data.SENTRY_DSN,
{{ '}' }},
{{ '}' }} as const;
// Type for config
export type Config = typeof config;
.env.예제 파일
# ═══════════════════════════════════════════════════════════
# TaskFlow Backend Configuration
# Copy this file to .env and fill in your values
# ═══════════════════════════════════════════════════════════
# ─────────────────────────────────────────────────────────────
# Environment
# ─────────────────────────────────────────────────────────────
NODE_ENV=development
PORT=3000
# ─────────────────────────────────────────────────────────────
# Database (PostgreSQL)
# ─────────────────────────────────────────────────────────────
DATABASE_URL=postgresql://user:password@localhost:5432/taskflow
DATABASE_POOL_MIN=2
DATABASE_POOL_MAX=10
# ─────────────────────────────────────────────────────────────
# Authentication
# ─────────────────────────────────────────────────────────────
# Generate with: openssl rand -base64 32
JWT_SECRET=your-super-secret-key-at-least-32-chars
JWT_EXPIRES_IN=7d
BCRYPT_ROUNDS=12
# ─────────────────────────────────────────────────────────────
# CORS
# ─────────────────────────────────────────────────────────────
CORS_ORIGIN=http://localhost:4200
# ─────────────────────────────────────────────────────────────
# External Services (optional for development)
# ─────────────────────────────────────────────────────────────
# SENDGRID_API_KEY=SG.xxxxx
# SENTRY_DSN=https://xxxxx@sentry.io/xxxxx
인덱스 파일 및 배럴 내보내기
깔끔한 내보내기와 단순화된 가져오기를 위해 index.ts 파일을 사용하세요.
// ═══════════════════════════════════════════════════════════
// shared/components/index.ts - Barrel Export
// ═══════════════════════════════════════════════════════════
// UI Components
export {{ '{' }} ButtonComponent {{ '}' }} from './ui/button/button.component';
export {{ '{' }} InputComponent {{ '}' }} from './ui/input/input.component';
export {{ '{' }} SelectComponent {{ '}' }} from './ui/select/select.component';
export {{ '{' }} ModalComponent {{ '}' }} from './ui/modal/modal.component';
// Layout Components
export {{ '{' }} CardComponent {{ '}' }} from './layout/card/card.component';
export {{ '{' }} PageHeaderComponent {{ '}' }} from './layout/page-header/page-header.component';
export {{ '{' }} EmptyStateComponent {{ '}' }} from './layout/empty-state/empty-state.component';
// Feedback Components
export {{ '{' }} LoadingSpinnerComponent {{ '}' }} from './feedback/loading-spinner/loading-spinner.component';
export {{ '{' }} SkeletonComponent {{ '}' }} from './feedback/skeleton/skeleton.component';
export {{ '{' }} ToastComponent {{ '}' }} from './feedback/toast/toast.component';
// ═══════════════════════════════════════════════════════════
// Usage in feature module
// ═══════════════════════════════════════════════════════════
// PRIMA (senza barrel)
import {{ '{' }} ButtonComponent {{ '}' }} from '../../../shared/components/ui/button/button.component';
import {{ '{' }} InputComponent {{ '}' }} from '../../../shared/components/ui/input/input.component';
import {{ '{' }} CardComponent {{ '}' }} from '../../../shared/components/layout/card/card.component';
// DOPO (con barrel) ✅
import {{ '{' }}
ButtonComponent,
InputComponent,
CardComponent
{{ '}' }} from '@shared/components';
// ═══════════════════════════════════════════════════════════
// tsconfig.json - Path aliases
// ═══════════════════════════════════════════════════════════
{{ '{' }}
"compilerOptions": {{ '{' }}
"baseUrl": "./src",
"paths": {{ '{' }}
"@shared/*": ["app/shared/*"],
"@core/*": ["app/core/*"],
"@features/*": ["app/features/*"],
"@environments/*": ["environments/*"]
{{ '}' }}
{{ '}' }}
{{ '}' }}
수입기관
// ═══════════════════════════════════════════════════════════
// Ordine consigliato degli import
// ═══════════════════════════════════════════════════════════
// 1. Angular/Framework imports
import {{ '{' }} Component, OnInit, inject {{ '}' }} from '@angular/core';
import {{ '{' }} CommonModule {{ '}' }} from '@angular/common';
import {{ '{' }} RouterModule {{ '}' }} from '@angular/router';
import {{ '{' }} FormBuilder, ReactiveFormsModule {{ '}' }} from '@angular/forms';
// 2. Third-party library imports
import {{ '{' }} Observable {{ '}' }} from 'rxjs';
import {{ '{' }} map, catchError {{ '}' }} from 'rxjs/operators';
// 3. Core/Shared imports (using path aliases)
import {{ '{' }} AuthService {{ '}' }} from '@core/services/auth.service';
import {{ '{' }} ButtonComponent, CardComponent {{ '}' }} from '@shared/components';
import {{ '{' }} TimeAgoPipe {{ '}' }} from '@shared/pipes';
// 4. Feature-specific imports (relative paths)
import {{ '{' }} TasksApiService {{ '}' }} from './services/tasks-api.service';
import {{ '{' }} TaskItemComponent {{ '}' }} from './components/task-item/task-item.component';
import {{ '{' }} Task {{ '}' }} from './models/task.model';
// 5. Constants and types
import {{ '{' }} TASK_STATUS {{ '}' }} from './constants/task.constants';
import type {{ '{' }} TaskFilters {{ '}' }} from './types/task.types';
Copilot용 템플릿 파일
Copilot이 참조하여 일관된 코드를 생성할 수 있는 템플릿을 만듭니다.
// ═══════════════════════════════════════════════════════════
// Template: Angular Standalone Component
// ═══════════════════════════════════════════════════════════
import {{ '{' }} Component, Input, Output, EventEmitter, ChangeDetectionStrategy {{ '}' }} from '@angular/core';
import {{ '{' }} CommonModule {{ '}' }} from '@angular/common';
/**
* [ComponentName] - [Brief description]
*
* @example
* <app-[component-name]
* [inputProp]="value"
* (outputEvent)="handler($event)"
* />
*/
@Component({{ '{' }}
selector: 'app-[component-name]',
standalone: true,
imports: [CommonModule],
templateUrl: './[component-name].component.html',
styleUrl: './[component-name].component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
{{ '}' }})
export class [ComponentName]Component {{ '{' }}
// ─────────────────────────────────────────────────────────
// Inputs
// ─────────────────────────────────────────────────────────
@Input() inputProp: string = '';
// ─────────────────────────────────────────────────────────
// Outputs
// ─────────────────────────────────────────────────────────
@Output() outputEvent = new EventEmitter<void>();
// ─────────────────────────────────────────────────────────
// Public methods
// ─────────────────────────────────────────────────────────
onAction(): void {{ '{' }}
this.outputEvent.emit();
{{ '}' }}
{{ '}' }}
코드 구성 체크리스트
✅ 체크리스트
| Item | 완전한 |
|---|---|
| 폴더 구조가 정의되고 문서화됨 | ☐ |
| 문서화된 명명 규칙 | ☐ |
| tsconfig에 구성된 경로 별칭 | ☐ |
| 공유 모듈을 위한 배럴 내보내기 | ☐ |
| 검증을 통한 중앙 집중식 구성 관리 | ☐ |
| 모든 변수가 포함된 .env.example | ☐ |
| ESLint/Prettier 구성 | ☐ |
| 사전 커밋 후크를 위한 Husky + Lint-staged | ☐ |
결론
잘 구성된 코드 구조는 프로젝트 수명 전반에 걸쳐 배당금을 지급합니다. 명확한 규칙을 정의하는 데 시간을 투자하면 개발 속도가 빨라지고, 더 유지 관리하기 쉬운 코드 및 새로운 기여자 온보딩(또는 6개월 후 본인) 훨씬 간단합니다.
🎯 기사의 핵심 사항
- 기능 기반 구조: 파일 유형이 아닌 기능별로 정리
- 명명 규칙: 파일용 kebab-case, 클래스용 PascalCase
- 우려사항 분리: 컨트롤러 → 서비스 → 저장소
- 구성 관리: 스키마 검증으로 중앙화(Zod)
- 배럴 수출: 깨끗한 가져오기를 위한 Index.ts
- 경로 별칭: @shared, @core, @피해야 할 기능 ../../../
📚 다음 기사
다음 기사에서 "신속한 엔지니어링 및 MCP 에이전트" 효과적인 프롬프트 작성을 위한 고급 기술과 설정 방법을 살펴보겠습니다. 프로젝트의 지속적인 컨텍스트를 위한 MCP 에이전트.







