본문 바로가기
Spring

[레이어] 비즈니스 컴포넌트

by amoomar 2022. 4. 11.
반응형

 

목차는 다음과 같다.

1. 개요
2. 적용
   1) dao관련 코드 serviceImpl로 변경
   2) Business Layer 먼저 동작시키기

 

 

1. 개요

 

현재까지의 작동

1. 클라이언트가 서버에게 요청하면,
2. fc가 모든 요청을 받음, (dispatcherServlet) 서블릿 컨테이너가 생성된다.
3. 스프링 컨테이너가 생성한 controller객체에게 요청을 전달한다.
4. 매개변수의 command객체(DAO)를 이용해서 요청을 처리한다.
이때, 사용자의 요청을 처리하기 위해 DAO를 직접 이용하면 좋지 않으므로, 직접 이용하는 상황을 방지하기 위해 비즈니스 컴포넌트(serviceImpl)가 필요해진다. serviceImpl은 dao를 내부에 갖고 있는 친구이다.

 


왜 안좋을까?

: 문제는 크게 두가지로 본다.
1. DAO클래스의 교체가 매우 불리하다. : 인자를 하나하나 전부 갈아 끼워야하므로 결합도가 높아 유지보수에 불리하다.
2. AOP적용을 용이하게 하기 위함.

 


비즈니스 컴포넌트(serviceImpl)의 등장

Controller -> DAO가 이전의 연결 방식이었다면, Controller -> ServiceImpl -> DAO로 변경하겠다는 의미가 된다. 따라서 serviceImpld의 입장에서 사용자는 controller이다.


비즈니스 컴포넌트를 활용하게 되면, 코드의 변화 없이 설정 변화에 따라(무엇을 DI하느냐에 따라) 연결방식에 변화가 생기게 된다.

: service클래스가 어떤 버전의 dao를 멤버변수로 갖고 있느냐에 따라!


=> 객체 지향 언어가 가진 특징(다형성) : 사용하는 주체에 따라 연결 방식이 달라지기 때문이다.

 

 

 


 

 

2. 적용

 

1) dao관련 코드 serviceImpl로 변경

ServiceImpl객체가 비즈니스 메서드를 사용할 수 있도록 하기 위해 멤버변수로 선언하고, 초기화를 진행한다. 또한 기존에 비즈니스 메서드를 사용하기 위해 쓰이던 DAO를 삭제해준다. 변경된 코드는 아래와 같다. 

package com.test.app.controller;

import java.util.HashMap;
import java.util.List;
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.SessionAttributes;

import com.test.app.board.BoardService;
import com.test.app.board.BoardVO;
import com.test.app.board.impl.BoardDAO;

@Controller // ★x100
@SessionAttributes("data") // data라는 이름의 정보가 Model에 들어온다면, session에 기억해둬라! ☆
public class BoardController {

	@Autowired
	private BoardService boardService; // serviceImpl 멤버변수화 및 초기화
	
	@ModelAttribute("conMap") // @RequestMapping이 설정된 메서드보다 먼저 수행됨
	public Map<String,String> searchConditionMap() {
		Map<String,String> conMap=new HashMap<String,String>();
		conMap.put("제목", "title");
		conMap.put("작성자", "writer");
		conMap.put("내용", "content");
		return conMap; // 반환값은 자동으로 Model에 저장
	}
	
	@RequestMapping(value="/main.do")
	public String getBoardList(BoardVO vo, Model model) {
		// 검색 로직 추가할 예정
		List<BoardVO> datas=boardService.getBoardList(vo);
		model.addAttribute("datas",datas); // Model을 이용하여 전달할 정보를 저장!
		return "main.jsp";
	}
	@RequestMapping(value="/board.do")
	public String getBoard(BoardVO vo,Model model) {
		vo=boardService.getBoard(vo);
		model.addAttribute("data", vo); // ☆
		return "board.jsp";
	}
	@RequestMapping(value="/insertBoard.do")
	public String insertBoard(BoardVO vo) {
		boardService.insertBoard(vo);
		return "redirect:main.do";
	}
	@RequestMapping(value="/deleteBoard.do")
	public String deleteBoard(BoardVO vo) {
		boardService.deleteBoard(vo);
		return "redirect:main.do";
	}
	@RequestMapping(value="/updateBoard.do")
	public String updateBoard(@ModelAttribute("data")BoardVO vo) {  // ☆
		System.out.println(" null 업데이트 이슈 확인: "+vo.getWriter()); // session에 저장해둔 정보가 먼저 setter
		System.out.println(" null 업데이트 이슈 확인: "+vo.getTitle()); // 이후에 파라미터로 들어온 정보가 나중에 setter
		System.out.println(" null 업데이트 이슈 확인: "+vo.getContent());
		boardService.updateBoard(vo);
		return "redirect:main.do";
	}
}

 

