route 지시문
route 지시문 (그룹을 그대로 평가)
route 지시문은 지시문 그룹을 문자 그대로, 하나의 단위로 평가해요. route 블록 안의 지시문은 내부적으로 재정렬되지 않아요.
출처: Caddy 공식 문서
본문
지시문 그룹을 문자 그대로, 하나의 단위로 평가해요.
route 블록에 포함된 지시문은 내부적으로 재정렬되지 않아요. route 블록에는 HTTP 핸들러 지시문(체인에 핸들러나 미들웨어를 추가하는 지시문)만 사용할 수 있어요.
이 지시문은 하위 지시문도 일반 지시문이라는 점에서 특별한 경우예요.
문법 (Syntax)
route [<matcher>] {
<directives...>
}
- **<directives...>**는 route 블록 밖과 마찬가지로 한 줄에 하나씩 나열하는 지시문 또는 지시문 블록 목록이에요. 단, 이 지시문들은 재정렬되지 않아요. HTTP 핸들러 지시문만 사용할 수 있어요.
유용성 (Utility)
route 지시문은 HTTP 핸들러 체인의 일부를 절대적으로 제어해야 하는 특정 고급 사용 사례나 엣지 케이스에서 유용해요.
HTTP 미들웨어 평가 순서는 중요하기 때문에, Caddyfile은 보통 파싱 후 지시문을 재정렬해 Caddyfile을 더 쉽게 사용하게 해줘요. 입력한 순서에 신경 쓸 필요가 없어요.
내장 순서가 대부분의 사이트와 호환되지만, 때로는 사이트 전체든 일부든 순서를 수동으로 제어해야 할 때가 있어요. 그럴 때 쓰는 게 route 지시문이에요.
예를 들어 두 종료(terminating) 핸들러를 생각해볼게요: redir와 file_server. 둘 다 응답을 클라이언트에 쓰고 체인의 다음 핸들러를 호출하지 않아서, 특정 요청에는 하나만 실행돼요. 그러면 어느 것이 먼저 올까요? 보통 redir가 file_server보다 먼저 실행돼요. 대부분 특정 경우에만 리다이렉트하고 일반 경우에는 파일을 서빙하고 싶기 때문이에요.
하지만 첫 번째 지시문(file_server)의 matcher가 두 번째(redir)보다 더 구체적인 경우가 있을 수 있어요. 즉 일반 경우에는 리다이렉트하고, 특정 파일만 서빙하고 싶은 거예요.
그래서 이렇게 Caddyfile을 시도할 수 있지만(예상대로 동작하지 않을 거예요!):
example.com {
file_server /specific.html
redir https://anothersite.com{uri}
}
문제는 지시문이 정렬된 후에 redir가 file_server보다 앞에 온다는 거예요.
하지만 이 경우 redir의 matcher(암시적 *)는 file_server의 matcher(*가 /specific.html의 상위 집합)의 상위 집합이에요.
다행히 해결책은 간단해요. 그 두 지시문을 route 블록으로 감싸면 file_server가 redir보다 먼저 실행되도록 보장돼요:
example.com {
route {
file_server /specific.html
redir https://anothersite.com{uri}
}
}
또 다른 방법은 두 matcher를 상호 배타적으로 만드는 거지만, 조건이 한두 개보다 많으면 빠르게 복잡해질 수 있어요. route 지시문으로는 두 핸들러가 모두 종료 핸들러라서 그 상호 배타성이 암시적이에요.
이제 순서가 문자 그대로 받아들여지므로 file_server가 redir보다 먼저 체인에 들어가요.
비슷한 지시문 (Similar directives)
HTTP 핸들러 지시문을 감쌀 수 있는 다른 지시문도 있는데, 전달하려는 동작에 따라 각각 용도가 달라요:
-
handle은route처럼 다른 지시문을 감싸는데 두 가지 차이가 있어요: 1) handle 블록은 서로 상호 배타적이고, 2) handle 안의 지시문은 정상적으로 재정렬돼요. -
handle_path는handle과 동일하지만, 핸들러를 실행하기 전에 요청에서 접두사를 제거해요. -
handle_errors는handle과 비슷하지만, 요청 처리 중 Caddy가 에러를 만났을 때만 호출돼요.
예시 (Examples)
/api 요청은 그대로 프록시하고, 그 외 요청은 파일 일치 여부에 따라 재작성하되 그 외엔 /index.html로 재작성해요. 그다음 해당 파일을 서빙해요.
try_files는 reverse_proxy보다 지시문 순서가 높아서 보통 더 높게 정렬되어 먼저 실행돼요. 이러면 API 요청이 모두 /index.html로 재작성되어 /api*와 일치하지 않게 되고, 어떤 요청도 프록시되지 않아 대신 file_server에서 404가 나요. 전부 route로 감싸면 reverse_proxy가 요청이 재작성되기 전에 항상 먼저 실행되도록 보장돼요.
example.com {
root /srv
route {
reverse_proxy /api* localhost:9000
try_files {path} /index.html
file_server
}
}
이 문제의 유일한 해결책은 아니에요. handle 블록 한 쌍을 사용할 수도 있는데, 첫 번째가 /api*를 reverse_proxy에 매칭하고 두 번째가 폴백 역할을 하며 파일을 서빙해요. SPA의 이 예시를 참고해요.