열심히 끝까지

디바이스 융합 자바(Java) day18 - [재코딩] 자판기 with MVC 본문

디바이스 융합 자바(Java)기반 풀스택 개발자 양성과정(수업내용)

디바이스 융합 자바(Java) day18 - [재코딩] 자판기 with MVC

노유림 2022. 6. 30. 15:43

[day18]

클라이언트
      ex)
          사용자
          웹 브라우저
          >> 같은 단어

>> 어플 객체
Controller app = new Controller();
        app -> new 어플 객체

- 유지보수가 용이한 프로그램 제작하려면?
   > 모듈의 크기를 줄여야 한다.(잘게잘게 쪼개야 한다)
         ex ) 레고
                     : 크기가 큰 레고조각으로 만들 수 있는 것이 얼마 없지만
                       크기가 작은 여러 개의 레고조각으로는
                       여러가지를 만들 수  있다.

사용자가 View에서 입력한 값들은 Controller까지 도착한다!
    Why?
       >> Controller에서 View의 메서드를 호출했기 때문!
       >> 추가를 하면...
           > 사용자가 추가하려고 잠깐잠깐 입력한 것들은 갈색
              천년만년 저장하는 것이 아니기 때문에 지역변수!
           > Controller에서는 사용자가 입력한 값을
              일반변수(지역변수!, 멤버변수가 아니다!!)에 저장해둔다.
       >> Model에서는 실질적인 기능을 수행한다!!
             >> 핵심 로직/ 비즈니스 메서드/ CRUD
                : 사용자가 입력한 값을 필요로 하므로
                  Controller가 이 값을 전달해준다!


---ProductVO-------

package model;
// 웹 개발에서, 일반적으로 기본생성자를 사용하여 VO를 생성(new)
public class ProductVO {
	private int num; // PK
	private String name;
	private int cnt; // 재고 데이터
	private int price;
	/*
	public ProductVO(int num, String name, int price) {
		this(num, name, price, 0);
		// 재고 지정이 안되어 있으면 0으로 디폴트
	}
	public ProductVO(int num, String name, int price, int cnt) {
		this.num = num;
		this.name = name;
		this.price = price;
		this.cnt = cnt;
	}
	*/
	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;
	}
	@Override
	public String toString() {
		return "ProductVO [num=" + num + ", name=" + name + ", cnt=" + cnt + ", price=" + price + "]";
	}
}

 

 

---ProductDAO-------

