웹_프론트_백엔드/단과

[단과_JAVA_심화반] 2020.10.06

shine94 2020. 10. 7. 03:07

1. 소프트웨어 디자인 설계 패턴 - MVC
 : 분업의 목적을 위해 사용...!


① Model : 저장소에서 가져온 데이터를 담을 수 있는 틀을 선언

② Controller : 추가, 수정, 삭제, 검색, 목록 등 메소드가 선언되어 있으며, 
                   결과값을 Model 객체에 담아서 리턴하도록 설계

③ View : 사용자가 직접 눈으로 보는 화면을 구현하는 부분
             Controller에 선언된 메소드를 사용하여 결과를 뿌려주는 작업 설계

 

 

2. MVC model1과 model2

1) model 1

 : 소규모 혹은 중규모 프로젝트에 어울리는 설계 패턴

   View와 Controller가 함께 존재

   [장점]

   선언하는 페이지와 사용하는 페이지가 한 페이지에 같이 있다는 건 (MVC model2보다) 설계가 쉽다는 뜻.

   설계가 쉽다는 것은 (MVC model2에 비해) 소규모 프로젝트 적합하다는 뜻이다.
   [단점]
   분업이 어렵다. 때문에 (MVC model2보다) 유지보수가 어렵다.

 

2) model 2

 : 대규모 프로젝트에 어울리는 설계 패턴

   [장점]
   분리가 되어 (MVC model1보다) 분업하기 편하다.
   분업하기 편하기 때문에 (MVC model1에 비해) 많은 사람들이 단기간에 편하게 할 수 있다.
   (MVC model1보다) 유지보수 쉽다.
   [단점]
   (MVC model1) 설계가 어렵다.

 

 

3. MVC 모델은 필수는 아니며 페이지가 몇 개 없는 소규모 곳에서는 안써도 된다.

   [예] 배달의 민족



4. 저장소는 프로그램을 종료해도 데이터가 소멸되지 않게 하기 위해 사용한다.

   바꿔말하면 저장소는 사용자에게 서비스를 제공한다.

   서비스를 제공하는 것을 서버라고 하며 그러니깐 DB 서버라고 부르는 것이다.

 

 

5. 규모에 따른 이관 작업 순서

 : MVC 모델 사용 안함 > MVC model1 > MVC model2 > Spring MVC model2

 


6. 박스오피스(미완성, 다음 시간에 수정, 삭제, 검색, 목록 공부할 예정)

 : 파일 입출력을 DB 저장소로 MVC model1으로 설계

 

1) 박스오피스 역대 순위 자료를 이용하여 외부 저장소 만들기

 : 구글에 박스오피스 역대 순위 검색하여 역대 박스오피스 - KOFIC 영화관 입장권 통합전산망 클릭

> 500개의 튜플 데이터를 복사하기

> 아까 복사한 튜플들을 붙여넣어 box_office.txt, box_office_bk.txt 이름으로 만들어준다.

   [이때] 만약 마지막 줄에 엔터가 있으면 오류가 발생하니, 엔터 없이 만들어야 한다...!

 

2) 소스코드

** com.lec.java.vo > BoxOfficeVO.java

package com.lec.java.vo;

// Model
public class BoxOfficeVO {
	// 여러 줄 수정하고 싶을때는 Alt + Shift + A,
	// 범위를 늘리고 싶을때는 Shift
	private int rating;
	private String film_name;
	private String release_date;
	private long income;
	private int geust_cnt;
	private int screen_cnt;
	
	
	public BoxOfficeVO() {;}

	
	public int getRating() {
		return rating;
	}
	public void setRating(int rating) {
		this.rating = rating;
	}

	public String getFilm_name() {
		return film_name;
	}
	public void setFilm_name(String film_name) {
		this.film_name = film_name;
	}

	public String getRelease_date() {
		return release_date;
	}
	public void setRelease_date(String release_date) {
		this.release_date = release_date;
	}

	public long getIncome() {
		return income;
	}
	public void setIncome(long income) {
		this.income = income;
	}

	public int getGeust_cnt() {
		return geust_cnt;
	}
	public void setGeust_cnt(int geust_cnt) {
		this.geust_cnt = geust_cnt;
	}

	public int getScreen_cnt() {
		return screen_cnt;
	}
	public void setScreen_cnt(int screen_cnt) {
		this.screen_cnt = screen_cnt;
	}
}


