Blog

  • Belajar Golang dari Nol #6: Perulangan for dan Percabangan

    Belajar Golang dari Nol #6: Perulangan for dan Percabangan

    Di Belajar Golang dari Nol #5 kita sudah kenalan dengan slice dan map. Waktu itu kita sempat memakai for range sekilas untuk membaca isinya, tapi belum dibedah dalam. Sekarang giliran materi itu dituntaskan. Di bagian keenam ini kita bahas perulangan dan percabangan di Go: for dengan semua bentuknya, for range sampai tuntas, break dan continue, if else, switch, sampai label untuk loop bersarang.

    Dua materi ini adalah otot utama program. Hampir semua logika, dari menghitung total belanja sampai menyaring data kotor, dibangun dari perulangan dan percabangan. Jadi kita jalan pelan seperti biasa, satu konsep, satu contoh, langsung praktik.

    for, Satu-satunya Perulangan di Go

    Kalau kamu pernah menyentuh bahasa lain, mungkin kamu terbiasa dengan while, do while, dan for sebagai tiga hal berbeda. Go memangkas semuanya jadi satu kata kunci saja: for. Tidak ada while di Go. Satu kata kunci, tiga cara pakai.

    Bentuk klasik: init, kondisi, post

    Ini bentuk yang paling sering kamu temui. Ada tiga bagian yang dipisah titik koma: inisialisasi, kondisi, dan apa yang dilakukan setiap akhir putaran.

    package main
    
    import "fmt"
    
    func main() {
    	for i := 1; i <= 5; i++ {
    		fmt.Println("Putaran ke", i)
    	}
    }

    Bacanya seperti resep: mulai dari i bernilai 1, ulangi selama i masih 5 atau kurang, dan setiap selesai satu putaran tambahkan i sebanyak satu. Program ini mencetak lima baris, dari putaran 1 sampai 5.

    Bentuk while: kondisi saja

    Buang bagian init dan post, sisakan kondisinya. Hasilnya persis while di bahasa lain.

    saldo := 100000
    for saldo > 0 {
    	fmt.Println("Sisa saldo:", saldo)
    	saldo -= 30000
    }

    Selama saldo masih di atas nol, loop terus jalan. Begitu saldo habis, loop berhenti sendiri. Anggap saja seperti main game dengan koin: selama koin masih ada, kamu boleh main terus.

    Bentuk tanpa kondisi: loop tak berujung

    Tulis for polos tanpa apa pun, maka loop akan berputar selamanya. Ini bukan bug, ini fitur. Server web misalnya, memang harus terus hidup menunggu request. Tapi untuk program biasa, kamu wajib menyediakan pintu keluar lewat break.

    hitung := 0
    for {
    	hitung++
    	fmt.Println("Masih jalan, hitung:", hitung)
    	if hitung == 3 {
    		break
    	}
    }

    Tanpa break di situ, program ini tidak akan pernah selesai dan terminal kamu penuh angka. Kalau itu terjadi, tekan Ctrl+C untuk menghentikannya.

    for range: Cara Nyaman Menyusuri Slice dan Map

    Di bagian 5 kita sudah pakai for range sekilas. Sekarang kita bongkar aturannya satu per satu, karena bentuk inilah yang paling sering kamu tulis sehari-hari.

    Range pada slice

    Saat dipakai pada slice, range memberi dua nilai setiap putaran: index dan isinya.

    buah := []string{"mangga", "jeruk", "salak"}
    
    for i, nama := range buah {
    	fmt.Println(i, nama)
    }

    Output:

    0 mangga
    1 jeruk
    2 salak

    Sering kali kamu hanya butuh isinya, tidak peduli index-nya. Di sinilah underscore berperan. Tanda _ artinya kamu bilang ke Go: nilai ini sengaja saya abaikan. Ini penting karena Go menolak compile kalau ada variabel yang dideklarasikan tapi tidak dipakai.

    for _, nama := range buah {
    	fmt.Println(nama)
    }

    Sebaliknya, kalau kamu cuma butuh index, tulis satu variabel saja: for i := range buah. Nilainya otomatis hanya index.

    Range pada map

    Pada map, dua nilai yang diberikan adalah key dan value.

    stok := map[string]int{
    	"mangga": 10,
    	"jeruk":  5,
    	"salak":  8,
    }
    
    for nama, jumlah := range stok {
    	fmt.Println(nama, "=", jumlah)
    }

    Nah, ini catatan yang sering bikin pemula bingung. Urutan hasil range pada map itu acak. Jalankan program yang sama dua kali, urutannya bisa beda. Ini disengaja oleh pembuat Go supaya programmer tidak menggantungkan logika pada urutan map. Jadi kalau output map kamu terlihat berantakan urutannya, program kamu tidak rusak. Memang begitu sifatnya. Kalau butuh urutan yang pasti, kumpulkan dulu key-nya ke slice, urutkan, lalu loop slice itu.

    break dan continue

    Dua kata ini adalah rem dan gas di dalam loop. break menghentikan loop sepenuhnya. continue melompati sisa putaran saat ini dan langsung lanjut ke putaran berikutnya.

    Contoh nyata untuk continue: menyaring data yang tidak valid. Misal kamu punya daftar nilai ujian, tapi ada beberapa angka aneh yang harus dilewati.

    nilai := []int{80, -5, 90, 200, 75}
    
    for _, n := range nilai {
    	if n < 0 || n > 100 {
    		continue
    	}
    	fmt.Println("Nilai valid:", n)
    }

    Angka -5 dan 200 dilompati, sisanya dicetak. Sedangkan break cocok saat kamu mencari sesuatu dan ingin berhenti begitu ketemu, supaya tidak buang tenaga memeriksa sisa data.

    daftar := []string{"Andi", "Budi", "Citra", "Dewi"}
    dicari := "Citra"
    
    for i, nama := range daftar {
    	if nama == dicari {
    		fmt.Println("Ketemu di index", i)
    		break
    	}
    }

    if, else if, dan else

    Percabangan di Go bentuknya sederhana. Kondisi tidak perlu tanda kurung, tapi kurung kurawal wajib ada.

    skor := 78
    
    if skor >= 85 {
    	fmt.Println("Grade A")
    } else if skor >= 70 {
    	fmt.Println("Grade B")
    } else {
    	fmt.Println("Belum lulus")
    }

    Go mengecek dari atas ke bawah dan berhenti di kondisi pertama yang cocok. Skor 78 masuk Grade B, karena syarat Grade A gagal lebih dulu.

    if dengan statement pendek

    Ini gaya khas Go yang akan sering kamu lihat di kode orang lain. Kamu boleh menaruh satu statement pendek sebelum kondisi, dipisah titik koma. Masih ingat pola comma ok saat mengecek isi map di bagian 5? Biasanya pola itu ditulis begini:

    stok := map[string]int{"mangga": 10}
    
    if jumlah, ok := stok["mangga"]; ok {
    	fmt.Println("Stok mangga:", jumlah)
    } else {
    	fmt.Println("Mangga tidak ada di data")
    }

    Variabel jumlah dan ok lahir di dalam if itu dan hanya hidup di situ. Setelah blok if selesai, keduanya lenyap. Ini bagus, karena variabel bantu tidak berserakan mengotori bagian lain fungsi.

    switch: if else Berantai yang Lebih Rapi

    Kalau cabangnya banyak, if else berantai mulai capek dibaca. switch hadir untuk itu.

    hari := "sabtu"
    
    switch hari {
    case "sabtu", "minggu":
    	fmt.Println("Akhir pekan")
    case "jumat":
    	fmt.Println("Sebentar lagi libur")
    default:
    	fmt.Println("Hari kerja")
    }

    Perhatikan satu case boleh menampung beberapa nilai sekaligus, dipisah koma. default jalan kalau tidak ada case yang cocok.

    Ada satu perbedaan besar dengan bahasa seperti C atau JavaScript: switch di Go tidak butuh break. Begitu satu case cocok dan selesai dijalankan, Go otomatis keluar dari switch. Kamu tidak akan kena bug klasik lupa break yang bikin eksekusi bocor ke case berikutnya. Kalau kamu memang sengaja ingin lanjut ke case di bawahnya, ada kata kunci fallthrough. Cukup tahu saja dulu, praktiknya jarang sekali dipakai.

    switch tanpa kondisi

    Bentuk ini favorit banyak programmer Go. Tulis switch tanpa apa pun, lalu tiap case berisi kondisi boolean. Hasilnya pengganti if else berantai yang jauh lebih enak dibaca.

    skor := 78
    
    switch {
    case skor >= 85:
    	fmt.Println("Grade A")
    case skor >= 70:
    	fmt.Println("Grade B")
    default:
    	fmt.Println("Belum lulus")
    }

    Sama seperti if else, pengecekan berjalan dari atas ke bawah dan berhenti di yang pertama cocok.

    Label: Keluar dari Loop Bersarang

    Satu materi kecil sebelum latihan. break biasa hanya menghentikan loop terdalam. Kalau kamu punya loop di dalam loop dan ingin keluar dari semuanya sekaligus, pakai label.

    luar:
    	for i := 0; i < 3; i++ {
    		for j := 0; j < 3; j++ {
    			if i*j >= 4 {
    				break luar
    			}
    			fmt.Println(i, j)
    		}
    	}

    break luar langsung melompat keluar dari kedua loop. Jujur saja, label jarang dipakai di kode sehari-hari. Cukup tahu bahwa fitur ini ada, supaya tidak bingung saat menemukannya di kode orang lain.

    Latihan: Rekap Penjualan Mingguan

    Sekarang kita gabungkan semua materi hari ini plus struct dari bagian 4 dan slice dari bagian 5. Skenarionya: kamu punya catatan transaksi toko selama seminggu. Sebagian transaksi batal dan harus dilewati. Sisanya dihitung totalnya, dirata-rata, dan diberi kategori nominal pakai switch.

    package main
    
    import "fmt"
    
    type Transaksi struct {
    	ID     int
    	Produk string
    	Nilai  int
    	Batal  bool
    }
    
    func main() {
    	transaksi := []Transaksi{
    		{ID: 1, Produk: "Kaos Polos", Nilai: 150000, Batal: false},
    		{ID: 2, Produk: "Jaket Hoodie", Nilai: 450000, Batal: false},
    		{ID: 3, Produk: "Topi Baseball", Nilai: 75000, Batal: true},
    		{ID: 4, Produk: "Celana Chino", Nilai: 320000, Batal: false},
    		{ID: 5, Produk: "Kemeja Flanel", Nilai: 260000, Batal: false},
    		{ID: 6, Produk: "Sandal Jepit", Nilai: 35000, Batal: true},
    		{ID: 7, Produk: "Sepatu Lari", Nilai: 780000, Batal: false},
    	}
    
    	total := 0
    	jumlahValid := 0
    
    	fmt.Println("=== Rekap Penjualan Mingguan ===")
    
    	for _, t := range transaksi {
    		if t.Batal {
    			fmt.Printf("Transaksi #%d (%s) batal, dilewati\n", t.ID, t.Produk)
    			continue
    		}
    
    		var kategori string
    		switch {
    		case t.Nilai >= 500000:
    			kategori = "besar"
    		case t.Nilai >= 200000:
    			kategori = "sedang"
    		default:
    			kategori = "kecil"
    		}
    
    		fmt.Printf("#%d %-15s Rp%-8d kategori %s\n", t.ID, t.Produk, t.Nilai, kategori)
    
    		total += t.Nilai
    		jumlahValid++
    	}
    
    	fmt.Println("--------------------------------")
    	fmt.Println("Transaksi valid :", jumlahValid)
    	fmt.Println("Total penjualan : Rp", total)
    
    	if jumlahValid > 0 {
    		rata := total / jumlahValid
    		fmt.Println("Rata-rata       : Rp", rata)
    	}
    }

    Jalankan dengan go run main.go. Hasilnya:

    === Rekap Penjualan Mingguan ===
    #1 Kaos Polos      Rp150000   kategori kecil
    #2 Jaket Hoodie    Rp450000   kategori sedang
    Transaksi #3 (Topi Baseball) batal, dilewati
    #4 Celana Chino    Rp320000   kategori sedang
    #5 Kemeja Flanel   Rp260000   kategori sedang
    Transaksi #6 (Sandal Jepit) batal, dilewati
    #7 Sepatu Lari     Rp780000   kategori besar
    --------------------------------
    Transaksi valid : 5
    Total penjualan : Rp 1960000
    Rata-rata       : Rp 392000

    Coba telusuri alurnya pelan-pelan. Loop for range menyusuri slice of struct. continue melewati dua transaksi yang batal, jadi keduanya tidak ikut total. Switch tanpa kondisi memberi label kategori berdasarkan nominal. Terakhir, if di bawah memastikan pembagian rata-rata hanya jalan kalau ada transaksi valid, supaya program tidak membagi dengan nol.

    Sebagai latihan mandiri, coba ubah programnya sedikit. Tambahkan variabel penghitung untuk tiap kategori, lalu cetak berapa transaksi besar, sedang, dan kecil di bagian rekap. Semua bahannya sudah kamu pegang.

    Rangkuman dan Bagian Selanjutnya

    Hari ini kamu sudah menguasai alat kontrol alur di Go:

    • for adalah satu-satunya perulangan, dengan tiga bentuk: klasik, gaya while, dan tanpa kondisi
    • for range menyusuri slice dan map, dengan catatan urutan map selalu acak
    • break menghentikan loop, continue melompati satu putaran
    • if bisa membawa statement pendek, cocok untuk pola comma ok
    • switch di Go otomatis berhenti per case, tanpa perlu break, dan bentuk tanpa kondisinya menggantikan if else berantai
    • label dipakai untuk keluar dari loop bersarang, meski jarang dibutuhkan

    Di bagian 7 kita masuk ke topik yang sering dianggap seram padahal sebenarnya ramah: Pointer di Go: Memahami Alamat Memori Tanpa Pusing. Sampai ketemu di sana.

    Seri ini ditulis dari pengalaman tim Arrazy membangun berbagai sistem aplikasi untuk bisnis di Indonesia.

  • Apa itu Jaringan Komputer + Setup Lab Praktik di Linux

    Apa itu Jaringan Komputer + Setup Lab Praktik di Linux

    Jaringan komputer adalah kumpulan dua perangkat atau lebih yang saling terhubung supaya bisa bertukar data. Terhubungnya bisa lewat kabel, bisa lewat WiFi. Laptop kamu yang tersambung ke router rumah itu sudah jaringan komputer. HP dan smart TV yang nonton YouTube lewat WiFi yang sama juga bagian dari jaringan itu. Internet sendiri pada dasarnya jaringan raksasa yang menghubungkan jutaan jaringan kecil di seluruh dunia.

    Artikel ini bagian pertama dari seri Belajar Jaringan Komputer dari Nol. Di bagian ini kita bahas apa itu jaringan komputer dengan contoh yang dekat dengan keseharian, lalu langsung praktik: menyiapkan lab di Linux dan menjalankan perintah jaringan pertama kamu. Tidak ada teori panjang dulu. Konsep seperti IP address, DNS, dan routing akan dikupas satu per satu di bagian-bagian berikutnya.

    Apa Itu Jaringan Komputer, dari LAN Rumah sampai Internet

    Biar tidak abstrak, kita lihat tiga contoh nyata dengan skala yang makin besar.

    Contoh 1: LAN di rumah

    Di rumah kamu ada satu router WiFi. Laptop, HP, printer, dan smart TV terhubung ke router itu. Semua perangkat ini membentuk satu LAN (Local Area Network), jaringan lokal dengan cakupan kecil. Karena satu jaringan, laptop bisa mengirim dokumen ke printer tanpa lewat internet sama sekali. Router bertugas jadi penghubung antar perangkat sekaligus gerbang keluar menuju internet.

    Contoh 2: Warnet atau lab kampus

    Warnet dan lab komputer kampus juga LAN, hanya skalanya lebih besar. Puluhan PC terhubung lewat kabel ke satu perangkat bernama switch, lalu switch tersambung ke router. Kalau kamu pernah main game multiplayer antar PC di warnet tanpa internet, itu bukti bahwa komunikasi antar komputer dalam LAN tidak butuh internet. Datanya cukup berputar di dalam jaringan lokal.

    Contoh 3: Internet

    Sekarang bayangkan LAN rumah kamu, LAN warnet, jaringan kantor, dan jaringan data center saling dihubungkan lewat jaringan milik ISP (penyedia internet), lalu ISP di seluruh dunia saling terhubung juga. Itulah internet: jaringan dari banyak jaringan. Saat kamu membuka sebuah website, data berjalan dari server di suatu data center, melompat lewat banyak router, sampai akhirnya tiba di laptop kamu. Perjalanan paket ini akan kita bedah tuntas di bagian 2 seri ini.

    Kenapa developer dan pengelola server wajib paham jaringan

    Kalau kamu cuma pakai komputer untuk browsing, pengetahuan jaringan memang jarang terasa perlu. Tapi begitu kamu menulis kode atau memegang server, ceritanya beda. Beberapa situasi yang pasti kamu temui:

    • API yang kamu panggil timeout. Masalahnya di kode, di DNS, di firewall, atau servernya memang mati? Tanpa dasar jaringan, kamu cuma bisa menebak.
    • Aplikasi jalan di laptop tapi tidak bisa diakses dari HP di WiFi yang sama. Ini soal IP dan port, bukan soal kode.
    • Website klien tiba-tiba tidak bisa dibuka. Kamu perlu tahu cara memastikan apakah masalahnya di DNS, di server, atau di jaringan pengunjung.
    • Deploy aplikasi dengan Docker, lalu container tidak bisa saling komunikasi. Docker punya lapisan jaringan sendiri yang perlu dipahami.

    Tim kami di Arrazy merasakan ini langsung. Saat membangun dan memelihara sistem aplikasi untuk klien, sebagian besar masalah produksi yang kami tangani ujungnya bukan bug di kode, tapi urusan jaringan: DNS yang belum propagasi, port yang tertutup firewall, atau konfigurasi reverse proxy. Developer yang paham dasar jaringan menyelesaikan masalah seperti ini dalam hitungan menit, bukan jam.

    Persiapan Lab: Linux dan Terminal

    Seri ini praktik di Linux karena hampir semua server di dunia berjalan di Linux, dan tool jaringan terbaik tersedia natif di sana. Yang kamu butuhkan:

    • Ubuntu 24.04 LTS atau turunannya seperti Linux Mint. Ubuntu 22.04 juga aman, perintahnya sama.
    • Pengguna Windows bisa pakai WSL 2 (Windows Subsystem for Linux) dengan distro Ubuntu. Sebagian besar praktik seri ini jalan di WSL, dengan beberapa catatan yang saya tulis di bagian troubleshooting.
    • Akses sudo untuk install paket.

    Kamu tidak perlu jago terminal, tapi minimal harus nyaman berpindah direktori, menjalankan perintah, dan membaca output. Kalau perintah seperti cd, ls, dan sudo masih terasa asing, selesaikan dulu beberapa bagian awal seri Belajar Linux dari Nol. Seri jaringan ini menganggap kamu sudah bisa hal-hal dasar itu.

    Install Toolkit Jaringan di Linux via apt

    Sekarang kita pasang semua tool yang dipakai sepanjang 26 bagian seri ini. Sekali install, beres untuk seterusnya. Buka terminal, lalu jalankan:

    sudo apt update
    sudo apt install -y iputils-ping traceroute dnsutils curl iproute2 tcpdump wireshark

    Saat instalasi Wireshark, akan muncul dialog biru bertanya “Should non-superusers be able to capture packets?”. Pilih Yes supaya nanti kamu bisa capture paket tanpa harus jadi root. Kalau terlanjur pilih No, ada cara memperbaikinya di bagian troubleshooting.

    Ini fungsi tiap paket, biar kamu tahu apa yang barusan dipasang:

    Paket Perintah utama Kegunaan
    iputils-ping ping Mengecek apakah sebuah host bisa dijangkau
    traceroute traceroute Melihat rute yang dilewati paket menuju tujuan
    dnsutils dig, nslookup Query DNS, menerjemahkan nama domain ke IP
    curl curl Mengirim request HTTP dari terminal
    iproute2 ip, ss Melihat IP address, tabel routing, dan port terbuka
    tcpdump tcpdump Menangkap paket jaringan lewat terminal
    wireshark wireshark Analisis paket dengan tampilan grafis

    Di Ubuntu 24.04, iproute2 dan curl biasanya sudah terpasang bawaan, tapi tidak masalah ikut ditulis di perintah install. apt akan melewatinya kalau sudah ada.

    Verifikasi instalasi dengan mengecek versi beberapa tool:

    ping -V
    dig -v
    tcpdump --version

    Output yang diharapkan kurang lebih seperti ini (angka versi bisa sedikit beda tergantung distro):

    ping from iputils 20240117
    DiG 9.18.30-0ubuntu0.24.04.2-Ubuntu
    tcpdump version 4.99.4
    libpcap version 1.10.4 (with TPACKET_V3)

    Kalau semua perintah di atas mengeluarkan versi dan bukan error, lab kamu siap.

    Praktik Pertama: Kenali Jaringan Kamu Sendiri

    Dua perintah ini adalah perintah jaringan yang paling sering kamu pakai seumur hidup sebagai developer. Kita jalankan sekarang, penjelasan mendalamnya menyusul di bagian-bagian berikutnya.

    Melihat IP address dengan ip addr

    ip addr

    Outputnya kira-kira seperti ini (punya kamu pasti beda di detailnya):

    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN ...
        inet 127.0.0.1/8 scope host lo
    2: wlp3s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP ...
        link/ether a4:6b:b6:3e:12:9f brd ff:ff:ff:ff:ff:ff
        inet 192.168.1.7/24 brd 192.168.1.255 scope global dynamic wlp3s0

    Tiga hal yang cukup kamu kenali dulu:

    • lo adalah loopback, alamat internal komputer kamu sendiri. IP-nya selalu 127.0.0.1.
    • wlp3s0 (atau eth0, enp2s0, tergantung perangkat) adalah interface jaringan yang aktif. Baris inet 192.168.1.7/24 artinya komputer kamu punya IP 192.168.1.7 di jaringan lokal.
    • Baris link/ether berisi MAC address, alamat fisik perangkat jaringan kamu.

    Apa arti /24, kenapa IP-nya mulai dengan 192.168, dan apa bedanya dengan IP publik, semua dibahas di bagian 6 dan 7 seri ini. Sekarang cukup tahu bahwa komputer kamu punya alamat di jaringan lokal.

    Mengecek koneksi dengan ping

    ping -c 4 google.com

    Opsi -c 4 artinya kirim 4 kali lalu berhenti. Tanpa opsi ini, ping di Linux jalan terus sampai kamu tekan Ctrl+C. Output yang diharapkan:

    PING google.com (142.251.10.101) 56(84) bytes of data.
    64 bytes from 142.251.10.101: icmp_seq=1 ttl=115 time=24.3 ms
    64 bytes from 142.251.10.101: icmp_seq=2 ttl=115 time=23.8 ms
    64 bytes from 142.251.10.101: icmp_seq=3 ttl=115 time=25.1 ms
    64 bytes from 142.251.10.101: icmp_seq=4 ttl=115 time=24.0 ms
    
    --- google.com ping statistics ---
    4 packets transmitted, 4 received, 0% packet loss, time 3005ms
    rtt min/avg/max/mdev = 23.812/24.300/25.104/0.492 ms

    Baca hasilnya begini: komputer kamu mengirim paket kecil ke server Google, dan server membalas dalam waktu sekitar 24 milidetik. 0% packet loss artinya semua paket sampai dan dibalas, koneksi kamu sehat. Perhatikan juga baris pertama: kamu mengetik google.com, tapi sistem menerjemahkannya jadi IP 142.251.10.101. Penerjemah itu namanya DNS, dan dia dapat bagian khusus di seri ini.

    Kalau dua perintah ini jalan, selamat, lab kamu berfungsi dan kamu baru saja melakukan diagnosis jaringan pertama.

    Error Umum Saat Setup Lab dan Solusinya

    Command not found saat menjalankan ping, dig, atau traceroute

    Contoh pesannya: Command 'traceroute' not found, but can be installed with: sudo apt install traceroute. Penyebabnya jelas, paketnya belum terinstall. Image minimal seperti container Docker atau VPS baru sering tidak menyertakan tool dasar sekalipun. Solusinya jalankan ulang perintah install di atas. Kalau apt-nya sendiri yang error, jalankan sudo apt update dulu untuk menyegarkan daftar paket.

    Wireshark tidak bisa capture: You don’t have permission to capture on that device

    Ini muncul kalau saat instalasi kamu menjawab No pada dialog non-superuser, atau user kamu belum masuk group wireshark. Perbaiki dengan:

    sudo dpkg-reconfigure wireshark-common
    sudo usermod -aG wireshark $USER

    Pada dialog yang muncul, pilih Yes. Setelah itu logout lalu login lagi (atau restart) supaya keanggotaan group aktif. Cek dengan perintah groups, pastikan kata wireshark muncul di daftarnya.

    ping: Temporary failure in name resolution

    Artinya komputer kamu gagal menerjemahkan google.com jadi IP. Coba ping -c 4 8.8.8.8. Kalau ping ke angka ini berhasil tapi ke google.com gagal, masalahnya di DNS, bukan koneksi. Di WSL, kasus ini sering beres dengan restart WSL: tutup terminal, jalankan wsl --shutdown di PowerShell, lalu buka lagi. Kalau ping ke 8.8.8.8 juga gagal, cek dulu apakah WiFi atau kabel kamu memang tersambung.

    Catatan khusus pengguna WSL

    WSL 2 menjalankan Linux di dalam mesin virtual, jadi ada beberapa perbedaan yang wajar kamu temui:

    • ip addr menampilkan interface eth0 dengan IP virtual (misalnya 172.x.x.x), bukan IP WiFi laptop kamu. Ini normal, WSL punya jaringan virtual sendiri di belakang Windows.
    • Wireshark versi GUI butuh WSLg (bawaan Windows 11). Alternatifnya, capture pakai tcpdump di WSL lalu buka file hasilnya di Wireshark versi Windows.
    • Beberapa praktik lanjutan seperti sniffing interface WiFi langsung tidak bisa dilakukan dari dalam WSL. Untuk 80 persen materi seri ini, WSL tetap cukup.

    Lanjut ke Bagian 2: Perjalanan Paket di Internet

    Hari ini kamu sudah paham apa itu jaringan komputer, dari LAN rumah sampai internet, dan lab praktik kamu sudah siap dengan toolkit lengkap. Dua perintah pertama juga sudah jalan: ip addr untuk melihat alamat sendiri dan ping untuk mengecek koneksi.

    Bagian berikutnya berjudul “Cara Kerja Internet: Perjalanan Paket Saat Membuka Website”. Di sana kita ikuti langkah demi langkah apa yang terjadi sejak kamu menekan Enter di address bar sampai halaman muncul, lengkap dengan praktik traceroute untuk melihat rute paketnya. Artikelnya terbit menyusul, pantau daftar lengkapnya di halaman hub seri Belajar Jaringan Komputer.

    Referensi

  • Biaya API AI Membengkak? Kenali Cara Kerja AI Gateway

    Biaya API AI Membengkak? Kenali Cara Kerja AI Gateway

    AI gateway adalah satu pintu perantara antara aplikasimu dan berbagai penyedia model AI seperti OpenAI, Anthropic, atau Google. Semua request dari aplikasi masuk ke gateway dulu, baru diteruskan ke penyedia model. Karena semua lalu lintas lewat satu titik, gateway bisa mencatat pemakaian per fitur atau per user, mengatur limit, memilih model paling murah yang masih cukup untuk tugasnya, dan menyimpan semua API key di satu tempat. Aplikasimu cukup pegang satu API key untuk mengakses semua model.

    Konsep ini jadi relevan begitu tagihan API AI mulai terasa. Banyak produk memulai dengan satu model dan satu API key. Enam bulan kemudian ada tiga fitur AI, dua penyedia model, dan tagihan bulanan yang naik terus tanpa ada yang bisa menjelaskan kenapa. AI gateway lahir untuk masalah itu. Artikel ini membahas cara kerjanya, fungsi apa saja yang dia pegang, dan kapan aplikasimu benar-benar membutuhkannya.

    Gejala biaya API AI mulai tidak terkendali

    Membengkaknya biaya API AI jarang terjadi mendadak. Biasanya ada pola yang menumpuk pelan-pelan. Beberapa gejala yang paling sering muncul:

    • Tagihan naik tapi tidak tahu fitur mana yang boros. Dashboard penyedia model hanya menampilkan total token per API key. Kalau satu key dipakai lima fitur sekaligus, kamu tidak bisa memisahkan mana yang menyumbang berapa. Fitur ringkasan dokumen dan chatbot support tercampur di satu angka.
    • API key tersebar di banyak tempat. Satu key di backend utama, satu di worker, satu di script cron, satu lagi dipegang developer untuk testing dan tidak pernah dicabut. Ketika key bocor atau ada pemakaian aneh, susah melacak sumbernya.
    • Semua request pakai model paling mahal. Waktu awal integrasi, wajar pilih model terbaik biar hasilnya bagus. Masalahnya, tugas ringan seperti klasifikasi atau ekstraksi data ikut lewat model itu. Padahal model yang lebih kecil bisa mengerjakannya dengan hasil setara dan biaya jauh lebih rendah.
    • Ganti provider berarti ubah kode. Setiap penyedia punya format API sendiri. Kalau integrasinya langsung ke SDK masing-masing, pindah model berarti refactor, testing ulang, dan deploy. Akhirnya tim malas pindah walaupun ada opsi yang lebih murah.
    • Tidak ada rem. Satu user iseng atau satu bug loop bisa mengirim ribuan request dalam semalam. Kamu baru tahu setelah invoice datang.

    Kalau dua atau tiga poin di atas terasa familiar, masalahnya bukan di model AI yang kamu pakai. Masalahnya di lapisan kontrol yang belum ada.

    Fungsi AI gateway satu per satu

    Gateway bekerja seperti resepsionis gedung. Semua tamu lewat satu pintu, dicatat, dicek keperluannya, lalu diarahkan ke ruangan yang tepat. Berikut fungsi-fungsi utamanya.

    Routing model

    Gateway menerima request dalam satu format, lalu meneruskannya ke model yang sesuai aturan yang kamu tentukan. Aturannya bisa sederhana, misalnya fitur chatbot pakai model A dan fitur klasifikasi pakai model B yang lebih murah. Bisa juga lebih dinamis, misalnya request pendek diarahkan ke model kecil dan request kompleks ke model besar. Aplikasimu tidak perlu tahu detail ini. Dia cuma kirim request ke satu endpoint.

    Pencatatan biaya per fitur dan per user

    Karena semua request lewat gateway, setiap request bisa diberi label. Fitur apa, user siapa, tim mana. Dari situ kamu bisa lihat laporan seperti ini: fitur ringkasan menghabiskan 60 persen biaya bulan ini, dan 10 user teratas menyumbang setengahnya. Data ini yang jadi dasar keputusan. Mau optimasi prompt, ganti model, atau batasi pemakaian, semuanya berangkat dari angka, bukan tebakan.

    Rate limit dan budget cap

    Gateway bisa membatasi jumlah request per user, per key, atau per periode. Kalau ada yang melewati batas, request ditolak atau diantri, bukan diteruskan ke provider dan menambah tagihan. Beberapa gateway juga mendukung budget cap, jadi pemakaian berhenti otomatis saat menyentuh angka tertentu. Ini rem yang tidak kamu punya kalau aplikasi bicara langsung ke provider.

    Fallback saat provider down

    Penyedia model AI juga bisa gangguan. Kalau integrasimu hanya ke satu provider, fitur AI di produkmu ikut mati. Gateway bisa diset untuk otomatis mengalihkan request ke model cadangan di provider lain saat provider utama error atau lambat. User tidak sadar ada pergantian di belakang layar.

    Manajemen API key

    Key asli dari provider disimpan hanya di gateway. Aplikasi, worker, dan tim developer memakai key turunan yang diterbitkan gateway. Key turunan ini bisa dibatasi aksesnya, dipantau pemakaiannya, dan dicabut kapan saja tanpa mengganggu bagian lain. Kalau ada key yang bocor, kamu matikan satu key itu saja, bukan mengganti key utama yang dipakai di mana-mana.

    Kapan belum butuh, kapan sudah

    Tidak semua aplikasi butuh AI gateway. Menambahkannya terlalu dini justru menambah komponen yang harus dirawat.

    Kamu belum butuh gateway kalau kondisinya seperti ini: satu model, satu fitur AI, trafik masih kecil, dan tagihan bulanan masih di angka yang tidak bikin mikir. Di fase ini, cukup rapikan dasar-dasarnya. Simpan API key di environment variable, pasang alert billing di dashboard provider, dan beri logging sederhana di sisi aplikasi.

    Kamu mulai butuh gateway saat salah satu dari ini terjadi:

    • Fitur AI lebih dari satu dan kamu ingin tahu biaya per fitur.
    • Sudah atau berencana memakai lebih dari satu penyedia model.
    • Produkmu multi-tenant dan pemakaian tiap client perlu dicatat terpisah, misalnya untuk billing.
    • API key mulai dipegang banyak orang atau banyak service.
    • Downtime provider pernah bikin fitur AI kamu ikut tumbang.

    Untuk mulai, kamu tidak harus langsung membangun sendiri. Ada layanan gateway siap pakai yang bisa dicoba dulu. Tapi kalau kebutuhanmu spesifik, misalnya aturan routing khusus, integrasi ke sistem billing internal, atau kontrol penuh atas data yang lewat, membangun gateway sendiri jadi masuk akal. Ini masuk kategori pembangunan sistem aplikasi yang dirancang mengikuti alur bisnismu, bukan sebaliknya.

    Contoh implementasi: VibeRouter

    Salah satu proyek yang kami kerjakan di area ini adalah VibeRouter, sistem AI router untuk tim developer yang butuh endpoint stabil, monitoring pemakaian yang jelas, dan kesiapan scale ke banyak client.

    Latar belakangnya relevan dengan bahasan artikel ini. Ketika workflow coding AI dipakai tim besar, bottleneck sering muncul di tiga titik: endpoint, monitoring, dan kontrol akses. Request AI datang terus-menerus, jadi fondasinya harus siap dipakai 24/7 dengan visibilitas operasional yang jelas.

    Yang kami bangun untuk VibeRouter mencakup beberapa lapisan:

    • Sistem router yang stabil untuk menangani request AI intensif.
    • Dashboard pengguna untuk kontrol akses dan pemantauan pemakaian.
    • Manajemen API key untuk skenario multi-client.
    • Struktur dokumentasi onboarding supaya tim baru bisa cepat mengadopsi.

    Polanya sama dengan yang dibahas di atas. Satu pintu untuk semua request, pemakaian tercatat, akses terkontrol lewat API key yang dikelola terpusat. Sistemnya kini sudah live dan dipakai untuk trafik tinggi.

    Pertanyaan yang sering muncul

    Apakah AI gateway menambah latency?

    Ada tambahan satu hop di jalur request, jadi secara teknis iya. Tapi porsinya kecil sekali dibanding waktu yang dihabiskan model AI untuk menghasilkan jawaban. Model bisa butuh beberapa detik untuk merespons, sementara overhead gateway yang sehat biasanya hanya hitungan milidetik. Dalam praktik, user tidak akan merasakan bedanya. Justru fallback otomatis di gateway sering membuat pengalaman terasa lebih cepat, karena request tidak menggantung saat provider utama bermasalah.

    Apakah aman menaruh API key di gateway?

    Lebih aman dibanding menyebar key asli ke banyak service dan banyak orang. Key dari provider cukup disimpan di satu tempat yang dijaga ketat, sementara semua pihak lain memakai key turunan yang scope-nya terbatas dan bisa dicabut kapan saja. Syaratnya gateway itu sendiri dikelola dengan benar: key disimpan terenkripsi, akses dashboard dibatasi, dan ada audit log. Kalau kamu memakai gateway pihak ketiga, cek dulu bagaimana mereka menyimpan key dan data request kamu.

    Apa bedanya AI gateway dengan API gateway biasa?

    API gateway umum seperti Kong atau Nginx mengatur lalu lintas HTTP secara generik. AI gateway mengerti konteks LLM: dia bisa menghitung token, membandingkan harga antar model, menerjemahkan format request antar provider, dan mengatur fallback antar model. Kamu bisa saja merakit sebagian fungsi ini di API gateway biasa, tapi butuh banyak konfigurasi tambahan yang di AI gateway sudah tersedia sejak awal.

    Mulai dari mana

    Langkah pertama bukan langsung pasang gateway. Mulai dari audit sederhana: daftar semua fitur yang memanggil API AI, model apa yang dipakai masing-masing, dan siapa saja yang memegang API key. Dari daftar itu biasanya sudah kelihatan kebocoran paling besar, entah itu tugas ringan yang lewat model mahal atau key lama yang belum dicabut.

    Kalau setelah audit kamu merasa butuh lapisan kontrol seperti yang dibahas di artikel ini, entah memakai layanan yang ada atau membangun sistem sendiri seperti VibeRouter, tim kami bisa bantu memetakan opsinya. Ceritakan kondisi produkmu lewat halaman kontak Arrazy, nanti kita diskusikan pendekatan yang paling masuk akal untuk skala dan budgetmu.

  • Kenapa Harga Aplikasi Custom Bisa Beda Jauh Antar Vendor

    Kenapa Harga Aplikasi Custom Bisa Beda Jauh Antar Vendor

    Anda mengirim brief yang sama ke tiga vendor. Penawaran yang masuk selisihnya berkali lipat. Ada yang murah sekali, ada yang terasa mahal, dan semuanya sama-sama bilang sanggup mengerjakan apa yang Anda minta. Jawaban singkatnya begini. Harga aplikasi custom bisa beda jauh karena empat hal utama: scope yang ditafsirkan berbeda, siapa yang mengerjakan, kualitas yang tidak kelihatan saat demo, dan layanan setelah aplikasi rilis.

    Kami menulis ini dari posisi orang dalam. Kami pernah kalah tender karena penawaran kami dianggap kemahalan. Kami juga pernah diminta membenahi aplikasi murah yang ujungnya lebih mahal daripada membangun ulang dari awal. Artikel ini membedah satu per satu faktor yang membuat penawaran bisa selisih jauh, supaya Anda bisa membandingkan dengan adil, bukan sekadar memilih angka terkecil.

    Scope yang sama di brief, beda jauh di kepala vendor

    Ini penyebab paling sering. Istilah seperti “aplikasi kasir” atau “aplikasi manajemen stok” itu bukan satu barang yang bakunya sama di semua tempat. Satu vendor membacanya sebagai 10 fitur. Vendor lain membacanya sebagai 40 fitur. Keduanya jujur, tapi mereka menghitung barang yang berbeda.

    Contoh nyata. Aplikasi kasir versi sederhana isinya input barang, catat transaksi, cetak struk. Versi lengkapnya bisa punya multi cabang, manajemen stok dengan stok opname, laporan laba rugi, hak akses per karyawan, integrasi pembayaran digital, sampai mode offline saat internet mati. Nama di proposal sama-sama “aplikasi kasir”. Beban kerjanya bisa beda empat kali lipat.

    Kalau brief Anda hanya satu paragraf, setiap vendor akan mengisi kekosongannya dengan asumsi masing-masing. Vendor yang menawar murah mungkin berasumsi versi minimal. Vendor yang menawar mahal mungkin sudah menghitung skenario lengkap. Selisih harga di tahap ini belum tentu soal mahal atau murah. Sering kali itu soal dua tafsir yang tidak pernah disamakan.

    Siapa yang mengerjakan: satu orang atau satu tim

    Faktor kedua adalah struktur orang di baliknya. Freelancer solo bisa menawar jauh lebih murah karena semua peran dia pegang sendiri. Dia yang desain, dia yang koding, dia yang tes, dia juga yang balas chat Anda. Tidak ada biaya tim yang harus ditanggung.

    Vendor dengan tim menghitung beberapa orang sekaligus. Developer yang menulis kode, QA yang mencari bug sebelum Anda yang menemukannya, dan project manager yang menjaga jadwal serta jadi jembatan komunikasi. Setiap peran itu digaji, dan gaji itu masuk ke harga penawaran Anda.

    Konsekuensinya nyata, bukan sekadar teori. Aplikasi yang dites hanya oleh pembuatnya sendiri hampir selalu lolos dengan bug yang tidak dia sadari, karena dia mengetes dengan cara dia memakai, bukan cara user memakai. Dan kalau freelancer tunggal sakit, pindah kerja, atau menghilang di tengah proyek, tidak ada orang kedua yang paham kodenya. Proyek berhenti total. Di tim, ada orang lain yang bisa melanjutkan.

    Bukan berarti freelancer selalu pilihan buruk. Untuk aplikasi kecil dengan risiko rendah, freelancer yang bagus bisa sangat masuk akal. Tapi untuk sistem yang dipakai operasional harian bisnis Anda, sebagian dari selisih harga itu adalah harga dari tidak bergantung pada satu orang.

    Kualitas yang tidak kelihatan di demo

    Dua aplikasi bisa terlihat identik saat demo. Tampilan mirip, fitur jalan, tombol berfungsi. Yang membedakan harganya justru hal-hal yang tidak muncul di layar presentasi.

    Pertama, keamanan. Apakah password disimpan dengan benar, apakah aplikasi tahan terhadap serangan umum seperti SQL injection, apakah data pelanggan Anda tidak bocor lewat celah yang sudah lama dikenal. Mengerjakan ini butuh waktu dan keahlian, dan hasilnya tidak kelihatan sama sekali sampai ada insiden.

    Kedua, backup dan pemulihan. Aplikasi murah sering tidak punya backup otomatis. Semuanya baik-baik saja sampai server bermasalah dan data transaksi bertahun-tahun hilang dalam satu malam.

    Ketiga, kemampuan menangani banyak user. Aplikasi yang lancar dipakai dua orang saat demo bisa melambat parah saat dipakai lima puluh kasir di jam sibuk. Membuat aplikasi yang kuat di beban tinggi butuh arsitektur yang dipikirkan sejak awal, bukan tambalan belakangan.

    Keempat, kode yang bisa dirawat. Kode yang rapi dan terdokumentasi membuat penambahan fitur di tahun kedua jadi murah dan cepat. Kode yang asal jadi membuat setiap perubahan kecil terasa seperti operasi besar, dan sering kali developer lain menolak menyentuhnya. Anda tidak bisa melihat perbedaan ini di demo. Anda baru merasakannya setahun kemudian.

    Teknologi dan infrastruktur di baliknya

    Cara vendor membangun aplikasi juga sangat menentukan harga. Ada vendor yang memakai template jadi atau platform no-code, lalu menyesuaikan seperlunya. Ada yang membangun dari nol sesuai proses bisnis Anda. Keduanya sah, tapi harganya jelas beda jauh.

    Template dan no-code cepat dan murah di awal. Cocok kalau kebutuhan Anda memang standar. Masalah muncul kalau proses bisnis Anda tidak persis sama dengan asumsi template. Anda yang akhirnya menyesuaikan cara kerja ke aplikasinya, bukan sebaliknya. Beberapa platform no-code juga mengikat Anda pada biaya langganan yang terus berjalan, dan aplikasi tidak bisa dipindah keluar dari platform itu.

    Bangun dari nol lebih mahal karena semua dibuat mengikuti alur kerja Anda. Tapi hasilnya milik Anda sepenuhnya dan bisa dikembangkan ke arah mana pun.

    Infrastruktur juga sering luput dari perbandingan. Server yang layak untuk aplikasi bisnis, dengan spesifikasi cukup, monitoring, dan backup, biayanya berbeda jauh dengan hosting murah yang sebenarnya untuk website profil. Sebagian vendor memasukkan biaya server yang layak ke penawaran. Sebagian memakai yang paling murah supaya angka penawarannya menang. Angka totalnya jadi tidak sebanding, padahal kelihatannya sama-sama “sudah termasuk server”.

    Layanan setelah rilis, dan apa yang dipotong vendor banting harga

    Aplikasi itu bukan proyek yang selesai saat serah terima. Bug pasti ada, kebutuhan pasti berkembang, dan sistem bisa mati di waktu yang paling tidak enak. Di sinilah penawaran antar vendor sering beda isi walau beda harganya tidak kelihatan di halaman depan proposal.

    Hal yang perlu Anda cek: berapa lama garansi bug setelah rilis, apakah ada paket maintenance bulanan, dan seberapa cepat vendor merespons kalau sistem down saat operasional. Vendor yang menjanjikan respons cepat harus menyiapkan orang untuk siaga, dan itu ada biayanya. Vendor yang tidak menjanjikan apa-apa memang bisa lebih murah, karena setelah serah terima Anda pada dasarnya sendirian.

    Lalu soal banting harga. Kami akan jujur di sini, karena polanya hampir selalu sama. Vendor yang menawar jauh di bawah pasar jarang memotong fitur, karena fitur itu yang dilihat klien. Yang dipotong adalah hal-hal yang tidak kelihatan: waktu testing dipangkas, dokumentasi tidak dibuat, keamanan dikerjakan seadanya, backup dilewati, dan kode ditulis secepatnya tanpa memikirkan perawatan.

    Risikonya jatuh ke Anda, bukan ke vendor. Aplikasi tetap terlihat jadi di hari serah terima. Masalahnya muncul tiga sampai enam bulan kemudian, saat bug bermunculan, data bermasalah, dan vendor aslinya sudah sulit dihubungi. Beberapa klien kami datang persis di titik ini, dan biaya membenahinya sering lebih besar dari selisih harga yang dulu dihemat. Murah di depan bisa jadi mahal di belakang. Bukan karena semua vendor murah nakal, tapi karena angka yang terlalu rendah memang tidak cukup untuk membayar pekerjaan yang utuh.

    Cara membandingkan penawaran secara adil

    Setelah tahu faktor-faktor di atas, cara membandingkannya jadi lebih jelas. Intinya satu: pastikan Anda membandingkan barang yang sama.

    • Samakan scope secara tertulis. Buat daftar fitur yang rinci, termasuk jumlah user, jumlah cabang, dan laporan apa saja yang dibutuhkan. Kirim daftar yang sama ke semua vendor dan minta penawaran mengacu ke daftar itu, bukan ke tafsir masing-masing.
    • Tanya apa yang tidak termasuk. Ini pertanyaan yang paling jarang diajukan dan paling banyak membongkar. Apakah testing termasuk. Apakah server dan domain termasuk, dan untuk berapa lama. Apakah training user termasuk. Apakah revisi setelah rilis termasuk, dan sampai kapan.
    • Tanya siapa yang mengerjakan. Satu orang atau tim. Kalau tim, siapa saja perannya. Kalau orang kuncinya berhalangan, siapa penggantinya.
    • Tanya soal garansi dan maintenance. Berapa lama garansi bug, berapa biaya maintenance setelahnya, dan berapa lama waktu respons kalau sistem mati.
    • Tanya siapa yang memegang source code. Pastikan sejak awal bahwa source code diserahkan ke Anda, bukan ditahan vendor. Detail apa saja yang harus Anda terima saat proyek selesai sudah kami tulis lengkap di checklist serah terima proyek aplikasi dari vendor.

    Dengan lima hal itu tertulis hitam di atas putih, selisih harga antar vendor biasanya langsung bisa dijelaskan. Anda akan lihat vendor mana yang murah karena memang scope-nya kecil, dan mana yang murah karena ada yang dipotong diam-diam.

    Kalau Anda sedang membandingkan penawaran dan ingin pembanding yang jelas isinya, silakan lihat layanan pembuatan sistem aplikasi kami. Kami terbiasa merinci scope, apa yang termasuk dan tidak termasuk, serta skema garansi sejak awal. Atau kalau mau lebih cepat, hubungi kami dan bawa penawaran yang sudah Anda terima. Kami bisa bantu membacanya, bahkan kalau akhirnya Anda memilih vendor lain.

  • Belajar Golang dari Nol #5: Slice dan Map, Kumpulan Data di Go

    Belajar Golang dari Nol #5: Slice dan Map, Kumpulan Data di Go

    Di Belajar Golang dari Nol #4 kita sudah belajar membungkus data ke dalam struct dan menempelkan method padanya. Satu struct mewakili satu barang, satu user, atau satu produk. Tapi program nyata jarang mengurus satu data saja. Toko punya banyak produk. Sekolah punya banyak siswa. Kita butuh wadah untuk menampung kumpulan data. Di Go, dua wadah yang paling sering dipakai adalah slice dan map. Keduanya jadi menu utama kita hari ini.

    Sekilas tentang array

    Sebelum masuk ke slice, kenalan dulu dengan fondasinya, yaitu array. Array adalah deretan data dengan ukuran tetap. Sekali dibuat berisi 3 slot, selamanya 3 slot.

    var nilai [3]int
    nilai[0] = 80
    nilai[1] = 75
    nilai[2] = 90

    Bayangkan rak dengan jumlah laci yang sudah dipatok dari pabrik. Mau menambah laci keempat, tidak bisa. Karena kaku seperti itu, array jarang dipakai langsung dalam kode Go sehari-hari. Yang hampir selalu dipakai adalah slice, yang di belakang layar sebenarnya menumpang pada array. Cukup tahu itu dulu. Fokus kita sekarang ke slice.

    Slice, daftar yang bisa memanjang

    Slice mirip daftar belanja di kertas. Awalnya berisi tiga barang. Ingat sesuatu, tulis lagi di bawahnya. Daftarnya memanjang sendiri tanpa perlu ganti kertas.

    Membuat slice hampir sama dengan array, hanya tanpa angka di dalam kurung siku.

    package main
    
    import "fmt"
    
    func main() {
    	buah := []string{"apel", "mangga", "jeruk"}
    
    	fmt.Println(buah)      // [apel mangga jeruk]
    	fmt.Println(len(buah)) // 3
    	fmt.Println(buah[0])   // apel
    	fmt.Println(buah[2])   // jeruk
    }

    Tiga hal yang perlu dicatat dari kode di atas.

    • []string{...} disebut slice literal. Kita langsung mengisi datanya saat membuat.
    • len(buah) menghitung jumlah isi slice.
    • buah[0] mengakses isi berdasarkan posisi. Posisi dihitung dari 0, bukan dari 1.

    Untuk menambah isi, pakai append.

    buah = append(buah, "salak")
    fmt.Println(buah)      // [apel mangga jeruk salak]
    fmt.Println(len(buah)) // 4

    Perhatikan polanya. Hasil append harus disimpan kembali ke variabelnya. Menulis append(buah, "salak") saja tanpa menampung hasilnya tidak akan mengubah apa pun, dan Go bahkan menolaknya saat compile. Anggap append seperti fotokopi daftar lama plus satu baris baru. Daftar barunya harus kita pegang.

    Slice juga bisa dipotong. Sintaksnya a[awal:akhir]. Posisi awal ikut diambil, posisi akhir tidak.

    potongan := buah[1:3]
    fmt.Println(potongan) // [mangga jeruk]

    buah[1:3] artinya ambil mulai posisi 1 sampai sebelum posisi 3. Jadi posisi 1 dan 2 saja. Awalnya sering bikin bingung, tapi lama-lama terasa natural.

    Slice berisi struct

    Di bagian 4 kita sudah bisa membuat struct Produk. Sekarang gabungkan. Slice tidak hanya bisa menampung string atau angka. Ia bisa menampung struct. Inilah cara Go menyimpan daftar produk, daftar siswa, atau daftar transaksi.

    package main
    
    import "fmt"
    
    type Produk struct {
    	Nama  string
    	Harga int
    }
    
    func main() {
    	daftar := []Produk{
    		{Nama: "Kopi Bubuk", Harga: 25000},
    		{Nama: "Gula Pasir", Harga: 15000},
    		{Nama: "Teh Celup", Harga: 10000},
    	}
    
    	for _, p := range daftar {
    		fmt.Println(p.Nama, "harganya Rp", p.Harga)
    	}
    }

    Baris for _, p := range daftar artinya kunjungi setiap isi slice satu per satu, lalu simpan isinya ke variabel p. Tanda _ dipakai karena kita tidak butuh nomor urutnya. Detail lengkap soal for dan range kita bahas tuntas di bagian 6. Sekarang cukup pahami bahwa pola ini dipakai untuk menelusuri isi slice.

    Map, mencari data lewat kunci

    Slice mencari data lewat posisi. Map mencari data lewat kunci. Bayangkan loker di kolam renang. Anda tidak menghafal loker keberapa dari kiri. Anda cukup pegang kuncinya, lalu langsung buka loker yang cocok. Kunci pada map bisa berupa string, misalnya nama barang, dan nilainya bisa apa saja.

    Bentuk penulisannya map[tipeKunci]tipeNilai. Contoh map untuk mencatat stok barang.

    package main
    
    import "fmt"
    
    func main() {
    	stok := map[string]int{
    		"kopi": 10,
    		"gula": 5,
    	}
    
    	// menambah data baru
    	stok["teh"] = 8
    
    	// mengubah data yang sudah ada
    	stok["kopi"] = 12
    
    	// menghapus data
    	delete(stok, "gula")
    
    	fmt.Println(stok)        // map[kopi:12 teh:8]
    	fmt.Println(stok["teh"]) // 8
    }

    Menambah dan mengubah pakai sintaks yang sama, yaitu stok["kunci"] = nilai. Kalau kuncinya belum ada, data baru dibuat. Kalau sudah ada, nilainya ditimpa. Menghapus pakai fungsi bawaan delete.

    Comma ok, cara aman mengecek isi map

    Sekarang bagian penting. Apa yang terjadi kalau kita membaca kunci yang tidak ada?

    fmt.Println(stok["beras"]) // 0

    Go tidak error. Ia mengembalikan zero value dari tipe nilainya. Masih ingat materi zero value di bagian 2? Untuk int, zero value adalah 0. Di sinilah masalahnya. Angka 0 itu ambigu. Apakah beras memang tidak terdaftar, atau beras terdaftar tapi stoknya sedang kosong? Dua kondisi yang sangat berbeda, hasilnya sama-sama 0.

    Go menyediakan cara untuk membedakannya. Namanya comma ok idiom.

    jumlah, ok := stok["beras"]
    if ok {
    	fmt.Println("beras terdaftar, stok:", jumlah)
    } else {
    	fmt.Println("beras tidak terdaftar di toko")
    }

    Saat membaca map dengan dua variabel penampung, variabel kedua berisi true kalau kuncinya memang ada, dan false kalau tidak. Dengan begitu kita tidak lagi tertipu oleh zero value. Biasakan pakai pola ini setiap kali keberadaan kunci itu penting untuk logika program. Di program latihan nanti, pola ini yang membedakan barang habis dengan barang yang memang tidak dijual.

    Map dengan value struct

    Sama seperti slice, nilai pada map juga boleh berupa struct. Cocok untuk katalog, di mana nama barang jadi kunci dan detailnya jadi nilai.

    katalog := map[string]Produk{
    	"kopi": {Nama: "Kopi Bubuk", Harga: 25000},
    	"gula": {Nama: "Gula Pasir", Harga: 15000},
    }
    
    fmt.Println(katalog["kopi"].Harga) // 25000

    Sekali akses, kita langsung dapat struct utuh lengkap dengan semua field-nya.

    Kapan pakai slice, kapan pakai map

    Aturan praktisnya sederhana.

    • Pakai slice kalau urutan itu penting atau data memang berbentuk antrean. Daftar transaksi, riwayat chat, isi keranjang belanja. Data tersimpan berurutan dan boleh ada isi yang kembar.
    • Pakai map kalau Anda sering mencari data berdasarkan kunci tertentu. Stok per nama barang, user per email, konfigurasi per nama setting. Aksesnya langsung ketemu tanpa menelusuri satu per satu, tapi urutannya tidak dijamin.

    Satu catatan soal urutan. Saat map ditelusuri dengan range, Go sengaja mengacak urutannya. Jadi jangan pernah mengandalkan urutan pada map. Kalau butuh urutan, pakailah slice. Keduanya juga sering dipakai bersamaan dalam satu program, seperti yang akan kita lakukan di latihan.

    Dua jebakan yang sering menjerat pemula

    Nil map tidak bisa diisi

    Kalau map hanya dideklarasikan tanpa diinisialisasi, nilainya nil. Membaca nil map masih aman, hasilnya zero value. Tapi menulis ke nil map membuat program langsung panic alias berhenti mendadak.

    var umur map[string]int
    umur["budi"] = 20 // panic: assignment to entry in nil map

    Solusinya, siapkan map-nya dulu dengan make atau dengan literal kosong.

    umur := make(map[string]int)
    umur["budi"] = 20 // aman

    Anggap make seperti memasang papan loker sebelum kunci pertama dibagikan. Slice lebih pemaaf di sini, karena append pada slice nil tetap aman. Map tidak. Ini salah satu penyebab panic paling umum pada program Go pemula.

    Slice yang berbagi data

    Jebakan kedua lebih halus. Hasil pemotongan slice tidak menyalin data. Ia menunjuk ke deretan data yang sama dengan slice asalnya. Mengubah satu, yang lain ikut berubah.

    angka := []int{1, 2, 3, 4}
    potongan := angka[0:2]
    potongan[0] = 99
    
    fmt.Println(potongan) // [99 2]
    fmt.Println(angka)    // [99 2 3 4]

    Perhatikan, angka ikut berubah padahal kita hanya mengubah potongan. Keduanya memandang lemari data yang sama dari jendela yang berbeda. Ini bukan bug, ini memang desain Go supaya pemotongan slice murah dan cepat. Tapi kalau tidak sadar, efek samping seperti ini bisa bikin bingung berjam-jam. Kalau memang butuh salinan yang benar-benar terpisah, Go punya fungsi copy yang bisa Anda eksplorasi sendiri.

    Latihan: keranjang belanja mini

    Waktunya merangkai semua materi hari ini jadi satu program utuh. Kita buat keranjang belanja mini dengan aturan begini. Stok barang tersimpan di map. Katalog produk tersimpan di map dengan value struct. Isi keranjang tersimpan di slice. Fungsi penambah barang wajib mengecek dulu lewat comma ok, supaya barang yang tidak dijual dan barang yang stoknya habis diperlakukan beda.

    package main
    
    import "fmt"
    
    type Produk struct {
    	Nama  string
    	Harga int
    }
    
    func tambahKeKeranjang(keranjang []Produk, stok map[string]int, katalog map[string]Produk, nama string) []Produk {
    	sisa, ok := stok[nama]
    	if !ok {
    		fmt.Println(nama, ": barang tidak ada di toko")
    		return keranjang
    	}
    	if sisa == 0 {
    		fmt.Println(nama, ": stok sedang habis")
    		return keranjang
    	}
    
    	stok[nama] = sisa - 1
    	keranjang = append(keranjang, katalog[nama])
    	fmt.Println(nama, ": masuk keranjang")
    	return keranjang
    }
    
    func main() {
    	stok := map[string]int{
    		"kopi": 2,
    		"gula": 0,
    		"teh":  5,
    	}
    
    	katalog := map[string]Produk{
    		"kopi": {Nama: "Kopi Bubuk", Harga: 25000},
    		"gula": {Nama: "Gula Pasir", Harga: 15000},
    		"teh":  {Nama: "Teh Celup", Harga: 10000},
    	}
    
    	var keranjang []Produk
    	keranjang = tambahKeKeranjang(keranjang, stok, katalog, "kopi")
    	keranjang = tambahKeKeranjang(keranjang, stok, katalog, "teh")
    	keranjang = tambahKeKeranjang(keranjang, stok, katalog, "gula")
    	keranjang = tambahKeKeranjang(keranjang, stok, katalog, "beras")
    
    	fmt.Println()
    	fmt.Println("Isi keranjang:")
    	total := 0
    	for _, p := range keranjang {
    		fmt.Println("-", p.Nama, "Rp", p.Harga)
    		total = total + p.Harga
    	}
    	fmt.Println("Total belanja: Rp", total)
    }

    Jalankan dengan go run main.go. Hasilnya seperti ini.

    kopi : masuk keranjang
    teh : masuk keranjang
    gula : stok sedang habis
    beras : barang tidak ada di toko
    
    Isi keranjang:
    - Kopi Bubuk Rp 25000
    - Teh Celup Rp 10000
    Total belanja: Rp 35000

    Coba telusuri alurnya pelan-pelan. Kopi dan teh masuk keranjang karena kuncinya ada dan stoknya lebih dari 0. Gula ditolak dengan pesan stok habis, karena comma ok mengembalikan true tapi sisanya 0. Beras ditolak dengan pesan berbeda, karena comma ok mengembalikan false. Tanpa idiom itu, gula dan beras akan terlihat sama persis di mata program. Perhatikan juga bahwa tambahKeKeranjang mengembalikan slice baru dan hasilnya selalu kita tampung lagi, konsisten dengan pola append yang tadi kita bahas.

    Sebagai latihan tambahan, coba dua hal ini. Pertama, tambahkan barang baru ke katalog dan stok, lalu beli beberapa kali sampai stoknya habis. Kedua, ubah program supaya satu barang bisa dibeli dengan jumlah tertentu sekaligus, bukan satu-satu.

    Selanjutnya di seri ini

    Anda sekarang pegang dua wadah data paling penting di Go. Slice untuk daftar yang berurutan, map untuk pencarian cepat lewat kunci, plus comma ok untuk membaca map dengan aman. Sepanjang artikel ini kita sudah beberapa kali memakai for range dan if tanpa membedahnya dalam-dalam. Itu sengaja. Di bagian 6, Perulangan for dan Percabangan: Mengendalikan Alur Program, keduanya kita kupas tuntas, mulai dari berbagai bentuk for sampai switch. Sampai jumpa di bagian berikutnya.

    Kalau bisnis Anda butuh aplikasi dengan pengelolaan data seperti ini, mulai dari katalog produk sampai sistem stok, tim kami siap membantu lewat layanan pengembangan sistem aplikasi.

  • Belajar Database dari Nol #1: Kenalan & Install MySQL 8.4

    Belajar Database dari Nol #1: Kenalan & Install MySQL 8.4

    Cara install MySQL 8.4 sebenarnya sederhana: di Windows pakai installer MSI resmi, di Ubuntu pakai repositori APT dari MySQL, di macOS pakai Homebrew. Setelah terpasang, kamu login lewat terminal dengan mysql -u root -p lalu cek versinya dengan SELECT VERSION();. Artikel ini memandu ketiganya langkah demi langkah, plus solusi untuk error yang paling sering muncul di instalasi pertama.

    Ini bagian pertama dari seri Belajar Database dari Nol, seri tutorial 26 bagian yang membawa kamu dari nol sampai bisa mendesain dan mengelola database sendiri. Kita pakai MySQL 8.4 karena statusnya LTS (Long Term Support), didukung sampai tahun 2032, dan paling banyak dipakai di industri. Sebelum praktik, kita luruskan dulu satu hal: apa sebenarnya database itu.

    Apa Itu Database dan Kenapa Aplikasi Membutuhkannya

    Database adalah kumpulan data yang disimpan secara terstruktur supaya mudah dicari, diubah, dan dijaga konsistensinya. Aplikasi yang mengelola database itu disebut DBMS, singkatan dari Database Management System. MySQL adalah salah satu DBMS. Jadi kalau ada yang bilang “databasenya MySQL”, maksudnya data mereka dikelola oleh software MySQL.

    Pertanyaan yang wajar dari pemula: kenapa tidak pakai Excel atau file teks saja? Untuk data pribadi yang diakses satu orang, Excel memang cukup. Masalah muncul saat data dipakai aplikasi:

    • Akses bersamaan. Ratusan pengguna bisa menulis data di detik yang sama. File Excel akan korup atau saling menimpa. DBMS mengatur antrean ini secara otomatis.
    • Kecepatan pencarian. Mencari satu baris di antara jutaan baris di file teks berarti membaca seluruh file. DBMS punya index sehingga pencarian tetap cepat walau datanya jutaan baris.
    • Konsistensi. Saat transfer saldo gagal di tengah jalan, DBMS bisa membatalkan seluruh operasi supaya uang tidak hilang sebagian. File biasa tidak punya mekanisme ini.
    • Aturan data. DBMS bisa memaksa kolom email selalu terisi atau stok tidak boleh minus. Di Excel, aturan seperti ini gampang dilanggar.

    Di Arrazy, hampir semua sistem aplikasi yang kami bangun untuk klien memakai database relasional seperti MySQL atau PostgreSQL di belakangnya, dari sistem inventaris gudang sampai backend API. Skill database adalah fondasi yang akan terus kamu pakai selama berkarier di dunia software.

    Persiapan Sebelum Install MySQL 8.4

    Yang kamu butuhkan hanya komputer dengan salah satu sistem operasi berikut, koneksi internet, dan ruang disk sekitar 2 GB:

    Sistem Operasi Metode Install Sumber
    Windows 10/11 Installer MSI dev.mysql.com/downloads
    Ubuntu 22.04/24.04 Repositori APT MySQL dev.mysql.com/downloads/repo/apt
    macOS (Apple Silicon/Intel) Homebrew brew.sh

    Satu catatan penting: selama proses install kamu akan diminta membuat password untuk user root. Catat password ini baik-baik. Sebagian besar masalah login di kemudian hari berawal dari password root yang lupa.

    Cara Install MySQL 8.4 di Windows

    Mulai versi 8.1, MySQL di Windows tidak lagi memakai aplikasi “MySQL Installer” yang lama. Sekarang instalasinya lewat paket MSI langsung, lalu konfigurasi lewat MySQL Configurator.

    1. Buka https://dev.mysql.com/downloads/mysql/, pilih versi 8.4.x LTS, lalu unduh Windows (x86, 64-bit), MSI Installer yang ukurannya besar (bukan yang web installer). Kamu boleh klik “No thanks, just start my download” tanpa login Oracle.
    2. Jalankan file mysql-8.4.x-winx64.msi, pilih tipe instalasi Typical, lalu selesaikan wizard-nya.
    3. Di akhir instalasi, centang opsi untuk menjalankan MySQL Configurator. Di sinilah kamu mengatur MySQL sebagai Windows Service, membiarkan port default 3306, dan membuat password root.
    4. Selesaikan Configurator sampai semua langkah bertanda hijau.

    Supaya perintah mysql bisa dipanggil dari terminal mana saja, tambahkan folder C:\Program Files\MySQL\MySQL Server 8.4\bin ke environment variable Path. Caranya: cari “Edit the system environment variables” di Start Menu, klik Environment Variables, pilih Path, klik Edit, lalu New dan tempel path tadi. Buka Command Prompt baru dan cek:

    mysql --version

    Output yang diharapkan kurang lebih seperti ini (versi minor bisa berbeda):

    mysql  Ver 8.4.5 for Win64 on x86_64 (MySQL Community Server - GPL)

    Cara Install MySQL 8.4 di Ubuntu dan Linux Lain

    Repositori bawaan Ubuntu 22.04 dan 24.04 masih menyediakan MySQL 8.0. Untuk mendapatkan 8.4 LTS, tambahkan dulu repositori APT resmi dari MySQL. Cek versi paket mysql-apt-config terbaru di https://dev.mysql.com/downloads/repo/apt/, lalu jalankan:

    wget https://dev.mysql.com/get/mysql-apt-config_0.8.34-1_all.deb
    sudo dpkg -i mysql-apt-config_0.8.34-1_all.deb

    Akan muncul layar konfigurasi berwarna ungu. Pilih MySQL Server & Cluster, ganti ke mysql-8.4-lts, lalu pilih Ok. Setelah itu install servernya:

    sudo apt update
    sudo apt install mysql-server

    Saat instalasi kamu akan diminta membuat password root. Isi dan catat. Kalau kolomnya kamu kosongkan, MySQL akan memakai mode autentikasi auth_socket, artinya login root hanya bisa lewat sudo mysql. Setelah selesai, pastikan service-nya jalan:

    sudo systemctl status mysql

    Cari baris ini di output:

    Active: active (running)

    Kalau statusnya inactive, nyalakan dengan sudo systemctl start mysql dan aktifkan autostart dengan sudo systemctl enable mysql. Langkah opsional tapi disarankan, jalankan sudo mysql_secure_installation untuk menghapus user anonim dan database test bawaan.

    Cara Install MySQL 8.4 di macOS

    Cara paling praktis di macOS adalah lewat Homebrew. Kalau belum punya Homebrew, install dulu dari https://brew.sh. Lalu jalankan:

    brew install mysql@8.4
    brew services start mysql@8.4

    Formula mysql@8.4 bersifat keg-only, artinya tidak otomatis masuk ke PATH. Tambahkan manual (contoh untuk Mac Apple Silicon dengan zsh):

    echo 'export PATH="/opt/homebrew/opt/mysql@8.4/bin:$PATH"' >> ~/.zshrc
    source ~/.zshrc

    Untuk Mac Intel, ganti /opt/homebrew dengan /usr/local. Instalasi lewat Homebrew membuat user root tanpa password. Rapikan dengan menjalankan mysql_secure_installation dan set password root di sana. Alternatif lain kalau tidak mau pakai Homebrew: unduh installer DMG dari https://dev.mysql.com/downloads/mysql/ dan ikuti wizard-nya, mirip proses di Windows.

    Login Pertama Lewat Terminal dan Verifikasi Instalasi

    Apa pun sistem operasinya, momen kebenarannya sama. Buka terminal (Command Prompt atau PowerShell di Windows) dan jalankan:

    mysql -u root -p

    Arti perintahnya: masuk ke MySQL sebagai user root (-u root) dan minta prompt password (-p). Ketik password yang kamu buat saat instalasi. Password tidak akan terlihat saat diketik, itu normal. Kalau berhasil, kamu akan disambut prompt MySQL:

    Welcome to the MySQL monitor.  Commands end with ; or \g.
    Your MySQL connection id is 10
    Server version: 8.4.5 MySQL Community Server - GPL
    
    mysql>

    Sekarang jalankan query pertamamu. Jangan lupa titik koma di akhir:

    SELECT VERSION();

    Outputnya:

    +-----------+
    | VERSION() |
    +-----------+
    | 8.4.5     |
    +-----------+
    1 row in set (0.00 sec)

    Kalau angka yang muncul diawali 8.4, instalasimu sukses. Ketik exit untuk keluar dari prompt MySQL. Selamat, kamu resmi punya server database sendiri.

    Error Umum Saat Install MySQL dan Solusinya

    Tiga error di bawah ini adalah yang paling sering kami temui saat mendampingi pemula maupun saat menyiapkan server klien. Cek satu per satu sebelum panik.

    ERROR 1045: Access denied for user ‘root’@’localhost’

    Penyebab paling umum: password salah, atau di Ubuntu kamu mengosongkan password saat install sehingga root memakai auth_socket. Coba dulu login lewat sudo:

    sudo mysql

    Kalau berhasil masuk, set password root dari dalam prompt MySQL supaya selanjutnya bisa login normal:

    ALTER USER 'root'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'PasswordBaruKamu123';
    FLUSH PRIVILEGES;

    Ganti PasswordBaruKamu123 dengan password pilihanmu. Kalau password benar-benar lupa dan sudo mysql juga ditolak, jalan keluarnya adalah menjalankan server dengan opsi --skip-grant-tables lalu reset password, prosedurnya ada di dokumentasi resmi bagian “How to Reset the Root Password”.

    ERROR 2002: Can’t connect to local MySQL server (service tidak jalan)

    Error ini artinya client tidak menemukan server yang hidup. Di Linux dan macOS cek dengan sudo systemctl status mysql atau brew services list, lalu nyalakan dengan sudo systemctl start mysql atau brew services start mysql@8.4. Di Windows, tekan Win + R, ketik services.msc, cari service bernama MySQL84, klik kanan lalu Start. Kalau service menolak jalan, baca log error di /var/log/mysql/error.log (Ubuntu) atau folder data MySQL di Windows, biasanya penyebab pastinya tertulis di baris terakhir.

    Port 3306 sudah dipakai program lain

    MySQL default berjalan di port 3306. Kalau di komputermu pernah terpasang XAMPP, Laragon, MariaDB, atau MySQL versi lain, port ini bisa bentrok dan service baru gagal start. Cek siapa yang memakai port itu:

    # Linux / macOS
    sudo lsof -i :3306
    
    # Windows (PowerShell)
    netstat -ano | findstr :3306

    Solusinya pilih salah satu: matikan program lama yang menempati port itu (misalnya stop MySQL bawaan XAMPP), uninstall MySQL/MariaDB lama yang sudah tidak dipakai, atau ubah port MySQL 8.4 lewat file konfigurasi (my.ini di Windows, /etc/mysql/mysql.conf.d/mysqld.cnf di Ubuntu) dengan mengganti nilai port=3306 lalu restart service. Untuk mengikuti seri ini, cara paling bersih adalah satu MySQL saja di port default.

    Menyiapkan MySQL Client dan GUI Opsional

    Sepanjang seri ini kita akan bekerja terutama lewat mysql client di terminal, program yang barusan kamu pakai untuk login. Alasannya sederhana: semua perintah SQL yang kamu ketik manual akan menempel lebih lama di ingatan, dan di server production nanti sering kali terminal adalah satu-satunya akses yang tersedia.

    Meski begitu, GUI boleh dipakai sebagai pendamping. Beberapa pilihan yang umum:

    • MySQL Workbench, GUI resmi dari MySQL, gratis, tersedia untuk Windows, Linux, dan macOS. Unduh di https://dev.mysql.com/downloads/workbench/. Saat membuat koneksi baru, isi host 127.0.0.1, port 3306, user root, dan password yang tadi kamu buat.
    • DBeaver Community, gratis dan mendukung banyak DBMS sekaligus, berguna nanti saat kita menyentuh PostgreSQL di bagian 24.
    • phpMyAdmin, berbasis web, sering ditemui di shared hosting.

    Saran kami untuk pemula: install MySQL Workbench sebagai alat bantu melihat isi tabel, tapi tetap kerjakan latihan di terminal. Kombinasi ini yang paling cepat membangun pemahaman.

    Rangkuman dan Bagian Berikutnya

    Hari ini kamu sudah paham beda database, DBMS, dan alasan aplikasi tidak cukup pakai Excel. Kamu juga sudah praktik cara install MySQL 8.4 LTS di sistem operasimu, login pertama dengan mysql -u root -p, memverifikasi instalasi lewat SELECT VERSION();, dan tahu cara keluar dari tiga error paling umum.

    Di bagian berikutnya, “Belajar Database dari Nol #2: Konsep Database Relasional”, kita bahas cara MySQL menyusun data dalam tabel, baris, kolom, dan primary key, fondasi berpikir sebelum menulis SQL lebih jauh. Artikelnya terbit menyusul dan bisa kamu pantau di halaman hub seri Belajar Database. Sampai jumpa di bagian dua.

    Referensi

  • Template Brief Website: Isi 7 Hal Ini Sebelum Ketemu Developer

    Template Brief Website: Isi 7 Hal Ini Sebelum Ketemu Developer

    Brief website adalah catatan singkat berisi gambaran proyek yang Anda serahkan ke developer sebelum diskusi dimulai. Tidak perlu rumit. Cukup satu sampai dua halaman yang menjawab pertanyaan dasar: website ini untuk apa, isinya apa saja, dan siapa yang berhak memutuskan. Dokumen sesederhana ini sering jadi pembeda antara proyek yang selesai tepat waktu dengan proyek yang molor karena revisi tidak habis-habis.

    Ada tujuh hal yang perlu Anda isi sebelum ketemu developer mana pun: tujuan website dalam satu kalimat, dua sampai tiga referensi website yang Anda suka, daftar konten yang sudah siap, daftar halaman yang dibutuhkan, status domain dan hosting, rentang budget beserta target waktu, lalu siapa PIC dan pengambil keputusan. Artikel ini membahas satu per satu, termasuk alasan kenapa developer selalu menanyakan hal-hal itu di awal.

    Kenapa brief yang jelas bikin proyek lebih cepat dan murah

    Kami menulis ini dari sisi meja yang satunya. Sebagai software house, kami menerima banyak permintaan yang bunyinya kurang lebih sama: “mau bikin website, kira-kira berapa ya?” Pertanyaan itu wajar. Tapi tanpa gambaran proyek, jawaban paling jujur dari developer adalah rentang harga yang lebar, dan biasanya diambil dari sisi yang aman untuk mereka.

    Logikanya sederhana. Kalau lingkup pekerjaan kabur, developer akan menebak. Tebakan itu dilapisi buffer supaya mereka tidak rugi kalau ternyata permintaannya membengkak di tengah jalan. Anda yang membayar buffer itu. Sebaliknya, brief yang jelas membuat estimasi lebih presisi, timeline lebih realistis, dan revisi berkurang drastis karena semua pihak membayangkan hasil akhir yang sama sejak awal.

    Brief juga menyaring ekspektasi Anda sendiri. Saat menulis tujuan website dalam satu kalimat, banyak calon klien baru sadar bahwa yang mereka butuhkan ternyata lebih sederhana dari bayangan awal. Atau sebaliknya, lebih kompleks. Dua-duanya lebih baik diketahui sebelum kontrak diteken, bukan sesudahnya.

    Tujuan website dan referensi yang Anda suka

    1. Tujuan website: cukup satu kalimat

    Tulis satu kalimat yang menjawab: setelah pengunjung membuka website ini, mereka diharapkan ngapain? Chat ke WhatsApp? Mengisi form penawaran? Membeli langsung? Sekadar percaya bahwa bisnis Anda kredibel sebelum mereka datang ke toko?

    Developer menanyakan ini karena satu tujuan utama menentukan hampir semua keputusan desain. Website yang tujuannya “pengunjung chat WA” akan punya tombol WhatsApp di mana-mana dan halaman yang pendek. Website yang tujuannya “pengunjung beli langsung” butuh katalog, keranjang, dan integrasi pembayaran. Struktur, biaya, dan waktu pengerjaannya beda jauh. Kalau tujuannya lebih dari satu, urutkan mana yang paling penting.

    2. Referensi: dua sampai tiga website, plus alasannya

    Cari dua sampai tiga website yang menurut Anda enak dilihat, lalu tulis kenapa Anda menyukainya. Bagian “kenapa” ini yang paling sering dilewatkan. “Saya suka website A karena bagus” tidak membantu siapa pun. “Saya suka website A karena warnanya kalem dan menunya sedikit” langsung bisa diterjemahkan jadi keputusan desain.

    Developer menanyakan referensi bukan untuk meniru, tapi untuk menangkap selera Anda tanpa harus lewat lima putaran revisi desain. Referensi juga tidak harus dari industri yang sama. Website hotel yang rapi bisa jadi acuan untuk website catering, selama Anda bisa menjelaskan bagian mana yang ingin diambil semangatnya.

    Konten yang sudah siap dan halaman yang dibutuhkan

    3. Cek dulu konten yang sudah Anda punya

    Buat daftar sederhana berisi status konten Anda saat ini:

    • Logo: sudah ada file aslinya atau baru gambar kecil dari media sosial
    • Foto produk atau kegiatan: sudah ada yang layak tampil atau perlu foto ulang
    • Teks profil perusahaan: sudah tertulis atau baru ada di kepala
    • Daftar layanan atau produk beserta deskripsinya
    • Testimoni pelanggan, kalau ada

    Kenapa developer peduli soal ini? Karena konten adalah penyebab molor nomor satu di proyek website. Desain bisa selesai dalam dua minggu, lalu website menganggur sebulan menunggu teks profil yang “sebentar lagi jadi”. Kalau dari awal jelas konten mana yang belum ada, developer bisa membantu menyiapkannya atau setidaknya memasukkan waktu tunggunya ke timeline.

    4. Daftar halaman yang realistis

    Tulis halaman apa saja yang Anda butuhkan. Untuk kebanyakan bisnis, daftarnya tidak panjang: beranda, tentang kami, layanan atau produk, galeri, dan kontak. Hindari jawaban “pokoknya yang lengkap” karena kata lengkap itu bisa berarti lima halaman atau lima puluh halaman, dan harganya jelas beda.

    Daftar halaman dipakai developer untuk menghitung lingkup kerja. Setiap halaman butuh desain, konten, dan pengujian. Kalau ragu suatu halaman perlu atau tidak, tanyakan saja saat diskusi. Menghapus halaman dari rencana itu gratis. Menambah halaman setelah proyek jalan hampir selalu ada biayanya.

    Domain dan hosting: sudah punya atau belum, atas nama siapa

    Poin kelima ini sering dianggap urusan teknis yang bisa dipikirkan nanti. Padahal jawabannya cuma dua pertanyaan: apakah Anda sudah punya domain dan hosting, dan kalau sudah, terdaftar atas nama siapa.

    Bagian “atas nama siapa” itu penting. Cukup banyak kasus bisnis yang domainnya didaftarkan atas nama vendor lama atau karyawan yang sudah keluar, lalu kesulitan mengaksesnya saat mau pindah atau perpanjang. Domain adalah alamat bisnis Anda di internet. Idealnya terdaftar atas nama Anda atau perusahaan Anda, dengan akses akun yang Anda pegang sendiri, siapa pun yang mengerjakan websitenya.

    Developer menanyakan ini di awal karena memengaruhi rencana kerja. Kalau domain sudah ada, mereka perlu akses untuk mengarahkannya ke server baru. Kalau belum, pembelian domain bisa sekalian diurus, dan Anda tinggal memastikan kepemilikannya jelas sejak hari pertama.

    Budget, target waktu, dan siapa yang memutuskan

    5. Kenapa jujur soal budget justru menguntungkan Anda

    Banyak calon klien menahan angka budget karena takut dimanfaatkan. Kekhawatiran itu bisa dipahami, tapi menyembunyikan budget biasanya malah merugikan. Tanpa angka, developer akan menawarkan paket standar yang belum tentu cocok, atau bolak-balik menyusun ulang penawaran sampai ketemu angka yang pas. Itu buang waktu dua belah pihak.

    Dengan rentang budget yang jelas, misalnya lima sampai sepuluh juta, developer yang baik akan menyusun solusi terbaik di dalam rentang itu. Fitur mana yang masuk sekarang, mana yang bisa ditunda ke tahap dua. Kalau kebutuhan Anda memang tidak mungkin dikerjakan dengan budget itu, Anda juga akan tahu lebih cepat, sebelum keluar biaya apa pun. Sepaket dengan budget, tulis juga target waktu: apakah ada momen tertentu seperti pembukaan cabang atau event yang websitenya harus tayang sebelum itu.

    6. Tentukan PIC dan pengambil keputusan

    Tulis dua nama di brief Anda. Pertama, PIC: orang yang jadi kontak sehari-hari, mengirim materi, dan menjawab pertanyaan developer. Kedua, pengambil keputusan: orang yang kata “oke”-nya bersifat final untuk desain dan konten. Boleh orang yang sama, dan justru lebih sederhana kalau begitu.

    Developer menanyakan ini karena revisi yang muter hampir selalu bersumber dari keputusan yang tidak jelas pemiliknya. Desain sudah disetujui PIC, lalu minggu depan dibatalkan pemilik usaha yang baru sempat melihat. Kejadian seperti ini bisa mengulang pekerjaan dua minggu. Kalau pengambil keputusan orangnya sibuk, sepakati saja titik-titik mana yang butuh persetujuannya, misalnya cukup di desain awal dan sebelum tayang.

    Contoh brief yang sudah jadi

    Supaya kebayang bentuk akhirnya, ini contoh brief fiktif dari sebuah usaha catering rumahan. Perhatikan betapa pendeknya:

    • Usaha: Catering Dapur Renjana, melayani nasi kotak dan prasmanan untuk acara kantor dan hajatan di Purwokerto dan sekitarnya.
    • Tujuan website: pengunjung melihat daftar paket dan harga, lalu chat ke WhatsApp untuk pesan.
    • Referensi: website catering X karena daftar menunya rapi dengan foto besar, website bakery Y karena warnanya hangat dan terasa homey. Tidak suka website yang banyak animasi.
    • Konten siap: logo (file dari desainer masih ada), sekitar 40 foto masakan hasil foto sendiri, daftar 6 paket beserta harga. Belum ada: teks profil usaha, testimoni masih berupa screenshot chat.
    • Halaman: beranda, daftar paket dan harga, galeri, tentang kami, kontak.
    • Domain dan hosting: belum punya. Ingin domain .com atas nama pemilik usaha.
    • Budget dan waktu: 4 sampai 7 juta. Target tayang sebelum musim hajatan, sekitar 2 bulan dari sekarang.
    • PIC: Rani (admin, pegang WhatsApp usaha). Keputusan final: Bu Sari (pemilik), cukup dilibatkan saat pilih desain dan sebelum website tayang.

    Brief seperti ini bisa Anda tulis dalam satu jam. Dengan dokumen ini, developer mana pun bisa langsung memberi estimasi yang masuk akal di pertemuan pertama, bukan janji “nanti kami hitung dulu” yang menggantung berminggu-minggu.

    Setelah brief siap, apa langkah berikutnya

    Kirim brief Anda ke beberapa penyedia jasa pembuatan website, lalu bandingkan respons mereka. Dari cara developer menanggapi brief, Anda bisa menilai banyak hal: apakah mereka membaca dengan teliti, apakah pertanyaannya menunjukkan pemahaman, dan apakah estimasinya dijelaskan dengan alasan yang jelas.

    Satu catatan untuk nanti. Brief yang rapi di awal sebaiknya ditutup dengan serah terima yang rapi di akhir. Setelah proyek berjalan, sempatkan baca checklist serah terima proyek dari vendor supaya Anda tahu apa saja yang wajib Anda terima saat proyek selesai, termasuk akses dan kredensial.

    Kalau briefnya sudah jadi dan Anda ingin tahu kira-kira berapa biayanya, silakan kirim brief Anda ke tim Arrazy. Kami akan membalas dengan estimasi biaya dan waktu yang jujur, termasuk kalau menurut kami ada bagian yang sebaiknya disederhanakan dulu. Brief Anda tidak harus sempurna. Yang penting mulai ditulis.

  • Pelanggan Tanya Harga Lalu Hilang? Begini Cara Follow-Up-nya

    Pelanggan Tanya Harga Lalu Hilang? Begini Cara Follow-Up-nya

    Pelanggan tanya harga, kita jawab dengan semangat, lalu dia hilang begitu saja. Hampir semua yang jualan pernah mengalami ini. Kabar baiknya, sebagian dari mereka sebenarnya masih bisa kembali. Caranya bukan dengan menagih “jadi order kak?”, tapi dengan follow-up yang punya alasan. Setiap kali chat lagi, bawa sesuatu yang baru. Bisa info promo, info stok, penawaran alternatif, atau sekadar jawaban tambahan yang relevan dengan pertanyaan dia kemarin.

    Aturan mainnya sederhana. Follow-up pertama dikirim sehari setelah dia tanya harga, follow-up berikutnya beri jeda dua sampai tiga hari, dan totalnya cukup dua sampai tiga kali saja. Kirim di jam yang wajar, misalnya antara jam 10 pagi sampai jam 8 malam. Kalau setelah itu dia tetap diam, lepaskan dengan elegan. Di artikel ini saya bahas kenapa orang menghilang setelah tanya harga, prinsip follow-up yang tidak bikin ilfeel, contoh kalimat siap pakai untuk berbagai situasi, sampai cara mencatat siapa saja yang perlu dihubungi ulang.

    Kenapa Orang Hilang Setelah Tanya Harga

    Sebelum bahas teknik, pahami dulu satu hal. Orang yang tanya harga lalu diam itu bukan berarti PHP atau tidak niat beli. Kebanyakan punya alasan yang sangat manusiawi.

    • Dia lagi bandingkan harga. Sebelum chat kita, dia mungkin sudah chat tiga sampai lima penjual lain. Setelah semua jawaban terkumpul, dia baru duduk membandingkan. Proses ini bisa makan waktu berhari-hari.
    • Belum urgent. Barangnya memang dibutuhkan, tapi tidak sekarang. Misalnya tanya harga seragam bulan ini padahal acaranya masih dua bulan lagi. Dia cuma mengumpulkan info dulu.
    • Kaget dengan harganya. Ekspektasinya 200 ribu, ternyata 500 ribu. Daripada bilang “wah mahal ya”, banyak orang memilih diam karena tidak enak hati. Diam itu cara halus mereka untuk mundur.
    • Cuma survei. Ada yang memang belum berencana beli sama sekali. Sekadar penasaran, atau sedang riset untuk keperluan nanti. Suatu saat bisa jadi pembeli, tapi bukan minggu ini.

    Coba ingat cara kita sendiri belanja. Kita juga sering tanya harga di beberapa toko lalu tidak membalas semuanya. Bukan karena jahat, tapi karena memang begitu cara orang mengambil keputusan. Jadi jangan buru-buru menghakimi calon pembeli yang diam. Anggap saja dia sedang di tengah proses mikir, dan tugas kita cuma satu: hadir lagi di waktu yang tepat dengan cara yang enak.

    Prinsip Dasar: Chat Lagi Harus Punya Alasan

    Ini inti dari semua teknik follow-up. Perbedaan antara follow-up yang disambut baik dan yang bikin ilfeel ada di satu hal: apakah chat kita membawa sesuatu yang baru, atau cuma menagih keputusan.

    “Jadi order kak?” atau “Gimana kak, jadi ambil?” itu contoh chat yang cuma menagih. Dari sisi pembeli, chat seperti ini terasa seperti ditodong. Dia belum siap memutuskan, lalu dipaksa menjawab. Pilihan dia cuma dua, menolak dengan tidak enak hati atau lanjut diam. Kebanyakan pilih diam, dan hubungan pun makin dingin.

    Bandingkan dengan chat yang membawa alasan baru. Misalnya info bahwa minggu ini ada gratis ongkir, atau kabar bahwa varian yang dia tanyakan tinggal sedikit, atau tawaran paket yang lebih sesuai budget. Chat seperti ini tetap mengingatkan dia soal produk kita, tapi terasa seperti diberi info, bukan ditagih. Dia bisa membalas tanpa merasa terpojok.

    Beberapa prinsip tambahan yang saya pegang:

    • Satu chat, satu pesan. Jangan kirim lima bubble beruntun. Cukup satu pesan yang jelas.
    • Pakai nama atau sapaan yang sama seperti obrolan sebelumnya. Biar terasa nyambung, bukan broadcast massal.
    • Sebut konteks obrolan kemarin. Contohnya “yang kemarin tanya paket katering 50 pax”. Orang lebih respect ke penjual yang ingat detail.
    • Kirim di jam wajar. Hindari subuh, larut malam, atau jam istirahat. Chat jualan jam 11 malam itu ganggu, sebagus apa pun isinya.
    • Jangan baper kalau tidak dibalas. Tidak dibalas bukan penolakan final. Kadang orang baca, niat balas nanti, lalu lupa.

    Contoh Kalimat Follow-Up Siap Pakai

    Berikut contoh kalimat untuk beberapa situasi berbeda. Silakan sesuaikan dengan gaya bahasa dan produk masing-masing. Yang penting bukan kalimatnya persis, tapi polanya: sopan, ada konteks, dan ada alasan kenapa kita chat lagi.

    1. H+1 setelah tanya harga

    Follow-up pertama sebaiknya ringan saja. Tujuannya membuka pintu obrolan lagi, bukan menutup penjualan.

    “Halo Kak Rina, saya dari Toko Melati yang kemarin Kakak tanya harga gamis seri Arunika. Kalau ada yang mau ditanyakan lagi soal bahan atau ukurannya, tanya aja ya Kak, saya bantu jelasin.”

    2. H+3, masih belum ada kabar

    Kalau follow-up pertama belum dibalas, tunggu dua sampai tiga hari. Kali ini boleh sedikit lebih spesifik, misalnya menawarkan bantuan mengambil keputusan.

    “Kak, biasanya yang bikin bingung antara paket A dan paket B itu soal kapasitasnya. Kalau Kakak ceritain kebutuhannya dipakai untuk berapa orang, nanti saya bantu hitungin mana yang paling pas. Gratis kok, tanya-tanya dulu nggak harus langsung order.”

    3. Ada promo yang relevan

    Promo adalah alasan follow-up paling natural. Tapi pastikan promonya nyata dan relevan dengan barang yang dia tanyakan.

    “Kak, kabar baik. Produk yang kemarin Kakak tanyakan lagi ada promo sampai hari Minggu, dari 250 ribu jadi 210 ribu. Kalau memang masih butuh, sekarang waktu yang pas buat ambil. Kalau belum, nggak apa-apa, saya cuma info aja biar nggak kelewat.”

    4. Stok menipis

    Info stok boleh dipakai kalau memang benar stoknya tinggal sedikit. Jangan pakai kelangkaan palsu. Sekali pembeli tahu kita bohong soal “stok terakhir” padahal minggu depan masih ada, kepercayaan hilang dan susah balik.

    “Kak, mau info aja. Warna sage yang kemarin Kakak tanyakan sisa 2 pcs, dan restock berikutnya belum tentu bulan ini. Kalau Kakak memang naksir warna itu, mungkin bisa dipertimbangkan dulu sebelum keburu habis.”

    5. Penawaran alternatif yang lebih terjangkau

    Kalau dari gelagatnya dia mundur karena harga, jangan diamkan. Tawarkan opsi lain yang lebih masuk budget. Banyak penjualan gagal bukan karena pembeli tidak mau, tapi karena tidak ada jalan tengah.

    “Kak, kalau paket kemarin dirasa belum masuk budget, kami juga ada paket hemat di 150 ribu. Isinya lebih ringkas, tapi fungsi utamanya sama. Banyak pelanggan kami mulai dari paket ini dulu. Mau saya kirimkan detailnya?”

    6. Follow-up terakhir yang elegan

    Ini yang sering disebut break up message, pesan penutup sebelum kita berhenti menghubungi. Nadanya harus lapang dada, bukan merajuk.

    “Halo Kak, ini chat terakhir dari saya soal penawaran kemarin, biar nggak ganggu ya. Kalau suatu saat butuh, chat aja kapan pun, saya siap bantu. Makasih banyak sudah sempat mampir dan tanya-tanya, Kak. Sukses selalu.”

    Menariknya, pesan penutup seperti ini justru sering dibalas. Karena tidak ada tekanan, orang jadi lebih nyaman merespons. Ada yang akhirnya jujur bilang belum ada dana, ada juga yang malah langsung order karena baru ingat.

    Kapan Harus Berhenti Follow-Up

    Batas yang sehat itu dua sampai tiga kali follow-up, sudah termasuk pesan penutup. Lebih dari itu, kita bukan lagi menawarkan, tapi mengganggu.

    Kenapa memaksa itu merugikan? Pertama, peluang dibalasnya makin kecil. Orang yang sudah tiga kali tidak membalas hampir pasti tidak akan berubah pikiran di chat keempat. Kedua, risiko diblokir naik. Sekali diblokir, kita kehilangan akses selamanya, termasuk saat dia sebenarnya butuh produk kita enam bulan lagi. Ketiga, dan ini yang paling mahal, reputasi. Pengalaman dikejar-kejar penjual itu jenis cerita yang gampang menyebar ke teman dan keluarga. Satu calon pembeli yang ilfeel bisa menutup pintu ke lima calon pembeli lainnya.

    Ingat juga, berhenti follow-up bukan berarti hubungan putus. Nomor dia tetap tersimpan. Kalau tiga bulan lagi ada produk baru atau promo besar, dia tetap bisa dihubungi sekali lagi dengan konteks yang benar-benar baru. Bedanya jauh antara “menghubungi lagi setelah lama dengan kabar baru” dan “menagih jawaban tiap dua hari”.

    Cara Mencatat Siapa yang Perlu Di-Follow-Up

    Teknik follow-up sebagus apa pun tidak jalan kalau kita lupa siapa saja yang kemarin tanya harga. Ini masalah paling umum. Chat masuk banyak, semua dijawab, lalu tenggelam. Seminggu kemudian baru sadar ada lima orang yang tanya harga dan tidak pernah di-follow-up sama sekali.

    Mulai saja dari catatan sederhana. Buku tulis atau spreadsheet dengan lima kolom sudah cukup: nama, nomor WhatsApp, produk yang ditanyakan, tanggal tanya, dan status terakhir. Setiap malam, luangkan lima menit untuk mengecek siapa yang hari ini tanya harga dan siapa yang besok jadwalnya di-follow-up. Kalau pakai WhatsApp Business, fitur label juga membantu. Beri label seperti “tanya harga”, “follow-up 1”, “follow-up 2”, dan “closing” supaya sekali buka langsung kelihatan posisi tiap orang.

    Kalau chat yang masuk masih belasan per hari, cara manual ini cukup. Tapi begitu volumenya ratusan, catatan manual mulai keteteran. Ada yang kelewat, ada yang malah di-follow-up dua kali oleh admin berbeda. Di titik itu biasanya usaha mulai beralih ke sistem CRM supaya semua riwayat chat dan jadwal follow-up tercatat rapi di satu tempat. Saya pernah tulis lengkap soal ini di artikel apa itu CRM dan kenapa bisnis kecil juga butuh. Untuk urusan menjawab pertanyaan berulang dan menyapa ulang banyak kontak sekaligus, sebagian bisnis juga terbantu dengan otomasi chat, jadi admin bisa fokus ke follow-up yang butuh sentuhan personal.

    Mulai dari Satu Chat Hari Ini

    Tidak perlu langsung sempurna. Buka WhatsApp sekarang, cari satu orang yang tanya harga minggu ini lalu menghilang, dan kirim satu follow-up yang sopan dengan alasan yang jelas. Pakai salah satu contoh di atas, sesuaikan sedikit dengan gaya bicara sendiri.

    Prinsipnya diingat saja: pahami kenapa dia diam, chat lagi dengan membawa alasan baru, maksimal dua sampai tiga kali, lalu tutup dengan elegan kalau memang belum jodoh. Pembeli yang hari ini diam bisa jadi pelanggan bulan depan, asal ingatannya tentang kita adalah penjual yang enak diajak ngobrol, bukan yang mengejar-ngejar.

  • Belajar Golang dari Nol #4: Struct dan Method, Cara Go Menyusun Data

    Belajar Golang dari Nol #4: Struct dan Method, Cara Go Menyusun Data

    Di Belajar Golang dari Nol #3 kita sudah belajar membungkus logika ke dalam function, mengembalikan lebih dari satu nilai, dan menangani error pertama kita. Sekarang saatnya naik satu tingkat. Kalau function adalah cara Go menyusun logika, maka struct adalah cara Go menyusun data. Di bagian ini kita akan kenalan dengan struct, method, dan pola constructor yang dipakai hampir di semua project Go sungguhan.

    Kenapa Kita Butuh Struct

    Bayangkan kamu bikin program kasir sederhana. Satu produk punya nama, harga, dan stok. Kalau cuma pakai variabel biasa, kodenya jadi seperti ini.

    var namaProduk string = "Kopi Arabika"
    var hargaProduk int = 25000
    var stokProduk int = 10

    Untuk satu produk masih aman. Tapi begitu ada produk kedua, kamu harus bikin namaProduk2, hargaProduk2, stokProduk2. Produk ketiga, tambah lagi. Datanya tercerai berai, padahal ketiganya jelas satu paket. Nama, harga, dan stok itu milik produk yang sama.

    Struct menyelesaikan masalah ini. Anggap saja struct itu seperti formulir. Formulir pendaftaran punya kolom nama, alamat, dan nomor telepon. Kolomnya sudah ditentukan dari awal. Setiap orang yang mendaftar mengisi formulir yang sama, tapi isinya beda. Struct persis begitu. Kamu mendefinisikan kolomnya sekali, lalu bisa membuat banyak isian dari cetakan yang sama.

    Deklarasi Struct dan Cara Membuat Instance

    Mendefinisikan struct di Go pakai kata kunci type dan struct. Kolomnya disebut field. Setiap field punya nama dan tipe data.

    package main
    
    import "fmt"
    
    type Produk struct {
    	Nama  string
    	Harga int
    	Stok  int
    }
    
    func main() {
    	kopi := Produk{
    		Nama:  "Kopi Arabika",
    		Harga: 25000,
    		Stok:  10,
    	}
    
    	fmt.Println(kopi.Nama)
    	fmt.Println(kopi.Harga)
    
    	kopi.Stok = 15
    	fmt.Println(kopi.Stok)
    }

    Beberapa hal yang perlu kamu perhatikan dari kode di atas.

    • type Produk struct hanya mendefinisikan cetakannya. Belum ada data apa pun di sini.
    • kopi := Produk{...} membuat isian nyata dari cetakan itu. Ini yang disebut instance.
    • Saat mengisi, kita menulis nama field-nya secara eksplisit, seperti Nama: "Kopi Arabika". Ini cara yang paling aman dan paling mudah dibaca. Biasakan pakai gaya ini.
    • Membaca dan mengubah field pakai tanda titik. kopi.Nama untuk membaca, kopi.Stok = 15 untuk mengubah.

    Satu catatan yang nyambung ke materi bagian 2 soal zero value. Kalau kamu menulis var kosong Produk tanpa mengisi apa pun, setiap field otomatis berisi zero value tipenya, jadi Nama berisi string kosong dan Harga serta Stok berisi nol.

    Struct di Dalam Struct

    Field sebuah struct tidak harus tipe dasar. Dia boleh berupa struct lain. Ini berguna waktu datamu memang bertingkat. Contohnya data pelanggan yang punya alamat.

    type Alamat struct {
    	Jalan string
    	Kota  string
    }
    
    type Pelanggan struct {
    	Nama   string
    	Alamat Alamat
    }
    
    func main() {
    	budi := Pelanggan{
    		Nama: "Budi",
    		Alamat: Alamat{
    			Jalan: "Jl. Merdeka No. 10",
    			Kota:  "Purwokerto",
    		},
    	}
    
    	fmt.Println(budi.Alamat.Kota)
    }

    Cara aksesnya tinggal dirantai pakai titik. budi.Alamat.Kota artinya ambil field Alamat milik budi, lalu ambil field Kota di dalamnya. Sederhana, tapi pola ini yang nanti kamu pakai untuk memodelkan data dunia nyata seperti pesanan yang punya pelanggan, pelanggan yang punya alamat.

    Method: Function yang Menempel di Struct

    Sejauh ini struct kita cuma tempat menyimpan data. Padahal data biasanya punya perilaku. Produk bisa dihitung harga diskonnya. Di bagian 3 kamu sudah bisa menulis function seperti ini.

    func HitungDiskon(p Produk, persen int) int {
    	potongan := p.Harga * persen / 100
    	return p.Harga - potongan
    }

    Kode itu jalan. Tapi Go punya cara yang lebih rapi, namanya method. Method adalah function biasa yang diberi satu tambahan di depan namanya, disebut receiver. Receiver menentukan struct mana yang memiliki method itu.

    func (p Produk) HitungDiskon(persen int) int {
    	potongan := p.Harga * persen / 100
    	return p.Harga - potongan
    }

    Perhatikan bagian (p Produk) sebelum nama function. Itulah receiver. Artinya, method HitungDiskon milik struct Produk, dan di dalam method kita bisa mengakses datanya lewat p.

    Cara memanggilnya juga berubah. Dari HitungDiskon(kopi, 10) menjadi kopi.HitungDiskon(10). Bedanya terasa waktu kode mulai besar. Dengan method, kamu bertanya ke datanya langsung. Ketik kopi. di editor, dan semua kemampuan produk muncul sebagai saran. Data dan perilakunya jadi satu paket, tidak berserakan sebagai function lepas.

    Pointer Receiver vs Value Receiver

    Sekarang bagian yang paling sering bikin pemula bingung. Tenang, kita bahas pelan pakai contoh. Coba tebak output kode ini.

    func (p Produk) TambahStokSalah(jumlah int) {
    	p.Stok = p.Stok + jumlah
    }
    
    func (p *Produk) TambahStokBenar(jumlah int) {
    	p.Stok = p.Stok + jumlah
    }
    
    func main() {
    	kopi := Produk{Nama: "Kopi Arabika", Harga: 25000, Stok: 10}
    
    	kopi.TambahStokSalah(5)
    	fmt.Println(kopi.Stok)
    
    	kopi.TambahStokBenar(5)
    	fmt.Println(kopi.Stok)
    }

    Outputnya:

    10
    15

    Panggilan pertama tidak mengubah apa pun. Kenapa bisa begitu. Receiver (p Produk) disebut value receiver. Saat method dipanggil, Go membuat salinan dari kopi, lalu method bekerja pada salinan itu. Stok salinannya memang bertambah, tapi begitu method selesai, salinan itu dibuang. kopi yang asli tidak tersentuh.

    Receiver (p *Produk) dengan tanda bintang disebut pointer receiver. Method tidak menerima salinan, tapi semacam alamat yang menunjuk ke kopi yang asli. Jadi perubahan stoknya benar-benar nempel.

    Kamu belum perlu paham teori pointer secara mendalam sekarang. Pointer akan kita bahas khusus di bagian mendatang. Untuk saat ini, pegang satu aturan praktis ini saja.

    • Method mau mengubah data di struct: pakai pointer receiver, (p *Produk).
    • Method cuma membaca data, seperti menghitung atau menampilkan: value receiver, (p Produk), sudah cukup.

    Kalau masih ragu, banyak programmer Go memilih pointer receiver untuk semua method di satu struct supaya konsisten. Itu juga pilihan yang wajar.

    Constructor Pattern ala Go

    Ada satu masalah kecil yang belum kita selesaikan. Tidak ada yang melarang orang membuat produk dengan data ngawur, misalnya Produk{Nama: "", Harga: -5000}. Go tidak punya constructor bawaan seperti bahasa lain. Sebagai gantinya, komunitas Go punya kebiasaan: buat function biasa dengan awalan New yang tugasnya membuat struct sekaligus memvalidasi isinya.

    Di sinilah materi error dari bagian 3 kepakai lagi. Constructor mengembalikan dua nilai, struct dan error.

    func NewProduk(nama string, harga int, stok int) (Produk, error) {
    	if nama == "" {
    		return Produk{}, errors.New("nama produk tidak boleh kosong")
    	}
    	if harga <= 0 {
    		return Produk{}, errors.New("harga harus lebih dari nol")
    	}
    	return Produk{Nama: nama, Harga: harga, Stok: stok}, nil
    }

    Kalau data valid, kita kembalikan struct yang sudah terisi dan error nil. Kalau tidak valid, kita kembalikan struct kosong plus error yang menjelaskan masalahnya. Pemanggilnya wajib mengecek error dulu sebelum memakai struct-nya, persis pola if err != nil yang sudah kamu latih di bagian 3. Dengan pola ini, semua produk yang lolos dari NewProduk dijamin datanya masuk akal.

    Latihan: Sistem Stok Mini

    Sekarang kita gabungkan semuanya. Struct, method dengan pointer receiver, constructor dengan validasi, dan error. Kita bikin sistem stok mini untuk satu produk. Ketik ulang program ini, jangan copas, supaya tanganmu ikut hafal.

    package main
    
    import (
    	"errors"
    	"fmt"
    )
    
    type Produk struct {
    	Nama  string
    	Harga int
    	Stok  int
    }
    
    func NewProduk(nama string, harga int, stok int) (Produk, error) {
    	if nama == "" {
    		return Produk{}, errors.New("nama produk tidak boleh kosong")
    	}
    	if harga <= 0 {
    		return Produk{}, errors.New("harga harus lebih dari nol")
    	}
    	if stok < 0 {
    		return Produk{}, errors.New("stok awal tidak boleh negatif")
    	}
    	return Produk{Nama: nama, Harga: harga, Stok: stok}, nil
    }
    
    func (p *Produk) TambahStok(jumlah int) error {
    	if jumlah <= 0 {
    		return errors.New("jumlah tambahan harus lebih dari nol")
    	}
    	p.Stok = p.Stok + jumlah
    	return nil
    }
    
    func (p *Produk) KurangiStok(jumlah int) error {
    	if jumlah <= 0 {
    		return errors.New("jumlah pengurangan harus lebih dari nol")
    	}
    	if jumlah > p.Stok {
    		return fmt.Errorf("stok tidak cukup: sisa %d, diminta %d", p.Stok, jumlah)
    	}
    	p.Stok = p.Stok - jumlah
    	return nil
    }
    
    func (p Produk) Info() string {
    	return fmt.Sprintf("%s | harga %d | stok %d", p.Nama, p.Harga, p.Stok)
    }
    
    func main() {
    	kopi, err := NewProduk("Kopi Arabika", 25000, 10)
    	if err != nil {
    		fmt.Println("gagal membuat produk:", err)
    		return
    	}
    	fmt.Println(kopi.Info())
    
    	err = kopi.TambahStok(5)
    	if err != nil {
    		fmt.Println("gagal tambah stok:", err)
    	}
    	fmt.Println(kopi.Info())
    
    	err = kopi.KurangiStok(8)
    	if err != nil {
    		fmt.Println("gagal kurangi stok:", err)
    	}
    	fmt.Println(kopi.Info())
    
    	err = kopi.KurangiStok(100)
    	if err != nil {
    		fmt.Println("gagal kurangi stok:", err)
    	}
    	fmt.Println(kopi.Info())
    
    	_, err = NewProduk("", 25000, 10)
    	if err != nil {
    		fmt.Println("gagal membuat produk:", err)
    	}
    }

    Jalankan dengan go run main.go. Outputnya seperti ini.

    Kopi Arabika | harga 25000 | stok 10
    Kopi Arabika | harga 25000 | stok 15
    Kopi Arabika | harga 25000 | stok 7
    gagal kurangi stok: stok tidak cukup: sisa 7, diminta 100
    Kopi Arabika | harga 25000 | stok 7
    gagal membuat produk: nama produk tidak boleh kosong

    Baca outputnya baris per baris dan cocokkan dengan kodenya.

    • Stok awal 10, tambah 5 jadi 15, kurangi 8 jadi 7. Method TambahStok dan KurangiStok pakai pointer receiver, jadi perubahannya nempel.
    • Waktu kita minta 100 padahal sisa 7, method menolak dan mengembalikan error. Perhatikan baris setelahnya: stok tetap 7, tidak berubah jadi minus.
    • Percobaan membuat produk tanpa nama juga ditolak oleh NewProduk. Data yang tidak valid tidak pernah sempat jadi struct yang dipakai program.

    Sebagai latihan tambahan, coba tambahkan method HitungDiskon dari bab sebelumnya ke program ini. Lalu coba tambahkan field Kategori ke struct Produk dan lihat bagian mana saja yang perlu kamu sesuaikan.

    Rangkuman dan Bagian Selanjutnya

    Hari ini kamu sudah pegang fondasi penting Go.

    • Struct mengelompokkan data yang berhubungan ke satu tempat, seperti formulir dengan kolom yang sudah ditentukan.
    • Field diakses dan diubah pakai tanda titik, dan struct bisa berisi struct lain.
    • Method adalah function dengan receiver, sehingga data dan perilakunya jadi satu paket.
    • Mau mengubah data, pakai pointer receiver. Cuma membaca, value receiver cukup.
    • Constructor NewProduk memastikan struct selalu lahir dengan data yang valid.

    Pola yang barusan kamu tulis ini bukan sekadar latihan. Struct dengan method dan validasi seperti ini juga fondasi yang kami pakai saat membangun sistem aplikasi berbasis Go untuk klien.

    Di bagian 5 kita akan bahas Slice dan Map: Kumpulan Data di Go. Di sana sistem stok mini ini akan naik kelas, dari satu produk menjadi daftar banyak produk. Sampai ketemu di bagian berikutnya.

  • Apa Itu Docker dan Cara Install Docker di Ubuntu & Windows

    Apa Itu Docker dan Cara Install Docker di Ubuntu & Windows

    Docker adalah platform untuk mengemas aplikasi beserta semua dependensinya ke dalam satu paket yang disebut container. Container ini bisa jalan di mana saja dengan perilaku yang sama persis: di laptop kamu, di laptop teman satu tim, sampai di server produksi. Artikel ini adalah bagian pertama dari seri Belajar Docker dari Nol. Kita mulai dari dua hal paling dasar: paham kenapa Docker ada, lalu praktik cara install Docker di Ubuntu dan Windows sampai benar-benar bisa dipakai.

    Ringkasnya begini. Di Ubuntu, Docker Engine versi 27.x diinstall lewat repositori resmi apt dari download.docker.com. Di Windows, kamu install Docker Desktop yang berjalan di atas WSL2. Setelah terpasang, verifikasi dengan docker run hello-world. Detail tiap langkah kita bahas di bawah, termasuk error yang paling sering bikin pemula nyangkut.

    Masalah Klasik “Di Laptop Saya Jalan Kok”

    Hampir semua developer pernah mengalami ini. Aplikasi jalan mulus di laptop, begitu dideploy ke server langsung error. Penyebabnya macam-macam. Versi PHP di server beda dengan di laptop. Ekstensi yang dibutuhkan belum terpasang. Versi Node atau Go tidak sama. Library sistem yang di laptop ada, di server tidak ada.

    Cara deploy manual di VPS memperbesar peluang masalah ini. Alurnya biasanya begini: sewa VPS, SSH masuk, install runtime satu per satu, install database, atur konfigurasi, baru upload kode. Setiap langkah dikerjakan tangan, dan setiap server bisa berakhir dengan kondisi yang sedikit berbeda. Server seperti ini sering disebut snowflake server, tidak ada dua yang benar-benar identik. Saat aplikasi harus pindah server atau tim bertambah orang, proses setup harus diulang dari nol dan hasilnya belum tentu sama.

    Container lahir untuk memotong masalah itu. Aplikasi, runtime, library, dan konfigurasi dikemas jadi satu unit bernama image. Dari image itu kamu menjalankan container, dan container tersebut membawa lingkungannya sendiri ke mana pun dia jalan. Kalau container jalan di laptop, dia akan jalan dengan cara yang sama di server. Beda dengan virtual machine, container tidak membawa sistem operasi utuh. Container berbagi kernel dengan host, jadi ukurannya jauh lebih kecil dan startup-nya hitungan detik, bukan menit.

    Di tim Arrazy, hampir semua proyek klien berjalan di atas Docker, dari backend Go, aplikasi Laravel, sampai database di server klien. Alasannya sederhana: environment developer dan server produksi jadi seragam, dan onboarding anggota tim baru cukup dengan satu perintah, bukan setengah hari install dependensi. Pola yang sama juga kami pakai saat membangun sistem aplikasi untuk klien yang servernya kami kelola.

    Prasyarat: Nyaman di Terminal Dulu

    Seri ini banyak bermain di terminal. Kamu tidak perlu jago, tapi minimal paham perintah dasar seperti cd, ls, sudo, dan cara SSH ke server. Kalau bagian itu masih asing, selesaikan dulu bagian dasar shell dan SSH di seri Belajar Linux dari Nol, baru lanjut ke sini. Percaya deh, belajar Docker tanpa dasar terminal itu seperti belajar nyetir sambil belajar baca rambu.

    Kebutuhan sistemnya:

    • Ubuntu: versi 22.04 (Jammy) atau 24.04 (Noble), arsitektur 64-bit. VPS atau laptop sama saja.
    • Windows: Windows 10 64-bit versi 22H2 ke atas atau Windows 11, dengan fitur virtualisasi aktif di BIOS. RAM 8 GB sangat disarankan.

    Cara Install Docker di Ubuntu Lewat Repo Resmi apt

    Ubuntu punya paket docker.io di repo bawaan, tapi versinya sering tertinggal. Kita pakai repo resmi Docker supaya dapat Docker Engine 27.x yang terbaru beserta plugin Compose dan Buildx.

    1. Bersihkan Paket Lama dan Siapkan Dependensi

    Kalau sebelumnya pernah coba-coba install Docker dari repo bawaan, hapus dulu supaya tidak bentrok:

    for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do sudo apt-get remove $pkg; done

    Kalau paketnya memang tidak ada, apt hanya bilang paket tidak terpasang. Aman, lanjut saja. Lalu update index dan install alat bantu:

    sudo apt-get update
    sudo apt-get install ca-certificates curl

    2. Tambahkan GPG Key dan Repositori Docker

    GPG key dipakai apt untuk memastikan paket yang diunduh benar-benar dari Docker, bukan dari pihak lain:

    sudo install -m 0755 -d /etc/apt/keyrings
    sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
    sudo chmod a+r /etc/apt/keyrings/docker.asc

    Lalu daftarkan repositorinya:

    echo \
      "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
      $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
      sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    sudo apt-get update

    Perintah di atas otomatis mendeteksi arsitektur dan kode nama Ubuntu kamu, jadi bisa dicopy apa adanya.

    3. Install Docker Engine dan Plugin-nya

    sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

    Lima paket itu isinya: daemon Docker (docker-ce), perintah docker di terminal (docker-ce-cli), runtime container (containerd.io), plus plugin build dan Compose yang akan kita pakai di bagian-bagian berikutnya seri ini. Setelah selesai, service Docker otomatis jalan. Cek versinya:

    docker --version

    Output yang diharapkan kurang lebih seperti ini:

    Docker version 27.5.1, build 9f9e405

    Angka minor dan build bisa beda tergantung rilis terbaru saat kamu install. Yang penting versi mayornya 27 ke atas.

    Cara Install Docker di Windows: Docker Desktop dan WSL2

    Docker butuh kernel Linux. Di Windows, kebutuhan itu dipenuhi lewat WSL2 (Windows Subsystem for Linux versi 2), lalu Docker Desktop jalan di atasnya. Urutannya: aktifkan WSL2 dulu, baru install Docker Desktop.

    1. Aktifkan WSL2

    Buka PowerShell sebagai Administrator (klik kanan, Run as administrator), lalu jalankan:

    wsl --install

    Perintah ini mengaktifkan fitur WSL, mengunduh kernel Linux, menjadikan WSL2 sebagai default, dan menginstall distro Ubuntu. Setelah selesai, restart komputer. Kalau WSL sudah pernah terpasang, pastikan versinya 2 dengan wsl --status dan update dengan wsl --update.

    2. Install Docker Desktop

    1. Unduh installer dari https://docs.docker.com/desktop/setup/install/windows-install/, pilih yang x86_64.
    2. Jalankan Docker Desktop Installer.exe. Saat muncul pilihan backend, pastikan opsi Use WSL 2 instead of Hyper-V tercentang.
    3. Selesaikan instalasi, logout atau restart bila diminta, lalu buka aplikasi Docker Desktop.

    Tunggu sampai indikator di pojok kiri bawah Docker Desktop berwarna hijau dengan status Engine running. Setelah itu semua perintah docker bisa kamu jalankan dari PowerShell maupun dari terminal Ubuntu di WSL. Sepanjang seri ini perintahnya sama persis untuk Ubuntu asli maupun WSL, jadi pengguna Windows tidak perlu artikel terpisah.

    Verifikasi Instalasi: docker version, docker info, hello-world

    Ada tiga pengecekan standar setelah install. Pertama, docker version (tanpa tanda strip). Bedanya dengan docker --version, perintah ini menampilkan versi client sekaligus server. Kalau bagian Server muncul, artinya daemon Docker hidup dan bisa diajak bicara:

    docker version
    Client: Docker Engine - Community
     Version:           27.5.1
     ...
    Server: Docker Engine - Community
     Engine:
      Version:          27.5.1
     ...

    Kedua, docker info untuk melihat kondisi keseluruhan: jumlah container, jumlah image, storage driver, dan lainnya. Untuk instalasi baru, nilai Containers dan Images masih 0.

    Ketiga, tes paling memuaskan: jalankan container pertamamu.

    docker run hello-world

    Output yang diharapkan:

    Unable to find image 'hello-world:latest' locally
    latest: Pulling from library/hello-world
    e6590344b1a5: Pull complete
    Digest: sha256:...
    Status: Downloaded newer image for hello-world:latest
    
    Hello from Docker!
    This message shows that your installation appears to be working correctly.

    Perhatikan alurnya, karena ini inti kerja Docker: image hello-world tidak ada di lokal, jadi Docker mengunduhnya dari Docker Hub, membuat container dari image itu, menjalankannya, lalu container mencetak pesan dan berhenti. Konsep image dan container ini kita bedah tuntas di bagian 2.

    Jalankan Docker Tanpa sudo: usermod -aG docker

    Di Ubuntu, daemon Docker jalan sebagai root dan socket-nya hanya bisa diakses root serta anggota grup docker. Karena itu, secara default setiap perintah harus diawali sudo. Capek kalau tiap perintah begitu. Solusinya, masukkan user kamu ke grup docker:

    sudo usermod -aG docker $USER

    Keanggotaan grup baru terbaca setelah kamu logout dan login lagi. Kalau tidak mau logout, jalankan newgrp docker untuk membuka shell baru dengan grup yang sudah aktif. Tes tanpa sudo:

    docker run hello-world

    Kalau pesan Hello from Docker muncul tanpa error, beres. Satu catatan penting: anggota grup docker pada praktiknya setara root di mesin itu, karena bisa me-mount filesystem host lewat container. Di laptop pribadi ini bukan masalah. Di server produksi yang dipakai banyak orang, pikirkan dulu siapa saja yang dimasukkan ke grup ini. Pengguna Docker Desktop di Windows tidak perlu langkah ini sama sekali.

    Troubleshooting: Error yang Sering Muncul Saat Install Docker

    permission denied while trying to connect to /var/run/docker.sock

    Pesan lengkapnya kurang lebih: permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock. Penyebabnya user kamu belum masuk grup docker, atau sudah dimasukkan tapi belum logout-login. Solusi: jalankan sudo usermod -aG docker $USER, lalu logout dan login lagi. Cek dengan perintah groups, pastikan kata docker muncul di daftarnya.

    WSL2 Belum Aktif: “Docker Desktop requires a newer WSL kernel”

    Docker Desktop menolak jalan dan menyuruh update WSL. Ini terjadi karena WSL belum terpasang, masih versi 1, atau kernelnya usang. Buka PowerShell sebagai Administrator lalu jalankan wsl --update dan wsl --set-default-version 2, kemudian restart Docker Desktop. Kalau masih gagal, cek apakah virtualisasi aktif: buka Task Manager, tab Performance, lihat baris Virtualization. Kalau statusnya Disabled, aktifkan Intel VT-x atau AMD-V dari BIOS/UEFI.

    Unable to locate package docker-ce

    Muncul saat apt-get install docker-ce di Ubuntu. Artinya repositori Docker belum benar-benar terdaftar. Biasanya karena lupa menjalankan sudo apt-get update setelah menambah repo, atau file /etc/apt/sources.list.d/docker.list isinya salah karena perintah echo tadi tidak tercopy utuh. Cek isi file itu dengan cat, pastikan ada satu baris berisi kode nama Ubuntu kamu (misalnya noble), lalu ulangi sudo apt-get update.

    Cannot connect to the Docker daemon. Is the docker daemon running?

    Perintah docker jalan tapi daemon-nya mati. Di Ubuntu, nyalakan dengan sudo systemctl start docker dan supaya otomatis hidup saat boot jalankan sudo systemctl enable docker. Di Windows, error ini biasanya berarti aplikasi Docker Desktop belum dibuka. Buka dulu, tunggu status Engine running, baru jalankan perintah dari terminal.

    Sudah Terinstall, Selanjutnya Apa

    Sampai sini kamu sudah paham masalah yang diselesaikan container, punya Docker Engine 27.x yang jalan di Ubuntu atau Docker Desktop di Windows, dan sudah menjalankan container pertama tanpa sudo. Fondasi selesai.

    Di bagian 2, “Perbedaan Docker Image dan Container + Praktik Pertama”, kita bongkar konsep paling penting di Docker: apa itu image, apa itu container, dan kenapa keduanya sering tertukar di kepala pemula. Artikelnya terbit menyusul, pantau daftarnya di halaman hub Belajar Docker dari Nol.

    Referensi