페이스북은 MVC 모델이 확장이 어렵고 거대한 시스템에 어울리지 않는다고 결론을 내렸습니다.

 

그 이유는 새로운 기능 추가 시에 시스템의 복잡도가 기하급수적으로 증가하여

개발자는 기존 기능에 대한 영향을 주지 않을지에 대한 불안함을 주게됩니다.

모델(Model)과 뷰(view)의 수가 커지고 데이터의 흐름이 양방향으로 이루어질 수 있도록 

복잡도는 더욱 증가하고 디버깅 및 코드를 이해하기 어려워지므로 MVC는 작은 앱에 어울린다는 것입니다.

 

그래서 좀 더 예측 가능하도록 코드 구조화에 대한 목표로

데이터 흐름이 단방향인 시스템 아기텍처인 "Flux"를 제안합니다.

 

 

- Action : view에서 사용자가 취한 액션

- Store : APP의 모든 데이터를 저장하며 MVC의 Model을 담당합니다.

- Dispatcher : 모든 데이터를 관리하는 Controller 이며 Action이 시작될 때 어떻게 store가 업데이트 되어여 하는지 결정합니다.

- View : Store가 변경된 경우 같이 변경됩니다.

- 단방향 : 데이터는 단방향으로 흐르고 데이터 계층이 자기가 영향을 미치는 View 업데이트 완료 후 다음 작업을 진행합니다.

 

View에서 어떠한 Action이 발생하면 

Dispatcher는 해당 Action을 처리하기 위한 적절한 Store를 찾아서 전달한다. 

Store는 Dispatcher에서 들어온 요청을 처리하여 데이터가 변화되었음을 감지하면 해당 View를 재 렌더링 한다.




이번 포스팅에는 Mapper 인터페이스와 Mapper XML을 비교하다가 알게된 점을 기록하려고 합니다.

 

일단 root-context.xml에 해당 코드를 추가하도록 하겠습니다.

 

<!-- root-context.xml -->

<mybatis-spring:scan base-package="org.zerock.mapper"/>

 

이제 Mapper 인터페이스로 DB에 있는 값을 가져오는 방식과

Mapper.xml로 가져오는 방식을 비교하도록 할텐데요?

 

1. Mapper 인터페이스 방식

 

org.zerock.mapper 패키지를 만든 후 BoardMapper.java 파일을 작성합니다.

 

<!-- BoardMapper.java -->

package org.zerock.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Select;
import org.zerock.domain.BoardVO;

public interface BoardMapper {
	
	@Select("select * from tbl_board where bno > 0")
	public List<BoardVO> getList();
}

 

이제 작성한 코드를 테스트 하겠습니다.

 

package org.zerock.mapper;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import lombok.Setter;
import lombok.extern.log4j.Log4j;

@RunWith(SpringJUnit4ClassRunner.class) // 현재 테스트 코드가 스프링을 실행하는 역할을 할 것이다 라는 것을 나타냄
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml") // 지정된 클래스나 문자열을 이용해 필요한 객체들을 스프링내에 객체로 등록합니다.
@Log4j
public class BoardMapperTests {
	
	@Setter(onMethod_ = @Autowired)
	private BoardMapper mapper;
	

	
	@Test
	public void testGetList() {
		mapper.getList().forEach(board -> log.info(board));
	}
	
}

 

 

테스트의 결과는 다음과 같습니다.

 

 

 

이렇게 BoardVO의 담겨있는 값들이 잘나오는데 문제는 지금 부터입니다!

 

 

2. xml 방식

 

위의 클래스와 같은 이름의 mapper를 작성하겠습니다.

 

<!-- BoardMapper.xml -->

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.zerock.mapper.BoardMapper">

  <select id="getList" resultType="org.zerock.domain.BoardVO">
  <![CDATA[
  select * from tbl_board where bno > 0
  ]]>
</select>

</mapper>

 

XML에 SQL문이 처리 되었으니 BoardMapper 인터페이스에 있는 어노테이션 sql은 주석처리 하겠습니다.

 

<!-- BoardMapper.java -->

package org.zerock.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Select;
import org.zerock.domain.BoardVO;

public interface BoardMapper {
	
//	@Select("select * from tbl_board where bno > 0")
	public List<BoardVO> getList();
}

 

 

마찬가지로 테스트를 실행하도록 하겠습니다.

 

 

 

아까의 mapper 인터페이스와 같은 결과값이 나왔습니다!

 

본인이 기존에 알고 있던것은 

- src/main/java/org/zerock/mapper   

- src/main/resouces/org/zerock/mapper

 

이 두 경로가 일치하고,

 

<!-- root-context.xml -->

<mybatis-spring:scan base-package="org.zerock.mapper"/>

 

root-context.xml에서 다음과 같이 base-package를 설정하면

당연히 src/main/java 경로를 탈 줄 알았는데

resources경로여도 패키지명만 같으면 인식 할 줄 몰랐는데

오늘 해보니 경로가 달라도 패키지명만 같으면 된다는걸 깨달은 좋은 경험이 되었습니다.

 

그리고 알아야 할 것은

 

Mapper 인테페이스와 Mapper XML 둘 다 한번에 사용하지는 못하고

mapper.xml을 사용할 때는 mapper.java의 sql 어노테이션을 주석처리하거나 삭제하고,

mapper.java를 사용할 때는 해당 sql 태그를 주석처리 or 삭제 해야합니다.

 

WAS의 구동 중 가장 흔한 에러와 관련된 HTTP 상태 코드는 '404'와 '500' 에러 코드 입니다.

500 메시지는 'Internal Server Error'이므로 @ExceptionHandler를 이용해서 처리되지만,

잘못된 URL을 호출할 때 보이는 404 에러 페이지의 경우는 조금 다르게 처리하는 것이 좋습니다.

 

서블릿이나 JSP를 이용했던 개발 시에는 web.xml을 이용해서 별도의 에러 페이지를 지정할 수 있습니다.

에러 발생 시 추가적인 작업을 하기는 어렵기 때문에 스프링을 이용해서 404와 같이

WAS 내부에서 발생하는 에러를 처리하는 방식을 알아야 합니다.

 

스프링 MVC의 모든 요청은 DispatcherServlet을 이용해서 처리되므로 404에러도 같이 처리할 수 있도록

web.xml을 수정합니다.

 

<!-- web.xml -->

<!-- Processes application requests -->
	<servlet>
		<servlet-name>appServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
		</init-param>
        
       		 // 추가
		<init-param>  
			<param-name>throwExceptionIfNoHandlerFound</param-name>
			<param-value>true</param-value>
		</init-param>
        
		<load-on-startup>1</load-on-startup>
	</servlet>

 

그리고 org.zerock.exception.CommonExceptionAdvice에는 다음과 같은 메서드를 추가합니다.

 

@ExceptionHandler(NoHandlerFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public String handle404(NoHandlerFoundException ex) {
	
	return "custom404";
}

 

리턴값은 custom404이기 때문에 view도 만들겠습니다.

 

<!-- coustom404.jsp -->
<%@ page language="java" contentType="text/html; charset=EUC-KR"
    pageEncoding="EUC-KR"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>Insert title here</title>
</head>
<body>
	<h1>해당 URL은 존재하지 않습니다.</h1>
</body>
</html>

 

이제 존재하지 않는 URL을 호출하면 custom404.jsp가 호출되어 출력되는것입니다.

 

+ Recent posts