코딩항해기

[실습/JAVA] 학생부 최종! MVC 패턴 적용하기 본문

problem solving/과제&실습 코딩

[실습/JAVA] 학생부 최종! MVC 패턴 적용하기

miniBcake 2024. 7. 17. 20:53

 

 

전에 진행한 학생부 프로그램 바로보기!

 

[실습/JAVA] 출석부 프로그램 만들기 (+풀이 0628)

배열에 대해 공부하기 위해 출석부 프로그램 만들기 실습을 진행하였다.(풀이 비교 추가함 2024.06.28.)[문제]학생부 프로그램 만들기 요구사항최대 5명까지 저장할 수 있는 학생부가 있습니다.학

minibcake.tistory.com

+함수화 적용

 

[과제/JAVA] 학생부 프로그램 함수화 (+풀이 0701)

기존 실습 때 진행했던 학생부 프로그램 코드를 메소드를 활용한 방식으로 디벨롭 시켜보자.240701 풀이 추가  [실습][JAVA] 출석부 프로그램 만들기 (+풀이비교 0628추가)배열에 대해 공부하기 위

minibcake.tistory.com

+객체 적용

 

[과제/JAVA] 학생부 프로그램에 객체 더하기 (+풀이 0703)

기존 진행했던 학생부 프로그램을 디벨롭 시켜 객체를 더하고 기능을 추가했다.(재시험 기능은 자유)  [과제/JAVA] 학생부 프로그램 함수화 (+풀이 0701)기존 실습 때 진행했던 학생부 프로그램 코

minibcake.tistory.com

+컬렉션 프레임워크 적용(ArrayList)

 

[실습/JAVA] 학생부에 컬렉션프레임워크 더하기 (+풀이 0715)

기존 진행해오던 학생부 프로그램에 컬렉션 프레임워크를 더해보자.이번 실습 때 사용할 컬렉션 프레임워크는 ArrayList다. [풀이 전 코드]더보기package class03;import java.util.ArrayList;import java.util.Scann

minibcake.tistory.com

 

 

오늘은 마지막 단계인 MVC 패턴 더하기다.

MVC 패턴은 Model View Controller로 이루어져있으며, package로 이를 구분해주었다.

아직 DB 나 DBMS, 프론트 등은 진도가 나가지 않아 DB는 ArrayList로, 프론트는 콘솔창으로 대신하였다.

 

 

(+ 더 나은 코드 고민하기)

  • 로직이 겹칠 때는 다른 메소드를 호출하는게 좋을까? 로직을 겹치더라도 매번 써주는게 좋을까?
  • (평균 계산 관련하여) 평균 값은 어디서 계산하는게 좋을까?
  • (평균 계산 관련하여) 평균 계산은 selectOne일까, selectAll일까?

 

[Model]

DTO

더보기
package model;


//MVC 패턴에서 data를 VO, DTO라고 함
public class StudentDTO {
	private int num;//학번
	private String name;//이름
	private int score;//점수
	
	//개발자용
	private double avg;//평균
	private String condition;
	
	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 getScore() {
		return score;
	}
	public void setScore(int score) {
		this.score = score;
	}
	public double getAvg() {
		return avg;
	}
	public void setAvg(double avg) {
		this.avg = avg;
	}
	public String getCondition() {
		return condition;
	}
	public void setCondition(String condition) {
		this.condition = condition;
	}
	@Override
	public String toString() {
		return "StudentDTO [num=" + num + ", name=" + name + ", score=" + score + "]";
	}
}

DAO

더보기
package model;

import java.util.ArrayList;
public class StudentDAO {
	private ArrayList<StudentDTO> datas;
	//JAVA에 존재하지 않고 Oracle 등의 DBMS로 이동될 예정

	public StudentDAO() {
		System.out.println("    service.model.StudentDAO StudentDAO() 생성자 시작");
		this.datas = new ArrayList<>();

		//샘플데이터
		StudentDTO data1 = new StudentDTO();
		data1.setNum(101);
		data1.setName("오리아나");
		data1.setScore(50);
		StudentDTO data2 = new StudentDTO();
		data2.setNum(102);
		data2.setName("모르가나");
		data2.setScore(87);
		StudentDTO data3 = new StudentDTO();
		data3.setNum(103);
		data3.setName("티모");
		data3.setScore(67);

		//web에서는 기본 생성자를 사용
		this.datas.add(data1);
		this.datas.add(data2);
		this.datas.add(data3);
		System.out.println("    service.model.StudentDAO StudentDAO() 생성자 종료");
	}
	
	
	
