Java Web Application 발전 - 2 (Spring Framework)

David Jeon·2023년 2월 9일
1

Springboot 기초 과정

목록 보기
3/4
post-thumbnail

EJB에 이어 2007년까지 Sprinf Framework을 포함하여 Server Framework, UI Framework의 춘추전국시대를 지나다가 Spring Framework이 Java Web Application 개발에 대표 Framework으로 자리를 잡음

  • 2003년 스프링 프레임워크 1.0 출시
  • 2007년 11월에 Spring Framework 2.5 버전이 출시가 되면서 Spring Framework이 한국 표준(?) Framework으로 자리를 잡아감
  • 2009년 대한민국 안전행정부에서 국내 공공부문 국가정보화사업(전자정부) JAVA 플랫폼 기반의 추진 시에 개발 프레임워크(Spring Framework 기반)의 표준 정립으로 응용 소프트웨어의 표준화, 품질 및 재사용성을 향상을 목표로 하며 특정 업체의 종속성 심화와 사업별 공통 컴포넌트 중복 개발을 막기 위해 개발
  • 2013년 스프링 프레임워크 4.0 출시 - 자바8 지원

- 본 강의의 전체 소스 공유 링크 : 진행중 오류가 있을 경우 소스 참조 바람
https://drive.google.com/file/d/1qX7HOMBDxcyb-8aontzdUL6PkQHD42L8/view?usp=share_link

5. Spring Framework

5.1 About Spring Framework

  • Java Service 개발을 위한 Open Source 기반의 Web Framework.
    -> 현재는 Web Service 개발을 위한 Java Backend Framework.

5.2 Spring Framework 특징

5.2.1 Light Weight Container

Enterprise Application 개발하기 위한 기능(J2EE중 EJB제외)을 종합적으로 제공하는 Light weight Container
Servlet Container를 지원하는 WAS(Undertow, Tomcat, Jetty) 만으로도 운영 가능

5.2.2 IOC(Inversion of Control)

  • 일반적인 프로그램 구조
    객체 생성 -> 의존성 객체 생성 -> 의존성 객채 내의 메소드 호출
    각 객체들이 프로그램의 흐름을 결정하고 각 객체를 구성하는 작업에 직접적으로 참여
    즉, 모든 작업을 의존성 객체를 생성한 객체가 제어하는 구조

  • IOC 구조
    모든 권한을 다른 대상에 위임함으로써 제어 권한을 위임받은 특별한 객체에 의해 결정되고 만들어짐. 제어의 흐름을 사용자가 컨트롤 하지 않고 권한을 위임한 특별한 객체에 모든 것을 맡기는 구조. IOC는 Dependency Lookup과 Dependency Injection을 이용하여 구현함

    • DL(Dependency Lookup, 의존성 검색)
      컨테이너에서는 객체들을 관리하기 위해 별도의 저장소에 Bean을 저장하는데 저장소에 저장되어 있는 컨테이너에서 제공하는 API 를 이용하여 사용하고자 하는 Bean을 검색하는 방법
    • DI(Dependency Injection, 의존성 주입)
      의존성 주입이란 객체가 서로 의존하는 관계가 되게 의존성을 주입하는 것
      객체지향 프로그램에서 의존성 이란 하나의 객체가 어떠한 다른 객체를 사용하고 있음을 의미
      각 Class 사이에 필요로 하는 의존 관계를 Bean 설정 정보를 바탕으로 Container가 자동으로 연결해 주는 것

5.2.3 AOP(Aspect Orientated Programming)

  • OOP (객체 지향 프로그래밍)
    객체지향 원칙에 따라 관심사가 같은 데이터를 한곳에 모아 분리, 낮은 결합도를 갖게 해 독립적이고 유연한 모듈로 캡슐화를 함
    큰 프로그램을 모듈단위로 축소시켜 작성할 수 있게 되었지만 발생되는 중복을 피할수 없음

  • AOP : 관점 지향 프로그래밍
    Transaction, Logging, Performance Analysys 등 횡단 관심사(Crosscutting-Concerns)를 모듈화 하는 기법
    핵심기능과 공통기능을 분리시켜 핵심 로직에 영향을 끼치지 않게 공통기능을 끼워 넣는 개발 형태
    무분별하게 중복되는 코드를 한 곳에 모아 공통기능을 한 곳에 보관함으로써 공통 기능 하나의 수정으로 모든 핵심기능들의 공통기능을 수정 할 수 있어 효율적인 유지보수 가능 및 재활용성이 극대화

