끊임없이 검증하라

나에게 당연할지라도

Project

P1_클론 프로젝트(feat. 스프링부트와 AWS로 혼자 구현하는 웹 서비스)_4

fadet 2022. 3. 28. 17:51

* 이 포스트는 전 배달의민족, 현재 인프런에 계시고 유튜브 개발바닥의 크리에이터이신 개발자 이동욱님의 '스프링부트와 AWS로 혼자 구현하는 웹 서비스'를 기반으로 작성되었음을 알립니다. 포스트 맨 아래에 관련 링크가 있습니다. 책의 내용을 기반으로 작성되기에 실습 중이라면 책을 main 해당 포스트를 sub로 참고해주세요. 책의 설명이 부족한 부분 위주로 포스트가 구성됩니다.

 


4장부터는 본격적인 화면 영역을 구성합니다. ready 포스트에서 다룬 것처럼 이 책에선 API를 통해 View가 통신하는 구조를 따르지 react, vue 등 클라이언트 템플릿엔진이 아닌 서버 템플릿 엔진을 사용하여 화면을 개발합니다. 여기서 일반적으로 스프링에서 사용되는 템플릿 엔진인 thymleaf나 JSP를 사용하지 않고 mustache를 사용합니다. 그 이유는 책에 설명되어 있고 우리가 살펴볼 부분은 따로 있습니다. 기존 스프링 부트를 배울때 thymleaf로 화면 개발하신 분들은 API controller가 추가된 통신 구조가 생소하실 수도 있습니다. 그러나 단순히 view와 api의 역할이 좀 더 명확하게 분리된 것으로 생각하시면 쉽습니다. 기존에 client에서 url로 요청이 오면 한 컨트롤러에서 각각 get, post 등을 메소드마다 매핑하여 view를 반환하거나 data를 변경한 뒤 redirect했으면 현재 방식은 IndexController는 조회처럼 요청이 데이터를 변경하지 않을때 model에 정보를 담아 view를 반환하고 PostsApiController는 수정처럼 요청이 데이터를 변경해야한다면 DB 수정 트랜잭션을 요청하는 역할로 분리합니다.

 

4.1 머스테치로 화면 구성

 

# 흐름 파악

화면 개발시 작동 방식의 큰 흐름은 다음과 같습니다(글 등록 기준)

[IndexController] 1 홈페이지 진입시 홈 view 화면 반환 > [IndexController] 2 글등록 버튼 클릭시 글 등록 view 화면 반환 > [PostsApiController] 3 등록 완료 버튼 클릭시 post요청으로 등록 트랜잭션 요청 > [IndexController] 4 등록이 완료된 후 알림과 함께 홈 화면 반환

 

이 과정에서 2,3,4 과정은 index.js라는 js를 통해 api 요청 호출부터 리다이렉트까지가 제어됩니다. 그러므로 이번 장에선 1 실제 보여질 화면 2 CRUD 흐름을 제어할 index.js 3 요청이 오면 매핑할 Controller 이 세가지를 개발하는 것이라 생각하시면 됩니다.

 

4.2 기본 페이지 만들기

 

# index.mutache

이제 전체적인 윤곽은 잡혔으니 책의 내용을 살펴보겠습니다. Index.mustache와 IndexControllerTest까지의 과정은 어려울 것이 없으니 넘어가고 부트스트랩, 제이쿼리가 나오는데 부트스트랩은 개발자가 화면을 직접 디자인하려고 html, css, js 등으로 고생할 일을 줄여주는 프레임워크로 그냥 html에 class만 적어주면 깔끔하게 디자인해주는 툴 정도로 알고 넘어가셔도 됩니다. 제이쿼리의 경우 자세히 알기위해선 front-end 지식인 DOM 등을 알고 있어야 하므로 그냥 백엔드 개발자가 js를 쉽게 다루기 위한 라이브러리로 알고 넘어가시면 됩니다.

 

화면 구성의 경우 header와 footer를 따로 생성하고 이에 css와 js를 어떻게 넣어야 효율적일지 책에 다 나와있습니다. 다음은 posts-save.mustache의 html 태그 중 각 목록의 id="title" 등과 마지막에 있는 id="btn-save"만 기억하고 넘어가면 됩니다. 이 id는 바로 뒤의 js에서 사용할겁니다.

<div class="form-group">
	<label for="title">제목</label>
	<input type="text" class="form-control" id="title" placeholder="제목을 입력하세요"> // 1
</div>
...

<button type="button" class="btn btn-primary" id="btn-save">등록</button> // 2

 

# index.js

index.js를 만들고 내용을 보면 대략적으로 1 아까 기억했던 btn-save를 클릭하면 save function이 실행 2 save function은 아까 각 목록의 id를 객체의 필드들과 매핑 3 POST api 요청을 호출하여 url에 담아 전송 4 성공시 알림과 함께 홈으로 redirect 

