삼각형의 넓이를 계산하는 프로그램을 만든다고 생각해 봅시다(이 기능만을 위한 프로그램을 만드는 것은 비효율적이지만). 지금까지 배운 '함수'와 '구조체'를 활용해보겠습니다.
은 삼각형의 너비와 높이 값을 필드로 갖는 구조체입니다. 그리고 triangle
triArea
는 삼각형 높이와 너비의 정보가 있는 구조체 객체를 매개변수로 전달 받아 삼각형의 넓이를 계산하고 반환합니다. 그냥 생각하면 굉장히 잘 정리된 코드같지만 객체 지향에 익숙한 개발자들은 무언가 어색할 것입니다. 바로 함수의 역할 때문입니다.
구조체는 함수와 마찬가지로 만드는 데 기준과 목적이 있습니다. 따라서 구조체도 변수가 많다는 이유로 단순히 값들을 묶지 않습니다. 삼각형의 넓이를 구하기 위해 관련된 변수인 너비와 높이를 필드로 설정하고, 사람의 정보를 저장하기 위해 이름, 나이를 필드로 설정하는 것처럼 특정 속성들의 기능을 수행하기 위해 만들어진 특별한 함수를 '메소드'라고 합니다. Java에서는 이들을 한 곳에 묶은 클래스 안에 필드와 메소드가 있습니다. 하지만 구조체 강의에서도 말했다시피 Go언어에서는 구조체 내에서 메소드를 선언하지 않고 일반 함수처럼 별도로 분리되어 선언됩니다.
쉽게 말하자면 메소드는 구조체의 필드들을 이용해 특정 기능을 하는 특별한 함수입니다. 특별한 함수인 만큼 선언 방법도 특이합니다.
- 기본적으로 메소드는 "func (매개변수이름 구조체이름) 메소드이름() 반환형 {" 형식으로 선언합니다. 매개변수 이름은 구조체 변수명으로서 메소드 내에서 매개변수처럼 사용됩니다.
- '함수이름'을 입력하지 않고 구조체이름 뒤에 메소드 이름을 입력합니다. 본문에서 메소드를 이용하기 위해 이름을 사용합니다.
위 코드를 메소드를 이용해서 간단하게 만들어보겠습니다. 바로 실행해보세요.
위 코드에서 (s triangle)
은 어떤 구조체를 전달 받는지 명시하는 'receiver'입니다. 구조체 객체 자체를 전달받는 것이 아니라 구조체 객체 정보를 전달 받고 메소드의 기능을 수행하는 것입니다. 함수를 사용해서 매개변수로서 객체를 활용하는 모습과는 조금 다릅니다. 이는 triarea := tri1.triArea()
를 보면 알 수 있습니다.
물론 메소드에서도 값을 복사해서 받는 것이 아닌 포인터 receiver도 있습니다.
Value Receiver와 Pointer Receiver
위 코드는 구조체의 '값' 정보를 전달(복사) 받아 연산한 후 반환합니다. 하지만 포인터 정보를 전달한다면 구조체 필드 값을 메소드에서 직접 접근해 수정할 수 있습니다. 메소드를 호출할 때는 다른 점이 없지만 메소드의 receiver 부분에서 주솟값을 참조하는 연산자인 '*'를 구조체 이름 앞에 붙여주면 됩니다.
이를 비교하는 아래 코드를 바로 실행하세요.
Value receiver를 이용한 메소드는 전달받은 메소드 객체의 필드 값을 변경해도 메소드를 빠져나가면 값이 소멸되어 바뀌지 않습니다. 하지만 Pointer receiver는 구조체 객체의 포인터를 전달받아 연산했기 때문에 객체의 실제 필드 값이 바뀝니다.