結論:AIコーディング時代、Angularの「制約」が武器になる
最近、GitHub CopilotやClaude、ChatGPTを使ってフロントエンド開発してる人、増えてきましたよね。
「AIにコード書かせたら、なんかぐちゃぐちゃになった...」
そんな経験ありませんか?実は僕もReact/Next.jsでそれをやらかしました。コンポーネントにロジックが入り乱れ、hooksが肥大化し、「これ誰が書いたの...?」状態に。
で、試しにAngularでAIコーディングしてみたら、これがめちゃくちゃ相性良かったんですよ。
この記事では、AIコーディングでAngularを使うべき理由を、実際のコード例とともに解説します。
なぜAngularがAIコーディングと相性が良いのか
1. レイヤー分離が強制される
Angularでは、以下の構造がほぼ強制されます:
| レイヤー | 役割 |
|---|---|
| Component | UIの表示・ユーザー操作 |
| Service | ビジネスロジック・API通信 |
| Module | 機能のまとまり |
// user.component.ts - UIに専念
@Component({
selector: 'app-user',
template: `
<div *ngFor="let user of users$ | async">
{{ user.name }}
</div>
`
})
export class UserComponent {
users$ = this.userService.getUsers();
constructor(private userService: UserService) {}
}
// user.service.ts - ロジックはこっち
@Injectable({ providedIn: 'root' })
export class UserService {
constructor(private http: HttpClient) {}
getUsers(): Observable<User[]> {
return this.http.get<User[]>('/api/users');
}
}
AIに「ユーザー一覧を表示して」と頼むと、自然とこの形式でコードが生成されるんですね。Reactだと「コンポーネント内にfetch書く?custom hooks作る?」みたいな判断をAIに委ねることになって、結果バラバラになりがちだった。
2. TypeScriptがネイティブで一体化している
ReactでTypeScript使うとき、こんな経験ないですか?
-
useStateの型推論が微妙 - propsの型定義がめんどくさい
- hooksの返り値の型が複雑になる
Angularは最初からTypeScript前提で設計されてるので、デコレーターやDIも含めて型が一貫してるんです。
// Angularのデコレーターベースの型定義
@Component({
selector: 'app-product',
templateUrl: './product.component.html'
})
export class ProductComponent implements OnInit {
@Input() productId!: string; // 明確な入力
@Output() purchase = new EventEmitter<Product>(); // 明確な出力
ngOnInit(): void {
// ライフサイクルも型で保証
}
}
AIが生成するコードも自然と型が揃うので、後から「この変数何型だっけ...」みたいなことが減りましたね。
3. DI(依存性注入)でテストしやすいコードが自動的に生まれる
AngularのDIコンテナは本当に強力です。
// テスト時にモックを注入するのが超簡単
describe('UserComponent', () => {
let component: UserComponent;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
{ provide: UserService, useValue: mockUserService }
]
});
component = TestBed.createComponent(UserComponent).componentInstance;
});
});
Reactだと、Context APIやprops drilling、あるいは外部ライブラリでDIを実現する必要があって、AIに「テストしやすいコード書いて」と頼んでも方針がブレやすかったんですよね。
4. CLIが規約に沿ったコードを自動生成する
# これだけでベストプラクティスに沿ったファイル群が生成される
ng generate component features/user/user-list
ng generate service core/services/user
ng generate guard core/guards/auth
ng generate pipe shared/pipes/date-format
AIに「新しいコンポーネント作って」と頼む前に、CLIで骨組みを作っておくと、AIも既存の構造に合わせたコードを書いてくれるようになります。「郷に入っては郷に従え」ってやつですね。
実践:AIコーディングで使えるAngularの機能
NgRxのEffect - 副作用の管理
状態管理でNgRx使ってる場合、Effectsがめちゃくちゃ便利です。
@Injectable()
export class UserEffects {
loadUsers$ = createEffect(() =>
this.actions$.pipe(
ofType(UserActions.loadUsers),
exhaustMap(() =>
this.userService.getUsers().pipe(
map(users => UserActions.loadUsersSuccess({ users })),
catchError(error => of(UserActions.loadUsersFailure({ error })))
)
)
)
);
constructor(
private actions$: Actions,
private userService: UserService
) {}
}
AIに「ユーザー読み込みのEffectを追加して」と頼めば、このパターンで生成してくれます。RxJSのオペレーターも含めて、型安全に。
Pipe - 表示ロジックの再利用
@Pipe({ name: 'truncate' })
export class TruncatePipe implements PipeTransform {
transform(value: string, limit: number = 100): string {
return value.length > limit ? value.substring(0, limit) + '...' : value;
}
}
<!-- テンプレートでシンプルに使える -->
<p>{{ description | truncate:50 }}</p>
Guard - ルーティングの保護
@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
canActivate(): boolean | Observable<boolean> {
if (this.authService.isLoggedIn()) {
return true;
}
this.router.navigate(['/login']);
return false;
}
}
Known Issues:Angularの注意点
正直に言うと、Angularにも課題はあります:
- 学習コストが高い - RxJS、DI、デコレーターなど、覚えることが多い
- バンドルサイズ - 小規模アプリだとオーバーヘッドを感じることも
- AIの学習データ - ReactよりAngularの情報が少ない場合がある
ただ、AIコーディングという文脈では、この「学習コストの高さ」がむしろプラスに働くんですよね。フレームワークが厳格な分、AIの出力も一貫性が保たれやすい。
まとめ
AIコーディングでフロントエンド開発するなら、Angularの「制約」を味方につけるのがおすすめです。
- レイヤー分離が強制される → コードが散らからない
- TypeScriptネイティブ → 型が一貫する
- DIが標準装備 → テストしやすい
- CLIが強力 → 規約に沿ったコードが生まれる
「自由度の高さ」が魅力のReact/Next.jsも良いフレームワークですが、AIと協働するなら、ある程度の「型」があったほうがうまくいく。そんな気づきを得た開発でした。