스프링 부트와 AWS로 혼자 구현하는 웹 서비스(3)

김나우·2022년 4월 3일
0

이 포스팅은 스프링 부트와 AWS로 혼자 구현하는 웹 서비스
를 보며 공부한 내용을 정리하는 포스팅입니다.

머스테치로 화면 구성하기

머스테치란?

수많은 언어를 지원하는 가장 심플한 템플릿 엔진이다.
현존하는 대부분 언어를 지원한다.

기본 페이지 만들기

일단 먼저 plugin을 설치해주고 build.gradile에 의존성을 등록한다.

머스테치의 파일 위치는 기본적으로 src/main/resources/templates이다
이 위치에 머스테치 파일을 두면 스프링 부트에서 자동으로 로딩한다

저 위치에다가 첫 페이지를 담당할 index.mustache를 생성한다

머스테치에 URL 매핑을 해주기 위해 IndexController를 만들어 준다.

머스테치 스타터 덕분에 컨트롤러에서 문자열을 반홯할 때 앞의 경로와 뒤의 파일 확장자는
자동으로 지정된다


성공!


게시글 등록 화면 만들기

html과 bootstrap 관련한것들은 글에서는 생략하겠다.

index.mustache에 수정이 끝나면 컨트롤러도 수정을 해준다

페이지에 관련된 컨트롤러는 모두 IndexController를 사용해주겠다.

/posts/save를 호출하면 posts-save.mustache를 호출하는 메서드를 추가해줬다

이제 posts-save.mustache 파일을 생성해준다

성공!

하지만 여기선 등록 버튼에 기능이 없다 API를 호출하는 JS가 없기 때문이다
그래서 scr/main/resouces에 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').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();

근데 실행이 안된다.. 왜지

등록을 클릭을 해도 먹지를 않는다 이유가 무엇일까..

검색을 해보니 cdn으로 jquery를 불러왔는데 intellj에서 인식을 못한거였다
해결방법은 간단했다.

라이브러리를 다운로드 해줬다.

인식은 잘된다 근데 아직도 홈페이지는 먹통이다 뭐지..

footer 부분에 index.js가 인식이 안되는 것을 발견했다 왜지..? 분명히 만들어 줬는데


갑자기 됐다.. 왜지?
postman으로 값을 넘기는거는 문제가 없길래 조회, 수정, 삭제 부분을 먼저 하고있었는데
갑자기 된다..


전체 조회화면 만들기

{{>layout/header}}

<h1>스프링부트로 시작하는 웹 서비스 Ver.2</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>
            {{#userName}}
                Logged in as: <span id="user">{{userName}}</span>
                <a href="/logout" class="btn btn-info active" role="button">Logout</a>
            {{/userName}}
            {{^userName}}
                <a href="/oauth2/authorization/google" class="btn btn-success active" role="button">Google Login</a>
                <a href="/oauth2/authorization/naver" class="btn btn-secondary active" role="button">Naver Login</a>
            {{/userName}}
        </div>
    </div>
    <br>
    <!-- 목록 출력 영역 -->
    <table class="table table-horizontal table-bordered">
        <thead class="thead-strong">
        <tr>
            <th>게시글번호</th>
            <th>제목</th>
            <th>작성자</th>
            <th>최종수정일</th>
        </tr>
        </thead>
        <tbody id="tbody">
        {{#posts}}
            <tr>
                <td>{{id}}</td>
                <td><a href="/posts/update/{{id}}">{{title}}</a></td>
                <td>{{author}}</td>
                <td>{{modifiedDate}}</td>
            </tr>
        {{/posts}}
        </tbody>
    </table>
</div>

{{>layout/footer}}

{{#posts}}
-> posts라는 List를 순회한다
-> Java의 for문과 동일하게 생각하면 된다

{{id}}등의 {{변수명}}
-> List에서 뽑아낸 객체의 필드를 사용한다.

Controller, Service, Repository 코드를 작성해준다.

PostsRepository를 수정 해주었다.
SpringDataJpa에서 제공하지 않는 메소드는 위처럼 쿼리로 작성해도 된다는 것을 보여주기 위해
@Query를 사용했다

Querydsl을 추천하는 이유
1. 타입 안정성이 보장된다
2. 국내 많은 회사에서 사용중이다
3. 레퍼런스가 많다


PostsService에 추가해줬다

@Transactional
-> readOnly = true를 추면 트랜잭션 범위는 유지하되, 조회 기능만 남겨두어 조회 속도가 개선된다

PostsListResponseDto도 만들어줬다.

Controller도 변경해주었다.
@RequiredArgsConstructor로 생성자주입을 받고
model을 사용해서 값을 넣어줬다.

Model
-> 서버 템플릿 엔진에서 사용할 수 있는 객체를 저장할 수 있다.
-> 여기서는 postsService.findAllDesc()로 가져온 결과를 posts라는 이름으로
index.mustache에 전달한다.

작동도 잘 된다!


게시글 수정, 삭제 화면 만들기

게시글 수정

게시글 수정 API는 이미 만들어 두었으니 화면을 만들어 주자

{{>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="id" value="{{post.id}}" readonly>
            </div>
            <div class="form-group">
                <label for="title">제목</label>
                <input type="text" class="form-control" id="title" value="{{post.title}}">
            </div>
            <div class="form-group">
                <label for="author"> 작성자 </label>
                <input type="text" class="form-control" id="author" value="{{post.author}}" readonly>
            </div>
            <div class="form-group">
                <label for="content"> 내용 </label>
                <textarea class="form-control" id="content">{{post.content}}</textarea>
            </div>
        </form>
        <a href="/" role="button" class="btn btn-secondary">취소</a>
        <button type="button" class="btn btn-primary" id="btn-update">수정 완료</button>
        <button type="button" class="btn btn-danger" id="btn-delete">삭제</button>
    </div>
</div>

{{>layout/footer}}

posts-update.mustache 파일을 만들어 줬다.

{{post.id}}
-> 머스테치는 객체의 필드 접근 시 점(Dot)로 구분한다.
-> 즉, Post클래스의 id에 대한 접근은 psot.id로 사용할 수 있다.

readonly
-> Input 태그에 읽기 가능만 허용하는 속성입니다
-> id와 author는 수정할 수 없도록 읽기만 허용하도록 추가한다.


index.js에 update function을 추가해줬다.

$('#btn-update').on('click')
-> btn-update란 id를 가진 HTML 엘리먼트에 click 이벤트가 발생할 때 update function을 실행하도록 이벤트를 등록한다.

update : function()
-> 신규로 추가될 update function이다

type:'PUT'
-> 여러 HTTP Method중 PUT 메소드를 선택한다

url:'api/v1/posts/'+id
-> 어느 게시글을 수정할지 URL Path로 구분하기 위해 Path에 id를 추가한다.


indxController에 추가해준다


성공


게시글 삭제

먼저 삭제 이벤트를 진행할 JS 코드를 추가해준다

그리고 나서 삭제 API를 만들어 준다

postsRepository.delete(posts)
-> JpaRepository에서 이미 delete 메소드를 지원하고 있으니 이를 활용한다
-> 엔티티를 파라미터로 삭제할 수도 있고, deleteById 메소드를 이용하면 id로 삭제할 수 있다.
-> 존재는 posts인지 확인을 위해 엔티티 조회 후 그대로 삭제한다.


PostsApiController에 추가해준다.


성공!

profile
안녕하세요

0개의 댓글

관련 채용 정보