** com.lec.java.dao > DBConnector.java

package com.lec.java.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;

// 외부 저장소와 연결할 수 있는 메소드를 선언해 놓는다.
public class DBConnector {
	// 왜 static으로 만들었을까?
	// 1. 쓰기 편하기 위해
	// 2. 객체간 공유가 필요하기 때문에
	
	// 덮어쓰기 위해 외부 저장소와 연결
	public static BufferedWriter getWriter() throws IOException {
		BufferedWriter bw = new BufferedWriter(new FileWriter("src/box_office.txt"));
		return bw;
	} // end getWriter()
	
	// 이어쓰기 위해 외부 저장소와 연결
	public static BufferedWriter getAppender() throws IOException {
		BufferedWriter bw = new BufferedWriter(new FileWriter("src/box_office.txt", true));
		return bw;
	} // end getAppender()
	
	// 읽어오기 위해 외부 저장소와 연결
	public static BufferedReader getReader() throws IOException {
		// BufferedReader를 사용할 때 조심해야한다.
		// 파일이 없으면 만들어서 읽어오는 것이 아니라 오류가 난다.
		// 그렇기 때문에 FileNotFoundException 예외 처리를 해줘야 한다!!
		BufferedReader br = null;
		try {
			br = new BufferedReader(new FileReader("src/box_office.txt"));
		} catch (FileNotFoundException fnfe) {
			System.out.println("파일을 찾을 수 없습니다." + fnfe);
		}
		return br;
	} // end getReader()
} // end class

 

** com.lec.java.dao > BoxOfficeDAO.java

package com.lec.java.dao;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

import com.lec.java.vo.BoxOfficeVO;

// Controller
public class BoxOfficeDAO {
	// 추가, 이 메소드를 뷰단에서 사용할 것임!
	/**
	 * 랭킹을 전달하면 해당 랭킹에 삽입<br>
	 * 랭킹을 전달하지 않으면 마지막 줄에 추가
	 * @return boolean
	 */
	public boolean insertOrAppend(BoxOfficeVO vo) throws IOException {
		if(vo.getRating() == 0) {
			// 외부에서 전달받은 순위가 없다면 마지막 줄에 추가
			if(append(vo)) {
				// 추가 성공시
				return true;
			}
			// 추가 실패시
			return false;
			
		} else {
			// 외부에서 전달받은 순위가 있다면 해당 랭킹에 삽입
			BufferedReader br = DBConnector.getReader();
			String line = null;
			int last_rating = 0;
			
			while(true) {
				line = br.readLine();
				if(line == null) {break;}
				last_rating = Integer.parseInt(line.split("\t")[0]);
			}
			// 사용자가 삽입할 랭킹이 마지막 순위보다 크면 삽입 실패
			if(vo.getRating() > last_rating) {return false;}
			
			// 사용자가 삽입할 랭킹이 마지막 순위보다 작으면 삽입 시도
			if(insert(vo)) {
				// 삽입 성공
				return true;
			}
			// 삽입 실패
			return false;
		}
	} // end insertOrAppend()
	
	// append()와 insert()를 사용자가 직접 사용하면 안되기 때문에 private로 보호해줬다!
	// 추가(마지막 줄에)
	private boolean append(BoxOfficeVO vo) throws IOException {
		// 외부에서 전달한 값이 없다면 false를 리턴
		if(vo == null) {return false;}
		
		BufferedReader br = DBConnector.getReader();
		String line = null;
		// 마지막 랭킹을 담을 변수
		int last_ranking = 0;
		
		while(true) {
			line = br.readLine();
			if(line == null) {break;}
			
			// "문자열".split("구분점");
			// 동일한 구분점을 기준으로 각각의 문자열 값을 분리한다.
			// 구분점이 있다는 뜻은 최소 2개 이상의 값이 나온다는 것이므로, 리턴 타입은 배열이다.
			last_ranking = Integer.parseInt(line.split("\t")[0]);
		}
		br.close();
		
		// 해당 경로의 파일 모든 내용을 전부 가져온다(줄바꿈 포함)
		String contents = new String(Files.readAllBytes(Paths.get("src/box_office.txt")));
		
		// 파일의 가장 마지막 문자열 값
		String last_str = contents.substring(contents.length() - 1);
		
		BufferedWriter bw = DBConnector.getAppender();
		
		// 가장 마지막 문자열이 /n이 아니면 줄바꿈 후 데이터 추가
		if(!last_str.equals("\n")) {
			bw.newLine();
		}
		
		// 가장 마지막 문자열의 \n이라면 줄바꿈 없이 데이터 추가
		bw.write((last_ranking + 1) + "\t");		
		bw.write(vo.getFilm_name() + "\t");		
		bw.write(vo.getRelease_date() + "\t");		
		bw.write(vo.getGeust_cnt() + "\t");		
		bw.write(vo.getIncome() + "\t");
		bw.write(vo.getScreen_cnt() + "");
		
		// write는 무조건 문자열이어야 정상적으로 추가된다
		// 만약 문자열이 아니라면 깨진다.

		bw.close();
		return true;
	} // end append()
	
