[Spring 15-1] SpringMVC 프로그램을 작성하는 방법(DB연동)

임승현·2023년 2월 27일

Spring

목록 보기
40/46

🌈Step_1 테이블 만들기

기존의 Student 테이블 사용

🌈Step_2 DTO 클래스 생성

📃Student.java

※ xyz.itwill10.dto 패키지에 Student.java 클래스 생성

package xyz.itwill10.dto;
/*
이름       널?       유형            
-------- -------- ------------- 
NO       NOT NULL NUMBER(4)     
NAME              VARCHAR2(50)  
PHONE             VARCHAR2(20)  
ADDRESS           VARCHAR2(100) 
BIRTHDAY          DATE    
 */
import lombok.Data;
//
@Data
public class Student {
	private int no;
	private String name;
	private String phone;
	private String address;
	private String birthday;
}

🌈Step_3 매퍼 파일 생성

📃StudentMapper.java(인터페이스)

※ src/main/java 폴더에 xyz.itwill10.mapper 패키지 생성
※ xyz.itwill10.mapper 패키지에 StudentMapper.java 인터페이스 파일 생성

package xyz.itwll10.mapper;
//
import java.util.List;
import xyz.itwill10.dto.Student;
//
public interface StudentMapper {
	int insertStudent(Student student);
	List<Student> selectStudentList();
}

📃StudentMapper.xml