5.2.4 MVC(Model2)

웹 프로그램밍 개발 시 "Spring MVC"를 이용하여 Model-View-Controller 구조로 사용자 인터페이스와 비지니스 로직을 분리하여 개발

5.2.5 POJO(Plain Old Java Object) 방식

일반적인 java 코드를 이용해서 프로그래밍이 가능
EJB에선 필수 인터페이스나 클래스를 상속받아 무거운 객체들을 만들어야만 했음
But Spring은 일반적인 java코드로만으로도 객체를 구성 가능하여 프로그래밍이 가볍고 생산성이 높아짐

5.3 Spring Framework 구조

이미지 : 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)를 포함한 수많은 뷰 기술을 지원

5.4 Spring Framework 프로젝트 구현

  • 본 Chapter는 Spring 4.3.30.RELEASE를 기본으로 실습을 진행함
    1) Maven Project를 생성 - 실습
  • Group Id: com.sample
  • Artifact Id: springframeworkweb

2) <상황발생시> Build Error 문제 해결 및 JDK8 변경 - 실습

5.4.1 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/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>

5.4.1.1 Maven Update 실행

1) project 이름 위에서 "우측버튼 > Maven > Update Project"를 선택

2) "Update Maven Project" 창이 뜨고 springframeworkweb 프로젝트가 선택된 것을 확인후 "OK" 버튼 클릭

  • "OK" 버튼을 클릭하면 pom.xml에 설정한 Library를 다운로드 받고 Compile이 진행됨

5.4.1.2 프로젝트 속성중 Dynamic Web Module 버전 확인

  • Project 이름 위에서 우측버튼 > Properties 선택
  • Project Facets > Dynamic Web Module 의 Version 확인
  • Maven Update를 진행하면 STS의 버그인지 Dynamic Web Module 버전이 4.0으로 자동으로 변경되는 경우가 있음. 아래와 같이 조치하면 됨
    1) Dynamic Web Module 버전을 3.0으로 변경하고 "Apply and Close" 클릭

2) src > main > webapp > WEB-INF > web.xml 파일의 버전 변경

  • web.xml에 오류표시가 나지만 STS 버그로 보임.그냥 사용하면 됨.

5.4.1.3 다시 Maven Update를 실행후 버전확인

  • Project Facets > Dynamic Web Module 의 Version 확인
  • 3.0 에서 변경되지 않았으면 설정 완료

5.4.2 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>

5.4.3 Spring Framework 설정

5.4.3.1 설정을 위해 src/main/resources 하위 디렉토리를 생성

  • mybatis, properties, spring, sqlmap

5.4.3.2 app.properties 설정

  • 개발할 Web Application에서 사용하는 설정을 저장한 파일
  • src/main/resources/properties 아래에 app.properties 파일 생성
## 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

5.4.3.3 Servlet Context 설정

  • Spring Framework의 DespatcherServler이 사용하는 설정 파일
  • src/main/resources/spring 아래에 servlet-context.xml 파일 생성
<?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>

5.4.3.4 Spring Context 설정

  • Spring Container가 생성될 때 사용할 설정 파일
  • src/main/resources/spring 아래에 root-context.xml 파일 생성
<?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>

5.4.3.5 MyBatis 환경 설정

  • MyBatis가 사용할 설정 파일
  • src/main/resources/mybatis 아래에 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="cacheEnabled" value="false" />
		<setting name="defaultExecutorType" value="REUSE" />
		<setting name="mapUnderscoreToCamelCase" value="true" />
	</settings>
</configuration>

5.4.4 Hello 출력과 GET, POST 방식의 서비스 구현

5.4.4.1 index.jsp 개발

1) 먼저 jsp 디렉토리 설정이 정상적으로 되는지 확인을 위해 index.jsp 개발

  • src/main/webapp/WEB-INF/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>

