2.1 뷰 템플릿과 MVC 패턴
2.1.1 뷰 템플릿이란
뷰 템플릿(View Template)은 화면을 담당하는 기술로, 웹 페이지(View)를 하나의 틀(Template)로 만들고 여기에 변수를 삽입해 서로 다른 페이지로 보여줍니다.
1장에서 스프링 부트 프로젝트를 만들 때 머스테치(Mustache)라는 도구를 추가했습니다. 이 도구가 바로 뷰 템플릿을 만드는 도구입니다.
2.1.2 MVC 패턴
화면을 담당하는 뷰 템플릿은 간단히 '뷰'라고도 합니다. 뷰는 컨트롤러와 모델이라는 두 동료가 있습니다.
컨트롤러(Controller)는 클라이언트의 요청에 따라 서버에서 이를 처리하는 역할을 하고, 모델(Model)은 데이터를 관리하는 역할을 합니다. 이처럼 웹 페이지를 화면에 보여주고(View), 클라이언트의 요청을 받아 처리하고(Controller), 데이터를 관리하는(Model) 역할을 나누는 기법을 MVC 패턴(Model-View-Controller Pattern)이라고 합니다.
2.2 MVC 패턴을 활용해 뷰 템플릿 페이지 만들기
2.2.1 뷰 템플릿 페이지 만들기
인텔리제이를 실행하고 1장에서 만들었던 firstproject를 엽니다.
프로젝트 탐색기에서 src > main > resources를 펼치면 static과 templates 디렉터리가 있습니다. templates 디렉터리에 뷰 템플릿을 만듭니다.
1. templates 디렉터리에서 마우스 오른쪽 버튼을 누르고 New -> File을 선택합니다. 파일명은 greetings.mustache로 입력하고 Enter를 누릅니다.
참고로 확장자 mustache는 뷰 템플릿을 만드는 도구, 즉 뷰 템플릿 엔진을 의미합니다.
머스테치 파일의 기본 위치는 src > main > resources > templates 입니다. 이 위치에 머스테치 파일을 저장하면 스프링 부트에서 자동으로 로딩합니다.
머스테치 외의 템플릿 엔진으로는 Thymeleaf, JSP 등이 있습니다.
2. 머스테치(.mustache) 파일을 지원하는 플러그인을 발견했다고 뜹니다. 머스테치 플러그인을 설치하기 위해 Install Handlebars/Mustache plugin을 클릭하고 Install 창이 뜨면 [OK] 버튼을 클릭합니다. 그러면 머스테치 플러그인을 설치합니다.
메뉴에서 선택해 머스테치 플러그인을 설치하는 방법
Settings를 클릭합니다. 왼쪽 목록에서 plugins를 선택하고 오른쪽에 [Marketplace] 탭을 클릭한 후 mustache를 검색합니다. 검색 결과에서 Handlebars/Mustache를 선택하고 [Install] 버튼을 클릭합니다. 설치가 끝나면 [OK] 버튼을 클릭합니다.
3. 빈 화면의 greetings.mustache 파일이 뜹니다. 제일 윗줄에 doc를 입력한 다음 Tab 키를 누르면 기본 HTML 코드가 자동으로 작성됩니다. 본문에 다음과 같이 <h1>길동님, 반갑습니다!</h1>를 추가합니다.
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Document</title>
</head>
<body>
<h1>길동님, 반갑습니다!</h1>
</body>
</html>
2.2.2 컨트롤러 만들고 실행하기
뷰 템플릿 페이지를 templates 디렉터리에 만들었듯이 컨드롤러도 만들어야 할 위치가 있습니다.
프로젝트 탐색기에서 src > main > java 디렉터리에 가면 기본 패키지인 com.example.firstproject가 존재합니다. 컨트롤러는 여기에 하나의 패키지로 만듭니다.
1. com.example.firstproject에서 마우스 오른쪽 버튼을 누르고 메뉴에서 New -> Package를 선택합니다. 패키지명은 기본으로 입력된 패키지명 뒤에 controller를 추가해 com.example.firstproject.controller로 만듭니다.
기본 패키지 안에 controller 패키지가 생성됩니다. 계속해서 controller 패키지에 자바 클래스를 만들겠습니다.
2. controller 패키지에서 마우스 오른쪽 버튼을 누르고 메뉴에서 New -> Java Class를 선택합니다. 클래스 명은 FirstController라고 입력합니다.
3. 자동 생성된 FirstController 코드가 열리면 다음과 같이 작성합니다.
- 이 클래스가 컨트롤러임을 선언하는 @Controller 어노테이션을 작성합니다. 이렇게 하면 Controller 클래스 패키지(org.springframework.stereotype.Controller)가 자동으로 임포트됩니다.
- 반환형이 문자열인 niceToMeetYou() 메서드를 선언합니다. 그리고 공백 문자열 (" ")을 반환하도록 return ""; 문을 추가합니다. 이 반환문을 이용해 앞에서 만든 greetings.mustache 페이지를 반환하겠습니다.
package com.example.firstproject.controller;
import org.springframework.stereotype.Controller;
@Controller
public class FirstController {
public String niceToMeetYou(){
return "";
}
}
어노테이션(annotation)이란 소스 코드에 추가해 사용하는 메타 데이터의 일종입니다. 메타 데이터는 프로그램에서 처리해야 할 데이터가 아니라 컴파일 및 실행 과정에서 코드를 어떻게 처리해야 할지 알려 주는 추가 정보입니다. 자바에서 어노테이션은 앞에 @ 기호를 붙여 사용합니다.
4. niceToMeetYou() 메서드로 greetings.mustache 페이지를 반환하려면 파일 이름인 greetings만 반환값으로 적어 주면 됩니다. 즉 return "greetings"; 로 적어 주면 서버가 알아서 templates 디렉터리에서 greeings.mustache 파일을 찾아 웹 브라우저로 전송합니다.
public String niceToMeetYou(){
return "greetings";
}
5. FirstController에 가보면 niceToMeetYou() 메서드가 greetings.mustache를 반환하기 위해 return greetings; 라고 써 줬습니다. 그런데 빠진 게 하나 있습니다. 페이지(greetings.mustache)를 반환해 달라는 URL 요청을 접수하는 부분입니다.
- niceToMeetYou() 메서드 앞에 @GetMapping()을 추가합니다. 그러면 자동으로 org.springframework.web.bind.annotation.GetMapping 패키지가 임포트됩니다.
- @GetMapping의 괄호 안에 URL 주소인 "/hi"를 넣습니다.
package com.example.firstproject.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class FirstController {
@GetMapping("/hi")
public String niceToMeetYou(){
return "greetings";
}
}
이는 웹 브라우저에서 localhost:8080/hi로 접속하면 greetings.mustache 파일을 찾아 반환하라는 뜻입니다.
다시 정리하자면 컨트롤러를 만들 때 먼저 컨트롤러를 선언(@Controller)하고, 반환값으로 보여 줄 페이지의 이름만 따서 적은 다음(return "greetings";), URL 요청을 접수해야(@GetMapping("/hi") 제대로 동작합니다.
6. 서버를 시작하고 웹 브라우저에서 localhost:8080/hi로 접속합니다. "길동님, 반갑습니다!"라는 뷰 템플릿 페이지가 잘 나옵니다.

실행 결과 한글 깨짐 현상이 발생하면 src > main > resources > application.properties 파일을 열어 다음 코드를 추가한 후 서버를 재시작합니다. 웹 브라우저를 새로 고침해 보면 한글이 깨지지 않고 잘 출력됩니다.
server.servlet.encoding.force=true
2.2.3 모델 추가하기
1. 앞에서 출력한 "길동님, 반갑습니다!"에서 '길동'을 다른 이름으로 바꾸고 싶다면 어떻게 해야 할까요? 예를 들어 '길동' 대신 username이라는 변수를 써서 어떨 때는 '길동', 어떨 때는 'gildong'이라고 나오게 만드는 거죠.
이럴 땐 머스테치 문법을 사용해 뷰 템플릿 페이지에 변수를 삽입합니다. 형식은 다음과 같습니다. 변수명을 적고 두 겹의 중괄호로 감쌉니다.
형식 ) {{변수명}}
greetings.mustache에서 길동님을 {{username}}님 이라고 수정합니다. 이렇게 이름 대신 들어갈 변수명을 쓰면 변숫값에 따라 결과가 다르게 출력됩니다.
<body>
<h1>{{username}}님, 반갑습니다!</h1>
</body>
2. 모델은 MVC 패턴에서 데이터를 관리하는 역할을 합니다.
모델은 컨트롤러의 메서드에서 매개변수로 받아 옵니다. FirstController로 가서 niceToMeetYou() 메서드에 Model 타입의 model 매개변수를 추가합니다. 그러면 Model 클래스 패키지가 자동으로 임포트됩니다.
import org.springframework.ui.Model;
(중략)
@GetMapping("/hi")
public String niceToMeetYou(Model model){
return "greetings";
}
3. Model 클래스 패키지가 임포트됐다면 모델을 통해 변수를 등록할 수 있습니다. 모델에서 변수를 등록할 때는 addAttribute() 메서드를 사용합니다
형식 ) model.addAttribute("변수명", 변숫값) //변숫값을 "변수명"이라는 이름으로 추가
niceToMeetYou() 메서드 내부에 model.addAttribute("username", "홍길동"); 코드를 추가합니다.
public String niceToMeetYou(Model model){
model.addAttribute("username","홍길동");
return "greetings";
}
TIP - "username"과 "홍길동" 앞에 attributeName과 attributeValue:가 뜹니다. 이는 인텔리제이에서 어느 것이 변수명이고 어느 것이 변숫값인지 자동으로 보여주는 기능입니다.

