쿼카러버의 기술 블로그
[Golang gin] io.NopCloser란? (ioutil.ReadAll()로 읽은 content를 날리지 않는 법) (io.ReadCloser란) 본문
[Golang gin] io.NopCloser란? (ioutil.ReadAll()로 읽은 content를 날리지 않는 법) (io.ReadCloser란)
quokkalover 2022. 2. 9. 23:58Go는 바이트(bytes)를 사용하여 작업하기 위해 만들어진 프로그래밍 언어다. 근데, gin 프레임워크나 HTTP request 처리하다보면 바이트를 다루기 위한 io 패키지를 사용하다가 아찔한 함정에 빠질 수 있다.
그 함정은 ioutil.ReadAll()
이나 이를 활용하는 ctx.GetRawBody()
와 같은 메서드 등을 통해 request의 Body를 한 번 읽고나면 다시 읽을 수 없다는 점이다. 이는 컨텐츠를 모두 읽어냈기 때문인데, gin뿐 아니라 http패키지의 Request의 Body를 읽을 때도 마찬가지다. (당연한 소리) (io.Reader를 통해 stream을 한번 읽고나면 idx가 뒤로 가기 때문에 다시 읽을 수 없음)
하지만 API를 개발하다보면, Request Body의 내용을 미리 확인한 뒤, 어떤 처리를 할지 정하고, 다시 Request를 넘겨야 하는 경우가 종종 생긴다. 이럴때는 어떻게 해야 할까? 방법은 바로 `io.Nopcloser` 를 사용하는 것이다.
io.NopCloser란 io.ReadCloser타입을 리턴하는 메소드다. bytes.Buffer와 같이 Close()를 지원하지 않는 io.Reader를 래핑해서 no-op Close메소드(with an empty method body)를 지원하는 io.ReadCloser를 만들고 싶을 때 사용한다. (http.Request의 Body가 io.ReadCloser만 받기 때문)
바로 예시를 들어 설명하겠다.
bodyBytes, err := ctx.GetRawData()
fmt.Println(bodyBytes)
bodyBytes, err = ctx.GetRawData()
fmt.Println(bodyBytes)
위처럼 코드를 실행하게 되면 두번째 pritn문에는 아무것도 출력이 되지 않는다.
다시 출력되게 하고 싶으면 아래처럼 ioutil.NopCloser
를 사용하면 된다.
bodyBytes, err := ctx.GetRawData()
fmt.Println(bodyBytes)
ctx.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
bodyBytes, err = ctx.GetRawData()
fmt.Println(bodyBytes)
위 코드를 보면 bytes.NewBuffer라는 메소드를 사용하는데, 매개변수로 들어가는 데이터를 담은 새로운 Buffer를 만들어준다.
즉, io.NopCloser()
를 통해 ReadCloser를 만들고, 컨텐츠에 사용되는 Buffer를 새로 만들어서 넣어주는 것이다.
이렇게 하면, 다시 ctx.GetRawData()나, Body를 ioutil.ReadAll()로 읽고난 뒤에도 다시 사용할 수 있다.
참고로 go 1.16버전 부터는 io.NopCloser, 이전 버전은 ioutil.Nocloser를 사용해야 한다.
참고자료:
https://medium.com/@xoen/golang-read-from-an-io-readwriter-without-loosing-its-content-2c6911805361
'[Golang]' 카테고리의 다른 글
[Golang] Go의 Naming Convention (네이밍 컨벤션) (Go스러운 코딩 시리즈) (0) | 2022.02.20 |
---|---|
[Golang] go가 byte stream을 다루는 방법에 대하여 (io.Reader, io.Writer, Copy, Close, Seeker, Pipe, Buffer 등 개념 뿌시기) (0) | 2022.02.13 |
[golang] Unit Test 마스터하기 (step1. 테스트 전에 미리 설정이 필요할때) (testify suite패키지) (0) | 2021.12.08 |
[golang] Type assertion과 Type conversion 차이 (0) | 2021.09.24 |
[golang] time.After이란? (memory leak 가능성) (0) | 2021.08.30 |