var main = {
    init : function () {
        var _this = this;
        $('#btn-save').on('click', function () {
            _this.save();
        }); // 1
    },
    save : function () {
        var data = {
            title: $('#title').val(),
            author: $('#author').val(),
            content: $('#content').val()
        }; // 2

        $.ajax({
            type: 'POST',
            url: '/api/v1/posts',
            dataType: 'json',
            contentType:'application/json; charset=utf-8',
            data: JSON.stringify(data) // 3
        }).done(function() {
            alert('글이 등록되었습니다.');
            window.location.href = '/'; // 4
        }).fail(function (error) {
            alert(JSON.stringify(error));
        });

# IndexController

유효범위(scope)에 대한 내용은 자바를 공부하셨다면 이 또한 쉽게 이해하실거라 여기고 넘어가겠습니다. 책에 소개된 조회 화면의 {{#posts}} 같은 문법은 기존 thymleaf나 JSP를 통해 이미 익숙하실겁니다만 혹시 몰라 말씀드리면 IndexController에서 model로 전달된 값을 이용합니다.

    @GetMapping("/")
    public String index(Model model, @LoginUser SessionUser user) {
        model.addAttribute("posts", postsService.findAllDesc()); // 이 부분

4.3 게시글 등록 화면 만들기

4.4 전체 조회 화면 만들기

 

# PostRepository

다음은 화면에 매핑될 Class들입니다. PostsRepository를 보면 @Query 어노테이션이 붙어 있는데 쉽게 findAlldesc 메소드가 호출되면 DB에 작성된 쿼리를 날린다고 생각하고 넘어가셔도 됩니다. 그런데 여기서 작성된 쿼리는 흔히 볼 수 있는 SQL이 아닙니다. 이는 JPA가 지원하는 쿼리이고 우리는 DB가 변경될때마다 그에 맞는 쿼리를 일일이 작성할 필요가 없어집니다. 또한 jpql은 테이블이 아닌 객체에게 쿼리를 날리고 이 쿼리가 변환되어 DB에 전달되는 것이고 이는 책의 초반부에 언급된 패러다임 불일치 부분과 이어진다고 생각하시면 됩니다. 책에서 소개된 querydsl이나 jpql의 자세한 설명 등은 이 책을끝낸 후에 JPA를 본격적으로 학습하신다면 알게되는 부분이니 일단 넘어가도록 합시다.

 

# PostService

PostsService에서 대해서는 책에 나온 readonly에 대한 짧은 팁 하나를 드리는 것으로 넘어가겠습니다. 아래 코드 처럼 클래스 위에 readonly를 작성하면 해당 클래스의 모든 메소드는 @Transaction(readOnly=true)가 기본값이 되므로 데이터가 변경되는 메소드에만 @Transaction을 달면됩니다. 원래 커스텀은 우리 포스트에서 다루지 않지만 해당 부분은 커스텀이라기보단 팁에 가까워 소개해드리고 넘어갑니다.

@RequiredArgsConstructor
@Service
@Transactional(readOnly = true) // 여기에 추가하면
public class PostsService {
    private final PostsRepository postsRepository;
	// 데이터 변경시에만 추가
    @Transactional
    public Long save(PostsSaveRequestDto requestDto) {
        return postsRepository.save(requestDto.toEntity()).getId();
    }
	// 여기는 기본 적용
    public PostsResponseDto findById(Long id) {
        Posts entity = postsRepository.findById(id)

또한 findAllDesc 메소드에는 자바8에서 추가된 stream이 있어 해당 부분을 잘 모르시면 List, Map 같은 결이 다른 컬렉션 프레임워크를 stream 메소드로 표준화하여 다루는 기능이라고 아시면 됩니다. stream 내에 있는 ::는 메소드 참조라 하니 넘어가도 무방하지만 궁금하신 분들은 stream과 메소드 참조에 대한 학습을 하시면 됩니다.

 

# PostsListResponseDto

PostsListResponseDto는 조회 화면에서 client에게 보여질 정보만을 posts 엔티티에서 선택하여 작성한 dto 클래스입니다. 이전과 마찬가지로 생성자는 entity를 받습니다.

 

# IndexController

책에서 언급한 IndexController의 model같은 경우 이전 포스트들을 이해하는데 어려움이 없으셨다면 쉽게 넘어가실거라 생각합니다. 여기서 나온 객체 model은 MVC 패턴의 Model과는 차이가 있습니다. MVC에서 Controller가 어떻게?에 해당한다면 Model은 무엇을?에 해당하는, 데이터와 알고리즘을 포함하는 Component이며 객체 model은 단순히 view로 Model 정보를 전달하는 객체 덩어리일 뿐입니다. 따라서 기존에 이를 잘 모르시는 분이라면 MVC 기본 구조에 대해서 체크하고 넘어가시면 될 것 같습니다. 또한 @ModelAttribute에 익숙하신 분들은 addAttribute와의 차이를 체크하시면 좋겠네요.

 

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

 

# index.js와 IndexController

게시글 작성부분까지 진행하셨다면 수정/삭제 부분 역시 포스트와 책의 내용만 따라가더라도 큰 무리가 없을거라 생각합니다. 여기서 하나만 짚고가면 충분할 것 같네요. 게시글을 수정, 삭제하기 위해선 각 게시글 id를 매핑해줘야 합니다. 따라서 index.js에선 id를 다음과 같이 매핑해주고

update : function () {
...
var id = $('#id').val(); // id 매핑
        $.ajax({
            type: 'PUT',
            url: '/api/v1/posts/'+id, // url에 id 추가

IndexController에선 @PathVariable를 사용하여 url의 id와 객체 id를 매핑해주면 됩니다.

public class IndexController
...
   @GetMapping("posts/update/{id}")
    public String postsUpdate(@PathVariable Long id, Model model) { // 이부분

 

이렇게 4장을 마무리합니다. 4장까지 기본적인 구조 설계와 화면 개발까지 모두 했다고 생각하면 됩니다. 다음부터 진행할 5장은 스프링 시큐리티를 이용해 소셜 로그인을 어플에 적용시키는 것을 알아봅니다.


 

 

refer

이동욱님 블로그의 관련 포스트 : https://jojoldu.tistory.com/539?category=717427

개발바닥 유튜브 :https://www.youtube.com/channel/UCSEOUzkGNCT_29EU_vnBYjg

 

개발바닥

본격 세계최초 DEV 엔터테인먼트 토크쇼 두 스타트업 개발자의 요절복통 이야기 구독 안하면 장애남!!

www.youtube.com

이동욱님 github의 해당 repository : https://github.com/jojoldu/freelec-springboot2-webservice