정부기관, 금융권은 Spring Framework를
스타트업 등 최신 기술을 사용하는 업역은 Spring Boot를 많이 사용한다고 하셨다.
어쩌면 나에게 맞는건 Spring Boot가 아닐까,,
그래도 다 열심히 배워둬야겠다!
- Project Name은 그냥 이름일 뿐이다
- Spring MVC Project
(M : Model DB와 관련된 업무를 하는 Java 클래스,
V : View JSP, HTML,
C : Controller Java Action => MVC 패턴)
- topLevelPackage (== base package) 보통 본인 회사의 도메인을 거꾸로 작성
(com.spring.app 인 경우 app 가 context path name이 됨)
pom(Project Object Model).xml은
Maven의 정보를 담고있는 파일로 프로젝트 내 빌드 옵션을 설정
자바 프로젝트를 관리하는 툴로,
미리 작성된 xml 파일을 이용하여
라이브러리를 자동으로 다운로드하거나 프로젝트를 빌드해준다.
즉, 자바 소스를 컴파일하고 패키지해서 deploy까지 자동화 해준다는 것이다.
<dependencies> : 프로젝트가 의존하는 라이브러리들의 정보
이 태그에 라이브러리를 다운 받는다.
파일경로는 C:\Users\USER\.m2\repository
만약 com.spring.app이 top level 이라면
com.spring.app.user.model
com.spring.app.user.controller
com.spring.app.user.service
com.spring.app.user.mapper
...
toplevel 이하의 경로로 java class를 생성해야 한다.
MyBatis는 자바 개발자들이 데이터베이스를 쉽게 다룰 수 있도록 도와주는
오픈 소스 ORM(Object-Relational Mapping) 프레임워크이다.
기존의
controller -> DAO -> ds.conn / pstmt / while(rs.next()){} -> Oracle DB
방법을
-------------- mybatis --------------
middle ware
(미들웨어, 단독으로는 사용하지않고 중간에서 사용함)
대신에 별도로 SQL문만 작성해주면 됨
.java에서
return "cartList";
return "member/memberList";
return "product/new/productList"; 해줄경우
-> /WEB-INF/views/ .jsp
prefix suffix
접두어 접미어를 통해
/WEB-INF/views/cartList.jsp
/WEB-INF/views/member/memberList.jsp
/WEB-INF/views/product/new/productList.jsp 이런식으로 연결된다.
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>
<!-- appServlet 의 위치 -->
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<!-- <url-pattern>/</url-pattern> -->
<!-- URL 패턴 아무거나 다 받아준다 -->
<!--
URL 패턴이 끝나는 글자가
.action 인 것으로 요청을 했을때
appServlet 서블릿이 받아서 처리를 해준다.
-->
<url-pattern>*.action</url-pattern>
</servlet-mapping>
web.xml(배치서술자) -> servlet-mapping, url-pattern (servlet)
스프링(Spring) 컨테이너가 관리하는 자바 객체를 빈(Bean)이라 한다.스프링의 특징에는 제어의 역전(IoC)이 있다. 제어의 역전이란, 간단히 말해서 객체의 생성 및 제어권을 사용자가 아닌 스프링에게 맡기는 것이다. 지금까지는 사용자가 new연산을 통해 객체를 생성하고 메소드를 호출했다. IoC가 적용된 경우에는 이러한 객체의 생성과 사용자의 제어권을 스프링에게 넘긴다. 사용자는 직접 new를 이용해 생성한 객체를 사용하지 않고, 스프링에 의하여 관리당하는 자바 객체를 사용한다. 이 객체를 '빈(bean)'이라 한다.
Mapper 인터페이스는 Mapping 파일에 기재된 SQL을 호출하기 위한 인터페이스이다.
따라서 Mapping 파일에 있는 SQL을 자바 인터페이스를 통해 호출할 수 있도록 해준다.
<?xml version="1.0" encoding="UTF-8"?>
<!-- ==== #29. mapper 기본설정 ==== -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- ==== #29. 루트 엘리먼트 & 네임스페이스 설정(프로젝트 전체내에서 유일해야 한다.) ==== -->
<mapper namespace="board">
<insert id="test_insert">
insert into spring_test(no, name, writeday)
values(101, '이순신', default)
</insert>
</mapper>
사용자 웹브라우저 요청(View) ==> DispatcherServlet ==> @Controller 클래스 <==>> Service단(핵심업무로직단, business logic단) <==>> Model단[Repository](DAO, DTO) <==>> myBatis <==>> DB(오라클)
(http://... *.action) |
↑ View Resolver
| ↓
| View단(.jsp 또는 Bean명)
-------------------------------------------------------|
사용자(클라이언트)가 웹브라우저에서 http://localhost:9090/board/test/test_insert.action 을 실행하면
배치서술자인 web.xml 에 기술된 대로 org.springframework.web.servlet.DispatcherServlet 이 작동된다.
DispatcherServlet 은 bean 으로 등록된 객체중 controller 빈을 찾아서 URL값이 "/test_insert.action" 으로
매핑된 메소드를 실행시키게 된다.
Service(서비스)단 객체를 업무 로직단(비지니스 로직단)이라고 부른다.
Service(서비스)단 객체가 하는 일은 Model단에서 작성된 데이터베이스 관련 여러 메소드들 중 관련있는것들만을 모아 모아서
하나의 트랜잭션 처리 작업이 이루어지도록 만들어주는 객체이다.
여기서 업무라는 것은 데이터베이스와 관련된 처리 업무를 말하는 것으로 Model 단에서 작성된 메소드를 말하는 것이다.
이 서비스 객체는 @Controller 단에서 넘겨받은 어떤 값을 가지고 Model 단에서 작성된 여러 메소드를 호출하여 실행되어지도록 해주는 것이다.
실행되어진 결과값을 @Controller 단으로 넘겨준다.
XML에서 빈을 만드는 대신에 클래스명 앞에 @Component 어노테이션을 적어주면
해당 클래스는 bean으로 자동 등록된다.
그리고 bean의 이름(첫글자는 소문자)은 해당 클래스명이 된다.
즉, 여기서 bean의 이름은 boardController 이 된다.
여기서는 @Controller 를 사용하므로 @Component 기능이 이미 있으므로
@Component를 명기하지 않아도 BoardController 는 bean 으로 등록되어
스프링컨테이너가 자동적으로 관리해준다.
@Controller
public class BoardController {
// BoardController 클래스가 빈으로 올라가면 첫글자가
// 소문자인 boardController가 이름이 되어진다.
@Autowired // Type에 따라 알아서 Bean 을 주입해준다.
private BoardService bservice;
// service 단을 가져오는 필드
@RequestMapping(value="/test/test_insert.action")
public String test_insert(HttpServletRequest request) {
// http://localhost:9090/board/test/test_insert.action
int n = bservice.test_insert();
String message = "";
if(n==1) {
message = "데이터 입력 성공!!";
}
else {
message = "데이터 입력 실패!!";
}
request.setAttribute("message", message);
request.setAttribute("n", n);
return "test/test_insert";
// /WEB-INF/views/test/test_insert.jsp 페이지를 만들어야 한다.
}
// 트랜잭션 처리를 담당하는 곳, 업무를 처리하는 곳, 비지니스(Business)단
// @Component
@Service
public class BoardService_imple implements BoardService {
@Autowired // Type에 따라 알아서 Bean 을 주입해준다.
private BoardDAO bdao;
// Type 에 따라 Spring 컨테이너가 알아서 bean 으로 등록된
// com.spring.board.model.BoardDAO_imple() 의 bean을 bdao에 주입시켜준다.
// 그러므로 bdao 는 null 이 아니다.
@Override
public int test_insert() {
int n = bdao.test_insert();
return n;
}
}
=== 의존객체 주입하기(DI: Dependency Injection) ===
>>> 의존 객체 자동 주입(Automatic Dependency Injection)은
스프링 컨테이너가 자동적으로 의존 대상 객체를 찾아서 해당 객체에 필요한 의존객체를 주입하는 것을 말한다.
단, 의존객체는 스프링 컨테이너속에 bean 으로 등록되어 있어야 한다.
의존 객체 자동 주입(Automatic Dependency Injection)방법 3가지
1. @Autowired ==> Spring Framework에서 지원하는 어노테이션이다.
스프링 컨테이너에 담겨진 의존객체를 주입할때 타입을 찾아서 연결(의존객체주입)한다.
2. @Resource ==> Java 에서 지원하는 어노테이션이다.
스프링 컨테이너에 담겨진 의존객체를 주입할때 필드명(이름)을 찾아서 연결(의존객체주입)한다.
3. @Inject ==> Java 에서 지원하는 어노테이션이다.
스프링 컨테이너에 담겨진 의존객체를 주입할때 타입을 찾아서 연결(의존객체주입)한다.
DAO_imple() ( => class)
@Repository
public class BoardDAO_imple implements BoardDAO { // 빈 이름 boardDAO_imple
@Autowired // Type에 따라 알아서 Bean 을 주입해준다.
private SqlSessionTemplate abc;
// Type 에 따라 Spring 컨테이너가 알아서 root-context.xml 에 생성된 org.mybatis.spring.SqlSessionTemplate 의 bean 을 abc 에 주입시켜준다.
// 그러므로 abc 는 null 이 아니다.
// spring_test 테이블에 insert 하기
@Override
public int test_insert() {
int n = abc.insert("board.test_insert");
// .Mapper 클래스에 있는 board.xml 에서
// id가 test_insert 인 태그를 실행하여 데이터베이스 호출
return n;
}
}
<insert id="test_insert">
insert into spring_test(no, name, writeday)
values(101, '이순신', default)
</insert>

Controller 처리 결과 후 응답할 view와 view에 전달할 값을 저장
setViewName(String view) : 응답할 view 이름을 설정
addObject(String name, Object value) : view에 전달할 값을 설정
...
// 구글링을 해보니 꽤나 예전 방식이라고 한다.
view의 jsp들을 상단, 사이드, 메인, 하단을 설정 상태로 include 처리해주는 구조
WEB-INF/tiles/tiles-layout.xml
<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 2.1//EN"
"http://tiles.apache.org/dtds/tiles-config_2_1.dtd">
tiles 레이아웃을 설정한다.
<tiles-definitions>
<!-- ==== tiles 를 사용하는 레이아웃(header, content, footer) 설정 시작 ==== -->
<definition name="layout-tiles1" template="/WEB-INF/tiles/layout/layout-tiles1.jsp">
<put-attribute name="header" value="/WEB-INF/tiles/tile1/header.jsp" />
<put-attribute name="content" value="" />
<put-attribute name="footer" value="/WEB-INF/tiles/tile1/footer.jsp" />
</definition>
<definition name="*/*/*.tiles1" extends="layout-tiles1">
<put-attribute name="content" value="/WEB-INF/views/tiles1/{1}/{2}/{3}.jsp"/>
</definition>
<definition name="*/*.tiles1" extends="layout-tiles1">
<put-attribute name="content" value="/WEB-INF/views/tiles1/{1}/{2}.jsp"/>
</definition>
<definition name="*.tiles1" extends="layout-tiles1">
<put-attribute name="content" value="/WEB-INF/views/tiles1/{1}.jsp"/>
</definition>
</tiles-definitions>
controller 에서 return값 설정
return "member/special/memberList.tiles1"; // tiles1은 layout-tiles1을 상속받는데 // /WEB-INF/views/tiles1/{1}/{2}/{3}.jsp 의 value를 갖는다.폴더1/폴더2/.../파일명으로 매핑
" */*/*.tiles1"; {1}/{2}/{3}xml에서 value값으로 설정 가능
value = "/WEB-INF/view/tiles1/{1}/{2}/{3}.jsp" value = "/WEB-INF/view/tiles1/member/special/memberList.jsp"
DAO 에서 mapper로 넘길때 parameter의 VO의 field명과
table의 컬럼명과 같다면 auto 매핑 된다.
VO의 field
public class BoardVO {
// 필드
private String seq; // 글번호
private String fk_userid; // 사용자ID
private String name; // 글쓴이
private String subject; // 글제목
private String content; // 글내용
private String pw; // 글암호
private String readCount; // 글조회수
private String regDate; // 글쓴시간
private String status; // 글삭제여부 1:사용가능한 글, 0:삭제된글
private String previousseq; // 이전글번호
private String previoussubject; // 이전글제목
private String nextseq; // 다음글번호
private String nextsubject; // 다음글제목
}
mapper의 sql에서 parameter타입으로 받아주면 VO에 오토매핑된다.
<mapper namespace="board">
<insert id="add" parameterType="com.spring.app.board.domain.BoardVO">
insert into tbl_board(seq, fk_userid, name, subject, content, pw, readCount, regDate, status)
values(boardSeq.nextval, #{fk_userid}, #{name}, #{subject}, #{content}, #{pw}, default, default, default)
</insert>
</mapper>
전혀 다른 클래스의 부모자식이 아니지만 공통의 Aspect(관심사)를 가질때
=> 관심지향 프로그래밍
주업무(<예: 글쓰기, 글수정, 댓글쓰기, 직원목록조회 등등>)를 실행하기 앞서서
이러한 주업무들은 먼저 로그인을 해야만 사용가능한 작업이므로
주업무에 대한 보조업무<예: 로그인 유무검사> 객체로 로그인 여부를 체크하는
관심 클래스(Aspect 클래스)를 생성하여 포인트컷(주업무)과
어드바이스(보조업무)를 생성하여동작하도록 만들겠다.
어노테이션
@Aspect // 공통관심사 클래스(Aspect)로 등록된다.
@Component // bean으로 등록된다.
Pointcut(주업무)을 설정
// Pointcut 이란 공통관심사<예: 로그인 유무검사>를 필요로 하는 메소드를 말한다.
@Pointcut("execution(public * com.spring.app..*Controller.requiredLogin_*(..))")
@Pointcut("execution(접근제한자 / 리턴타입 / 베이스패키지 안에 있는 Controller 로 끝나는 서브패키지 / requiredLogin_로 시작하는 모든 메소드(파라미터가 있든 없든)")
안쓰면 public
Before Advice(공통관심사, 보조업무)를 구현
@Before("requiredLogin()")
public void loginCheck(JoinPoint joinpoint) { // 로그인 유무 검사를 하는 메소드 작성하기
// JoinPoint joinpoint 는 포인트컷 되어진 주업무의 메소드이다.
// 로그인 유무를 확인하기 위해서는 request를 통해 session 을 얻어와야 한다.
HttpServletRequest request = (HttpServletRequest)joinpoint.getArgs()[0]; // 주업무(메소드)에 있던 파라미터들 중(배열) 첫번째 마라미터를 가져온다.
HttpServletResponse response = (HttpServletResponse)joinpoint.getArgs()[1]; // 주업무(메소드)에 있던 파라미터들 중(배열) 두번째 마라미터를 가져온다.
HttpSession session = request.getSession();
if(session.getAttribute("loginuser") == null) {
String message = "먼저 로그인 하세요!";
String loc = request.getContextPath() + "/login.action";
request.setAttribute("message", message);
request.setAttribute("loc", loc);
// >>> 로그인 성공 후 로그인 하기 전 페이지로 돌아가는 작업 만들기 <<< //
String url = MyUtil.getCurrentURL(request);
session.setAttribute("goBackURL", url); // 세션에 url 정보를 저장시켜둔다.
RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/views/msg.jsp");
try {
dispatcher.forward(request, response);
} catch (ServletException | IOException e) {
e.printStackTrace();
}
}
}