※ src/main/java 폴더에 xyz.itwill10.mapper 패키지 생성
※ xyz.itwill10.mapper 패키지에 StudentMapper.xml 파일 생성

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- Interface Mapper 파일과 바인딩 하기 위한 namespace 속성값으로 Interface Mapper 파일의 경로 설정 -->
<mapper namespace="xyz.itwill10.mapper.StudentMapper">
	<!-- Interface Mapper 파일과 바인딩할 경우 parameterType 속성 생략 가능 -->
	<insert id="insertStudent">
		insert into student values(#{no},#{name},#{phone},#{address},#{birthday})
	</insert>
	<!-- ========================================================================================= -->
	<!-- 검색행의 컬럼명과 DTO 클래스의 필드명이 같은 경우 resultType 속성을 사용하여 자동 매핑 처리 -->
	<select id="selectStudentList" resultType="Student">
		select * from student order by no	
	</select>
</mapper>

🌈Step_4 DAO 클래스 생성(Mybatis로 작성)

📃StudentDAO(인터페이스)

※ src/main/java 폴더에 xyz.itwill10.dao 패키지 생성
※ xyz.itwill10.dao 패키지에 StudentDAO 인터페이스 생성

package xyz.itwill10.dao;
//
import java.util.List;
import xyz.itwill10.dto.Student;
//결합도를 느슨하게 하기 위해서 인터페이스 생성
public interface StudentDAO {
	int insertStudent(Student student);
	List<Student> selectStudentList();
}

📃StudentDAOImpl

※ xyz.itwill10.dao 패키지에 StudentDAOImpl 클래스 생성

package xyz.itwill10.dao;
//
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.springframework.stereotype.Repository;
import lombok.RequiredArgsConstructor;
import xyz.itwill10.dto.Student;
import xyz.itwill10.mapper.StudentMapper;
//
//SpringMVC Framework에 Mybatis Framework를 사용하여 DAO 클래스를 작성하는 방법
//1. DataSource 관련 라이브러리와 Mybatis 관련 라이브러리를 프로젝트에 빌드 처리 - 메이븐 : pom.xml
//→ ojdbc, spring-jdbc(spring-tx), mybatis, mybatis-spring
//2. Mybatis Framework의 환경설정파일 작성 - 생략 가능
//→ src/main/webapp 폴더에 작성해야만 스프링 컨테이너(WebApplicationContenxt 객체)가 Mybatis Framework의 환경설정파일에 접근 가능
//→ 환경설정에 따라 src/main/java 또는 src/main/resources  폴더에 작성 가능
//3. DataSource 관련 클래스, SqlSessionFactory 관련 클래스, SqlSession 관련 클래스를 Spring Bean으로 등록
//→ SpringMVC Framework의 스프링 컨테이너를 초기화 처리하기 위한 Spring Bean Configuration File에서 bean 엘리먼트로 해당 클래스를 Spring Bean으로 등록
//→ root-context.xml 또는 servlet-context.xml
//4. 테이블 생성 >> DTO 클래스 작성 >> 매퍼 파일 작성 >> DAO 클래스 작성 - 반복
//
//Mybatis Framework의 로그 팩토리에 의해 생성된 로그 이벤트를 Spring Framework의 로그 구현체를 사용하여 기록하는 방법
//1. log4jdbc-log4j2-jdbc4 라이브러리를 프로젝트에 빌드 처리
//2. DataSource 관련 클래스의 Spring Bean으로 등록한 Spring Bean Configuration File의 bean 엘리먼트에서 driverClassName 필드값과 url 필드값 변경 - root-context.xml
//3. [src/main/resources] 폴더에 [log4jdbc.log4j2.properties] 파일 작성
//→ Mybatis Framework의 로그 이벤트를 Spring Framework에게 전달하기 위한 SpyLogDelegator 클래스를 지정하기 위한 파일
//4. SpyLogDelegator 객체에 의해 발생된 로그 이벤트를 로그 구현체에 의해 기록되도록 환경설정파일 변경 - log4j.xml : logger 엘리먼트 추가
//
//DAO 클래스 : 저장매채에서 행에 대한 삽입,변경,삭제,검색 기능을 제공하는 객체를 생성하기 위한 클래스
//→ DAO 클래스의 메소드에서는 DBMS 서버에 SQL 명령을 전달하여 실행하고 실행결과를 제공받아 Java 객체(원시값)로 변환하여 반환되도록 작성
//→ DAO 클래스가 변경돼도 의존관계로 설정된 Service 클래스에 영향을 최소화 하기 위한 인터페이스를 상속받아 작성
//
//DAO 클래스는 Service 클래스에서 객체로 제공받아 사용되도록 반드시 Spring Bean으로 등록
//→ DAO 클래스는 @Repository 어노테이션을 사용하여 Spring Bean으로 등록되도록 처리
//→ @Repository 어노테이션을 사용하면 SQL 명령으로 발생되는 예외를 Spring 관련 예외로 제공되도록 처리
//→ @Repository 어노테이션을 스프링 컨테이너가 처리하기 위해서는 반드시 클래스를 작성한 패키지를 
//	Spring Bean Configuration File(servlet-context.xml)의 component-scan 엘리먼트로 검색되도록 설정
@Repository
//final 필드만 초기화 설정하는 생성자를 만들어 주는 어노테이션
//→ 생성자만 하나만 작성된 경우 @Autowired 어노테이션 생략 가능
@RequiredArgsConstructor//final이 꼭 있어야함
public class StudentDAOImpl implements StudentDAO {
	//Mybatis Framework로 DAO 클래스를 작성할 경우 매퍼에 등록된 SQL 명령을 전달하여 실행하고 결과를 Java 객체로 반환받기 위해 SqlSession 객체가 반드시 필요
	//→ SqlSession 객체를 저장하기 위한 필드에 스프링 컨테이너에 의해 관리되는 Spring Bean 중 SqlSession 관련 객체를 제공받아 필드에 인젝션 처리 - DI
	//→ 필드를 초기화하는 생성자를 생성하여 @Autowired 어노테이션을 사용하여 의존성 주입
	private final SqlSession sqlSession;
	//
	@Override
	public int insertStudent(Student student) {
		return sqlSession.getMapper(StudentMapper.class).insertStudent(student);
	}
	@Override
	public List<Student> selectStudentList() {
		return sqlSession.getMapper(StudentMapper.class).selectStudentList();
	}
}

📃servlet-context.xml

※ WEB-INF/spring/appServlet 폴더의 servlet-context.xml 수정

<!-- xyz.itwill10.dao 패키지에 DAO 클래스를 작성하고 스프링 어노테이션을 이용하여 Spring Bean으로 등록 -->
<context:component-scan base-package="xyz.itwill10.dao" />

① 빌드 처리

📃pom.xml

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<!-- → Mybatis Framework 관련 라이브러리 -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.11</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<!-- → Spring Framework에 Mybatis Framework 기능을 제공하기 위한 라이브러리 -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.1.0</version>
</dependency>

② Mybatis Framework의 환경설정파일 작성

📃mybatis-config.xml

※ WEB-INF/spring 폴더에 mybatis-config.xml 파일 생성

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<settings>
		<setting name="mapUnderscoreToCamelCase" value="true"/>
		<setting name="jdbcTypeForNull" value="NULL"/>
		<setting name="callSettersOnNulls" value="true"/>
	</settings>
</configuration>

③ 클래스를 Spring Bean으로 등록

📃root-context.xml

※ WEB-INF/spring 폴더에 root-context.xml 파일 수정

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
	<!-- ========================================================================================= -->
	<!-- Root Context: defines shared resources visible to all other web components -->
	<!-- root-context.xml : 모든 Front Controller 역할의 객체에서 공통적으로 사용될 클래스를 Spring Bean으로 등록하기 위한 Spring Bean Configuration File -->
	<!-- → DAO 클래스 작성에 필요한 클래스를 Spring Bean으로 등록 -->
	<!-- → DataSource, SQLSessionFactory, SqlSession, TransactionManager 등 -->
	<!-- ========================================================================================= -->
	<!-- DataSource 관련 클래스를 Spring Bean으로 등록 - DBCP(DataBase Connection Pool) -->
	<!-- → DataSource 객체 필드에 Connection 객체 생성에 필요한 값을 인젝션 처리 -->
	<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource">
		<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
		<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe"/>
		<property name="username" value="scott"/>
		<property name="password" value="tiger"/>
	</bean>
	<!-- ========================================================================================= -->
	<!-- SqlSessionFactory 관련 클래스를 Spring Bean으로 등록 -->
	<!-- → SqlSessionFactoryBean 객체 필드에 객체 생성에 필요한 값을 인젝션 처리 -->
	<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactoryBean">
		<!-- configLocation 필드에 Mybatis Framework의 환경설정파일에 경로가 저장되도록 설정 -->
		<!-- → SpringMVC Framework에서 사용하는 스프링 컨테이너(WebApplicationContext 객체)는 [src/main/webapp] 폴더를 최상위 폴더로 처리하여 파일 경로를 제공 -->
		<!-- <property name="configLocation" value="/WEB-INF/spring/mybatis-config.xml"/> -->
		<!-- [src/main/java] 폴더 또는 [src/main/resources] 폴더에 환경설정파일이 있는 경우 classpath 접두사를 사용하여 스프링 컨데이너로 접근 -->
		<!-- → 폴더의 패키지에 작성된 환경설정파일은 파일 시스템 경로 표현하여 접근 가능 -->
		<!-- <property name="configLocation" value="classpath:xyz/itwill/config/mybatis-config.xml"/> -->
		<property name="configLocation" value="classpath:mybatis-config.xml"/>
		<!--                                                                                       -->
		<!-- dataSource 필드에 DataSource 관련 클래스의 Spring Bean이 저장되도록 설정 - DI -->
		<!--  -->
		<property name="dataSource" ref="dataSource"/>
		<!--                                                                                       -->
		<!-- typeAliasesPackage 필드에 DTO 클래스를 작성한 패키지를 저장하도록 설정 -->
		<!-- → XML 기반의 매퍼파일(MapperFile)에서 Java 자료형 대신 사용할 별칭을 제공하기 위해 
			Mybatis 환경설정파일의 typeAliases 엘리먼트 package 엘리먼트와 유사한 기능 제공 -->
		<property name="typeAliasesPackage" value="xyz.itwill10.dto"/>
		<!--                                                                                       -->
		<!-- mapperLocations 필드에 List 객체를 생성하여 저장되도록 설정 -->
		<!-- → List 객체의 요소에는 XML 기반의 매퍼 파일의 경로가 요소값으로 저장되도록 추가 - 매퍼 등록 -->
		<property name="mapperLocations">
			<list>
				<!-- [src/main/java] 폴더의 패키지에 매퍼 파일을 작성하기 위한 classpath 접두사 사용  -->
				<value>classpath:xyz/itwill10/mapper/*.xml</value>
			</list>
		</property>				
	</bean>
	<!-- ========================================================================================= -->
	<!-- SqlSession 관련 클래스를 Spring Bean으로 등록 -->
	<!-- → SqlSessionTemplate 클래스로 객체를 생성하기 위한 생성자 매개변수에 SqlSessionFactory 객체를 전달하여 필드 초기화 처리 - DI -->
	<!-- destroy-method 속성을 사용하여 Spring Bean 소멸전 clearCache 메소드가 자동 호출되도록 설정 -->
	<!-- → clearCache 메소드는 SqlSessionTemplate 객체 소멸전 SqlSessionTemplate 객체에 의해 사용된 JDBC 관련 객체를 정리하는 메소드 -->
	<!-- → DAO 클래스의 메소드에서 SqlSession 객체 사용후 close() 메소드를 호출하지 않도록 설정 -->
	<bean class="org.mybatis.spring.SqlSessionTemplate" id="sqlSession" destroy-method="clearCache">
		<constructor-arg ref="sqlSessionFactoryBean"/>
	</bean>
</beans>

🌈Step_5 Service 클래스 생성

📃StudentService.java(인터페이스)

※ src/main/java 폴더에 xyz.itwill10.service 패키지 생성
※ xyz.itwill10.service 패키지에 StudentService.java 인터페이스 파일 생성

package xyz.itwill10.service;
//
import java.util.List;
import xyz.itwill10.dto.Student;
//
public interface StudentService {
	void addStudent(Student student);
	List<Student> getStudentList();
}

📃StudentServiceImpl.java

※ xyz.itwill10.service 패키지에 StudentServiceImpl.java 클래스 생성

package xyz.itwill10.service;
//
import java.util.List;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import xyz.itwill10.dao.StudentDAO;
import xyz.itwill10.dto.Student;
//
//Service 클래스 : 요청 처리 메소드에서 데이터 처리 관련 기능을 모듈화하여 기능을 제공하는 객체를 생성하기 위한 클래스
//→ Service 클래스의 메소드에서는 데이터 차리에 필요한 다수의 DAO 클래스의 메소드를 호출하여 프로그램이 필요한 데이터 처리 기능을 모듈화하여 제공하도록 작성
//→ Service 클래스가 변경돼도 의존관계로 설정된 Controller 클래스에 영향을 최소화 하기 위한 인터페이스를 상속받아 작성
//
//Service 클래스는 Controller 클래스의 객체로 제공받아 사용되도록 반드시 Spring Bean으로 등록
//→ Service 클래스는 @Service 어노테이션을 사용하여 Spring Bean으로 등록되도록 처리
//→ @Service 어노테이션을 사용하여 TransactionManager 객체에 의해 트렌젝션 관리 가능
//→ @Service 어노테이션을 스프링 컨테이너가 처리하기 위해서는 반드시 클래스를 작성한 패키지를 
//	Spring Bean Configuration File(servlet-context.xml)의 component-scan 엘리먼트로 검색되도록 설정
@Service
@RequiredArgsConstructor
public class StudentServiceImpl implements StudentService {
	//Service 클래스의 메소드에서 사용될 DAO 객체를 저장하기 위한 필드 선언
	//→ @Autowired 어노테이션을 사용한 생성자로 필드의 인젝션 처리
	private final StudentDAO studentDAO;//인터페이스를 불러와야지 결합도가 낮아짐
	//
	@Override
	public void addStudent(Student student) {
		studentDAO.insertStudent(student);
	}
	@Override
	public List<Student> getStudentList() {
		return studentDAO.selectStudentList();
	}
}

📃servlet-context.xml

※ WEB-INF/spring/appServlet 폴더의 servlet-context.xml 수정

<context:component-scan base-package="xyz.itwill10.service" />

🌈Step_6 Controller 클래스 생성

📃StudentController.java 클래스 생성

※ xyz.itwill10.controller 패키지에 StudentController.java 클래스 생성

package xyz.itwill10.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 org.springframework.web.bind.annotation.RequestMethod;
import lombok.RequiredArgsConstructor;
import xyz.itwill10.dto.Student;
import xyz.itwill10.service.StudentService;
//
//SpringMVC Framework를 사용하여 웹프로그램을 작성하는 방법
//→ 테이블 >> DTO 클래스(Mybatis) >> Service 클래스 >> Controller 클래스
//>> 테스트 프로그램(JUit)- 단위 프로그램(모듈) 테스트 >> HTML 문서를 JSP 문서로 변환 >> 토합 프로그램 테스트 - 브라우저 이용
//
//Controller 클래스 : 클라이언트 요청을 처리하기 위한 기능의 객체를 생성하기 위한 클래스
//
//Controller 클래스는 Front Controller(DispatcherServlet 객체)의 객체로 제공받아 사용되도록 반드시 Spring Bean으로 등록
//→ @Controller 클래스는 @Controlle 어노테이션을 사용하여 Spring Bean으로 등록되도록 처리
//→ @Controller 어노테이션을 사용하면 클라이언트 요청에 의해 호출될 요청 처리 메소드 작성
//→ @Controller 어노테이션를 스프링 컨테이너가 처리하기 위해서는 반드시 클래스를 작성한 패키지를  
//	Spring Bean Configuration File(servlet-context.xml)의 component-scan 엘리먼트로 검색되도록 설정
//
@Controller
//@RequestMapping 어노테이션을 클래스에 선언하면 Controller 클래스의 모든 요청 처리 메소드에 요청 URL 주소 앞부분에 공통적으로 포함될 URL 주소를 제공
@RequestMapping("/student")
@RequiredArgsConstructor
public class StudentController {
	//Controller 클래스의 요청 처리 메소드에서 사용될 Service 객체를 저장하기 위한 필드 선언
	//→ @Autowired 어노테이션을 사용한 생성자로 필드의 인젝션 처리
	private final StudentService studentService;
	//
	//학생정보를 입력받기 위한 JSP 문서 관련 뷰이름을 반환하는 요청 처리 메소드
	@RequestMapping(value = "/student/add", method = RequestMethod.GET)
	public String add() {
		return "student/student_add";
	}
	//
	//학생정보를 전달받아 STUDENT 테이블에 삽입하고 회원목록 출력페이지를 요청할 수 있는 URL 주소를 클라이언트에게 전달하는 요청 처리 메소드
	@RequestMapping(value = "/student/add", method = RequestMethod.POST)
	public String add(@ModelAttribute Student student,Model model) {
		try {
			//STUDENT 테이블에 학생정보 삽입시 PK 제약조건에 의해 예외 발생 가능
			studentService.addStudent(student);
		} catch (Exception e) {
			model.addAttribute("message", "이미 사용중인 학번을 입력 하였습니다.");
			return "student/student_add";
		}
		return "redirect:/student/display";//리다이렉트 이동
	}
	//
	//STUDENT 테이블에 저장된 모든 학생정보를 검색하여 속성값으로 저장하고 학생목록을 출력하느 JSP 문서 관련 뷰이름을 반환하는 요청 처리 메소드
	@RequestMapping("/student/display")
	public String display(Model model) {
		model.addAttribute("studentList",studentService.getStudentList());
		return "student/student_display";
	}
}

🌈Step_7 테스트 프로그램 작성

※ 테스트 프로그램은 아래 폴더에 만들어서 작성

📃DataSourceTest.java

※ src/test/java 폴더 → xyz.itiwll.controller 패키지에 DataSourceTest.java 클래스 생성

package xyz.itwill.controller;
//
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
//
//Spring Framework를 사용하여 테스트 프로그램을 작성하여 단위 프로그램(모듈)을 테스트 하는 방법
//→ SpringMvc에서의 모듈 : DAO 클래스, Service 클래스, Controller 클래스의 메소드
//1. junit 라이브러리와 spring-test 라이브러리를 프로젝트에 빌드 처리 - 메이븐 : pom.xml
//2. 테스트 프로그램에서 사용될 로그 구현체의 환경설정파일 변경 - log4j.xml
//→ [src/test/resources] 폴더의 log4j.xml 파일의 내용 수정
//3. [src/test/java] 폴더에 테스트 프로그램에 대한 클래스 작성
//→ junit 라이브러리와 spring-test 라이브러리에서 scope 속성을 주석 처리해야 테스트 프로그램 관련 클래스 작성 가능 - 테스트 프로그램 실행 후 주석 제거
//4. 테스트 프로그램 실행 - 모듈 클래스
//
//@RunWith : 테스트 프로그램의 클래스를 객체로 생성하여 테스트 메소드를 호출하기 위한 클래스를 설정하는 어노테이션
//→ 테스트 클래스를 실행하기 위한 클래스 설정
//value 속성 : 테스트 클래스를 실행하기 위한 클래스(Class 객체)를 속성값으로 설정
//→ 다른 속성이 없는 경우 속성값만 설정 가능
//SpringJUnit4ClassRunner 클래스를 사용하여 테스트 클래스를 실행할 경우 ApplicatrionContext 객체(Spring Container)가 생성되어 제공 
@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration : 테스트 클래스에서 사용할 수 있는 Spring Bean을 제공하기 위한 Spring Bean Configuration File을 설정하는 어노테이션
//→ 스프링 컨테이너에 객체에 의해 관리하기 위한 객체
//locations 속성 : Spring Bean Configuration File의 경로를 요소로 저장한 배열을 속성값으로 설정
@ContextConfiguration(locations = {"file:src/main/webapp/WEB-INF/spring/root-context.xml"})
public class DataSourceTest {
	private static final Logger logger=LoggerFactory.getLogger(DataSourceTest.class);
	//
	//테스트 클래스의 메소드에서 사용할 객체를 저장하기 위한 필드 선언
	// => @AutoWired 어노테이션을 필드에 사용하여 의존성 주입 - 생성자를 이용한 의존성 주입 불가능
	@Autowired
	private DataSource dataSource;
	//
	//@Test : 테스트 메소드를 설정하는 어노테이션
	// => SpringJUnit4ClassRunner에 의해 호출되어 모듈 테스트를 실행할 메소드
	@Test
	public void testDataSource() throws SQLException {
		logger.info("DataSource = "+dataSource);
		Connection connection=dataSource.getConnection();
		logger.info("Connection = "+connection);
		connection.close();
	}
}

① 빌드 처리

📃pom.xml

<!-- Test -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    <!-- <scope>test</scope> -->
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<!-- => Spring Framework를 이용하여 테스트 프로그램 작성에 필요한 기능을 제공하는 라이브러리 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>${org.springframework-version}</version>
	<!-- <scope>test</scope> -->
</dependency>

📃StudentServiceTest.java

※ xyz.itiwll.controller 패키지에 StudentServiceTest.java 클래스 생성

package xyz.itwill.controller;
//
import java.util.List;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import xyz.itwill10.dto.Student;
import xyz.itwill10.service.StudentService;
//
//테스트 클래스의 메소드에서는 일반적으로 Service 클래스의 메소드 또는 Controller 클래스의 메소드를 호출하여 메소드가 정상적으로 동작되는지 검사할 목적으로 작성
//
@RunWith(SpringJUnit4ClassRunner.class)
//@WebAppConfiguration : ApplicationContext 객체가 아닌 WebApplicationContext 객체로 스프링 컨테이너 역할을 제공하도록 설정하기 위한 어노테이션
@WebAppConfiguration
@ContextConfiguration(locations = {"file:src/main/webapp/WEB-INF/spring/root-context.xml"
		,"file:src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml"})
//@FixMethodOrder : 테스트 메소드 호출순서을 설정하기 위한 어노테이션
//value 속성 : MethodSorters 자료형(Enum)의 상수 중 하나를 속성값으로 설정
//→ MethodSorters.DEFAULT : JUnit 프로그램의 내부 규칙에 의해 정렬되어 메소드 호출 - 테스트마다 동일한 순서로 메소드 호출
//→ MethodSorters.JVM : JVM에 의해 정렬되어 메소드 호출 - 테스트마다 변경된 순서로 메소드 호출
//→ MethodSorters.NAME_ASCENDING : 테스트 메소드의 이름을 오름차순 정렬하여 메소드 호출
@FixMethodOrder(value = MethodSorters.NAME_ASCENDING)
public class StudentServiceTest {
	private static final Logger logger=LoggerFactory.getLogger(StudentServiceTest.class);
	//
	@Autowired
	private StudentService studentService;
	//
	@Test
	public void testAddStudent() {
		Student student=new Student();
		student.setNo(6000);
		student.setName("홍경래");
		student.setPhone("010-6781-4311");
		student.setAddress("서울시 중랑구");
		student.setBirthday("2000-09-10");
		//
		studentService.addStudent(student);
	}
	//
	@Test
	public void testStudentList() {
		List<Student> studentList=studentService.getStudentList();
		//
		for(Student student:studentList) {
			//DTO 클래스의 toString() 메소드 호출 - 모든 필드값을 문자열로 변환하여 반환
			logger.info(student.toString());
		}
	}
}

📃StudentControllerTset.java

※ xyz.itiwll.controller 패키지에 StudentControllerTset.java 클래스 생성

package xyz.itwill.controller;
//
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
//
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
//[*] 패턴문자를 사용하여 Spring Bean Configuration File 설정 가능
//→ [**] 형식으로 0개 이상의 하위 폴더를 표현 가능
@ContextConfiguration(locations = {"file:src/main/webapp/WEB-INF/spring/**/*.xml"})
public class StudentControllerTset {
	private static final Logger logger=LoggerFactory.getLogger(StudentControllerTset.class);
	//
	//WebApplicationContext 객체를 저장하기 위한 필드 선언 - DI
	//→ WebApplicationContext 객체 : SpringMVC 프로그램에서 스프링 컨테이너 역할을 제공하기 위한 객체
	@Autowired
	private WebApplicationContext context;
	//
	//MockMvc 객체르 저장하기 위한 필드 선언
	//→ MockMvc 객체 : 
	private MockMvc mvc;
	//
	//@Before : 테스트 메소드 호출 전 실행될 명령을 작성한 메소드를 설정하는 어노테이션 - 초기화 작업
	@Before
	public void setup() {
		//MockMvcBuilders.webAppContextSetup(WebApplicationContext context)
		//→ MockMvcBuilder 객체를 생성하여 반환하기 위한 메소드
		//MockMvcBuilder.build() : MockMvc 객체를 생성하여 반환하기 위한 메소드
		mvc=MockMvcBuilders.webAppContextSetup(context).build();
		logger.info("MockMvc 객체 생성");
	}
	//
	@Test
	public void testStudentDisplay() throws Exception {
		//MockMvc.perform(Builder requestBulider) : 가상으로 페이지를 요청하는 메소드
		//→ Controller 클래스에서 해당 페이지의 요청 처리 메소드 호출
		//→ 요청에 대한 처리결과가 저장된 ResultActions 객체 반환
		//MockMvcRequestBuilders.get(String url) : URL 주소를 전달받아 GET 방식으로 요청하는 메소드
		//→ 페이지에 대한 URL 주소의 요청 관련 정보(리퀘스트 메세지)가 저장된 Builder 객체 반환
		//ResultActions.andReturn() : 요청 처리 메소드의 실행 결과를 MvcResult 객체로 반환하는 메소드
		MvcResult result=mvc.perform(MockMvcRequestBuilders.get("/student/display")).andReturn();
		//
		logger.info(result.getModelAndView().getViewName());
		logger.info(result.getModelAndView().getModel().toString());
	}
}

🌈Step_8 JSP 작성

📃 student_add.jsp

※ WEB-INF/views/student 폴더에 student_add.jsp 생성

<%@ 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>SPRING</title>
</head>
<body>
	<h1 align="center">학생정보 입력</h1>
	<hr>
	<form name="studentForm" method="post">
	<table align="center" border="1" cellpadding="1" cellspacing="0" width="300">
		<tr height="40">
			<th bgcolor="yellow" width="100">학생번호</th>
			<td width="200" align="center">
				<input type="text" name="no" value="${student.no }">
			</td>
		</tr>
		<tr height="40">
			<th bgcolor="yellow" width="100">이름</th>
			<td width="200" align="center">
				<input type="text" name="name" value="${student.name }">
			</td>
		</tr>
		<tr height="40">
			<th bgcolor="yellow" width="100">전화번호</th>
			<td width="200" align="center">
				<input type="text" name="phone" value="${student.phone }">
			</td>
		</tr>
		<tr height="40">
			<th bgcolor="yellow" width="100">주소</th>
			<td width="200" align="center">
				<input type="text" name="address" value="${student.address }">
			</td>
		</tr>
		<tr height="40">
			<th bgcolor="yellow" width="100">생년월일</th>
			<td width="200" align="center">
				<input type="text" name="birthday" value="${student.birthday}">
			</td>
		</tr>
		<tr height="40">
			<td width="200" colspan="2" align="center">
				<input type="button" value="학생추가" onclick="submitCheck();">
				<input type="reset" value="초기화">
				<input type="button" value="학생목록" onclick="location.href='${pageContext.request.contextPath}/student/display';">
			</td>
		</tr>
	</table>
	</form>
	<p align="center" style="color: red;">${message }</p>
	<script type="text/javascript">
	studentForm.no.focus();
	function submitCheck() {
		if(studentForm.no.value=="") {
			alert("학생번호를 입력해 주세요.");
			studentForm.no.focus();
			return;
		}
		var noReg=/\d{4}/g;
		if(!noReg.test(studentForm.no.value)) {
			alert("학생번호는 정수 4자리로 입력해주세요.");
			studentForm.no.focus();
			return;
		}
		if(studentForm.name.value=="") {
			alert("이름을 입력해 주세요.");
			studentForm.name.focus();
			return;
		}
		if(studentForm.phone.value=="") {
			alert("전화번호을 입력해 주세요.");
			studentForm.phone.focus();
			return;
		}
		var phoneReg=/(01[016789])-\d{3,4}-\d{4}/g;
		if(!phoneReg.test(studentForm.phone.value)) {
			alert("전화번호를 형식에 맞게 입력해주세요.");
			studentForm.phone.focus();
			return;
		}
		if(studentForm.address.value=="") {
			alert("주소을 입력해 주세요.");
			studentForm.address.focus();
			return;
		}
		if(studentForm.birthday.value=="") {
			alert("생년월일을 입력해 주세요.");
			studentForm.birthday.focus();
			return;
		}
		var birthdayReg=/(18|19|20)\d{2}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])/g;
		if(!birthdayReg.test(studentForm.birthday.value)) {
			alert("생년월일을 형식에 맞게 입력해주세요.");
			studentForm.birthday.focus();
			return;
		}
		studentForm.submit();
	} 
	</script>
