웹 계층 개발

slee2·2022년 2월 14일
0
post-thumbnail

홈 화면과 레이아웃

HomeController

home.html

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/header :: header">
    <title>Hello</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<div class="container">
    <div th:replace="fragments/bodyHeader :: bodyHeader" />
    <div class="jumbotron"> <h1>HELLO SHOP</h1>
        <p class="lead">회원 기능</p> <p>
            <a class="btn btn-lg btn-secondary" href="/members/new">회원 가입</a>
            <a class="btn btn-lg btn-secondary" href="/members">회원 목록</a> </p>
        <p class="lead">상품 기능</p> <p>
            <a class="btn btn-lg btn-dark" href="/items/new">상품 등록</a>
            <a class="btn btn-lg btn-dark" href="/items">상품 목록</a> </p>
        <p class="lead">주문 기능</p> <p>
            <a class="btn btn-lg btn-info" href="/order">상품 주문</a>
            <a class="btn btn-lg btn-info" href="/orders">주문 내역</a> </p>
    </div>
    <div th:replace="fragments/footer :: footer" />
</div> <!-- /container -->
</body>
</html>

fragments/header.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head th:fragment="header">
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
    <scrip src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></scrip>
    <!-- Custom styles for this template -->
    <link href="/css/jumbotron-narrow.css" rel="stylesheet">
    <title>Hello, world!</title>
</head>

fragments/bodyHeader.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<div class="header" th:fragment="bodyHeader">
    <ul class="nav nav-pills pull-right">
        <li><a href="/">Home</a></li>
    </ul>
    <a href="/"><h3 class="text-muted">HELLO SHOP</h3></a>
</div>

fragments/footer.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<div class="footer" th:fragment="footer">
    <p>&copy; Hello Shop V2</p>
</div>

https://www.thymeleaf.org/doc/articles/layouts.html
위 내용을 살펴보면, 현재 방법은

이 include-style 레이아웃 이다. 그런데 이 방법은 좀 무식한 방식이고 원래 더 좋은 방식이있다.

그것 바로 이 Hierachical-style 이다. 실무에서는 이 방식으로 많이 사용한다. 그런데 이것에 대한 설명이나 세팅을 하기에는 또 시간이 많이 소요될 수 있어서 예제에서는 무식한 방법으로 접근한다.

뷰 템플릿을 서버 재시작 없이 반영하기

  1. spring-boot-devtools 추가
  2. html 파일 build-> Recompile

아니 이런 좋은 라이브러리가..?

getbootstrap에서 css 파일을 참고하여 아래와 같이 완성

회원 등록

MemberForm

MemberController

별 내용 없다. MVC 2편에서 나왔던 validation 처리와 관련된 이야기가 있지만, 이미 배운 내용이다.

회원 목록 조회

타임리프 기술도 중간에 설명하신다 이것도 MVC 2편 내용이다.

요구사항이 단순한 경우에는 Member를 폼으로 활용해도 된다.
그런데 실무에서 요구사항이 단순하게 오는 경우는 거의 없다.

JPA를 사용할때 엔티티는 최대한 순수하게 엔티티로만 사용해야 한다.
나중에 복잡해지기 때문

또 api를 개발할때는 절대 엔티티를 외부로 반환하면 안된다.
api는 스펙인데 멤버 엔티티를 반환하게 되면 엔티티에 어떤 객체가 추가되면 aip 스펙이 변하게 되기 때문이다.

상품 등록

BookForm

상품 목록

상품 수정

중간의 내용을 요약하자면, 데이터 조작이 가능할 수도 있으므로 해당 아이템을 수정할 권한이 있는지 체크하는 부분이 필요하다고 말씀하신다. 세션으로 체크하는 방법도 있다고 말씀하시는데 요즘에는 잘 안쓴다고 하심.

그리고 여기서 merge가 나온다아 제일 궁금했던거

ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 머지가 도대체 머지라니 음

변경 감지와 병합(merge)

진짜 중요한 내용이라고 거듭 말씀하신다.

준영속 엔티티?

내가 잘 이해한 것인지 잘 모르겠다.
현재 이 Book은 엔티티 매니저에 접근을 하지 않았다. 그저 new Book();으로 만든 객체일 뿐이다. 하지만, DB에 한번 저장되어서 Book 객체의 식별자가 존재한다. id값을 현재 갖고있다. 이걸로 식별이 가능하다는 뜻이다. 그렇다면 준영속 엔티티라고 본다.

준영속 엔티티를 수정하는 2가지 방법

  • 변경 감지 기능 사용
  • merge 사용

변경 감지 기능 사용

영속성 컨텍스트에서 엔티티를 다시 조회한 후에 데이터를 수정하는 방법이다.
@Transactional에 의해 해당 메서드를 빠져나올때 변경 감지를 하여 flush를 날리게 된다.

병합 사용

이거다.

동작 방식

  1. merge()를 실행한다.
  2. 파라미터로 넘어온 준영속 엔티티의 식별자 값으로 1차 캐시에서 엔티티를 조회한다.
    만약 1차 캐시에 엔티티가 없으면 데이터베이스에서 엔티티를 조회하고, 1차 캐시에 저장한다.
  3. 조회한 영속 엔티티(mergeMember)에 member 엔티티의 값을 채워넣는다.
  4. 영속 상태인 mergeMember를 반환한다.

주의해야할 점은 오른쪽은 준영속성 엔티티고
merge를 통해 반환된 아이템 merge가 영속성 엔티티이다.
그러므로 후에 작업할 것이 있다면 영속성인 이 반환값으로 사용해야 한다.

또 merge 방법에 치명적인 단점이 있다.
merge를 쓸때 필드를 하나라도 빼먹으면 해당 필드의 값이 null로 업데이트가 되버린다.

그렇기 때문에 매번 사용할때마다 모든 값을 세팅해줘야하는 불편함이 있다.

그러니 항상 첫번째 방법인 변경 감지를 사용하자.

또, 폼으로 아이템을 세팅할 때 set으로 남발하지말고 item.change(price, name); 과 같이 의미있는 메서드로 세팅을 해주는 것이 좋다.

  • 컨트롤러에서 어설프게 엔티티를 생성하지 말아라.
  • 트랜잭션이 있는 서비스 계층에 식별자(id)와 변경할 데이터를 명확하게 전달하자.(dto)
  • 트랜잭션이 있는 서비스 계층에서 영속 상태의 엔티티를 조회하고, 엔티티의 데이터를 직접 변경하자.

이렇게 하지 말고

음 이렇게 하란 소리인가 잘 모르겠다. UpdateItem을 새로 만드는건 알겠는데 여기서 change를 어떤식으로 활용하라는지 아직 덜 이해됨.

상품 주문

와 이거 너무 좋아보여 이건 활용도 100% 가능

위의 order 메서드에서 모든 것이 해결된다.

이처럼 단순화를 시키면 컨트롤러에서 할 수 있는게 많아진다.

주문 목록, 검색, 취소


검색을 누르면 GET 방식으로 status를 뿌리게 된다.
Enum 타입의 상태를 each로 돌리는데 신박하고 좋아보인다.

주문한 경우에만 버튼이 노출되도록 되어있다.

취소 버튼을 누르면, post로 취소 url을 생성하고 이동한다.

취소해주면 끝.

0개의 댓글