	public boolean insert(StudentDTO stuDTO) {
		System.out.println("    service.model.StudentDAO insert() 시작");
		StudentDTO data = new StudentDTO();
		data.setNum(stuDTO.getNum());
		data.setName(stuDTO.getName());
		data.setScore(stuDTO.getScore());
		this.datas.add(data);
		System.out.println("    service.model.StudentDAO insert() 종료");
		return true;
	}
	
	public boolean update(StudentDTO stuDTO) {
		System.out.println("    service.model.StudentDAO update() 시작");
		for(StudentDTO data : datas) {
			if(data.getNum() == stuDTO.getNum()) {
				data.setScore(stuDTO.getScore());
			}
		}
		System.out.println("    service.model.StudentDAO update() 성공");
		return true;
	}
	
	public boolean delete(StudentDTO stuDTO) {
		System.out.println("    service.model.StudentDAO delete() 시작");
		for(int i=0; i<datas.size(); i++) {
			if(stuDTO.getNum() == datas.get(i).getNum()) {
				datas.remove(i);
				System.out.println("    service.model.StudentDAO delete() 성공");
				return true;
			}
		}
		System.out.println("    service.model.StudentDAO delete() 실패");
		return false;
	}

	public ArrayList<StudentDTO> selectAll(StudentDTO stuDTO){
		System.out.println("    service.model.StudentDAO selectAll() name 시작");
		ArrayList<StudentDTO> datas = new ArrayList<>();
		if(stuDTO.getCondition().equals("ALL")) {
			datas.addAll(this.datas);
		}

		else if (stuDTO.getCondition().equals("KEYWORD")) {
			for(StudentDTO data : this.datas) {
				if(data.getName().contains(stuDTO.getName())) {
					datas.add(data);
					System.out.println("    service.model.StudentDAO selectAll() 일치하는 값을 찾음");
				}
			}
			System.out.println("    service.model.StudentDAO selectAll() name 종료");
		}

		return datas;
	}

	public StudentDTO selectOne(StudentDTO stuDTO) {
		
		if(stuDTO.getCondition().equals("AVG")) {
			System.out.println("    service.model.StudentDAO selectOne() avg 시작");
			int sum = 0;
			for(StudentDTO data : datas) {
				sum += data.getScore();
			}
			double avg = 1.0 * sum / datas.size();
			StudentDTO dto = new StudentDTO();
			dto.setAvg(avg);
			System.out.println("    service.model.StudentDAO selectOne() avg 종료");
			return dto;
		}
		
		else if(stuDTO.getCondition().equals("PK")) {
		System.out.println("    service.model.StudentDAO selectOne() 시작");
		for(StudentDTO data : datas) {
			if(data.getNum() == stuDTO.getNum()) {
				System.out.println("    service.model.StudentDAO selectOne() 데이터일치 종료 : data = "+data+"/num : "+stuDTO.getNum());
				return data;
			}
		}
		System.out.println("    service.model.StudentDAO selectOne() 데이터불일치 종료");
		}
		
		return null;
	}
}

 

[View]

더보기
package view;

import java.util.ArrayList;
import java.util.Scanner;

import model.StudentDTO;

public class StudentView {
	static Scanner sc = new Scanner(System.in);
	//print01, 02는 하면 안됨!! 편의상 진행
	public void print00() {
		System.out.println("프로그램이 종료됩니다.");
	}
	public void print01() {
		System.out.println("=== 메 뉴 ===");
		System.out.println("1. 학생추가");
		System.out.println("2. 전체출력");
		System.out.println("3. 번호검색");
		System.out.println("4. 이름검색");
		System.out.println("5. 평균출력");
		System.out.println("6. 점수변경");
		System.out.println("7. 학생삭제");
		System.out.println("0. 프로그램 종료");
		System.out.println("=============");
		System.out.print("메뉴 입력 >> ");
	}