</body>
</html>

📃 student_display.jsp

※ WEB-INF/views/student 폴더에 student_display.jsp 생성

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>    
<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>    
<!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>SPRING</title>
</head>
<body>
	<h1 align="center">학생목록</h1>
	<table align="center" cellspacing="0" cellpadding="1" width="700">
		<tr align="right">
			<td>
				<%-- <input type="button" value="학생추가" onclick="location.href='add';"> --%>
				<input type="button" value="학생추가" onclick="location.href='${pageContext.request.contextPath}/student/add';">
			</td>
		</tr>
	</table>
	<table align="center" border="1" cellspacing="0" cellpadding="1" width="700">
		<tr bgcolor="yellow">
			<th width="100">학생번호</th>
			<th width="100">이름</th>
			<th width="150">전화번호</th>
			<th width="250">주소</th>
			<th width="100">생년월일</th>
		</tr>
		<c:choose>
			<c:when test="${empty(studentList)}">
				<tr align="center">
					<td colspan="5">검색된 학생정보가 없습니다.</td>
				</tr>		
			</c:when>
			<c:otherwise>
				<c:forEach var="student" items="${studentList }">
					<tr align="center">
						<td width="100">${student.no }</td>				
						<td width="100">${student.name }</td>				
						<td width="150">${student.phone }</td>				
						<td width="250">${student.address }</td>				
						<td width="100">${fn:substring(student.birthday,0,10) }</td>				
					</tr>
				</c:forEach>
			</c:otherwise>
		</c:choose>	
	</table>
