MVC 모델

● 소프트웨어 설계시 사용되는 디자인 패턴
● 만드는 방법이 아닌 잘 만드는 방법에 대해 배우는 것
● 구성요소들의 재사용
● 확정성 증가
● 중복 코딩 제거
● 각요소들에 집중

M : Model(데이터와 대응)
데이터와 비즈니스 로직을 관리합니다.
V : View (보여지는 화면)
레이아웃과 화면을 처리합니다.
C : Controller (흐름제어)
명령을 모델과 뷰 부분으로 라우팅합니다.

Model

● 모델은 앱이 포함해야할 데이터가 무엇인지를 정의합니다.

데이터의 상태가 변경되면 모델을 일반적으로 뷰에게 알리며(따라서 필요한대로 화면을 변경할 수 있습니다) 가끔 컨트롤러에게 알리기도 합니다(업데이트된 뷰를 제거하기 위해 다른 로직이 필요한 경우).

애플리케이션의 정보, 데이터를 나타냅니다. 데이터베이스, 처음의 정의하는 상수, 초기화값, 변수 등을 뜻합니다. 또한 이러한 DATA, 정보들의 가공을 책임지는 '컴포넌트'를 말합니다.

이 모델은 다음과 같은 규칙을 가지고 있습니다.

 1. 사용자가 편집하길 원하는 모든 데이터를 가지고 있어야 한다. 
   즉, 화면안의 네모박스에 글자가 표현된다면, 네모박스의 화면 위치 정보, 네모박스의 크기정보, 
   글자내용, 글자의 위치, 글자의 포맷 정보 등을 가지고 있어야 한다.

2. 뷰나 컨트롤러에 대해서 어떤 정보도 알지 말아야 한다.
   데이터 변경이 일어났을 때 모델에서 화면 UI를 직접 조정해서 수정할 수 있도록 
   뷰를 참조하는 내부 속성값을 가지면 안 된다는 말입니다.

3. 변경이 일어나면, 변경 통지에 대한 처리방법을 구현해야만 한다.
  모델의 속성 중 텍스트 정보가 변경이 된다면, 이벤트를 발생시켜 누군가에게 전달해야 하며, 
  누군가 모델을 변경하도록 요청하는 이벤트를 보냈을 때 이를 수신할 수 있는 처리 방법을 
  구현해야 합니다. 또한 모델은 재사용가능해야 하며 다른 인터페이스에서도 변하지 않아야 합니다. 

4. Model은 Controller와 View에 의존하지 않아야 한다.
   → Model 내부에 Controller와 View에 관련된 코드가 있으면 안된다.

View

  • 뷰는 앱의 데이터를 보여주는 방식을 정의합니다.
  • 뷰는 항목이 사용자에게 보여지는 방식을 정의하며, 표시할 데이터를 모델로부터 받습니다.
   1. 모델이 가지고 있는 정보를 따로 저장해서는 안된다.
      화면에 글자를 표시 하기 위해, 모델이 가지고 있는 정보를 전달받게 될텐데, 
      그 정보를 유지하기 위해서 임의의 뷰 내뷰에 저장하면 안됩니다. 
      단순히 네모 박스를 그리라는 명령을 받으면, 화면에 표시하기만 하고 그 
      화면을 그릴 때 필요한 정보들은 저장하지 않아야 합니다.

   2. 모델이나 컨트롤러와 같이 다른 구성요소들을 몰라야 된다.
      모델과 같은 자기 자신의 빼고는 다른 요소는 참조하거나 어떻게 동작하는지 알아서는 안됩니다. 
      그냥 뷰는 데이터를 받으면 화면에 표시해주는 역할만 가진다고 보면 됩니다.

   3. 변경이 일어나면 변경통지에 대한 처리방법을 구현해야만 한다.
      모델과 같이 변경이 일어났을 때 이른 누군가에게 변경을 알려줘야 하는 방법을 구현해야 합니다. 
      뷰에서는 화면에서 사용자가 화면에 표시된 내용을 변경하게 되면 이를 모델에게 전달해서 모델을 
      변경해야 할 것이다. 그 작업을 하기 위해 변경 통지를 구현합니다. 그리고 재사용가능하게끔 
      설계를 해야 하며 다른 정보들을 표현할 때 쉽게 설계를 해야 합니다.

  4. View는 Model에만 의존해야 하고, Controller에는 의존하면 안된다.
     View 내부에 Model의 코드만 있을 수 있고, Controller의 코드가 있으면 안된다.
     
  5. View가 Model로 부터 데이터를 받을 때는, 사용자마다 다르게 보여주어야 하는 데이터에 대해서만 받아야 한다.
  


▲ 위의 사진은 View에서 사용자한테 다르게 보여줘야 하는 부분


▲ 여기서 사용자가 공통적으로 봐야하는 부분은 '주문하기', '흰색배경' 이런 것들은 Model로 부터 받으면 안되고 View가 자체적으로 가지고 있어야 하는 부분이다.

Controller

  • 컨트롤러는 앱의 사용자로부터의 입력에 대한 응답으로 모델 및/또는 뷰를 업데이트하는 로직을 포함합니다. 데이터와 사용자인터페이스 요소들을 잇는 다리역할을 합니다.
    즉, 사용자가 데이터를 클릭하고, 수정하는 것에 대한 "이벤트"들을 처리하는 부분을 뜻합니다.

    컨트롤러 또한 다음과 같은 규칙을 이해해야 합니다.
    1. 모델이나 뷰에 대해서 알고 있어야 한다.
      모델이나 뷰는 서로의 존재를 모르고, 변경을 외부로 알리고, 수신하는 방법만 가지고 있는데
      이를 컨트롤러가 중재하기 위해 모델과 그에 관련된 뷰에 대해서 알고 있어야 합니다.
    2. 모델이나 뷰의 변경을 모니터링 해야 한다.
      모델이나 뷰의 변경 통지를 받으면 이를 해석해서 각각의 구성 요소에게 통지를 해야 합니다.
      또한, 애플리케이션의 메인 로직은 컨트롤러가 담당하게 됩니다.
    3. Controller는 Model과 View에 의존해도 된다.
      → Controller 내부에는 Model과 View의 코드가 있을 수 있다. 왜냐하면 Controller은
      View의 중개자 역할을 하면서 전체 로직을 구성하기 때문이다.
    4. View가 Model로부터 데이터를 받을 때, 반드시 Controller에서 받아야한다.

      ▲ 코드를 보면 모델인 Student 클래스로부터 학생의 데이터를 만들어서 View의 printProfile 메소드한테 파라미터로 전달한다.
      ● 사용자가 controller를 조작하면 controller는 model을 통해서 데이터를 가져오고 
        그 정보를 바탕으로 시각적인 표현을 담당하는 View를 제어해서 사용자에게 전달하게 됩니다. 

