/welcome.html을 고쳤는데 브라우저에는 예전 문구가 남아 있다. 캐시 문제로 생각하기 전에 같은 URL을 처리하는 Controller가 있는지 확인해 보자. 정적 파일과 Controller가 정확히 같은 경로를 가리킬 때 어느 쪽 응답이 선택되는지 따라가면 원인이 보인다.
Controller 매핑은 요청을 가장 먼저 해석한다
일반적인 Spring Boot MVC 설정에서는 Controller 매핑이 정적 리소스 처리보다 먼저 선택된다. 아래 예제에는 src/main/resources/static/welcome.html 파일이 있고, Controller도 같은 /welcome.html에 응답한다고 가정한다.
두 응답을 구별하기 위해 Controller는 controller page, 정적 파일은 static page를 내보내게 한다.
@Controller
class WelcomeController {
@GetMapping("/welcome.html")
@ResponseBody
String welcome() {
return "controller page";
}
}@ResponseBody를 붙였으므로 이 문자열은 뷰 이름이 아니라 HTTP 본문이다. 같은 URL의 정적 파일이 있어도 이 매핑이 살아 있다면 /welcome.html의 응답 본문은 controller page다. 매핑을 제거하거나 경로를 바꾼 뒤 다시 요청해야 정적 파일의 static page를 볼 수 있다.
정적 파일은 매핑이 없을 때 후보가 된다
Controller 매핑이 없으면 Spring Boot의 정적 리소스 위치가 후보가 된다. src/main/resources/static 아래의 파일은 기본적으로 URL 경로와 연결된다.
src/main/resources/static/welcome.html
└── /welcome.html이번에는 같은 /welcome.html 요청에서 Controller만 제거한 것이다. 응답이 static page로 바뀌면 파일이 없어졌던 게 아니라 앞선 매핑이 가리고 있었던 셈이다. 반면 /welcome은 다른 URL이므로 이 파일을 자동으로 가리킨다고 기대하면 안 된다.
충돌처럼 보일 때는 경로와 응답을 분리해 확인한다
처음에는 Controller와 정적 파일을 둘 다 두고 /welcome.html을 요청한다. 다음에는 Controller의 매핑만 제거하고 같은 URL을 다시 요청한다. 요청 경로까지 바꾸면 어느 설정 때문에 결과가 달라졌는지 판단하기 어렵다.
같은 요청 /welcome.html | 예상 본문 | 먼저 확인할 곳 |
|---|---|---|
| Controller 매핑 있음 | controller page | 해당 경로의 매핑과 @ResponseBody |
| Controller 매핑 제거 | static page | 정적 파일의 실제 위치와 이름 |
둘 다 기대와 다르면 요청 경로·프로파일별 설정·리소스 핸들러 설정을 확인한다. Controller가 뷰 이름을 반환하도록 바꾼 경우에는 @ResponseBody를 제거하고 템플릿 위치를 따로 확인해야 한다. 응답 본문과 뷰 이름은 같은 문자열이어도 다른 의미다.
경로 이름을 공유하지 않는 설계가 더 안전하다
정적 파일은 assets/, docs/처럼 성격이 드러나는 경로에 두고, 화면 진입점은 Controller URL로 정하면 유지보수가 편하다. 동일한 URL을 두 방식에 기대면 변경하는 사람은 매번 탐색 순서를 떠올려야 한다.
테스트에서도 '200이 왔다'만 보지 말고 기대한 본문 또는 뷰 이름을 확인한다.
mockMvc.perform(get("/welcome.html"))
.andExpect(status().isOk())
.andExpect(content().string("controller page"));이 테스트는 Controller 매핑이 있는 설정에서만 기대한 결과다. 매핑을 제거한 경우에는 별도 테스트에서 static page가 나오는지 확인한다. 정적 콘텐츠는 파일을 제공하는 데 알맞고, Controller는 입력 검증·데이터 조회·권한처럼 요청별 판단이 필요할 때 알맞다. 같은 URL을 두 책임에 동시에 맡기지 않는 편이 안전하다.
핵심 요약
같은 /welcome.html 요청에 Controller 매핑이 있으면 그 메서드가 먼저 응답하고, 매핑을 없애면 정적 파일이 후보가 된다. 두 경우의 응답 문구를 구별해 확인하자. /welcome은 별도 URL이며, 정적 자산과 동적 화면의 경로를 의도적으로 나누면 이런 충돌을 피하기 쉽다.

