MVC 패턴

이동영·2025년 6월 13일

웹개발

목록 보기
6/36

개념정리

뷰 템플릿

뷰 템플릿(View Template)은 화면을 담당하는 기술로, 웹 페이지(View)를 하난의 틀(Template)로 만들고 여기에 변수를 삽입해 서로 다른 페이지로 보여준다. 처음에 프로젝트를 생성할 때, Mustache라는 도구를 추가해주었는데, 이것이 바로 뷰 템플릿을 만드는 도구이다. 참고로 머스태치 외 템플릿 엔진으로는 Thymeleaf, JSP가 있다. 그리고 뷰 템플릿을 간단히 '뷰'라고도 부른다.

MVC 패턴

모델-뷰-컨트롤러 패턴을 MVC 패턴이라고 한다. 컨트롤러(Controller)는 클라이언트의 요청에 따라 서버에서 이를 처리하는 역할을 하고, 모델(Model)은 데이터를 관리하는 역할을 한다. 이렇게 웹 페이지를 화면에 보여주고(View), 클라이언트의 요청을 받아 처리하고(Controller), 데이터를 관리하는(Model) 역할을 나누는 기법을 말한다.

실습

뷰 템플릿 페이지 만들기

머스태치 플러그인을 설치하고, src > main > resources > templates 디렉터리에 뷰 템플릿 greeting.mustache를 만들었다.
해당 파일에서 가장 윗줄에 doc을 입력하고 [Tab]키를 누르면 자동으로 기본 HTML 코드가 작성된다.
그냥 이렇게 만들면 일반 HTML과 다름 없다.
mustache는 변수명을 적고 두 겹의 중괄호({{}})로 감싸면 변수를 사용할 수 있다.

<body>
	<h1>{{username}}님, 반갑습니다!</h1>
</body>

이렇게 작성한 후에, 컨트롤러에서 모델 타입의 변수를 만들어주면 된다.

컨트롤러 작성

src > main > java에 com.example.firstprojcet.controller 패키지를 만들고, 그 안에 FirstController를 만들었다.

package com.example.firstproject.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller     // 컨트롤러 선언
public class FirstController {

	@GetMapping("/hi")		// URL 요청 접수
    public String niceToMeetYou(Model model) {  // 메서드 작성, model 객체 받아 오기
    model.addAttribute("username", "덩엉");		// model 객체가 "덩엉" 값을 "username"에 연결해 웹 브라우저로 보냄
        return "greetings";		// greetings.mustache 파일 반환
    }
}

이렇게 해서 localhost:8080/hi로 접속하면 해당 컨트롤러에게 요청을 보내고, 서버가 알아서 templates 디렉터리에서 greetings.mustache 파일을 찾아서 웹 브라우저로 전송한다.

뷰 템플릿 페이지에 레이아웃 적용

헤더-푸터 레이아웃(header-footer layout)이 가장 기본이 되는 레이아웃이다. 샌드위치 같은 구조로, 상단의 헤더(header) 영역에는 사이트 안내를 위한 내비게이션을 넣고, 하단의 푸터(footer) 영역에는 사이트 정보를 넣는다. 그리고 두 영역 사이에는 사용자가 볼 핵심 내용인 콘텐트(content)를 배치한다.
쉽고 빠르게 페이지를 꾸미기 위해서 부트스트랩(Bootstrap)을 사용했다.

첫 페이지에서 버전을 선택하고,

아래로 내려 스타터 템플릿을 복사하여 greetings.mustache 파일에 붙여넣었다.
그리고 내비게이션 바를 가져오기 위해 'navbar'를 검색하여 복사해서 넣을 위치에 붙여넣으면 된다.

<!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>
    <div class="bg-dark text-white p-5">
        <h1>{{username}}님, 반갑습니다!</h1>
    </div>
    <!-- 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>

이것저것 꾸미게 되면

이렇게 나온다.

보통 헤더와 푸터는 사이트 어디를 가도 사용할 수 있기 때문에, 템플릿 파일로 따로 분리해서 변수화해 사용이 가능하다.
templates 디렉터리 하위에 layouts 디렉터리를 만들고, header.mustache 파일과 footer.mustache 파일을 만들었다. 그리고 처음부터 내비게이션 부분까지 header로 떼고, 사이트 정보 부분을 footer로 떼어내서 greetings.mustache의 코드를 이렇게 바꿔주면 된다.

{{>layouts/header}}
<!-- content -->
<div class="bg-dark text-white p-5">
    <h1>{{username}}님, 반갑습니다!</h1>
</div>
{{>layouts/footer}}

템플릿 파일을 불러올 때는 파일 경로를 포함해서 {{>파일경로/파일명}} 이런 식으로 작성한다.

0개의 댓글