</body>
</html>

📢로그 구현체를 사용하여 기록하는 방법

📢Mybatis Framework의 로그 팩토리에 의해 생성된 로그 이벤트를 Spring Framework의 로그 구현체를 사용하여 기록하는 방법
1. log4jdbc-log4j2-jdbc4 라이브러리를 프로젝트에 빌드 처리
2. DataSource 관련 클래스의 Spring Bean으로 등록한 Spring Bean Configuration File의 bean 엘리먼트에서 driverClassName 필드값과 url 필드값 변경 - root-context.xml
3. [src/main/resources] 폴더에 [log4jdbc.log4j2.properties] 파일 작성
→ Mybatis Framework의 로그 이벤트를 Spring Framework에게 전달하기 위한 SpyLogDelegator 클래스를 지정하기 위한 파일
4. SpyLogDelegator 객체에 의해 발생된 로그 이벤트를 로그 구현체에 의해 기록되도록 환경설정파일 변경 - log4j.xml : logger 엘리먼트 추가

①빌드 처리

📃pom.xml

<!-- https://mvnrepository.com/artifact/org.bgee.log4jdbc-log4j2/log4jdbc-log4j2-jdbc4 -->
<!-- => 퍼시스턴스 프레임워크(Persistence Framework)에 의해 발생되는 로그 이벤트를
Spring Framework의 로그 구현체로 기록하기 위한 기능을 제공하는 라이브러리 -->
<dependency>
    <groupId>org.bgee.log4jdbc-log4j2</groupId>
    <artifactId>log4jdbc-log4j2-jdbc4</artifactId>
    <version>1.16</version>
