EJB에 이어 2007년까지 Sprinf Framework을 포함하여 Server Framework, UI Framework의 춘추전국시대를 지나다가 Spring Framework이 Java Web Application 개발에 대표 Framework으로 자리를 잡음
- 본 강의의 전체 소스 공유 링크 : 진행중 오류가 있을 경우 소스 참조 바람
https://drive.google.com/file/d/1qX7HOMBDxcyb-8aontzdUL6PkQHD42L8/view?usp=share_link
Enterprise Application 개발하기 위한 기능(J2EE중 EJB제외)을 종합적으로 제공하는 Light weight Container
Servlet Container를 지원하는 WAS(Undertow, Tomcat, Jetty) 만으로도 운영 가능
일반적인 프로그램 구조
객체 생성 -> 의존성 객체 생성 -> 의존성 객채 내의 메소드 호출
각 객체들이 프로그램의 흐름을 결정하고 각 객체를 구성하는 작업에 직접적으로 참여
즉, 모든 작업을 의존성 객체를 생성한 객체가 제어하는 구조
IOC 구조
모든 권한을 다른 대상에 위임함으로써 제어 권한을 위임받은 특별한 객체에 의해 결정되고 만들어짐. 제어의 흐름을 사용자가 컨트롤 하지 않고 권한을 위임한 특별한 객체에 모든 것을 맡기는 구조. IOC는 Dependency Lookup과 Dependency Injection을 이용하여 구현함
OOP (객체 지향 프로그래밍)
객체지향 원칙에 따라 관심사가 같은 데이터를 한곳에 모아 분리, 낮은 결합도를 갖게 해 독립적이고 유연한 모듈로 캡슐화를 함
큰 프로그램을 모듈단위로 축소시켜 작성할 수 있게 되었지만 발생되는 중복을 피할수 없음
AOP : 관점 지향 프로그래밍
Transaction, Logging, Performance Analysys 등 횡단 관심사(Crosscutting-Concerns)를 모듈화 하는 기법
핵심기능과 공통기능을 분리시켜 핵심 로직에 영향을 끼치지 않게 공통기능을 끼워 넣는 개발 형태
무분별하게 중복되는 코드를 한 곳에 모아 공통기능을 한 곳에 보관함으로써 공통 기능 하나의 수정으로 모든 핵심기능들의 공통기능을 수정 할 수 있어 효율적인 유지보수 가능 및 재활용성이 극대화
웹 프로그램밍 개발 시 "Spring MVC"를 이용하여 Model-View-Controller 구조로 사용자 인터페이스와 비지니스 로직을 분리하여 개발
일반적인 java 코드를 이용해서 프로그래밍이 가능
EJB에선 필수 인터페이스나 클래스를 상속받아 무거운 객체들을 만들어야만 했음
But Spring은 일반적인 java코드로만으로도 객체를 구성 가능하여 프로그래밍이 가볍고 생산성이 높아짐
이미지 : https://docs.spring.io/spring-framework/docs/2.5.5/reference/introduction.html 참조
Spring Core
Core는 Spring Container로 Spring Framework의 핵심이며 그중 핵심은 Bean Factory Container임
Bean Factory는 IoC Pattern을 적용하여 객체 생성부터 의존성 처리까지 모든 일을 담당하는 역할
Spring AOP
Spring Framework에서 관점지향 프로그래밍 지원
Spring DAO (Data Access Object)
Database Data에 접근하는 객체로 Spring JDBC DAO는 추상 레이어 지원
Spring ORM (Object Relational Mapping)
Hibernate, JPA, iBatis 등 객체 관계형 도구를 사용할 수 있도록 지원
Spring JEE
JMX(Java Management Extensions), JMS(Java Message Service), JCA(Java Cryptography Architecture), EJB, Email 등 엔터프라이즈 서비스를 지원
Spring Web
Web 기반 응용 프로그램에 대한 Context를 제공, 일반적인 Web Application 개발에 필요한 기본적인 기능을 지원
Model2 구조로 Apllication을 만들 수 있도록 지원
웹 응용 프로그램을 작성하기 위한 완전한 기능을 갖춘 MVC를 구현
JSP, Struts, WebWork, JSF, Velocity, FreeMarker, PDF, POI(Excel)를 포함한 수많은 뷰 기술을 지원
2) <상황발생시> Build Error 문제 해결 및 JDK8 변경 - 실습
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.sample</groupId>
<artifactId>springframeworkweb</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>springframeworkweb Maven Webapp</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<springframework.version>5.0.6.RELEASE</springframework.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<!-- mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
<!-- DB Pool Library -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.1.1</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<!-- jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${springframework.version}</version>
</dependency>
<!-- jstl -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
</dependencies>
<build>
<finalName>springframeworkweb</finalName>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<compilerArgument>-Xlint:all</compilerArgument>
<showWarnings>true</showWarnings>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
1) project 이름 위에서 "우측버튼 > Maven > Update Project"를 선택
2) "Update Maven Project" 창이 뜨고 springframeworkweb 프로젝트가 선택된 것을 확인후 "OK" 버튼 클릭
2) src > main > webapp > WEB-INF > web.xml 파일의 버전 변경
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name>Spring Framework Sample Web</display-name>
<!-- Servlet을 Springframework의 DispatcherServlet으로 설정 -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- .do 로 호출될 경우 상기 Spring DispatcherServlet이 동작하도록 Mapping -->
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<!-- Spring Container 생성 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Spring Context 설정파일 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/root-context.xml</param-value>
</context-param>
<welcome-file-list>
<welcome-file>/WEB-INF/jsp/index.jsp</welcome-file>
</welcome-file-list>
</web-app>
## DB Connection 정보
db.driverClassName=com.mysql.cj.jdbc.Driver
db.url=jdbc:mysql://127.0.0.1:3306/TEST_DB?useUniCode=yes&characterEncoding=UTF-8
db.username=test_user
db.password=testpw
## Application에서 사용할 정보
hello.defaultRandomDisit=10
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<mvc:annotation-driven/>
<context:component-scan base-package="com.demo"/>
<!-- properties 설정 방법 -->
<context:property-placeholder location="classpath:properties/app.properties"/>
<!-- HTTP Response를 표시할 Resolver 설정 : String으로 jsp파일의 경로포함 파일명을 Return 시 .jsp 파일 호출-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- property name="viewClass" value="org.springframework.web.servlet.view.JstlView" / -->
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
<property name="order" value="1" />
<property name="contentType" value="text/html; charset=UTF-8"/>
</bean>
<!-- Interceptor 설정 : 특정 URI 접근시 Controller가 호출되기 전에 먼저 실행됨 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- Session 권한이 있어야 볼수 있는 페이지에 대해 URI 설정 부분 -->
<mvc:mapping path="/auth/checkSession*"/>
<mvc:mapping path="/soccer/**"/>
<bean id="loginInterceptor" class="com.demo.common.interceptor.LoginInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- com.demo Package 아래의 Component(Controller, Service, Repository, Component)를 Scan -->
<context:component-scan base-package="com.demo"/>
<!-- app.properties 파일을 Loading -->
<context:property-placeholder location="classpath:properties/app.properties"/>
<!-- DB Connection 정보 및 Connection Pool 설정 -->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
<property name="driverClassName" value="${db.driverClassName}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</bean>
<!-- SQL Session Factory Bean 설정 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:/mybatis/mybatis-config.xml" />
<property name="mapperLocations" value="classpath:/sqlmap/**/*Mapper.xml" />
</bean>
<!-- SQL Session 설정 -->
<bean id="sqlSession" name="sqlSession" class="org.mybatis.spring.SqlSessionTemplate" destroy-method="clearCache">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
</beans>
<?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="cacheEnabled" value="false" />
<setting name="defaultExecutorType" value="REUSE" />
<setting name="mapUnderscoreToCamelCase" value="true" />
</settings>
</configuration>
1) 먼저 jsp 디렉토리 설정이 정상적으로 되는지 확인을 위해 index.jsp 개발
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<html>
<body>
<h2>Hello World!!!!</h2>
<h2>This is index.jsp for springframeworkweb project!!!</h2>
<%
for(int i = 0 ; i < 5 ; i++){
out.println("JSP Sample i = ["+ i +"]<br/>");
}
%>
<br/>
<a href="/hello.do?digit=8">HTTP GET 방식 ::: Hello 페이지로 이동</a>
<br/><br/>
<a href="/loginPage.do">로그인 페이지로 이동</a>
</body>
</html>
1) "src/main/java" 디렉토리 아래에 "com.demo" Package 생성
2) "src/main/java/com.demo" 아래에 "HelloController.java" 생성
package com.demo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class HelloController {
@Value("${hello.defaultRandomDisit}")
private int defaultRandomDisit;
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String helloGetMethod(Model model, @RequestParam(value = "digit", required = false) Integer digit) {
System.out.println("HelloController.helloGetMethod() invoked.");
// JSP 화면에 서버의 처리값 전달 예시
int random = getRandamByDisit(digit);
model.addAttribute("randomNumber", random);
System.out.println("HelloController.helloGetMethod() ::: Random Number = [" + random + "]");
// WEB-INF/jsp/helloJSP.jsp 호출
return "helloJSP";
}
@RequestMapping(value = "/hello", method = RequestMethod.POST)
public String helloPostMethod(Model model, @RequestParam(value = "digit", required = false) Integer digit) {
System.out.println("HelloController.helloPostMethod() invoked.");
// JSP 화면에 서버의 처리값 전달 예시
int random = getRandamByDisit(digit);
model.addAttribute("randomNumber", random);
System.out.println("HelloController.helloPostMethod() ::: Random Number = [" + random + "]");
return "helloJSP";
}
private int getRandamByDisit(Integer digit) {
System.out.println("digit = [" + digit + "] defaultRandomDisit = [" + defaultRandomDisit + "]");
// Input Vault가 null 일 경우 app.properties의 hello.defaultRandomDisit 값을 사용함
digit = (digit == null) ? defaultRandomDisit : digit;
System.out.println("digit = [" + digit + "]");
int random = (int) (Math.random() * digit) + 1;
return random;
}
}
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<html>
<body>
<h2>This is hello.jsp for springframeworkweb project!!!</h2>
<br/><br/>
랜덤수 가져오기(Default Max: 10)<br/>
<%
int random = request.getAttribute("randomNumber") == null ? 0 : (int)request.getAttribute("randomNumber");
out.println("<br/> Random Number = "+ random);
%>
<br/><br/>
<!-- Controller에서 생성한 값을 JSP에서 사용 방법 2 -->
이렇게도 출력 가능 : ${randomNumber}
<br/><br/><br/>
<b>HTTP POST방식 ::: 랜덤수 다시 가져오기</b>
<form method="POST" action="/hello.do">
Ramdom Number의 Max 값 입력(0 ~ MAX) : <input type="text" name="digit" value='<%=request.getParameter("digit")%>'/>
<input type="submit" value="랜덤수 가져오기"/>
</form>
<br/><br/>
<a href="/loginPage.do">로그인 페이지로 이동</a>
<br/><br/>
<a href="/">메인 페이지로 이동</a>
</body>
</body>
</html>
*) HTTP Method 설명
HTTP Reqiest 메소드의 종류는 총 9가지가 있다. 이 중 주로 쓰이는 메소드는 5가지로 보면 된다.
1) "Servers" 탭에서 "No servers are available. Click this link..." 링크를 클릭
2) Apache > "Tomcat v8.5 Server"를 선택하고 "Next" 클릭
3) "Add & Remove" 창에서 springframeworkweb 프로젝트를 선택하여 "Add"하고 "Finish" 클릭
4) 서버에 프로젝트가 Add 된 상태에서 서버를 더블클릭하면 서버 설정 화면이 표시됨
5) "Modules" 탭을 선택
1) Tomcat 기동 후 "http://localhost:8080" 호출
- 참고 : "로그인 페이지로 이동"은 아직 구현하지 않았으므로 동작안함
2) 웹화면에서 "HTTP GET 방식 ::: Hello 페이지로 이동" 링크 클릭
3) 화면에서 "랜덤수 가져오기" 클릭 ( HTTP POST방식으로 Controller를 호출 )
1) src/main/webapp/WEB-INF/jsp 디렉토리 하위로 auth 디렉토리 생성
2) src/main/webapp/WEB-INF/jsp/auth/login.jsp 소스 개발
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="com.demo.auth.UserVO" %>
<html>
<body>
<h2>로그인 페이지</h2>
<br/><br/>
<form method="POST" action="/auth/login.do">
Login ID : <input type="text" name="userId" /><br/>
Password : <input type="text" name="passWd" />
<input type="submit" value="로그인"/>
</form>
<%
UserVO userResult = (UserVO)request.getAttribute("userResult");
if( userResult != null){
if( -1 == userResult.getUserLevel() ){
out.println("인증에 실패했습니다.");
}
}
%>
<br/><br/>
<a href="/">메인 페이지로 이동</a>
</body>
</html>
1) "src/main/java/com/demo" 디렉토리에 "auth" Package 생성
2) "src/main/java/com/demo/UserVO.java" 구현
package com.demo.auth;
import java.io.Serializable;
public class UserVO implements Serializable {
private String userId;
private String passWd;
private String userName;
private int userLevel;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getPassWd() {
return passWd;
}
public void setPassWd(String passWd) {
this.passWd = passWd;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public int getUserLevel() {
return userLevel;
}
public void setUserLevel(int userLevel) {
this.userLevel = userLevel;
}
}
1) "src/main/java/com/demo/AuthController.java" 구현
package com.demo.auth;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class AuthController {
@Autowired
AuthServiceImpl authService;
@RequestMapping(value = "/loginPage", method = RequestMethod.GET)
public String goLoginPage(Model model) {
System.out.println("LoginController.goLoginPage() invoked.");
model.addAttribute("userLevel", 0);
return "/auth/login";
}
// JSP의 input name들이 UserVO의 Member 변수와 같은 경우 UserVO에 Mappging되어 Parameter로 전달됨
@RequestMapping(value = "/auth/login", method = RequestMethod.POST)
public String loginFromDB(HttpServletRequest request, Model model, UserVO user) {
System.out.println("LoginController.login() invoked. userId =[" + user.getUserId() + "] passwd =["
+ user.getPassWd() + "]");
String returnPage = "/auth/login";
UserVO userResult = authService.checkAuth(user);
// 로그인 계정과 비번이 일치하면 User Level이 '0'이 아닌값이 Setting 됨
if (userResult != null && userResult.getUserLevel() != 0) {
System.out.println("Login Success~~~!!!!");
// JSP 화면에 서버의 처리값 전달 예시
System.out
.println("User ID =[" + userResult.getUserId() + "] Level = [" + userResult.getUserLevel() + "]");
// Session 처리
HttpSession session = request.getSession();
session.setAttribute("LoginUser", userResult);
returnPage = "/auth/loginResult";
} else {
System.out.println("Login Fail~~~ ㅠㅠ;");
userResult = new UserVO();
userResult.setUserLevel(-1);
}
model.addAttribute("userResult", userResult);
return returnPage;
}
@RequestMapping(value = "/auth/checkSession", method = RequestMethod.GET)
public String checkSession(Model model) {
System.out.println("LoginController.checkSession() invoked. Do Nothing!!!");
return "/auth/loginResultSession";
}
@RequestMapping(value = "/auth/logout", method = RequestMethod.GET)
public String logout(HttpServletRequest request) {
System.out.println("LoginController.logout() invoked.");
HttpSession session = request.getSession();
session.setAttribute("LoginUser", null);
return "/auth/login";
}
}
package com.demo.auth;
public interface AuthService {
public UserVO checkAuth(UserVO user);
}
package com.demo.auth;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service("authService")
public class AuthServiceImpl implements AuthService {
@Autowired
@Qualifier("dataSource")
private DataSource dataSource;
// Data Source를 직접 이용한 CASE : MyBatis가 없던 시절의 구현방법
// 프로젝트로 진행할 경우 MyBatis로 변경 필요
public UserVO checkAuth(UserVO user) {
UserVO userResult = new UserVO();
try (Connection conn = (Connection) dataSource.getConnection()) {
System.out.println("Connection 성공 : " + conn);
String sql = "SELECT userid, username, userlevel FROM user_mng WHERE userid = ? and passwd = ? ";
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setString(1, user.getUserId());
stmt.setString(2, user.getPassWd());
ResultSet rs = stmt.executeQuery();
System.out.println("rs = [" + rs + "]");
if (rs != null && rs.next()) {
// 레코드의 칼럼은 배열과 달리 0부터 시작하지 않고 1부터 시작한다.
// 데이터베이스에서 가져오는 데이터의 타입에 맞게 getString 또는 getInt 등을 호출한다.
userResult.setUserId(rs.getString(1));
userResult.setUserName(rs.getString(2));
userResult.setUserLevel(rs.getInt(3));
System.out.println("User Data existed. ( userResult from DB = [" + userResult.getUserId() + "] ["
+ userResult.getUserLevel() + "])");
}
} catch (Exception ex) {
System.out.println("실패..!");
ex.printStackTrace();
}
return userResult;
}
}
1) src/main/webapp/WEB-INF/jsp/auth/loginResult.jsp 소스 개발
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="com.demo.auth.UserVO" %>
<%!int userLevel;%>
<html>
<body>
<h2>로그인 결과 페이지</h2><br/>
<h2>Model Data</h2>
<%
UserVO userResult = (UserVO)request.getAttribute("userResult");
if( userResult != null ){
userLevel = userResult.getUserLevel();
}
%>
사용자 ID : <%= userResult.getUserId() %> <br/><br/>
사용자 레벨 : <%= userLevel %> 등급
<br/><br/><br/>
<h2>Session Data</h2>
사용자 ID : ${LoginUser.userId} <br/><br/>
사용자 ID : ${LoginUser.userLevel} <br/><br/>
<br/>
<!-- 아래 링크는 아직구현하지 않아서 동작안함 -->
<a href="/auth/checkSession.do">Session Check 페이지로 이동</a>
<br/><br/>
<a href="/soccer/players.do">선수 목록 페이지로 이동</a>
<br/><br/>
<a href="/auth/logout.do">Logout</a>
</body>
</html>
1) DB Table 생성 및 Data Insert
DROP TABLE user_mng;
CREATE TABLE user_mng (
userid VARCHAR(20),
passwd VARCHAR(20),
username VARCHAR(20),
userlevel TINYINT,
PRIMARY KEY(userid)
);
INSERT INTO user_mng (userid, passwd, username, userlevel)
VALUES ('user','1234','사용자', 3);
INSERT INTO user_mng (userid, passwd, username, userlevel)
VALUES ('admin','1234','관리자', 1);
1) 톰켓 기동 후 "http://localhost:8080" 호출
2) 로그인 페이지 표시
- 참고) Interceptor는 특정 URL을 호출할 경우 Controller로 호출되기 이전에 전처리를 수행할 수 있는 방법임
package com.demo.common.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import com.demo.auth.UserVO;
// 본 클래스는 Login Session을 처리하는 Interceptor
public class LoginInterceptor extends HandlerInterceptorAdapter {
// Controller로 호출 전 실행됨
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("<LoginInterceptor> ======================= Started =======================");
HttpSession session = request.getSession();
UserVO loginUser = (UserVO) session.getAttribute("LoginUser");
// Login Session 정보가 없는 경우 index.jsp(/)로 Redirect 시킴
if (loginUser == null) {
System.out.println("Unauthorized User........ㅠㅠ;");
response.sendRedirect("/");
return false;
}
return true;
}
// Controller가 모두 처리된 후 실행됨
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("<LoginInterceptor> ======================= Finished =======================");
super.postHandle(request, response, handler, modelAndView);
}
// 사용자에게 Response된 이후에 실행됨
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("LoginInterceptor.afterCompletion() invoked.....");
super.afterCompletion(request, response, handler, ex);
}
}
1) src/main/resources/spring/servlet-context.xml 에 아래의 내용을 삽입
<!-- Interceptor 설정 : 특정 URI 접근시 Controller가 호출되기 전에 먼저 실행됨 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- Session 권한이 있어야 볼수 있는 페이지에 대해 URI 설정 부분 -->
<mvc:mapping path="/auth/checkSession*"/>
<mvc:mapping path="/soccer/**"/>
<bean id="loginInterceptor" class="com.demo.common.interceptor.LoginInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
// com.demo.auth.AuthController.java 58줄
@RequestMapping(value = "/auth/checkSession", method = RequestMethod.GET)
public String checkSession(Model model) {
System.out.println("LoginController.checkSession() invoked. Do Nothing!!!");
return "/auth/loginResultSession";
}
1) src/main/webapp/WEB-INF/jsp/auth/loginResultSession.jsp 소스 개발
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="com.demo.auth.UserVO" %>
<%!int userLevel;%>
<html>
<body>
<h2>Session Check 페이지</h2>
<b>[중요] 세션이 생성되어야 이 페이지가 표시됨</b>
<br/><br/>
<h3>Model Data</h3>
<!-- 아래의 Model Data를 표시하는 Code의 주석("--")을 제거하면 에러가 발생함 -->
<%--
UserVO userResult = (UserVO)request.getAttribute("userResult");
if( userResult != null ){
userLevel = userResult.getUserLevel();
}
out.println("사용자 ID : "+ userResult.getUserId()+"<br/><br/>");
out.println("사용자 Level : "+ userLevel+"<br/><br/>");
--%>
<br/>
<h3>Session Data</h3>
사용자 ID : ${LoginUser.userId} <br/><br/>
사용자 Level : ${LoginUser.userLevel} <br/><br/>
<br/><br/><br/>
<a href="/auth/logout.do">Logout</a>
<br/><br/>
로그아웃하고 세션이 지워지면 로그인 페이지로 이동함<br/>
com.demo.auth.AuthController.java 31 Line 참조 ::: String returnPage = "/auth/login";
<br/><br/>
<a href="/loginPage.do">로그인 페이지로 이동</a>
</body>
</html>
1) 톰캣을 기동후 "http://localhost:8080/loginPage.do" 페이지 호출
2) 로그인 결과 화면에서 "Session Check 페이지로 이동" 링크 클릭
3) HttpSession에 저장된 정보로 JSP 화면이 표시됨
4) 동일한 브라우져의 다른 탭을 열어서 "http://localhost:8080/auth/checkSession.do" 입력 시 아무런 데이터를 저장하지 않고 로그만 출력하는 AuthController.checkSession() Method가 호출이 되지만 3)번의 화면과 동일하게 표시됨 (Session 이 저장된 것을 의미)
5) 로그아웃을 클릭 한 이후 브라우져 URL에 "http://localhost:8080/auth/checkSession.do" 입력 시 Session에서 정보를 지웠기 때문에 index.jsp로 이동함 (LoginInterceptor 의 response.sendRedirect("/"); 가 동작한 것임)
1) DB Table 생성, Data 생성
DROP TABLE player;
CREATE TABLE player (
p_id INTEGER,
p_name VARCHAR(20),
p_position VARCHAR(20),
height INTEGER,
PRIMARY KEY(p_id)
);
INSERT INTO player (p_id, p_name, p_position, height)
VALUES (1,'조규성','수비수', 181);
INSERT INTO player (p_id, p_name, p_position, height)
VALUES (2,'홍길동','공격수', 192);
2) pom.xml 에 JDBC, MyBatis 관련 Library Dependency 추가
- 윗 부분의 5.4.1 pom.xml에 이미 설정되어 있음
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.1.1</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<!-- jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${springframework.version}</version>
</dependency>
3) Spring root-context.xml 설정 추가
<!-- SQL Session Factory Bean 설정 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:/mybatis/mybatis-config.xml" />
<property name="mapperLocations" value="classpath:/sqlmap/**/*Mapper.xml" />
</bean>
4) src/main/resources/mybatis/mybatis-config.xml 파일 구현
- 윗 부분의 5.4.3.5 MyBatis 환경 설정에서 이미 설정되어 있음
<?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="cacheEnabled" value="false" />
<setting name="defaultExecutorType" value="REUSE" />
<setting name="mapUnderscoreToCamelCase" value="true" />
</settings>
</configuration>
<?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">
<!-- SQL Mapping -->
<mapper namespace="SoccerPlayerDAO">
<select id="getPlayerList" resultType="com.demo.soccer.SoccerPlayer">
SELECT p_id, p_name, p_position, height
FROM PLAYER
</select>
<select id="getPlayer" parameterType="int" resultType="com.demo.soccer.SoccerPlayer">
SELECT p_id, p_name, p_position, height
FROM PLAYER
WHERE p_id = #{pId}
</select>
</mapper>
package com.demo.soccer;
public class SoccerPlayer {
int pId;
String pName;
String pPosition;
int height;
public int getpId() {
return pId;
}
public void setpId(int pId) {
this.pId = pId;
}
public String getpName() {
return pName;
}
public void setpName(String pName) {
this.pName = pName;
}
public String getpPosition() {
return pPosition;
}
public void setpPosition(String pPosition) {
this.pPosition = pPosition;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
}
package com.demo.soccer;
import java.util.List;
public interface SoccerPlayerDao {
public List<SoccerPlayer> getPalyerList();
public SoccerPlayer getPlayer(int pId);
}
package com.demo.soccer;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@Repository
public class SoccerPlayerDaoImpl implements SoccerPlayerDao {
@Autowired
SqlSession sqlSession;
public List<SoccerPlayer> getPalyerList() {
System.out.println("Invoked SoccerPlayerDAO.getPalyerList()....sqlSession = [" + sqlSession + "]");
return sqlSession.selectList("SoccerPlayerDAO.getPlayerList");
}
@Override
public SoccerPlayer getPlayer(int pId) {
return sqlSession.selectOne("SoccerPlayerDAO.getPlayer", pId);
}
}
package com.demo.soccer;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class SoccerPlayerServiceImpl {
@Autowired
SoccerPlayerDao spDao;
public List<SoccerPlayer> getSoccerPlayers() {
List<SoccerPlayer> playerList = spDao.getPalyerList();
System.out.println("Player List size = [" + playerList.size() + "]");
return playerList;
}
public SoccerPlayer getPlayer(int pId) {
SoccerPlayer player = spDao.getPlayer(pId);
System.out.println("Player = [" + player + "]");
return player;
}
}
package com.demo.soccer;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class SoccerPlayerController {
@Autowired
SoccerPlayerServiceImpl soccerPlayerService;
@RequestMapping(value = "/soccer/players", method = RequestMethod.GET)
public String getSoccerPlayers(Model model) {
System.out.println("SoccerPlayerController.getSoccerPlayers() invoked.");
List<SoccerPlayer> soccerPlayers = soccerPlayerService.getSoccerPlayers();
model.addAttribute("soccerPlayers", soccerPlayers);
return "/soccer/soccerPlayerList";
}
@RequestMapping(value = "/soccer/players/player", method = RequestMethod.GET)
public String getSoccerPlayer(Model model, @RequestParam("pId") int pId) {
System.out.println("SoccerPlayerController.getSoccerPlayer() invoked. pId = [" + pId + "]");
SoccerPlayer soccerPlayer = soccerPlayerService.getPlayer(pId);
model.addAttribute("soccerPlayer", soccerPlayer);
return "/soccer/soccerPlayer";
}
}
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<body>
<h2>축구 선수 목록</h2><br/>
<table>
<tr align="center">
<td>ID</td>
<td>Name</td>
<td>Position</td>
<td>Height</td>
</tr>
<c:forEach var="item" items="${soccerPlayers}" begin="0" end="10" step="1" varStatus="status">
<tr align="center">
<td width="30" onclick="">${item.pId}</td>
<td width="60">${item.pName}</td>
<td width="60">${item.pPosition}</td>
<td width="60">${item.height}</td>
</tr>
</c:forEach>
</table>
<br/><br/>
<a href="/soccer/players/player.do?pId=1">1번 선수 정보</a>
<br/><br/>
<a href="/auth/logout.do">Logout</a>
</body>
</html>
9) WEB-INF/jsp/soccer/soccerPlayer.jsp구현
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<body>
<h2>축구 선수 상세</h2>
<br/>
ID : ${soccerPlayer.pId}
<br/>
Name : ${soccerPlayer.pName}
<br/>
Position : ${soccerPlayer.pPosition}
<br/>
Height : ${soccerPlayer.height}
<br/><br/>
<a href="/auth/logout.do">Logout</a>
<br/>
</body>
</html>
1) 로그인 이후 "선수 목록 페이지로 이동" 링크 클릭
2) 1) 화면에서 "1번 선수 정보" 링크 클릭
수고하셨습니다~
Spring Framework은 3-Party Library를 엄청나게 많이 제공하는 장점이 있지만 설정이 복잡하고 힘들어서 아직 개발자들에게는 봄(Spring)이 온것 같지는 않네요.
이 상태로 10년 이상 전세계의 Java Web Service Framework으로써 자리매김을 하고 있었습니다.
Spring Framework 공부는 여기까지 하겠습니다. ^^