이때 ServiceImpl은 Service클래스를 implements 하고 있으므로, 최상위 클래스를 멤버변수로 선언하면 ServiceImpl을 사용할 수 있다.

 

 


 


2) Business Layer 먼저 동작시키기

위의 상태에서 바로 동작시키게 되면, Controller의 DI대상인 비즈니스 컴포넌트(SI객체들)이 없으므로 예외가 발생하게 된다.

따라서  ServiceImpl객체들을 생성(메모리에 적재)해주어야한다. 이때, 이를 담당해줄 스프링 컨테이너가 한개 더 필요해지게 되는데, 이 스프링 컨테이너를 controller의 객체들을 생성하는 스프링 컨테이너보다 먼저 동작 시켜야한다. 즉, 2-Layered아키텍처(컨테이너를 두개 사용하는 구조)를 활용한다.

 

2-Layered아키텍처란

스프링 프레임워크 기반의 웹 프로젝트에서 채택하는 스타일로, MVC(Presentation) Layer와 Business Layer로 나누어서 컨테이너를 동작시킨다.

 

 

① web.xml에 리스너 등록

현재 상황에서는 Business Layer를 먼저 동작시켜야한다는 결론이 도출된다. 해당 Layer가 참조하는 xml파일은 applicationContext.xml이다. 리스너를 활용하여, 특정 이벤트의 발생을 기준으로 동작 시점을 설정해줄 수 있다. 이때 사용되는 리스너는 스프링 프레임 워크에서 제공하는 리스너 클래스(ContextLoaderListener)이다.

 

web.xml파일을 서블릿 컨테이너가 읽어서 동작할때 수행시킬 계획이다. == Pre-loading : 요청 전 객체들을 로딩(메모리에 적재) 하므로

 

기존 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">
	<filter>
		<filter-name>encFilter</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>encFilter</filter-name>
		<url-pattern>*.do</url-pattern>
	</filter-mapping>

	<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>

	<!-- 리스너 등록 -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	
</web-app>

 

 


 

② web.xml에 필요하다는 xml파일을 참조시켜 줌

위의 상태에서 바로 동작시키게 되면, 위에서 생성한 컨테이너가 기본으로 참조하는 /WEB-INF/applicationContext.xml가 없으므로 예외가 발생하게 된다. 동일한 이름의 xml파일은 이미 존재하나, 필요로 하는 위치에 존재하지 않아서 발생하는 오류이다.

 

해결하는 방법으로는

1. 에러에 해당하는 내용대로 파일을 생성해주거나,

2. 설정 정보를 변경하여 기존에 존재하는 xml파일을 참조할 수 있도록 해주는 것이다.

 

1번 방법을 채택할시, 원하는 위치에 필요로 하는 applicationContext.xml에는 기존 위치에 존재하는 applicationContext.xml의 내용이 동일하게 필요하므로, 같은 코드를 담은 파일이 중복되게 된다(기존 파일 삭제 불가능, 사용되고 있으므로). 이러한 이유로 인해 보편적으로 사용되는 방식은 아니다. 따라서 두 번째 방법을 일반적으로 채택하여 사용하고 있다. 해당 방법에 대한 코드를 아래에 작성하였다.

 

기존 web.xml파일의 내용에 주석을 포함하여 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">
	<filter>
		<filter-name>encFilter</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>encFilter</filter-name>
		<url-pattern>*.do</url-pattern>
	</filter-mapping>

	<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>
	<!-- web-inf하위에 applicationContext.xml이 필요하다고 하는데, 그거 이미 위에 있으니까 그냥 걔 참조하라고 명령 -->
	<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>

 

 

 


 

반응형

'Spring' 카테고리의 다른 글

다국어 처리 : 국제화  (0) 2022.04.13
이미지 업로드 + 예외처리  (0) 2022.04.12
[AOP] 트랜잭션 관리자  (2) 2022.04.08
[AOP] 다양한 어노테이션 : Controller_3  (0) 2022.04.07
[AOP] JDBC Template_DAO2  (0) 2022.04.06