🔥 연습문제 - ROT13 Reader 구현하기

435자
5분

Go 언어에서는 io.Reader를 감싸는 것이 일반적인 패턴이에요. 이렇게 하면 스트림을 어떤 식으로든 수정할 수 있죠.

예를 들어, gzip.NewReader 함수는 io.Reader(압축된 데이터의 스트림)를 가져와서 io.Reader(압축이 해제된 데이터의 스트림)도 구현하는 *gzip.Reader를 반환하지요.

이제 io.Reader를 구현하고 io.Reader에서 읽는 rot13Reader를 구현해 볼 거예요. 이 리더는 모든 알파벳 문자에 ROT13 치환 암호를 적용하여 스트림을 수정할 거랍니다.

rot13Reader 타입은 이미 제공되어 있어요. 이 타입의 Read 메서드를 구현하여 io.Reader로 만들어 보죠.

package main
 
import (
	"io"
	"os"
	"strings"
)
 
type rot13Reader struct {
	r io.Reader
}
 
func (r *rot13Reader) Read(p []byte) (n int, err error) {
	n, err = r.r.Read(p)
	for i := 0; i < n; i++ {
		if (p[i] >= 'A' && p[i] <= 'M') || (p[i] >= 'a' && p[i] <= 'm') {
			p[i] += 13
		} else if (p[i] >= 'N' && p[i] <= 'Z') || (p[i] >= 'n' && p[i] <= 'z') {
			p[i] -= 13
		}
	}
	return
}
 
func main() {
	s := strings.NewReader("Lbh penpxrq gur pbqr!")
	r := rot13Reader{s}
	io.Copy(os.Stdout, &r)
}
 
go

이 코드를 하나하나 뜯어보면서 설명해 드릴게요.

type rot13Reader struct {
	r io.Reader
}
 
go

먼저, rot13Reader 구조체를 정의하고 있어요. 이 구조체는 io.Reader를 필드로 가지고 있죠. 이 필드는 ROT13 변환을 적용할 원본 리더를 나타내요.

func (r *rot13Reader) Read(p []byte) (n int, err error) {
	n, err = r.r.Read(p)
	for i := 0; i < n; i++ {
		if (p[i] >= 'A' && p[i] <= 'M') || (p[i] >= 'a' && p[i] <= 'm') {
			p[i] += 13
		} else if (p[i] >= 'N' && p[i] <= 'Z') || (p[i] >= 'n' && p[i] <= 'z') {
			p[i] -= 13
		}
	}
	return
}
 
go

그 다음엔 rot13Reader에 대한 Read 메서드를 구현하고 있습니다. 이 메서드는 io.Reader 인터페이스를 충족시키죠.

우선 원본 리더(r.r)에서 데이터를 읽어와 p 슬라이스에 저장해요. 이때 읽은 바이트 수 n과 에러 err도 함께 반환됩니다.

그런 다음 for 루프를 사용하여 p 슬라이스의 각 바이트에 ROT13 변환을 적용하지요.

  • 바이트가 'A'에서 'M' 사이의 대문자이거나 'a'에서 'm' 사이의 소문자인 경우, 해당 바이트에 13을 더해 주어요.
  • 반대로 바이트가 'N'에서 'Z' 사이의 대문자이거나 'n'에서 'z' 사이의 소문자인 경우에는 13을 빼 주죠.

이렇게 하면 알파벳을 13자리씩 이동시키는 ROT13 치환이 이루어집니다.

마지막으로 읽은 바이트 수 n과 에러 err을 반환하여 Read 메서드를 완성하게 됩니다.

func main() {
	s := strings.NewReader("Lbh penpxrq gur pbqr!")
	r := rot13Reader{s}
	io.Copy(os.Stdout, &r)
}
 
go

main 함수에서는 strings.NewReader를 사용하여 ROT13으로 인코딩된 문자열 "Lbh penpxrq gur pbqr!"를 읽어들이는 io.Reader를 생성해요.

그리고 이 리더를 rot13Reader로 감싸서 r이라는 새로운 io.Reader를 만들죠.

마지막으로 io.Copy를 사용하여 r에서 읽은 데이터를 표준 출력(os.Stdout)에 쓰게 됩니다. 이때 rot13ReaderRead 메서드가 호출되면서 ROT13 디코딩이 이루어지고, 원래 문자열이 출력되죠. 아래는 출력 결과입니다.

You cracked the code!
text