앞선 포스팅에서 뷰가 왜 등장하게 되었는지 그 배경에 대해 잠깐 언급했던 바가 있었다. HTML코드를 HTTP response에 추가해서 화면에 보여주는 작업을 조금 더 단순화하기 위해 였다. 아무래도 코드 자체를 @ResponseBody로 추가하면 너무 하드코딩이었으니깐..
뷰(view)는 사용자 인터페이스(UI)의 구성을 도와주는 일종의 템플릿이라 할 수 있다. 웹 어플리케이션의 뷰는 주로 HTML, XML, JSP 등을 통해 동적인 웹페이지를 생성한다.
다시 말해, Spring MVC에서의 뷰는 컨트롤러가 처리한 모델(비즈니스 코드)의 데이터를 화면에 어떻게 표시할지를 정의한다. 뷰는 이 데이터를 HTML 등의 마크업 언어로 렌더링해, 최종적으로 사용자에게 제공해준다.
화면에 보여주고자 하는 내용은,
<html>
<head>
<title>My first HTML Page</title>
</head>
<body>
My first HTML page with body
</body>
</html>
이 코드이다!
하고 싶은 것은 "localhost:8080/say-hello-jsp" url로 가면, sayHello.jsp로 리다이렉션하는 것이다.
모든 JSP파일은 관례상 특정 폴더 안에서 만들어진다.
spring에서는 src/main/webapp/ 밑에 jsp파일을 위치하게 하면 스프링에서 바로 jsp파일을 찾아 실행시킬 수 있다.
나는 /src/main/resources/META-INF/resources/WEB-INF/jsp/ 폴더 밑에 jsp 파일을 위치시키기로 했다.
WEB-INF 안에 '뷰'들을 만들 수 있는데, 조금 더 보기 깔끔하기 하기 위해 jsp 폴더를 하나 더 만들어, 해당 jsp 폴더 안에 모든 뷰들을 만들고자 했다.
그리고 sayHello.jsp파일에 앞선 HTML 코드를 넣어준다. 하드코딩했던 앞선 포스팅보다 훨씬 깔끔해진 것을 볼 수 있다!
이제 @Controller로 설정해둔 SayHelloController의 메서드인 sayHelloJSP메서드에서 sayHello.jsp로 페이지를 리다이렉션할 것이다.
만일 다음과 같이 리다이렉션하고 싶은 뷰 파일 3개가 있다고 하자.
..너무 길다!
그래서 파일명 빼고 동일한 앞부분을 전부 application.properties에서 해당 부분을 변수화했다.
문자그대로 view의 접두어(prefix), 접미어(suffix)를 설정해두었다.
Spring MVC에서 @Controller 어노테이션을 사용한 클래스의 메서드가 뷰 이름을 반환하면, 해당하는 뷰를 렌더링하도록 동작한다.
이때 렌더링이란, 웹 어플리케이션에서 서버측(이 예제에서는 JSP)에서 생성된 데이터나 정보를 클라이언트에 보여주기 위해 HTML, CSS 및 기타 컨텐츠로 변환하는 프로세스이다.
@Controller
public class SayHelloController {
@RequestMapping("/say-hello-jsp")
public String sayHelloJSP() {
String str = "sayHello";
return str;
}
}
다시 말하자면, /say-hello-jsp에 웹 request가 들어올 때, sayHelloJSP 메서드에서 반환한 뷰 이름인 "sayHello"를 기반으로 실제 JSP파일의 경로가 결정되어, (application에서의 prefix, suffix를 참고해서) 해당 JSP파일이 렌더링된다.
그렇다면 @Controller의 메서드의 리턴값이 뷰 파일명과 다르다면 어떻게 될까?
궁금해서 해봤다.
@Controller
public class SayHelloController {
@RequestMapping("/say-hello-jsp")
public String sayHelloJSP() {
String str = "sayHello1";
return str;
}
}
메서드의 리턴값은 sayHello1인데, 뷰 파일명이 sayHello.jsp라면
어김없이 페이지 404에러가 뜬다. 애초에 sayHello1.jsp파일을 찾지 못할테니 당연한 사실이다.
다시 뷰의 리턴값은 sayHello.jsp로 고쳐서, 뷰의 파일명과 맞춰주었다.
하지만 실행결과 에러였다. 🙃
왜 에러가 났는지 분석해보자. SayHelloController코드에서 import된 부분을 보면, 전부 org.springframework까지 동일하기에 해당 부분의 모든 debug 레벨에서 로깅을 했다.
sayHello 뷰로 리다이렉션이 되는 걸 볼 수 있었다.
pom.xml의 의존성 부분을 살펴보다 apache tomcat embed 부분 의존성 주입을 추가해줬다.
이때 주의할 점은, 아무리 devtools 의존성 주입을 해서 코드 변경 시, 저장만 하면 자동실행이 된다 하더라도, pom.xml에서 의존성을 추가하면 항상 어플리케이션을 멈췄다가 다시 실행해주어야 한다.
실행결과..
여전히 에러가 난 걸 확인할 수 있었다..
위 에러메세지의 페이지 404에러는, 해당 경로에 대한 매핑이나 리소스를 찾을 수 없을 때 나타나는 에러다.
그래서 해당 JSP 파일의 경로 설정이 잘못되었나 싶어, application.properties의 prefix와 suffix 경로로 돌아가 다음과 같이 수정했다.
그랬더니 실행은 잘 됐다. 왜 그럴까?
이 에러는 스프링부트에서 JSP파일을 찾는 방법과 관련있다.
기본적으로 스프링부트는 /src/main/webapp/
디렉터리를 classpath의 루트
로 간주한다. 그리고 웹 어플리케이션의 정적 자원 및 JSP파일은 WEB-INF
디렉터리 내부에 위치한다. 이것이 일반적인 웹 어플리케이션의 구조이다.
따라서 스프링부트에서는 spring.mvc.view.prefix를 /WEB-INF/jsp/ 로 설정하여, classpath의 루트에서 시작해 (spring.mvc.view.prefix를 참고해) WEB-INF/jsp/ 디렉터리 아래에 있는 JSP 파일을 찾게 된다.
@Controller
public class SayHelloController {
@RequestMapping("/say-hello-jsp")
public String sayHelloJSP() {
String str = "sayHello";
return str;
}
}
정리해보자.
스프링이 @Controller로 해당 SayHelloController 인스턴스를 관리해준다. @RequestMapping으로 /say-hello-jsp url로 http request가 들어오면 해당 메서드를 실행해준다. 해당 메서드 sayHelloJSP에서는 리턴값으로 sayHello 문자열을 리턴하는데, 그렇게 생각하면 안된다.
Spring MVC에서 @Controller 어노테이션의 클래스의 메서드가 뷰 이름을 반환하면, 해당 뷰를 렌더링하도록 동작한다.
그렇기에 sayHelloJSP 메서드에서 반환한 뷰 이름 "sayHello"를 기반으로 실제 JSP 파일의 경로가 결정되어 해당 JSP 파일로 렌더링된다.
application.properties에서 정의한 패키지 경로에 위치한 뷰를 찾아내, 클라이언트는 서버로부터 받은 HTML코드를 렌더링해서 시각화해서 보여준다.
이 시리즈는 Udemy 강의의 내용을 정리한 것입니다.
https://www.udemy.com/course/spring-boot-and-spring-framework-korean/