자동으로 스프링 라이브러리 기능 관리
프로젝트 구조와 내용을 기술하는 선언적 접근 방식의 오픈 소스 빌드 툴
프로젝트 종속 라이브러리, dependency 자원 등 프로젝트 전반 리소스 관리
설정 파일, 표준 디렉토리 구조를 일관된 형태로 구성하여 관리
groupId: 프로젝트 그룹 id (도메인 이름)
artifactId: 프로젝트 아티팩트 id (패지키 이름)
version: 프로젝트 버전 설정
packging: 애플리케이션 배포 시 패키징 타입 설정 (war)
dependency: 의존 라이브러리 정보
qroupId: 의존 프로젝트 그룹 id
artifactId: 의존 프로젝트 아티팩트 id
version: 의존 프로젝트 버전 정보
pom.xml
프로젝트 정보 표시, 스프링 라이브러리 설정 및 다운로드
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/200
1/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.spring</groupId>
<artifactId>mytest2</artifactId>
<name>spring_test2</name>
<packaging>war</packaging>
<version>1.0.0-BUILD-SNAPSHOT</version>
<properties>
<java-version>1.6</java-version>
<org.springframework-version>4.0.0.RELEASE</org.springframework-version>
<org.aspectj-version>1.6.10</org.aspectj-version>
<org.slf4j-version>1.6.6</org.slf4j-version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
</dependencies>
</project>
Spring Tool Suite
이클립스 스프링 기반 애플리케이션 개발용 도구
편리한 개발 환경
STS 프로젝트 생성 후 Maven 적용해서 사용 가능
pro27/src/main/webapp/WEB-INF/web.xml
...
<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 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>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
pro27/src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml
...
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<!-- jsp에서 사용할 js, img 파일 경로 지정 -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<!-- InternalResourceViewResolver bean 생성 및 jsp 경로 지정-->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<!-- 패키지, 애너테이션 지정 -->
<context:component-scan base-package="com.spring.pro27" />
/beans:beans>
pro27/src/main/java/com/myspring/pro27/HomeController.java
package com.myspring.pro27;
...
/* Handles requests for the application home page. */
@Controller
public class HomeController {
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
/* Simply selects the home view to render by returning its name. */
@RequestMapping(value = "/", method = RequestMethod.GET) //모든 요청 시
public String home(Locale locale, Model model) {
logger.info("Welcome home! The client locale is {}.", locale);
Date date = new Date();
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG,
DateFormat.LONG, locale);
String formattedDate = dateFormat.format(date);
model.addAttribute("serverTime", formattedDate ); //브라우저 요청 시각
return "home"; //jsp 이름 반환
}
}
pro27/src/main/webapp/WEB-INF/views/home.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
<title>Home</title>
</head>
<body>
<h1> Hello world! </h1>
<P> The time on the server is ${serverTime}. </P>
</body>
</html>
pro27/pom.xml
...
<!-- datasource 관련 라이브러리 -->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.2</version>
</dependency>
<!-- MyBatis 관련 라이브러리 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.1.0</version>
</dependency>
<!-- oracle driver 라이브러리 -->
<dependency>
<groupId>jdbc.oracle</groupId>
<artifactId>OracleDriver</artifactId>
<version>12.1.0.2.0</version>
<scope>system</scope>
<systemPath>${basedir}/src/main/webapp/WEB-INF/lib/ojdbc6.jar</systemPath>
</dependency>
</dependencies>
...
pro27/src/main/webapp/WEB-INF/web.xml
...
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/action-mybatis.xml</param-value>
</context-param>
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
...
pro27/src/main/webapp/WEB-INF/spring/action-mybatis.xml
...
<bean id="propertyPlaceholderConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<value>/WEB-INF/config/jdbc/jdbc.properties</value>
</property>
</bean>
...
</beans>
pro27/src/main/resources/mybatis/model/modelConfig.xml
..
<configuration>
<typeAliases>
<typeAlias type="com.myspring.pro27.member.vo.MemberVO" alias="memberVO" />
</typeAliases>
</configuration>
MemberVO alias 설정
pro27/src/main/java/com/myspring/pro27/member/controller/MemberControllerImpl.java
@Controller
package com.myspring.pro27.member.controller;
...
@Controller("memberController")
public class MemberControllerImpl implements MemberController {
@Autowired
private MemberService memberService;
@Autowired
private MemberVO memberVO;
@Override
@RequestMapping(value="/member/listMembers.do" ,method = RequestMethod.GET)
public ModelAndView listMembers(HttpServletRequest request, HttpServletResponse response) throws Exception {
String viewName = getViewName(request);
List membersList = memberService.listMembers();
ModelAndView mav = new ModelAndView(viewName);
mav.addObject("membersList", membersList);
return mav;
}
...
private String getViewName(HttpServletRequest request) throws Exception {
...
if(viewName.lastIndexOf("/") != -1) {
viewName = viewName.substring(viewName.lastIndexOf("/",1), viewName.length());
}
return viewName;
}
}
콘솔 메세지 출력 대신 코드 정상 작동을 확인하기 위해 사용하는 로그
Appender: 로그 출력 위치
Layout: 출력 형식
Logger: 로그 레벨, 로그 출력 여부
ConsoleAppender: 로그 메세지 위치를 콘솔에 지정
FileAppender: 로그 메세지 위치를 파일에 지정
RollingFileAppender: 파일 크기가 일정 기준을 넘을 시 백업 후 다시 기록
DailyRollingAppender: 설정 기간 단위마다 새 파일 생성하여 기록
%p 로그 레벨 이름
%n 로그 메세지
%d 로깅 이벤트 발생 시각
%F 로깅 발생 프로그램 파일 이름
%l 로깅 발생 caller 정보
%L 로깅 발생 caller 라인 수
%M 로깅 발생 method 이름
%c 전체 패키지명 또는 파일명 + 로깅 메세지
최고 수준(심각)의 레벨부터 설정한 레벨까지 출력
DEBUG 이하로 설정 시 SQL 로그 출력 가능
FATAL: 심각, 시스템 문제, 애플리케이션 작동 불가
ERROR: 실행 중 문제 발생
WARN: 경고 메세지, 향후 시스템 오류 발생 가능
INFO: 정보 메세지, 애플리케이션 운영 정보 (로그인, 상태 변경 등)
DEBUG: 디버깅 용도
TRACE: 상세 로깅 정보
pro27/src/main/resources/log4j.xml
...
<!-- Console Appenders -->
<!-- 로그 위치: 콘솔 -->
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out" />
<!-- 로그 출력 형식 -->
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p: %c - %m%n" />
</layout>
</appender>
<!-- DailyRollingFile Appenders -->
<!-- 로그 위치: 파일 / 설정 기간마다 새 파일 생성 -->
<appender name="dailyFileAppender" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="C:\\spring\\logs\\output.log" /> <!-- 파일 생성 위치 -->
<param name="Append" value="true" />
<!-- 로그 출력 형식 -->
<layout class="org.apache.log4j.PatternLayout">
<param name="DatePattern" value="'.'yyyy-MM-dd"/>
<param name="ConversionPattern" value="[%d{HH:mm:ss}][%-5p](%F:%L)-%m%n"/>
</layout>
</appender>
...
<!-- Application Loggers -->
<!-- name 위치 로그 레벨 value 설정 -->
<logger name="com.myspring.pro27">
<level value="info" />
</logger>
...
<!-- Root Logger -->
<!-- 애플리케이션 전체 로그 설정 -->
<root>
<priority value="info" /> <!-- 로그 레벨 info 설정 -->
<appender-ref ref="console" /> <!-- 콘솔 출력 -->
<appender-ref ref="dailyFileAppender" /> <!-- 파일 출력 -->
</root>
pro27/src/main/java/com/myspring/pro27/member/controller/MemberControllerImpl.java
...
@Controller("memberController")
@RequestMapping("/member")
public class MemberControllerImpl implements MemberController {
//Logger 객체
private static final Logger logger = LoggerFactory.getLogger(MemberControllerImpl.class);
...
@Override
@RequestMapping(value="/listMembers.do" ,method = RequestMethod.GET)
public ModelAndView listMembers(HttpServletRequest request, HttpServletResponse response) throws Exception {
/* 요청받은 뷰 이름 확인 */
String viewName = getViewName(request);
logger.info("info 레벨 : viewName = "+ viewName); //info 레벨 로그 메소드
logger.debug("debug 레벨 : viewName = "+ viewName); //debug 레벨 로그 메소드
List membersList = memberService.listMembers();
ModelAndView mav = new ModelAndView(viewName);
mav.addObject("membersList", membersList);
return mav;
}
...
JSP 페이지 레이아웃을 위한 프레임워크
쉽고 단순하게 페이지 레이아웃 구현 가능
공통 레이아웃 사용을 통한 쉬운 유지관리
pro27/pom.xml
...
<!-- 타일즈 관련 라이브러리 -->
<dependency>
<groupId>org.apache.tiles</groupId>
<artifactId>tiles-core</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.tiles</groupId>
<artifactId>tiles-jsp</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.tiles</groupId>
<artifactId>tiles-servlet</artifactId>
<version>2.2.2</version>
</dependency>
...
pro27/src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml
...
<!-- InternalResourceViewResolver 사용안함 -->
<!-- TilesConfigurer bean 생성 -->
<beans:bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
<beans:property name="definitions">
<beans:list>
<beans:value>classpath:tiles/*.xml</beans:value> <!-- tiles 패지키 xml 파일 -->
</beans:list>
</beans:property>
<beans:property name="preparerFactoryClass" value="org.springframework.web.servlet.view.tiles2.SpringBeanPreparerFactory"/>
</beans:bean>
<!-- TilesView UrlBasedViewResolver 화면 표시 -->
<beans:bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<beans:property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView"/>
</beans:bean>
<context:component-scan base-package="com.myspring.pro27" />
</beans:beans>
pro27/src/main/resources/tiles/tiles_member.xml
layout jsp 설정
...
<tiles-definitions>
<!-- 전체(공통) 레이아웃 -->
<definition name="baseLayout" template="/WEB-INF/views/common/layout.jsp">
<put-attribute name="title" value="" />
<put-attribute name="header" value="/WEB-INF/views/common/header.jsp" />
<put-attribute name="side" value="/WEB-INF/views/common/side.jsp" />
<put-attribute name="body" value="" />
<put-attribute name="footer" value="/WEB-INF/views/common/footer.jsp" />
</definition>
<!-- 메인 화면: 레이아웃 상속 받음 -->
<definition name="main" extends=" baseLayout">
<put-attribute name="title" value="메인 페이지" />
<put-attribute name="body" value="/WEB-INF/views/main.jsp" />
</definition>
</tiles-definitions>
pro27/src/main/webapp/WEB-INF/views/common/layout.jsp
레이아웃 형태
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false" %>
<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %> <!-- Tiles 사용 시 필요 -->
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
...
<title> <tiles:insertAttribute name="title" /> </title>
</head>
<body>
<div id="container">
<div id="header"> <tiles:insertAttribute name="header"/> </div>
<div id="sidebar-left"> <tiles:insertAttribute name="side"/> </div>
<div id="content"> <tiles:insertAttribute name="body"/> </div>
<div id="footer"> <tiles:insertAttribute name="footer"/> </div>
</div>
</body>
</html>
pro27/src/main/webapp/WEB-INF/views/common/header.jsp
pro27/src/main/webapp/WEB-INF/views/common/side.jsp
pro27/src/main/webapp/WEB-INF/views/common/footer.jsp
각 레이아웃 부분
pro27/src/main/webapp/WEB-INF/views/common/main.jsp
메인 내용
pro27/src/java/com/myspring/pro27/HomeController.java
뷰 요청 컨트롤러
...
@Controller
public class HomeController {
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
/* 메인화면 요청 */
@RequestMapping(value = "/main.do", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
return "main"; //Tiles xml 파일에서 설정한 jsp
}
}
pro27/src/main/resources/tiles/tiles_member.xml
...
<tiles-definitions>
<!-- 전체(공통) 레이아웃 -->
<definition name="baseLayout" template="/WEB-INF/views/common/layout.jsp">
<put-attribute name="title" value="" />
<put-attribute name="header" value="/WEB-INF/views/common/header.jsp" />
<put-attribute name="side" value="/WEB-INF/views/common/side.jsp" />
<put-attribute name="body" value="" />
<put-attribute name="footer" value="/WEB-INF/views/common/footer.jsp" />
</definition>
<!-- 메인 화면: 레이아웃 상속 받음 -->
<definition name="main" extends=" baseLayout">
<put-attribute name="title" value="메인 페이지" />
<put-attribute name="body" value="/WEB-INF/views/main.jsp" />
</definition>
<!-- 회원 목록창: 레이아웃 상속 받음 -->
<definition name="/member/listMembers" extends="baseLayout">
<put-attribute name="title" value="회원목록창" />
<put-attribute name="body" value="/WEB-INF/views/member/listMembers.jsp" />
</definition>
<!-- 로그인 창: 레이아웃 상속 받음 -->
<definition name="/member/loginForm" extends="baseLayout">
<put-attribute name="title" value="로그인창" />
<put-attribute name="body" value="/WEB-INF/views/member/loginForm.jsp" />
</definition>
</tiles-definitions>
pro27/src/main/resources/mybatis/mappers/member.xml
...
<!-- 로그인 id, pw 조회 -->
<select id="loginById" resultType="memberVO" parameterType="memberVO" >
<![CDATA[
select * from t_member
where id = #{id} and pwd = #{pwd}
]]>
</select>
...
pro27/src/main/java/com/myspring/pro27/member/controller/MemberControllerImpl.java
...
@Controller("memberController")
public class MemberControllerImpl implements MemberController {
private static final Logger logger = LoggerFactory.getLogger(MemberControllerImpl.class);
@Autowired
private MemberService memberService; //자동 주입
@Autowired
private MemberVO memberVO; //자동 주입
/* 회원 목록 요청 */
@Override
@RequestMapping(value="/member/listMembers.do", method = RequestMethod.GET)
public ModelAndView listMembers(HttpServletRequest request, HttpServletResponse response) throws Exception {
String viewName = getViewName(request);
logger.debug("debug레벨 : viewName = "+ viewName);
List membersList = memberService.listMembers();
ModelAndView mav = new ModelAndView(viewName); //viewName == listMembers
mav.addObject("membersList", membersList);
return mav;
}
/* 로그인 요청 */
@Override
@RequestMapping(value = "/member/login.do", method = RequestMethod.POST)
public ModelAndView login( @ModelAttribute("member") MemberVO member, RedirectAttributes rAttr, HttpServletRequest request, HttpServletResponse response ) throws Exception {
ModelAndView mav = new ModelAndView();
memberVO = memberService.login(member); //회원 조회 결과
if(memberVO != null) { //회원 존재
HttpSession session = request.getSession();
session.setAttribute("member", memberVO);
session.setAttribute("isLogOn", true);
mav.setViewName("redirect:/member/listMembers.do"); // -> 회원 목록 요청
}
else { //해당 회원 없음 (로그인 실패)
rAttr.addAttribute("result","loginFailed");
mav.setViewName("redirect:/member/loginForm.do");
}
return mav;
}
/* 로그아웃 요청 */
@Override
@RequestMapping(value = "/member/logout.do", method = RequestMethod.GET)
public ModelAndView logout(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpSession session = request.getSession();
session.removeAttribute("member");
session.removeAttribute("isLogOn");
ModelAndView mav = new ModelAndView();
mav.setViewName("redirect:/member/listMembers.do");
return mav;
}
/* 로그인 창 요청 */
@RequestMapping(value = "/member/*Form.do", method = RequestMethod.GET)
private ModelAndView form( @RequestParam(value= "result", required=false) String result, HttpServletRequest request, HttpServletResponse response ) throws Exception {
String viewName = getViewName(request);
ModelAndView mav = new ModelAndView();
mav.addObject("result",result);
mav.setViewName(viewName);
return mav;
}
...
pro27/src/main/java/com/myspring/pro27/member/service/MemberServiceImpl.java
controller -> service -> DAO
...
@Override
public MemberVO login(MemberVO memberVO) throws Exception{
return memberDAO.loginById(memberVO);
}
...
pro27/src/main/java/com/myspring/pro27/memberdao/MemberDAOImpl.java
service -> DAO -> VO
...
public MemberVO loginById(MemberVO memberVO) throws DataAccessException {
MemberVO vo = sqlSession.selectOne("mapper.member.loginById", memberVO);
return vo;
}
...
pro27/src/main/webapp/WEB-INF/views/common/header.jsp
...
<td>
<!-- <a href="#"><h3>로그인</h3></a> -->
<c:choose>
<!-- 로그인 상태 -->
<c:when test="${isLogOn == true && member!= null}">
<h3>환영합니다. ${member.name }님!</h3>
<a href="${contextPath}/member/logout.do"><h3>로그아웃</h3></a>
</c:when>
<!-- 비로그인 상태 -->
<c:otherwise>
<a href="${contextPath}/member/loginForm.do"><h3>로그인</h3></a>
</c:otherwise>
</c:choose>
</td>
</tr>
</table>
...
pro27/src/main/webapp/WEB-INF/views/member/loginForm.jsp
...
<c:set var="result" value="${param.result}" />
...
<head>
<meta charset="UTF-8">
<title>로그인창</title>
<!-- 로그인 실패 후 리로드 시 실패 메세지 표시 -->
<c:choose>
<c:when test="${result=='loginFailed' }">
<script>
window.onload=function(){
alert("아이디나 비밀번호가 틀립니다. 다시 로그인 하세요!");
}
</script>
</c:when>
</c:choose>
</head>
...
*자바 웹을 다루는 기술