5.4.4.2 Hello Controller 개발

  • Controller를 이용하여 Hello JSP 페이지로 이동하는 Controller 개발

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;
	}
}

5.4.4.2 결과화면인 "src/main/webapp/WEB-INF/jsp" 디렉토리에 "helloJSP.jsp" 생성

<%@ 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가지로 보면 된다.

  • 주요 메소드
    GET : 리소스 조회
    POST: 요청 데이터 처리, 주로 등록에 사용
    PUT : 리소스를 대체(덮어쓰기), 해당 리소스가 없으면 생성
    PATCH : 리소스 부분 변경 (PUT이 전체 변경, PATCH는 일부 변경)
    DELETE : 리소스 삭제
  • 기타 메소드
    HEAD : GET과 동일하지만 메시지 부분(body 부분)을 제외하고, 상태 줄과 헤더만 반환
    OPTIONS : 대상 리소스에 대한 통신 가능 옵션(메서드)을 설명(주로 CORS에서 사용)
    CONNECT : 대상 자원으로 식별되는 서버에 대한 터널을 설정
    TRACE : 대상 리소스에 대한 경로를 따라 메시지 루프백 테스트를 수행

5.4.4.3 Tomcat 서버 생성및 Project Add

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" 탭을 선택

  • Path가 자동으로 프로젝트 명이 부여됨. 이것을 Context Path라고 부르는데 이럴경우 URL에 항상 포함해야 함. 예) http://localhost:8080/**springframeworkweb**/...
  • 이 Context Path를 "/"로 변경하기 위해 목록에서 선택 후 "Edit" 클릭

    6) 팝업창에서 Path 부분의 Context Path를 삭제하고 "/" 만 남기고 "OK" 클릭

    7) Ctrl + S로 설정을 저장

5.4.4.4 Hello Service 테스트

1) Tomcat 기동 후 "http://localhost:8080" 호출

- 참고 : "로그인 페이지로 이동"은 아직 구현하지 않았으므로 동작안함

2) 웹화면에서 "HTTP GET 방식 ::: Hello 페이지로 이동" 링크 클릭

  • HTTP GET 방식으로 호출된 helloJSP.jsp 화면이 표시됨
  • GET 방식은 서버로 전달하는 값이 빨간박스의 &digit=8 과 같이 URL에 표시됨
  • 서버의 helloGetMethod() 메소드 로그도 확인 바람

3) 화면에서 "랜덤수 가져오기" 클릭 ( HTTP POST방식으로 Controller를 호출 )

  • HTTP POST 방식으로 호출된 helloJSP.jsp 화면이 표시됨
  • POST 방식은 서버로 전달하는 값이 빨간박스의 &digit=8이 없이 xxx.do까지 URL에 표시됨
  • 서버의 helloPostMethod() 메소드 로그도 확인 바람

5.4.5 Login Service 구현

  • MyBatis와 차이를 확인하기 위해 DataSource를 직접 사용하는 것으로 구현

5.4.5.1 login.jsp 파일 구현

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>

5.4.5.2 로그인 정보를 전달할 UserVO.java 구현

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;
	}
}

5.4.5.3 로그인 요청을 수신할 AuthController.java 구현

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";
	}
}

5.4.5.3 Business Logic을 구현하기 위한 AuthService.java Interface 구현

package com.demo.auth;

public interface AuthService {
	public UserVO checkAuth(UserVO user);
}

5.4.5.3 AuthServiceImpl.java에 Business Logic을 구현

  • 소스를 보면 Spring에서 선언한 DataSource를 이용하여 DB Connection을 생성하여 SQL을 이용하여 직접 DB를 호출하는 것을 확인할 수 있음
  • 이 부분을 다음 챕터에서 구현할 MyBatis 적용과 비교 바람
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;
	}
}

5.4.5.4 Login 결과를 표시할 JSP 구현

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>

5.4.5.5 DB에 "user_mng" Table 생성후 Data 입력

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);

5.4.5.6 Login 테스트

1) 톰켓 기동 후 "http://localhost:8080" 호출

2) 로그인 페이지 표시

  • ID / PW 입력 후 로그인 클릭
    3) 로그인 결과화면 표시됨

    4) Logout 링크를 클릭하여 로그아웃 실행
    5) (실습) admin / 1234를 입력 후 테스트 진행

