이번 포스팅에서는 MVC의 개념과 각 단계에서의 역할, 작업 순서와 작업 내용, 이론을 활용한 예제, DAO & VO를 활용한 여러개의 데이터 저장 방법, 그리고 그의 사용 실습예시에 대해 다루었다.
1. 기본개념
: MVC는 Model, View, Controller의 약자이다. 각각이 무엇을 의미하고 다루는지에 대해 알아보자.
- Model : DB와 연동되는 재고 데이터. DBMS(MySQL, Oracle)로 저장된 데이터를 java로 가져오는 역할을 한다.
- View : 사용자 공간(console, browser). 제공대상자(pc, phone..)가 다를 수 있으며, 그에 따라 화면구성이 상이하다. 사용자 입력에 대한 유효성검사 등이 진행되는 공간이다.
- Controller : Model과 View를 연결하는 역할을 하는데, 이는 코드의 응집도를 높이기 위한 목적이 있다.
*코드의 응집도*
유지보수와 직접적인 관련이 있다. 수정사항이 발생했을경우, 1개의 수정사항에 대해서 1번의 코드 변경이 일어나는 코드를 응집도가 높은 코드라고하며, 반대로 1개의 수정사항에 대해서 N번의 코드 변경이 일어나는 코드를 응집도가 낮다고 표현한다. 따라서 코드의 응집도가 높을수록 유지보수가 용이한 코드이므로 코딩을 할때는 항상 코드의 응집도를 높일 수 있도록 작업하는 방향을 지향해야한다.
2. 작업순서와 내용
각 작업은 보편적으로 설계 후 여러 개발자들과 동시다발적으로 발생하기 때문에, 설계방향 이외의 부분은 다른 작업자의 상황을 알 수 없으므로 단독적인 코딩이 없어야한다.
혼자서 작업을 진행하는 경우에는 뷰 -> 모델 -> 컨트롤러 순으로 작업을 진행하고, 각 단계에 대한 내용을 정리해보았다.
- View
- 프론트엔드의 작업이다.
- 선택사항 출력, 사용자의 입력을 설계단계에서 결정된 멤버변수로 저장하는 역할(Public)
- 이외로는 view의 구성에 필요한 다양한 메서드들을 생성하는 작업
- 메서드들의 예시로는 사용자가 원하는 부분의 출력, 종료문 출력 등이 있다.
- Model
- Java에서의 DB 그 자체라고 할 수 있다.
- 다양한 정보들이 담겨있으므로 보안을 위해 멤버변수에는 일반적으로 접근권한을 private으로 지정한다.
- Model에 담겨있는 정보와 직접 상호작용해야하는 메서드를 생성한다.(ex. 구매 -> 재고-사용자의 입력이므로)
- Controller
- View와 Model의 연결을 위해 존재하므로, 모든 작업상황에서 View와 Model사이의 직접 접근이 절대적으로 없어야한다. 즉, 필수로 controller를 거칠 수 있도록 한다.
- 작업 시작단계에서 V, M에 접근할 목적으로 상단에 변수를 먼저 선언하고, 컨트롤러의 실행메서드 안에서 선언된 변수의 객체화를 진행한다. -> 프로그램 실행 전 리소스처리
모든 단계가 진행되면 확인을 위해 다른 클래스(Client)를 생성하여, main만을 통해 컨트롤러의 객체화를 통해 모든 메서들을 수행시켜볼 수 있다.
3. 예제
: 오렌지주스를 뽑는 자판기 프로그램 구현.
- View
: 프로그램의 시작화면과, 이외의 프로그램 작동을 위한 메서드들이 이곳에서 생성된다.package view; import java.util.InputMismatchException; import java.util.Scanner; public class DrinkView { Scanner sc = new Scanner(System.in); public int action; String startMsg = "1. 구매 \n2. 재고확인 \n3. 가격확인 \n4. 프로그램종료"; // 프로그램 첫 화면 public void startView() { while(true) { System.out.println("음료 자판기 입니다"); System.out.println(); System.out.println(startMsg); System.out.println("=========="); System.out.println(">>>"); try { int action = sc.nextInt(); if(action<1 || 4<action) { throw new Exception("범위 외 입력!"); } // 혹시혹시라도 올바르지 않은 입력이 this.action에 들어갈까봐 // 위에서 별도의 action변수를 선언, but 필수는 아님 this.action = action; //정상적인 값임이 보장됨! break; //정상적으로 입력되면 종료되도록 } catch(InputMismatchException e) { sc.nextLine(); System.out.println("타입 확인!"); continue; } catch(Exception e) { System.out.println("예외발생 : " + e.getMessage()); } } } // 예쁜 종료를 위한 메서드,,! public void endView() { for(int i=0; i<5; i++) { System.out.println("★☆★☆★☆"); try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println("안녕!"); } // 무엇이든 출력해주는 메서드! (이름이든 가격이든 다 : 최상위클래스) public void printView(Object obj) { System.out.println("사용자가 원하는 데이터는.."); System.out.println(obj); System.out.println("..입니다!"); } public int printPay() { System.out.println("구매할 개수 입력>>"); int cnt = sc.nextInt(); //유효성체크필요 return cnt; //다른 곳에서도 cnt정보 써야하니까 리턴 } }
- Model
: 데이터와 직접접근해야 하는 경우가 발생하는 경우 해당 클래스에서 로직만을 제공한다. 이때 다른 부분의 고려는 생각할 필요는 없다.package model; public class OrangeJuice { // 멤버변수 생성 private String name; private int cnt; private int price; // getter & setter public String getName() { return name; } public void setName(String name) { this.name = name; } public int getCnt() { return cnt; } public void setCnt(int cnt) { this.cnt = cnt; } public String getPrice() { return "가격: " + price + "원"; } public void setPrice(int price) { this.price = price; } @Override public String toString() { return "Drink [name=" + name + ", cnt=" + cnt + ", price=" + price + "]"; } // 구매 : 로직만 제공 // 경계값, 유효성, 객체유무 다 상관없고, // 내꺼 하나 주는 로직만 제공하면 된다. public void pay(int cnt) { if(this.cnt<cnt) { //재고없음 System.out.println("로그:재고없음"); return; } //구매완료 System.out.println("로그:구매완료"); this.cnt-=cnt; } // 이 모델클래스의 정보 // 오렌지주스를 만들어주는 작업 public void startModel() { this.name = "오렌지주스"; this.cnt = 3; this.price = 3500; } }
- Controller
: V와 M사이에서 접근이 발생하는 모든 부분은 이곳에서 각 클래스의 객체들을 통해 연결해주도록 한다.package controller; import java.util.Scanner; import model.OrangeJuice; import view.DrinkView; public class DrinkController { Scanner sc = new Scanner(System.in); // 두 클래스에 접근할 목적 OrangeJuice model; DrinkView view; // 위 클래스의 객체를 만들겠다는 말 public void startController() { model = new OrangeJuice(); view = new DrinkView(); //오렌지주스모델의 정보를 불러옴 model.startModel(); } public void startApp() { while(true) { view.startView(); if(view.action==1) { // 입력받은만큼 구매 model.pay(view.printPay()); } else if (view.action==2) { //2. 재고확인 -> model접근 -> view보여주기 view.printView("재고: " + model.getCnt() + "개"); } else if (view.action==3) { //3. 가격확인 -> model접근 -> view보여주기 view.printView(model.getPrice()); } else if (view.action==4) { view.endView(); break; //여기서 아래 괄호 거치고 거치고 나와 // return; 여기서 바로종료 } } } }
- 전체 작동 확인
package class01; import controller.DrinkController; public class DrinkClient { public static void main(String[] args) { DrinkController ctrl = new DrinkController(); ctrl.startController(); ctrl.startApp(); } }
[출력확인 : ★ -> 입력]
음료 자판기 입니다
1. 구매
2. 재고확인
3. 가격확인
4. 프로그램종료
==========
>>>
2
사용자가 원하는 데이터는..
재고: 3개
..입니다!
음료 자판기 입니다
1. 구매
2. 재고확인
3. 가격확인
4. 프로그램종료
==========
>>>
3
사용자가 원하는 데이터는..
가격: 3500원
..입니다!
음료 자판기 입니다
1. 구매
2. 재고확인
3. 가격확인
4. 프로그램종료
==========
>>>
1
구매할 개수 입력>>
2
로그:구매완료
음료 자판기 입니다
1. 구매
2. 재고확인
3. 가격확인
4. 프로그램종료
==========
>>>
2
사용자가 원하는 데이터는..
재고: 1개
..입니다!
음료 자판기 입니다
1. 구매
2. 재고확인
3. 가격확인
4. 프로그램종료
==========
>>>
종료
타입 확인!
음료 자판기 입니다
1. 구매
2. 재고확인
3. 가격확인
4. 프로그램종료
==========
>>>
4
★☆★☆★☆
★☆★☆★☆
★☆★☆★☆
★☆★☆★☆
★☆★☆★☆
안녕!
View 클래스의 printView메서드에서는 사용자가 원하는 데이터를 출력하는 기능을 하지만, 해당 데이터에 대한 단위(n원, n개 등의 표시)가 어려우므로 Controller와 Model클래스에서 각각 다른 방법으로 단위표시를 진행한 것을 알 수 있다.
4. DAO & VO : 여러개의 데이터 저장
: DAO와 VO는 Model에서 여러개의 데이터를 저장할때 사용되는 클래스이다.
1) VO
- DB는 객체화언어가 아니므로 객체화가 필수이다.
- DB에 존재하는 데이터를 Java객체로 전환시키는 역할을 한다.
- 데이터들에게 공통적으로 들어가야하는 전체적인 정보들을 담아두는 클래스이다.
- 멤버변수, 게터세터, 생성자 등등 객체에서 필요한 함수를 담는다.
package model;
// DB에 존재하는 데이터 -> JAVA객체로 만들기
public class ProductVO {
//멤버변수
private int num; //pk
private String name;
private int cnt;
//ver업데이트로 추가된 정보
private int price;
private String content;
// 생성자
public ProductVO(int num, String name, int cnt) {
this.num = num;
this.name = name;
this.cnt = cnt;
}
//ver업데이트로 인한 생성자 추가
public ProductVO(int num, String name, int cnt, int price, String content) {
super();
this.price = price;
this.content = content;
}
// getter & setter
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getcnt() {
return cnt;
}
public void setcnt(int cnt) {
this.cnt = cnt;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
2) DAO
- Java객체들이 수행하는 로직을 구현한 클래스
- VO클래스의 객체들은 이곳에서 생성된다.
- 비즈니스메서드만을 담는다.
private int pk = 1001;
// 리스트 생성을 위한 변수 선언
// datas = 전체 데이터들 그 자체!
ArrayList <ProductVO> datas;
// 생성자
// DAO가 만들어지는 순간에 초기데이터가 생성된다는 의미
public ProductDAO() {
datas = new ArrayList <ProductVO> ();
datas.add(new ProductVO(pk++, "후드티", 2, 20000, "기모를 사용한 후드티"));
datas.add(new ProductVO(pk++, "운동화", 3, 21000, "가벼운 운동화"));
}
: DAO에서 메서드 구현 전의 작업
* 비즈니스 메서드 *
: 핵심로직 -> CRUD -> 서비스 메서드(제공 메서드)
*CRUD*
보통의 CRUD는 아래와 같은 5가지가 기본으로 구축된다.(응집도를 높히기 위해 인자는 VO클래스로 할 것)
- insert( );
// 삽입 = VO를 datas리스트에 넣는것 public void insert(ProductVO vo) { // 상품등록! datas.add(new ProductVO(pk++, vo.getName(), vo.getcnt(), vo.getPrice(), vo.getContent())); System.out.println("로그 : insert완료"); }
- 삽입 메서드.
- VO를 배열리스트에 넣는 작업, 사용자의 입력을 배열리스트에 넣는 작업, 뷰의 입력을 모델에 저장하는 작업 등 3개의 문장과 같은 뉘앙스로 작업하는 메서드이다.
- selectOne( );
- 하나의 data만 선택하여 확인하는 메서드 (ex. 로그인, 게시물 1개 보기 등)
- T / F의 결과만 반환하는 경우와 검색된 객체를 반환하는 경우로 나누어진다.(둘 중 하나 선택)
- T / F으로 반환되는 경우는 Output이 boolean으로 처리되며, 인증처리와 같은 경우에 사용된다.
// T/F를 반환하는 메서드 : 인증처리 public boolean selectOne(int pk) { for(int i=0; i<datas.size(); i++) { if(datas.get(i).getNum()==pk) { return true; } } return false; }
- 검색된 객체를 반환하는 경우는 Output이 VO클래스이며, 로그인과 게시물 선택하여 보기의 경우의 사용된다.
// 검색된 객체를 반환하는 메서드 : 로그인, 게시글, ... public ProductVO selectOne(ProductVO vo) { ProductVO data = null; //입력된 pk가 i의 pk와 같다면! for(int i=0; i<datas.size(); i++) { if(datas.get(i).getNum()==vo.getNum()) { data=datas.get(i); break; } } System.out.println("로그 : " + data + "이(가) 반환됩니다."); return data; } // null이 반환될 수 있음에 주의할 것! // 에러는 아니지만 오류의 이유를 찾기 어렵다
- selectAll( );
- 게시글의 목록보기, 검색하기 등의 기능을 구현한다.
- 목록보기의 기능
public ArrayList <ProductVO> selectAll() { System.out.println("로그 : selectAll 완료"); return datas; }
- 검색하기의 기능
public ArrayList <ProductVO> selectAll (ProductVO vo) { //검색결과담을 리스트 ArrayList<ProductVO> al = new ArrayList<ProductVO>(); for(int i=0; i<al.size(); i++) { if(datas.get(i).getcnt()>=vo.getcnt()) { al.add(datas.get(i)); } } System.out.println("로그 : selecAll 완료"); return al; }
- delet( );
- 삭제
// Output이 boolean이 되도록 코드구현해도 상관 없다. public void delet(int pk) { for(int i=0; i<datas.size(); i++) { if(datas.get(i).getNum()==pk) { datas.remove(i); System.out.println("로그 : delet완료"); return; //리턴을 만나면 출력 } } System.out.println("로그 : delet실패"); // 리턴 못만나면 출력 }
- 삭제
- upDate( );
- 수정, 변경
public void update(ProductVO vo) { for(int i=0; i<datas.size(); i++) { if(datas.get(i).getNum()==vo.getNum()) { //이름과 상세설명중 하나or둘 다 재설정하고싶을때의 로직 if(vo.getName() != null) { datas.get(i).setName(vo.getName()); } if(vo.getContent() != null) { datas.get(i).setContent(vo.getContent()); } System.out.println("로그 : update완료"); return; //리턴을 만나면 출력 } } System.out.println("로그 : update실패"); // 리턴 못만나면 출력 }
- 수정, 변경
아래로는 전체 코드블럭을 붙여서 첨부하였다.
package model;
import java.util.ArrayList;
// JAVA 객체들(VO)이 수행하는 로직을 구현
public class ProductDAO {
private int pk = 1001;
// 리스트 생성을 위한 변수 선언
// datas = 전체 데이터들 그 자체!
ArrayList <ProductVO> datas;
// 생성자
// DAO가 만들어지는 순간에 초기데이터가 생성된다는 의미
public ProductDAO() {
datas = new ArrayList <ProductVO> ();
datas.add(new ProductVO(pk++, "후드티", 2, 20000, "기모를 사용한 후드티"));
datas.add(new ProductVO(pk++, "운동화", 3, 21000, "가벼운 운동화"));
}
// 삽입 = VO를 datas리스트에 넣는것
public void insert(ProductVO vo) { // 상품등록!
datas.add(new ProductVO(pk++, vo.getName(), vo.getcnt(), vo.getPrice(), vo.getContent()));
System.out.println("로그 : insert완료");
}
// ex) 게시글 1개보기, 로그인하기, ....
// 2개의 종류로 나누어진다.(둘 중 하나만 쓰임)
// public boolean selectOne(int pk) {
// // 1. T/F를 반환하는 메서드 : 인증처리
//
// for(int i=0; i<datas.size(); i++) {
// if(datas.get(i).getNum()==pk) {
// return true;
// }
// }
// return false;
// }
public ProductVO selectOne(ProductVO vo) {
// 2. 검색된 객체를 반환하는 메서드 : 로그인, 게시글, ...
//입력된 pk가 i의 pk와 같다면!
ProductVO data = null;
for(int i=0; i<datas.size(); i++) {
if(datas.get(i).getNum()==vo.getNum()) {
vo=datas.get(i);
break;
}
}
System.out.println("로그 : " + data + "이(가) 반환됩니다.");
return data;
// -> null이 반환될 수 있음에 주의할 것! - 에러는 아니지만 오류의 이유를 못찾아,,
}
// ex) 게시글 목록보기, 검색하기, ....
/* public ArrayList <ProductVO> selectAll() { //목록보기
System.out.println("로그 : selectAll 완료");
return datas;
}*/
public ArrayList <ProductVO> selectAll (ProductVO vo) { //검색하기
ArrayList<ProductVO> al = new ArrayList<ProductVO>(); //검색결과담을 리스트
for(int i=0; i<al.size(); i++) {
if(datas.get(i).getcnt()>=vo.getcnt()) {
al.add(datas.get(i));
}
}
System.out.println("로그 : selecAll 완료");
return al;
}
// public void update(int index) { //수정,변경 -> 복잡해서 오늘 안해,,,
// datas.get(index).cc();
// }
public void delet(int pk) { // boolean도 갠짜나
for(int i=0; i<datas.size(); i++) {
if(datas.get(i).getNum()==pk) {
datas.remove(i);
System.out.println("로그 : delet완료");
return; //리턴을 만나면 출력
}
}
System.out.println("로그 : delet실패"); // 리턴 못만나면 출력
}
//
public void update(ProductVO vo) {
for(int i=0; i<datas.size(); i++) {
if(datas.get(i).getNum()==vo.getNum()) {
//이름과 상세설명중 하나or둘 다 재설정하고싶을때의 로직
if(vo.getName() != null) {
datas.get(i).setName(vo.getName());
}
if(vo.getContent() != null) {
datas.get(i).setContent(vo.getContent());
}
System.out.println("로그 : update완료");
return; //리턴을 만나면 출력
}
}
System.out.println("로그 : update실패"); // 리턴 못만나면 출력
}
}
'JAVA' 카테고리의 다른 글
[MVC] 학생관리시스템 : 예제 (0) | 2022.01.17 |
---|---|
[컬렉션 프레임워크] 학생관리시스템_배열리스트 (0) | 2022.01.15 |
[파일 입출력] TXT & 이미지형식 (+Wrapper Class) (0) | 2022.01.14 |
[Thread] 블록 & 동기화 (0) | 2022.01.13 |
[예외처리] 예외화 & 예외미루기 (0) | 2022.01.13 |