▲ 위의 그림을 보면 모델은 컨트롤러에 컨트롤러는 뷰에 뷰는 다시 유저, 유저는 다시 컨트롤러를 향해서 갑니다.

MVC Model 1

   ● View, Controller 함께 공존하는 형태
   ● 사이즈가 작고 간단한 프로젝트에 어울림
		import java.util.Scanner;

//index : 시작하는 페이지
public class Index {
	public static void main(String[] args) {
		System.out.println("22.03 개강반 최종 프로젝트 / UMS 프로그램 입니다.");
		
		Scanner sc = new Scanner(System.in);
		while(true) {
			System.out.println("1. 회원가입\n2. 로그인\n3. 나가기");
			int choice = sc.nextInt();
			
			//Controller
			if(choice == 3) {
				System.out.println("안녕히가세요");
				break;
			}
			switch(choice) {
			case 1:
				//회원가입
				new JoinView();
				break;
			case 2:
				//로그인
				new LoginView();
				break;
			default:
				System.out.println("다시 입력하세요");
			}
		}
	}
}

MVC Model 2

    ● View, Controller가 완벽하게 분리된 형태
   
    

DTO(Data Transfer Object) / VO(Value Object)

  • 양쪽으로 전송되어 오고가는 데이터들을 담은 객체
  • 여러 데이터들을 포장해서 만든 데이터 포장용 객체
  • 데이터 전송 객체
  • 실습예제에서는 사용자 정보를 JoinView에서 전달 받아 모두 포장하여 DAO로 넘겨주고 있다.
  • DTO는 로직을 가지지 않는 데이터 객체이고 getter/setter 메소드만 가진 클래스를 의미한다.
	로직이라는 것은 어떤 프로그램을 만들 때의 논리적인 흐름

DAO(Data Access Object)

  • 저장되어 있는 데이터에 접근하기 위한 객체
  • 데이터들을 관리(추가,수정,삭제,읽기)하는 메소드들이 정의되어 있다.
    → 실습 예제에서는 DBConnection을 만들어 CRUD를 처리한다.
  • DB의 data에 접근하기 위한 객체이다. DB에 접근하기 위한 로직을 분리하기 위해 사용한다.
  • 직접 DB에 접근하여 data를 삽입, 삭제, 조회 등 조작할 수 있는 기능을 수행한다.
  • MVC 패턴의 Model에서 이와 같은 일을 수행한다.
  • 데이터 접근 객체
    → DBConnection은 각 CRUD를 정의하여 외부 파일과 소통한다.
	CRUD : CREATE, READ, UPDATE, DELETE 

왜 MVC 패턴을 사용해야 할까?

● 서로 분리되어 각자의 역할에 집중할 수 있게끔하여 개발을 하고 그렇게 애플리케이션을 
  만든다면, 유지보수성, 애플리케이션의 확장성, 그리고 유연성이 증가하고, 
  중복코딩이라는 문제점 또한 사라지게 되는 것입니다.  그러기 위한 MVC패턴입니다.

예제 코드

● view / index
● 사이트 키면 제일 처음으로 나오는 화면
package view;

import java.util.Scanner;

//index : 시작하는 페이지
public class Index {
	public static void main(String[] args) {
		System.out.println("22.03 개강반 최종 프로젝트 / UMS 프로그램 입니다.");
		
		Scanner sc = new Scanner(System.in);
		while(true) {
			System.out.println("1. 회원가입\n2. 로그인\n3. 나가기");
			int choice = sc.nextInt();
			
			//Controller
			if(choice == 3) {
				System.out.println("안녕히가세요");
				break;
			}
			switch(choice) {
			case 1:
				// 회원가입
                // JoinView에서 생성자를 생성하고 코드를 작성한것을 메소드처럼 불러옴
                // 많은 데이터들의 입출력이 일어나기 때문에 코드가 길어진다.
				// 따라서 새로운 View를 띄워준다.(흐름을 이동시킨다)
				new JoinView();
				break;
			case 2:
				//로그인
				new LoginView();
				break;
			default:
				System.out.println("다시 입력하세요");
			}
		}
	}
}
● 2번째
view / JoinView
package view;

import java.util.Scanner;

