[Golang] 오류 처리, 깔끔한 방법!

13001 단어 errorsgol
저는 golang 커뮤니티에서 특히 최근에 golang의 단순성 때문에 이동하여 golang에서 어떻게 처리해야 하는지 빠르게 판단하는 개발자를 많이 보았습니다.

이러한 것 중 하나는 오류 처리입니다. 대부분의 언어 현재 개발자는 OOP 배경에서 왔으며 예외를 처리하는 데 사용되거나 응용 프로그램의 현재 흐름을 중지하기 위해 예외를 "던지는"개념을 가정하고 대부분 이것이 방법이라고 생각합니다. golang에 들어가려면 문제가 발생할 경우를 대비하여 애플리케이션의 흐름을 중지해야 합니다.
글쎄요!

도구를 남용하지 마세요.



저도 많이 봤고 저도 그랬거든요. 그냥 os.exit(1) 예기치 않은 일이 발생할 때마다 계속 진행하십시오. 글쎄, 그것은 go와 함께가는 올바른 방법이 아닙니다!

이것이 널리 사용되는 이유를 알 수 있습니다. 대부분의 초기 golang 애플리케이션은 단지 터미널 도구일 뿐이었고 이러한 도구 중 상당수는 .sh 실행 파일을 사용하여 빌드되어 1을 종료하는 데 사용되었습니다. 예상치 못한 일이 발생하여 종료하고 싶다는 것을 나타냅니다.

우리는 이 습관을 우리의 golang 간단한 터미널 응용 프로그램에 적용한 다음 복잡한 응용 프로그램에 적용했습니다. 이는 또 다른 카고 컬트 프로그래밍입니다.

다음과 같이 해야 할 경우를 대비하여 매우 신중하게 수행할 것을 적극 권장합니다.
  • 애플리케이션이 커짐에 따라 유지 관리하기가 매우 어렵습니다.
  • 가장 중요한 것은 명확하지 않음을 나타내는 코드를 단위 테스트하는 것이 불가능하다는 것입니다.
  • 이러한 방식으로 종료하면 지연된 작업의 실행이 방지되고 프로그램이 즉시 종료되며 이로 인해 리소스 누수가 발생할 수 있습니다. 예를 들어 다음을 고려하십시오.

  • func main() {
        dbConnection := db.connect("...")
        defer dbConnection.Close() // this operation won't be executed!
        entity := Entity{}
        err := dbConnection.Save(entity)
        if err != nil {
            os.Exist(1)
        }
    }
    

    오류 전파 고려



    오류는 golang의 또 다른 유형일 뿐이며 프로그램 실행 흐름을 제어하려면 오류를 사용해야 합니다.

    그렇게 하기 위해서는 적절한 처리 지점까지 프로그램 전체에 이러한 오류를 전파해야 합니다.

    클라이언트가 특정 조건으로 주문하는 것을 금지하려는 주문을 관리하기 위한 HTTP API를 고려하십시오. 예를 들면 다음과 같습니다.

    package order
    // package errors
    var (
        UnableToShipToCountry = errors.New("unable to ship order to the requested country")
    )
    type Order struct {
        // ... order fields
    }
    type OrderRepo struct {
        DB db
        // ...
    }
    func newOrderFromRequest(o OrderRequest) (Order, error) {
        if o.ShippingAddress.Country != "DE" {
        return UnableToShipToCountry
        }
        // ... the creation logic
        return Order{...}, nil
    }
    func (r *OrderRepo)PlaceOrder(o OrderRequest) error {
        order, err := newOrderFromRequest(o)
        if err != nil {
            // don't handle the error here, its handling may differ
            return err
        }
        // ... db transaction may also return an error
        return r.db.Save(order)
    }
    


    http 핸들러 패키지에서:

    package http
    http.HandleFunc("/order", func (w http.ResponseWriter, r *http.Request) {
        orderRequest := createOrderRequest(r)
        err := orderRepo.PlaceOrder(orderRequest)
        if errors.Is(err, order.UnableToShipToCountry) {
            w.WriteHeader(http.StatusBadRequest)
            return
        }
        if err != nil {
            // this error in case of DB transaction failure
            w.WriteHeader(http.StatusInternalServerError)
            return
        }
        // ...
        w.WriteHeader(http.StatusOK)
    
    })
    


    오류 맞춤설정



    오류 추적과 같은 몇 가지 유용한 정보를 여기에 추가하는 것을 고려하면서 사용자 지정 오류 값을 만들고 프로그램 전체에서 사용할 수 있습니다! 특히 디버깅 중에 로그에 유익한 값을 추가할 수 있습니다.

    예를 들어:

    type AppErr struct {
        msg string
        code int
        trace string
    }
    
    func (e AppErr) Error() string {
        return fmt.Sprintf("Msg: %s, code: %d, trace:\n %s", e.msg, e.code, e.trace)
    }
    
    func NewAppErr(msg string, code int) AppErr {
        stackSlice := make([]byte, 512)
        s := runtime.Stack(stackSlice, false)
    
        return AppErr{msg, code, fmt.Sprintf("\n%s", stackSlice[0:s])}
    }
    


    그리고 우리는 관리자 패키지 내부에 다음과 같은 사용 사례가 있습니다.
    패키지 관리자

    func A() error {
        return b()
    }
    
    func b() error {
        return NewAppErr("error from b function!", 3)
    }
    And our main.go is:
    func main() {
        err := admin.A()
    
        fmt.Println(err)
    }
    


    기록된 오류 메시지는 다음과 같습니다.

    Msg: error from b function!, code: 3, trace:
    
    goroutine 1 [running]:
    ./cmd/app/error.NewAppErr({0x1f42b0, 0x17}, 0x7)
            ./cmd/app/error/error.go:16 +0x35
    ./cmd/app/admin.b(...)
            ./cmd/app/admin/admin.go:12
    ./cmd/app/admin.A(...)
            ./cmd/app/admin/admin.go:8
    main.main()
            ./cmd/app/main.go:10 +0x8d
    


    prod에서 추적 인쇄를 종료하거나 다른 구성 값을 확인하여 종료하는 것도 고려할 수 있습니다.

    도움이 되었기를 바랍니다.

    좋은 웹페이지 즐겨찾기