golang context 파헤치기

dasd412·2024년 10월 20일
0

golang

목록 보기
1/5
post-thumbnail

context가 필요한 이유

golang에선 context라는 것을 사용한다.

go 공식 블로그를 보면, http request를 예제로 들고 있다. 다음은 그 번역이다.

공식 블로그 번역

서버에 들어오는 각각의 모든 request에 대해 go 서버는 새로운 고루틴을 생성한다. 이를 통해 서버는 여러 요청을 동시에 처리할 수 있다.

이 하나의 requestjwt, deadline 등 여러가지 문맥 정보를 담고 있다. 만약 하나의 요청이 취소되거나 타임아웃되면, 해당 요청에 대한 모든 고루틴은 빠르게 종료되고 자원이 회수되어야 한다. 그래서 컨트롤러, 서비스, 리포지토리 등 여러 레이어에 걸쳐있으면서, request에 엮인 모든 고루틴을 다루기 쉽도록 context를 개발했다고 한다.

번역 내용을 풀어쓰면

위 내용을 이해할 수 있으려면, 하나의 요청이 서버의 자원을 점유한다는 것자원 점유가 지나치면 서버가 죽을 수 있다는 것을 알고 있어야 한다.

정상적인 상황을 먼저 그림으로 들어보자.

정상 상황

각 클라이언트의 요청은 서버를 경유하여 DB 등의 자원을 점유하게 된다. 각 요청과 점유하는 자원은 똑같은 색으로 표시하였다.

그런데 DB 등의 자원을 사용하고자 할 때, 네트워크 에러가 발생한다면? gRPC나 네트워크 패킷이 너무 늦게 도착한다면? MSA인데 다른 마이크로 서비스가 죽어버렸다면?

이 때, 빠른 실패 처리를 위한 타임 아웃이나 서킷 브레이커등이 없다고 해보자. 그러면 어떻게 될까?

비정상 상황


제일 처음 문제가 된 요청에서 block이 발생할 것이고, 레이어에 걸친 모든 요청 역시 block될 것이다. 즉, 문제가 연쇄적으로 전파된다.

그런데 이러한 에러는 다른 요청에 대해서도 비슷한 문제 상황을 발생시킬 가능성이 크다.

시간이 지나면서 다음 그림처럼 쓸데 없는 자원 점유가 서버에 퍼져나갈 것이다.

이처럼 사소한 버그나 에러, 흔히 발생하는 네트워크 문제로 서버 전체가 죽을 수가 있다.

따라서 golang 개발자들은 이러한 문제가 발생하지 않도록, 여러 레이어에 걸쳐있으면서 하나의 요청과 연관된 모든 고루틴들을 빠르게 실패하도록 context를 개발한 것이라 볼 수 있다.

이러한 context를 이용해 에러가 레이어를 넘어 전파되는 것을 방지할 수 있고, 최종적으로 서버가 뻗는 것을 예방할 수 있다.


context 트리

context는 자료구조 트리처럼 최상위 root context에서 파생된다. root contextBackground()TODO() 메서드들을 이용해 생성되며, 파생되는 context들은 주입된 context에서 WithCancel, WithDeadline, WithTimeout, WithValue로 생성된다. (자료구조 트리에서 리프 노드로 갈라지는 것을 상상해보자.)
WithValue를 제외하면 모든 파생 메서드들은 CancelFunc라는 취소 전용 함수를 리턴한다.

해당 함수를 사용하게 되면, 자식 context와 그 후손 context의 모든 작업을 취소시킨다. 뿐만 아니라 부모 context가 자식 context에 대한 참조를 삭제하게 한다.

이 역시 의존되는 레이어(자식 context)에서 문제가 발생할 경우 의존하는 레이어(부모 context)에게 문제가 전파되지 않도록 하는 조치라 볼 수 있다.


참고 자료

https://go.dev/blog/context
https://btree.dev/golang-context
릴리즈의 모든 것


profile
시스템 아키텍쳐 설계에 관심이 많은 백엔드 개발자입니다. (Go/Python/MSA/graphql/Spring)

0개의 댓글