Spring 2 Layered 아키텍쳐 스타일 day 64

stan·2023년 8월 9일
0

Spring

목록 보기
6/22

2 Layered 아키텍쳐 스타일

표현 계층과 비지니스(핵심) 계층의 통합

실무 : 프레젠테이션 레이어와 비지니스 레이어의 통합

  • 비지니스 : 핵심로직 (관심, concern)
  • CRUD 모든 기능은 아무리 복잡해도 CRUD

※ 현재 유지보수 불리한 상태

1) 브라우저(Client, 사용자)에서 서버로 요청
- request(※jsp의 내장객체 request가 아님. 그냥 키워드)
2) .do로 요청이 왔으니 DispatcherServlet 서블릿을 생성해야함
- 의존주입(DI)를 필요로함
- 스프링에서 기본제공하는 DispatcherServlet을 사용중
- DispatcherServlet-servlet.xml을 참고하여 DI해줌
- 스프링컨테이너가 구동됨
- 프레젠테이션 레이어 (그냥 컨트롤러 파트 : .do로 요청이 와서 DispatcherServlet을 생성 하고 의존주입 하는 부분 )

3) 요청에 맞는 Controller 객체를 호출하여 사용
- C, DAO, VO, Model ...
- Command 객체

※ 현재 유지보수 불리한 상태

※ 1) 서버에서는 DBMS 변경이 자주 발생함; DBMS가 가장 많이 바뀜; 조금이라도 싸면 그쪽으로 넘어감
즉, DAO 변경 이 자주 발생함
DAO를 직접 이용하고 있으므로, ★결합도★가 높아서 유지보수가 불리함 (뭔가 객체를 직접 이용하면 ★결합도★가 높아짐)
다른 DAO로의 변경이 매우 불리한 상태

※ 2) AOP 적용이 불가능한 상태
AOP : xxxService 류에만 호출가능함
( AOP를 사용하면 로그, 보안, 권한확인, ... 등을 자동호출할수있음 )
( 위의 코드를 별도로 분리해서 관리 할 수 있기때문에 응집도가 높아짐 )
( 유지보수 유리)
매번 로그, 보안, 권한확인, ... 등을 직접 처리해야하므로 응집도가 낮음
유지보수 불리한 상태


  • 유지보수를 유리한 상태로 바꿔보자! (비즈니스 레이어를 추가해보자!) (DAO를 직접 이용하지않고, Service를 통해 사용해보자!)

BoardServiceImpl

package com.spring.biz.board;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
//메모리에로딩한다
@Service("boardService")
public class BoardServiceImpl implements BoardService {
	// Service 레이어가 관념적으로 존재하는데, 그것을 구현한 클래스
	// Service 레이어에서는 DAO를 사용함
	//  == C 파트
	//  : DAO를 사용할것이기때문에
	//    DAO와 메서드 시그니처를 맞추면 유리함
	// 아~~ 메서드 시그니처를 강제하고싶다!
	//  => 인터페이스
	
	@Autowired //의존주입
	private BoardDAO boardDAO;
	// 의존관계 -> DI
	
	@Override
	public BoardVO selectOne(BoardVO bVO) {
		return boardDAO.selectOne(bVO);
	}

	@Override
	public List<BoardVO> selectAll(BoardVO bVO) {
		return boardDAO.selectAll(bVO);
	}

	@Override
	public boolean insert(BoardVO bVO) {
		return boardDAO.insert(bVO);
	}

	@Override
	public boolean update(BoardVO bVO) {
		return boardDAO.update(bVO);
	}

	@Override
	public boolean delete(BoardVO bVO) {
		return boardDAO.delete(bVO);
	}
}

자바클래스로 Service 레이어를 구현

BoardController

package com.spring.view.controller;

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

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 com.spring.biz.board.BoardService;
import com.spring.biz.board.BoardVO;
import com.spring.biz.member.MemberVO;

@Controller
public class BoardController {
	
	@Autowired
	private BoardService boardService;

	@ModelAttribute("searchMap")
	public Map<String,String> searchMap(){
		Map<String,String> map=new HashMap<String,String>();
		map.put("제목", "TITLE");
		map.put("작성자", "WRITER");
		return map;
	}
	
	@RequestMapping(value="/main.do")
	public String main(@ModelAttribute("mem")MemberVO mVO, BoardVO bVO, Model model) {
		System.out.println("searchCondition: "+bVO.getSearchCondition());
		System.out.println("searchContent: "+bVO.getSearchContent());
		
		mVO.setMid("test");
		mVO.setMpw("1234");
		
		System.out.println("MainController 로그");
		
		// model.addAttribute("mem", mVO);
		model.addAttribute("datas", boardService.selectAll(bVO));
		return "main.jsp";
	}
	//HandlerMapping이라 web.xml에서 .do 해서 여기로 옴
	@RequestMapping(value="/board.do")
	public String selectBoard(BoardVO bVO, Model model) {
		System.out.println("BoardController 로그");
		model.addAttribute("data", boardService.selectOne(bVO));
		boardService.update(bVO);
		return "board.jsp";
	}
	
	@RequestMapping(value="/updateBoard.do")
	public String updateBoard(BoardVO bVO) {
		System.out.println("BoardController 로그");
		boardService.update(bVO);
		return "board.do";
	}
	
	@RequestMapping(value="/deleteBoard.do")
	public String deleteBoard(BoardVO bVO) {
		System.out.println("BoardController 로그");
		if(boardService.delete(bVO)){
			return "redirect:main.do";
		}
		else{
			return "board.do";
		}
	}
	
