1. Layered Architecture
- Layered Architecture는 소프트웨어 개발에서 일반적으로 널리 사용되는 아키텍처를 지칭하는 용어.
- 프로그램을 개발할 때 몇 개의 계층(Layer)를 이용할 지에 따라서 그 형태가 달라지는 데 일반적으로 많이 사용하는 것은 4계층이다. (4-Tier Architecture)
- 객체지향적 개념으로 보자면 관심사의 분리(Seperation of Concern)와 동일한 개념이라고 생각하면 된다.
- Layered Architecture는 소프트웨어 시스템을 여러 개의 논리적인 계층으로 나누어 구조화하는 방법론.
- 각 계층은 특정한 역할을 수행하며, 시스템을 유연하고 유지보수가 용이하도록 만든다.
- 각 계층은 서로 분리되어 있어, 한 계층의 변경이 다른 계층에 미치는 영향을 최소화한다.
- 이렇게 각 계층은 자신의 역할에 집중하며, 시스템을 구조화하여 유지보수와 확장을 용이하게 한다.
- 변경이 필요한 경우 한 계층만 수정하면 되므로 시스템의 유연성이 향상된다.
- Presentation Layer : 사용자 인터페이스(UI), HTML, CSS, JavaScript
- 화면을 만들어주는 Class ⇒ View 라고 한다.
- Business Layer : 비즈니스 로직을 처리하고, 시스템의 핵심 기능을 구현, 주문을 처리하거나 계산을 수행하는 등의 작업 (데이터를 이용해서 로직을 수행, 단, DB 처리 제외)
- Service(실제 로직을 담당하는 객체), (Domain) Model이라고 하는 데이터 객체가 포함된다. (VO)
- Persistence Layer : 데이터의 영구적인 저장과 관리 담당, 데이터베이스와의 상호작용을 처리하고, 데이터를 저장하거나 불러오는 등의 작업, CRUD
- 데이터베이스를 핸들링하는 객체(DAO - Data Access Object)
- Database Layer : 실제 데이터를 저장하고 관리, 데이터베이스 관리 시스템(DBMS)
2. MVC Pattern
- MVC는 소프트웨어 시스템을 3가지 타입의 component로 분할해서 개발하는 소프트웨어 개발 패턴.
- Model(M) - Service (Model), Domain Model(VO)
- View(V) - 사용자 interface 담당
- Controller(C) - 시스템 흐름제어(View와 Service 간의 Bridge)
3. 도서 검색 프로그램을 MVC 형태로 구현
- View : Class 하나 이용해서 화면 처리를 해야 한다. FXML로 처리한다.
- booksearch.controller.BookSearchController.java
package booksearch.controller;
import booksearch.service.BookSearchService;
import booksearch.vo.BookVO;
import javafx.beans.Observable;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import java.net.URL;
import java.util.ResourceBundle;
public class BookSearchController implements Initializable {
@FXML private TextField textField;
@FXML private Button searchBtn;
@FXML private Button deleteBtn;
@FXML private TableView tableView;
@FXML private TableColumn<BookVO, String> isbn;
@FXML private TableColumn<BookVO, String> title;
@FXML private TableColumn<BookVO, Integer> price;
@FXML private TableColumn<BookVO, String> author;
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
isbn.setCellValueFactory(new PropertyValueFactory<>("bisbn"));
title.setCellValueFactory(new PropertyValueFactory<>("btitle"));
price.setCellValueFactory(new PropertyValueFactory<>("bprice"));
author.setCellValueFactory(new PropertyValueFactory<>("bauthor"));
searchBtn.setOnAction(e -> {
BookSearchService service = new BookSearchService();
String keyword = textField.getText();
ObservableList<BookVO> data = service.searchBookByKeyword(keyword);
tableView.setItems(data);
});
deleteBtn.setOnAction(e -> {
BookVO selectedBook = (BookVO) tableView.getSelectionModel().getSelectedItem();
if (selectedBook == null) {
System.out.println("삭제할 책을 선택해주세요.");
return;
}
String keywordDelete = selectedBook.getBisbn();
String currentKeyword = textField.getText();
BookSearchService service = new BookSearchService();
ObservableList<BookVO> updatedData = service.deleteBookByKeyword(keywordDelete);
ObservableList<BookVO> searchData = service.searchBookByKeyword(currentKeyword);
tableView.setItems(searchData);
tableView.getSelectionModel().clearSelection();
});
}
}
- booksearch.dao.BookDAO.java
package booksearch.dao;
import booksearch.vo.BookVO;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class BookDAO {
private Connection con;
public BookDAO() {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
String id = "root";
String pw = "jiyun9163!";
String JDBC_URL = "jdbc:mysql://localhost:3306/library?characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true";
con = DriverManager.getConnection(JDBC_URL, id, pw);
} catch (Exception e) {
System.out.println(e);
}
}
public ObservableList<BookVO> select(String keyword) {
String sql = "SELECT bisbn, btitle, bprice, bauthor FROM book " + "WHERE btitle Like ?";
ObservableList<BookVO> data = FXCollections.observableArrayList();
try {
PreparedStatement pstmt = con.prepareStatement(sql);
pstmt.setString(1, "%" + keyword + "%");
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
BookVO book = new BookVO(
rs.getString("bisbn"),
rs.getString("btitle"),
rs.getInt("bprice"),
rs.getString("bauthor"));
data.add(book);
}
} catch (Exception e) {
}
return data;
}
public ObservableList<BookVO> delete(String keyword) {
String sqlDelete = "DELETE FROM book WHERE bisbn = ?";
String sqlSelect = "SELECT bisbn, btitle, bprice, bauthor FROM book";
ObservableList<BookVO> data = FXCollections.observableArrayList();
try {
PreparedStatement pstmtDelete = con.prepareStatement(sqlDelete);
pstmtDelete.setString(1, keyword);
int rowsAffected = pstmtDelete.executeUpdate();
if (rowsAffected > 0) {
System.out.println(keyword + "을(를) 삭제했습니다.");
PreparedStatement pstmtSelect = con.prepareStatement(sqlSelect);
ResultSet rs = pstmtSelect.executeQuery();
while (rs.next()) {
BookVO book = new BookVO(
rs.getString("bisbn"),
rs.getString("btitle"),
rs.getInt("bprice"),
rs.getString("bauthor"));
data.add(book);
}
} else {
System.out.println("삭제할 데이터가 없습니다.");
}
} catch (Exception e) {
System.out.println("삭제 중 오류 발생: " + e.getMessage());
e.printStackTrace();
}
return data;
}
}
- booksearch.service.BookSearchService.java
package booksearch.service;
import booksearch.dao.BookDAO;
import booksearch.vo.BookVO;
import javafx.collections.ObservableList;
public class BookSearchService {
public ObservableList<BookVO> searchBookByKeyword(String keyword) {
BookDAO dao = new BookDAO();
ObservableList<BookVO> result = dao.select(keyword);
return result;
}
public ObservableList<BookVO> deleteBookByKeyword(String keyword) {
BookDAO dao = new BookDAO();
ObservableList<BookVO> result = dao.delete(keyword);
return result;
}
}
- booksearch.vo.BookVO.java
package booksearch.vo;
public class BookVO {
private String bisbn;
private String btitle;
private int bprice;
private String bauthor;
public BookVO() {
}
public BookVO(String bisbn, String btitle, int bprice, String bauthor) {
this.bisbn = bisbn;
this.btitle = btitle;
this.bprice = bprice;
this.bauthor = bauthor;
}
public String getBisbn() {
return bisbn;
}
public void setBisbn(String bisbn) {
this.bisbn = bisbn;
}
public String getBtitle() {
return btitle;
}
public void setBtitle(String btitle) {
this.btitle = btitle;
}
public int getBprice() {
return bprice;
}
public void setBprice(int bprice) {
this.bprice = bprice;
}
public String getBauthor() {
return bauthor;
}
public void setBauthor(String bauthor) {
this.bauthor = bauthor;
}
}
- booksearch.view.JavaFXBookSearchTableView.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.FlowPane?>
<BorderPane fx:controller="booksearch.controller.BookSearchController" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1">
<bottom>
<FlowPane prefHeight="28.0" prefWidth="600.0" BorderPane.alignment="CENTER">
<children>
<Label prefHeight="60.0" prefWidth="60.0" text="검색키워드">
<FlowPane.margin>
<Insets left="10.0" />
</FlowPane.margin>
</Label>
<TextField fx:id="textField" prefHeight="39.0" prefWidth="263.0">
<FlowPane.margin>
<Insets left="10.0" />
</FlowPane.margin>
</TextField>
<Button fx:id="searchBtn" mnemonicParsing="false" prefHeight="31.0" prefWidth="103.0" text="검색">
<FlowPane.margin>
<Insets left="20.0" />
</FlowPane.margin>
</Button>
<Button fx:id="deleteBtn" mnemonicParsing="false" prefHeight="30.0" prefWidth="105.0" text="선택된 책 삭제">
<FlowPane.margin>
<Insets left="20.0" />
</FlowPane.margin>
</Button>
</children>
</FlowPane>
</bottom>
<center>
<TableView fx:id="tableView" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<columns>
<TableColumn fx:id="isbn" prefWidth="75.0" text="책 ISBN" />
<TableColumn fx:id="title" prefWidth="322.0" text="책 제목" />
<TableColumn fx:id="price" minWidth="0.0" prefWidth="75.0" text="책 가격" />
<TableColumn fx:id="author" prefWidth="106.0" text="책 저자" />
</columns>
</TableView>
</center>
</BorderPane>
package booksearch.view;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class BookSearchView extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
Parent root = null;
FXMLLoader loader = new FXMLLoader(
getClass().getResource("JavaFXBookSearchTableView.fxml"));
try {
root = loader.load();
} catch (Exception e) {
System.err.println(e);
}
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}