import dao.UserDAO;
import dto.UserDTO;
// 회원가입을 위해 생성
public class JoinView {
	// 생성자도 메소드랑 같은 역할을 한다.
	// 호출하면 내부로 와서 내부에 있는 내용 실행이 된다.
	// JoinView 생성자에 코드를 작성하고 Index에서 메소드처럼 불러오기
	public JoinView() {
		Scanner sc = new Scanner(System.in);
        //새로 가입할 "회원의 정보들" 을 데이터베이스에 저장해야 하므로
		//기능을 구현해 놓은 DAO가 필요하고, 회원 관련된 데이터이므로
		//UserDAO를 사용해야 한다.
		UserDAO udao = new UserDAO();
		
		System.out.print("아이디 : ");
		String userid = sc.next();
        // userid를 생성할때 중복검사 할 도구가 필요하다. 
        // 데이터와 통신할 클래스를 별도로 만드는데 DAO로 관리하면 된다.
        // DAO에는 데이터베이스와 직접 통신할 DBConnection Class와 직접 소통하는 클래스로 나눌 것이다. 
        // UserDAO에서 만든 메소드를 이용해 사용자 중복 검사를 실시하자
		if(udao.checkDup(userid)) {
			System.out.print("비밀번호 : ");
			String userpw = sc.next();
			System.out.print("이름 : ");
			String username = sc.next();
			System.out.print("나이 : ");
			int userage = sc.nextInt();
			System.out.print("핸드폰 번호 : ");
			String userphone = sc.next();
			System.out.print("주소 : ");
            // 새롭게 스캐너 객체를 만든이유는 nextLine()을 제대로 사용하기 위해서
			sc = new Scanner(System.in);
			String useraddr = sc.nextLine();
			
            //여기까지 왔다면 회원 가입에 필요한 모든 정보를 입력받았다는
			//뜻이므로, UserTable에 이 모든 데이터들을 저장해 주어야 한다.
			//저장하는 기능적인 코드들은 View에서 구현하는 것이 아니고
			//Userdao에 구현해 놓고 메소드를 호출하는 식으로 사용한다.
			//호출시 넘겨주어야 할 데이터들이 총 6개나 되므로 UserDTO 타입의
			//객체 하나로 포장해서 그 객체를 매개변수에 넘겨준다.
			UserDTO user = new UserDTO(userid, userpw, username, userage, userphone, useraddr);
            // UserDAO에서는 여기서 입력한 것들을 보내주기 위해서 포장한 DTO 객체를 매개변수로 넘겨준다.
			if(udao.join(user)) {
				System.out.println("회원가입 성공!");
				System.out.println(username+"님 가입을 환영합니다!");
			}
			else {
				System.out.println("회원가입 실패 / 다시 시도해 주세요.");
			}
		}
		else {
			System.out.println("중복된 아이디가 있습니다. 다시 시도해 주세요.");
		}
	}
}
● 3번째
dao / UserDAO
package dao;

import java.util.HashSet;

import dto.UserDTO;

public class UserDAO {
	//DBConnction과 소통하기 위해 기본 생성자에 정의해둔다.
	DBConnection conn = null;
	
	public UserDAO() {
    	//UserDAO 클래스는 User에 관한 데이터만 관리하는 클래스 이므로
		//접근할 파일은 "database/UserTable.txt" 이다.
		//객체가 생성되자 마자 conn을 그 파일로 잡아준다.
		conn = new DBConnection("database/UserTable.txt");
	}
	// DBConnection클래스 안에는 데이터를 넣는 메소드를 정의했고 메소드명은 insert이다.'
    // 매개변수로 회원가입 시 받는 아이디를 받아서 insert메소드에 전달해주자.
	public boolean join(UserDTO user) {
    	//toString도 재정의 하였음.
    	//회원 가입은 새로운 데이터를 추가하는 것이므로
		//conn에 있는 insert()를 이용한다.
		return conn.insert(user.toString());
	}
	// DBConnection클래스 안에 데이터를 가져오는 select메소드가 있다. 
    // 이 메소드 타입은 Set타입으로 HashSet타입을 이용해 가져올 수 있다. 
	public boolean checkDup(String userid) {
    	//기존에 가입되어 있는 유저들의 id를 검사하는 것이므로,
		//select()를 이용해서 데이터를 검색해온다.
		//UserTable.txt 에는 0번방이 userid들이 담겨있는 열 이므로
		//0번에서 내가 입력한 userid가 존재하는지를 검색한다.
		HashSet<String> rs = conn.select(0,userid);
        // 가져온 데이터가 0일 경우 중복되는 데이터가 없다는 것이고 true이다. 
        // 우리는 이 메소드가 true를 리턴하면 회원가입 시키도록 설계할 것이다.
		return rs.size() == 0;
	}
    //로그인 메소드도 같이 정의해주자, 데이터베이스와 소통하는 클래스로 직접 소통은 DBConnection이 하지만
    // 우리는 DBConnection과 연결되도록 정의하면 된다.

	//							apple		 abcd1234
	public boolean login(String userid, String userpw) {
 // DBConnection을 통해 데이터를 가져옴
 //우리가 입력받아서 넘겨준 userid를 UserTable의 0번방에서 검색
		HashSet<String> rs = conn.select(0, userid);
        // 아이디는 중복이 불가능하므로 검색된 결과는 1개 아니면 0개이다. 
		// size가 1이라면 로그인 하려는 아이디가 존재한다는 뜻임.
		if(rs.size() == 1) {
        	//rs에서 안에 있는 line 한 줄을 꺼내오면서 반복
			for(String line : rs) {
            //그 아이디가 담겨있는 데이터 한줄을 가져와서 split하여 비밀번호만 쓴다는 거임
				//line : "apple	abcd1234	김사과	10	01012341234	서울시 강남구 역삼동"
				//"문자열1".split("문자열2") : "문자열1" 을 "문자열2" 기준으로 나누기
				// 						나뉘어진 문자열들이 담겨있는 배열 return
				//{"apple","abcd1234","김사과","10","01012341234","서울시 강남구 역삼동"}
                // 1번방에 있는 비밀번호가 내가 입력받아서 넘겨준 userpw와 같은지를 비교
				if(line.split("\t")[1].equals(userpw)) {
                // 줄을 가져와서 split을 통해 비밀번호를 가져오고  Session에 로그인 값을 저장한다음 로그인 성공을 리턴한다.
					// 로그인 성공
//					Session.datas.put("login_id",userid);
					// 로그인 된 정보를 저장하는 Session에 세팅
					// KEY : "login_id" / VALUE : "apple"(로그인 성공한 유저의 id)
					Session.put("login_id", userid);
					return true;
				}
				
			}
		}
        // 아이디 정보가 없을때, 비밀번호 안 맞을때 false;
		return false;
	}
	public String myInfo() {
		HashSet<String> rs = conn.select(0, Session.get("login_id"));
		String result = "";
		
		for(String line : rs) {
			String[] datas = line.split("\t");
			result += "======"+datas[2]+"님의 회원정보======\n";
			result += "아이디 : "+datas[0]+"\n"; 
			result += "비밀번호 : "+datas[1]+"\n"; 
			result += "핸드폰 번호 : "+datas[4]+"\n"; 
			result += "주소 : "+datas[5]+"\n"; 
		}
		return result;
	}

