이제 거의 다왔습니다..
실제로 개발의 기반이되는 (이론 따위 필요없고 개발 하고 싶으면 이것만 보면된다.)
근데 개발만 할껀아니잖아요.. 앞에 이론도 좀 봐야함...
아무튼!
우리의 Spring의 흐름은 간단하게 얘기하면
1. Dispatcher-Servlet
2. Controller
3. Service(impl)
4. DAO(impl)
5. Mybatis
6. 다시 Controller로 돌아와 -> JSP 호출(동적페이지)
이런식으로 흐른다고 보면된다. 실제로 개발을 할 때에 해당 프로세스는 숙지하고 있어야하며 다 알아야한다.
안그러면 힘들다... 그래서 하나하나 살펴보려고 한다.
1, 2번 Dispatcher-servlet 이 받은 요청 처리해주기.
(Dispatcher-Servlet-Controller)
자. 첫단추를 끼워야한다.
@RequestMapping , @Controller 이라는 어노테이션만 알면 끝난다. 위치와 소스는 다음과 같다.
위치는 보통 com/kitao/kr 까지만 생성되있을 껀데, 본인은 구분을 쉽게하기 위해 co, coa 까지 생성해 주었다.
package com.kitao.kr.co.coa.controller;
import java.util.List;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.kitao.kr.co.coe.service.BoardService;
import com.kitao.kr.co.coe.vo.BoardVO;
/**
* Handles requests for the application home page.
*/
@Controller
public class HomeController {
@Value("#{properties['ACTION_NAME']}")
public String ACTION_NAME;
@Resource(name="boardService")
private BoardService boardService;
@RequestMapping(value = "/")
public ModelAndView home(ModelMap model, HttpServletResponse response, HttpServletRequest request) throws Exception {
request.setCharacterEncoding("UTF-8");
List <BoardVO> boardVO = boardService.selectBoardList();
ModelAndView mv = new ModelAndView("co/coa/home");
mv.addObject("serverTime", "이거 넣을껀데?");
mv.addObject("boardVO", boardVO);
return mv;
}
}
소스는 이렇게 하나하나 차례로 살펴보자...
@Controller는 여기가 컨트롤러단이다! 라는걸 의미한다.
또한, @RequestMapping(value = "/") 의 의미는 가장 쉽게 말해서 Home에 접근하면 여기를 와라 라는 것이다.
즉, https://wwwhttp://localhost:9122/kr/ 이게 호출되면 해당 Controller를 탈 것이다.
여기서 부터 시작인 셈이다. 다음 class home을 봐보자.
return 형이 ModelAndView인건 아직 신경쓰지말자 다음에 얘기할 때가 오겠지..
아무튼 다시 돌아오면,
그리고 그 다음 단계인 Service 비스.. 무리한 애가 보인다. 전에 배웠던, 의존성 역전의 법칙을 사용하여(@Resoure) 먼저 의존성을 주입하고, Service단이 실행되는 것으로 보인다. 아마 Service 단에서 실행된 애가 boardVO형의 리스트에 넣어지는 것으로 보인다. 그러면 Service 단을 먼저 살펴보자.
저기에서 service는 coe쪽에 있지만, 뭐 상관없다. 어디에 위치해 있든, @Service 이노테이션을 통해 얘가 service단이다! 라는걸 선언해주면 된다. 그럼 안에 소스를 봐보자. 아 보기전에. VO를 봐보겠다.
BoardVO.java
package com.kitao.kr.co.coe.vo;
import java.util.Date;
public class BoardVO {
private String boardId;
private String boardContents;
private String boardTitle;
private Date boardRegDtm;
public String getBoardId() {
return boardId;
}
public void setBoardId(String boardId) {
this.boardId = boardId;
}
public String getBoardContents() {
return boardContents;
}
public void setBoardContents(String boardContents) {
this.boardContents = boardContents;
}
public String getBoardTitle() {
return boardTitle;
}
public void setBoardTitle(String boardTitle) {
this.boardTitle = boardTitle;
}
public Date getBoardRegDtm() {
return boardRegDtm;
}
public void setBoardRegDtm(Date boardRegDtm) {
this.boardRegDtm = boardRegDtm;
}
}
보면 그냥.. 맴버변수에 메소드변수가 들어가있는 것이다. 이게 끝이다. 다음 Service단을 보겠다.
BoardService.java
package com.kitao.kr.co.coe.service;
import java.util.List;
import org.springframework.stereotype.Repository;
import com.kitao.kr.co.coe.vo.BoardVO;
public interface BoardService {
List<BoardVO> selectBoardList() throws Exception;
}
아니 이건 뭐 설명할 필요도 없다. 그냥 Service의 부모가 되는 빈 인터페이스가 선언 되어 있다.
그다음 BoardServiceImpl이 중요하다.
3번 Service(impl)
BoardServiceImpl.java
package com.kitao.kr.co.coe.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.kitao.kr.co.coe.dao.COEBoardDAO;
import com.kitao.kr.co.coe.memory.test;
import com.kitao.kr.co.coe.vo.BoardVO;
@Service("boardService")
public class BoardServiceImpl implements BoardService {
@Autowired
private COEBoardDAO boardDAO;
@Override
public List<BoardVO> selectBoardList() throws Exception {
// 비즈니스 로직 수행
return boardDAO.boardSearch();
}
}
자 여기 다있다.. @Servce("boardService") 라는 애는 얘가 서비스이다! 라는걸알려준다. 따라서, Controller에서
호출된 서비스는 여기로 올 수 있었던 것이다. 실제로 구현도 여기서 다 이루어져 있다.
엄~청 간단한 impl단이지만, 실제로 비즈니스 로직을 수행하는 곳이기도 하다.
이제 쿼리를 실행하는 Mybatis를 실행하는 DAO를 보겠다.
4번, 5번 DAO(impl)과 Mybatis
COEBoardDAO.java
package com.kitao.kr.co.coe.dao;
import java.util.List;
import javax.inject.Inject;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.kitao.kr.co.coe.vo.BoardVO;
@Repository
public class COEBoardDAO {
@Inject
private SqlSession session;
private static String namespace = "com.kitao.kr.boardListSearch";
public List<BoardVO> boardSearch() throws Exception {
return session.selectList(namespace+".selectBoardSearch");
}
}
자 이제. Mybatis를 실행하기 위해 여기 까지 왔다.. 쿼리를 실행하면 된다.
본인은 DAO, DAOImp로 나누지 않고 DAO에 선언과 구현을 동시에 하였다. 본인이 편한걸로 하면된다.
여기서 선언과 동시에 return으로 쿼리를 반환하는 것을 볼 수 있다.
본인의 쿼리문은 com.kitao.kr.boardListSearch에 있다는 것을 어느정도 유추할 수 있다.
다음단계 쿼리문 작성하는 곳을 가보자.
ds.COEBoardList_sql.xml
자 낚시 조심하다 위치는 java가 아니라 resources/mappers에 있다. 여기에 생성해 주어야한다. 또한 파일명은 상관없다.
<?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="com.kitao.kr.boardListSearch">
<select id='selectBoardSearch' resultType="com.kitao.kr.co.coe.vo.BoardVO">
SELECT
BOARD_ID,
BOARD_TITLE,
BOARD_CONTENTS,
BOARD_REG_DTM
FROM
BOARD_MST
</select>
</mapper>
자 소스는 다음과 같다. 오.. 뭔가 쿼리문이 보이지 않는가....여........
아~까 session.selectList("com.kitao.kr.boardListSearch"); 이게 여기를 찾아 온다고 생각하면된다..
근데 어떻게 찾아오냐고 물으신다면... 조~기 위에 보이는 mapper로 인해서 여기로 찾아 올 수 있게 된다.
그래서 여기서 실행된 쿼리문에 의해서 Controller에 존재하는 BoardVO에 담기는 것이다!!!! ...
신기하지 않은가?...
근데 하나의문이 있는게 쿼리문에서 실행 된 결과는 다음과 같다.
이런식인데 컬럼명은 BOARD_ID 인데.. BoardVO의 한 변수를 보면 boardId로 선언 되어 있을 것이다..
조금 규칙이 보이지 않는가? 우리는 여기서 '카멜케이스'라는걸 사용해야한다. (모르면 찾아보세요...ㅎ)
이 카멜케이스라는 것을 설정해주면 자바스프링에서는 자동으로 매칭 해 줄 수 있다.
예를들어..
boardVO.boardPw <-> BOARD_PW
boardVO.boardTitle<-> BOARD_TITLE
boardVO.boardContents<-> BOARD_CONTENTS
이런식으로 자동으로 매칭된다는 것이다. 설정은 다음과 같이 할 수 있다.
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="callSettersOnNulls" value="true"/>
<setting name="jdbcTypeForNull" value="NULL"/>
</settings>
</configuration>
전에 mybatis사용하려고 만든 mybatis-config.xml에 설정을 하고 소스는 상단과 같다.
여기서 카멜케이스를 사용하여 자동 매핑하는 세팅은 setting에 mapUnderscoreToCamelCase라는 친구이다.
callSettersOnNulls, jdbcTypeForNull 얘는 몬지모르겠다. 구글링했는데 뭐 같이 딸려온애 같은데.
나중에 배울꺼지만 callSettersOnNulls 얘는 Mybatis ResultMap을 동적으로 받기위해 HashMap으로 받을 시
value 값이 Null 일때 칼럼이 누락되는 현상을 방지하기 위에 존재한다.
jdbcTypeForNull 이 아이는.. Oracle을 사용할 때, Null을 넣을 수 있는 컬럼값에 Null을 넣게 되면 부적합반 열 유형이라는 에러를 뱉게 되는데, 이걸 방지하기 위해 선언한 것이라고 생각하면 된다.
6. 다시 Controller로 돌아와 -> JSP 호출(동적페이지)
자 이제 그럼 다시 Controller단을 봐보자.
package com.kitao.kr.co.coa.controller;
import java.util.List;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.kitao.kr.co.coe.service.BoardService;
import com.kitao.kr.co.coe.vo.BoardVO;
/**
* Handles requests for the application home page.
*/
@Controller
public class HomeController {
@Value("#{properties['ACTION_NAME']}")
public String ACTION_NAME;
@Resource(name="boardService")
private BoardService boardService;
@RequestMapping(value = "/")
public ModelAndView home(ModelMap model, HttpServletResponse response, HttpServletRequest request) throws Exception {
request.setCharacterEncoding("UTF-8");
List <BoardVO> boardVO = boardService.selectBoardList();
ModelAndView mv = new ModelAndView("co/coa/home");
mv.addObject("serverTime", "이거 넣을껀데?");
mv.addObject("boardVO", boardVO);
return mv;
}
}
자 여기를 보면, List <BoardVO> boardVO에 자동으로 매핑되어(카멜케이스 이용) 쿼리문 결과를 넣어준다. 그 다음,
ModelAndview mv = new ModelAndView("co/coa/home"); <- 이건 마지막단계인 jsp가 어디있는지 넣고
jsp로 갈 때 같이 넘기고 싶은 변수를 설정해서(mv.addObject 사용) jsp에 넘겨준다.
그러면 JSP가 실행 되는 것이다. jsp 위치와 소스를 봐보자.
home.jsp
home.jsp는 기본적으로 src/main/webapp/WEB-INF/views에 있다.
여기서 WEB-INF/views 위치 설정은 servlet-context.xml에서 설정할 수 있다. (전에 봤었다..)
소스는 다음과 같다.
<%@ page language="java" contentType="text/html; charset=EUC-KR" pageEncoding="EUC-KR"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
<title>Home</title>
</head>
<body>
<h1>${serverTime}</h1>
<c:forEach var="cw" items="${boardVO}">
<p>번호 : ${cw.boardId}</p>
<p>번호 : ${cw.boardTitle}</p>
<p>번호 : ${cw.boardContents}</p>
<p>번호 : ${cw.boardRegDtm}</p>
<br><br><br>
</c:forEach>
</body>
</html>
이렇게 jsp의 동적페이지 생성으로 끝난다. 그러면 결과를 보자.
자... 감격스럽다... 이렇게 사용자가 이 화면을 보는 것이다. 비전공자는 모를것이다. 페이지가 이렇게 많은 단계를 거치고 걸쳐서 사용자를 위해 제공 되는지.. 원래 아는 만큼 보이는 것이다.
이로써 1~6단계 Controller에서 시작해서 Mybatis-JSP 까지 가는 과정이 끝나게 된다.
사실, 프로세스적으로도 중요하지만, 가장 중요한건 HTML, CSS, JS, JAVA, 쿼리문 작성 능력도 중요하다.
프로세스를 다 알고있는 가정하에, 해당 언어로 누가 더 좋은 로직을 짜느냐, 효율성 있는 로직을 짜느냐, 보안이슈를
다 틀어 막느냐, 유지보수를 가정하고 코드를 짜느냐, 스파게티(?) 코드를 덜짜느냐의 문제로 나아가는 것이다.
도움이 되셨다면 아래 공감 버튼 눌러주세요.(로그인 필요없어요!)
'Java Spring' 카테고리의 다른 글
[Java Spring] Servlet 에 관해서 (0) | 2021.04.08 |
---|---|
[Java Spring] web.xml, root-context.xml (0) | 2021.04.05 |
[Java Spring] pom.xml (0) | 2021.04.04 |
[Java Spring] dispatcher-servlet과 MVC2 Model (0) | 2021.04.04 |
[Java Spring] web.xml과 url-pattern의 의미 등 (0) | 2021.04.04 |