Skip to content

인터페이스 정의

Client

go
type Client interface {
    Doer

    // HTTP 메서드
    Get(url string, options ...RequestOption) (*Result, error)
    Post(url string, options ...RequestOption) (*Result, error)
    Put(url string, options ...RequestOption) (*Result, error)
    Patch(url string, options ...RequestOption) (*Result, error)
    Delete(url string, options ...RequestOption) (*Result, error)
    Head(url string, options ...RequestOption) (*Result, error)
    Options(url string, options ...RequestOption) (*Result, error)

    // 파일 다운로드
    Download(ctx context.Context, url string, cfg *DownloadConfig, options ...RequestOption) (*DownloadResult, error)

    // 수명 주기
    Close() error
}

메인 클라이언트 인터페이스로, New()로 생성합니다. 자세한 내용은 패키지 함수와 클라이언트 메서드를 참조하세요.

Doer

go
type Doer interface {
    Request(ctx context.Context, method, url string, options ...RequestOption) (*Result, error)
}

최소 인터페이스로, 핵심 Request 메서드만 포함합니다. 커스텀 구현에 적합합니다.

go
type MyDoer struct{}

func (d *MyDoer) Request(ctx context.Context, method, url string, options ...httpc.RequestOption) (*httpc.Result, error) {
    // 커스텀 구현
    return nil, nil
}

DomainClienter

go
type DomainClienter interface {
    Client

    // URL 접근
    URL() string
    Domain() string

    // 세션 헤더 관리
    SetHeader(key, value string) error
    SetHeaders(headers map[string]string) error
    DeleteHeader(key string)
    ClearHeaders()
    GetHeaders() map[string]string

    // 세션 Cookie 관리
    SetCookie(cookie *http.Cookie) error
    SetCookies(cookies []*http.Cookie) error
    DeleteCookie(name string)
    ClearCookies()
    GetCookies() []*http.Cookie
    GetCookie(name string) *http.Cookie

    // 세션 접근
    Session() *SessionManager
}

도메인 범위 클라이언트로, Cookie 와 요청 헤더를 자동으로 관리합니다. 자세한 내용은 도메인 클라이언트세션 관리를 참조하세요.

RetryPolicy

go
type RetryPolicy interface {
    ShouldRetry(resp ResponseReader, err error, attempt int) bool
    GetDelay(attempt int) time.Duration
    MaxRetries() int
}

커스텀 재시도 전략 인터페이스입니다.

메서드설명
ShouldRetry(resp, err, attempt)재시도 여부 판단, attempt는 0 부터 시작
GetDelay(attempt)다음 재시도 전 대기 시간 반환
MaxRetries()최대 재시도 횟수 반환

내부 타입 제한

ShouldRetry 의 resp 매개변수 타입 ResponseReader 는 내부 인터페이스 (internal/types 패키지에 위치) 이므로 외부 코드에서 직접 참조할 수 없습니다. 따라서 RetryPolicy는 같은 모듈 내에서만 구현할 수 있습니다. 대부분의 시나리오는 RetryConfig 설정과 WithMaxRetries 옵션으로 재시도 요구사항을 충족할 수 있습니다. 커스텀 전략이 필요한 경우 프로젝트 내부 패키지에서 RetryPolicy 인터페이스를 구현하세요.

다음 예제는 RetryPolicy의 구현 패턴을 보여줍니다. ResponseReader 는 내부 타입이므로 이 코드는 httpc 모듈 내부에서만 컴파일됩니다:

go
// 주의: ResponseReader 는 내부 타입 (internal/types 패키지) 입니다.
// 이 코드는 httpc 모듈 외부에서 컴파일할 수 없습니다.
// 대부분의 사용자는 RetryConfig 와 WithMaxRetries 로 재시도를 설정해야 합니다.

type MyRetryPolicy struct {
    maxRetries int
}