	public void print02() {
		System.out.print("추가할 학생의 이름 입력 >> ");
	}
	public void print03() {
		System.out.print("추가할 학생의 성적 입력 >> ");
	}
	public void print04() {
		System.out.println("학생 추가 완료!");
	}
	public void print04_1() {
		System.out.println("학생 추가 실패..");
	}
	public void print05(ArrayList<StudentDTO> datas) {
		System.out.println("==== 전체 목록 ====");
		for(StudentDTO data : datas) {
			System.out.println(data);
		}
		System.out.println("==================");
	}
	public void print06() {
		System.out.println("출력할 데이터가 없습니다!");
	}
	public void print07() {
		System.out.print("검색할 학생의 번호 입력 >> ");
	}
	public void print08(StudentDTO data) {
		System.out.println("==== 검색 결과 ====");
		System.out.println(data);
		System.out.println("==================");
	}
	public void print09() {
		System.out.print("삭제할 학생의 번호 입력 >> ");
	}
	public void print10() {
		System.out.println("학생 삭제 완료!");
	}
	public void print11() {
		System.out.println("학생 삭제 실패...");
		System.out.println("관리자에게 문의하세요.");
	}
	//4,5,6 진행
	public void printNameSearch() {
		System.out.print("검색어 입력 >> ");
	}
	public void printNameResult(ArrayList<StudentDTO> datas) {
		System.out.println("==== 검색 결과 ====");
		if(datas.isEmpty()) {
			print06();
		}
		else {
			for(StudentDTO data : datas) {
				System.out.println(data);
			}
		}
		System.out.println("==================");
	}
	public void printStuAvg(double avg) {
		System.out.println("전체 평균 : "+avg+"점");
	}

	
	public void printUpdateNum() {
		System.out.print("변경할 학생 번호 입력 >> ");
	}
	public void printUpdateScore() {
		System.out.print("변경할 새 점수 입력 >> ");
	}
	public void printUpdateSuccess(StudentDTO data) {
		System.out.println("점수 변경 성공!");
		System.out.println(data.getName()+" 학생의 점수가 "+data.getScore()+"점으로 변경되었습니다.");
	}
	public void printUpdateFail() {
		System.out.println("점수 변경 실패...");
		System.out.println("관리자에게 문의하세요.");
	}

	public int inputAction() {
		int action=sc.nextInt();
		return action;
	}
	public String inputName() {
		String name=sc.next();
		return name;
	}
	public int inputScore() {
		int score;
		while(true) {
			score=sc.nextInt();
			if(0<=score && score<=100) {
				break;
			}
			System.out.println("점수는 0~100까지만 가능합니다.");
			System.out.print("다시 입력 >> ");
		}
		return score;
	}
	public int inputNum(int lastPK) {
		int num;
		while(true) {
			num=sc.nextInt();
			if(100 < num && num < lastPK) {//제대로 입력하면
				break;
			}
			System.out.print("제대로 입력해주세요! >> ");
		}
		return num;
	}
	
}

 

[Controller]

더보기
package controller;

import java.util.ArrayList;

import model.StudentDAO;
import model.StudentDTO;
import view.StudentView;

public class StudentController {
	private StudentDAO model;
	private StudentView view;
	private int PK;

	public StudentController() {
		super();
		this.model = new StudentDAO();
		this.view = new StudentView();
		this.PK = 104;
	}

