열심히 끝까지

디바이스 융합 자바(Java) day73 - 2-Layered 아키텍쳐 본문

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

디바이스 융합 자바(Java) day73 - 2-Layered 아키텍쳐

노유림 2022. 9. 26. 17:35

[오늘 진도]
[ 2-Layered 아키텍쳐 ] 
    - 입사지원서에 구조를 이해하고 있다고 어필 가능

Spring MVC를 기반으로한 프로젝트 순서
1. xxx.do 요청
2. 서블릿 컨테이너 구동
         == DispatcherServlet을 생성한다는 뜻
              >> DS-servlet.xml(설정 파일)를 참고(로드)해서 생성
                    : 현재의 계층(Layer)을 "프레젠테이션 레이어"라고 함
3. 스프링컨테이너 구동
      : Controller 객체들 생성
      : @, requestMapping 해줄 것들 
      : 이 때, Controller 객체로 DAO 객체를 사용함
         -> DAO2를 사용하고 싶었으나 잘 안됨! @Autowired(의존성 주입)가 미리 되어 있어야 함!!!
   

☆ Controller의 모든 메서드는 DAO 객체를 직접 이용하고 있음!
☆ Spring(일반적인 프레임워크)에서는 DAO 객체를 직접 이용하지 않고,
        반드시 "비즈니스 컴포넌트(ServiceImpl)"를 이용해서 DAO 객체를 다룰 수 있게끔 구성해야 함!!!!
★ 반드시 "비즈니스 컴포넌트"를 사용해야하는 이유?
    1) DAO 클래스 교체 등의 유지보수 유리 및 용이
            "비즈니스 컴포넌트"의 입장에서는 자신을 이용하는 클라이언트는 Controller!
            클라이언트인 Controller는 ServiceImpl를 멤버변수로 사용하면 ServiceImpl가 변경되어도 Controller 자체는 변화 없음
              == 낮은 결합도
    2) AOP 적용 용이
            횡단관심(어드바이스)이 동작하려면  Service 클래스(ex. pointcut.java)의 비즈니스 메서드가 실행되어야 함
            
    결론 ) Controller 클래스는 "비즈니스 컴포넌트"를 멤버변수로 사용해야하며, 
             해당 객체에게 "의존성 주입(DI)"을 해야 함


★오늘의 핵심★
> 비즈니스 컴포넌트를 이용하려면??'
      Controller가 Service를 멤버변수로 가지고 있고,
      Service는 DAO를 멤버변수로 가지고 있음
       >> Controller보다 ServiceImpl가 먼저 생성되어 있어야 함!!!
             : Controller에 SI가 의존성 주입 될 예정이기 때문!!!!!
             : ServiceImpl(비즈니스 컴포넌트)는 스프링 컨테이너가 생성해줘야하는 "객체"
             == ServiceImpl를 생성하는 스프링컨테이너를 C를 생성하는 

                  스프링컨테이너보다 먼저 구동시켜야 함!!

현재까지 진행순서
서블릿컨테이너 -> 스프링 컨테이너

앞으로의 진행순서
스프링컨테이너? -> 서블릿 컨테이너 -> 스프링 컨테이너?->스프링컨테이너

결론 ) 기존의 구조에서 스프링컨테이너가 1개 더 필요하고, 이 스프링컨테이너는 

          기존의 스프링컨테이너보다 먼저 구동되어야 함

기존의 구조 + 스프링컨테이너x1
>> [ 2-Layered 아키텍쳐 ]
           기존의 Presentation Layer(MVC Layer : 책에서는 이렇게 등장)보다 "먼저 구동"되는
           Business Layer(스프링 컨테이너)를 추가한 것
           >> 먼저 구동되어야 하기 때문에 ContextLoaderListener 등록할 것
                  == 무조건 "web.xml"에 등록할 것
                              1. Listener는 Servlet
                              2. 기존의 스프링 컨테이너보다 먼저 구동 되어야 하기 때문에
                                 기존의 스프링컨테이너보다 우선 적용되는 것에 등록
                  == /WEB-INF/applicationContext.xml 설정 파일이 없기 때문!!


                               applicationContext.xml은 JAVA 관련 설정파일이기 때문에