	// 삽입(중간에)
	private boolean insert(BoxOfficeVO vo) throws IOException {
		if(vo == null) {return false;}
		
		BufferedReader br = DBConnector.getReader();
		String contents = "";
		String line = null;
		boolean check = false;
		int inserted_rating = vo.getRating();
		
		while(true) {
			line = br.readLine();
			if(line == null) {break;}
			
			// 사용자가 삽입할 랭킹이 현재 행의 랭킹과 같다면
			if(Integer.parseInt(line.split("\t")[0]) == vo.getRating()) {
				// 기존의 데이터 전, 전달받은 데이터를 삽입
				contents += vo.getRating() + "\t"
							+ vo.getFilm_name() + "\t"
							+ vo.getRelease_date() + "\t"
							+ vo.getGeust_cnt() + "\t"
							+ vo.getIncome() + "\t"
							+ vo.getScreen_cnt() + "\n";
				
				// 삽입을 했는지 안했는지 판단하는 flag
				// 삽입 성공시 true로 변경
				check = true;
			}
			
			// 삽입 성공시
			if(check) {
				// 기존 랭킹에 있던 영화 순위부터 마지막 영화 순위까지 모두 +1 처리
				// line.substring(line.indexOf("\t")) : 순위를 제외한 정보
				contents += ++inserted_rating + line.substring(line.indexOf("\t")) + "\n";
				continue;
			}
			// 삽입 전 혹은 삽입 실패시 기존 데이터를 글대로 누적
			contents += line + "\n";
		}
		br.close();
		
		// 변경된 내용으로 덮어쓰기
		BufferedWriter bw = DBConnector.getWriter();
		bw.write(contents);
		bw.close();
		return true;
		
	} // end insert()
	
	// 수정
	// 삭제
	// 검색
	// 목록
}


** com.lec.java.box_office > Test.java

package com.lec.java.box_office;

import java.io.IOException;

import com.lec.java.dao.BoxOfficeDAO;
import com.lec.java.vo.BoxOfficeVO;

// View
public class Test {
	public static void main(String[] args) throws IOException {
		BoxOfficeVO vo = new BoxOfficeVO();
		BoxOfficeDAO dao = new BoxOfficeDAO();
		
		vo.setRating(7);
		vo.setFilm_name("샤인3");
		
		if(dao.insertOrAppend(vo)) {
			System.out.println("추가 성공");
		} else {
			System.out.println("추가 실패");
		}
	} // end main()
} // end class

 

3) 삽입 결과 테스트

① 마지막 줄에 정상적으로 추가되는지 테스트

② 추가된 마지막 줄에 또 마지막 줄에 잘 추가되는지 확인하는 테스트

③ 중간에 삽입이 정상 작동하는지 7번째에 삽입 테스트

 

 

7. MVC를 분리한다고 모두 MVC 설계 방식이라고 할 수 없다.
   MVC의 규칙에 맞게 설계해야 MVC 설계이다.

 

 

8. Model 필드의 접근자는 반드시 private으로 설정한다.
   getter, setter 메소드가 반드시 있어야 하며, public 접근자를 설정해야 한다.
   반드시 기본 생성자가 존재해야 한다.
   패키지화를 해야한다.(즉, default package 쓰지 말기 > 왜? 뷰단에서 인식 못하기 때문에...!)

 

 

9. 다음 수업 내용 : 미완성된 박스오피스(수정, 삭제, 검색, 목록) 완성시키기, DBMS