행위

"스프링 프레임워크(Spring Framework) 외부연수"의 두 판 사이의 차이

라이언의 꿀팁백과

 
(같은 사용자의 중간 판 45개는 보이지 않습니다)
36번째 줄: 36번째 줄:
|3일
|3일
|Bean, MVC 복습, IoC 컨테이너, Bean, Dependency Injection(DI), Annotation, SpringMVP, SpringAOP
|Bean, MVC 복습, IoC 컨테이너, Bean, Dependency Injection(DI), Annotation, SpringMVP, SpringAOP
|Spring Framework 수업 착수
|Spring Framework 수업 시작
|-
|-
|2023.5.18(목)
|2023.5.18(목)
|4일
|4일
|
|AOP 실습, Spring JDBC, DB Connection Pool, JNDI, 트랜잭션, SpringMVC
|
|
|-
|-
|2023.5.19(금)
|2023.5.19(금)
|5일
|5일
|
|SpringMVC, Mybatis, Single Page Application, 동기·비동기 통신, JSON
|
|
|}
|}
914번째 줄: 914번째 줄:
=== 이클립스 꿀팁 ===
=== 이클립스 꿀팁 ===


* Ctrl-Alt-↑ 또는 ↓ : 현재 선택한 줄이 화살표 방향에 맞게 그대로 복사가 된다.
* Ctr + -Alt + ↑ 또는 ↓ : 현재 선택한 줄이 화살표 방향에 맞게 그대로 복사가 된다.
* Alt + Shift + T : 인터페이스 자동 생성
* Alt + Shift + T : 인터페이스 자동 생성
*Alt + Shift + S : Getters 와 Setters 자동 생성
*Alt + Shift + S : Getters 와 Setters 자동 생성
*Ctrl + D : 현재 줄 삭제
*Ctrl+Shift+O : 소스코드 내 관련 패키지 일괄 import


=== 스프링 프레임워크가 해주는 일 ===
=== 스프링 프레임워크가 해주는 일 ===
1,057번째 줄: 1,059번째 줄:
* 어드바이스 : 횡단 관심
* 어드바이스 : 횡단 관심
* 애스팩트 : 횡단 관심과 핵심 관심 관계 설정
* 애스팩트 : 횡단 관심과 핵심 관심 관계 설정
아래는 위에 대한 내용을 간단히 그림으로 정리한 내용이다.
아래는 위에 대한 내용을 간단히 그림으로 정리한 내용이다.
[[파일:Spring-aspect-oriented-programming.png|가운데|프레임없음|800x800픽셀|Spring의 AOP 개념]]
[[파일:Spring-aspect-oriented-programming.png|가운데|프레임없음|800x800픽셀|Spring의 AOP 개념]]
참고로 AOP와 Filter는 다르다. Filter는 서블릿 단위에서 실행이 되고, Spring의 AOP는 Spring 영역, 즉 Application 영역에서 실행된다. 더 상세한 설명은 아래 링크를 참고하자.
* https://dejavuhyo.github.io/posts/spring-filter-interceptor-aop-differences/
[[파일:Concepts-of-filter-interceptor-aop.png|가운데|프레임없음|900x900픽셀|Filter, Interceptor, AOP 흐름]]
어드바이스(advice)는 적용 시점에 따라 총 5개 종류가 있다.
어드바이스(advice)는 적용 시점에 따라 총 5개 종류가 있다.


1,068번째 줄: 1,076번째 줄:
** ... After : 비즈니스 메소드 실행 후 (메소드 실행 결과에 관계 없이 항상 실행 / try-catch-finally 구문 중 finally에 해당)
** ... After : 비즈니스 메소드 실행 후 (메소드 실행 결과에 관계 없이 항상 실행 / try-catch-finally 구문 중 finally에 해당)
* Around : 메소드 실행 전·후
* Around : 메소드 실행 전·후
=== 3-Tier Architecture와 Spring Framework ===
{| class="wikitable"
|+
!'''Spring Framework'''
!'''레이어명'''
!'''기능'''
|-
|SpringMVC
|Presentation
|View, Controller
|-
|SpringAOP
|Business
|Service
|-
|SpringJDBC (또는 MyBatis, JPA 활용)
|Persistent
|DAO(=JDBC)
|}


=== Maven Repository ===
=== Maven Repository ===
1,075번째 줄: 1,103번째 줄:


참고로 Maven 혹은 Gradle을 망분리 환경에서 사용하거나 내부 기업에서 사용 시 중앙 통제를 하고 싶으면 Nexus를 통해 이를 구축할 수 있다.
참고로 Maven 혹은 Gradle을 망분리 환경에서 사용하거나 내부 기업에서 사용 시 중앙 통제를 하고 싶으면 Nexus를 통해 이를 구축할 수 있다.
=== JointPoint ===
횡단 관심에 해당하는 어드바이스 메소드를 의미있게 구현하려면 클라이언트가 호출한 비즈니스 메소드의 정보가 필요하다. 이를 위해서 스프링은 org.aspectj.lang 패키지에 속한 JointPoint 인터페이스를 제공한다. 단, Around 어드바이스에서는 ProceedingJointPoint 객체를 사용해야 한다. ProceedingJoinPoint 객체는 비즈니스 메소드를 호출하는 proceed() 메소드를 가지고 있으며 JoinPoint를 상속했다. proceed() 호출을 통해 실제 호출할 메소드를 실행한다.
{| class="wikitable"
|+
!'''메소드'''
!'''설명'''
|-
|Signature getSignature()
|클라이언트가 호출한 메소드의 시그니처(리턴타입, 이름, 매개변수) 정보가 저장된 Signature 객체 리턴
|-
|Object getTarget()
|클라이언트가 호출한 비즈니스 메소드를 포함하는 비즈니스 객체 리턴
|-
|Object[] getArgs()
|클라이언트가 메소드를 호출할 때 넘겨준 인자 목록을 Object 배열로 리턴
|}
=== Spring AOP's Under the Hood ===
'''Spring AOP는 Java Reflection을 이용해서 내부적으로 구현'''이 되어있기 때문에 메소드만을 대상으로 할 수밖에 없습니다. 그 이상을 원한다면 AspectJ를 이용해서 Spring AOP가 아닌 Java AOP를 구현하셔야 합니다. ([https://velog.io/@18k7102dy/Spring-AOP-Spring-AOP%EB%A5%BC-%EC%82%AC%EC%9A%A9%EC%8B%9C-%EC%A3%BC%EC%9D%98%ED%95%B4%EC%95%BC%ED%95%A0-%EC%A0%90 링크])
=== AOP에서 사용되는 용어 복습 ===
* Target: 어떤 대상에 부가 기능을 부여할 것인지
* Advice: 어떤 부가 기능을 부여할 것인지
* Join Point: 어디에 적용할 것인지
* Point Cut: 실제 Advice가 적용될 시점을 의미. Spring AOP에서는 advice가 적용될 메서드를 선정.
=== 어노테이션 기반 AOP ===
스프링 설정 파일(예: applicationContext.xml)에 엘리먼트를 선언해야 한다.
=== JNDI ===
'''JNDI(Java Naming and Directory Interface)'''는 <u>디렉터리 서비스에서 제공하는 데이터 및 객체를 발견(discover)하고 참고(lookup) 하기 위한 자바 API</u>다. 간단히 요약하자면 우리가 연결하고 싶은 데이터베이스의 DB Pool을 미리 Naming 시켜주는 방법 중 하나이다. 우리가 저장해놓은 WAS 의 데이터베이스 정보에 JNDI를 설정해 놓으면 웹 애플리케이션은 JNDI만 호출하면 간단해진다. (링크: https://go-coding.tistory.com/76)
DataSource 설정을 위해서 applicationContext.xml에 아래와 같이 Bean 객체를 생성한다.
<syntaxhighlight lang="xml" line="1">
...
<!-- DataSource 설정 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="org.h2.Driver" />
    <property name="url" value="jdbc:h2:tcp://localhost/~/test" />
    <property name="username" value="sa" />
    <property name="password" value="" />
</bean>
...
</syntaxhighlight>
위 코드에서 property 는 setter 메소드를 호출한다 의미, name 은 호출할 메소드 이름, value는 설정하려는 값이다.
참고로 암호는 상기와 같이 xml 파일에 직접 넣지는 않고 database.properties와 같은 파일을 별도로 분리해서 보관한다. 이 파일은 아래와 같이 생겼다.
<syntaxhighlight line="1">
jdbc.driver = oracle.jdbc.driver.OracleDriver
jdbc.url = jdbc:oracle:thin:@localhost:1521:xe
jdbc.username = scott
jdbc.password = tiger
</syntaxhighlight>
위 파일을 읽어들이기 위한 웹 애플리케이션 환경설정은 아래와 같이 하면 된다.
<syntaxhighlight lang="xml" line="1">
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<context:component-scan base-package="com.springbook.biz" />
<context:property-placeholder location="classpath:database.properties" />
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
</beans>
</syntaxhighlight>
참고로 OS 환경변수에 필요한 값을 설정하기도 한다. 여기서는 이에 대해서 더 다루지는 않는다.
=== XML의 ref 값 ===
※ <u>xml에서 ref는 Bean에서 설정한 이름인 id를 의미한다.</u>
=== ORM 프레임워크, JPA ===
소스 코드에 SQL을 없애고자 한다면 ORM 프레임워크를 사용하면 되며, 대표적으로 JPA가 있다. 그런데 JDBC Template 보다는 어렵다.
=== query() vs. queryForObject() ===
ChatGPT에게 JdbcTemplate 객체의 query() 메소드와 queryForObject() 메소드 차이점을 물어봤다.
[[파일:ChatGPT-Answer-06.png|가운데|프레임없음|852x852픽셀|query() vs. queryForObject()]]
=== RowMapper ===
[[파일:ChatGPT-Answer-07.png|가운데|프레임없음|800x800픽셀|RowMapper 구현 관련 ChatGPT 답변]]
=== 트랜잭션(Transaction) ===
트랜잭션 관리를 위해 처음부터 관련 코드를 작성하는 것은 비효율적이고 해당 코드가 실제로 잘 작동할지 보장하는 것도 어렵다. 그래서 스프링은 Transaction 처리를 위해 Transaction Manager를 제공한다. 다만, ... 문법을 사용하지 못한느데 그 이유는 사전에 method 명을 알 수 없기 때문이다. 그래서 Transaction Manager 사용을 이해 ... 문법을 추가했다.
==== 선언적 트랜잭션 ====
스프링에서도 EJB와 마찬가지로  트랜잭션 처리를 컨테이너가 자동으로 처리하도록 설정할 수 이는데, 이를 '''선언적 트랜잭션 처리'''라고 한다. Transaction Advice 설정을 하기 위한 가장 첫 번째 작업은 Transaction Manager를 Bean에 등록하는 것이다.
==== 트랜잭션 처리 레이어 ====
Persistent Layer가 아니라 Business Layer에서 처리해야한다.
==== 트랜잭션 매니저 설정 코드 ====
<syntaxhighlight lang="xml" line="1">
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<context:component-scan base-package="com.springbook.biz" />
<context:property-placeholder location="classpath:databases.properties" />
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- Spring JDBC 설정 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- Transaction 설정 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true" />
<tx:method name="*" />
</tx:attributes>
</tx:advice>
</beans>
</syntaxhighlight>
=== Model 1 아키텍처 ===
90년대 말부터 2000년대 초까지 자바 기반의 웹 애플리케이션 개발에 사용됐던 아키텍처를 Model 1 이라고 부른다. Model 1 아키텍처는 JSP와 JavaBeans만 사용하여 웹을 개발하는 것으로 그림 1-1과 같은 구조다.
[[파일:Model1-Architecture-Diagram.png|가운데|프레임없음|400x400픽셀|Model 1 Architecture]]그러나 Model 1 아키텍처는 엔터프라이즈 시스템에 적합하지 않다. 그 이유는 자바 로직과 화면 디자인이 통합되어 유지보수가 어렵기 때문이다. 이러한 Model 1 아키텍처의 문제를 해결하기 위해 고안된 웹 개발 모델이 Model 2 아키텍처, 즉 MVC 아키텍처다.
=== Model 2 아키텍처 ===
Model 2 아키텍처에서 가장 중요한 특징은 Controller의 등장이며, 이 Controller는 서블릿 클래스를 중심으로 구현한다.
Model 2 아키텍처에서는 기존에 JSP가 담당했던 Controller 로직이 별도의 Controller 기능의 서블릿으로 옮겨졌다. 따라서 기존에 Model 1 아키텍처로 개발한 프로그램에서 JSP 파일에 있는 자바 코드만 Controller로 이동하면 Model 2 아키텍처가 된다. 결과적으로 Controller 로직이 사라진 JSP에는 View와 관련된 디자인만 남게 되어 디자이너는 JSP 파일을 관리하고, 자바 개발자는 Controller와 Model만 관리하면 된다.[[파일:Model2-Architecture-Diagram.png|가운데|프레임없음|500x500픽셀|Model2 Architecture]]
=== 프론트 컨트롤러(Front Controller) 디자인 패턴 ===
서비스 요청을 중앙집중에서 처리하기 위한 패턴으로 SpringMVC에서 이 패턴을 지원한다. 이름은 '''<u>DispatcherServlet</u>''' 이다.
[[파일:Front-Controller-Design-Pattern.png|가운데|프레임없음|700x700픽셀|Front Contoller Design Pattern]]프론트 컨트롤러 특징은 아래와 같다.
* 프론트 컨트롤러 서블릿 하나로 클라이언트의 요청을 받음
* 프론트 컨트롤러가 요청에 맞는 컨트롤러를 호출(mapping 정보기반)
* 공통 처리를 입구에서 할 수 있음
* 프론트 컨트롤러를 둠으로써 나머지 컨트롤러는 서블릿을 사용하지 않아도 됨
DispatcherServlet의 개념도는 아래와 같다. 참고로 DispatcherServle이 시작되면 애플리케이션을 실행할 때 호출했던 GenericXmlApplicationContext("applicationContext.xml")을 함께 실행하여 컨테이너를 구동한다.
[[파일:DispatcherServlet.png|가운데|프레임없음|700x700픽셀|SpringMVC's DispatcherServlet]]
=== SpringMVC의 수행 흐름 ===
SpringMVC가 클라이언트 요청을 처리하는 흐름은 아래 도표와 같다.[[파일:SpringMVC-Process-Flow.png|가운데|프레임없음|800x800픽셀|SpringMVC's process flow]]참고로 ④번의 리턴값은 실제로 단순 "문자열"이며 이 "문자열" 값을 토대로 실제 경로를 얻는 것은 ViewResolver다.
=== web.xml 파일 ===
수업 중 실습한 web.xml 파일의 중간버전은 아래와 같다. 실습 중 <context-param>...</context-param>의 <param-name> 태그의 값을 contextConfiguration으로 잘못 입력해서 오류가 발생했다. contextConfigLocation이라는 값으로 수정 후 Server를 실행했더니 정상적으로 게시글 등록 화면이 나왔다.<syntaxhighlight lang="xml" line="1">
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
<!-- Filter 추가 및 맵핑-->
<filter>
<filter-name>characterEncoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncoding</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
<!-- Context Param 등록 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- Listener 등록 -->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<!-- DispatcherServlet 등록 및 맵핑-->
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/config/presentation-layer.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
</syntaxhighlight>
애플리케이션을 실행하면 아래와 같은 화면이 나온다.
[[파일:InsertBoard-예제화면.png|가운데|프레임없음|600x600픽셀|insertBoard.do 호출 화면]]
=== 기타 ===
* Negative 테스트를 잊지 말자. (대부분이 Positive 테스트를 시나리오 테스트로 수행하고, Negative 테스트는 생략을 하거나 탐색적 테스팅으로 수행함)


== 5일차 ==
== 5일차 ==
업데이트 예정
 
=== 복습 ===
Spring Framework는 각 영역별로 Bean을 갖고 있다.
 
 
'''ContextLoaderListener'''는 Listener인데  객체 생성 및 초기화를 자동으로 수행한다. 정확히 설명하자면 ContextLoaderListener는 '''RootApplicationContext를 생성'''하는 클래스다.
 
 
'''DispatcherServlet'''는 Presentation 영역의 Bean을 처리하는 Spring Container를 구동하는데 구동을 위한 정보는 '''contextConfigLocation''' 변수에 저장되어 있다. web.xml에 아래 코드와 같이 설정한다. 참고로 DispatcherServlet의 다른 이름은 '''Front Controller''' 이다.
 
<syntaxhighlight lang="xml" line="1">
 
<!-- DispatcherServlet 등록 및 맵핑-->
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/config/presentation-layer.xml</param-value>
</init-param>
</servlet>
</syntaxhighlight>
 
 
※ 만약 web.xml 파일에서 <init-param>이 없으면 <servlet-name>action</servlet-name> 값을 토대로 '''/WEB-INF/서블릿이름-servlet.xml''' 파일을 찾는다.
 
=== Spring 프레임워크 프로젝트에서 가장 먼저 확인해야 하는 것 ===
Spring Framework에서 제일 먼저 하는 일은 '''Bean에 대한 설정을 확인'''하는 것이고 이는 web.xml 파일에 등록을 한다.
 
=== classpath ===
classpath는 프로젝트 기준으로 아래 위치를 의미한다.
[[파일:Classspath.png|가운데|프레임없음|331x331픽셀|이클립스 프로젝트에서 classpath 의미]]
 
=== HandlerMapping ===
어떤 URL에 대해 어떤 메소드를 실행할지 결정하는 객체다. Annotation 설정 시 @RequestMapping을 사용한다. 과거에는 XML 파일에 Property로 등록을 했지만 더 이상 사용하지 않는다.
[[파일:RequestMapping-example.png|가운데|프레임없음|400x400픽셀|Spring 프레임워크에서 HandlerMapping 하는 방법]]
호출하는 메소드에 parameter가 있다라고 하면 아래와 같은 일을 수행한다.
 
# 객체생성
# Request 객체에 값이 null이 아닌 property를 바인딩 (예: setSeq(request.getParameter("seq"); 호출)
# 모든 setter 메소드 호출
 
 
즉, Spring 프레임워크에서는 parameter만 추가해주면 위의 세가지 일을 한다. 이거를 하는 애가 바로 Controller 역할을 하는 HandlerMapping 객체다.
 
=== ViewResolver ===
ViewResolver 미 생성 시, Controller가 return 하는 값이 곧 이동하는 URL이 된다. 예를 들어 아래 함수는 test.jsp 로 이동한다.
[[파일:Default-viewResolver.png|가운데|프레임없음|500x500픽셀|디폴트 ViewResolver 사용]]
 
=== Model 인터페이스 ===
Spring 프레임워크에서 Request 객체에 parameter를 전달하고 싶으면 Model 인터페이스를 사용한다. 참고로 HanlderMapping에 의해서 호출하는 insertBoard() 함수에 있는 parameter인 BoardVO는 자동으로 등록이 된다. 즉, 아래 코드가 프레임워크에 의해 자동으로 추가된다.
 
<syntaxhighlight lang="java" line="1">
model.addAttribute("boardVO", board);
</syntaxhighlight>
 
 
실제 BoardController.java 파일은 아래와 같다.
 
<syntaxhighlight lang="java" line="1">
package com.springbook.biz.board.controller;
 
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
 
import com.springbook.biz.board.BoardVO;
 
@Controller
public class BoardController {
// 글 등록
@RequestMapping("/insertBoard.do")
public String  insertBoard(BoardVO boardVO, Model model) {
System.out.println("BoardController의 insertBoard()");
System.out.println(boardVO);
// Parameter 설정
model.addAttribute("msg", "Have a good day!");
// 서비스 호출
return "test.jsp";
}
}
</syntaxhighlight>
 
 
이 값을 확인하려면 아래와 같이 EL 태그를 사용하면 된다.
 
<syntaxhighlight lang="jsp" line="1">
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>테스트 페이지 입니다 😊</h1>
<h2>${msg}</h2>
<h3>${boardVO}</h3>
</body>
</html>
</syntaxhighlight>
 
=== @ModelAttribute ===
만약 Model로 선언한 변수의 이름을 바꾸고 싶으면 @ModelAttribute Annotation을 사용한다.<syntaxhighlight lang="java" line="1">
package com.springbook.biz.board.controller;
 
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
 
import com.springbook.biz.board.BoardVO;
 
@Controller
public class BoardController {
// 글 등록
@RequestMapping("/insertBoard.do")
public String  insertBoard(@ModelAttribute("board") BoardVO boardVO, Model model) {
System.out.println("BoardController의 insertBoard()");
System.out.println(boardVO);
// Parameter 설정
model.addAttribute("msg", "Have a good day!");
// 서비스 호출
return "test.jsp";
}
}
</syntaxhighlight>
 
=== Model 객체로 넘어온 VO의 Property 가져오기 ===
struct 혹은 객체의 멤버 변수에 접근하는 것처럼 처리하면 된다. 내부적으로 getter method를 호출해서 데이터를 출력한다.<syntaxhighlight lang="jsp" line="1">
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>테스트 페이지 입니다 😊</h1>
<h2>${msg}</h2>
<h3>${board}</h3>
<h3>${board.title}</h3>
<h3>${board.content}</h3>
</body>
</html>
</syntaxhighlight>
 
=== redirect 명령어 ===
Controller에서 주소 값을 return 하면 기본적으로 RequestDispatcher 객체의 forward() 함수로 페이지가 이동이 된다. 즉, URL 주소가 이동한 페이지로 바뀌지 않는다. URL 주소를 바꾸러면 HttpServletResponse 객체의 sendRedirect() 함수를 호출해야 한다. 이를 위해서는 "redirect:" 를 추가하면 된다. 아래 소스 코드를 참고하자.
 
<syntaxhighlight lang="java" line="1">
// 글 등록
@RequestMapping("/insertBoard.do")
public String insertBoard(BoardVO vo) {
System.out.println("BoardController의 insertBoard() 호출");
// 서비스 호출
service.insertBoard(vo);
 
// 페이지 이동
return "redirect:getBoardList.do";
}
</syntaxhighlight>
 
=== 홈페이지 URL 변경하기 ===
[[파일:Apache-tomecat-url.png|가운데|프레임없음|1200x1200픽셀|톰캣에서 Root 주소 변경하기]]
 
=== @RequestMapping 옵션 ===
value, method 값을 별도로 줌으로써 HTTP 요청 Method를 구별할 수 있다.
 
* @RequestMapping(value="주소", method=RequestMethod.{GET, POST, ...})
 
 
아래 코드를 참고하자.<syntaxhighlight lang="java" line="1">
// (핸들러 맵핑) 로그인 GET - 페이지 출력
@RequestMapping(value="/login.do", method=RequestMethod.GET)
public String loginForm() {
return "login.jsp";
}
// (핸들러 맵핑) 로그인 POST - 처리
@RequestMapping(value="/login.do", method=RequestMethod.POST)
public String login(UserVO vo, HttpSession session) {
System.out.println("UserController의 getUser() 호출");
 
// 서비스 호출
UserVO user = service.getUser(vo);
 
if (user != null) {
// 로그인 작업
session.setAttribute("user", user);
session.setAttribute("session", session.getId());
return "redirect:getBoardList.do";
} else {
return "login.jsp";
}
}
</syntaxhighlight>
 
=== Mybatis ===
 
==== 정의 ====
한두 줄의 자바 코드로 DB 연동을 처리하는 JDBC 프레임워크다.
 
==== 구성 ====
 
# 하나의 환경설정 파일
# 여러 개의 SQL Mapper 파일
 
=== 특징 ===
Mybatis는 DAO에 쿼리가 나오지 않는다. XML Mapper 파일에서 관리한다.
 
==== Mapper 파일 ====
Spring JDBC와는 다르게 별도 파일에 SQL을 관리하며 이를 Mapper 파일이라고 한다.
 
==== pom.xml 파일 ====
수업에서는 3.3.1 버전을 사용했다. 아래 코드에서 mybatis는 Mybatis 프레임워크 자체이며 mybatis-spring은 Spring Framework와 연동하기 위한 라이브러리다.
 
<syntaxhighlight lang="xml" line="1">
<!-- Mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
mybatis
<version>3.3.1</version>
</dependency>
 
<dependency>
<groupId>org.mybatis</groupId>
mybatis-spring
<version>1.2.4</version>
</dependency>
</syntaxhighlight>
 
==== 스프링 연동 설정 ====
스프링과 Mybatis를 연동하려면 우선 스프링 설정 파일에 SqlSessionFactoryBean 클래스를 Bean으로 등록해야 한다. 그래야 SqlSessionFactoryBean 객체로부터 DB 연동 구현에 사용할 SqlSession 객체를 얻을 수 있다.<syntaxhighlight lang="xml" line="1">
...
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- Mybatis 설정 -->
<bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:sql-map-config.xml" />
</bean>
<bean class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg ref="sessionFactory"></constructor-arg>
</bean>
...
</syntaxhighlight>
 
 
Spring JDBC는 Spring Framework의 일부이기 때문에 이러한 스프림 프레임워크와의 연동 설정이 불필요하다.
 
==== SqlSessionFactory 클래스 ====
SqlSessionFactory는 이름에서 알 수 있듯이 SqlSession 객체에 대한 공장 역할을 한다. 즉, Factory Pattern을 사용한다. 이 객체는 openSession()이라는 메소드를 제공하며, 이 메소드를 이용해서 SqlSession 객체를 얻을 수 있다.
 
=== HttpMessageConverter ===
HTTP 요청에 대한 응답 결과를 HTML과 같은 웹 페이지가 아니라 JSON이나 XML과 같은 데이터로 변환하여 메시지 보디에 저장하려면 스프링에서 제공하는 변환기(converter)를 사용해야 한다.
 
 
스프링은 HttpMessageConverter를 구현한 다양한 변환기를 제공하는데, 이 변환기를 이용하면 자바 객체를 다양한 타입으로 변환하여 HTTP 응답 보디에 설정할 수 있다. 자바 객체를 JSON 응답 보디로 변환할 때는 MappingJackson2HttpMessageConverter를 사용한다.
 
=== @RestController 어노테이션 ===
@Controller는 요청에 대해 일반적인 응답을 제공할 때 사용하지만, JSON, XML 같은 데이터로 응답을 할 때는 @RestController를 사용한다. 이 어노테이션을 사용하려면 presentation-layer.xml에 아래와 같이 Namespaces에 mvc를 추가하고 <mvc:annotation-driven /> 태그를 추가해야 한다. 아래 코드 기준으로 5번째 줄, 13번째 줄에 해당한다.<syntaxhighlight lang="xml" line="1">
<?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
 
<context:component-scan base-package="com.springbook.biz.board.controller" />
<context:component-scan base-package="com.springbook.biz.user.controller" />
 
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
</syntaxhighlight>


= 강의자료 =
= 강의자료 =
1,084번째 줄: 1,653번째 줄:
= 참고자료 =
= 참고자료 =


* Maven Repository - https://mvnrepository.com/
* Spring Framework GitHub - https://github.com/spring-projects/spring-framework
*Javadoc for Class DispatcherServlet - https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/DispatcherServlet.html
*AOP 서비스 설명(eGovFramework) - https://www.egovframe.go.kr/wiki/doku.php?id=egovframework:rte2:fdl:aop#:~:text=%ED%9A%A1%EB%8B%A8%20%EA%B4%80%EC%8B%AC%EC%82%AC(Cross%2Dcutting%20concern,%EC%B2%98%EB%A6%AC%20%EC%98%81%EC%97%AD%EC%9D%B4%20%ED%95%B4%EB%8B%B9%EB%90%9C%EB%8B%A4.
*Maven Repository - https://mvnrepository.com/
*Spring Tools - https://spring.io/tools
*Spring Tools - https://spring.io/tools
*Java Language and Virtual Machine Specifications - https://docs.oracle.com/javase/specs/index.html
*Java Language and Virtual Machine Specifications - https://docs.oracle.com/javase/specs/index.html
1,094번째 줄: 1,666번째 줄:
*Java Downloads - https://www.oracle.com/kr/java/technologies/downloads/
*Java Downloads - https://www.oracle.com/kr/java/technologies/downloads/
*Spring API - https://docs.spring.io/spring/docs/4.3.8.RELEASE/javadoc-api/
*Spring API - https://docs.spring.io/spring/docs/4.3.8.RELEASE/javadoc-api/
* Mybatis - <nowiki>http://www.mybatis.org/mybatis-3/apidocs/reference/org/apache/ibatis/session/SqlSession.html</nowiki>
* Mybatis - http://www.mybatis.org/mybatis-3/apidocs/reference/org/apache/ibatis/session/SqlSession.html
* Mybatis-Spring - http://www.mybatis.org/spring/apidocs/
* Mybatis-Spring - http://www.mybatis.org/spring/apidocs/
* Mybatis Mapper XML Files  - http://www.mybatis.org/mybatis-3/sqlmap-xml.html
* Mybatis Mapper XML Files  - http://www.mybatis.org/mybatis-3/sqlmap-xml.html
1,101번째 줄: 1,673번째 줄:
* CSS - https://www.kobzarev.com/wp-content/uploads/cheatsheets/css/css3-cheat-sheet.pdf / https://developer.mozilla.org/ko/docs/Web/CSS/Css_Reference
* CSS - https://www.kobzarev.com/wp-content/uploads/cheatsheets/css/css3-cheat-sheet.pdf / https://developer.mozilla.org/ko/docs/Web/CSS/Css_Reference
* jQuery - https://oscarotero.com/jquery/
* jQuery - https://oscarotero.com/jquery/
[[분류:한국생산성본부]]
[[분류:생산성본부]]
[[분류:생산성본부]]
[[분류:KPC]]
[[분류:KPC]]
[[분류:35시간]]
[[분류:35시간]]
[[분류:Spring]]
[[분류:Spring Framework]]
[[분류:스프링]]
[[분류:스프링 프레임워크]]
[[분류:웹 프로그래밍]]
[[분류:웹 개발]]
[[분류:한국생산성본부]]
[[분류:SpringIoC]]
[[분류:SpringAOP]]
[[분류:SpringMVC]]
[[분류:Mybatis]]

2023년 5월 19일 (금) 16:58 기준 최신판

1 강사님[편집 | 원본 편집]

오정임 purum01@naver.com

2 목표[편집 | 원본 편집]

  • 스프링 프레임워크 이해
    • Spring Framework 은 결국 JSP & Servlet을 더 효율적으로 사용하도록 돕는 도구
  • 웹 시스템 유지보수 및 개발역량 함양

3 수업방식[편집 | 원본 편집]

  • 100% 실습


※ (주의사항) Java 기본문법을 알고 JSP & Servlet(=Web)에 대한 기본이해가 있다는 것을 전제로 하고 수업을 함

4 커리큘럼[편집 | 원본 편집]

교육일자 회차 내용 비고
2023.5.15(월) 1일 JSP &Servlet(=Web) 이해 Spring Frameowrk 선수과정
2023.5.16(화) 2일 Cookie, Session, Filter, Listener, DB, JSP 태그, MVC
2023.5.17(수) 3일 Bean, MVC 복습, IoC 컨테이너, Bean, Dependency Injection(DI), Annotation, SpringMVP, SpringAOP Spring Framework 수업 시작
2023.5.18(목) 4일 AOP 실습, Spring JDBC, DB Connection Pool, JNDI, 트랜잭션, SpringMVC
2023.5.19(금) 5일 SpringMVC, Mybatis, Single Page Application, 동기·비동기 통신, JSON

5 필기노트[편집 | 원본 편집]

5.1 1일차[편집 | 원본 편집]

5.1.1 Java SE vs. Java EE[편집 | 원본 편집]

자바 언어를 활용한 개발은 크게 두 가지로 나뉜다.

  • Java SE : Standalone 프로그램 개발 시 활용
  • Java EE : 다른 프로그램(컨테이너, 엔진, 서버)에 의존해서 실행하는 프로그램 개발 시 활용 (예: JSP, Servlet)


정확히 말하자면 자바 언어를 활용한 개발은 정확히는 크게 네 가지로 나뉜다.

Differences between Java EE and Java SE


Java technology is both a programming language and a platform. The Java programming language is a high-level object-oriented language that has a particular syntax and style. A Java platform is a particular environment in which Java programming language applications run.


There are several Java platforms. Many developers, even long-time Java programming language developers, do not understand how the different platforms relate to each other.


The Java Programming Language Platforms


There are four platforms of the Java programming language:

  • Java Platform, Standard Edition (Java SE)
  • Java Platform, Enterprise Edition (Java EE)
  • Java Platform, Micro Edition (Java ME)
  • JavaFX


All Java platforms consist of a Java Virtual Machine (VM) and an application programming interface (API). The Java Virtual Machine is a program, for a particular hardware and software platform, that runs Java technology applications. An API is a collection of software components that you can use to create other software components or applications. Each Java platform provides a virtual machine and an API, and this allows applications written for that platform to run on any compatible system with all the advantages of the Java programming language: platform-independence, power, stability, ease-of-development, and security.


Java SE

When most people think of the Java programming language, they think of the Java SE API. Java SE's API provides the core functionality of the Java programming language. It defines everything from the basic types and objects of the Java programming language to high-level classes that are used for networking, security, database access, graphical user interface (GUI) development, and XML parsing.

In addition to the core API, the Java SE platform consists of a virtual machine, development tools, deployment technologies, and other class libraries and toolkits commonly used in Java technology applications.


Java EE

The Java EE platform is built on top of the Java SE platform. The Java EE platform provides an API and runtime environment for developing and running large-scale, multi-tiered, scalable, reliable, and secure network applications.


Java ME

The Java ME platform provides an API and a small-footprint virtual machine for running Java programming language applications on small devices, like mobile phones. The API is a subset of the Java SE API, along with special class libraries useful for small device application development. Java ME applications are often clients of Java EE platform services.


JavaFX

JavaFX is a platform for creating rich internet applications using a lightweight user-interface API. JavaFX applications use hardware-accelerated graphics and media engines to take advantage of higher-performance clients and a modern look-and-feel as well as high-level APIs for connecting to networked data sources. JavaFX applications may be clients of Java EE platform services.

ChatGPT는 Java SE 와 JavaEE 차이점을 아래와 같이 대답했다.

What are the differences between Java SE and Jave EE?

5.1.2 WAS[편집 | 원본 편집]

WAS는 Web Application Server으로 JSP, Servlet을 처리하는 미들웨어 소프트웨어다. 우리는 수업 때, 전통적으로 많이 쓰이는 Tomcat 중 버전 8.5를 쓴다. JDK는 8을 쓴다. ChatGPT 설명을 참고하면 좀 더 이해가 쉽다. 쉽게 말해서 WAS는 웹 서버와 애플리케이션 서버를 연결하는 역할을 한다. WAS가 애플리케이션 서버는 아니다.

ChatGPT-Answer-02.png

5.1.3 JDK[편집 | 원본 편집]

Sun 시절에는 JDK 8 버전까지는 오픈소스이나 그 이후부터는 Oracle 에서 라이선스 구독이 필요하다. 그러나 무료로 사용할 수 없는 것은 아니다. JDK는 OracleJDK, OpenJDK로 나뉜다. 이름에서 알 수 있듯이 OpenJDK는 무료다. OpenJDK의 종류로는 Azul 등이 있음.


참고로 JDK와 Tomcat 버전은 동일하게 맞춰야 제대로 작동한다. 정확히는 Tomcat 의 아래 표 중 Supported Java Versions를 살펴본 후, Tomcat을 설치해야 한다.

Apache Tomcat Versions.png

Oracle10gExpressEdition

5.1.4 개발환경 구성[편집 | 원본 편집]

  1. JDK 설치 (JDK 1.8.0_91)
  2. WAS 설치 (Tomcat 8.5)
  3. IDE 설치 (Eclipse Java EE Neon 2)
  4. Eclipse Plugin 설치 (OJDBC)
  5. DB 설치 (Oracle 10g DB Express Ediition)

5.1.5 web.xml 관련[편집 | 원본 편집]

WEB-INF/web.xml 파일은 컨테이너 환경설정 파일이다. Servlet 2.5 에는 항상 환경설정을 web.xml 에서만 해야했음. 그러나 Servlet 3.0 부터는 소스코드에 Annotation을 추가함으로써 환경설정을 추가할 수 있는 기능을 제공했다.

5.1.6 context[편집 | 원본 편집]

웹 애플리케이션 이름이라고 생각하면 됨

5.1.7 WEB-INF 디렉토리[편집 | 원본 편집]

  • 기본적으로 애플리케이션 서버는 Context Root d아래, WEB-INF 이하 classes 에서 *.class 파일을, lib 에서 *.jar 파일을 찾음.
  • Maven, Gradle 과 같은 패키지 관리 도구는 필요한 패키지(*.jar)를 lib 폴더에 자동으로 다운로드 및 복사를 함.

5.1.8 서블릿[편집 | 원본 편집]

웹에서 요청해서 실행할 수 있는 유일한 자바 프로그램을 서블릿이라고 한다. 서블릿은 아래와 같은 특성을 갖는다.

  1. 반드시 javax.servlet.http.HttpServlet 상속

(Lunch Time)


javax.servlet 과 javax.servlet.http 기억하기


컨테이너가 최초 호출이 되면 아래와 같은 일이 일어남.

  1. 객체 생성
  2. init() 호출
  3. HttpServletRequest(①), HttpServletRespons(②) 객체 생성
  4. service(①, ②)


두 번째 부터는 아래와 같은 일만 일어남.

  1. HttpServletRequest(①), HttpServletRespons(②) 객체 생성
  2. service(①, ②)


HttpServlet은 GenericServlet을 상속하고 있음. init() 함수는 GenericServlet에 있고, service() 함수는 HttpServlet에 있다.

5.1.9 서블릿 경로 만들기[편집 | 원본 편집]

  1. web.xml 수정
  2. 어노테이션 @WebServlet("경로") 사용


<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">

  <display-name>edu</display-name>

  <servlet>

  <servlet-name>first</servlet-name>

  <servlet-class>com.edu.test.FirstServlet</servlet-class>

  </servlet>

 

  <servlet-mapping>

  <servlet-name>first</servlet-name>

  <url-pattern>/exam01</url-pattern>

</servlet-mapping>

</web-app>

5.1.10 Tomcat의 GET 방식 한글처리[편집 | 원본 편집]

server.xml 파일 내 <Connector> 태그의 속성으로 URIEncoding="UTF-8" 라는 코드와 같이 클라이언트와 동일하게 맞추면 된다. 참고로 기본값은 UTF-8 이다.

5.1.11 Servlet 저장소[편집 | 원본 편집]

  1. ServletContext
  2. HttpSession
  3. HttpServletRequest


데이터 저장과 적재를 위해서는 아래 두 함수를 사용한다.

  • (저장) void setAttribution(String name, Object value)
  • (추출) Object getAttribute(String name)
  • (삭제) removeAttribute(String name)
저장소명 사용범위 생성시점 삭제시점 생성개수 용도
ServletContext 하나의 애플리케이션 서버 시작 시 서버 종료 시 웹 애플리케이션 수(=1개) Application 단위로 데이터 공유,

Application 변수 사용

HttpSession 하나의 클라이언트 클라이언트(=브라우저) 접속 시


HttpServletRequest 객체

- getSession()

- getSession(boolean)

1. 클라이언트(=브라우저 등) 종료 시


2. HttpSession.invalidate() 호출

클라이언트 수 로그인, 장바구니
HttpServletRequest 하나의 요청 요청 시 응답 시 요청 수만큼 일반적인 요청 및 응답 (가장 많이 쓰임)


<init-param>은 변수를 선언하는 용도이며 web.xml 안에서 정의할 수 있다. <servlet> 안에서 지정한 <init-param>은 FirstServlet 에서만 접근이 가능하다. <web-app> 안에서 지정한 <context-param>은 전체 애플리케이션에서 접근할 수 있다.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
  <display-name>edu</display-name>
  <context-param>
  	<param-name>config</param-name>
  	<param-value>layout.xml</param-value>
  </context-param>
  
  <servlet>
	  <servlet-name>first</servlet-name>
	  <servlet-class>com.edu.test.FirstServlet</servlet-class>
	  <init-param>
	  	<param-name>code</param-name>
	  	<param-value>utf-8</param-value>	  
	  </init-param>
  </servlet>
  
  <servlet-mapping>
  	<servlet-name>first</servlet-name>
  	<url-pattern>/exam01</url-pattern>
  </servlet-mapping>
</web-app>


<param-name>이 변수명이고 <param-value>는 값이다. 아래와 같은 코드로 변수 값을 가져올 수 있다.

package com.edu.test;

import java.io.IOException;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class FirstServlet extends HttpServlet {
	@Override
	public void init(ServletConfig config) throws ServletException {
		String code = config.getInitParameter("code");
		ServletContext sc = config.getServletContext();
		System.out.println("FirstServlet's init() 요청" + " : init-param 'code' = " + code + " : " + "context-param 'config' = " + sc.getInitParameter("config"));
		
	}
	
	@Override
	protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		System.out.println("FirstServlet's service() 요청");		
	}
}

5.1.12 페이지 이동[편집 | 원본 편집]

RequestDispatcher 클래스는 페이지 이동 시 이전 페이지의 request, response를 그대로 사용할 때 사용한다. 그런데 여기에는 두 개 메소드가 있다.

  • RequestDispatcher.forward(req, resp) : 이동하고 다시 돌아오지 않음 (이 때 요청 URL은 변경되지 않음)
  • RequestDispatcher.inclue(req, resp) : 이동했다가 다시 돌아옴


※ include는 최초 요청받은 페이지에서 응답을 보낼 때 사용하고, forward는 이동한 페이지에서 응답을 보내고자 할 때 사용

RequestDispatcher 객체는 HttpServletRequest.getRequestDispatcher() 함수를 통해 얻을 수 있다.

페이지 이동 시 새로운 request, response를 생성할 때 사용한다.

  • HttpServletResponse.sendRedirect(path) : 이전 페이지의 request, response를 사용하지 않음. (만약, 내가 요청한 페이지의 URL이 변경되었다면 sendRedirect() 메소드를 사용한 것이다.)

5.2 2일차[편집 | 원본 편집]

스프링 프레임워크에서는 페이지 이동 시 기본적으로 RequestDispatcher 객체를 사용한다.

5.2.1 데이터 관리[편집 | 원본 편집]

클라이언트 단위로 데이터를 유지하는 방법은 아래와 같다.

구분 저장위치 저장형태 보안 사용방법
HttpSession 서버 Object (쿠키에 비해) 매우 좋음
Cookie 클라이언트 text (name=value) 매우 취약 - (생성) new Cookie(name, value)

- (전송) void HttpServletResponse.addCookie(Cookie)

- (추출) Cookie[] HttpServletRequest.getCookies()

5.2.2 쿠키[편집 | 원본 편집]

아래 코드는 쿠키를 생성하고 응답 객체인 HttpServletResponse에 쿠키를 추가한다.

package com.edu.test;

import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;

@WebServlet("/cookie1")
public class CookieTest1 extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		resp.setContentType("text/html;charset=UTF-8");
		PrintWriter out = resp.getWriter();		
		out.println("<h1>Cookie 전송</h1>");
		
		Cookie c1 = new Cookie("code", "Java");
		resp.addCookie(c1);
		
		Cookie c2 = new Cookie("city", "Seattle");
		resp.addCookie(c2);		
		c2.setMaxAge(60*60*3); // 유효시간 설정 : 양수(초), 음수(세션과 동일), 0(삭제), default(세션)
		resp.addCookie(c2);
		
		out.close();
	}
}

5.2.3 세션[편집 | 원본 편집]

HttpServletRequest 객체의 getSession() 혹은 getSession(true)을 호출하면 세션 ID 가 생성이 된다. 참고로 getSession(false)와 getSession(true)의 차이점은 다음과 같다.

  • HttpServletRequest.getSession(true) : 세션 ID가 없으면 생성해서 리턴한다.
  • HttpServletRequest.getSession(false) : 세션 ID가 없으면 null을 리턴한다.


세션 객체 생성 및 삭제, 세션 내 데이터 저장 및 삭제 등은 아래 코드를 통해 수행이 가능하다.

package com.edu.test;

import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;

@WebServlet("/session")
public class SessionTest extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		String param = req.getParameter("p");
		
		HttpSession session = null;
		String msg = null;
				
		resp.setContentType("text/html;charset=UTF-8");
		PrintWriter out = resp.getWriter();		
//		out.println("<h1>HttpSession 테스트</h1>");
//		out.println("<h3>" + param + "</h3>");
//		out.close();
		
		if (param.equals("create")) {
			session = req.getSession();
			
			if(session.isNew()) {
				msg = "새로운 세션 객체가 생성되었습니다. " + session.getId();
			} else {
				msg = "기존 세션 객체가 반환되었습니다. " + session.getId();
			}
		} else if (param.equals("destory")) {
			session = req.getSession(false);
			if (session == null) {
				msg = "삭제할 세션 객체가 없습니다.";	
			} else {				
				msg = "세션 객체를 삭제했습니다. " + session.getId();
				session.invalidate();
			}
		} else if (param.equals("add")) {
			session = req.getSession(true);
			session.setAttribute("id", "guest");
			msg = "세션에 id 등록을 완료했습니다. " + session.getAttribute("id");
		} else if (param.equals("get")) {
			session = req.getSession(false);
			
			if (session != null) {
				String id = (String)session.getAttribute("id");
				msg = "세션에 등록한 id 객체의 값을 가져왔습니다. " + session.getAttribute("id");
			} else {
				msg = "세션이 존재하지 않습니다.";
			}
			
		} else if (param.equals("remove")) {
			session = req.getSession(false);
			
			if (session != null) {				
				msg = "세션에 등록한 id 객체의 값을 삭제했습니다. " + session.getId() + " : " + session.getAttribute("id");
				session.removeAttribute("id");
			} else {
				msg = "세션이 존재하지 않습니다.";
			}
		}
		
		out.println("<h3>" + msg + "</h3>");
	}
}

5.2.4 Filter 와 Listener[편집 | 원본 편집]

5.2.4.1 Filter[편집 | 원본 편집]

Filter는 요청을 처리하는 서비스 전(=request 필터) 혹은 후(=response 필터)에 항상 실행해야 하는 기능을 추가할 때 사용하며, 아래와 같은 절차를 통해 수행할 수 있다.

  1. Filter 생성 - Filter 인터페이스를 구현 (... implements Filter)
  2. Filter 서버 등록
  3. Filter 맵핑

request 필터는 인증, 로깅, 감사, 인코딩 등으로 사용하고, response 필터는 데이터 압축, 총 서비스 시간 측정 등으로 사용한다..


아래는 Filter 생성 및 삭제 관련 함수다.

함수명 용도 시점
init() 필터 객체 생성 시 서버 시작 시
doFilter() 매핑 페이지 요청 시
destory() 필터 객체 삭제 시 서버 종료 시

Filter 등록은 web.xml 에 하며 아래와 같은 형식으로 하거나 Filter 클래스에 어노테이션으로 @WebFilter("/*"); 등을 추가해서 설정할 수 있다.

...

<filter>
    <filter-name>필터이름</filter-name>
    <filter-class>클래스 경로</filter-class>
</filter>

...

Filter 생성은 Spring Framework에서 자동으로 하기 때문에 개발자는 2,3번만 직접 수행하면 된다. 그리고 Filter 맵핑은 아래와 같이 하면 된다.

...

<filter-mapping>
    <filter-name>필터이름</filter-name>
    <url-pattern>요청 URL</url-pattern>
</filter-mapping>

...


Filter를 시각화하면 아래와 같다.

Filter 클래스

5.2.4.2 Listener[편집 | 원본 편집]

~Listener 클래스가 있다면 아래와 같은 특성이 있다고 기억하자.

  1. 인터페이스
  2. 콜백(callback) 메소드


Javadoc에 보면 아래와 같은 인터페이스를 볼 수 있다.

  • HttpSessionActivationListener
  • HttpSessionAttributeListener
  • HttpSessionBindingListener
  • HttpSessionIdListener
  • HttpSessionListener
  • ServletContextListener


어디서 어떤 상황이냐? ~Listener 앞에 있는 명칭 통해 유추 가능.

ChatGPT의 자바 웹 기술의 Filter와 Listener 설명


아래는 Listener 객체를 사용하기 위한 코드다.

package com.edu.test;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class MyListener implements ServletContextListener {

	@Override
	public void contextDestroyed(ServletContextEvent arg0) {
		System.out.println("MyListener's contextDestroyed() 호출!");
	}

	@Override
	public void contextInitialized(ServletContextEvent arg0) {
		System.out.println("MyListener's contextInitialized() 호출!");
	}
}


Listener를 사용하려면 web.xml에 관련 설정 값을 추가해야한다. 아래 코드 중 <listener> ... </listener> 부분을 추가하면 된다.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
  <display-name>edu</display-name>
  <context-param>
  	<param-name>config</param-name>
  	<param-value>layout.xml</param-value>
  </context-param>

  <listener>
  	<listener-class>com.edu.test.MyListener</listener-class>
  </listener>

  <filter>
    <filter-name>f1</filter-name>
    <filter-class>com.edu.test.MyFilter</filter-class>
  </filter>
  
  <filter-mapping>
    <filter-name>f1</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  
  <servlet>
	  <servlet-name>first</servlet-name>
	  <servlet-class>com.edu.test.FirstServlet</servlet-class>
	  <init-param>
	  	<param-name>code</param-name>
	  	<param-value>utf-8</param-value>	  
	  </init-param>
  </servlet>
  
  <servlet-mapping>
  	<servlet-name>first</servlet-name>
  	<url-pattern>/exam01</url-pattern>
  </servlet-mapping>
</web-app>
Filter-Listener-JSP.png

5.2.5 데이터베이스[편집 | 원본 편집]

책에서는 H2 DB를 사용하지만 수업에서는 Oracle 10g Express Edition을 사용한다. 관리자 계정은 sys이며 암호는 DB 설치 시 설정했던 admin123으로 로그인한다. 웹사이트 주소는 http://127.0.0.1:8080/apex 이다.

5.2.5.1 사용절차[편집 | 원본 편집]

자바에서 DB 처리는 java.sql 패키지에 있는 라이브러리를 사용한다. 여기의 라이브러리는 모두 인터페이스다. 실제 구현체는 각 DB 업체에서 제공하는 JDBC 드라이버에 있다. DB 처리는 아래와 같은 순서대로 수행한다.

  1. JDBC Driver 파일 준비 (로딩)
  2. DB 서버 접속하기
  3. Statement or PreparedStatement 객체 생성하기
  4. SQL 문 실행하기
  5. 자원 해제하기

5.2.5.2 예제코드[편집 | 원본 편집]

아래는 DB에 접속해서 테이블을 추가하는 예제 코드다.

package com.edu.test;

import java.sql.*;

public class DBTest {
	public static void main(String[] args) {
		Connection conn = null;
		String id = "samoyed7";
		String pwd = "milk7";
		
		try {
			// 1. JDBC Driver 파일 준비 (로딩)
			Class.forName("oracle.jdbc.driver.OracleDriver");
			// 2. DB 서버 접속하기
			conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe", "scott", "tiger");
			// 3. Statement or PreparedStatement 객체 생성하기
//			Statement stmt = conn.createStatement();
			PreparedStatement pstmt = conn.prepareStatement("INSERT INTO TEST VALUES(?,?)");
			pstmt.setString(1, id);
			pstmt.setString(2, pwd);
			pstmt.executeUpdate();
			// 4. SQL 문 실행하기
//			stmt.executeUpdate("CREATE TABLE TEST(ID VARCHAR2(20) PRIMARY KEY, PWD VARCHAR2(20))");
//			stmt.executeUpdate("INSERT INTO TEST VALUES('happydog','11')");
//			stmt.executeUpdate("INSERT INTO TEST VALUES('ryanyang','22')");

//			ResultSet rs = stmt.executeQuery("SELECT * FROM TEST");
			pstmt = conn.prepareStatement("SELECT * FROM TEST");
			ResultSet rs = pstmt.executeQuery();
			while (rs.next()) {
				System.out.println(rs.getString(1) + " : " + rs.getString(2));
			}
			
			// 5. 자원 해제하기
			rs.close();
//			stmt.close();
			pstmt.close();
			conn.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		System.out.println("Okay : " + conn);
	}
}

5.2.5.3 Auto Commit 설정[편집 | 원본 편집]

Connection 객체의 setAutoCommit(boolean) 메소드를 통해 Auto Commit 옵션을 설정할 수 있다. 기본값은 Auto Commit 이다.

5.2.6 JSP Container[편집 | 원본 편집]

JSP Container는 jsp 를 java로 변환한 후 class 파일로 컴파일을 한다.

5.2.6.1 JSP 태그[편집 | 원본 편집]

  • <% ... %> ← service() 메소드에 들어가는 코드
  • <%= ... %> ← service() 메소드에 out.print()로 들어가는 코드
  • <%@ ... %> ← package 정의 및 import 관련 코드로 변환이 됨
  • <%! ... %> ← 멤버변수 및 멤버함수 정의하는 코드로 변환이 됨 (즉, class { ... } 사이에 들어감


모든 JSP 코드는 java 코드로 변환이 되는데 이 때 항상 아래 세 가지 객체가 암묵적으로 추가된다.

  • HttpServlet Request request = ...
  • HttpServlet Response response = ...
  • HttpSession session = ...


위에서 진하게 표기한 변수명으로 각 객체에 접근할 수 있다.

<%@ page contentType="text/html; charset=UTF-8" import="java.util.*, java.io.*" %>

<h1>Test</h1>
<h1>스프링</h1>

<% Date d = new Date(); %>

<%= d %>

<%
	request.setAttribute("msg", "Hello JSP 테스트 메시지입니다.");
	RequestDispatcher rd = request.getRequestDispatcher("test2.jsp");
	rd.forward(request, response);
%>

5.2.6.2 XML 태그[편집 | 원본 편집]

  • 표준 액션 태그
  • 사용자 태그


사용자 태그의 경우 <태그라이브러리 : 태그이름> 으로 사용할 수 있는데 Spring Framework 에서는 <spring : 태그이름> 과 같이 사용할 수 있다. 아래는 JSP 코드 예제이다.

5.2.6.3 JSP Expression Language(EL)[편집 | 원본 편집]

EL의 경우 request.getAttribute("xxx")로 데이터를 찾을 때, 가장 먼저 ServletRequest에서 해당 값을 찾는다. 만약에 여기에 없다면 Session에서 해당 값을 찾는다. 만약 여기에도 없다면 ServletContext에서 "xxx"에 해당하는 데이터를 찾는다.

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%
	String v = (String)request.getAttribute("msg");
%>

<%= v %>

<hr/>

${ msg }

5.2.7 MVC 디자인 패턴[편집 | 원본 편집]

  • Model, View, Controller 패턴으로 웹 개발을 많이 함. Model-View-ViewModel 혹은 MVVM 이라는 패턴도 많이 씀.
  • edu_MVC.zip 파일을 Eclipse에 존재하는 프로젝트로 import를 한 후, 관련 코드를 작성함.
  • MVC를 구현하면 자연스럽게 3-Tier Architecture가 구현됨

5.2.7.1 View[편집 | 원본 편집]

클라이언트가 보는 화면으로 HTML, CSS, JavaScript, JSP, VueJS, React 등이 있음

5.2.7.2 Model[편집 | 원본 편집]

VO(Value Object) 혹은 DTO(Data Transfer Object)로 Getter, Setter 그리고 toString()을 구현한다.

5.2.7.3 Controller[편집 | 원본 편집]

비즈니스 로직을 처리하는 부분으로 아래와 같은 처리를 함 (우리가 학습한 예제 중심으로 설명)

  1. Parameter 추출 (필요 시 유효성 검증)
  2. 서비스 메소드 호출
  3. View로 이동

5.2.7.4 Model1, Model2[편집 | 원본 편집]

MVC Model1 은 JSP에 View와 Controller가 혼용되어 있다. MVC Model2는 View는 JSP에서 Controller는 Servlet에서 처리한다.

5.2.7.5 Model : POJO[편집 | 원본 편집]

POJO("포조")라고 하는 Plain Old Java Object로 모델을 만든다. 그리고 모델의 객체는 통상 두 개로 나뉜다.

  1. 서비스 처리 객체
  2. DB 처리 객체(=DAO "다오", Data Access Object)

5.3 3일차[편집 | 원본 편집]

Spring Framework는 "IoC와 AOP를 지원하는 경량의 컨테이너 프레임워크"다. 오늘부터 Spring Framework를 본격적으로 학습함. 가장 먼저 STS를 설치했다.

5.3.1 MVC 복습[편집 | 원본 편집]

MVC는 3-Tire Architecture를 구현하도록 하는 시스템 아키텍처 패턴이다.

MVC구분 기능 처리흐름 처리구분 관련 프레임워크
View 클라이언트에게 보여지는 화면 1 (요청받음) Presentation SpringMVC
Controller 1. Parameter 추출

2. 서비스 메소드 호출

3. 출력부 이동

2 (요청확인 및 라우팅) Business SpringAOP
Model Service 객체 - 서비스 구현 3 (서비스 처리) Persistent SpringJDBC, MyBatis, SpringDataJPA(=ORM)
DAO 객체 - DB처리 4 (필요 시 DB 등 Data Source 조회)

3-Tier Architecture를 ChatGPT에게 설명을 요청했다.

ChatGPT's answer to what 3-tier architecture is

5.3.2 프레임워크(Framework)[편집 | 원본 편집]

5.3.2.1 장점 및 단점[편집 | 원본 편집]

Framework 은 반제품으로 ready-made 된 building blocks가 많아서 생산성이 높다. 그러나 이를 사용하려면 Framework을 알아야 한다.

5.3.2.2 프레임워크 예시[편집 | 원본 편집]

Presentation 영역의 프레임워크는 SpringMVC가 있다. SpringAOP는 Controller 영역의 프레임워크다. Model 영역의 프레임워크는 SpringJDBC, MyBatis, JPA가 있다. JPA의 경우, Java 객체만 만들면 SQL은 프레임워크가 만들어준다. JPA와 같은 프레임워크를 ORM 이라고 부른다. JPA는 ORM을 위한 자바 EE 표준이며 SpringDataJPA는 JPA를 쉽게 사용하기 위해 스프링에서 제공하고 있는 프레임워크다.

5.3.2.3 Spring Framework[편집 | 원본 편집]

결론적으로 Spring Framework 의 Architecture Diagram은 아래와 같다.

Spring Framework Architecture Diagram
Spring Framework Artifacts


ChatGPT의 Spring Framework 설명을 살펴보자.

ChatGPT's explanation on Spring Framwork

5.3.3 SQL 명령줄에서 Oracle DB 10g XE 접속[편집 | 원본 편집]

아래와 같이 conn 사용자명/암호 를 입력라면 DB에 접속이 된다.

Sql-명령줄-오라클.png

5.3.4 IoC 컨테이너[편집 | 원본 편집]

서블릿(Servlet)은 서블릿 컨테이너(Servlet Container)가 실행한다. 즉, 프로그램 흐름은 서블릿 컨테이너(Servlet Container)가 한다. 개발자가 할 수 없다. 개발자는 서블릿 컨테이너(Servlet Container)가 사전에 정한 프로세스를 준수해야 한다. 이러한 컨테이너를 IoC 컨테이너라고도 부름. 참고로 IoC는 Inversion of Control의 약자다.

5.3.5 Bean이란[편집 | 원본 편집]

Bean은 컨테이너가 관리하는 자바 객체다. 여기서 관리한다는 것은 객체를 생성하거나 삭제하는 일 그리고 메소드를 호출하는 일 등을 말한다. 스프링 프레임워크에서 Bean 을 사용하면 스프링 프레임워크가 Bean을 생성하고 초기화한다.


Bean 객체는 각 Layer별로 설정할 수 있다. 즉, Presentation, Business, Persistent 각각 설정이 가능하다. 개발을 하면 Bean 객체가 많이 늘어난다. 이 경우에는 <import> 태그를 통해 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:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

	<!--<bean id="tv" class="polymorphism.SamsungTV"></bean>-->

	<import resource="context-tv.xml"/>
</beans>


위에서 import 한 context-tv.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:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
		
	<bean id="tv" class="polymorphism.SamsungTV" init-method="init" destroy-method="destroy"></bean>
	
</beans>

Bean 객체 생성 시 lazy-init 속성을 주면 해당 bean을 미리 생성하지 않고, 클라이언트가 요청하는 시점에 생성한다. 결국, 메모리 관리를 더 효율적으로 할 수 있다. 아래와 같이 bean 객체에 lazy-init 속성을 "true"로 하면, 객체 생성을 요청하는 경우에만 init-method인 init()이 호출됨을 알 수 있다. lazy-init 속성의 기본값은 "false"이며 GenericXmlApplicationContext 객체가 생성이 될 때, 즉 스프링 컨테이너가 구동이 될 때, bean 객체를 생성한다.

<?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:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
		
	<bean id="tv" class="polymorphism.SamsungTV" init-method="init" destroy-method="destroy" lazy-init="true"></bean>
	
</beans>


그리고 scope 속성을 통해 생성하는 Bean을 singleton(기본값)으로 할지 아니면 요청하는 만큼 새로 생성할지(prototype)를 설정할 수 있다.

<?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:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
		
	<bean id="tv" class="polymorphism.SamsungTV" init-method="init" destroy-method="destroy" lazy-init="true" scope="prototype"></bean>
	
</beans>

5.3.6 이클립스 꿀팁[편집 | 원본 편집]

  • Ctr + -Alt + ↑ 또는 ↓ : 현재 선택한 줄이 화살표 방향에 맞게 그대로 복사가 된다.
  • Alt + Shift + T : 인터페이스 자동 생성
  • Alt + Shift + S : Getters 와 Setters 자동 생성
  • Ctrl + D : 현재 줄 삭제
  • Ctrl+Shift+O : 소스코드 내 관련 패키지 일괄 import

5.3.7 스프링 프레임워크가 해주는 일[편집 | 원본 편집]

  1. Inversion of Control 혹은 IoC라는 제어의 역행을 지원한다.
  2. Bean 객체를 자동으로 생성한다.
  3. 컨테이너가 생성한 객체를 검색하는 Dependency Lookup을 지원한다.
  4. Dependency Injection 혹은 DI라고 하는 의존성 주입을 통해 객체 사이의 의존관계를 자동으로 설정한다.
  5. AOP를 통해 관심 분리(separtion of concerns)를 하여 횡단 관심(crosscutting concerns)이 아닌 핵심 관심(core concerns)에 집중하게 한다.

5.3.8 인젝션 방법[편집 | 원본 편집]

인젝션은 멤버변수를 초기화하는 것을 의미한다. 인젝션생성자 인젝션Setter 인젝션 두 가지가 있다.

5.3.8.1 생성자 인젝션[편집 | 원본 편집]

아래와 같이 constructor-arg 태그를 활용한다.

<?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:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
		
	<bean id="tv" class="polymorphism.SamsungTV" init-method="init" destroy-method="destroy" lazy-init="true" scope="prototype">
		<constructor-arg index="0" ref="apple"></constructor-arg>
		<constructor-arg index="1" value="2700000"></constructor-arg>
	</bean>
		
</beans>

5.3.8.2 Setter 인젝션[편집 | 원본 편집]

Setter 인젝션의 경우 두 가지 방법으로 설정이 가능하다.

5.3.8.3 Property 태그 사용[편집 | 원본 편집]

아래와 같이 property 태그를 활용한다.

<?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:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
		
	<bean id="tv" class="polymorphism.SamsungTV" init-method="init" destroy-method="destroy" lazy-init="true" scope="prototype">
		<property name="speaker" ref="apple"></property>
		<property name="price" value="2700000"></property>
	</bean>
		
</beans>

5.3.8.4 p 네임스페이스 사용[편집 | 원본 편집]

네임스페이스만 적절히 선언하고 사용하면 된다. (상세 코드는 생략)

  • (참조형 변수) p:변수명-ref="참조할 객체의 이름이나 아이디"
  • (기본형 번수) p:변수명="설정할 값"

5.3.9 Annotation[편집 | 원본 편집]

스프링 프레임워크에서 의존성을 주입할 때, 앞에선느 XML 설정을 통해 처리했지만, 그 방법 외에 어노테이션(Annotation)을 통해서도 의존성 주입이 가능하다.

의존성 주입 방법 장점 단점
XML 1. 자바 소스 수정 불필요

2. 유지보수 간편

1. XML 설정 부담

2. XML 설정 해석 필요(소스 코드로는 객체 의존성 관계 확인 불가)

Annotation 1. XML 설정 부담 없음

2. 직관적 (의존관계에 대한 정보가 자바소스에 있음)

1. 자바 소스를 수정하지 않고서는 의존성 관계 수정 불가

2. 설정이 소스 코드 전체에 흩어져 있음

5.3.9.1 스프링 프레임워크가 추가로 제공하는 어노테이션[편집 | 원본 편집]

컨트롤러 객체는 @Controller, 서비스 개체는 @Service, DAO 객체는 @Repository로 등록을 한다. 스프링 프레임워크에서는 @Component를 상속하여 다음과 같은 세 개의 어노테이션을 추가로 제공한다.

어노테이션 위치 의미
@Service XXXServiceImpl 비즈니스 로직을 처리하는 Service 클래스
@Repository XXXDAO 데이터베이스 연동을 처리하는 DAO 클래스
@Controller XXXController 사용자 요청을 제어하는 Controller 클래스

5.3.10 DTO vs. VO[편집 | 원본 편집]

과거에는 실무에서 항상 Value Object, 즉 VO라고만 불렀으나 해당 VO가 여러 Layer를 돌아다니게 되면서 Data Transfer Object, 즉 DTO라고 부르게 되었다고 함. 그래서 현재 실무에서는 항상 DTO라고 부름. 학술적으로는 DTO와 VO가 다르지만, 실무에선느 혼용해서 사용했음. 과거에는 VO라고 했으나, 현재는 DTO라고 부름.


※ 정확한 설명은 아니지만 개념적으로 이렇게 이해해도 된다. "VO는 Setter가 존재하지 않는다"는 등의 설명이 있지만 실무적으로는 "동일하며, 모두 다 DTO다." 라고 생각하는 게 나을 수도 있다. (관련 링크 : https://mosei.tistory.com/entry/DTO-vs-VO)

5.3.11 AOP[편집 | 원본 편집]

Aspect Oriented Programming, 즉 AOP는 개발자가 공통 기능이 아니라 핵심 비즈니스에 자신의 관심사를 한정하도록 돕는다. 어떻게? 관심 분리(separation of concerns)를 통해서다. 로그, 트랜잭션, 오류 처리 등을 횡단 관심(crosscutting of concerns)이라고 한다. 비즈니스 로직은 핵심 관심(core concerns)이라고 한다.


AOP와 관련한 용어를 정리한다.

5.3.11.1 조인포인트(Joinpoint)[편집 | 원본 편집]

클라이언트가 호출하는 모든 비즈니스 메소드

5.3.11.2 포인트컷(Pointcut)[편집 | 원본 편집]

조인포인트가 적용되서 실행되는 비즈니스 메소드

5.3.11.3 어드바이스(Advice)[편집 | 원본 편집]

횡단 관심에 해당하는 공통 기능의 코드. 즉, 비즈니스 로직과 관계 없이 추가로 실행하는 메소드다.

5.3.11.4 위빙(Weaving)[편집 | 원본 편집]

포인트컷으로 지정한 핵심 메소드가 호출될 때, 어드바이스에 해당하는 횡단 관심 메소드가 삽입되는 과정을 의미. 이 위빙을 통해서 비즈니스 메소드를 수정하지 않고도 횡단 관심에 해당하는 기능을 추가하거나 변경할 수 있다.

5.3.11.5 애스팩트(Aspect) 또는 어드바이저(Advisor)[편집 | 원본 편집]

애스팩트(Aspect)는 포인트컷(pointcut)과 어드바이스(advice)의 결합. 어떤 포인트컷 메소드에 대해서 어떤 어드바이스 메소드를 실행할지 결정한다. 이 애스팩트 설정에 따라 AOP의 동작 방식이 결정되므로 AOP 용어 중에서 가장 중요한 개념이다.

5.4 4일차[편집 | 원본 편집]

5.4.1 3일차 학습 복습[편집 | 원본 편집]

포인트컷(pointcut)어드바이스(advice)를 연결하는 게 애스팩트(aspect)이다.

  • 포인트컷 : 핵심 관심
  • 어드바이스 : 횡단 관심
  • 애스팩트 : 횡단 관심과 핵심 관심 관계 설정

아래는 위에 대한 내용을 간단히 그림으로 정리한 내용이다.

Spring의 AOP 개념

참고로 AOP와 Filter는 다르다. Filter는 서블릿 단위에서 실행이 되고, Spring의 AOP는 Spring 영역, 즉 Application 영역에서 실행된다. 더 상세한 설명은 아래 링크를 참고하자.

Filter, Interceptor, AOP 흐름


어드바이스(advice)는 적용 시점에 따라 총 5개 종류가 있다.

  • Before : 비즈니스 메소드 실행 전
  • After
    • ... Returning : 비즈니스 메소드가 성공적으로 종료한 이후
    • ... Throwing : 비즈니스 메소드 실행 중 예외 발생 시 (try ~ catch 중 catch 블록)
    • ... After : 비즈니스 메소드 실행 후 (메소드 실행 결과에 관계 없이 항상 실행 / try-catch-finally 구문 중 finally에 해당)
  • Around : 메소드 실행 전·후

5.4.2 3-Tier Architecture와 Spring Framework[편집 | 원본 편집]

Spring Framework 레이어명 기능
SpringMVC Presentation View, Controller
SpringAOP Business Service
SpringJDBC (또는 MyBatis, JPA 활용) Persistent DAO(=JDBC)

5.4.3 Maven Repository[편집 | 원본 편집]

공식 사이트(https://mvnrepository.com/)에서 검색을 한 후, 아래와 같이 <dependency> ... </dependency> 코드를 pom.xml 등에 추가하면 된다.

메이븐 저장소 사용 예시


참고로 Maven 혹은 Gradle을 망분리 환경에서 사용하거나 내부 기업에서 사용 시 중앙 통제를 하고 싶으면 Nexus를 통해 이를 구축할 수 있다.

5.4.4 JointPoint[편집 | 원본 편집]

횡단 관심에 해당하는 어드바이스 메소드를 의미있게 구현하려면 클라이언트가 호출한 비즈니스 메소드의 정보가 필요하다. 이를 위해서 스프링은 org.aspectj.lang 패키지에 속한 JointPoint 인터페이스를 제공한다. 단, Around 어드바이스에서는 ProceedingJointPoint 객체를 사용해야 한다. ProceedingJoinPoint 객체는 비즈니스 메소드를 호출하는 proceed() 메소드를 가지고 있으며 JoinPoint를 상속했다. proceed() 호출을 통해 실제 호출할 메소드를 실행한다.

메소드 설명
Signature getSignature() 클라이언트가 호출한 메소드의 시그니처(리턴타입, 이름, 매개변수) 정보가 저장된 Signature 객체 리턴
Object getTarget() 클라이언트가 호출한 비즈니스 메소드를 포함하는 비즈니스 객체 리턴
Object[] getArgs() 클라이언트가 메소드를 호출할 때 넘겨준 인자 목록을 Object 배열로 리턴

5.4.5 Spring AOP's Under the Hood[편집 | 원본 편집]

Spring AOP는 Java Reflection을 이용해서 내부적으로 구현이 되어있기 때문에 메소드만을 대상으로 할 수밖에 없습니다. 그 이상을 원한다면 AspectJ를 이용해서 Spring AOP가 아닌 Java AOP를 구현하셔야 합니다. (링크)

5.4.6 AOP에서 사용되는 용어 복습[편집 | 원본 편집]

  • Target: 어떤 대상에 부가 기능을 부여할 것인지
  • Advice: 어떤 부가 기능을 부여할 것인지
  • Join Point: 어디에 적용할 것인지
  • Point Cut: 실제 Advice가 적용될 시점을 의미. Spring AOP에서는 advice가 적용될 메서드를 선정.

5.4.7 어노테이션 기반 AOP[편집 | 원본 편집]

스프링 설정 파일(예: applicationContext.xml)에 엘리먼트를 선언해야 한다.

5.4.8 JNDI[편집 | 원본 편집]

JNDI(Java Naming and Directory Interface)디렉터리 서비스에서 제공하는 데이터 및 객체를 발견(discover)하고 참고(lookup) 하기 위한 자바 API다. 간단히 요약하자면 우리가 연결하고 싶은 데이터베이스의 DB Pool을 미리 Naming 시켜주는 방법 중 하나이다. 우리가 저장해놓은 WAS 의 데이터베이스 정보에 JNDI를 설정해 놓으면 웹 애플리케이션은 JNDI만 호출하면 간단해진다. (링크: https://go-coding.tistory.com/76)


DataSource 설정을 위해서 applicationContext.xml에 아래와 같이 Bean 객체를 생성한다.

...
<!-- DataSource 설정 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="org.h2.Driver" />
    <property name="url" value="jdbc:h2:tcp://localhost/~/test" />
    <property name="username" value="sa" />
    <property name="password" value="" />
</bean>
...


위 코드에서 property 는 setter 메소드를 호출한다 의미, name 은 호출할 메소드 이름, value는 설정하려는 값이다.


참고로 암호는 상기와 같이 xml 파일에 직접 넣지는 않고 database.properties와 같은 파일을 별도로 분리해서 보관한다. 이 파일은 아래와 같이 생겼다.

jdbc.driver = oracle.jdbc.driver.OracleDriver
jdbc.url = jdbc:oracle:thin:@localhost:1521:xe
jdbc.username = scott
jdbc.password = tiger


위 파일을 읽어들이기 위한 웹 애플리케이션 환경설정은 아래와 같이 하면 된다.

<?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:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

	<context:component-scan base-package="com.springbook.biz" />
	
	<context:property-placeholder location="classpath:database.properties" />

	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
		<property name="driverClassName" value="${jdbc.driver}" />
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
	</bean>
	
</beans>


참고로 OS 환경변수에 필요한 값을 설정하기도 한다. 여기서는 이에 대해서 더 다루지는 않는다.

5.4.9 XML의 ref 값[편집 | 원본 편집]

xml에서 ref는 Bean에서 설정한 이름인 id를 의미한다.

5.4.10 ORM 프레임워크, JPA[편집 | 원본 편집]

소스 코드에 SQL을 없애고자 한다면 ORM 프레임워크를 사용하면 되며, 대표적으로 JPA가 있다. 그런데 JDBC Template 보다는 어렵다.

5.4.11 query() vs. queryForObject()[편집 | 원본 편집]

ChatGPT에게 JdbcTemplate 객체의 query() 메소드와 queryForObject() 메소드 차이점을 물어봤다.

query() vs. queryForObject()

5.4.12 RowMapper[편집 | 원본 편집]

RowMapper 구현 관련 ChatGPT 답변

5.4.13 트랜잭션(Transaction)[편집 | 원본 편집]

트랜잭션 관리를 위해 처음부터 관련 코드를 작성하는 것은 비효율적이고 해당 코드가 실제로 잘 작동할지 보장하는 것도 어렵다. 그래서 스프링은 Transaction 처리를 위해 Transaction Manager를 제공한다. 다만, ... 문법을 사용하지 못한느데 그 이유는 사전에 method 명을 알 수 없기 때문이다. 그래서 Transaction Manager 사용을 이해 ... 문법을 추가했다.

5.4.13.1 선언적 트랜잭션[편집 | 원본 편집]

스프링에서도 EJB와 마찬가지로 트랜잭션 처리를 컨테이너가 자동으로 처리하도록 설정할 수 이는데, 이를 선언적 트랜잭션 처리라고 한다. Transaction Advice 설정을 하기 위한 가장 첫 번째 작업은 Transaction Manager를 Bean에 등록하는 것이다.

5.4.13.2 트랜잭션 처리 레이어[편집 | 원본 편집]

Persistent Layer가 아니라 Business Layer에서 처리해야한다.

5.4.13.3 트랜잭션 매니저 설정 코드[편집 | 원본 편집]

<?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:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">

	<context:component-scan base-package="com.springbook.biz" />
	
	<context:property-placeholder location="classpath:databases.properties" />

	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
		<property name="driverClassName" value="${jdbc.driver}" />
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
	</bean>
	
	<!-- Spring JDBC 설정 -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource" />
	</bean>
	
	<!-- Transaction 설정 -->
	<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>
	
	<tx:advice id="txAdvice" transaction-manager="txManager">
		<tx:attributes>
			<tx:method name="get*" read-only="true" />
			<tx:method name="*" />
		</tx:attributes>
	</tx:advice>
	
	
		 id="txPointcut" expression="execution(* com.springbook.biz..*(..))" />
		 pointcut-ref="txPointcut" advice-ref="txAdvice" />
	
</beans>

5.4.14 Model 1 아키텍처[편집 | 원본 편집]

90년대 말부터 2000년대 초까지 자바 기반의 웹 애플리케이션 개발에 사용됐던 아키텍처를 Model 1 이라고 부른다. Model 1 아키텍처는 JSP와 JavaBeans만 사용하여 웹을 개발하는 것으로 그림 1-1과 같은 구조다.

Model 1 Architecture

그러나 Model 1 아키텍처는 엔터프라이즈 시스템에 적합하지 않다. 그 이유는 자바 로직과 화면 디자인이 통합되어 유지보수가 어렵기 때문이다. 이러한 Model 1 아키텍처의 문제를 해결하기 위해 고안된 웹 개발 모델이 Model 2 아키텍처, 즉 MVC 아키텍처다.

5.4.15 Model 2 아키텍처[편집 | 원본 편집]

Model 2 아키텍처에서 가장 중요한 특징은 Controller의 등장이며, 이 Controller는 서블릿 클래스를 중심으로 구현한다.


Model 2 아키텍처에서는 기존에 JSP가 담당했던 Controller 로직이 별도의 Controller 기능의 서블릿으로 옮겨졌다. 따라서 기존에 Model 1 아키텍처로 개발한 프로그램에서 JSP 파일에 있는 자바 코드만 Controller로 이동하면 Model 2 아키텍처가 된다. 결과적으로 Controller 로직이 사라진 JSP에는 View와 관련된 디자인만 남게 되어 디자이너는 JSP 파일을 관리하고, 자바 개발자는 Controller와 Model만 관리하면 된다.

Model2 Architecture

5.4.16 프론트 컨트롤러(Front Controller) 디자인 패턴[편집 | 원본 편집]

서비스 요청을 중앙집중에서 처리하기 위한 패턴으로 SpringMVC에서 이 패턴을 지원한다. 이름은 DispatcherServlet 이다.

Front Contoller Design Pattern

프론트 컨트롤러 특징은 아래와 같다.

  • 프론트 컨트롤러 서블릿 하나로 클라이언트의 요청을 받음
  • 프론트 컨트롤러가 요청에 맞는 컨트롤러를 호출(mapping 정보기반)
  • 공통 처리를 입구에서 할 수 있음
  • 프론트 컨트롤러를 둠으로써 나머지 컨트롤러는 서블릿을 사용하지 않아도 됨


DispatcherServlet의 개념도는 아래와 같다. 참고로 DispatcherServle이 시작되면 애플리케이션을 실행할 때 호출했던 GenericXmlApplicationContext("applicationContext.xml")을 함께 실행하여 컨테이너를 구동한다.

SpringMVC's DispatcherServlet

5.4.17 SpringMVC의 수행 흐름[편집 | 원본 편집]

SpringMVC가 클라이언트 요청을 처리하는 흐름은 아래 도표와 같다.

SpringMVC's process flow

참고로 ④번의 리턴값은 실제로 단순 "문자열"이며 이 "문자열" 값을 토대로 실제 경로를 얻는 것은 ViewResolver다.

5.4.18 web.xml 파일[편집 | 원본 편집]

수업 중 실습한 web.xml 파일의 중간버전은 아래와 같다. 실습 중 <context-param>...</context-param>의 <param-name> 태그의 값을 contextConfiguration으로 잘못 입력해서 오류가 발생했다. contextConfigLocation이라는 값으로 수정 후 Server를 실행했더니 정상적으로 게시글 등록 화면이 나왔다.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://xmlns.jcp.org/xml/ns/javaee"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
	id="WebApp_ID" version="3.1">

	<!-- Filter 추가 및 맵핑-->
	<filter>
		<filter-name>characterEncoding</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>characterEncoding</filter-name>
		<url-pattern>*.do</url-pattern>
	</filter-mapping>
	
	<!-- Context Param 등록 -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:applicationContext.xml</param-value>
	</context-param>
	
	<!-- Listener 등록 -->
	<listener>
		<listener-class>
			org.springframework.web.context.ContextLoaderListener
		</listener-class>
	</listener>

	<!-- DispatcherServlet 등록 및 맵핑-->
	<servlet>
		<servlet-name>action</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/config/presentation-layer.xml</param-value>
		</init-param>
	</servlet>
	<servlet-mapping>
		<servlet-name>action</servlet-name>
		<url-pattern>*.do</url-pattern>
	</servlet-mapping>

</web-app>


애플리케이션을 실행하면 아래와 같은 화면이 나온다.

insertBoard.do 호출 화면

5.4.19 기타[편집 | 원본 편집]

  • Negative 테스트를 잊지 말자. (대부분이 Positive 테스트를 시나리오 테스트로 수행하고, Negative 테스트는 생략을 하거나 탐색적 테스팅으로 수행함)

5.5 5일차[편집 | 원본 편집]

5.5.1 복습[편집 | 원본 편집]

Spring Framework는 각 영역별로 Bean을 갖고 있다.


ContextLoaderListener는 Listener인데 객체 생성 및 초기화를 자동으로 수행한다. 정확히 설명하자면 ContextLoaderListener는 RootApplicationContext를 생성하는 클래스다.


DispatcherServlet는 Presentation 영역의 Bean을 처리하는 Spring Container를 구동하는데 구동을 위한 정보는 contextConfigLocation 변수에 저장되어 있다. web.xml에 아래 코드와 같이 설정한다. 참고로 DispatcherServlet의 다른 이름은 Front Controller 이다.

	<!-- DispatcherServlet 등록 및 맵핑-->
	<servlet>
		<servlet-name>action</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/config/presentation-layer.xml</param-value>
		</init-param>
	</servlet>


※ 만약 web.xml 파일에서 <init-param>이 없으면 <servlet-name>action</servlet-name> 값을 토대로 /WEB-INF/서블릿이름-servlet.xml 파일을 찾는다.

5.5.2 Spring 프레임워크 프로젝트에서 가장 먼저 확인해야 하는 것[편집 | 원본 편집]

Spring Framework에서 제일 먼저 하는 일은 Bean에 대한 설정을 확인하는 것이고 이는 web.xml 파일에 등록을 한다.

5.5.3 classpath[편집 | 원본 편집]

classpath는 프로젝트 기준으로 아래 위치를 의미한다.

이클립스 프로젝트에서 classpath 의미

5.5.4 HandlerMapping[편집 | 원본 편집]

어떤 URL에 대해 어떤 메소드를 실행할지 결정하는 객체다. Annotation 설정 시 @RequestMapping을 사용한다. 과거에는 XML 파일에 Property로 등록을 했지만 더 이상 사용하지 않는다.

Spring 프레임워크에서 HandlerMapping 하는 방법

호출하는 메소드에 parameter가 있다라고 하면 아래와 같은 일을 수행한다.

  1. 객체생성
  2. Request 객체에 값이 null이 아닌 property를 바인딩 (예: setSeq(request.getParameter("seq"); 호출)
  3. 모든 setter 메소드 호출


즉, Spring 프레임워크에서는 parameter만 추가해주면 위의 세가지 일을 한다. 이거를 하는 애가 바로 Controller 역할을 하는 HandlerMapping 객체다.

5.5.5 ViewResolver[편집 | 원본 편집]

ViewResolver 미 생성 시, Controller가 return 하는 값이 곧 이동하는 URL이 된다. 예를 들어 아래 함수는 test.jsp 로 이동한다.

디폴트 ViewResolver 사용

5.5.6 Model 인터페이스[편집 | 원본 편집]

Spring 프레임워크에서 Request 객체에 parameter를 전달하고 싶으면 Model 인터페이스를 사용한다. 참고로 HanlderMapping에 의해서 호출하는 insertBoard() 함수에 있는 parameter인 BoardVO는 자동으로 등록이 된다. 즉, 아래 코드가 프레임워크에 의해 자동으로 추가된다.

model.addAttribute("boardVO", board);


실제 BoardController.java 파일은 아래와 같다.

package com.springbook.biz.board.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import com.springbook.biz.board.BoardVO;

@Controller
public class BoardController {
	// 글 등록
	@RequestMapping("/insertBoard.do")
	public String  insertBoard(BoardVO boardVO, Model model) {
		System.out.println("BoardController의 insertBoard()");
		System.out.println(boardVO);
		
		// Parameter 설정
		model.addAttribute("msg", "Have a good day!");
		
		// 서비스 호출
		return "test.jsp";
	}
}


이 값을 확인하려면 아래와 같이 EL 태그를 사용하면 된다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>테스트 페이지 입니다 😊</h1>
<h2>${msg}</h2>
<h3>${boardVO}</h3>
</body>
</html>

5.5.7 @ModelAttribute[편집 | 원본 편집]

만약 Model로 선언한 변수의 이름을 바꾸고 싶으면 @ModelAttribute Annotation을 사용한다.

package com.springbook.biz.board.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;

import com.springbook.biz.board.BoardVO;

@Controller
public class BoardController {
	// 글 등록
	@RequestMapping("/insertBoard.do")
	public String  insertBoard(@ModelAttribute("board") BoardVO boardVO, Model model) {
		System.out.println("BoardController의 insertBoard()");
		System.out.println(boardVO);
		
		// Parameter 설정
		model.addAttribute("msg", "Have a good day!");
		
		// 서비스 호출
		return "test.jsp";
	}
}

5.5.8 Model 객체로 넘어온 VO의 Property 가져오기[편집 | 원본 편집]

struct 혹은 객체의 멤버 변수에 접근하는 것처럼 처리하면 된다. 내부적으로 getter method를 호출해서 데이터를 출력한다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>테스트 페이지 입니다 😊</h1>
<h2>${msg}</h2>
<h3>${board}</h3>
<h3>${board.title}</h3>
<h3>${board.content}</h3>
</body>
</html>

5.5.9 redirect 명령어[편집 | 원본 편집]

Controller에서 주소 값을 return 하면 기본적으로 RequestDispatcher 객체의 forward() 함수로 페이지가 이동이 된다. 즉, URL 주소가 이동한 페이지로 바뀌지 않는다. URL 주소를 바꾸러면 HttpServletResponse 객체의 sendRedirect() 함수를 호출해야 한다. 이를 위해서는 "redirect:" 를 추가하면 된다. 아래 소스 코드를 참고하자.

	// 글 등록
	@RequestMapping("/insertBoard.do")
	public String insertBoard(BoardVO vo) {
		System.out.println("BoardController의 insertBoard() 호출");
		
		// 서비스 호출
		service.insertBoard(vo);

		// 페이지 이동
		return "redirect:getBoardList.do";
	}

5.5.10 홈페이지 URL 변경하기[편집 | 원본 편집]

톰캣에서 Root 주소 변경하기

5.5.11 @RequestMapping 옵션[편집 | 원본 편집]

value, method 값을 별도로 줌으로써 HTTP 요청 Method를 구별할 수 있다.

  • @RequestMapping(value="주소", method=RequestMethod.{GET, POST, ...})


아래 코드를 참고하자.

	// (핸들러 맵핑) 로그인 GET - 페이지 출력
	@RequestMapping(value="/login.do", method=RequestMethod.GET)
	public String loginForm() {
		return "login.jsp";
	}
	
	// (핸들러 맵핑) 로그인 POST - 처리
	@RequestMapping(value="/login.do", method=RequestMethod.POST)
	public String login(UserVO vo, HttpSession session) {
		System.out.println("UserController의 getUser() 호출");

		// 서비스 호출
		UserVO user = service.getUser(vo);

		if (user != null) {
			// 로그인 작업
			session.setAttribute("user", user);
			session.setAttribute("session", session.getId());
			
			return "redirect:getBoardList.do";
		} else {
			return "login.jsp";
		}		
	}

5.5.12 Mybatis[편집 | 원본 편집]

5.5.12.1 정의[편집 | 원본 편집]

한두 줄의 자바 코드로 DB 연동을 처리하는 JDBC 프레임워크다.

5.5.12.2 구성[편집 | 원본 편집]

  1. 하나의 환경설정 파일
  2. 여러 개의 SQL Mapper 파일

5.5.13 특징[편집 | 원본 편집]

Mybatis는 DAO에 쿼리가 나오지 않는다. XML Mapper 파일에서 관리한다.

5.5.13.1 Mapper 파일[편집 | 원본 편집]

Spring JDBC와는 다르게 별도 파일에 SQL을 관리하며 이를 Mapper 파일이라고 한다.

5.5.13.2 pom.xml 파일[편집 | 원본 편집]

수업에서는 3.3.1 버전을 사용했다. 아래 코드에서 mybatis는 Mybatis 프레임워크 자체이며 mybatis-spring은 Spring Framework와 연동하기 위한 라이브러리다.

		<!-- Mybatis -->
		<dependency>
			<groupId>org.mybatis</groupId>
			mybatis
			<version>3.3.1</version>
		</dependency>

		<dependency>
			<groupId>org.mybatis</groupId>
			mybatis-spring
			<version>1.2.4</version>
		</dependency>

5.5.13.3 스프링 연동 설정[편집 | 원본 편집]

스프링과 Mybatis를 연동하려면 우선 스프링 설정 파일에 SqlSessionFactoryBean 클래스를 Bean으로 등록해야 한다. 그래야 SqlSessionFactoryBean 객체로부터 DB 연동 구현에 사용할 SqlSession 객체를 얻을 수 있다.

...
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
		<property name="driverClassName" value="${jdbc.driver}" />
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
	</bean>
	
	<!-- Mybatis 설정 -->
	<bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="configLocation" value="classpath:sql-map-config.xml" />
	</bean>
	
	<bean class="org.mybatis.spring.SqlSessionTemplate">
		<constructor-arg ref="sessionFactory"></constructor-arg>
	</bean>
...


Spring JDBC는 Spring Framework의 일부이기 때문에 이러한 스프림 프레임워크와의 연동 설정이 불필요하다.

5.5.13.4 SqlSessionFactory 클래스[편집 | 원본 편집]

SqlSessionFactory는 이름에서 알 수 있듯이 SqlSession 객체에 대한 공장 역할을 한다. 즉, Factory Pattern을 사용한다. 이 객체는 openSession()이라는 메소드를 제공하며, 이 메소드를 이용해서 SqlSession 객체를 얻을 수 있다.

5.5.14 HttpMessageConverter[편집 | 원본 편집]

HTTP 요청에 대한 응답 결과를 HTML과 같은 웹 페이지가 아니라 JSON이나 XML과 같은 데이터로 변환하여 메시지 보디에 저장하려면 스프링에서 제공하는 변환기(converter)를 사용해야 한다.


스프링은 HttpMessageConverter를 구현한 다양한 변환기를 제공하는데, 이 변환기를 이용하면 자바 객체를 다양한 타입으로 변환하여 HTTP 응답 보디에 설정할 수 있다. 자바 객체를 JSON 응답 보디로 변환할 때는 MappingJackson2HttpMessageConverter를 사용한다.

5.5.15 @RestController 어노테이션[편집 | 원본 편집]

@Controller는 요청에 대해 일반적인 응답을 제공할 때 사용하지만, JSON, XML 같은 데이터로 응답을 할 때는 @RestController를 사용한다. 이 어노테이션을 사용하려면 presentation-layer.xml에 아래와 같이 Namespaces에 mvc를 추가하고 <mvc:annotation-driven /> 태그를 추가해야 한다. 아래 코드 기준으로 5번째 줄, 13번째 줄에 해당한다.

<?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:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">

	<context:component-scan base-package="com.springbook.biz.board.controller" />
	<context:component-scan base-package="com.springbook.biz.user.controller" />

	<mvc:annotation-driven></mvc:annotation-driven>
</beans>

6 강의자료[편집 | 원본 편집]

https://naver.me/5DbzAFTA

7 참고자료[편집 | 원본 편집]