	public boolean modifyUser(int col, String newData) {
		//회원 정보 수정은 현재 로그인 된 유저의 정보를 수정해야 하므로 세션의 "login_id"로 저장된 것을 키값으로 넘겨준다.
		return conn.update(Session.get("login_id"), col, newData);
	}

	public boolean checkPw(String userpw) {
		HashSet<String> rs = conn.select(0,Session.get("login_id"));
		for(String line : rs) {
			return line.split("\t")[1].equals(userpw);
				
		}
		return false;
	}

	public boolean leaveId() {
		//탈퇴 진행시 현재 로그인한 유저의 아이디가 필요하므로 아래서 초기화 하기 전에 미리 백업 
		String userid = Session.get("login_id");
		//회원이 탈퇴되었으므로 로그인 유지 불가능, Session을 초기화 해준다.
		Session.put("login_id", null);
		return conn.delete(userid);
	}
}
● 4번째
DBConnection
//여기서 정의된 insert, update, delete, select를 통해 데이터베이스를 다룬다. 
package dao;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashSet;

public class DBConnection {
	String file;
	
	public DBConnection(String file) {
		this.file = file;
	}
	
	boolean insert(String data) {
		try {
			BufferedWriter bw = new BufferedWriter(new FileWriter(file,true));
			bw.write(data+"\r\n");
			bw.close();
			return true;
		} catch (IOException e) {
			System.out.println("======오류 발생 : DB 연결 실패======");
			System.out.println(e);
			System.out.println("===============================");
		}
		return false;
	}
	boolean update(String key,int col,String newData) {
		String result = "";
		boolean check = false;
		try {
			BufferedReader br =  new BufferedReader(new FileReader(file));
			while(true) {
				String line = br.readLine();
				if(line ==null) {
					break;
				}
				String[] datas=line.split("\t");
				if(datas[0].equals(key)) {
					result+=datas[0];
					for (int i = 1; i < datas.length; i++) {
						if(i==col) {
							result+="\t"+newData;
							check = true;
						}else {
							result+="\t"+datas[i];
						}
					}
					result+="\r\n";
				}else {
					result+=line+"\r\n";
				}
			}
			
		} catch (FileNotFoundException e) {
			System.out.println("======오류 발생 : DB 파일 오류======");
			System.out.println(e);
			System.out.println("===============================");
		} catch (IOException e) {
			System.out.println("======오류 발생 : DB 연결 실패======");
			System.out.println(e);
			System.out.println("===============================");
		}	
		if(check) {
			try {
				BufferedWriter bw = new BufferedWriter(new FileWriter(file));
				bw.write(result);
				bw.close();
			} catch (IOException e) {
				System.out.println("======오류 발생 : DB 연결 실패======");
				System.out.println(e);
				System.out.println("===============================");
			}
		}
		return check;
	}
	boolean delete(String key) {
		String result = "";
		boolean check = false;
		try {
			BufferedReader br =  new BufferedReader(new FileReader(file));
			while(true) {
				String line = br.readLine();
				if(line ==null) {
					break;
				}
				String[] datas=line.split("\t");
				if(datas[0].equals(key)) {
					check = true;				
				}else {
					result+=line+"\r\n";
				}
			}
			
		}catch (FileNotFoundException e) {
			System.out.println("======오류 발생 : DB 파일 오류======");
			System.out.println(e);
			System.out.println("===============================");
		} catch (IOException e) {
			System.out.println("======오류 발생 : DB 연결 실패======");
			System.out.println(e);
			System.out.println("===============================");
		}	
		if(check) {
			try {
				BufferedWriter bw = new BufferedWriter(new FileWriter(file));
				bw.write(result);
				bw.close();
			} catch (IOException e) {
				System.out.println("======오류 발생 : DB 연결 실패======");
				System.out.println(e);
				System.out.println("===============================");
			}
		}
		return check;
	}
	//"file" 안에 있는 모든 정보 검색
	HashSet<String> select() {
		HashSet<String> resultSet = new HashSet<>();
		try {
			BufferedReader br = new BufferedReader(new FileReader(file));
			while(true) {
				String line = br.readLine();
				if(line==null) {
					break;
				}
				resultSet.add(line);
			}
		} catch (FileNotFoundException e) {
			System.out.println("======오류 발생 : DB 파일 오류======");
			System.out.println(e);
			System.out.println("===============================");
		} catch (IOException e) {
			System.out.println("======오류 발생 : DB 연결 실패======");
			System.out.println(e);
			System.out.println("===============================");
		}	
		return resultSet;
	}
	HashSet<String> select(int col,String data) {
		HashSet<String> resultSet = new HashSet<>();
		try {
			BufferedReader br = new BufferedReader(new FileReader(file));
			while(true) {
				String line = br.readLine();
				if(line==null) {
					break;
				}
				String[] datas = line.split("\t");
				if(datas[col].equals(data)) {
					resultSet.add(line);//apple	abcd1234	김사과	10	01012341234	서울시 강남구 역삼동
				}
			}
		} catch (FileNotFoundException e) {
			System.out.println("======오류 발생 : DB 파일 오류======");
			System.out.println(e);
			System.out.println("===============================");
		} catch (IOException e) {
			System.out.println("======오류 발생 : DB 연결 실패======");
			System.out.println(e);
			System.out.println("===============================");
		}	
		return resultSet;
	}
	String lastPK() {
		String pk = null;
		try {
			BufferedReader br = new BufferedReader(new FileReader(file));
			while(true) {
				String line = br.readLine();
				if(line==null) {
					break;
				}
				pk=line.split("\t")[0];
			}
		} catch (FileNotFoundException e) {
			System.out.println("======오류 발생 : DB 파일 오류======");
			System.out.println(e);
			System.out.println("===============================");
		} catch (IOException e) {
			System.out.println("======오류 발생 : DB 연결 실패======");
			System.out.println(e);
			System.out.println("===============================");
		}
		return pk;
	}
}
● 5번째
dao / Session
package dao;