package model;
import java.util.ArrayList;
// CRUD
// 주석으로 좀 크게 쳐져있는 코드는 내가 짠 코드
// 강사님으 코드와 비교해서 해볼 것!
public class ProductDAO {
	// DB를 대체하는 컬렉션(배열리스트)
	ArrayList<ProductVO> datas;
	private int pk = 101; // 후위증감 연산을 선호한다
	// 어딘가에서 DAO 사용할텐데, 
	// DAO가 생성되면 그 즉시 datas도 생성되게끔 하고 싶다!
	// == DAO의 멤버변수인 datas도 값을 가질 수 있게 만들고 싶어요~
	// 생성자를 써야한다.
	public ProductDAO() { // default로 만들면 다른 패키지에서 쓸 수 없다...
		// 앞으로 public을 붙여서 사용!
		datas= new ArrayList<ProductVO>();
		ProductVO vo = new ProductVO();
		/*
		ProductVO vo1 = new ProductVO();
		ProductVO vo2 = new ProductVO();
		ProductVO vo3 = new ProductVO();
		 */
		// 초기데이터(혹은 샘플 데이터)
		/*
		datas.add(new ProductVO(pk++, "콜라", 1200));
		datas.add(new ProductVO(pk++, "사이다", 900, 5));
		datas.add(new ProductVO(pk++, "환타", 800, 2));
		 */
		/*
		vo1.setName("콜라");
		vo1.setPrice(1200);
		vo1.setNum(pk++);
		datas.add(vo1);
		vo2.setName("사이다");
		vo2.setPrice(900);
		vo2.setCnt(5);
		vo2.setNum(pk++);
		datas.add(vo2);
		vo3.setName("환타");
		vo3.setPrice(800);
		vo3.setCnt(2);
		vo3.setNum(pk++);
		datas.add(vo3);
		 */
		vo.setCnt(2);
		vo.setName("콜라");
		vo.setNum(pk);
		vo.setPrice(1000);
	}
	// 관리자
	// 새로운 음료 추가
	// 음료 재고 추가
	// 음료 삭제
	// 사용자(서비스)
	// 음료 출력
	// 음료 구매
	public ArrayList<ProductVO> selectAll(ProductVO vo) { // getAll()
		//DB에서는 선택하는 행위를 SelectAll()로 쓰이는 경우가 많다.
		// 여기서 출력을 한다?? 잘못된거다!
		// 직접 로직을 구현하는 경우가 있는데 그거 잘못된것이다!!
		// View측에서 무엇을 필요로 할 지 고려!
		System.out.println("로그 : selectAll()");
		return datas;
	}
	public ProductVO selectOne(ProductVO vo) { // getOne() R에 해당
		for(ProductVO data : datas) {
			if(data.getNum() == vo.getNum()) {
				System.out.println("로그 : " + data + " 반환");
				return data;
			}
		}
		System.out.println("로그 : 반환xxx -> 확인 필요!");
		return null; // 만날 확률이 거의 없다.
		// 이상한 숫자가 들어오게 되면 null값을 되돌려준다.
		// 이상한 숫자가 들어올 일이 없기 때문이다.
		// 유효성검사를 View가 해줄 것이다.
	}
	// 음료 구매
	// 사용자가 pk 입력
	// pk에 해당하는 음료를 출력
	// 구매 시도 시 음료의 재고 -- 
	/*
	public boolean buy(ProductVO vo) { // U에 해당
		// 실무에서는 다르게 작성하기도 하다(void도 하지만 boolean값도 쓰인다
		// update의 성공 여부
		if(vo==null) {
			System.out.println("없는 메뉴입니다!");
			return false;
		}
		if(vo.getCnt()==0) {
			System.out.println("로그 : " + vo.getNum() + " 재고가 없습니다.");
			return false;
		}
		vo.setCnt(vo.getCnt()-1); 
		System.out.println("로그 : " + vo.getNum() + " 구매 완료!");
		return true;
	}
	*/
	// 음료추가
	/*
	public boolean insert(ProductVO vo) {
		// boolean값으로 하는 것이 일반적이다 => 성공하면 true, 실패하면 false
		// 현재 프로그램 생성자가 오버로딩되어있어서
		// 재고가 없어도 메뉴생성이 가능함
		// -> 메서드 오버로딩
		return false;
	}
	 */
	public boolean insert(ProductVO vo) {
		// 강사님의 코드
		try {
			vo.setNum(pk++);
			datas.add(vo);
		}catch(Exception e) {
			e.printStackTrace();
			return false;
		}
		return true;
		/* 고쳐본 코드
		try {
			// productVO에 새 값 추가
			vo.setNum(pk++);
			datas.add(vo);
			System.out.println("   로그 : 메뉴추가성공!");
			return true;
		}
		catch(Exception e){ // 예외발생 시 로그 남겨주기
			System.out.println("    로그 : 메뉴추가실패 : 확인이 필요한 예외발생!");
			e.printStackTrace(); // 어떤 예외인지 출력!
			return false;
		}
		 */
	}
	// 재고 추가
	// 1. 결제를 하면 -1만 되는(한 개씩만 구매가 되는 상황)
	// 2. 결제가 원하는 개수만큼 되는 것 -N
	// 3. 결제 완료 시 총 금액 => 재고 혹은 금액을 반환하려면 뭐가 반환될지 몰라 ProductVO를 출력값을 해야 함
	//    Controller에서 update를 마치고 selectOne()을 수행하는 것이 낫다
	// 4. 재고를 추가했을 때 추가 되는 것 +N
	public boolean update(ProductVO vo) { // 상품 넘버, 재고량!
		for(ProductVO data : datas) { // 처음부터 끝까지 돈다
			if(data.getNum()==vo.getNum()) { // 만약 넘버가 같으면?
				data.setCnt((data.getCnt()+vo.getCnt()));
				// M: 결제를 할지, 재고추가를 할 지 난 모름
				//      -값        +값
				return true; // 성공하면 true반환
			}
		}
		return false; // 실패
	}
	public boolean delete(ProductVO vo) {
		for(ProductVO data : datas) {
			if(data.getNum()-100==vo.getNum()) {
				datas.remove(data);
				return true;
			}
		}
		return false; // 실패
	}
}
// 이걸로 model의 역할을 끝!

 

 

---ProductView-------

