[golang] A tour of Go exercise
Stringer
type Stringer interface {
String() string
}
특정 type의 기본 문자열 출력을 String() 메소드를 통해 구현 가능한 인터페이스
주어진 예시 코드를 보면
package main
import "fmt"
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}
func main() {
a := Person{"Arthur Dent", 42}
z := Person{"Zaphod Beeblebrox", 9001}
fmt.Println(a, z)
}
String() 메소드는 리시버로 구조체 Person을 받고 있다. (오로지 구조체 Person에 속한 인스턴스만 호출 가능한 메소드라는 의미)
fmt.Print 식으로 문자열로 출력할 때, "%v (%v years)" 이런 식으로 출력하겠다는 의미
Stringer는 fmt 패키지에 의해 정의된 인터페이스로, fmt 패키지를 비롯한 여러 다양한 패키지에서 값을 출력(print)하기 위해 사용한다.
자기 자신을 문자열로 설명하는 타입으로 그냥 기본 출력을 문자열로 나타내겠다는 의미
// TODO: Add a "String() string" method to IPAddr.
위의 "a.b.c.d" 형식으로 출력해주는 String() 메소드를 구현하라는 의미
아래는 정답
package main
import "fmt"
type IPAddr [4]byte
func (addr IPAddr) String() string {
return fmt.Sprintf("%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]) // 출력이 아니라 리턴을 해야하므로 Sprintf 사용
}
func main() {
hosts := map[string]IPAddr{
"loopback": {127, 0, 0, 1},
"googleDNS": {8, 8, 8, 8},
}
for name, ip := range hosts {
fmt.Printf("%v: %v\n", name, ip)
}
}
String() 메소드를 통해 문자열 출력 형식을 변경할 수 있음을 보여주는 예제
errors
Stringer와 비슷하게 내장 인터페이스로, go에서 오류 상태를 표현하는 인터페이스이다.
type error interface {
Error() string
}
fmt 패키지 또한 값을 출력할때 Stringer 인터페이스와 마찬가지로 error 인터페이스를 함께 동반한다.
i, err := strconv.Atoi("42")
if err != nil {
fmt.Printf("couldn't convert number: %v\n", err)
return
}
fmt.Println("Converted integer:", i)
// 오류 없이 잘 되면 err는 nil 값을 갖고, 오류가 발생할 경우 err는 nil이 아니다
// 이를 if 분기문을 통해 에러처리함
문제로 ㄱㄱ
쉽게 말해 float64 형식의 값을 반환하는 타입을 만들고, 그 타입에 error 메소드도 만들어 인스턴스 생성 시 오류가 발생하면 만든 error 메소드를 이용해서 미리 만든 형식의 에러를 뱉어내는 구조의 코드를 짜라는 의미
type 선언 -> 해당 type을 리비서로 받는 메소드 작성(Error()) -> Sqrt 함수에서 분기문으로 에러 처리(음수이면 ErrNegative(x)를 가져와서 오류 출력)
아래는 정답
package main
import (
"fmt"
)
func Sqrt(x float64) (float64, error) {
if x < 0 {
return 0, ErrNegativeSqrt(x)
}
return 0, nil
}
// go에서 순서는 중요치 x
type ErrNegativeSqrt float64 // 새로운 type 생성
func (e ErrNegativeSqrt) Error() string {
return fmt.Sprintf("cannot Sqrt negative number: %f", float64(e))
}
func main() {
fmt.Println(Sqrt(2))
fmt.Println(Sqrt(-2))
}
return fmt.Sprintf("cannot Sqrt negative number: %f", float64(e))에서 %f말고 %g로 바꿀 경우, -2로 출력이 된다.

%f는 실수(소수점)만을 표현하지만 %g는 소수점 여부에 따라 정수 / 실수 자동적으로 바꿔서 표현하기 때문
readers
※ io 패키지
golang에서 입출력을 위한 인터페이스와 함수 제공
type Reader interface {
Read(p []byte) (n int, err error)
}
Read 메소드는 최대 p 바이트를 읽어서 p에 저장 후, 읽은 바이트의 수인 n과 error를 반환한다.
실습 예제
'A'를 무한히 구현하는 메소드 작성
아래는 정답
package main
import "golang.org/x/tour/reader"
type MyReader struct{}
func (r MyReader) Read(p []byte) (int, error) {
for := range p {
p[i] = 'A'
}
return len(p), nil // 읽은 바이트 수, nil 반환
}
func main() {
reader.Validate(MyReader{}) // 사용자 정의 타입인 MyReader의 빈 구조체 인스턴스 생성
// Validate 함수를 이용해 검증
}
// type MyReader struct{}는 필드 없는 빈 구조체이기 때문에 중괄호 안에 특별히 초기화할 값이 x
실습 예제 - rot13Reader
해당 실습은 io.Reader의 데이터를 읽고, rot13 변환을 적용하여 변환이 적용된 문자열을 반환하여 출력하는 예제이다.
아래는 정답
type rot13Reader struct {
r io.Reader
} // rot13Reader 구조체 정의
func (rot13 *rot13Reader) Read(p p[]byte) (int, error) {
n, err := rot13.r.Read(p)
if err != nil {
return n, err
}
// rot13 변환
for i := 0; i < n; i++ {
if (p[i] >= 'A' && p[i] <= 'Z') || (p[i] >= 'a' && p[i] <= 'z') {
if (p[i] >= 'A' && p[i] <= 'Z') {
p[i] = 'A' + (p[i] - 'A' + 13) % 26
}
else if (p[i] >= 'a' && p[i] <= 'z') {
p[i] = 'a' + (p[i] - 'a' + 13) % 26
}
}
}
return n, err // if문에 안걸렸다면 err = nil일 것
}
- rot13Reader 구조체 선언 -> io.Reader 타입을 필드(변수)로 가짐
- Read 메소드에서 p바이트의 슬라이스를 읽어온 후 각 문자를 rot13 방식으로 변환
(대문자 / 소문자 경우의 수를 나누어서 각각 변환함) - rot13 변환 -> 13글자씩 이동한 후 순환(%26, 알파벳은 총 26개 있으므로)
- io.Copy 함수는 포인터 r을 통해 데이터를 읽어와서 stdout으로 출력
&는 변수 앞에 붙어서 포인터 변수임을 선언, *를 통해 포인터를 통한 메모리에 접근 가능, 메모리의 값을 변경 가능
전체 코드를 작성해보자.
package main
import (
"io"
"os"
"strings"
)
type rot13Reader struct {
r io.Reader
} // rot13Reader 구조체 정의
func (rot13 *rot13Reader) Read(p []byte) (int, error) {
n, err := rot13.r.Read(p)
if err != nil {
return n, err
}
// rot13 변환
for i := 0; i < n; i++ {
if (p[i] >= 'A' && p[i] <= 'Z') || (p[i] >= 'a' && p[i] <= 'z') {
if (p[i] >= 'A' && p[i] <= 'Z') {
p[i] = 'A' + (p[i] - 'A' + 13) % 26
} else if (p[i] >= 'a' && p[i] <= 'z') {
p[i] = 'a' + (p[i] - 'a' + 13) % 26
}
}
}
return n, err // if문에 안걸렸다면 err = nil일 것
}
func main() {
s := strings.NewReader("Lbh penpxrq gur pbqr!")
r := rot13Reader{s}
io.Copy(os.Stdout, &r)
}
go playground에 돌려보면

Images
Image 인터페이스는 이미지의 기본적인 속성과 픽셀 데이터를 읽어오는 방법을 정의한다.
package image
type Image interface {
ColorModel() color.Model
Bounds() Rectangle
At(x, y int) color.Color
}
※ ColorModel() color.Model
이미지의 색상 모델을 반환하는 메소드
색상 모델(color.Model)은 이미지의 픽셀이 어떤 색상 공간을 사용하는지 나타냄(예: RGB, Grayscale 등)
※ Bounds() Rectangle
이미지의 경계(Rectangle)를 반환하는 메소드
반환된 Rectangle은 이미지의 너비와 높이를 결정하며, 이미지의 좌표계를 정의함
※ At(x, y int) color.Color
(x, y) 좌표의 픽셀 색상 값을 반환하는 메소드
반환된 값은 color.Color 인터페이스를 구현하는 타입으로, 색상 정보를 포함한 값을 반환함
++) ColorModel, Bounds, At 메소드?
ColorModel() 메소드는 이미지의 색상 표현 방식을 반환하며, 이는 color.RGBAModel, color.GrayModel 등으로 구현될 수 있다.
Bounds() 메소드는 이미지의 크기를 반환하는데, Rectangle 타입은 Min과 Max 필드를 통해 이미지의 경계를 나타내는 타입이다.
At(x, y int) 메소드는 이미지의 (x, y) 좌표에 해당하는 픽셀의 색상 값을 반환한다. 해당 메소드를 통해 이미지를 읽을 때 특정 좌표의 색상 정보를 가져올 수 있다.
실습 예제
데이터 슬라이스를 이용하는 게 아닌, Image 인터페이스를 구현하는 사용자 정의 타입을 만들고, 그 타입을 통해 이미지를 생성하는 문제
만약 Image 인터페이스를 구현하는 나만의 Image 타입을 정의하고, 필요한 메소드를 구현한 후에 pic.ShowImage를 호출하면 우리가 원하는 대로 출력이 될 것이다.
해당 Image 인터페이스를 구현하기 위해? -> 인터페이스에 정의된 메소드들을 필수적으로 구현해야함
ColorModel : color.RGBA Model을 반환
Bounds : 이미지의 크기를 반환하는 image.Rectangle 반환
At : 특정 좌표의 픽셀의 색상을 반환하는 color.Color 반환
아래는 정답
// Image 타입정의
type Image struct {
width, height int
}
// ColorModel 메소드 구현
func (img Image) ColorModel() color.Model {
return color.RGBAModel
}
// Bounds 메서드 구현
func (img Image) Bounds() image.Rectangle {
return image.Rect(0, 0, img.width, img.height)
}
// At 메서드 구현
func (img Image) At(x, y int) color.Color {
v := uint8((x + y) % 256) // 임의의 픽셀 값 생성
return color.RGBA{v, v, 255, 255}
}
- type Image struct: 사용자 정의 Image 타입 생성
- ColorModel():
- color.RGBAModel을 반환하여 이미지가 RGBA 색상 모델을 사용함을 명시
- Bounds():
- 이미지의 경계를 image.Rect(0, 0, img.width, img.height)로 반환 -> 이미지의 좌표계를 정의하여 (0, 0)부터 (width, height)까지의 사각형을 나타냄
- At(x, y int):
- (x, y) 좌표의 픽셀 값 반환
- 예제에서는 v := uint8((x + y) % 256)을 사용해 단순한 패턴의 픽셀 값을 생성함.
- 이 값을 color.RGBA{v, v, 255, 255} 형태로 반환해 픽셀 색상 설정
pic.ShowImage(m)이 호출되어 256x256 크기의 이미지를 생성하고 표시하게 된다.
해당 이미지는 At() 메서드에서 정의한 대로 각 픽셀의 색상이 (x, y) 좌표에 따라 변경된다.
+) 전체 코드
여기서는 Image의 크기를 10x10으로 설정함
package main
import (
"image"
"image/color"
"golang.org/x/tour/pic"
)
// Image 타입정의
type Image struct {
width, height int
}
// ColorModel 메소드 구현
func (img Image) ColorModel() color.Model {
return color.RGBAModel
}
// Bounds 메서드 구현
func (img Image) Bounds() image.Rectangle {
return image.Rect(0, 0, img.width, img.height)
}
// At 메서드 구현
func (img Image) At(x, y int) color.Color {
v := uint8((x + y) % 256) // 임의의 픽셀 값 생성
return color.RGBA{v, v, 255, 255}
}
func main() {
m := Image{10, 10}
pic.ShowImage(m)
}