import java.util.HashMap;

public class Session {
//로그인이 완료된 값들(userid)를 받아서 datas에 저장하고 관리한다는 거임.
	//Session.datas.put("login_id","apple");
    //물론 이 데이터들은 밖에서 아무데나 사용하면 안됨으로 private로 만들었다.
	private static HashMap<String, String> datas = new HashMap<String, String>();

	// put메소드를 정의해주고 UserDAO에서  아래 주석과 같이 사용하게 된다. 
	//Session.put("login_id","apple");
	public static void put(String key, String value) {
    //아래 데이터와 같이 쌍으로 저장될 것이다. 
		datas.put(key, value);
	}
	public static String get(String key) {
		return datas.get(key);
	}
}
● 6번째
dto / UserDTO
회원가입을 하고 나면 많은 정보들을 처리할 곳이 필요하다. 회원가입 클래스에서 데이터를 받고
이 데이터를 저장하거나 관리하려면 DAO로 전달해주어야 한다.

package dto;

public class UserDTO {
	//Alt + Shift + A : 그리드 편집 모드(여러줄 동시에 편집)
   // 회원가입을 통해 받은 변수들이다. 
	public String userid;
	public String userpw;
	public String username;
	public int userage;
	public String userphone;
	public String useraddr;
	//아래와 같이 회원가입 view창에서 객체화를 시켜 값을 담았다. 
	public UserDTO(String userid, String userpw, String username, int userage, String userphone, String useraddr) {
		this.userid = userid;
		this.userpw = userpw;
		this.username = username;
		this.userage = userage;
		this.userphone = userphone;
		this.useraddr = useraddr;
	}
	
	@Override
	public boolean equals(Object obj) {
		if(obj instanceof UserDTO) {
			UserDTO target = (UserDTO)obj;
			
			if(target.userid.equals(this.userid)) {
				return true;
			}
		}
		return false;
	}
	
	@Override
	public String toString() {
		//apple	abcd1234	김사과	10	01012341234	서울시 강남구 역삼동
		return userid+"\t"+userpw+"\t"+username+
				"\t"+userage+"\t"+userphone+"\t"+useraddr;
	}
}

물론 여기서 바로 처리되지 않고 JoinView 클래스에서 객체화되고 그 객체가 데이터를 가지면 DAO에 전달해주게 된다. 전달 받은 데이터는 메소드를 통해 받고 이 메소드는 다시 DBConnection을 통해 데이터베이스에 저장되게 된다.

view / LoginView
마찬가지로 여기서도 UserDAO를 객체화 하여 사용한다. 사용자로부터 받은 데이터를 dao가 가지는 메소드를 통해 전달하게 되고 데이터를 전달받은 dao는 DBConnection을 통해 저장된 데이터베이스에 접근하여 값을 가져오고 userid를 비교하여 성공여부를 판단, 성공 시 Session에 로그인 정보를 전달한다.

● 7번째
package view;

import java.util.Scanner;

import dao.UserDAO;

public class LoginView {
	public LoginView() {
		Scanner sc = new Scanner(System.in);
		UserDAO udao = new UserDAO();
		
		System.out.print("아이디 : ");
		String userid = sc.next();
		System.out.print("비밀번호 : ");
		String userpw = sc.next();
		
		if(udao.login(userid,userpw)) {
			System.out.println(userid+"님 어서오세요~");
			
			//메인창 띄우기
			new MainView();
		}
		else {
			System.out.println("로그인 실패 / 다시 시도해 주세요.");
		}
	}
}

성공 시 메인창으로 가도록 코드가 작성되었다.

view / MainView
아직 구현을 다하지 않음...
session에는 영구적으로 userid가 저장된것은 아니다. 프로그램이 실행 될때마다
데이터베이스에서 select메소드를 통해 가져오도록 설계되어 있는 것이다.
session에 저장된 userid를 가져와서 사용자 정보를 보여주고 있다.

● 8번째
package view;

import java.util.Scanner;

import dao.Session;

public class MainView {
	public MainView() {
		Scanner sc = new Scanner(System.in);
//		세션에서 로그인 정보를 가져옴.
		String userid = Session.get("login_id");
		
		while(true) {
        	// 우리가 만든 프로젝트는 무조건 main()부터 시작하는 프로그램이다.
			// 즉 진입점이 한개이기 때문에 아래와 같은 코드를 생략 가능하다.
			// *웹 상이나 다른 프로그램에서는 진입점이 여러개일 수 있으므로
			// 아래처럼 로그인 되어있는지를 먼저 검사해주는것이 필요하다.
			System.out.println("☆★☆★☆★☆★"+userid+"님 어서오세요~☆★☆★☆★☆★\n"
					+ "1. 상품추가\n2. 상품수정\n3. 상품삭제\n"
					+ "4. 내 상품 보기\n5. 상품 검색\n6. 내 정보 수정\n7. 로그아웃");
			int choice = sc.nextInt();
			if(choice == 7) {
				//로그아웃
				System.out.println(Session.get("login_id")+"님 안녕히가세요~");
				//로그인한 정보를 담아주는 Session에서 login_id라는 KEY 와 쌍을 이루고 있는 값은
				//로그아웃을 했다면 없애주어야 한다. 따라서 null로 초기화 해주어야 한다.
				Session.put("login_id", null);
				break;
			}
			switch(choice) {
			case 1:
				//상품추가
				new AddProductView();
				break;
			case 2:
				//상품수정
				new ModifyProductView();
				break;
			case 3:
				//상품삭제
				//내 상품 목록 띄워주고 번호 입력받아서 삭제시키기
				System.out.println(pdao.getList());
				System.out.print("삭제할 상품 번호 : ");
				int prodnum = sc.nextInt();
				if(pdao.removeProduct(prodnum)) {
					System.out.println(prodnum+"번 상품 삭제 성공!");
				}
				else {
					System.out.println("알 수 없는 오류 / 다음에 다시 시도해 주세요.");
				}
				break;
			case 4:
				//내 상품 보기
				System.out.println("===========내가 올린 상품 목록===========");
				System.out.println(pdao.getList());
				System.out.println("===================================");
				break;
			case 5:
				//상품 검색
				System.out.print("검색어를 입력하세요 : ");
				sc = new Scanner(System.in);
				String keyword = sc.nextLine();
				
				System.out.println(pdao.search(keyword));
				System.out.print("자세히 볼 상품번호 : ");
				prodnum = sc.nextInt();
				//자세히 볼 상품번호를 선택했다면 그 번호에 해당하는 상품 정보를 띄워주는 ProductInfoView 만들기!
				//내부메뉴
				//1. 좋아요 누르기
				//2. 판매자 연락처 보기
				//3. 돌아가기
				break;
			case 6:
				//내 정보 수정
				new MyInfoView();
				break;
			}
			
		}
	}
}