	@RequestMapping(value="/insertBoard.do", method=RequestMethod.GET)
	public String insertBoardPage() {
		System.out.println("InsertBoardPageController 로그");
		
		return "redirect:insertBoard.jsp";
	}
	
	@RequestMapping(value="/insertBoard.do", method=RequestMethod.POST)
	public String insertBoard(BoardVO bVO) {
		System.out.println("InsertBoardController 로그");
		
		if(boardService.insert(bVO)){
			return "redirect:main.do";
		}
		else{
			return "redirect:insertBoard.jsp";
		}
	}
	
}

1) C의 메서드 인자로 존재하던 DAO를 제거

2) DAO를 대신 사용해줄 Service를 멤버변수로 추가
DAO의 메서드와 Service의 메서드는 동일

	똑같이 BoardDAO사용하고 있어서 MainController랑 InsertBoardController에 있는 메서드를 가져다 BoardController로 줌 
		통합이 가능해짐

	(Service가 DAO를 대신 사용하는것이기 때문에) 
	(DAO를 안쓰는것은 아님)

3) 이제는 DAO가 자주 변경되더라도 Service 내부의 멤버변수만 변경하면 됨
- 결합도가 낮아지므로 유지보수가 유리함

4) 멤버변수로 추가한 Service가 메서드 수행 주체로써 사용됨
- 의존관계임
- 의존주입(DI)해야함
- 그래서 @Autowired 써야함


5) BeanCreationException 발생!
BoardController를 못만든다고합니다...ㅠㅠ
boardService를 메모리에서 찾을수가 없어서...ㅠㅠ

어노테이션 Autowired및 Component다 붙어있는데 못만들어요

boardService가 메모리에 없다는건
@Service가 Component임 얘가 new임; 얘가 안됬다는거

6) boardService가 메모리에 없다는건 @Service가 동작하지 않았다는거. applicationContext에서 component scan의 영역이 아니였다는것
우리는 DispatcherServlet-servlet.xml을 쓰고 있는데 여기서 component-scan을 한적이 없다 !

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd">

	<context:component-scan base-package="com.spring.biz" />
			
</beans>

7) applicationContext.xml에서 미리 DAO, Service 류의 객체들을 생성하고,
web.xml로 DispatcherServlet 생성하고,
DispatcherServlet.xml로 Controller를 호출할수있게 할 예정!!

8) applicationContext.xml을 추가; DAO랑 Service류를 미리 new 해주려고 추가
스프링 컨테이너 추가 한다는걸 (루트컨테이너라고도함) (DS도 스프링컨테이너임)
비즈니스(서비스) 레이어를 추가한다고 함

9) 루트 컨테이너로써 "서버가 시작될때" applicationContext.xml을 구동시켰으면 좋겠다!
서버 시작을 감지(모니터링)
서블릿을 상속받은 리스너(not pojo) --->> new IoC => web.xml
서블릿류는 죄다 web.xml에다 쓰는겁니다

<listener>도 스프링에서 제공하는 걸 쓸거임

Web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <servlet> <!-- bean역할; 이 순간부터 DS.xml이 있어야 함; 그래야 웹이 동작 -->
    <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>characterEncoding</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>characterEncoding</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>

결론 : 루트 컨테이너(applicationContext.xml, 스프링 컨테이너, 비즈니스 레이어)가 객체들을 메모리에 new
서블릿 컨테이너(web.xml)가 DispatcherServlet을 메모리에 new
스프링 컨테이너(DS-servlet.xml,프레젠테이션 레이어)가 Controller를 호출하여 요청 처리 함
==>> 2-Layered 아키텍쳐 스타일


  1. .do요청 -> web.xml이 반응(서블릿 컨테이너)
    web.xml이 동작을 하려면 web.xml이라는 서블릿컨테이너의 객체인 DispatcherServlet이 생성(new)되어야 함
    -> DispatcherServlet 서블릿을 new해라!

  2. DS를 new하려면 DS-servlet.xml이 필요하다고함...
    -> 그래서 만들었음
    -> DS 내부에 HM,C,VR,... POJO들을 new해야함
    .do->C String->VIEW
    (DS내부에 있는 HandlerMapping, Controller, ViewResolver 모두 사용할거임)

    -> 그러면 스프링 컨테이너가 필요함 (일반 POJO(HM,C,VR)를 new하니깐)


  1. C가 DAO를 직접사용중
    Service를 끼워서 쓰려면??
    DAO,Service를 미리 new를 했었어야함
    "서버가 시작될때"
    서버시작을 감지==리스너 (리스너는서블릿임)
    web.xml에 등록

  2. DAO,Service가 POJO니까 새로운 xxx.xml이 필요 (서블릿컨테이너로 못함)
    applicationContext.xml(루트 스프링 컨테이너)

컨테이너 == 객체생성의 주체
new를 개발자대신 해줌
IoC

※HM과 VR은 태그로 해도 되고 어노테이션으로 해도 되고

※HM : 요청에대해 알맞는 C반환; 어떤 .do가 어떤 C인지 처리매핑
※VR : 보여주는 역할

※1 xml : 1 컨테이너
※서블릿 컨테이너 : 톰캣

※DS는 서블릿 컨테이너의 객체

리스너는 서블릿이니까 web.xml


profile
이진 입니다

0개의 댓글