앞서서 이미 API를 구현해 놓았으니 여기선 바로 화면을 개발하도록 한다. 아무래도 그냥 HTML만 사용하면 밋밋해 보이기 쉽다. 그래서 오픈소스인 부트스트랩을 사용하여 화면을 만들어 보겠다.
부트스트랩, 제이커리 등 프론트엔드 라이브러리를 사용할 수 있는 방법은 크게 2가지가 있따. 하나는 외부 CDN을 사용하는 것이고, 다른 하나는 직접 라이브러리를 받아서 사용하는 방법이다.
여기서 사용할 방법은 외부 CDN을 사용하는 방식이다.
서비스에서는 외부 서비스에 우리 서비스가 의존 하게 돼버려서 잘 사용하지 않는 방법이다.
매번 해당 라이브러리를 머스테치 파일에 추가하는 것은 귀찮으므로 레이아웃 파일을 만들어서 관리하도록 하자.
header.mustach
<!DOCTYPE HTML>
<html>
<head>
<title>스프링부트 웹서비스</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
</head>
<body>
footer.mustach
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
<!--index.js 추가-->
<script src="/js/app/index.js"></script>
</body>
</html>
코드를 보면 css와 js의 위치가 서로 다르다. 페이지 로딩속도를 높이기 위해 css는 header에, js는 footer에 두었다. HTML은 위에서부터 코드가 실행 되기 때문에 Head가 다 실행되고서야 body가 실행이 된다.
즉, Head가 다 불러지지 않으면 클라이언트에는 백지화면만 노출된다. 특히 JS의 용량이 크면 클수록 body 부분의 실행이 늦어지기 때문에, js는 body 하단에 두어 화면이 다 그려진 뒤에 호출하는 것이 좋다.
라이브러리를 비롯해 기타 HTML 태그들이 모두 레이아웃에 추가 되었으므로 이제 index.mustache에는 필요한 코드만 남게 된다.
{{>layout/header}}
<h1>안녕하세요. 머스테치입니다</h1>
{{>layout/footer}}
레이아웃으로 파일을 분리했으니 이제 글 등록 버튼을 추가해보자.
{{>layout/header}}
<h1>안녕하세요. 머스테치입니다</h1>
<div class="col-md-12">
<div class="row">
<div class="col-md-6">
<a href="/posts/save" role="button" class="btn btn-primary"/>글 등록</a>
</div>
</div>
</div>
{{>layout/footer}}
이후 IndexController로 이동해 /posts/save로 들어오는 주소를 처리해주도록 하자.
@GetMapping("/posts/save")
public String postsSave(){
return "posts-save";
}
이제 /posts/save가 호출되면 posts-save.mustach를 호출 하는 메소드가 추가 되었다. 이후 posts-save.mustach를 작성해주자
{{>layout/header}}
<h1>게시글 등록</h1>
<div class="col-md-12">
<div class="col-md-4">
<form>
<div class="form-group">
<label for="title">제목</label>
<input type="text" class="form-control" id="title" placeholder="제목을 입력하세요">
</div>
<div class="form-group">
<label for="author"> 작성자 </label>
<input type="text" class="form-control" id="author" placeholder="작성자를 입력하세요">
</div>
<div class="form-group">
<label for="content"> 내용 </label>
<textarea class="form-control" id="content" placeholder="내용을 입력하세요"></textarea>
</div>
</form>
<a href="/" role="button" class="btn btn-secondary">취소</a>
<button type="button" class="btn btn-primary" id="btn-save">등록</button>
</div>
</div>
{{>layout/footer}}
메인홈페이지에서 글등록 버튼을 누르면 글 등록 화면이 나오지만 아직 등록 화면에 등록 버튼은 기능이 없다. API 호출하는 JS가 전혀 없기 때문이다. 그래서 src/main/resources에 statics/js/app 디렉토리를 생성하고 index.js를 작성한다.
var main = {
init : function () {
var _this = this;
$('#btn-save').on('click', function () {
_this.save();
});
},
save : function () {
var data = {
title: $('#title').val(),
author: $('#author').val(),
content: $('#content').val()
};
$.ajax({
type: 'POST',
url: '/api/v1/posts',
dataType: 'json',
contentType:'application/json; charset=utf-8',
data: JSON.stringify(data)
}).done(function() {
alert('글이 등록되었습니다.');
window.location.href = '/';
}).fail(function (error) {
alert(JSON.stringify(error));
});
}
};
main.init();
index.js의 첫문장에 var main= {...}라는 코드를 선언했다. 굳이 index의 변수의 속성으로 function을 추가한 이유가 무엇일까? 예를 들어 다음과 같은 코드가 있다고 생각해보자.
var init = funtion(){
...
};
var save = funtion(){
...
};
index.mustache에서 다른 js가 추가 되어 그 js도 init과 savefuntion이 있다면 어떻게 될까? 브라우저의 스코프는 공용 공간으로 쓰이기 때문에 나중에 로딩된 init,save가 먼저 로딩된 함수를 덮어쓰게 된다.
여러사람이 참여한 프로젝트에서 중복된 함수 일들은 자주 발생 한다. 모든 funtion의 이름을 확인하면서는 만들 수는 없으므로 이런 문제를 피하기 위해 index.js만의 유효 범위를 만들어 사용한다.
ES6를 비롯한 최신 자바스크립트 버전이나 앵귤러,리액트,뷰 등은 이미 이런 기능을 프레임워크 레벨에서 지원하고 있다.
화면에서 제대로 등록 되는 것을 확인했으니 직접 h2-console에 들어가 확인해보도록 하자.
등록기능이 정상적으로 작동하는 것을 확인했다. 다음으로는 전체 조회 화면을 만들어보자.