[Spring Boot] 게시글 등록화면 만들기

윤동환·2023년 2월 27일
0

부트스트랩을 이용하여 화면 만들기

프론트엔드 라이브러리 사용 방법
1. 외부 CDN을 사용
2. 직접 라이브러리를 받아서 사용 (npm/yarn + webpack 등)

지금은 1번의 방법을 사용할 것입니다. 프로젝트에 직접 내려받지 않고 코드 한줄로 간단하게 사용할 수 있기 때문입니다.

CDN 문제점
실제 서비스에서 사용하게된다면 CDN에 우리의 서비스가 의존하게되어 외부의 문제에 따라 서비스 영향을 받을 수 있기 때문입니다.

지속 사용하는 라이브러리 레이아웃으로 관리하기

부트스트랩, 제이쿼리는 머스테치 화면에서 계속 사용하기때문에 매번 해당 라이브러리를 머스테치화면에 추가히지말고 레이아웃 파일들을 만들어 추가합니다.


header

<!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>

브라우저에서 html을 읽을 때 위에서 부터 읽기때문에 header에 css를 넣어 먼저 로딩이 되도록 합니다.

footer

        <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>
    </body>
</html>

footer에 js를 넣어 스크립트를 실행시켰으며 부트스트랩이 제이쿼리에 의존하므로 제이쿼리를 먼저 호출되도록 구성하였습니다.

index.mutache

{{>layout/header}}
    <h1>스프링 부트로 시작하는 웹 서비스</h1>
{{>layout/footer}}

이렇게 필요한 코드만으로 작성할 수 있습니다.

사용 문법 설명
{{>}} : 현재 mustache파일 기준으로 다른 파일을 가져옵니다.

게시글 등록 UI/UX 추가

index.mutachedp 글 등록 버튼 추가

{{>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}}

posts-save.mustache

{{>layout/header}}
<h1>게시글 등록</h1>

<div class="col-md-12">
    <div class="clo-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}}

코드를 실행해여 index화명의 글 등록 버튼을 기대하였는데 error창이 발생했다. 그 이유는 {{>layour/header}} 오타였습니다.
또한 class="btn btn=primary"라는 오타또한 있었는데 이 경우엔 파란 버튼의 테두리 없이 그냥 클릭 가능한 텍스트 버튼이었습니다.
제공하는 class 버튼을 가져오지 못하였고, 그러더라도 css 적 문제일뿐 실행엔 영향을 주지 않았습니다.

Posts-save 호출 순서
1. 브라우저에서 localhost:8080에 접속하면 contoller에서 index.mutache를 리턴해줍니다.
2. index.mutache에 있는 글 등록 버튼을 클릭하여 a태그에 등록한 url로 api를 재 요청합니다.(a 태그로 다른 api url 요청)

  1. /posts/save url로 api요청이 들어오면 indexController가 이를 받아 posts-save.mutache 파일을 return해줍니다.
  2. index page처럼 posats-save page를 랜더링합니다.

글 등록 버튼에 대한 기능 추가

/resources/static/js/app/index.js 파일 생성

var main = {
    init : function () {
        var _this = this;
        $('#btn-save').on('click', function () {
            _this.save();
        });
    },
    save : function () {
        var data = {
            title: $('#title').var(),
            author: $('#author').var(),
            content: $('#content').var()
        };
        
        $.ajax({
            type: 'POST',
            url: '/api/v1/posts',
            dataType: 'json',
            contentType: 'application/json; charset=uft-8',
            data: JSON.stringify(data)
        }).done(function () {
            alert('글이 등록되었습니다.');
            window.location.href = '/'; //글 등록시 메인페이지(/)로 이동  
        }).fail(function (error) {
            alert(JSON.stringify(error));
        });
    }
};

main.init();

index.js내에서 main이라는 변수 속성으로 function을 추가한 이유! (중요)

index.mutache의 js 파일이 index.js 말고 a.js가 추가되었다고 가정합니다.
브라우저의 scope는 공용공간으로 쓰기때문에 a.js파일 내에도 init(), save()가 있다면 나중에 로딩된 js의 init(), save()가 먼저 로딩된 js fucntion을 덮어쓰게 됩니다.
때문에 index.js만의 유효 scope를 만들어 사용하여 여러사람의 프로젝트에서 중복되는 함수이름으로 인해 발생하는 문제를 방지할 수 있습니다.

P.S.
ES6를 비롯한 JavaScript 문법과 앵귤러, 리액트, 뷰 등은 프레임워크에서 지원합니다.

index.js 스크립트 적용하기

스프링 부트 는 기본적으로 src/main/resources/static에 위치한 자바스크립트, CSS, 이 미지 등 정적 파일들은 URL에서 /로 설정됩니다.

때문에 src="js/app/index.js" 이렇게 위치에 맞게 호출이 가능합니다.
footer.mustache

        <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>

이제 index.js 스크립트가 html에 등록이 되고 btn-save라는 버튼이 눌리게 되면 index.js의 init()함수가 실행되며 ajax에 등록한 posts/v1/save api를 호출하게 됩니다.

결과

글이 등록되었다고 팝업이 떴습니다.

h2 console page를 통해 저장된 게시글 내역을 확인할 수 있습니다.
업로드중..

겪은 문제

문제 발생!

  • 등록 버튼을 눌렸는데 예상한 동작을 하지 않았습니다.
    취소 버튼을 눌렸을 때 메인페이지로 이동은 되길래 js파일의 문제라고 생각했습니다.
    개발자 페이지를 켜서보니 아니나 다를까 index.js 를 가져오는데 서버에서 찾지 못했다고(404) 합니다.

문제 원인
footer에서 index.js를 가져오는 부분을 확인했는데 spring bootjs, css와 같은 파일을 기본 경로로 "../resources/static"로 두기때문에 그 밑에 경로로 js/app/index.js로 되어있었습니다.
리소스를 가져오는 경로를 보면 posts/js/app/index.js로 되어있는데 기본 경로인 static의 경로를 찾지않고 posts/save에서 리소스를 받았기 때문에 posts를 상위 폴더로 두고있었습니다. 그 이유는 js/app/index.js 가 아닌 /js/app/index.js로 두어야 기본 경로 static으로 인식을 하기 때문입니다.

문제 해결
js/app/index.js/js/app/index.js로 수정하였습니다.

두번째 문제
그럼에도 불구하고 문제가 발생했습니다. 등록을 눌러도 아무것도 저장이 되지 않았습니다.
그 이유는 각 게시글 변수를 받아오는 val() 함수대신 var()라는 이상한 함수를 호출했기 때문입니다.
오타를 수정하여 문제를 해결하였습니다.

.val() : 일치하는 요소 집합에서 첫번째 요소의 현재 값 가져오기

profile
모르면 공부하고 알게되면 공유하는 개발자

0개의 댓글