</dependency>

②필드값 변경

📃root-context.xml

※ WEB-INF/spring 폴더에 root-context.xml 파일 수정

<!-- 
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource">
	<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
	<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe"/>
	<property name="username" value="scott"/>
	<property name="password" value="tiger"/>
</bean>
-->
<!-- 퍼시스턴스 프레임워크(Persistence Framework)에서 발생되는 로그 이벤트를 전달받아 Spring Framework에서 처리하기 위해 
	driverClassName 필드값과 url 필드값을 log4jdbc-log4j2-jdbc4 라이브러리에서 제공하는 값으로 변경 -->
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource">
	<property name="driverClassName" value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy"/>
	<property name="url" value="jdbc:log4jdbc:oracle:thin:@localhost:1521:xe"/>
	<property name="username" value="scott"/>
	<property name="password" value="tiger"/>
</bean>

③파일 작성

📃log4jdbc.log4j2.properties

※ src/main/resources 폴더에 log4jdbc.log4j2.properties 파일 생성

log4jdbc.spylogdelegator.name = net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator

④환경설정파일 변경

📃log4j.xml

※ src/main/resources 폴더에 log4j.xml 파일 수정

<!-- Slf4jSpyLogDelegator 객체에 의해 전달된 로그 이벤트를 기록하기 위한 logger 엘리먼트 -->
<!-- jdbc.sqlonly : 완성된 SQL 명령 기록 -->
<logger name="jdbc.sqlonly">
	<level value="info" />
</logger>
<!-- jdbc.sqltiming : SQL 명령의 실행시간(ms) 기록 -->
<logger name="jdbc.sqltiming">
	<level value="info" />
</logger>
<!-- jdbc.audit : ResultSet 관련 매핑 정보를 제외한 모든 JDBC 관련 정보 기록 -->
<logger name="jdbc.audit">
	<level value="info" />
</logger>
<!-- jdbc.resultset : ResultSet 관련 매핑 정보를 포함한 모든 JDBC 관련 정보 기록 -->
<!--  
<logger name="jdbc.resultset">
	<level value="info" />
</logger>
-->
<!-- jdbc.resultsettable : ResultSet 관련 매핑 정보를 표(Table)형식으로 기록 -->
<logger name="jdbc.resultsettable">
	<level value="info" />
</logger>
<!-- jdbc.connection : Connection 객체 관련 정보 기록 - Open 또는 Close -->
<logger name="jdbc.connection">
	<level value="info" />
</logger>

0개의 댓글