내 정보 수정

	import dao.ProductDAO;
import dao.UserDAO;

public class MyInfoView {
	public MyInfoView() {
		Scanner sc = new Scanner(System.in);
		UserDAO udao = new UserDAO();
		ProductDAO pdao = new ProductDAO();
		
		System.out.println(udao.myInfo());
		System.out.println("1. 비밀번호 수정\n2. 핸드폰 번호 수정\n3. 주소 수정\n4. 수정 취소\n5. 회원탈퇴");
		int choice = sc.nextInt();
		if(choice == 4) {
			System.out.println("메인으로 돌아갑니다.");
		} else if(choice == 5) {
			System.out.print("비밀번호 재입력 : ");
			String userpw = sc.next();
			//비밀번호 검사를 위해 udao의 checkPw() 호출
			if(udao.checkPw(userpw)) {
				//회원탈퇴 진행
				//탈퇴 진행 시 현재 탈퇴를 하려는 회원이 올린 모든 상품들도 삭제 혹은 사용불가 처리를 해주어야 한다.
				//후에 삽입될 데이터와 충돌이 발생할 수 있기 때문이다. 상품을 관리할 pdao를 통해 removeAll()을
				//먼저 진행해준다. *실제 DB 환경에서도 상품 삭제가 먼저 되어야지만 회원을 탈퇴 시킬 수 있다.
				pdao.removeAll();
				//상품까지 깨끗하게 처리되었으므로 실제 회원 데이터 삭제 진행
				if(udao.leaveId()) {
					//아련하게 인사
					System.out.println("그동안 이용해 주셔서 감사합니다...☆ 기다릴게요....");
				}
			}
			//비밀번호 검사 실패
			else {
				System.out.println("비밀번호 오류");
			}
		}
		//무언가 회원 정보를 수정하려고 선택했을 때
		else {
			System.out.print("새로운 정보 : ");
			sc = new Scanner(System.in);
			String newData = sc.nextLine();
			
			boolean check = false;
			switch(choice) {
			case 1:
				check = udao.modifyUser(1,newData);
				break;
//			case 2:
//				udao.modifyUser(4,newData);
//				break;
//			case 3:
//				udao.modifyUser(5,newData);
//				break;
				
			case 2: case 3:
				check = udao.modifyUser(choice+2,newData);
				break;				
			}
			if(check) {
				System.out.println("정보 수정 완료!");
			}
			else {
				System.out.println("정보 수정 실패");
			}
		}
	}
}

상품검색 부분 추가하기

case 5:
		//상품 검색
		System.out.println("검색어를 입력하세요 : ");
		sc = new Scanner(System.in);
		String keyword = sc.nextLine();
		System.out.println(pdao.search(keyword));
	//아래와 같이 새로운 클래스를 정의해보자.
		new ProductInfoView();

new ProductInfoView();

해당 클래스의 기능은 사용자로부터 자세히 볼 상품의 번호를 받아서 그 번호에 해당하는 상품의 상세 설명을 출력하는 것이다.
따라서 상세 설명을 출력하는 기능을 넣어보자.

package view;

import java.util.Scanner;

import dao.ProductDAO;
import dao.UserDAO;

public class ProductInfoView {
	public ProductInfoView() {
    //사용자로부터 입력받은 값을 가지기 위해 스캐너를 이용,
		Scanner sc = new Scanner(System.in);
     //우리는 상품 데이터를 활용해야 된다. 따라서 상품 데이터를 통신하는 클래스인 
     //ProductDAO를 객체화한다.
		ProductDAO pdao = new ProductDAO();
     // 자세히 볼 상품번호를 입력 받는다. 
		System.out.println("자세히 볼 상품번호 : ");
      //변수로 번호를 입력받는다. 
     	int prodnum = sc.nextInt();
//		번호를 입력하면 해당되는 product를 자세히 보여주는 메소드를 실행한다.
		pdao.search(prodnum);
	}
}

pdao.search메소드를 구현해보자. 우리는 기존 메소드명과 동일한 메소드가 있지만 매개변수의 타입을 int타입으로 받음으로써 overloading 할 것이다.
또한 편의상 메소드 사용 시 바로 출력하도록 하였다.