package view;
import java.util.ArrayList;
import java.util.Scanner;
import model.ProductVO;
public class ProductView {
	Scanner sc = new Scanner(System.in);
	public int action;
	public void startView() {
		while(true) { // 유효성 검사 -> 꼼꼼하게 작업해보기!
			System.out.println("=== 자판기 ===");
			System.out.println("1.메뉴확인 2.구매 3.종료");
			System.out.print("입력) ");
			action = sc.nextInt();
			if(action >= 1 && action <=3 || action == 1234) { // 관리자모드 1234
				break;
			}
			System.out.println("범위외입력!");
		}
	}
	/*
	public void manageView() {
		while(true) {
			System.out.println("=== 관리자모드 ===");
			System.out.println("1.음료추가 2.음료재고추가 3.음료삭제 4.종료");
			System.out.print("입력) ");
			action = sc.nextInt();
			if(action >= 1 && action <= 4) {
				break;
			}
			System.out.println("범위외입력!");
		}
	}
	public void funcM1(ArrayList<ProductVO> datas) {
		System.out.print("추가할 음료와 가격을 입력하세요) ");
		String name = sc.next();
		int price = sc.nextInt();
	}
	 */
	public void func1(ArrayList<ProductVO> datas) {
		int i = 1;
		for(ProductVO vo : datas) {
			System.out.println(i + "번 메뉴 " + vo.getName() + "[" + vo.getCnt() + "] : " + vo.getPrice() );
			i++;
		}
	}
	public int func2(ArrayList<ProductVO> datas) {
		for(int i = 0; i < datas.size(); i++) { // 다시 한번 출력 + 재고도 체크
			System.out.print(datas.get(i).getNum()+"번 메뉴");
			if(datas.get(i).getCnt()==0) {
				System.out.println("x");
				continue;
			}
			System.out.println(datas.get(i).getName());
		}
		while(true) {
			System.out.print("메뉴 입력 ) ");
			int menu = sc.nextInt();
			if(1<=menu && menu <= datas.size()) {
				return menu;
			}
			System.out.println("상품번호 확인 후 다시 입력해주세요!");
		}
		// action += 100; -> Controller 에서 해야한다고 강사님은 생각!
		// 상품번호가 몇번인지 모르는게 맞기 때문에!
	}
	public void funcT(ProductVO vo) {
		System.out.println(vo.getName() + "구매완료!");
	}
	public void funcF(ProductVO vo) {
		System.out.println(vo.getName() + " 재고가 없습니다...");
	}
	public void func3() {
		System.out.println("프로그램이 종료됩니다");
		for(int i = 0; i < 5; i++) {
			System.out.println(".");
			try {
				Thread.sleep(1000); // 1000당 1초 재운다 == sleep
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} 
		}
		System.out.println();
	}
	// 1~3 메뉴를 보여주는 출력문구(겹친건 지운다)
	// 음료추가
	// 음료이름입력
	// 음료가격입력
	// 음료재고입력
	// 음료 삭제
	// 수행이 완료되었습니다!(삭제 + 추가)
	// 수행실패! 다음에 다시 이용해주세요..
	// 음료재고추가
	// 번호입력
	// 음료삭제
	// 번호입력<- 위에 있으니 필요없다
	public void admin_func1() { // 관리자모드
		// return할 필요없이 이렇게 진행해도 된다!
		while(true) { // 무한반복(if문 확인되면 break)
			System.out.println("=== 관리자 모드 ===");
			System.out.println("1.음료추가 2.재고추가 3.음료삭제 4.관리자모드 종료");
			System.out.print("입력) ");
			action = sc.nextInt();
			if(1<=action&&action<=4) {
				break;
			}
			System.out.println("범위외입력!");
		}
	}
	public void admin_func2() { // 음료 추가
		System.out.println("음료추가 메뉴입니다.");
	}
	// 음료를 만드는 메서드로 묶어버리면
	// 재고를 추가하는 기능을 한번 더 만들어야한다.
	// 한 줄만 뜰 수 있게 만들게 다 따로 만드는 것이 좋다!!
	public void admin_func3() { // 음료이름입력
		System.out.println("음료 이름을 입력해주세요... ");
	}
	public void admin_func4() { // 음료가격입력
		System.out.println("음료 가격을 입력해주세요... ");
	}
	public void admin_func5() { // 음료재고입력
		System.out.println("음료 재고를 입력해주세요... ");
	}
	public void admin_func6() { // 수행이 완료되었습니다!
		System.out.println("수행이 완료되었습니다!");
	}
	public void admin_func7() { // 수행실패! 다음에 다시 이용해주세요...
		System.out.println("수행실패! 다음에 다시 이용해주세요...");
	}
	public void admin_func8() { // 음료재고추가
		System.out.println("음료재고추가 메뉴입니다.");
	}
	public void admin_func9() { // 번호입력
		System.out.print("번호를 입력해주세요... ");
	}
	public void admin_func10() { // 음료 삭제
		System.out.println("음료삭제 메뉴입니다.");
	}
	public int inputInt() {
		System.out.print("정수 입력) ");
		int i = sc.nextInt();
		return i;
	}
	public String inputString() {
		System.out.print("문자열 입력) ");
		String str = sc.next();
		return str;
	}
}

 

 

