ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • MVC 패턴
    Java 2023. 11. 12. 19:21

    개요

    학원에서 웹MVC 패턴의 프로젝트를 만들면서 비즈니스 로직을 모두 Controller에 넣어버렸다. 결국 Controller에서 다 가공된 데이터를 받은 Service는 DAO에게 데이터를 넘겨주는 역할만 할 뿐이었다. Service가 왜 필요하지? 라는 의문과 함께, 친구가 Controller가 하는 역할과 책임을 다시 공부해보면 좋을 것 피드백을 해줘서 MVC패턴을 더 자세하게 알아봐야겠고 생각했다.  

    MVC패턴이란

    Model-View-Controller의 약자로 어플리케이션을 세 가지 역할로 구분한 개발 방법론이다.

    사용자의 요청을 Controller가 받고 Model에 연결해주며, Model에서는 필요한 데이터와 비즈니스 로직을 처리한 후 가공된 데이터를 Controller로 주며, Controller는 데이터와 함께 사용자에게 보여줄  View를 리턴해준다.

    즉, 어플리케이션을 세 가지 역할로 구분해서 독립적이고 유지보수되기 쉽도록하게 한다는 장점이 있다.

    Model, View, Controller 각각의 역할

    • Model
      어플리케이션의 비즈니스 로직을 포함하는 부분
      DB와 연동해서 데이터를 가져오고 저장하는 역할
    • View
      Model에서 넘겨받은 데이터들을 사용자에게 보여주는 역할.(화면에 해당됨)

    • Controller
      사용자에게 받은 요청을 Model에 넘겨주는 역할
      사용자가 보낸 데이터가 있다면, 모델에 넘겨주기 전 데이터를 가공한 뒤 넘겨준다.
      Model이 업무수행이 끝나면 그 결과를 View에게 전달해준다.
      View와 Model이 직접적인 상호 소통을 하지 않도록 관리한다.  --> 결합도를 낮추기 위함

     

    MVC패턴 구조

    MVC Pattern format

     

    1. 유저가 컨트롤러에 요청을 보낸다.

    2. 컨트롤러는 모델에게 해당 요청을 다시 모델로 보내서 비즈니스 로직을 처리한다.
    (사용자가 요청에 데이터를 담고 있는 경우, 처리 후 데이터도 같이 넘겨준다.)
    3.모델에서 비즈니스 로직을 처리 후 가공된 데이터를 컨트롤러에게 넘겨준다.

    4. 컨트롤러는 뷰에게 가공된 데이터를 넘겨준다.

    5. 뷰는 가공된 데이터를 시각화 후 User에게 보여준다. 

     

    Spring MVC패턴 구조

    Spring MVC Pattern format

    1. 유저가 DispatcherServlet(프론트 컨트롤러)에 요청을 보낸다.

    DispatcherServlet은 우선 유저의 모든 요청을 받고, 그 요청을 분석해서 세부 컨트롤러들에게 나눠주는 역할을 한다.

    2. 요청에 맵핑되는 컨트롤러를 검색한다.

    3. 맵핑된 컨트롤러를 연결한다.

    4. 결과를 받은 컨트롤러에서 연결을 요청한 뷰가 있는지 ViewResolver를 통해서 검색하고 결정한다.

    5. 해당View와 연결하고 유저에게 보여준다.

    현재 프로젝트 수정

     

    현재 프로젝트는 Spring 웹MVC프로젝트로 흐름은 위의 구조와 똑같이 이뤄지고,Dispatcher Servlet이후 
    Controller Service DAO 순서로 이어진다.

    하지만, 피드백 이전까지 각각의 역할을 알지 못해서 Controller에서 대부분의 로직들을 처리했기 떄문에 위의 책임에 맞도록 수정해봤다.

     

    Controller에서는 사용자의 HTTP요청과 HttpSession을 처리하고 View를 리턴해주며, (RestController와 ResponseBody도 쓰긴 하지만..)

    Service는 비즈니스 로직을 처리하고, 
    DAO(Data Access Object)는 사용자의 요청과 비즈니스 로직을 위해 필요한 데이터를 DB에서 가져오거나 저장하는 역할만 하도록 했다.

     

    수정하기 전 컨트롤러(비즈니스 로직까지 처리할 때)

    @RequestMapping(value = "/sendMailProcess", method = RequestMethod.POST)
    	public String sendMail(HttpSession session,
    			HttpServletRequest request,
    			@RequestParam("mail_file") MultipartFile multipartFile) {
    		
    		ServletContext application = session.getServletContext();
    		String realPath = "C:/mail_upload";
    		
    		//사용자 정보
    		MemberDTO loginDto = (MemberDTO)session.getAttribute("login");
    		
    		//메일 받는 사람들
    		String addressListStr = request.getParameter("mail_receiver");
    		String addressList[] = addressListStr.split(" ");
    		List<Integer> rec_numList = service.findMemberNumByEmail(addressList);
    
    		//데이터 파싱
    		String mail_title = request.getParameter("mail_title");
    		String mail_content = request.getParameter("mail_content");
    		String mail_sender = loginDto.getMail();
    		int member_num = loginDto.getMember_num();	
    		
    		String mail_fileName = multipartFile.getOriginalFilename(); //사용자 지정 파일 이름
    		UUID uuid = UUID.randomUUID(); //파일 이름 중복 방지를 위한 식별자 
    		String mail_fileReName = uuid+"_"+mail_fileName; //식별자 이름 + 사용자 지정파일 이름으로 파일이름 중복 방지
    		
    		//파일 I.O처리
    		File saveFile = new File(realPath,mail_fileReName);
    		try {
    			multipartFile.transferTo(saveFile);
    		} catch (IOException e) {
    			e.printStackTrace();
    			System.out.println("IO에러");
    		}
    		
    		//DB에 넘겨줄 MailDTO
    		MailDTO mailDto = new MailDTO();
    		mailDto.setMail_title(mail_title);
    		mailDto.setMail_content(mail_content);
    		mailDto.setMail_sender(mail_sender);
    		mailDto.setMember_num(member_num);
    		mailDto.setMail_fileName(mail_fileName);
    		mailDto.setMail_filePath(realPath);
    		mailDto.setMail_fileReName(mail_fileReName);
    		
    		//최근 파일 고유번호
    		int recentEmailNum = service.mailSendProcess1(mailDto);
    
    		//DB에 넘겨줄 MailRecDto
    		MailRecDTO mailRecDto = new MailRecDTO();
    		mailRecDto.setMail_num(recentEmailNum);
    		
    		//Mail_Rec테이블 insert
    		int res = 0;
    		for(int i=0; i<rec_numList.size(); i++) {
    			mailRecDto.setRec_num(rec_numList.get(i));
    			mailRecDto.setMail_receiver(addressList[i]);
    			res += service.insertReceiveTable(mailRecDto); //insert 작업
    		}
    
    		String msg = "메일전송이 완료되었습니다!";
    		if(res != rec_numList.size()) {
    			msg = "다시 시도해주세요";
    			return "redirect:writeMail";
    		}
    		
    		session.setAttribute("msg", msg);
    		
    		return "redirect:writeMail";
    	}

     

    수정 후 컨트롤러(컨트롤러와 서비스로 나눴을 때)

    //컨트롤러
    @RequestMapping(value = "/sendMailProcess", method = RequestMethod.POST)
    	public String sendMail(HttpSession session, HttpServletRequest request,
    							@RequestParam("mail_file") MultipartFile multipartFile) {
    		//사용자 정보
    		MemberDTO loginDto = getUserInfoBySession(session);
    		
    		//메일 받는 사람들
    		String addressListStr = request.getParameter("mail_receiver");
    		
    		//데이터 파싱
    		String mail_title = request.getParameter("mail_title");
    		String mail_content = request.getParameter("mail_content");
    		String mail_sender = loginDto.getMail();
    		int member_num = loginDto.getMember_num();	
    		
    		//Service에 넘겨줄 MailDTO
    		MailDTO mailDto = new MailDTO();
    		mailDto.setMail_title(mail_title);
    		mailDto.setMail_content(mail_content);
    		mailDto.setMail_sender(mail_sender);
    		mailDto.setMember_num(member_num);
    
    		String msg = service.sendMail(mailDto, addressListStr, multipartFile);
    		session.setAttribute("msg", msg);	
    		return "redirect:writeMail";
    	}
        
        //서비스
    	public String sendMail(MailDTO mailDto, String addressListStr, MultipartFile multipartFile) {
    		System.out.println("multipartfile : " + multipartFile.isEmpty());
    		if(!multipartFile.isEmpty()) {
    			saveFile(mailDto, multipartFile);
    		}
    		dao.sendMail(mailDto);
    
    		//보낸 메일 고유번호
    		int recentMailNum = mailDto.getMail_num();
    		//DB에 넘겨줄 MailRecDto
    		MailRecDTO mailRecDto = new MailRecDTO();
    		mailRecDto.setMail_num(recentMailNum);
    		
    		//Mail_Rec테이블 insert (MappingTable -- 받은 사람들 저장하기)
    		String msg = "메일전송이 완료되었습니다!";
    		if(addressListStr != null) {
    			String addressList[] = addressListStr.split(" ");
    			List<Integer> rec_numList = dao.findMemberNumByMailAddress(addressList);
    			
    			int res = 0;
    			for(int i=0; i<rec_numList.size(); i++) {
    				mailRecDto.setRec_num(rec_numList.get(i));
    				mailRecDto.setMail_receiver(addressList[i]);
    				res += dao.insertReceiveTable(mailRecDto); //insert 작업
    			}
    			
    			if(res != rec_numList.size()) {
    				msg = "다시 시도해주세요";
    				return "redirect:writeMail";
    			}
    		} 
    		//내게쓰기인 경우
    		else if(addressListStr == null) {
    			mailRecDto.setRec_num(mailDto.getMember_num());
    			mailRecDto.setMail_receiver(mailDto.getMail_sender()); //보내는 사람이 받는 사람이기 떄문
    			dao.insertReceiveTable(mailRecDto); //insert 작업
    		}
    		return msg;
    	}

     

    수정하면서 가독성을 높히기 위해 메서드를 따로 만들기도 하고 메서드명을 바꾸기도 했지만,

    그걸 떠나서 확실히 컨트롤러는 유저의 요청만을 처리하거 나눈후 가독성이 높아진 것을 볼 수 있었고, 서비스에서 비즈니스 로직을 처리하니 비즈니스 로직만 모아놓고 볼 수 있어서 유지보수하기 편했다.

     

    'Java' 카테고리의 다른 글

    @Transactional이란 무엇인가  (0) 2023.11.22
    생성자 주입(Constructor Injection)을 권장하는 이유  (1) 2023.11.20
    Early Return  (0) 2023.11.18
    JVM  (0) 2023.11.16
    Token인증 방식(JWT)과 Session인증 방식  (0) 2023.11.13
Designed by Tistory.