본문 바로가기
golang

[go] Context 내부 속으로 - (1)

by 참된오징어 2022. 4. 25.

최근 회사에서 이미지 업로드 / 다운로드 서버를 개발하면서 고루틴을 많이 다루게 되었다.

이 기회에 고루틴 및 context에 개념을 정리하고 가고자 한다.

잘 모르면 주석부터 해석해보는 것이 진리 아니겠는가?

1부는 패키지에 소개된 주석들을 살펴보는 것이다.

https://pkg.go.dev/context

 

context package - context - pkg.go.dev

This example demonstrates the use of a cancelable context to prevent a goroutine leak. By the end of the example function, the goroutine started by gen will return without leaking. package main import ( "context" "fmt" ) func main() { // gen generates inte

pkg.go.dev

 

 

Context 패키지를 어떻게 설명하고 있는지 처음부터 차근차근 발로 해석해보자.

주석은 박스에, 해석은 회색 글자로, 개인 사견은 해설 아래 검은색으로 표시한다.

 


# 해석

Package context defines the Context type, which carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes.

 

Context 패키지는 Context라는 타입으로 정의할 수 있는데 어떤 타입이냐면

마감시간, 취소 신호, API 경계를 넘어서 프로세스 간에 요청 범위의 값이 3개를 전달할 수 있는 타입이다.

 

값을 전달할 수 있는 일종의 통로인가 보다. 다른 블로그들 통해 더 찾아보니 맥락을 유지하는 통로라고 표현을 볼 수 있다.

즉, 현재 맥락(흐름) 안에서 유지해야 할 값을 Context라는 통로에 담아 전달하고, 필요한 곳에서 Context에서 값을 꺼내 사용할 수 있다고 이해하자.

 

 

 

 

 

Incoming requests to a server should create a Context, and outgoing calls to servers should accept a Context.

The chain of function calls between them must propagate the Context, optionally replacing it with a derived Context created using WithCancel, WithDeadline, WithTimeout, or WithValue.

When a Context is canceled, all Contexts derived from it are also canceled.

 

서버에 들어오는 요청은 Context를 생성해야 하고, 서버에서 나가는 호출은 Context를 수락해야 한다.

 

서버로 들어와서 나가기까지 함수 호출 체이닝 간 Context를 전파해야 하며, 선택적으로 WithCancel, WithDeadline, WithTimeout, WithValue를 사용하여 생성된 Context에서 파생된 컨텍스트를 사용한다.

 

Context가 취소되면 해당 컨텍스트에서 파생된 모든 Context도 취소가 된다.

 

서버에서 나가는 호출은 컨텍스트를 수락해야 한다라는 의미는 아마도 in - out으로 작업 종료를 뜻하는 것 같다.

즉, 요청이 들어오면 컨텍스트를 생성하거나, 요청이 끝나면 컨텍스트도 종료시켜야 한다고 이해가 된다.

 

그리고 서버로 들어온 요청이 종료되기 전까지 연쇄로 호출되는 함수간 컨텍스트를 전달해야 하며,
컨텍스트 전달 시 4개의 함수 [WithCancel, WithDeadline, WithTimeout, WithValue]를 이용해서 컨텍스트를 파생시킬 수 있는 것 같다. 컨텍스트를 파생한다는 것은 자식 컨텍스트를 생성한다는 것이며, 부모 - 자식 관계라고 이해가 된다.

또한 부모 컨텍스트가 취소(종료)되면 자식 컨텍스트들도 취소가 되는 것 같다.

 

 

 

 

 

The WithCancel, WithDeadline, and WithTimeout functions take a Context (the parent) and return a derived Context (the child) and a CancelFunc.

Calling the CancelFunc cancels the child and its children, removes the parent's reference to the child, and stops any associated timers.

Failing to call the CancelFunc leaks the child and its children until the parent is canceled or the timer fires.
The go vet tool checks that CancelFuncs are used on all control-flow paths.

 

WithCancel, WithDeadline 및 WithTimeout 함수는 부모 Context를 사용하여 자식 Context와 CancelFunc를 반환한다.

 

CancelFunc를 호출하면 자식 Context와 자식의 자식 Context들이 취소되고, 부모 Context와 자식 Context의 참조가 제거되며 연결된 타이머가 중지된다.

 

CancelFunc 호출을 실패하면 부모 Context가 취소되거나 부모 Context의 타이머가 끝날 때까지

자식 Context와 자식의 자식 Context가 leak 된다.

 

go vet는 CancelFuncs가 모든 제어 흐름 경로에 사용되는지 확인한다.

 

 

WithCancel, WithDeadline 및 WithTimeout은 부모 컨텍스트를 참조로 자식 컨텍스트와  cancelFunc()를 반환한다.

ctx, cancelFunc := context.WithCancel()

반환된 cancelFunc()를 실행하면 부모의 자식들이 모두 취소가 되는 것 같다.

대신 부모는 취소가 되는지는 별도로 2부에서 확인이 필요해 보인다.

 

반환된 취소 함수를 실행했는데 실패할 수도 있는 것 같다. 이때, 자식 컨텍스트들이 leak이 된다는데 메모리 누수인지, 외부로 노출이 된다는 뜻인지 자세히는 모르겠다.

go vet라는 표준 패키지는 코드 정적 분석 툴인데 해당 툴이 함수 호출하면서 작업 플로우 간 CancelFuncs가 잘 사용되었는지 확인하는 것 같다.

 

 

 

 

 

Programs that use Contexts should follow these rules to keep interfaces consistent across packages and enable static analysis tools to check context propagation:

 

Context를 사용하는 프로그램은 다음 룰들을 따라야 한다.
패키지 간에 인터페이스를 일관되게 유지하고 Context 전파를 확인하기 위해 정적 분석 툴을 활성화해야 한다.

 

 

 

 

 

Do not store Contexts inside a struct type; instead, pass a Context explicitly to each function that needs it. The Context should be the first parameter, typically named ctx:
func DoSomething(ctx context.Context, arg Arg) error {
    // ... use ctx ...
}

 

구조체에 Context를 저장하지 마라
대신 Context를 필요로 하는 각 함수들에게 명시적으로 Context를 전달해라
Context는 정통적으로 첫 번째 파라미터로 ctx라는 이름으로 명명해야 한다.

 

 

 

 

 

Do not pass a nil Context, even if a function permits it.
Pass context.TODO if you are unsure about which Context to use.

 

함수가 허락할지라도, Context에 nil을 전달하지 말 것.
만약 사용할 Context가  확실하지 않는다면, Context.TODO()를 전달해라.

 

어떤 Context를 사용할지 모르겠다면 Context.TODO()를 전달하자

 

 

 

 

 

Use context Values only for request-scoped data that transits processes and APIs, not for passing optional parameters to functions.

 

오직 프로세스를 이행하고 있는 요청 범위 내의 데이터만 Context 값으로 사용해라
그리고 Conext는 함수 파라미터에 값을 전달하기 위한 API가 아니다

 

말 그대로 특정 프로세스 내에서 사용되는 데이터만 Context 값으로 사용하라는 뜻인 것 같다.

또한 Context를 함수에 값을 전달하는 파라미터 용도로 사용하지 말라는 것 같다.

 

 

 

 

 

The same Context may be passed to functions running in different goroutines; Contexts are safe for simultaneous use by multiple goroutines.

 

동일한 Context는 다른 고루틴들에서 실행되는 함수로 전달될 수 있다.
컨텍스트는 여러 고루틴에서 동시에 사용하기에 안전하다.

 

서로 다른 고루틴에 의해 실행되는 함수들에게 동일한 Context를 전달할 수 있다.

그리고 서로 다른 고루틴에서 Context를 동시에 사용하기에 안전하다.

 

동시에 사용하기에 안전하다는 것은 서로 다른 고루틴이 동시에 읽기 쓰기에 대한 동시성 보장을 해준다는 걸까?

해당 내용이 맞다면 go 언어의 철학이 생각나게 된다.

"Don’t communicate by sharing memory, share memory by communicating."

메모리를 공유함으로써 커뮤니케이션하지 말고, 커뮤니케이션을 통해 메모리를 공유하라.

컨텍스트에 서로 다른 고루틴들이 접근하는게 아니라 서로 다른 고루틴들 사이에 컨텍스트를 공유하자는 느낌을 받았다.

 

 

 

 


 

여기까지가 Context 패키지를 소개하는 주석이다. 요약을 해보자면 다음과 같다.

 

 

  • Context란 값을 전달할 수 있는 일종의 통로, 커뮤니케이션이다.
  • 하나의 프로세스 (request 등) 내에서 유지해야할 값을 Context에 담아 전달하고, 필요한 곳에서 Context에서 값을 꺼내 사용할 수 있다.

 

  • 하나의 프로세스가 시작되고 종료되었을 때 Context를 종료시켜야 한다.
  • Context에서 제공하는 함수들을 사용하면 Context를 생성할 수 있다.
  • WithCancel(), WithDeadline(), WithTimeout()은 부모 Context 기준으로 자식 Context를 생성한다.
    • 반환값으로 자식 Context와 CancelFunction을 반환하는데, CancelFunction을 실행시키면 자식 Context들이 모두 취소가 되는 것 같다.
  • 부모 Context가 종료되면 부모 Context에서 파생된 자식 Context들도 종료가 된다.

 

  • 구조체에 Context를 저장하지 말고
  • Context를 필요로 하는 함수에 파라미터로 전달하고
  • 파라미터로 전달할 때 첫번째 인자에 그리고 이름은 ctx로 명명한다.
  • Context 전달 시, nil을 전달하지 말고 어떤 Context를 전달해야 할지 모르겠다면 Context.TODO를 전달하라
  • Context를 함수에 값을 전달하는 파라미터 용도로만 활용하지 말라

 

  • Context는 서로 다른 고루틴에서 동시에 사용하기에 안전하다.
  • 서로 다른 고루틴으로 실행되는 함수들에게 동일한 컨텍스트를 전달할 수 있다.

 

 

다음 2부 때는 Context 타입이 무엇인지, 내부 구조가 어떻게 되어있는지 살펴보자

저급한 해석으로 인해 오역이 있을 수도 있습니다..! 알려주시면 감사하겠습니다 :)

'golang' 카테고리의 다른 글

[GORM] Implements Customized Data Type (feat.Scanner & Valuer)  (2) 2021.12.03

댓글