# Struktur Proyek TypeScript untuk Aplikasi Dunia Nyata: Apa yang Benar-Benar Berhasil

> Melampaui tutorial: struktur folder dan konfigurasi tsconfig yang telah teruji untuk proyek TypeScript yang berkembang tanpa menjadi labirin.

URL: https://www.matthewswong.com/id/blog/typescript-project-structure-real-world/

- Author: Matthews Wong (https://www.matthewswong.com/id/about/)
- Published: 2025-06-10
- Updated: 2025-06-10
- Category: Web Dev

Setiap tutorial TypeScript menunjukkan struktur awal yang sama: src/components, src/pages, mungkin src/utils. Ini berfungsi baik untuk aplikasi todo. Lalu Anda memulai proyek nyata dengan autentikasi, billing, API eksternal, banyak peran pengguna, dan sistem desain — dan struktur datar itu mulai runtuh dalam minggu-minggu pertama.

## Struktur Folder yang Bertahan Saat Fitur Ditambahkan dengan Cepat

Prinsip inti: kelompokkan berdasarkan batas fitur, bukan berdasarkan tipe file. Menempatkan semua hooks di /hooks, semua tipe di /types, dan semua komponen di /components terdengar terorganisir — sampai Anda mengerjakan fitur dan perlu melompat antara enam direktori untuk kode terkait.

### Pembagian /components: UI vs Fitur

Keputusan organisasi yang paling berdampak adalah memisahkan components/ui (sistem desain — Button, Input, Modal, Card, Badge) dari components/features (komposit khusus domain — UserCard, InvoiceTable, ApprovalBadge). Komponen UI bersifat bodoh: mereka menerima props, merender markup, tidak ada pengambilan data, tidak ada logika bisnis.

### Lapisan Layanan: Menjaga Pengambilan Data Keluar dari Komponen

Direktori services/ untuk semua panggilan API eksternal adalah pola yang saya adopsi dari NestJS dan bawa ke pekerjaan Next.js saya. Setiap file layanan bertanggung jawab atas satu batas eksternal: auth.ts untuk autentikasi, billing.ts untuk API pembayaran. Layanan adalah fungsi async biasa — tidak ada React, tidak ada hooks, tidak ada JSX.

> Tambahkan file barrel index.ts ke setiap direktori fitur, tetapi TIDAK ke components/ui. File barrel untuk sistem desain menciptakan risiko ketergantungan sirkular dan memperlambat language server TypeScript pada proyek besar. Biarkan impor komponen UI tetap eksplisit.

## tsconfig.json: Pengaturan yang Menyelamatkan Anda dari Diri Sendiri

Pengaturan default TypeScript terlalu permisif untuk kode produksi. Mengaktifkan strict true adalah hal minimal, tetapi ada tiga opsi tambahan yang menangkap kelas bug yang signifikan: noUncheckedIndexedAccess, exactOptionalPropertyTypes, dan noImplicitReturns.

### Mengetikkan Respons API secara Defensif

Bahaya paling besar any dalam codebase TypeScript adalah yang tersembunyi di dalam respons API. response.json() mengembalikan any, dan jika Anda tidak mempersempitnya, janji keamanan tipe seluruh aplikasi Anda adalah kebohongan. Saya menggunakan pendekatan dua lapisan: skema Zod untuk validasi runtime dan antarmuka TypeScript yang berasal dari skema via z.infer.

> **Peringatan File Barrel**

> Terlalu banyak menggunakan file barrel (index.ts re-export di mana-mana) adalah salah satu penyebab paling umum perlambatan language server TypeScript dan kesalahan ketergantungan sirkular dalam proyek besar. Jika IDE Anda terasa lambat, hapus file barrel dari jalur yang sering diakses dan gunakan impor langsung.

## Menegakkan Struktur dengan ESLint dan Batasan Arsitektur

eslint-plugin-import dengan aturan no-restricted-imports dapat menegakkan batasan arsitektur secara otomatis. Cegah services/ mengimpor dari components/, cegah lib/ mengimpor React — aturan-aturan ini menangkap pelanggaran pelapisan pada waktu lint.

## Pertimbangan Monorepo: Kapan Memisahkan Paket

Untuk proyek yang berkembang menjadi backend terpisah atau aplikasi mobile, monorepo Turborepo menjadi berharga. Aturan praktis: jika dua aplikasi berbeda perlu berbagi tipe atau komponen, ekstrak mereka ke paket. Jika hanya satu aplikasi, tetap di proyek Next.js tunggal.

## Uji Lakmus: Bisakah Developer Baru Menemukan Sesuatu?

Ukuran akhir dari setiap struktur folder adalah manusia: bisakah developer yang belum pernah melihat codebase menemukan file yang tepat dalam 60 detik? Jika jawabannya tidak, strukturnya terlalu cerdas. Simpan kode fitur bersama, pisahkan sistem desain, jaga utilitas tetap murni.

## Sumber & Bacaan Lanjutan

- Microsoft — TypeScript Handbook: tsconfig Reference
- Colby Fayock — Next.js Project Structure
- Colin McDonnell — Dokumentasi Zod

## Source links

- https://www.typescriptlang.org/tsconfig
- https://nextjs.org/docs/app/getting-started/project-structure
- https://zod.dev

## FAQ

### Mengapa komponen harus dipisah antara components/ui dan components/features, bukan disatukan dalam satu folder?

Memisahkan components/ui (sistem desain: Button, Input, Modal, Card, Badge) dari components/features (komposit khusus domain seperti UserCard, InvoiceTable, ApprovalBadge) menegakkan kontrak yang jelas: komponen UI bersifat bodoh — mereka hanya menerima props, merender markup, tanpa pengambilan data atau logika bisnis. Komponen fitur boleh lebih cerdas — bisa memanggil hook pengambil data atau menggabungkan beberapa primitif UI. Batas ini membuat jelas di mana harus menambahkan kode baru dan mencegah logika bisnis bocor ke dalam sistem desain.

### Opsi tsconfig apa yang paling penting selain mengaktifkan strict mode?

Selain strict: true, tiga opsi yang paling berdampak pada codebase nyata adalah noUncheckedIndexedAccess, exactOptionalPropertyTypes, dan noImplicitReturns. noUncheckedIndexedAccess adalah yang paling signifikan — TypeScript dipaksa memperlakukan arr[0] sebagai T | undefined, bukan T, sehingga Anda harus menangani kasus di mana indeks array tidak ada. Bersama-sama, opsi-opsi ini menangkap kelas bug yang tidak tertangkap oleh strict saja.

### Bagaimana cara mengetikkan respons API secara aman agar tidak ada any yang tersembunyi?

response.json() mengembalikan any, sehingga tanpa penyempitan eksplisit, keamanan tipe hanya ada di atas kertas. Pendekatan dua lapisan yang direkomendasikan adalah menulis skema Zod untuk validasi runtime terhadap bentuk respons, lalu menurunkan antarmuka TypeScript dari skema tersebut menggunakan z.infer. Cara ini memberikan keamanan tipe saat kompilasi sekaligus validasi runtime bahwa respons API nyata sesuai dengan yang diharapkan.

### Kapan file barrel (re-export index.ts) menimbulkan masalah dan bagaimana cara mengatasinya?

File barrel berguna untuk direktori fitur karena membuat impor lebih bersih antar batas fitur. Namun, menambahkan file barrel ke components/ui (sistem desain) menciptakan risiko ketergantungan sirkular dan memperlambat language server TypeScript pada proyek besar. Rekomendasinya adalah tambahkan index.ts ke setiap direktori fitur, tetapi biarkan impor komponen UI tetap eksplisit dan langsung. Jika IDE terasa lambat atau tsc --watch berjalan lambat, menghapus file barrel dari jalur yang sering diakses adalah langkah diagnostik pertama yang baik.

### Kapan sebuah proyek sudah terlalu besar untuk satu repositori Next.js dan perlu monorepo Turborepo?

Aturan praktisnya: jika dua aplikasi berbeda perlu berbagi tipe atau komponen, ekstrak ke paket terpisah dalam monorepo Turborepo. Jika hanya satu aplikasi dengan banyak fitur, organisasi folder yang baik dalam satu proyek Next.js sudah cukup. Memisahkan terlalu dini menambah beban tanpa manfaat, jadi pemicunya haruslah kebutuhan berbagi yang nyata — backend terpisah, aplikasi mobile, atau library komponen bersama.

---

- HTML version: https://www.matthewswong.com/id/blog/typescript-project-structure-real-world/
- English: https://www.matthewswong.com/en/blog/typescript-project-structure-real-world/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
