type Won int 처럼 재정의 할 수 있음. 다른 타입과 대입이나 연산이 금지(int와 Won 타입을 가진 변수끼리 연산X), 메서드 연결이 가능함. type Person struct { Name String }처럼 정의할 수 있음, 메서드 연결 가능.type Person struct { Name string }
// 일반 함수: Person 구조체를 매개변수로 받음
func PrintNameFunc(p Person) {
fmt.Println(p.Name)
}
// 호출 방식
PrintNameFunc(p)
package main
import "fmt"
type Person struct {
Name string
}
func (p Person) PrintNameMethod() {
fmt.Println(p.Name)
}
// 값 리시버: 원본 변경 불가
func (p Person) ChangeNameValue(newName string) {
p.Name = newName
}
// 포인터 리시버: 원본 변경 가능
func (p *Person) ChangeNamePointer(newName string) {
p.Name = newName
}
func main() {
p := Person{Name: "kdb"}
// 1. 값 리시버 호출 (복사본을 수정하므로 원본 변화 없음)
p.ChangeNameValue("kdv")
p.PrintNameMethod() // 출력: kdb
// 2. 포인터 리시버 호출 (Go 컴파일러가 &p로 자동 해석하여 원본 수정)
p.ChangeNamePointer("kdv")
p.PrintNameMethod() // 출력: kdv
}
type Speaker interface {
Speak() string
}
특정 타입이 인터페이스에 정의된 메서드를 모두 구현하면 명시적 선언 없이 암묵적으로 인터페이스를 구현한 것으로 인정함.(이를 Duck Typing이라 함)
아래 코드는 구조체와 커스텀 타입이 모두 인터페이스의 메서드를 구현해서 암묵적으로 인터페이스를 만족하므로 MakeSounds(s Speaker) 함수의 매개변수인 인터페이스 자리에 구조체와 커스텀 타입을 넣을 수 있음.
package main
import "fmt"
// 1. 인터페이스 정의 (자격 요건)
// "Speak() 메서드를 가진 모든 타입은 Speaker로 인정한다."
type Speaker interface {
Speak() string
}
// 2. 일반 구조체 생성 및 메서드 구현
type Dog struct{}
func (d Dog) Speak() string {
return "멍멍!"
}
// 3. 커스텀 타입 생성 및 메서드 구현 (기본형 int 기반)
type hotdog int
func (h hotdog) Speak() string {
return "나는 뜨거운 개다!"
}
// 4. 인터페이스를 매개변수로 받는 함수
func MakeSound(s Speaker) {
fmt.Println(s.Speak())
}
func main() {
myDog := Dog{}
myFood := hotdog(1)
// Dog와 hotdog는 형태가 완전히 다르지만,
// 둘 다 Speak()를 할 줄 알기 때문에 Speaker로 취급됩니다.
MakeSound(myDog)
MakeSound(myFood)
}
변수 자체를 인터페이스로 정의하고 값을 할당하면 새로운 기술이 도입될 때 기존 코드를 많이 바꾸지 않고 변수만 다른 것으로 바꿀 수 있음.
아래 코드는 db를 인터페이스로 정의하여 MySql 구조체 사용에서 PostgreSQL 구조체 사용으로 바뀌어도 다른 메소드 사용 코드를 수정하지 않고 db 변수 수정만 하면 됨.
package main
import "fmt"
// 1. 인터페이스 정의 (자격 요건 상자)
// "Save()와 Connect() 메서드를 가진 타입은 모두 Database로 취급한다"
type Database interface {
Connect()
Save(data string)
}
// ---------------------------------------------------
// 2-A. 첫 번째 구체적인 타입: MySQL
type MySQL struct {
host string
}
func (m MySQL) Connect() {
fmt.Printf("[MySQL] %s 서버에 성공적으로 연결되었습니다.\n", m.host)
}
func (m MySQL) Save(data string) {
fmt.Printf("[MySQL] 데이터 저장 완료: '%s'\n", data)
}
// ---------------------------------------------------
// 2-B. 두 번째 구체적인 타입: PostgreSQL (나중에 새로 추가됨)
type PostgreSQL struct {
host string
port int
}
func (p PostgreSQL) Connect() {
fmt.Printf("[PostgreSQL] %s:%d 서버에 성공적으로 연결되었습니다.\n", p.host, p.port)
}
func (p PostgreSQL) Save(data string) {
fmt.Printf("[PostgreSQL] 데이터 저장 완료: '%s'\n", data)
}
// ---------------------------------------------------
func main() {
// 3. 변수를 인터페이스 타입으로 선언! (다용도 상자 준비)
// 아직 이 안에는 아무것도 없습니다 (nil).
var db Database
fmt.Println("--- 서비스 초기 (MySQL 사용) ---")
// 인터페이스 변수에 MySQL 구조체를 할당
db = MySQL{host: "192.168.1.100"}
// 여기서부터는 db가 MySQL인지 신경 쓰지 않고 인터페이스의 메서드만 호출합니다.
db.Connect()
db.Save("user_001")
fmt.Println("\n--- 6개월 뒤 (PostgreSQL로 교체) ---")
// db 변수가 '인터페이스'이기 때문에, 코드를 엎을 필요 없이 알맹이만 스윽 갈아 끼웁니다.
db = PostgreSQL{host: "10.0.0.5", port: 5432}
// 아래 코드는 위와 동일하지만, 이제는 PostgreSQL의 메서드가 실행됩니다!
db.Connect()
db.Save("user_001")
}