mojo's Blog
웹 계층 개발 - (1) 본문
홈 화면과 레이아웃
template 에 html 을 다음과 같이 설정한다.
- fragment
- bodyHeader.html
- footer.html
- header.html
- home.html
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="/css/bootstrap.min.css" integrity="sha384-
ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
crossorigin="anonymous">
<!-- Custom styles for this template -->
<link href="/css/jumbotron-narrow.css" rel="stylesheet">
<title>Hello, world!</title>
</head>
fragments.footer.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<div class="footer" th:fragment="footer">
<p>© Hello Shop V2</p>
</div>
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>
실행 결과
밋밋한 결과가 나타난 것을 볼 수 있다.
이러한 밋밋함을 해결할 수 있도록 Bootstrap 사이트에 가서 Compiled CSS and JS 를 다운한다.
Download · Bootstrap (getbootstrap.com)
다운받으면 css, js 가 생긴다.
이걸 복사해서 resources 아래에 static 에 css, js 를 붙여넣기 한다.
이제 실행해보면 아까보다는 괜찮은 레이아웃이 나타나는 것을 볼 수 있다.
여기서 가운데 정렬을 해서 좀 더 이쁘게 만들어보도록 하자.
css 폴더에 jumbotron-narrow.css 를 추가하고 다음과 같이 작성한다.
/* Space out content a bit */
body {
padding-top: 20px;
padding-bottom: 20px;
}
/* Everything but the jumbotron gets side spacing for mobile first views */
.header,
.marketing,
.footer {
padding-left: 15px;
padding-right: 15px;
}
/* Custom page header */
.header {
border-bottom: 1px solid #e5e5e5;
}
/* Make the masthead heading the same height as the navigation */
.header h3 {
margin-top: 0;
margin-bottom: 0;
line-height: 40px;
padding-bottom: 19px;
}
/* Custom page footer */
.footer {
padding-top: 19px;
color: #777;
border-top: 1px solid #e5e5e5;
}
/* Customize container */
@media (min-width: 768px) {
.container {
max-width: 730px;
}
}
.container-narrow > hr {
margin: 30px 0;
}
/* Main marketing message and sign up button */
.jumbotron {
text-align: center;
border-bottom: 1px solid #e5e5e5;
}
.jumbotron .btn {
font-size: 21px;
padding: 14px 24px;
}
/* Supporting marketing content */
.marketing {
margin: 40px 0;
}
.marketing p + h4 {
margin-top: 28px;
}
/* Responsive: Portrait tablets and up */
@media screen and (min-width: 768px) {
/* Remove the padding we set earlier */
.header,
.marketing,
.footer {
padding-left: 0;
padding-right: 0;
}
/* Space out the masthead */
.header {
margin-bottom: 30px;
}
/* Remove the bottom border on the jumbotron for visual effect */
.jumbotron {
border-bottom: 0;
}
}
깔끔하게 정렬된 것을 확인할 수 있다.
회원 등록
회원가입 화면 설계를 해보도록 하자.
우선, 회원의 이름, 도시, 거리, 우편번호를 입력할 수 있도록 돕는 클래스를 생성하고자 한다.
MemberForm 클래스
@Getter @Setter
public class MemberForm {
@NotEmpty(message = "회원 이름은 필수 입니다")
private String name;
private String city;
private String street;
private String zipcode;
}
@NotEmpty 를 사용하면 name 이 없을 때 오류가 발생하게 된다.
@NotEmpty 를 쓰려면 의존성을 추가해줘야 한다.
build.gradle 에 가서 다음을 추가해주자.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-validation'
...
}
html 은 templates 아래에 members 디렉터리를 만들고 createMemberForm.html 을 생성한다.
createMemberForm.html 은 아래와 같이 작성한다.
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/header :: header" />
<style>
.fieldError {
border-color: #bd2130;
}
</style>
<body>
<div class="container">
<div th:replace="fragments/bodyHeader :: bodyHeader"/>
<form role="form" action="/members/new" th:object="${memberForm}"
method="post">
<div class="form-group">
<label th:for="name">이름</label>
<input type="text" th:field="*{name}" class="form-control"
placeholder="이름을 입력하세요"
th:class="${#fields.hasErrors('name')}? 'form-control fieldError' : 'form-control'">
<p th:if="${#fields.hasErrors('name')}"
th:errors="*{name}">Incorrect date</p>
</div>
<div class="form-group">
<label th:for="city">도시</label>
<input type="text" th:field="*{city}" class="form-control" placeholder="도시를 입력하세요">
</div>
<div class="form-group">
<label th:for="street">거리</label>
<input type="text" th:field="*{street}" class="form-control"
placeholder="거리를 입력하세요">
</div>
<div class="form-group">
<label th:for="zipcode">우편번호</label>
<input type="text" th:field="*{zipcode}" class="form-control"
placeholder="우편번호를 입력하세요">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<br/>
<div th:replace="fragments/footer :: footer" />
</div> <!-- /container -->
</body>
</html>
이제 컨트롤러를 구현하여 회원가입 버튼을 누르면 /members/new 로 이동하게 되는데 위에서 만든
createMemberForm.html 으로 이동할 수 있도록 구현한다.
@Controller
@RequiredArgsConstructor
public class MemberController {
private final MemberService memberService;
@GetMapping("/members/new")
public String createForm(Model model) {
model.addAttribute("memberForm", new MemberForm());
return "members/createMemberForm";
}
}
정상적으로 화면이 나타난 것을 볼 수 있다.
이번엔 컨트롤러에 이름 및 주소를 입력하고 전송버튼을 누르면 디비에 반영되면서 동시에 홈 화면으로
리다이렉션할 수 있도록 코드를 구현해보도록 하자.
@PostMapping("/members/new")
public String create(@Valid MemberForm form) {
Address address = new Address(form.getCity(), form.getStreet(), form.getZipcode());
Member member = new Member();
member.setName(form.getName());
member.setAddress(address);
memberService.join(member);
return "redirect:/";
}
정상적으로 들어가는 것을 확인할 수 있다.
이번엔 회원가입 화면에서 아무것도 입력하지 않은채 전송버튼을 누를 경우 똑같은 화면에 다시 진입할 수 있도록
하면서 "회원 이름은 필수입니다" 라는 문구가 뜰 수 있도록 컨트롤러를 바꿔보자.
@PostMapping("/members/new")
public String create(@Valid MemberForm form, BindingResult result) {
if (result.hasErrors()) {
return "members/createMemberForm";
}
Address address = new Address(form.getCity(), form.getStreet(), form.getZipcode());
Member member = new Member();
member.setName(form.getName());
member.setAddress(address);
memberService.join(member);
return "redirect:/";
}
BindingResult 객체를 통해 에러가 발견될 경우 다시 회원가입 화면으로 이동할 수 있도록 한 것이다.
여기서 "회원 이름은 필수 입니다" 라는 문구가 떴는데 이는 MemberForm 에서 이미 지정하였다.
@Getter @Setter
public class MemberForm {
@NotEmpty(message = "회원 이름은 필수 입니다")
private String name;
...
}
위와 같이 @NotEmpty 을 통해 이름을 입력하지 않을 경우 에러가 발생되는데 그 때 message 를
위와 같이 설정하였기 때문이다.
회원 목록 조회
이번엔 회원 목록 버튼을 누르면 회원 정보들이 뜨는 것을 구현해보도록 하자.
우선 회원 정보들이 뜰 수 있도록 하는 html 을 생성해야 한다.
members 패키지 아래에 memberList.html 을 아래와 같이 생성한다.
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/header :: header" />
<body>
<div class="container">
<div th:replace="fragments/bodyHeader :: bodyHeader" />
<div>
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>이름</th>
<th>도시</th>
<th>주소</th>
<th>우편번호</th>
</tr>
</thead>
<tbody>
<tr th:each="member : ${members}">
<td th:text="${member.id}"></td>
<td th:text="${member.name}"></td>
<td th:text="${member.address?.city}"></td>
<td th:text="${member.address?.street}"></td>
<td th:text="${member.address?.zipcode}"></td>
</tr>
</tbody>
</table>
</div>
<div th:replace="fragments/footer :: footer" />
</div> <!-- /container -->
</body>
</html>
그리고 컨트롤러에서 현재 데이터베이스에 저장된 멤버 전체 정보가 리스트로 받아서 html 에 for - loop 을 통해
각 멤버들의 정보들을 띄울 수 있도록 한다.
@GetMapping("/members")
public String list(Model model) {
List<Member> members = memberService.findMembers();
model.addAttribute("members", members);
return "members/memberList";
}
위와 같은 경우는 회원 정보를 입력하지 않아서 빈 경우이다.
회원 정보를 여러개 등록하면 아래와 같이 나타나게 된다.
상품 등록
이번엔 상품 등록버튼을 누르면 상품 등록이 되도록 해보자.
우선 상품은 책에 대한 상품만 등록할 수 있도록 하는 화면을 구성하려고 한다.
따라서, 책을 담을 수 있도록 돕는 클래스 BookForm 을 생성한다.
@Getter @Setter
public class BookForm {
private Long id;
private String name;
private int price;
private int stockQuantity;
private String author;
private String isbn;
}
그리고 상품이 등록될 수 있는 html 을 생성한다.
items 패키지를 생성하고 createItemForm.html 을 아래와 같이 작성하여 넣는다.
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/header :: header" />
<body>
<div class="container">
<div th:replace="fragments/bodyHeader :: bodyHeader"/>
<form th:action="@{/items/new}" th:object="${form}" method="post">
<div class="form-group">
<label th:for="name">상품명</label>
<input type="text" th:field="*{name}" class="form-control"
placeholder="이름을 입력하세요">
</div>
<div class="form-group">
<label th:for="price">가격</label>
<input type="number" th:field="*{price}" class="form-control"
placeholder="가격을 입력하세요">
</div>
<div class="form-group">
<label th:for="stockQuantity">수량</label>
<input type="number" th:field="*{stockQuantity}" class="form-control" placeholder="수량을 입력하세요">
</div>
<div class="form-group">
<label th:for="author">저자</label>
<input type="text" th:field="*{author}" class="form-control"
placeholder="저자를 입력하세요">
</div>
<div class="form-group">
<label th:for="isbn">ISBN</label>
<input type="text" th:field="*{isbn}" class="form-control"
placeholder="ISBN을 입력하세요">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<br/>
<div th:replace="fragments/footer :: footer" />
</div> <!-- /container -->
</body>
</html>
마지막으로 ItemController 클래스를 생성한다.
@Controller
@RequiredArgsConstructor
public class ItemController {
private final ItemService itemService;
@GetMapping("/items/new")
public String createForm(Model model) {
model.addAttribute("form", new BookForm());
return "items/createItemForm";
}
@PostMapping("/items/new")
public String create(BookForm form) {
Book book = new Book();
book.setName(form.getName());
book.setPrice(form.getPrice());
book.setStockQuantity(form.getStockQuantity());
book.setAuthor(form.getAuthor());
book.setIsbn(form.getIsbn());
itemService.saveItem(book);
return "redirect:/";
}
}
정상적으로 데이터베이스에 반영되는 것을 볼 수 있다.
'Spring' 카테고리의 다른 글
웹 계층 개발 - (2) (0) | 2022.09.08 |
---|---|
주문 도메인 개발 (0) | 2022.09.06 |
상품 도메인 개발 (0) | 2022.09.02 |
회원 도메인 개발 (1) | 2022.09.02 |
애플리케이션 구현 준비 (2) | 2022.09.02 |