ProductDAO에서 search메소드 정의
//의도한데로 매개변수를 int타입으로 받는다.
public void search(int prodnum) {
//데이터 베이스 통신을 해야한다. 데이터베이스와 통신하는 conn변수와 select메소드를 이용한다.
//상품번호는 0번째에 있고 해당 번호를 String타입으로 넘겨주면 
//HashSet에 데이터베이스 한줄이 들어가게 된다. 
		HashSet<String> hi = conn.select(0, prodnum+"");
        //우리가 원하는 정보를 담을 수 있는 result변수를 만든다.
		String result = "";
        //물론 선택하는 번호는 중복되는 경우는 없지만 HashSet 타입의 정보를 
        //쉽게 가져올 수 있는 foreach문을 사용했다. 
		for (String line : hi) {
        //datas 배열에 데이터 한줄에 대한 정보를 넣었다.
			String[] datas = line.split("\t");
            //우리가 원하는 상세 내용은 datas의 4번방에 있다. 
				result += String.format("상세 내용 : %s", datas[4]);
		}
		//result에 누적연산자를 이용해 값을 넣었고 그 값을 그대로 출력한다.
		System.out.println(result);
	}

다시 ProductInfoView로 돌아오자.
이제 그 상품에 대해 좋아요와 판매자 연락처, 돌아가기 기능을 구현하면된다.
먼저 사용자가 입력할 수 있도록
1. 좋아요
2. 판매자 연락처
3. 돌아가기
위와 같은 형태로 보여주자

System.out.println("1. 좋아요\n2. 판매자 연락처\n3. 돌아가기");
이제 사용자로부터 선택한 값을 변수에 할당하자

int choice = sc.nextInt();

우리는 여기서 생각할 수 있다, 좋아요를 누르고 판매자 연락처를 누르면 이전화면으로 돌려 보내야 되는가? 아니면 좋아요도 누르고 판매자 연락처를 알 수 있을까??
그래서 while문으로 해당 질문을 반복해서 보여 줄 수 있도록 하자

while(true) {
	System.out.println("1. 좋아요\n2. 판매자 연락처\n3. 돌아가기");
	int choice = sc.nextInt();
}

이제 사용자가 입력한 값에 따라 case별로 다른 기능을 구현하도록 하기위해 switch문을 사용한다.
또한 아래 case들이 의도치 않게 실행되는 것을 막기위해 break;를 각 case별로 작성하고 while 탈출을 막기위해 true가 아닌 불리언 타입의 일종의 체크포인트가 필요하다.
case3일 경우에만 while문을 탈출시키기 위해 flag라는 변수에 true를 할당하고 case3에서 flag에 false를 할당하여 탈출 시킬 것이다!

// while문안에서 작성하면 무한반복임. 
boolean flag = true;
while(true) {
	System.out.println("1. 좋아요\n2. 판매자 연락처\n3. 돌아가기");
	int choice = sc.nextInt();
		switch(choice) {
			case 1:
				//좋아요
				break;
			case 2:
				//판매자 연락처
				break;
			case 3 :
       //돌아가는 메시지와 함께 flag를 fasle로 만들어 while문을 탈출시킨다.
				System.out.println("돌아갑니다.");
				flag = false;
				break;
			default:
           		//다시 선택하도록 유도 
				System.out.println("번호를 다시 선택해주세요");
				break;
		 }
}

자, 이제 좋아요를 만들어 보자.
사용자가 1번을 누르면 좋아요가 실행되고 실제 데이터베이스에 저장되어 있는 상품의 정보도 수정되어야 한다.
어떤 상품이 수정되는지 알려면 상품번호를 알아야 될 것이다.
우리는 while문 위에서 상세 정보를 보여주기 위해 사용자로부터 해당 상품번호를 받았다. 이렇게 받아온 상품정보는 prodnum이라는 변수에 할당되어 있으며 이를 활용하자.

상품 데이터베이스에 접근하려면 pdao를 이용해야 되고 pdao에 likecnt 메소드를 새롭게 정의하도록 하자.