4. 서버를 시작하고 웹 브라우저로 가서 localhost:8080/hi에 접속합니다.
5. 변수값을 바꿔도 잘 출력하는지 확인해 봅시다. model.addAttribute()의 변숫값을 "HongGil-dong"으로 수정합니다. 서버를 재시작 한 후 결과를 보면 변숫값이 바뀌어 출력됩니다.
model.addAttribute("username","HongGil-dong");
2.3 MVC의 역할과 실행 흐름 이해하기
웹 서비스는 클라이언트의 요청에 대한 서버의 응답으로 동작합니다. 이때 스프링 부트는 서버의 역할을 합니다.
서버는 앞에서 배웠듯이 모델, 뷰, 컨트롤러가 유기적으로 역할을 분담해 클라이언트의 요청을 처리합니다. 컨트롤러가 클라이언트의 요청을 받고, 뷰가 최종 페이지를 만들고, 모델이 최종 페이지에 쓰일 데이터를 뷰에 전달합니다.
앞에서 뷰 템플릿 페이지(greetings.mustache)가 어떤 과정을 거쳐 출력되는지 알아봤으니 이 절에서는 모델, 뷰, 컨트롤러 관점에서 그 과정을 분석해 보겠습니다.
2.3.1 /hi 페이지의 실행 흐름
localhost:8080/hi에 접속해 결과를 다시 한번 확인해 봅시다. "HongGil-dong님, 반갑습니다!"가 잘 출력됩니다. 이렇게 클라이언트가 localhost:8080/hi라고 요청하면 서버 내부에서는 컨트롤러가 요청을 받아 처리합니다.
FisrtController.java 코드를 보며 동작을 이해해 봅시다.
1. 이 파일이 컨트롤러 임을 선언합니다.
2. 클라이언트로부터 "/hi"라는 요청을 받아 접수합니다.
3. "/hi"라는 요청을 받음과 동시에 niceToMeetYou() 메서드를 수행합니다.
4. 뷰 템플릿 페이지에서 사용할 변수를 등록하기 위해 모델 객체를 매개변수로 가져옵니다.
5. 모델에서 사용할 변수를 등록합니다. 변숫값에 따라 서로 다른 뷰 템플릿 페이지가 출력됩니다.
6. 메서드를 수행한 결과로 greetings.mustache 파일을 변환합니다. 이때 return 문에는 파일 이름만 작성하면 됩니다(return "greetings";). 그러면 서버가 알아서 templates 디렉터리에 있는 해당 뷰 템플릿 페이지를 찾아 웹 브라우저로 전송합니다.
@Controller //1.컨트롤러 선언
public class FirstController {
@GetMapping("/hi") //2.URL 요청 접수
public String niceToMeetYou(Model model){ //3.메서드 수행, 4.모델 객체 가져오기
model.addAttribute("username","HongGil-dong");//5.모델 변수 등록
return "greetings"; //6.뷰 템플릿 페이지 반환
}
}
2.3.2 /bye 페이지의 실행 흐름
이번에는 localhost:8080/bye로 요청을 받았을 때 "xx님, 다음에 또 만나요!"를 출력해 보겠습니다.
/bye 요청을 받아 줄 컨트롤러를 새롭게 만들어도 되지만, 이번에는 FirstController를 그대로 사용해 보겠습니다.
1. 컨트롤러는 @Controller 내부에 @GetMapping 어노테이션을 통해 클라이언트의 요청을 받습니다.
(1) @GetMapping("/bye") 어노테이션을 추가합니다.
(2) /bye 요청을 처리할 seeYouNext() 메서드를 만듭니다.
(3) 반환값은 요청에 따라 보여 줄 뷰 템플릿 페이지를 적는데, 아직 뷰 템플릿 페이지를 만들지 않았으므로 임의로 return "goodbye"; 라고 적습니다.
@GetMapping("/bye")
public String seeYouNext(){
return "goodbye";
}
컨트롤러는 그대로 사용하기로 했지만 뷰 템플릿 페이지는 새로 만들어야 합니다. templates 디렉터리에 goodbye.mustache 파일을 만들어 봅시다.
2. templates 디렉터리에서 마우스 오른쪽 버튼을 누르고 New -> File을 선택합니다. 파일명은 goodbye.mustache라고 입력하고 Enter를 누릅니다.
3. 새 파일이 열리면 doc를 입력한 다음 tab 키를 눌러 기본 HTML 코드를 자동으로 작성합니다. 그리고 본문에 <h1>xx님, 다음에 또 만나요!</h1>를 추가합니다.
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Document</title>
</head>
<body>
<h1>xx님, 다음에 또 만나요!</h1>
</body>
</html>
4. 그런데 사용자 이름을 그때그때 변경하고 싶습니다. xx님 부분을 변수로 처리해 봅시다. 변수명은 nickname으로 합니다.
<h1>{{nickname}}님, 다음에 또 만나요!</h1>로 코드를 수정합니다.
<h1>{{nickname}}님, 다음에 또 만나요!</h1>
5. 뷰 템플릿 페이지에서 변수(nickname)를 이용하려면 이 페이지를 반환하는 컨트롤러의 메서드(seeYouNext)에 변수를 등록해야 합니다. 모델을 통해서 말이죠. 컨트롤러로 돌아와서 다음과 같이 코드를 추가합니다.
(1) SeeYouNext() 메서드의 매개변수로 model 객체를 받아 옵니다.
(2) model.addAttribute() 메서드로 등록할 변수명과 변숫값을 적어 줍니다.
@GetMapping("/bye")
public String seeYouNext(Model model){ //1.model 객체 받아 오기
model.addAttribute("nickname","홍길동"); //2.모델 변수 등록하기
return "goodbye";
}
}
이렇게 등록하면 goodbye.mustache의 {{nickname}} 코드에서 변숫값을 받을 수 있습니다.
6. 잘 동작하는지 확인하기 위해 서버를 재시작하고 localhost:8080/bye에 접속합니다. 원하는 결과가 잘 나옵니다.
정리하면 컨트롤러는 클라이언트의 요청을 @GetMapping("/bye")로 받습니다. 그리고 return 값으로 goodbye.mustache를 반환합니다. 이때 "nickname"이라는 변수를 등록하고 "홍길동"이라는 값을 연결해 goodbye.mustache 파일에서 사용할 수 있게 합니다.
이렇게 해서 /hi 페이지와 /bye 페이지의 출력 과정에서 모델, 뷰, 컨트롤러의 역할과 실행 흐름을 살펴봤습니다.
2.4 뷰 템플릿 페이지에 레이아웃 적용하기
레이아웃(layout)이란 화면에 요소를 배치하는 일을 말합니다. 웹 페이지는 같은 요소로도 어떻게 배치하느냐에 따라서, 즉 레이아웃을 어떻게 잡느냐에 따라서 다른 느낌을 줄 수 있습니다.
헤더-푸터 레이아웃(header-footer layout)은 가장 기본이 되는 레이아웃입니다. 상단의 헤더(header) 영역에는 사이트 안내를 위한 내비게이션을 넣고, 하단의 푸터(footer) 영역에는 사이트 정보를 넣습니다. 그리고 두 영역 사이에는 사용자가 볼 핵심 내용인 콘텐트(content)를 배치합니다.
/hi 페이지와 /bye 페이지에 헤더-푸터 레이아웃을 적용하는 예제를 실습해 보겠습니다.
2.4.1 /hi 페이지에 헤더-푸터 레이아웃 적용하기
greetings.mustache 페이지르 쉽고 빠르게 꾸미기 위해 부트스트랩을 사용하겠습니다. 부트스트랩(Bootstrap)이란 웹 페이지를 쉽게 만들 수 있도록 작성해 놓은 코드 모음으로, 각종 레이아웃, 버튼, 입력창 등 디자인을 미리 구현해 놓은 것입니다. 사용자는 이 코드(HTML, CSS, 자바스크립트 코드)를 가져와 사용하기만 하면 되므로 편리하게 웹 페이지를 만들 수 있습니다.
1. 부트스트랩 홈페이지(https://getbootstrap.com)에 접속합니다. 오른쪽 위를 보면 현재 버전을 확인할 수 있습니다. v5.0.2를 선택합니다.
2. v5.0.2 페이지에서 스크롤을 내려 보면 스타터 템플릿(Starter template)이 보입니다. 이 코드를 보사해서 greetings.mustache 페이지의 기본 뼈대로 사용하겠습니다. [Copy] 버튼을 클릭합니다.
3. greetings.mustache 파일로 옵니다. 파일 전체 내용을 지우고 복사한 내용을 붙여 넣습니다. Option 주석은 모두 지웁니다.
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<title>Hello, world!</title>
</head>
<body>
<h1>Hello, world!</h1>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
</body>
</html>
4. greetings.mustache 페이지의 본문 영역을 3개의 레이아웃으로 나눠 보겠습니다.
(1) <!-- --> 주석 표기를 이용해 기존에 코드가 있던 부분은 content, 그 위는 navigation, 그 아래는 site info의 3단 구조로 잡습니다.
(2) content는 Hello, world!를 지우고 <h1>{{username}}님, 반갑습니다!</h1>로 수정합니다.
<body>
<!-- navigation -->
<!-- content -->
<h1>{{username}}님, 반갑습니다!</h1>
<!-- site info -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
</body>
5. 여기까지 잘 출력되는지 확인하기 위해 서버를 재시작 하고 localhost:8080/hi로 접속합니다. 원하는 결과가 잘 출력됩니다.
6. 콘텐트 영역을 만들었으니 헤더 영역에 내비게이션 바를 추가하겠습니다. 내비게이션 바도 부트스트랩에서 코드를 복사해 사용합니다. 부트스트랩 홈페이지(https://getbootstrap.com)에 다시 가서 버전을 v5.0.2로 선택한 후, 검색창에 'navbar'를 입력한 후 목록이 뜨면 Navbar를 선택합니다.
7. 스크롤을 내려 보면 내비게이션 바의 모습을 볼 수 있습니다. 내비게이션 바는 주로 사용하는 메뉴와 검색창으로 구성돼 있습니다. 웹 브라우저의 크기에 따라 모양이 바뀌는 반응형 디자인이므로 브라우저의 너비를 줄이면 메뉴와 검색창이 사라지고 너비를 늘리면 다시 나타납니다.
[Copy] 버튼을 클릭해 코드를 복사합니다.
8. greetings.mustache 파일로 와서 <!-- navigation --> 주석 아래에 붙여 넣습니다.
<!-- navigation -->
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="#">Navbar</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="#">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Link</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Dropdown
</a>
<ul class="dropdown-menu" aria-labelledby="navbarDropdown">
<li><a class="dropdown-item" href="#">Action</a></li>
<li><a class="dropdown-item" href="#">Another action</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#">Something else here</a></li>
</ul>
</li>
<li class="nav-item">
<a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disabled</a>
</li>
</ul>
<form class="d-flex">
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
<button class="btn btn-outline-success" type="submit">Search</button>
</form>
</div>
</div>
</nav>
9. 서버를 재시작한 후 localhost:8080/hi에서 새로 고침합니다. 웹 페이지 상단에 내비게이션 바가 잘 추가됐습니다.
10. 같은 방법으로 푸터 영역에 사이트 정보를 추가해 보겠습니다. 사이트 정보는 간단하게 구현해 보죠. <div> 태그 안에 <hr> 태그와 <p> 태그를 넣어서 만든 코드를 다음과 같이 작성합니다.
!-- site info -->
<div class="mb-5 container-fluid">
<hr>
<p>CloudStudying | <a href="#">Privacy</a>|<a href="#">Terms</a></p>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
11. 이번에는 서버를 재시작하지 않고 도구 바의 망치 아이콘을 클릭합니다. 망치 아이콘을 이용하면 프로젝트를 빌드해 수정된 HTML 코드를 좀 더 빠르게 서버에 반영할 수 있습니다.
12. 웹 브라우저에 가서 새로 고침하면 사이트 정보가 표시됩니다. 헤더-푸터 레이아웃과 상관없지만 보기 좋게 하기 위해 콘텐으 영역의 간격을 넓혀 보겠습니다.
13. 코드의 콘텐트 부분을 <div></div> 태그로 감싸고 부트스트랩에서 제공하는 class 속성을 추가합니다. 배경 색상은 어둡게(bg-dark), 텍스트 색상은 하얗게(text-white). 상하좌우 여백은 5배만큼(p-5) 줍니다.
<!-- content -->
<div class="bg-dark text-white p-5">
<h1>{{username}}님, 반갑습니다!</h1>
</div>
이렇게 해서 /hi 페이지에 헤더-푸터 레이아웃을 적용해 봤습니다.
2.4.2 /bye 페이지에 헤더-푸터 레이아웃 적용하기
/bye 페이지에도 똑같이 가운데에 content, 상단에 navigation, 하단에 site info를 넣어 보겠습니다. 단, 같은 방법을 사용하면 너무 길고 비효율적이니 /hi 페이지를 템플릿화해서 사용하겠습니다. '템플릿화한다'는 말은 코드를 하나의 틀로 만들어 변수화한다는 말입니다. 예를 들어, goodbye.mustache 파일의 콘텐트 영역만 남기고 그 위와 아래 코드를 싹 지운 후에는 {{>header}}, 아래는 {{>footer}}로 처리하는 식입니다.
- 템플릿 파일 만들고 적용하기(greetings.mustache)
템플릿 파일을 만들 위치는 templates 디렉터리입니다.
1. templates 디렉터리에서 마우스 오른쪽 버튼을 누르고 New -> Directory를 클릭합니다. 디렉터리 이름은 layouts로 입력합니다. layouts 디렉터리에서 마우스 오른쪽 버튼을 누르고 New -> File을 클릭해 header.mustache 파일을 만듭니다. 같은 방법으로 footer, mustache 파일도 만듭니다.
2. greetings.mustache 코드에서 상단 내비게이션 바 부분을 발췌해 header.mustache 파일로 만들겠습니다. 1행부터 콘텐트 영역의 바로 윗줄(<!doctype html>~</nav>)까지 선택해 자른 후 header.mustache 파일에 붙여 넣습니다. 1행의 <!doctype html> 부터 마지막 </nav>까지 잘 붙여 넣어졌는지 확인합니다.
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<title>Hello, world!</title>
</head>
<body>
<!-- navigation -->
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="#">Navbar</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="#">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Link</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Dropdown
</a>
<ul class="dropdown-menu" aria-labelledby="navbarDropdown">
<li><a class="dropdown-item" href="#">Action</a></li>
<li><a class="dropdown-item" href="#">Another action</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#">Something else here</a></li>
</ul>
</li>
<li class="nav-item">
<a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disabled</a>
</li>
</ul>
<form class="d-flex">
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
<button class="btn btn-outline-success" type="submit">Search</button>
</form>
</div>
</div>
</nav>
3.greetings.mustache 파일에서 상단 내비게이션 바 부분을 잘라냈으니 해당 부분을 템플릿으로 대체해야 합니다. 뷰 템플릿 파일을 불러올 때는 일반 변수를 {{변수명}}으로 사용하는 것과 달리 닫는 홑화살괄호(>)를 추가해 {{>파일명}}으로 작성합니다. 이때 layout 디렉터리 안에 header.mustache 파일을 만들었기 때문에 파일 경로를 포함해 {{>layouts/header}}라고 작성합니다.
4. 마찬가지로 greetings 코드의 푸터 영역(<!-- site info -->~</html>)도 잘라낸 후 footer.mustache 파일에 붙여 넣습니다.
<!-- site info -->
<div class="mb-5 container-fluid">
<hr>
<p>CloudStudying | <a href="#">Privacy</a>|<a href="#">Terms</a></p>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
</body>
</html>
5. greetings.mustache 코드의 콘텐트 영역 아래에 {{>layouts/footer}를 작성해 템플릿 화한 footer.mustache 파일을 변수로 가져옵니다.
{{>layouts/header}}
<!-- content -->
<div class="bg-dark text-white p-5">
<h1>{{username}}님, 반갑습니다!</h1>
</div>
{{>layouts/footer}}
최종적으로 greeting.mustache 코드가 매우 짧아졌습니다.
결과가 잘 나오는지 확인하기 위해 망치 아이콘을 누르고 localhost:8080/hi 페이지를 새로고침하면 문제없이 잘 나옵니다.
- 템플릿 적용하기(goodbye.mustache)
1. goodbye.mustache 파일도 다음과 같이 완성합니다.
(1) 콘텐트 영역(<h1> 태그)을 제외하고 기존 코드를 지운 후 헤더 템플릿과 푸터 템플릿을 삽입합니다.
(2) 콘텐트 영역을 <div> 태그로 감싸고 부트스트랩의 class 속성을 적용해 완성합니다.
{{>layouts/header}} <!-- 1.헤더 템플릿 삽입 -->
<div class="bg-dark text-white p-5"> <!-- 2.디자인 적용 -->
<h1>{{nickname}}님, 다음에 또 만나요!</h1>
</div>
{{>layouts/footer}} <!-- 1.푸터 템플릿 삽입 -->
2. 망치 아이콘을 누르고 localhost:8080/bye 페이지에 접속합니다. /hi 페이지에 삽입한 상단 내비게이션 바, 하단 사이트 정보와 콘텐트 영역에 적용한 디자인까지 잘 나옵니다.
'스프링부트' 카테고리의 다른 글
6장 게시판 내 페이지 이동하기 (2) | 2025.01.20 |
---|---|
5장 게시글 읽기: Read (1) | 2025.01.20 |
4장 롬복과 리팩터링 (2) | 2025.01.15 |
3장 게시판 만들고 새 글 작성하기: Create (2) | 2025.01.15 |
1장 스프링 부트 시작하기 (4) | 2025.01.06 |