<context-param>
     <param-name>contextConfigLocation</param-name>
  	 <param-value>classpath:applicationContext.xml</param-value>
</context-param>

 설정을 추가해서 사용할 것

 

1. BoardController에 

@Autowired private BoardService boardService;

작성

 

 

2. BoardController에 BoardDAO를 지우고 boardService로 변경
[과거 코드는 디바이스 융합 자바 day72 참고]

--------BoardController.java

package com.ryo.biz.controller;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;

//import javax.servlet.http.HttpServletRequest;
//import javax.servlet.http.HttpServletResponse;
//import javax.servlet.http.HttpSession;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
//import org.springframework.web.servlet.ModelAndView;

import com.ryo.biz.board.BoardService;
import com.ryo.biz.board.BoardVO;
import com.ryo.biz.board.impl.BoardDAO;

@Controller
@SessionAttributes("data") // "data"라는 이름의 데이터가 Model 객체에 세팅이 된다면, 그 것을 session에 기억시키겠다.
public class BoardController{
	
	@Autowired
	private BoardService boardService; // 비즈니스 컴포넌트, DAO를 직접 이용하지 않을 것!

	// main에 넣을 구분값
	@ModelAttribute("scMap")
	public Map<String,String> searchConditionMap(){
		// 인자 없고 맵 반환 예정
		// 검색 조건에 들어가야 할 맵
		Map<String,String> scMap = new HashMap<String,String>();
		// 옵션에 들어갈 내용 관리 : View에 어떻게 보여야 할지 Model이 어떤 값을 받아야 할지
		scMap.put("제목", "TITLE");
		scMap.put("작성자", "WRITER");
		// modeladdattribute 된 상태
		return scMap;
	}
	
	// main
	// API 분석 때 필요할 것, VO에 넣는 것이 좋음
	@RequestMapping("/main.do")
	public String main(@RequestParam(value="searchCondition", defaultValue="TITLE", required=false)String searchCondition, @RequestParam(value="searchContent", defaultValue="", required=false)String searchContent,BoardVO bVO, Model model){
		// 검색하는 것의 값을 자동매핑 불가(커맨드 객체에는 없기 때문에 불가)
		// java에서만 사용할 목적으로 BoardVO에 추가
		// >> 하지만 전체에서 딱 한번 쓰는 등의 너무 비효율적일 때, 사용하는 @
		//    ==> @RequestParam
		//        : 커맨드 객체에는 없는 파라미터를 Controller 클래스에 전달해주기 위해 사용
		System.out.println("검색조건: "+searchCondition);
	    System.out.println("검색어: "+searchContent);
		List<BoardVO> datas = boardService.selectAllBoard(bVO);
		
		
		model.addAttribute("datas", datas);
		// mVO = mDAO.selectOneMember(mVO);
		// model.addAttribute("userName", mVO.getName());
		return "main.jsp";
	}
	
	// 글 하나 선택
	@RequestMapping("/board.do")
	public String board(BoardVO bVO, Model model){

		bVO=boardService.selectOneBoard(bVO);
		// mav.addObject("data", bVO);
		model.addAttribute("data", bVO);
		return "board.jsp";
	}
	
	
	/*
	@RequestMapping("/board.do")
	public ModelAndView board(BoardVO bVO, BoardDAO bDAO,ModelAndView mav){

		bVO=bDAO.selectOneBoard(bVO);
		mav.addObject("data", bVO);
		mav.setViewName("board.jsp");
		return mav;
	}
	 */
	
	// 글 추가.jsp로 이동
	@RequestMapping(value="/binsert.do", method=RequestMethod.GET)
	public String binsertin() {
		return "insertBoard.jsp";
	}
	
	// 글 추가
	// get과 post일 때로 나눌 것 get은 이동 post일 때는 
	@RequestMapping(value="/binsert.do", method=RequestMethod.POST)
	public String binsert(BoardVO bVO) {
		
		boardService.insertBoard(bVO);
		
		return "redirect:main.do";
	}

