티스토리 뷰
1. ViewResolver
- MVC 패턴에서 Model 데이터를 이용하여, 최종 응답화면을 생성할 View 역할을 수행할 대상을 찾아낸다.
- 1 ) 접두사 : /WEB-INF/views/
- 2 ) 접미사 : .jsp
- 3 ) 최종 뷰의 이름 : 접두사 + 뷰 이름 + 접미사 = /WEB-INF/views/뷰 이름.jsp
2. DataSource 설정
- DB와 Connection을 맺고 끊는 작업은 리소스의 소모가 많은 작업이다.
- 그렇기에 Pooling이라는 기법을 통해서 객체를 미리 생성하고 빌려 쓰는 방식으로 이용해서 연결 시간을 단축시킨다.
3. Servlet – context.xml파일과 root – context.xml파일의 차이점
- 같이 빈을 관리하지만, 웹과 관련된 빈은 Servlet-context.xml에 관리하고
- 웹과 관련이 없는 빈은 root – context.xml 파일에서 관리한다.
[ 1. servlet-context.xml 파일내 태그의 의미 ]
[ 2. web.xml 파일의 설명 ]
[ + 코드 보기 ]
<?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_4_0.xsd"
version="4.0">
<display-name>chap02</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>index.htm</welcome-file>
</welcome-file-list>
<!-- ====================== -->
<!-- 공통 JSP 설정 -->
<!-- ====================== -->
<jsp-config>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<page-encoding>UTF-8</page-encoding>
<include-prelude>/WEB-INF/views/include.jsp</include-prelude>
<trim-directive-whitespaces>true</trim-directive-whitespaces>
<default-content-type>text/html; charset=utf8</default-content-type>
</jsp-property-group>
</jsp-config>
<!-- =============================== -->
<!-- Web Application 예외처리방법#1 -->
<!-- =============================== -->
<error-page>
<error-code>500</error-code>
<location>/WEB-INF/views/errors/500.jsp</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/views/errors/404.jsp</location>
</error-page>
<!-- =============================== -->
<!-- Web Application 예외처리방법#2 -->
<!-- =============================== -->
<error-page>
<exception-type>java.lang.NullPointerException</exception-type>
<location>/WEB-INF/views/errors/null.jsp</location>
</error-page>
<!-- =============================== -->
<!-- Http Session 만료시간설정(in minutes) -->
<!-- =============================== -->
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<!-- =============================== -->
<!-- 아래는 Spring Project 생성시 자동 생성되는 부분 -->
<!-- =============================== -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- =============================== -->
<!-- 프런트 컨트롤러 역할을 하는 Servlet : DispatcherServlet -->
<!-- =============================== -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- servlet-context.xml는 웹과 관련이 없는 빈을 저장한다. -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<!-- 프런트 컨트롤러가 헨들러를 찾지 못하면 나오는 예외 -->
<init-param>
<param-name>throwExceptionIfNoHandlerFound</param-name>
<param-value>true</param-value>
</init-param>
<!-- 요청을 받을시 로드되는 순서를 지정한다. -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- =============================== -->
<!-- 자동생성되지 않는 부분 -->
<!-- =============================== -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!-- 프론트 컨트롤러가 전송 파라미터를 깨지지 않고 잘 받을 수 있도록 설정 -->
<init-param>
<param-name>encoding</param-name>
<param-value>utf8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
[ 3. root-cotext.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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://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">
<!-- Root Context: defines shared resources visible to all other web components -->
<!-- 여기 안에 속한 것은 모두 빈 타입으로 등록하겠다는 의미이다. -->
<!-- <context:component-scan base-package="org.zerock.myapp.sample" /> -->
<!-- 빈을 수동으로 등록하는 법 -->
<!-- 빈의 이름은 name이 아니라 id값으로 나온다. (***) -->
<bean id="hotel" name="myHotel" class="org.zerock.myapp.sample.Hotel" />
</beans>
[ 4. Servlet - context.xml파일에서 view-resolvers 설정 ] (***)
[ + 코드 보기 ]
<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<!-- View로 어떤 JSP가 사용되어야 하는지 알려준다. -->
<!-- ViewResolver는 MVC 패턴에서 Model 데이터를 이용하여 최종 응답화면을 생성할 View 역할을 수행할 대상을 찾아낸다. -->
<!-- [ 예전 버전 ] -->
<!-- + 예전 버전은 빈으로 등록하는 방법을 사용했다. -->
<!-- <beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> -->
<!-- beans:property는 setPrefix와 setSuffix를 value값으로 해주는것이다. -->
<!-- <beans:property name="prefix" value="/WEB-INF/views/" /> -->
<!-- <beans:property name="suffix" value=".jsp" /> -->
<!-- </beans:bean> -->
<!-- [ 현재 버전 ] -->
<view-resolvers>
<jsp prefix="/WEB-INF/views/" suffix=".jsp" />
</view-resolvers>
[ 5. pom.xml파일에서 Hikari CP 추가하기 ] (**)
[ + 코드 보기 ]
<?xml version="1.0" encoding="UTF-8"?>
<!-- 1~2칸 띄우기 -->
<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 https://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.zerock</groupId>
<artifactId>chap3</artifactId>
<version>1.0.0-BUILD-SNAPSHOT</version>
<!-- 웹에 패키징되는 건 war -->
<packaging>war</packaging>
<name>chap3</name>
<url>http://springmvc.example.com</url>
<description>Spring MVC project</description>
<!-- 프로젝트 어디서든 가져다 쓸수 있도록 정의 -->
<properties>
<!-- 자바버전 -->
<java-version>11</java-version>
<!-- <java-home>${env.JAVA_HOME}</java-home> -->
<!-- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target> -->
<!-- 스프링버전 -->
<org.springframework-version>5.3.21</org.springframework-version>
<org.aspectj-version>1.9.9.1</org.aspectj-version>
<org.apache.logging.log4j-version>2.17.2</org.apache.logging.log4j-version>
</properties>
<dependencies>
<!-- =============== Logging =============== -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${org.apache.logging.log4j-version}</version>
</dependency>
<!-- For Spring framework, HikariCP, DriverSpy logging -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>${org.apache.logging.log4j-version}</version>
</dependency>
<!-- ============= Servlet/JSP ============= -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- =============== AspectJ (스프링할 때 rt, weaver 꼭)=============== -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId> <!-- rt = runtime -->
<version>${org.aspectj-version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<!-- ================ Spring (밑의 2개는 최소한 꼭) =============== -->
<!-- 스프링 빈즈 컨테이너 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- 강제 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework-version}</version>
<scope>test</scope>
</dependency>
<!-- =============== data source =============== -->
<!-- 히카리CP -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>5.0.1</version>
<exclusions>
<!-- 충돌이 발생하기에 제외시켜줘야 한다. -->
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- =============== JDBC =============== -->
<dependency>
<groupId>org.bgee.log4jdbc-log4j2</groupId>
<artifactId>log4jdbc-log4j2-jdbc4</artifactId>
<version>1.16</version>
</dependency>
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8-production</artifactId>
<version>21.5.0.0</version>
<type>pom</type>
<exclusions>
<exclusion>
<groupId>com.oracle.database.ha</groupId>
<artifactId>simplefan</artifactId>
</exclusion>
<exclusion>
<groupId>com.oracle.database.ha</groupId>
<artifactId>ons</artifactId>
</exclusion>
<exclusion>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>rsi</artifactId>
</exclusion>
<exclusion>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ucp</artifactId>
</exclusion>
<exclusion>
<groupId>com.oracle.database.xml</groupId>
<artifactId>xdb</artifactId>
</exclusion>
<exclusion>
<groupId>com.oracle.database.xml</groupId>
<artifactId>xmlparserv2</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- =============== Testing (버전 4,5 둘다 쓸줄알아야함)=============== -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<!-- ================ Misc ================= -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>/</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.10</version>
<configuration>
<downloadSources>true</downloadSources>
<downloadJavadocs>false</downloadJavadocs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<release>${java-version}</release>
</configuration>
</plugin>
</plugins>
</build>
</project>
[ 6. root-context.xml에서 HikariConfiguration과 hikariDataSource 빈 등록하기 ] (***)
[ + 코드 보기 ]
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://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">
<!-- Root Context: defines shared resources visible to all other web components -->
<!-- Hikari Configuration -->
<!-- primary는 같은 이름이 여러개 일때, 이것을 우선순위로 삼으라는 의미이다. -->
<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig" primary="true" >
<description>HikariCP Configuration</description>
<!-- 1. JDBC 연결정보 속성들에 값 설정 -->
<property name="driverClassName" value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy" />
<property name="jdbcUrl" value="jdbc:log4jdbc:oracle:thin:@db0000000_high?TNS_ADMIN=C:/opt/OracleCloudWallet/ATP"/>
<property name="username" value="HR"/>
<property name="password" value="Oracle"/>
<!-- 2. Connection Pool의 작동방식과 관련된 속성들에 값 설정 -->
<property name="maximumPoolSize" value="10"/>
<property name="minimumIdle" value="2"/>
<property name="idleTimeout" value="10000"/>
<property name="connectionTimeout" value="1000"/>
<property name="connectionTestQuery" value="SELECT 1 FROM dual"/>
<property name="dataSourceJNDI" value="jdbc/HikariCP"/>
<property name="poolName" value="*** HikariDataSource ***"/>
</bean>
<bean
id="hikariDataSource"
class="com.zaxxer.hikari.HikariDataSource"
destroy-method="close">
<description>HikariCP DataSource</description>
<!-- 생성자 매개변수가 2개 이상일 때 사용할 수 있는 속성 -->
<!-- <constructor-arg name="" value="" ref="" index=""/> -->
<constructor-arg ref="hikariConfig"/>
</bean>
</beans>
[ 7. 히카리CP 활용하기 ] (****)
[ + 코드 보기 ]
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import javax.sql.DataSource;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.Timeout;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;
@Log4j2
@NoArgsConstructor
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/**/*.xml" })
@TestInstance(Lifecycle.PER_CLASS)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class DataResourceTests {
// 테스트하는데 필요한 빈을 주입해 주세요! 라고 시그널을 Beans Container에 전송함.
// 그런데, 우리 테스트에 필요한 빈이 뭔가요!? => `HikariDataSource` Bean이 필요함!!!
@Autowired
private DataSource datasource;
// =================================================================
@BeforeAll
void beforeAll() {
log.trace("beforeAll() invokd.");
Objects.nonNull(this.datasource);
log.trace("\t + this.datasource : {}", this.datasource);
} // beforeAll
// =================================================================
@Test
@Order(1)
@DisplayName("testHikariCP")
@Timeout(value=1000, unit=TimeUnit.MILLISECONDS)
void testHikariCP() throws SQLException {
log.trace("testHikariCP() invoked.");
Connection conn = datasource.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM employees");
try ( conn; stmt; rs; ){
log.info("\t + conn : {}", conn);
log.info("\t + stmt : {}", stmt);
log.info("\t + rs : {}", rs);
while( rs.next() ) {
String employee_id = rs.getString("EMPLOYEE_ID");
String first_name = rs.getString("FIRST_NAME");
String last_name = rs.getString("LAST_NAME");
String email = rs.getString("EMAIL");
String phone_number = rs.getString("PHONE_NUMBER");
String hire_date = rs.getString("HIRE_DATE");
String job_id = rs.getString("JOB_ID");
String salary = rs.getString("SALARY");
String commission_pct = rs.getString("COMMISSION_PCT");
String department_id = rs.getString("DEPARTMENT_ID");
String employee = String.format(
"%S, %S, %S, %S, %S, %S, %S, %S, %S, %S",
employee_id, first_name, last_name, email, phone_number, hire_date, job_id, salary, commission_pct, department_id);
log.info(employee);
} // while
} // try - with - resources
} // testHikariCP
// =================================================================
} // end class
[ 8. 히카리CP 활용하기 2 ] (****)
[ + 코드 보기 ]
import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.sql.SQLException;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import javax.sql.DataSource;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.Timeout;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import com.zaxxer.hikari.HikariConfig;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.log4j.Log4j2;
@Log4j2
@NoArgsConstructor
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/**/*.xml" })
@TestInstance(Lifecycle.PER_CLASS)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class DataResourceTests2 {
// 테스트하는데 필요한 빈을 주입해 주세요! 라고 시그널을 Beans Container에 전송함.
// 그런데, 우리 테스트에 필요한 빈이 뭔가요!? => `HikariDataSource` Bean이 필요함!!!
@Autowired
private DataSource datasource;
// 테스트하는데 필요한 빈을 주입해 주세요!
// 어느 빈 ? => HikariConfig빈이요!
// 루트 컨텍스트에 등록한 HikariConfig빈을 얻은다.
@Setter(onMethod_= {@Autowired})
private HikariConfig config;
// =================================================================
@BeforeAll
void beforeAll() {
log.trace("beforeAll() invokd.");
Objects.nonNull(this.datasource);
log.trace("\t + this.datasource : {}", this.datasource);
assertNotNull(this.config);
log.trace("\t + this.config : {}", this.config);
} // beforeAll
// =================================================================
@Test
@Order(1)
@DisplayName("testHikariConfig")
@Timeout(value=1000, unit=TimeUnit.MILLISECONDS)
void testHikariConfig() throws SQLException {
log.trace("testHikariConfig() invoked.");
// =================================================
// 루트 컨텍스트에 등록한 HikariConfig의 속성을 활용
// =================================================
log.info("\t + 1. DriverClass : {}", this.config.getDriverClassName());
// + 1. DriverClass : net.sf.log4jdbc.sql.jdbcapi.DriverSpy
log.info("\t + 2. JdbcUrl : {}", this.config.getJdbcUrl() );
// + 2. JdbcUrl : jdbc:log4jdbc:oracle:thin:@000_high?TNS_ADMIN=C:/opt/OracleCloudWallet/ATP
log.info("\t + 3. Username : {}", this.config.getUsername() );
// + 3. Username : HR
log.info("\t + 4. IdleTimeout : {}", this.config.getIdleTimeout());
// + 4. IdleTimeout : 10000
} // testHikariConfig
// =================================================================
} // end class
'KH 정보교육원 [ Java ]' 카테고리의 다른 글
KH 109일차 - Spring ( mybatis-spring : mapper ) (******) (0) | 2022.08.02 |
---|---|
KH 108일차 - Spring ( MyBatis와 연동 ) (****) (0) | 2022.08.01 |
KH 106일차 - Spring ( 의존성 주입(DI) ) (*****) (0) | 2022.07.28 |
KH 105일차 - Spring 설치 및 프로젝트 생성 (*****) (0) | 2022.07.27 |
KH 104일차 - mybatis ( 동적 SQL 처리 ) / 쿼츠 (quartz) - 스케줄링 (*****) (0) | 2022.07.26 |