SOC 직업 분류 기준
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/Objective-Arts/lens-dist --skill angular-core명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
| name | angular-core |
| description | Angular patterns and testable architecture |
| allowed-tools | [] |
Miško Hevery's core belief: If it's hard to test, it's hard to use. Testability is not a feature—it's a design constraint that produces better architecture.
"The secret to writing testable code is to write code that is easy to test."
This sounds circular but isn't. Code that's easy to test has:
DI isn't just a pattern—it's the foundation of maintainable systems.
Not this:
class UserService {
private http = new HttpClient(); // Hidden dependency
private cache = new CacheService(); // Hidden dependency
getUser(id: string) {
return this.http.get(`/users/${id}`);
}
}
This:
@Injectable({ providedIn: 'root' })
class UserService {
constructor(
private http: HttpClient, // Explicit, injectable
private cache: CacheService // Explicit, injectable
) {}
getUser(id: string) {
return this.http.get(`/users/${id}`);
}
}
Why it matters:
Only inject what you directly use. Don't inject a service to get another service.
Not this:
class OrderComponent {
constructor(private userService: UserService) {}
getDiscount() {
// Reaching through userService to get pricingService
return this.userService.pricingService.calculateDiscount();
}
}
This:
class OrderComponent {
constructor(private pricingService: PricingService) {}
getDiscount() {
return this.pricingService.calculateDiscount();
}
}
Constructors are for assignment only. No logic, no calls, no initialization.
Not this:
class DashboardComponent {
data: any[];
constructor(private api: ApiService) {
this.data = this.api.fetchData(); // Logic in constructor
this.processData(); // Method call in constructor
}
}
This:
class DashboardComponent implements OnInit {
data: any[];
constructor(private api: ApiService) {} // Assignment only
ngOnInit() {
this.api.fetchData().subscribe(data => {
this.data = this.processData(data);
});
}
}
Why: Constructors run during DI resolution. Side effects there are:
Angular components should compose, not inherit.
Not this:
class BaseTableComponent {
sort() { /* ... */ }
filter() { /* ... */ }
paginate() { /* ... */ }
}
class UserTableComponent extends BaseTableComponent {
// Inherits everything, even what it doesn't need
}
This:
// Composable services
@Injectable()
class SortService { sort<T>(data: T[]) { /* ... */ } }
@Injectable()
class FilterService { filter<T>(data: T[]) { /* ... */ } }
class UserTableComponent {
constructor(
private sortService: SortService,
private filterService: FilterService
// Only inject what you need
) {}
}
Separate concerns: Smart components manage state, presentational components render.
SMART COMPONENT PRESENTATIONAL COMPONENT
────────────── ────────────────────────
Knows about services Knows only @Input/@Output
Manages state Stateless (mostly)
Handles side effects Pure rendering
Few in app Many in app
Hard to test Easy to test
Example:
// Smart component - has dependencies
@Component({
template: `<user-card [user]="user$ | async" (delete)="onDelete($event)">`
})
class UserContainerComponent {
user$ = this.store.select(selectCurrentUser);
constructor(private store: Store) {}
onDelete(userId: string) { this.store.dispatch(deleteUser({ userId })); }
}
// Presentational component - pure I/O
@Component({
template: `<div>{{user.name}}</div><button (click)="delete.emit(user.id)">Delete</button>`
})
class UserCardComponent {
@Input() user: User;
@Output() delete = new EventEmitter<string>();
}
Global state (window, localStorage, static variables) breaks testability.
Not this:
class ThemeService {
getTheme() {
return localStorage.getItem('theme'); // Global state
}
}
This:
// Abstract the global behind an injectable token
const STORAGE = new InjectionToken<Storage>('storage');
@Injectable()
class ThemeService {
constructor(@Inject(STORAGE) private storage: Storage) {}
getTheme() {
return this.storage.getItem('theme');
}
}
// In tests: provide mock storage
// In app: provide localStorage
With Angular's signals, prefer fine-grained reactivity over coarse change detection.
// Modern Angular with signals
@Component({
template: `
<div>Count: {{ count() }}</div>
<div>Doubled: {{ doubled() }}</div>
`
})
class CounterComponent {
count = signal(0);
doubled = computed(() => this.count() * 2);
increment() {
this.count.update(c => c + 1);
}
}
Benefits:
Before committing any Angular code, ask:
Apply these checks:
new keyword for services (except DTOs/models)providedIn: 'root' or explicit providers)Use a different skill when:
design-patternsjava (similar DI principles, different idioms)clarityangular-perfHevery is the Angular architecture skill—use it for DI, testability, and component design.
"The key to testability is the ability to construct the object under test in isolation." — Miško Hevery