기본 기능

내부값 활용

// 기본 Context 생성
ctx := context.Background()

// Context에 값 추가, 여기서 새로운 Context를 생성한다
ctx = context.WithValue(ctx, "CURRENT_USER", currentUser)

// 함수 파라미터로 Context 전달
callApi(ctx)

...

func callApi(ctx context.Context) error {
	var currentUser User
	if v := ctx.Value("CURRENT_USER"); v != nil {
		// 여기선 타입이 맞는지 비교, ok로 확인할 수 있다
		u, ok := v.(User)
		if !ok {
			return errors.New("Not Authroized")
		}
		currentUser = u
		...
	}
	return errors.New("Not Authroized")
}

Cancel

func longFunc() string {
	<-time.After(time.Second * 10)
	return "success"
}

func longFuncWithCtx(ctx context.Context) (string, error) {
	done := make(chan string)
	go func() {
		done <- longFunc()
	}()
	
	select {
		case result := <-done:
			return result, nil
		// Context의 Done은 Context가 강제 종료되었을 때 받게 되는 채널
		case <- ctx.Done():
			// Err는 Context가 종료되고 발생한 에러
			return "fail", ctx.Err()
	}
}

...

// longFuncWithCtx을 호출하는데, Context의 WithCancel 사용
ctx, cancel := context.WithCancel(context.Background())
go func() {
	// Go루틴을 종료해야 될 상황이면 cancel을 호출하여 Context에게 취소 신호를 전달
	cancel()
}()

result, err := longFuncWithCtx(ctx)

...

// 만약 여러 Go루틴을 한꺼번에 제어하고 싶다면 아래처럼 짜도 된다
ctx, cancel := context.WithCancel(context.Background())

var wg sync.WaitGroup
for i := 0; i < jobCount; i++ {
	wg.Add(1)
	go func() {
		defer wg.Done()
		result, err := longFuncWithCtx(ctx)
		...
	}()
}

Deadline, Timeout

func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)
func WithTimeout(parent Context, d time.Duration) (Context, CancelFunc)
ctx, cancel := context.WithTimeout(context.Background(), time.Second * 3)
go func() {
	cancel()
}()

start := time.Now()
result, err := longFuncWithCtx(ctx)
fmt.Printf("duration:%v, result:%s\\n", time.Since(start), result)