---ProductController-------

package controller;
import model.ProductDAO;
import model.ProductVO;
import view.ProductView;
public class ProductController {
	ProductDAO model;
	ProductView view;
	public ProductController() {
		model = new ProductDAO();
		view = new ProductView();
	}
	public void startApp() {
		while(true) {
			view.startView();
			if(view.action==1) {
				ProductVO vo = new ProductVO();
				view.func1(model.selectAll(vo));
			}
			else if(view.action==2) {
				ProductVO vo = new ProductVO();
				int num = view.func2(model.selectAll(vo));
				// 사용자가 먹고 싶은 메뉴의 번호를 입력햇는데,
				// C에서 [view.action]
				// C 에서는 사용자가 입력한 메뉴의 번호를 view.action으로 참조 가능하고,
				// M은 pk를 입력받으면 상품객체를 줄 준비가 되어있습니다.
				vo.setNum(num);
				vo = model.selectOne(vo);
				// pk를 넣으면 해당하는 상품을 반환하는 M의 selectOne()메서드
				if(vo==null) {
					continue;
				}
				vo.setCnt(/*vo.getCnt()*/-2);
				System.out.println("로그1 : "+vo);
				boolean flag = model.update(vo);
				// M은 반환받은(==사용자가 선택한) 객체의 재고를 --
				System.out.println("로그2 : " + model.selectOne(vo));
				ProductVO data2 = model.selectOne(vo);
				int cnt = vo.getCnt();
				// 구매한 개수
				int price = data2.getPrice();
				int total = cnt*price*-1;
				System.out.println("로그 : 총 결제 금액은 : " + total + "원");
				// boolean flag=model.update(model.selectOne(view.action+100)); // 상품객체 전달
				if(flag) {
					view.funcT(vo);
				}
				else {
					view.funcF(vo);
				}
			}
			else if(view.action==3) {
				view.func3();
				break;
			}
			else if(view.action == 1234) { // 관리자모드
				while(true) {
					view.admin_func1();
					if(view.action==1) { // 메뉴 추가
						ProductVO vo = new ProductVO();
						view.admin_func2();
						view.admin_func3();
						String name = view.inputString();
						vo.setName(name);
						view.admin_func4();
						int price = view.inputInt();
						vo.setPrice(price);
						view.admin_func5();
						int cnt = view.inputInt();
						vo.setCnt(cnt);
						boolean flag = model.insert(vo);
						if(flag) { //
							view.admin_func6();
						}
						else {
							view.admin_func7();
						}
					}
					else if(view.action==2) { // 메뉴 재고 업데이트
						ProductVO vo = new ProductVO();
						view.admin_func8();
						view.func1(model.selectAll(vo)); // 메뉴 전부 호출
						view.admin_func9(); // 메뉴번호
						int num = view.inputInt();
						view.admin_func5(); // 메뉴 재고
						int cnt = view.inputInt();
						vo.setNum(num);
						vo.setCnt(cnt);
						boolean flag = model.update(vo);
						if(flag) { // 만약 업데이트가 되면?
							view.admin_func6(); // 출력
						}
						else {
							view.admin_func7();
						}
					}
					else if(view.action==3) { // 메뉴 제거
						ProductVO vo = new ProductVO();
						view.admin_func10();
						view.admin_func9();
						int num = view.inputInt();
						vo.setNum(num);
						boolean flag = model.delete(vo);
						if(flag) {
							view.admin_func6();
						}
						else {
							view.admin_func7();
						}
					}
					else if(view.action==4) {
						view.func3(); // 숫자넣지 말고 영어로 기능을 유추할 수 있게
						break;
					}
				}
			}
		}
	}
}