func (p *MyRetryPolicy) ShouldRetry(resp ResponseReader, err error, attempt int) bool {
    if attempt >= p.maxRetries {
        return false
    }
    if err != nil {
        return true
    }
    return resp.StatusCode() >= 500
}

func (p *MyRetryPolicy) GetDelay(attempt int) time.Duration {
    return time.Second * time.Duration(1<<attempt)
}

func (p *MyRetryPolicy) MaxRetries() int {
    return p.maxRetries
}

핵심 타입

RequestMutator

go
type RequestMutator interface {
    // 읽기 메서드
    Method() string
    URL() string
    Headers() map[string]string
    QueryParams() map[string]any
    Body() any
    Timeout() time.Duration
    MaxRetries() int
    Context() context.Context
    Cookies() []http.Cookie
    FollowRedirects() *bool
    MaxRedirects() *int
    StreamBody() bool

    // 쓰기 메서드
    SetMethod(string)
    SetURL(string)
    SetHeaders(map[string]string)
    SetHeader(key, value string)
    SetQueryParams(map[string]any)
    SetBody(any)
    SetTimeout(time.Duration)
    SetMaxRetries(int)
    SetContext(context.Context)
    SetCookies([]http.Cookie)
    SetFollowRedirects(*bool)
    SetMaxRedirects(*int)
    SetStreamBody(bool)
}

미들웨어에서 사용하며, 요청에 대한 읽기/쓰기 접근을 제공합니다. 내부 인터페이스 RequestReader 와 RequestWriter 로 구성됩니다. 전체 메서드 계약과 읽기/쓰기 예제는 요청과 응답 뮤테이터를 참조하세요.

ResponseMutator

go
type ResponseMutator interface {
    // 읽기 메서드
    StatusCode() int
    Status() string
    Proto() string
    Headers() http.Header
    Body() string
    RawBody() []byte
    ContentLength() int64
    Duration() time.Duration
    Attempts() int
    Cookies() []*http.Cookie
    RedirectChain() []string
    RedirectCount() int
    RequestHeaders() http.Header
    RequestURL() string
    RequestMethod() string

    // 쓰기 메서드
    SetStatusCode(int)
    SetStatus(string)
    SetProto(string)
    SetHeaders(http.Header)
    SetBody(string)
    SetRawBody([]byte)
    SetContentLength(int64)
    SetDuration(time.Duration)
    SetAttempts(int)
    SetCookies([]*http.Cookie)
    SetRedirectChain([]string)
    SetRedirectCount(int)
    SetRequestHeaders(http.Header)
    SetRequestURL(string)
    SetRequestMethod(string)
    SetHeader(key string, values ...string)
}

미들웨어에서 사용하며, 응답에 대한 읽기/쓰기 접근을 제공합니다. 내부 인터페이스 ResponseReader 와 ResponseWriter 로 구성됩니다. 전체 메서드 계약은 요청과 응답 뮤테이터를 참조하세요.

Handler

go
type Handler func(ctx context.Context, req RequestMutator) (ResponseMutator, error)

요청 처리 함수 서명입니다.

MiddlewareFunc

go
type MiddlewareFunc func(Handler) Handler

미들웨어 함수 서명으로, 다음 Handler 를 받아 래핑된 Handler 를 반환합니다.

인증서 고정

인증서 고정 (Certificate Pinning) 은 TLS 핸드셰이크 단계에서 서버 인증서가 사전에 고정된 공개키/인증서와 일치하는지 검증합니다. 신뢰할 수 있는 CA 가 침해되더라도 핸드셰이크가 거부되어 중간자 공격을 방어할 수 있습니다.

CertificatePinner

go
type CertificatePinner = security.CertificatePinner

인증서 고정기 인터페이스. 아래 생성자로 생성한 후 SecurityConfig.CertificatePinner 필드 (Config.Security로 접근) 에 할당합니다:

go
pinner, err := httpc.NewSPKIHashPinner(
    "YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2fuihg=", // 현재 키
    "C5+lpZ7tcVwmwQIMcRtPbsQtWLABXhQzejna0wHFr8M=", // 백업 키 (로테이션)
)
if err != nil {
    log.Fatal(err)
}

cfg := httpc.DefaultConfig()
cfg.Security.CertificatePinner = pinner
client, err := httpc.New(cfg)

TIP

Pinner 구현은 동시에 안전하므로 동일한 Config로 생성된 여러 클라이언트가 공유할 수 있습니다 (딥 카피 시 참조로 전달되며 복제되지 않음). 고급 사용자는 이 인터페이스를 직접 구현하여 커스텀 고정 전략 (예: 공개키 대신 전체 인증서 고정) 을 지원할 수도 있습니다.

NewSPKIHashPinner

go
func NewSPKIHashPinner(hashes ...string) (CertificatePinner, error)

하나 이상의 base64 인코딩된 SHA-256 해시 값 (DER 인코딩된 SubjectPublicKeyInfo/SPKI에 대한) 으로 인증서 고정기를 생성합니다. 이는 가장 널리 사용되는 고정 형식 (HPKP 가 채택) 이며 권장되는 방식입니다.

여러 해시를 전달하면 키 로테이션을 지원합니다 — 대상 공개키가 고정된 해시 중 어느 하나와 일치하기만 하면 핸드셰이크가 성공합니다.

다음 명령으로 인증서에서 해시를 생성합니다:

bash
openssl x509 -in cert.pem -pubkey -noout | openssl pkey -pubin -outform der \
  | openssl dgst -sha256 -binary | openssl enc -base64

유효한 해시가 제공되지 않았거나, 해시가 유효한 base64 가 아닌 경우 오류를 반환합니다.

NewPublicKeyPinner

go
func NewPublicKeyPinner(publicKeys ...[]byte) (CertificatePinner, error)

하나 이상의 DER 인코딩된 PKIX 공개키 (x509.MarshalPKIXPublicKey가 반환) 로 인증서 고정기를 생성합니다. 내부적으로 각 공개키에 대해 SHA-256 해시를 계산합니다; 원본 공개키 바이트를 이미 보유하고 있다면 NewSPKIHashPinner보다 편리한 선택입니다.

유효한 공개키가 제공되지 않은 경우 오류를 반환합니다.

NewCertificatePinnerChain

go
func NewCertificatePinnerChain(pinners ...CertificatePinner) CertificatePinner

여러 고정기를 하나로 조합합니다. 래핑된 고정기 중 어느 하나라도 해당 인증서를 수락하면 인증서가 수락됩니다. 여러 고정 전략을 동시에 지원하거나, 서로 다른 생성자로 구축한 로테이션 키를 조합할 때 사용합니다.

인수 없음 동작: 모든 인증서 허용

인수를 전달하지 않으면 빈 체인을 반환하며, 이 체인은 어떤 인증서도 검증하지 않습니다 (검증 로직이 직접 nil을 반환합니다) — 즉 모든 인증서를 허용하며, 인증서 고정을 비활성화한 것과 동일합니다 (소스 코드 internal/security/certpin.go의 주석은 "No pinners means no pinning"). 이는 "pinner 가 없으면 거부한다"는 직관과 반대되는 fail-open 동작입니다. 항상 최소 하나의 유효한 pinner 를 전달하고, 인수 없음 동작에 의존하지 마세요.

더 읽어보기

인증서 고정의 완전한 가이드 (해시 생성, 키 로테이션 전략, 프로덕션 배포) 는 TLS 인증서 고정을 참조하세요.

관련 페이지

타입상세 참조
Result / RequestInfo / ResponseInfo / RequestMetaResult
SessionManager 메서드세션 관리
DomainClient 구현도메인 클라이언트
DownloadConfig / DownloadResult파일 다운로드
ClientError / ErrorType / 오류 변수오류 타입
FormData / FileData / BodyKind상수와 타입