	public void start() {
		while(true) {
			this.view.print01();
			int action = this.view.inputAction();

			if(action==1) {//학생추가
				this.view.print02();
				String name = this.view.inputName();
				this.view.print03();
				int score = this.view.inputScore();
				
				StudentDTO stuDTO = new StudentDTO();
				stuDTO.setNum(this.PK++);
				stuDTO.setName(name);
				stuDTO.setScore(score);
				
				boolean flag = this.model.insert(stuDTO);

				//결과
				if(flag) {
					//성공
					this.view.print04();
				}
				else {
					//실패
					this.view.print04_1();
				}
			}
			else if(action==2){//전체출력
				StudentDTO stuDTO = new StudentDTO();
				stuDTO.setCondition("ALL");
				ArrayList<StudentDTO> datas = this.model.selectAll(stuDTO);
				this.view.print05(datas);
			}
			else if(action==3){//번호검색
				this.view.print07();
				int num = this.view.inputNum(PK);
				StudentDTO stuDTO = new StudentDTO();
				stuDTO.setCondition("PK");
				stuDTO.setNum(num);
				StudentDTO data = this.model.selectOne(stuDTO);

				if(data != null) {
					//결과 있음
					this.view.print08(data);
				}
				else {
					//결과 없음
					this.view.print06();
				}
			}
			else if(action==4){//이름검색
				this.view.printNameSearch();
				String word = this.view.inputName();
				StudentDTO stuDTO = new StudentDTO();
				stuDTO.setCondition("KEYWORD");
				stuDTO.setName(word);
				ArrayList<StudentDTO> datas = this.model.selectAll(stuDTO);
				this.view.printNameResult(datas);
			}
			else if(action==5){//평균출력
				StudentDTO stuDTO = new StudentDTO();
				stuDTO.setCondition("AVG");
				double avg = this.model.selectOne(stuDTO).getAvg();
				this.view.printStuAvg(avg);
				
				//All 방식
				//데이터 정제, 가공
//				StudentDTO stuDTO = new StudentDTO();
//				stuDTO.setCondition("ALL");
//				ArrayList<StudentDTO> datas = this.model.selectAll(stuDTO);
//				int sum = 0;
//				for(StudentDTO data : datas) {
//					sum+=data.getScore();
//				}
//				double avg = 1.0 * sum / datas.size();
//				this.view.printStuAvg(avg);
			}
			else if(action==6){//점수업데이트
				//번호 입력
				this.view.printUpdateNum();
				int num = this.view.inputNum(PK);

				//점수 입력
				this.view.printUpdateScore();
				int score = this.view.inputScore();

				//객체 찾기
				StudentDTO stuDTO = new StudentDTO();
				stuDTO.setCondition("PK");
				stuDTO.setNum(num);
				StudentDTO data = this.model.selectOne(stuDTO);
				
				//업데이트
				if(data != null) {
					StudentDTO stuDTO01 = new StudentDTO();
					stuDTO01.setNum(num);
					stuDTO01.setScore(score);
					boolean flag = this.model.update(stuDTO01);
					if(flag) {//업데이트 성공시
						this.view.printUpdateSuccess(data);
						continue;
					}
				}
				this.view.printUpdateFail();
			}
			else if(action==7){
				view.print09();
				int num = view.inputNum(PK);
				StudentDTO stuDTO = new StudentDTO();
				stuDTO.setNum(num);
				boolean flag = model.delete(stuDTO);
				if(flag) {
					view.print10();
				}
				else {
					view.print11();
				}
			}
			else if(action==0) {//사용자가 0을 누르면
				this.view.print00();
				break;
			}
		}
	}//start
}

 

 

+ 더 나은 코드 고민하기

로직이 겹칠 때는 다른 메소드를 호출하는게 좋을까? 로직을 겹치더라도 매번 써주는게 좋을까?

수업에서는 보완성 문제로 매번 따로 작성해주는 것이 좋다는 결론이 나왔다.
해킹, 위변조의 상황을 대비하여 매번 데이터를 따로 확인을 거쳐 사용하는 것이 좋다고 한다.

일부 간단한 제작에서는 update 기능자체를 selectOne이후에 가능하도록 제작하기도 한다고 한다.
(ex. 로그인 후 진행되는 프로필 사진 변경 등)

 

 

(평균 계산 관련하여) 평균 값은 어디서 계산하는게 좋을까?

평균을 계산하는 것은 View, Service(Model), Controller 모두에서 가능한데, 
View의 경우 데이터의 양이 많아지면 프로그램이 무거워질 것으로 예상되지만,
데이터 값이 변경되지는 않는다는 점에서 View에서 진행하는 것도 가능하다.
아니면 Controller에서 Model로부터 받은 값을 View에 전달해주기 전에 값을 정제하는 것도 가능하다.
애초에 Model에서 데이터를 넘길 때부터 계산로직을 거쳐 평균값을 전달해주는 것도 가능하다.

상황에 따라 알맞은 방식이 존재하겠지만, 개인적으로 Model에서
계산로직을 거쳐 전달되는 방식이 현 학생부에서 더 깔끔하게 나올 것 같아
이번 학생부 프로그램에서는 Service(Model)에서 해당 기능을 구현하였다.

 

 

(평균 계산 관련하여) 평균 계산은 selectOne일까, selectAll일까?

어느 부분에서 계산로직을 거치는지에 따라 달라진다.
만약, Controller나 View단에서 계산로직을 거치게 된다면, 여러 개의 데이터를 받아와야하기 때문에 selectAll이다.

하지만 위에서 결정한 대로 Model에서 계산하여 값을 받아오게 된다면 넘어오는 값은 한 개이기 때문에
selectOne이 된다. 설계 단계에 따라 달라지게 된다.