	// 글 삭제
	@RequestMapping("/bdelete.do")
	public String bdelete(BoardVO bVO) {
		
		boardService.deleteBoard(bVO);
		
		return "redirect:main.do";
	}
	
	// 글 업데이트
	@RequestMapping("/bupdate.do")
	public String bupdate(@ModelAttribute("data")BoardVO bVO) {
		
		boardService.updateBoard(bVO);
		
		return "redirect:main.do";
	}
	
	/*
	@Override
	public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {

		BoardVO bVO = new BoardVO();
		bVO.setBid(Integer.parseInt(request.getParameter("bid")));

		BoardDAO bDAO = new BoardDAO();
		bVO=bDAO.selectOneBoard(bVO);

		HttpSession session = request.getSession();
		session.setAttribute("data", bVO);

		ModelAndView mav = new ModelAndView();
		mav.addObject("data", bVO);
		mav.setViewName("board.jsp");

		return mav;
	}
	 */
	//	@Override
	//	public String handleRequest(HttpServletRequest request, HttpServletResponse response) {
	//		BoardVO bVO = new BoardVO();
	//		bVO.setBid(Integer.parseInt(request.getParameter("bid")));
	//		
	//		BoardDAO bDAO = new BoardDAO();
	//		bVO=bDAO.selectOneBoard(bVO);
	//		
	//		HttpSession session = request.getSession();
	//		session.setAttribute("data", bVO);
	//		
	//		return "board";
	//	}

}

memberController.java---------------

package com.ryo.biz.controller;

import javax.servlet.http.HttpServletRequest;
// import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
//import org.springframework.web.bind.annotation.SessionAttributes;
//import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.bind.annotation.SessionAttributes;

import com.ryo.biz.member.MemberService;
import com.ryo.biz.member.MemberVO;
import com.ryo.biz.member.impl.MemberDAO;

@Controller
@SessionAttributes("member")
public class MemberController {
	
	@Autowired
	private MemberService memberService;

	// 로그인으로 들어가는 index
	@RequestMapping(value="/login.do", method=RequestMethod.GET)
	public String index() {
		// 로그인 화면을 보여줘!
		// 기본이 포워드 방식
		// 데이터를 넣어서 보낼 때는 그냥 쓰면 됨
		return "login.jsp";
	}
	// POST 요청에서만 할 수 있게 가능
	// 로그인 및 세션에 정보 저장
	@RequestMapping(value="/login.do", method=RequestMethod.POST)
	public String selectOneMember(MemberVO mVO, HttpSession session) {
		System.out.println("로그 : 로그인컨트롤러 들어옴");
		//ModelAndView mav = new ModelAndView();
		mVO = memberService.selectOneMember(mVO);
		if(mVO == null){
			return "login.jsp";
		}
		else {
//			mav.addObject("member", mVO);
//			mav.setViewName("main.do");
			session.setAttribute("user", mVO);
			return "redirect:main.do";
		}
	}



	// 데이터를 넣으려면 ModelAndView 사용해야 함
	/*
	@RequestMapping(value="/login.do", method=RequestMethod.POST)
	public ModelAndView selectOneMember(MemberVO mVO, MemberDAO mDAO, ModelAndView mav) {
		System.out.println("로그 : 로그인컨트롤러 들어옴");
		mVO = mDAO.selectOneMember(mVO);
		return "login.jsp";
	}
	 */
	// 로그아웃
	@RequestMapping("/logout.do")
	public String logout(HttpServletRequest request) {
		HttpSession session = request.getSession();
		session.invalidate();
		//view Resolver는 기본이 forward 방식이구나!
		return "redirect:login.do";
	}
	// 회원가입
	@RequestMapping("/signin.do")
	public String signin(MemberVO mVO) {

		memberService.insertMember(mVO);

		return "redirect:login.do";
	}
	
