Context-less Go — 쉽게 HTTP 서비스 작성하는 방법
컨텍스트를 IoC로 사용하지 않고 HTTP 서비스 작성을 좋아하는 방법

많은 Go 개발자, 특히 새로운 개발자가 명확하지 않다고 생각하는 것 중 하나는 다음과 같습니다. 내가 필요한 모든 것을 핸들러에 전달하는 방법은 무엇입니까?
Java 또는 C#과 같은 멋진 Inversion of Control 시스템이 없습니다. http.Handlers
정적 서명이므로 내가 정말로 원하는 것을 전달할 수 없습니다. 3가지 옵션만 있는 것 같습니다.
use globals
wrap handlers in a function
- 또는 물건을 전달
context.Context
!
이 세 가지 옵션을 모두 살펴보겠습니다.
— 말장난 확실히 의도
우리가 작성하는 핸들러는 일상적인 일반 전자 상거래 상점에서 볼 수 있는 것입니다.
우리의 임무는 특정 카테고리 ID가 주어지면 해당 카테고리의 항목 목록을 반환하는 끝점을 작성하는 것입니다.
이 끝점은 우리의 items.Service
실제 조회를 수행하려면 logging.Service
일이 잘못될 경우, 그리고 metrics.Service
그들에게 달콤한 마케팅 지표를 얻기 위해!
— 전화 미스터 월드와이드
핸들러에 물건을 가져오기 위한 첫 번째 시도는 전역입니다. 이것은 많은 초보자들이 하는 경향이 있는 상당히 자연스러운 방법입니다.
우리 모두는 한 번쯤은 이와 같은 코드를 작성해 본 적이 있습니다. 그런 다음 우리 모두 전역은 유연하지 않고 테스트할 수 없는 코드를 만들기 때문에 나쁘다는 것을 빠르게 배웠습니다.
이것은 자연스럽게 "당신의 의존성을 주입하십시오!"라고 말하는 책, 기사, 비디오 등의 토끼 구멍으로 이어집니다. 이 코드는 작동하지 않습니다. 약간의 주입을 시도해 보겠습니다.
— 부스터 얻기
글쎄, 우리는 전역을 사용하고 싶지 않지만 http.Handler
고정 서명이 있습니다. 어떻게 해야 할까요? 당연히 포장해 드립니다!
이것은 더 나은 우리는 더 이상 글로벌 상태에 의존하지 않기 때문에 여전히 원하는 것이 많이 남아 있습니다. 주로, 그것은 모든 핸들러를 작성하기에 크고 길고 성가신 혼란으로 만듭니다. 몇 가지 서비스를 더 추가하면 이 서명이 2~3배 더 길어질 것이라고 상상하기 쉽습니다.
그래서 우리는 주변을 읽고 우리의 *http.Request
가지고있다 context.Context
그 안에! 미들웨어를 만들고 모든 종속성을 주입하면 핸들러가 필요한 것만 가져올 수 있습니다! 이것은 분명히 우리의 모든 문제를 해결할 것입니다!
— 이제 컬러로!
먼저, 우리가 이야기했던 미들웨어를 만들어 봅시다. 우리는 모든 의존성을 설정한 후에 이것을 인라인으로 할 것입니다.
이제 모든 것이 추가되었으므로 핸들러를 다시 한 번 실행할 수 있습니다.
멋지고 쉽습니다! 여기에는 아무 것도 잘못될 수 없습니다. 글쎄, 3개월 후 누군가가 코드 줄을 앱의 다른 곳으로 옮기고 이러한 서비스 중 하나가 컨텍스트에 더 이상 존재하지 않는 경우를 제외하고.
“글쎄요, 스마트 미디엄 작가님, 제가 확인해 보겠습니다!” 당신은 말할 수 있습니다.
작고 단순하며 인위적인 예에서도 볼 수 있듯이 안전한 방식으로 이 작업을 수행하는 것은 약간 엉망이 됩니다.
Go를 사용하는 이유 중 하나는 이러한 모든 유형의 검사 혼란을 피하기 위함입니다! 컨텍스트를 의존성을 위한 일종의 잡기 가방으로 사용함으로써 우리는 효과적으로 안전을 포기합니다. 또한 런타임까지 사물이 존재하는지 알 수 없습니다. 이 문제를 해결하기 위해 우리는 모든 곳에서 긴 오류 검사 문자열로 끝납니다.
이것은 좋지 않다. 우리의 세 가지 옵션은 모두 각자의 방식으로 빨려들어갑니다. 의존성을 주입하고 싶다면 장황한 지옥에서 벗어날 수 없는 것 같습니다. 물론 이러한 문제를 해결하고 이것의 많은 부분을 감싸는 멋진 기능을 만들 수 있지만 실제로 문제를 해결하는 것은 아닙니다.
네번째 방법이 없다면....?
— 핸들러에 구조 추가 중…
여러분 중 일부는 이미 이 문제를 해결하기 위해 저에게 소리를 지르셨을지 모르지만 이 주제를 몇 번 잘 가르쳤으면 다른 솔루션을 먼저 보는 것이 정말 도움이 됩니다.
우리가 고려하지 않은 "네 번째 방법"은 실제로 매우 간단합니다.
- 필요한 종속성을 보유하는 구조체를 만듭니다.
- 해당 구조체의 메서드로 핸들러를 추가합니다.
예제를 살펴보겠습니다.
이 솔루션에는 다음과 같은 몇 가지 큰 이점이 있습니다.
- 우리가 의존하는 모든 것은 빌드 시간에 알려지며 유형이 안전합니다(컨텍스트와 달리)
- 래퍼 함수와 달리 최소한의 추가 상용구가 있습니다.
- 테스트 가능성 유지(글로벌과 달리)
- 관련 핸들러를 단위로 "그룹화"할 수 있습니다.
4는 아직 다루지 않았지만 이제 이 구조체가 있으므로 공통 종속성을 공유하거나 논리적으로 함께 가는 처리기 그룹을 만들 수 있습니다.
예를 들어 여기에 새 카테고리를 추가하기 위해 핸들러를 추가할 수 있습니다. 우리는 만들 수 있습니다 MetricsHandler
메트릭과 관련된 모든 엔드포인트를 함께 그룹화합니다. 원하는 만큼 세분화하거나 광범위하게 사용할 수 있습니다(세분화된, 아마도 대부분의 경우 더 좋습니다.).
누가 여기까지 읽었습니까?
드디어 목표가 생겼습니다. 완전히 고안된 전자 상거래 상점의 우리 상사는 끝점이 준비되어 기뻐할 것입니다(일부 테스트 제외). 다음 작업으로 넘어갈 수 있습니다.
이것은 새롭거나 새로운 아이디어가 아닙니다. https://pace.dev/blog/2018/05/09/how-I-write-http-services-after-eight-years.html 및 https://pace.dev/blog/2018/05/09/how-I-write-http-services-after-eight-years.html과 같은 고전적인 go 예제 게시물을 보면 /github.com/benbjohnson/wtf/ 이러한 아이디어가 실제로 실행되는 것을 볼 수 있습니다.
그것은 훌륭하고 분명하지 않은 해결책이라고 생각합니다.
물론 인터페이스 등을 사용하여 이것을 훨씬 더 테스트하기 쉽게 만들 수 있지만 그건 또 다른 기사입니다!
'Coding' 카테고리의 다른 글
Pandas 데이터 프레임을 Snowflake로 업데이트하는 방법 (0) | 2022.04.16 |
---|---|
Unity 프로덕션 패턴: 제네릭과의 일관된 연결하는 방법 (0) | 2022.04.15 |
Parcel을 사용하여 Phaser 3 게임 묶는 방법 (0) | 2022.04.13 |
프로덕션 전 대량 테스트 API - Azure Durable Functions가 포함된 Azure DevOps 릴리스 게이트하는 방법 (0) | 2022.04.12 |
Perflint 사용하는 방법 — Python용 성능 린터 (0) | 2022.04.11 |
댓글