5.4.6 구현된 Login Service를 이용하여 인증 기능 구현

5.4.6.1 인증처리를 위해 Interceptor 구현

- 참고) Interceptor는 특정 URL을 호출할 경우 Controller로 호출되기 이전에 전처리를 수행할 수 있는 방법임

  • 로그인을 해야 볼수 있는 화면들은 LoginInterceptor를 이용하여 제한이 되도록 구현
  • 상기 Login Service는 로그인 수행후 UserVO Object에 정보를 담아 HttpSession에 저장하고 있는데 HttpSession에 정보가 없을 경우 Login Page로 Redirect 하도록 구현함
    1) src/main/java/com/demo 아래에 common Package를 생성
    2) LoginInterceptor.java 구현
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);
	}
}

5.4.6.2 구현한 Interceptor가 동작하도록 설정

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>

5.4.6.3 Session 정보 생성 이후 확인을 위한 Controller의 Method 부분

  • com.demo.auth.AuthController.java 58줄 이후 소스 확인
    @RequestMapping(value = "/auth/checkSession", method = RequestMethod.GET)
  • 아래 소스는 로그인 이후 호출할 경우 Log만 찍고 Session 확인 JSP로 이동만 시킴
    // 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";
	}

5.4.6.4 인증정보 확인을 위한 JSP 구현

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>
 

5.4.6.5 인증 기능 테스트

1) 톰캣을 기동후 "http://localhost:8080/loginPage.do" 페이지 호출

  • ID / PW 입력 후 "로그인" 버튼 클릭

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("/"); 가 동작한 것임)

5.4.7 MyBatis 설정

5.4.7.1 DB Table 생성 및 Data Insert

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 설정 추가

  • JDBC 설정 중 <property name="configLocation" 과 <property name="mapperLocations" 항목 추가
    - 윗 부분의 5.4.3.4 Spring Context 설정에서 이미 설정되어 있음
	<!-- 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>

5.4.8 MyBatis를 이용한 Soccer Service 개발

5.4.8.1 MyBatis Mapper XML 개발

  • 상기 생성한 PLAYER Table의 데이터의 목록 및 상세를 조회하는 Query 작성
  • src/main/resources/sqlmap/SoccerPlayer-Mapper.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">

<!-- 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>

5.4.8.2 SoccerPlayer VO 개발

  • src/main/java/com.demo.soccer Package 생성 후 SoccerPlayer.java 구현
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;
	}
}

5.4.8.3 MyBatis와 연동되는 DAO Interface 구현

  • SoccerPlayerDao.java Interface 구현
package com.demo.soccer;

import java.util.List;

public interface SoccerPlayerDao {
	public List<SoccerPlayer> getPalyerList();
	public SoccerPlayer getPlayer(int pId);
}

5.4.8.4 DAO Interface의 구현체 SoccerPlayerDaoImpl.java 구현

  • seqSession을 이용하여 Mapper의 "namespace.sqlid"를 이용하여 호출함
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);
	}
}

5.4.8.5 SoccerPlayerServiceImpl.java 구현

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;
	}
}

5.4.8.5 SoccerPlayerController.java 구현

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";
	}
}

5.4.8.5 soccerPlayerList.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/>
<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>

5.4.8.6 soccerPlayer.jsp 구현

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>

5.4.8.7 조회 테스트

1) 로그인 이후 "선수 목록 페이지로 이동" 링크 클릭

  • 로그인 이후 선수 목록과 선수 조회가 가능하도록 되어있음

2) 1) 화면에서 "1번 선수 정보" 링크 클릭

수고하셨습니다~
Spring Framework은 3-Party Library를 엄청나게 많이 제공하는 장점이 있지만 설정이 복잡하고 힘들어서 아직 개발자들에게는 봄(Spring)이 온것 같지는 않네요.
이 상태로 10년 이상 전세계의 Java Web Service Framework으로써 자리매김을 하고 있었습니다.
Spring Framework 공부는 여기까지 하겠습니다. ^^

profile
코딩이 즐거운 아저씨

0개의 댓글