	// 멤버 삭제
	@RequestMapping("/mdelete.do")
	public String mdelete(HttpServletRequest request,MemberVO mVO) {
		System.out.println(mVO.getMid());

		System.out.println("삭제하려는 mVO의 값 : " + mVO.getMid());
		memberService.deleteMember(mVO);
		HttpSession session = request.getSession();
		session.invalidate();
		return "redirect:login.do";
	}
	
	// 멤버 업데이트
	@RequestMapping("/mupdate.do")
	public String mupdate(@ModelAttribute("member")MemberVO mVO) { // MemberVO mVO, MemberDAO mDAO,HttpSession session
		
		System.out.println("데이터 확인 : " + mVO);
		
		memberService.updateMember(mVO);
		// session.setAttribute("user", mVO);
		return "redirect:main.do";
	}
	// 마이페이지
	@RequestMapping("/mypage.do")
	public String mypage(HttpSession session, Model model) { // (MemberVO mVO, MemberDAO mDAO)
		// mVO = mDAO.selectOneMember(mVO);
		model.addAttribute("member",session.getAttribute("user"));
		return "mypage.jsp";
	}


	//	public ModelAndView mypage(MemberVO mVO, MemberDAO mDAO, ModelAndView mav) {
	//
	//		mVO = mDAO.selectOneMember(mVO);
	//		mav.addObject("member", mVO);
	//		mav.setViewName("mypage.jsp");
	//		return mav;
	//	}

}

 


3. web.xml에

  <listener>
  	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  위 코드 추가 
== 먼저 구동되어야 하기 때문에 web.xml에 작성할 것

web.xml 전체 코드-----------------

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
  <servlet>
  <!-- 사용하는 디스패쳐 서블릿 관련 작성 -->
    <servlet-name>DispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>DispatcherServlet</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>
  
  <!-- 인코딩 필터 -->
  <filter>
  	<filter-name>characterEncodingFilter</filter-name>
  	<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
  	<init-param>
  		<param-name>encoding</param-name>
  		<param-value>UTF-8</param-value>
  	</init-param>
  </filter>
  <!-- 언어 적용되는 곳 필터 -->
  <filter-mapping>
  	<filter-name>characterEncodingFilter</filter-name>
  	<url-pattern>*.do</url-pattern>
  </filter-mapping>
  
  
  <context-param>
    <param-name>contextConfigLocation</param-name>
  	<param-value>classpath:applicationContext.xml</param-value>
  </context-param>
  <listener>
  	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  
</web-app>

 

 

4. applicationContext.xml를 찾기 위해서 web.xml에  

  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>

위 코드 추가할 것

 

   > 추가하는 이유
         : /WEB-INF/applicationContext.xml 설정 파일이 없기 때문!!
           applicationContext.xml은 JAVA 관련 설정파일이기 때문에 위처럼 작성해줄 것

 

 


 

 

[정리]
1. 서버(tomcat) 시작
2. web.xml 파일 로딩, 서블릿 컨테이너 구동
3. 서블릿 컨테이너는 web.xml에 등록된 ContextLoaderListener 객체를 가장 먼저 생성
         -> Pre-loading 방식
4. 생성되는  ContextLoaderListener 객체는 src/main/resources의 applicationContext.xml을 로딩하여 
   스프링 컨테이너를 구동하라고 설정
         -> 먼저 실행되는 스프링 컨테이너를 "Root 컨테이너"라고 함
5. ServiceImpl, DAO 객체들을 메모리에 생성
6. 원래 하던 내용 실행
7. xxx.do 요청
8. 서블릿 컨테이너 구동
         == DispatcherServlet을 생성한다는 뜻
              >> DS-servlet.xml(설정 파일)를 참고(로드)해서 생성
                    : 현재의 계층(Layer)을 "프레젠테이션 레이어"라고 함
9. 스프링컨테이너 구동
      : Controller 객체들 생성
      : @, requestMapping 해줄 것들 
      : 이 때, Controller 객체로 DAO 객체를 사용함
         -> DAO2를 사용하고 싶었으나 잘 안됨! @Autowired(의존성 주입)가 미리 되어 있어야 함!!!