티스토리 뷰
Chapter4. 스프링 MVC 애플리케이션의 계층적 구조 : '도서 쇼핑몰' 계층적 공통 모듈 만들기
4.1 웹 애플리케이션의 계층적 구조
4.1.1 계층적 구조
계층 구조는 관심사를 분리해서 각 계층을 느슨하게 결합하고 계층 간에 유연하게 동작시킬 수 있다.
계층적 구조 없이 한곳에서 모든 작업을 한꺼번에 한다면 코드의 복잡성이 증가하고 유지보수의 어려움과 유연성 부족 등의 문제가 발생할 수 있다.
계층적 구조는 퍼시스턴스 계층, 서비스 계층, 프레젠테이션 계층으로 분리한다. 스프링 MVC에서 MVC패턴은 프레젠테이션 게층의 일부이다.
- 도메인 객체 : 데이터 모델로(DO), 객체 정보를 저장(DTO)하는 곳이다.
- 퍼시스턴스 계층 : 데이터 액세스 계층(DAO)이라고도 한다. 데이터베이스나 파일에 접근하여 데이터를 처리하는 곳이다.
- 서비스 계층 : 비즈니스 계층이라고도 한다. 비즈니스 계층이라고 하는 것은 단위작업을 한다는 말이다. 애플리케이션이 제공하는 포괄적인 서비스를 표현한다. 클라이언트에서 요청한 데이터를 가져오거나 변경하려고 퍼시스턴스 계층(DAO)을 호출하며, 프레젠테이션 계층(컨트롤러)과 퍼시스턴스 계층 사이를 연결하는 역할을 한다.
- 프레젠테이션 계층 : 애플리케이션과 사용자의 최종 접점이다. 사용자에게서 데이터를 입력받거나(=request) 데이터 결과를 웹 서버에 전달(=response)하여 사용자에게 보여 주는 계층이다. 애플리케이션 요청을 받아들여 처리하며 동시에 처리된 결과를 사용자에게 보여 준다.
4.1.2 계층적 구조의 구현 과정
일반적으로 퍼시스턴스 계층과 서비스 계층은 스프링의 주요 특징 중 하나인 객체 간 결합을 느슨하게 연결하는데 인터페이스를 사용한다. 인터페이스는 나중에 소스 코드를 변경하거나 유지 보수할 떄 유연하게 대응할 수 있도록 하기 위해 사용한다.
아래는 계층적 구조의 구현 과정을 그림으로 표현한 것이다.
* 핸들러 어댑터는 핸들러매핑과 디스패처 서블렛에 연결할때 사용된다.
* 하지만 사용하는 순서대로 만드는 것이 좋다. 반대로 프레젠테이션 계층부터 만든 후 마지막은 뷰를 만든다.
4.2 도서 쇼핑몰의 계층적 공통 모듈 만들기
4.2.2 도서 기본 정보가 담긴 도메인 객체 (=DTO)
도메인 객체는 애플리케이션에서 다른 계층들의 토대가 되는 역할을 하는 데이터 객체이다.
이 도서 쇼핑몰 애플리케이션에서 도메인 객체는 도서 목록을 표시하는 개별 도서들의 정보를 의미한다.
즉, 책의 정보를 표시한다.
도메인 객체는 데이터 모델로 필요한 속성(필드,변수)들을 정의하고 각 속성에 Setter()와 Getter() 메서드를 만들어 주어야 한다. (Setter() Getter()은 private이라 사용한다.)
도메인 객체 Book 클래스 생성하기 : Book.java
Book 클래스를 생성하고 생성한 Book 클래스 안에 필드(변수)를 선언한다.
package com.springmvc.domain;
public class Book
{
private String bookId;
private String name;
private int unitPrice;
private String author;
private String description;
private String publisher;
private String category;
private long unitsInStock;
private String releaseDate;
private String condition;
기본 생성자, 일반 생성자 추가하기
// 기본 생성자 생성
public Book() {
super();
}
// 일반 생성자
public Book(String bookId, String name, int unitPrice) {
super();
this.bookId = bookId;
this.name = name;
this.unitPrice = unitPrice;
}
Setter() Getter() 메서드 추가하기
직접 입력하는 것이 아닌 source를 클릭해 Generate Getters and Stters 를 선택하여 만들어 준다.
//getter setter
public String getBookId() {
return bookId;
}
public void setBookId(String bookId) {
this.bookId = bookId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getUnitPrice() {
return unitPrice;
}
public void setUnitPrice(int unitPrice) {
this.unitPrice = unitPrice;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getPublisher() {
return publisher;
}
public void setPublisher(String publisher) {
this.publisher = publisher;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public long getUnitsInStock() {
return unitsInStock;
}
public void setUnitsInStock(long unitsInStock) {
this.unitsInStock = unitsInStock;
}
public String getReleaseDate() {
return releaseDate;
}
public void setReleaseDate(String releaseDate) {
this.releaseDate = releaseDate;
}
public String getCondition() {
return condition;
}
public void setCondition(String condition) {
this.condition = condition;
}
모두 작성하면 아래와 같은 코드가 생성된다.
package com.springmvc.domain;
public class Book
{
private String bookId;
private String name;
private int unitPrice;
private String author;
private String description;
private String publisher;
private String category;
private long unitsInStock;
private String releaseDate;
private String condition;
// 기본 생성자 생성
public Book() {
super();
}
// 일반 생성자
public Book(String bookId, String name, int unitPrice) {
super();
this.bookId = bookId;
this.name = name;
this.unitPrice = unitPrice;
}
//getter setter
public String getBookId() {
return bookId;
}
public void setBookId(String bookId) {
this.bookId = bookId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getUnitPrice() {
return unitPrice;
}
public void setUnitPrice(int unitPrice) {
this.unitPrice = unitPrice;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getPublisher() {
return publisher;
}
public void setPublisher(String publisher) {
this.publisher = publisher;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public long getUnitsInStock() {
return unitsInStock;
}
public void setUnitsInStock(long unitsInStock) {
this.unitsInStock = unitsInStock;
}
public String getReleaseDate() {
return releaseDate;
}
public void setReleaseDate(String releaseDate) {
this.releaseDate = releaseDate;
}
public String getCondition() {
return condition;
}
public void setCondition(String condition) {
this.condition = condition;
}
}
4.2.3 도서 정보를 관리하는 퍼시스턴스 계층 (DAO)
퍼시스턴스 계층은 일반적으로 도메인 객체에 접근할 수 있는 저장소 객체이다.
데이터 액세스 계층(DAO)을 의미한다.
애너테이션을 사용하여 표현할 수 있고 @Repository를 선언하면 해당 클래스는 저장소 객체라는 의미이다.
지금은 DB를 안만들었기 때문에 데이터베이스를 대신해 메모리에 도서 정보를 저장하고 관리한다.
* ArrayList라인은 DB를 순차적으로 들어가서 대기열이 생긴다.
* Read 라인은 Arralist는 비었는지 확인한 후 한번도 조회하지 않으면 DB 연결하고 가져온다.
조회했을 떄 조회가 되면 ArrayList 에서 사용한다.
BookRepository.java
인터페이스를 먼저 만들어준다.
package com.springmvc.repository;
import java.util.List;
import com.springmvc.domain.Book;
//만들어질 함수를 미리 정의하는 것
public interface BookRepository {
List<Book> getAllBookList();
}
BookRepositoryImpl.java
인터페이스를 구현하는 클래스를 만든다.
이때 인터페이스를 구현하기 위해 java class 만드는 창에서 interfaces에 add를 클릭한다.
그 후 interface이름을 확인하고 add해준다.
BookRepositoryImpl.java만들면
package com.springmvc.repository;
import java.util.List;
import com.springmvc.domain.Book;
public class BookRepositoryImpl implements BookRepository {
@Override
public List<Book> getAllBookList() {
// TODO Auto-generated method stub
return null;
}
}
DB가 없으므로 입력해준다.
package com.springmvc.repository;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Repository;
import com.springmvc.domain.Book;
@Repository
public class BookRepositoryImpl implements BookRepository {
private List<Book> listOfBooks = new ArrayList<Book>();
public BookRepositoryImpl() {
Book book1 = new Book("ISBN1234", "C# 교과서", 30000);
book1.setAuthor("박용준");
book1.setDescription(
"C# 교과서』는 생애 첫 프로그래밍 언어로 C#을 시작하는 독자를 대상으로 한다. 특히 응용 프로그래머를 위한 C# 입문서로, C#을 사용하여 게임(유니티), 웹, 모바일, IoT 등을 개발할 때 필요한 C# 기초 문법을 익히고 기본기를 탄탄하게 다지는 것이 목적이다.");
book1.setPublisher("길벗");
book1.setCategory("IT전문서");
book1.setUnitsInStock(1000);
book1.setReleaseDate("2020/05/29");
Book book2 = new Book("ISBN1235", "Node.js 교과서", 36000);
book2.setAuthor("조현영");
book2.setDescription(
"이 책은 프런트부터 서버, 데이터베이스, 배포까지 아우르는 광범위한 내용을 다룬다. 군더더기 없는 직관적인 설명으로 기본 개념을 확실히 이해하고, 노드의 기능과 생태계를 사용해보면서 실제로 동작하는 서버를 만들어보자. 예제와 코드는 최신 문법을 사용했고 실무에 참고하거나 당장 적용할 수 있다.");
book2.setPublisher("길벗");
book2.setCategory("IT전문서");
book2.setUnitsInStock(1000);
book2.setReleaseDate("2020/07/25");
Book book3 = new Book("ISBN1236", "어도비 XD CC 2020", 25000);
book3.setAuthor("김두한");
book3.setDescription(
"어도비 XD 프로그램을 통해 UI/UX 디자인을 배우고자 하는 예비 디자이너의 눈높이에 맞게 기본적인 도구를 활용한 아이콘 디자인과 웹&앱 페이지 디자인, UI 디자인, 앱 디자인에 애니메이션과 인터랙션을 적용한 프로토타이핑을 학습합니다.");
book3.setPublisher("길벗");
book3.setCategory("IT활용서");
book3.setUnitsInStock(1000);
book3.setReleaseDate("2019/05/29");
listOfBooks.add(book1);
listOfBooks.add(book2);
listOfBooks.add(book3);
}
@Override
public List<Book> getAllBookList() {
// TODO Auto-generated method stub
return listOfBooks;
}
}
4.2.4 요청한 도서 목록을 반환하는 서비스 계층
서비스 계층은 애플리케이션이 제공하는 포괄적인 서비스들을 표현하는 계층이다. 프레젠테이션 계층과 퍼시스턴스 계층 사이를 연결한다.
서비스 계층은 비즈니스 인데 이것은 단위작업을 한다. 단위작업으로 생성되어 트랜잭션이 실행된다. (작업 취소도 단위로 취소된다.)
다른 계층과 구분은 @Service 애너테이션을 이용하여 표현한다. @Service를 선언할 경우 클래스가 서비스 객체임을 의미한다.
BookService.java
인터페이스를 생성한다.
package com.springmvc.service;
import java.util.List;
import com.springmvc.domain.Book;
public interface BookService {
List<Book> getAllBookList();
}
BookServiceImpl.java
이전에 만들었던 인터페이스를 implments 구현한다.
package com.springmvc.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.springmvc.domain.Book;
import com.springmvc.repository.BookRepository;
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookRepository bookRepository;
@Override
public List<Book> getAllBookList() {
// TODO Auto-generated method stub
return bookRepository.getAllBookList();
}
}
4.2.5 MVC를 담당하는 프레젠테이션 계층
프레젠테이션 계층은 웹 브라우저로 들어오는 요청을 처리하여 처리 결과를 웹 브라우저에 표현한다.
프레젠테이션 계층은 웹에서 들어오는 요청을 처리하는 자바 클래스인 컨트롤러, 웹 요청의 처리 결과를 웹 브라우저에 보여 주는 jsp 웹 페이지인 뷰, jsp 웹 페이지에 출력할 데이터인 모델을 포함한다.
요청한 도서 목록을 호출하는 컨트롤러 구현하기 : BookController.java
package com.springmvc.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.springmvc.domain.Book;
import com.springmvc.service.BookService;
@Controller
public class BookController {
@Autowired
private BookService bookService;
@RequestMapping(value="/books", method=RequestMethod.GET)
public String requestBookgList(Model model) {
List<Book> list = bookService.getAllBookList();
model.addAttribute("bookList",list);
return "books";
}
}
값이 books일 경우 위의 코드를 호출하여 실행하도록 작성했다.
컨트롤러와 의존 관계의 빈 객체 등록하기 : servlet-context.xml
아래와 같이 base-package를 "com.springmvc.*"로 바꿔주면 springmvc 안에 있는 annotation한 객체(=@)는 모두 다 자동 생성해준다.
<context:component-scan base-package="com.springmvc.*" />
도서 목록을 출력하는 뷰 구현하기 : books.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<!DOCTYPE html>
<html>
<head>
<link href="./resources/css/bootstrap.min.css" rel="stylesheet">
<meta charset="utf-8">
<title>도서 목록</title>
</head>
<body>
<nav class="navbar-expand navbar-dark bg-dark">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="./home">Home</a>
</div>
</div>
</nav>
<div class="jumbotron">
<div class="container">
<h1 class="display-3">도서 목록</h1>
</div>
</div>
<div class="container">
<div class="row" align="center">
<c:forEach items="${bookList}" var="book">
<div class="col-md-4">
<h3>${book.name}</h3>
<p>${book.author}
<br>${book.publisher} | ${book.releaseDate}
<p align=left>${fn:substring(book.description,0,100) }...
<p>${book.unitPrice}원
</div>
</c:forEach>
</div>
<hr>
<footer>
<p>© WebMarket</p>
</footer>
</div>
</body>
</html>
'코딩 > spring' 카테고리의 다른 글
[15주 4일차] 컨트롤러 구현 (2) (0) | 2024.01.18 |
---|---|
[15주 3일차] 컨트롤러 구현 (0) | 2024.01.17 |
[15주 2일차] 도서 쇼핑몰 프로젝트 (0) | 2024.01.16 |
[15주 2일차] 스프링 MVC의 프로젝트 구조 (0) | 2024.01.16 |
[15주 1일차] 스프링 MVC 개발 환경 설정 (0) | 2024.01.15 |
- Total
- Today
- Yesterday