본문 바로가기
WIKI 기술 지식 베이스

플레이스홀더

원문 보기 위키 갱신

플레이스홀더 (Placeholders)

출처: Caddy 공식 문서

본문

Caddy에서 플레이스홀더는 각 개별 플러그인이 필요할 때 처리해요. 어디서든 자동으로 동작하지는 않아요.

즉, 여러분의 플러그인이 플레이스홀더를 지원하길 원한다면 명시적으로 그 지원을 추가해야 해요.

플레이스홀더에 아직 익숙하지 않다면 여기부터 읽어 보세요!

플레이스홀더 개요

플레이스홀더는 {foo.bar} 형식의 문자열로, 동적 설정 값으로 사용되며 나중에 런타임에 평가돼요.

{$FOO}처럼 달러 기호로 시작하는 Caddyfile 환경 변수 치환은 Caddyfile 파싱 시점에 평가되므로 플러그인이 처리할 필요가 없어요. 이들은 같은 { } 문법을 공유하지만 플레이스홀더가 아니에요.

따라서 {env.HOST}(전역 플레이스홀더)가 {$HOST}(Caddyfile env-var 치환)와 본질적으로 다르다는 점을 이해하는 것이 중요해요.

예를 들어 다음 Caddyfile을 봐요:

:8080 {
	respond {$HOST} 200
}

:8081 {
	respond {env.HOST} 200
}

HOST=example caddy adapt로 이 Caddyfile을 JSON으로 어댑트하면 다음을 얻을 수 있어요:

{
  "apps": {
    "http": {
      "servers": {
        "srv0": {
          "listen": [":8080"],
          "routes": [
            {
              "handle": [
                {
                  "body": "example",
                  "handler": "static_response",
                  "status_code": 200
                }
              ]
            }
          ]
        },
        "srv1": {
          "listen": [":8081"],
          "routes": [
            {
              "handle": [
                {
                  "body": "{env.HOST}",
                  "handler": "static_response",
                  "status_code": 200
                }
              ]
            }
          ]
        }
      }
    }
  }
}

특히 srv0와 srv1 양쪽의 "body" 필드를 살펴보세요.

srv0는 {$HOST}(Caddyfile env-var 치환)를 사용했으므로, JSON 설정을 생성할 때 Caddyfile 파싱 시점에 처리되어 값이 example이 됐어요.

srv1은 {env.HOST}(전역 플레이스홀더)를 사용했으므로, JSON으로 어댑트할 때 그대로 남아 있어요.

이는 JSON 설정을 작성하는 사용자(Caddyfile을 쓰지 않는)는 {$ENV} 문법을 사용할 수 없다는 뜻이에요. 그런 이유로 플러그인 작성자는 설정이 프로비저닝될 때 플레이스홀더를 치환하는 지원을 구현하는 것이 중요해요. 아래에 설명돼 있어요.

플레이스홀더 지원 구현하기

UnmarshalCaddyfile()에서는 플레이스홀더를 처리하면 안 돼요. 대신 Provision() 단계나 모듈 실행 중(예: HTTP 핸들러의 ServeHTTP(), 매처의 Match() 등)에 caddy.Replacer를 사용해 나중에 플레이스홀더를 치환해야 해요.

예시

여기서는 새로 생성한 replacer를 사용해 플레이스홀더를 처리해요. 이 replacer는 {env.HOST} 같은 전역 플레이스홀더에는 접근할 수 있지만, 프로비저닝은 설정이 로드될 때 일어나고 요청 중에 일어나지 않기 때문에 {http.request.uri} 같은 HTTP 플레이스홀더에는 접근하지 못해요.

func (g *Gizmo) Provision(ctx caddy.Context) error {
	repl := caddy.NewReplacer()
	g.Name = repl.ReplaceAll(g.Name,"")
	return nil
}

여기서는 ServeHTTP 중에 요청 컨텍스트 r.Context()에서 replacer를 가져와요. 이 replacer는 전역 플레이스홀더와 {http.request.uri} 같은 요청별 HTTP 플레이스홀더 모두에 접근할 수 있어요.

func (g *Gizmo) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
	repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
	_, err := w.Write([]byte(repl.ReplaceAll(g.Name,"")))
	if err != nil {
		return err
	}
	return next.ServeHTTP(w, r)
}

더 알아보기 (Learn more)