switch(choice) {
	case 1:
    //	likecnt메소드는 위에서 받아온 변수를 매개변수로 할당하고 있다. 
		pdao.likecnt(prodnum);
		break;
        ...생략
ProductDAO에서 likecnt메소드 정의
//	prodnum은 사용자로부터 int타입으로 매개변수를 받는다.
	public boolean likecnt(int prodnum) {
//		search 메소드와 동일하게 conn.select를 이용하여 HashSet타입으로 만든다.
		HashSet<String> hi = conn.select(0, prodnum+"");
//		그 결과를 result에 담을 것이다.
		String result = "";
//		기존에 좋아요 갯수를 가져와서 그 좋아요 갯수에서 1개를 추가하는 방식으로 만들 것이다.
//		기존의 좋아요를 가지고 오기위해 아래와 같이 만든다. 
//		편의상 방식은 search와 동일하게 가져왔다. 
		for (String line : hi) {
			String[] datas = line.split("\t");
//			좋아요는 5번방에 들어있다. 
				result += String.format("%s", datas[5]);
		}
//		result는 문자열 타입으로 정수타입으로 형변환시키고 1을 더한 값을 likecnt에 할당한다.
		int likecnt = Integer.parseInt(result) + 1;
//		상품을 수정하기 위해 update메소드를 이용했다.
//		수정되는 상품번호는 prodnum이고 수정할 자리는 좋아요 자리인 5번이다.
//		좋아요가 추가된 likecnt를 newData매개변수 자리에 두면 된다.
		return conn.update(prodnum+"", 5, (likecnt+""));
	}

좋아요를 성공여부를 확인하기 위해 boolean타입으로 선언하였다.
다시 ProductInfoView로 돌아와서 case1을 다음과 같이 수정한다.

ProductInfoView
boolean flag = true;
while(true) {
	System.out.println("1. 좋아요\n2. 판매자 연락처\n3. 돌아가기");
	int choice = sc.nextInt();
		switch(choice) {
			case 1:
				//좋아요
                if(pdao.likecnt(prodnum)) {
                // 성공 시 다음과 같은 메시지 출력
					System.out.println("좋아요 감사");
				}else {
                //실패 시 다음과 같은 메시지 출력
					System.out.println("알 수 없는 오류");
				}
				break;
			case 2:
				//판매자 연락처
				break;
			case 3 :
       //돌아가는 메시지와 함께 flag를 fasle로 만들어 while문을 탈출시킨다.
				System.out.println("돌아갑니다.");
				flag = false;
				break;
			default:
           		//다시 선택하도록 유도 
				System.out.println("번호를 다시 선택해주세요");
				break;
		 }
}

이제 판매자 전화번호를 출력하는 기능을 구현하도록 하자
먼저 해당 상품의 정보를 살펴보자
만약 상품의 데이터 접근하면 다음과 같은 정보를 알 수 있다.

{상품번호, 상품명, 가격, 재고, 상세설명, 좋아요, 판매자id}

또한 유저 데이터에 접근하면 다음과 같은 정보를 알 수 있다.

{id, pw, 이름, 나이, 전화번호, 주소}

위와 같은 정보를 활용해서 판매자의 전화번호를 알아내면 된다.
먼저 특정 상품의 상품번호를 알 수 있다.

상품번호를 이용하여 판매자id를 가져오고 다시 판매자 id를 활용하여 전화번호에 접근하면 쉽게 문제를 해결 할 수 있다.
이제 유저 데이터베이스도 접근하니 UserDAO도 객체화하여 사용하자

package view;

import java.util.Scanner;

import dao.ProductDAO;
import dao.UserDAO;

public class ProductInfoView {
	public ProductInfoView() {
		Scanner sc = new Scanner(System.in);
		ProductDAO pdao = new ProductDAO();
        // 유저정보에 접근해서 전화번호를 알아낼 것이다.
		UserDAO udao = new UserDAO();
		System.out.println("자세히 볼 상품번호 : ");
//		sc = new Scanner(System.in);
		int prodnum = sc.nextInt();
//		번호를 입력하면 해당되는 product를 자세히 보여줌 
		pdao.search(prodnum);
		boolean flag = true;
		while(flag) {
			System.out.println("1. 좋아요\n2. 판매자 연락처\n3. 돌아가기");
			int choice = sc.nextInt();
			switch(choice) {
			case 1:
				if(pdao.likecnt(prodnum)) {
					System.out.println("좋아요 감사");
				}else {
					System.out.println("알 수 없는 오류");
				}
				break;
			case 2:
            	// 먼저 특정 상품에 해당되는 유저id를 알아내는 메소드를 만들자 
                //이 메소드는 pdao에서 알 수 있다.
                //또한 우리는 prodnum 변수에 특정 상품의 상품번호를 알고있다.
                //따라서 메소드의 매개변수로 넘겨주자
				 pdao.searchPhone(prodnum);
				break;
			case 3 :
				System.out.println("돌아갑니다.");
				flag = false;
				break;
			default:
				System.out.println("번호를 다시 선택해주세요");
				break;
		  }
		}
	}
}

productDAO

여기서 해당 상품번호에 해당되는 사용자정보를 가져오는 메소드를 정의한다.

// 상품번호를 받는 메소드임.
	public String searchPhone(int prodnum) {
//		상품 정보를 가져오는 코드는 동일함.
		HashSet<String> hi = conn.select(0, prodnum+"");
//		해당 사용자id를 result에 담을 것임.
		String result = "";
//		상품에 대한 정보를 가져온다.(search와 동일)
		for (String line : hi) {
			String[] datas = line.split("\t");
//			사용자 id는 6번방에 있다. 
				result += String.format("%s", datas[6]);
		}
//		사용자 id를 return 시킨다. String타입 메소드로 변경함.
		return result;
	}

ProductInfoView

사용자 정보를 ProductInfoView에서 활용할 수 있도록 변수에 할당하자
변수에 할당한 userid를 다시 udao에 넘겨주어 해당 판매자의 전화번호를 알아내자.

...생략
case 2:
// 메소드로 가져온 판매자 id를 userid 변수에 할당함.
	String userid = pdao.searchPhone(prodnum);
    // 해당 userid를 다시 udao에 넘겨주어 메소드를 통해 전화번호를 알아낸다.
	udao.contactSeller(userid);
	break;
...

UserDAO

전화번호를 가져오는 메소드를 정의하자

//	외부로부터 사용자 id를 전달받는 메소드 
	public void contactSeller(String userid) {
//		conn.select에 userid를 넘겨주어 해당되는 정보를 HashSet타입으로 받는다.
		HashSet<String> rs = conn.select(0, userid);
//		마찬가지로 해당 전화번호를 result로 받을 것이다. 
		String result = "";
//		해당 유저 정보도 1개에 불과하지만 HashSet으로 받았기에 foreach로 정보를 가져왔다.
		for (String line : rs) {
//			datas 배열에 유저 정보를 담고
			String[] datas = line.split("\t");
//			전화번호가 있는 배열방을 result에 담는다.
			result += "연락처 : "+datas[4]+"\n"; 
		}
//		해당 정보를 바로 출력했다. 
		System.out.println(result);
	}

ProductInfoView 전체 코드

	package view;
	import java.util.Scanner;
	import dao.ProductDAO;
	import dao.UserDAO;
	public class ProductInfoView {
		public ProductInfoView() {
			Scanner sc = new Scanner(System.in);
			ProductDAO pdao = new ProductDAO();
			UserDAO udao = new UserDAO();
		System.out.println("자세히 볼 상품번호 : ");
		// sc = new Scanner(System.in);
		int prodnum = sc.nextInt();
		// 번호를 입력하면 해당되는 product를 자세히 보여줌 
		pdao.search(prodnum);
		boolean flag = true;
		while(flag) {
			System.out.println("1. 좋아요\n2. 판매자 연락처\n3. 돌아가기");
			int choice = sc.nextInt();
			switch(choice) {
			case 1:
				if(pdao.likecnt(prodnum)) {
					System.out.println("좋아요 감사");
				}else {
					System.out.println("알 수 없는 오류");
				}
				break;
			case 2:
				String userid = pdao.searchPhone(prodnum);
				udao.contactSeller(userid);
				break;
			case 3 :
				System.out.println("돌아갑니다.");
				flag = false;
				break;
			default:
				System.out.println("번호를 다시 선택해주세요");
				break;
		  }
		}
	}
}
profile
발전하기 위한 공부

0개의 댓글