[KH정보교육원 당산] 74일 (Spring흐름 및 Ajax를 활용한 To-Do list)
Springframework 에서는 5개의 XML 파일을 다루게 된다.
또한 이번 프로젝트에서는 Mybatis를 사용함으로써 2개의 XML파일이 추가된다.
프로젝트를 생서하면 자동으로 만들어지는 pom.xml / web.xml / root-context.xml / servlet-context.xml / log4j.xml
Mybatis를 사용하기 위한 mybatis-config.xml / Mapper.xml
프로젝트 구조)
src/main/java : 개발되는 Java 코드
src/main/resources : 서버가 실행될 때 필요한 파일들 (Configuration / Mapper / DataSource.properties)
WEB-INF/spring : 스프링 설정 파일
WEB-INF/views : JSP 파일
pom.xml : Maven 설정
xml 문서 간의 연관 관계
web.xml => root-context.xml => mybatis-config.xml => todoMapper.xml
(미리 등록 해놓아야 할 객체들) (매핑 객체 및 SQL 매퍼파일) (SQL 쿼리)
=> servlet-context.xml
(요청이 들어왔을 때 등록해 놓아야 할 객체들)
프로젝트를 보면서 흐름을 정리)
pom.xml에서는 Maven 설정을 다룬다.
해당 xml에 필요한 라이브러리 dependency를 걸어서 라이브러리를 다운받는다.
또한 자바 설정 및 스프링 설정도 같이 다룬다.
필요한 라이브러리 다운
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.4</version>
</dependency>
<!-- Spring JDBC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- DBCP : Connection Pool -->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<!-- Jackson2 : JSON -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.7.2</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</dependency>
web.xml
<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Processes application requests -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</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>
프로젝트를 생성하면 기본적으로 코드가 들어있다.
web.xml에서는 크게 2분류로 나눌수 있다.
1. 사전 등록 부분
<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>
root-context.xml 내부에 설정해둔 bean태그
<!-- db.properties 등록 -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:db.properties</value>
</property>
</bean>
<!-- db.properties 읽기 -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${oracle.driver}" />
<property name="url" value="${oracle.url}" />
<property name="username" value="${oracle.id}" />
<property name="password" value="${oracle.pw}" />
</bean>
<!-- 트랜잭션 등록 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- SqlSessionTemplate 등록(Mybatis) -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:mybatis-config.xml" />
</bean>
<!-- SqlSession 등록(Mybatis) -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
db.properties
<bean id="dataSource" ~~>
<bean id="transactionManager" ~~>
<bean id="sqlSessionFactory" ~~>
<bean id="sqlSession" ~~~>
=> 해당 bean태그가 정상적으로 등록 되려면 먼저 db.properties 값을 가져와야 된다.
2. 요청시 등록
<servlet>
<servlet-name>appServlet</servlet-name> <=== 3.
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <==== 4. 생성 등록 시작
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value> <==== 5. 완성
</init-param>
<load-on-startup>1</load-on-startup>
</servlet> <==== 6. 생성 등록 끝
웹 브라우저로부터 요청사항이 들어오면
<servlet-mapping>
<servlet-name>appServlet</servlet-name> <=== 2.
<url-pattern>/</url-pattern> <=== 1.
</servlet-mapping>
프로젝트가 실행되면 가장 먼저 url-pattern을 확인한다.
문자열 / 를 확인 한 후에 값이 맞으면 다음인 appServlet을 확인하는데 이때 첫번째 appServlet과 두번째 appServlet의 값이 다르게 되면 에러 발생으로 404에러가 떨어진다.
두개의 값을 조회하여 값이 맞으면 해당 값을 DispatcherServlet으로 넘긴다.
그러면 다음 단계인 servlet-context.xml을 확인 하는데 이 때,
servlet-context.xml 이 부분을 확인 해보면
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<context:component-scan base-package="com.jdh.spring" />
먼저 <annotation-driven /> 이 실행되어 @ 붙은 클래스를 찾는다.
현재의 프로젝트에서는 @Controller / @Repository <- 먼저 등록
@Repository // Repository 어노테이션이 먼저 실행된다.
public class testDAO {
@Autowired
SqlSession sql;
} //따라서 sql 이 먼저 객체로 등록이 되게 되어서
===================================================================
@Controller
public class test {
@Autowired
testDAO dap;
@RequestMapping(~~) <-- 요청이 들어오지 않았기 때문에 대기 상태
}
// 마찬가지로 객체를 등록하려면 testDAO타입이 있는지 확인하게 된다.
Repository가 먼저 등록 되는데 이때 autowired를 통하여 객체가 등록 된다.
그렇기에 그다음 등록인 controller부분의 autowired 부분이 정상적으로 등록된다.
왜냐 repository를 먼저 등록 시키면서 그안에 객체가 주입 되기 때문에
프로젝트를 생성한 후에 필요한 라이브러리를 web.xml에 등록하여 다운받아준 후에
사용할 bean객체를 root-context.xml에 등록해준다.
mybatis를 사용하기 위해서 따로 설정.xml파일을 만들어주는데
JAVA ORM 플러그인을 사용하면 기본적인 코드가 작성된 설정 파일을 만들 수 있다.
mybatis-config.xml
<configuration>
<properties resource="db.properties" />
<typeAliases>
<typeAlias type="com.jdh.spring.dto.TodoDTO" alias="Todo"></typeAlias>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${oracle.dirver}" />
<property name="url" value="${oracle.url}" />
<property name="username" value="${oracle.id}" />
<property name="password" value="${oracle.pw}" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/todoMapper.xml" />
</mappers>
</configuration>
resource 태그를 보면 프로퍼티 파일이 하나있다. 이 안에 DB접속에 필요한 값을 넣어준다.
매퍼 파일의 경로를 mapper 안에 있는걸로 설정을 했다.
그렇기 때문에
src/main/resources 안에 mpper라는 패키지를 하나 만든다.
이 패키지 안에서는 mybatis에 관련된 mapper.xml을 만들것이며 모든 SQL 문은 이 매퍼 파일안에 들어가있다.
todoMapper.xml
<mapper namespace="com.jdh.spring.repository.TodoMapper">
<insert id="insertTodo" parameterType="Todo">
INSERT INTO todoList
(id, title, name, sequence, type, regdate)
VALUES
(todoid.nextval, #{title}, #{name}, #{sequence},'TODO',sysdate)
</insert>
<select id="selectTodo" resultType="Todo">
SELECT
*
FROM
todoList
ORDER BY regdate DESC
</select>
</mapper>
** Mybatis 관련 설정 파일과 매퍼 파일의 차이점
// 설정 파일
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
//매퍼 파일
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
상단 선언부의 차이가 있다.
현재의 프로젝트에서 필요한 xml파일 설정은 끝났다.
먼저 Mpper 역할을 할 인터페이스 생성
package com.jdh.spring.repository;
import java.util.List;
import com.jdh.spring.dto.TodoDTO;
public interface TodoMapper {
public int insertTodo(TodoDTO dto);
public List<TodoDTO> selectTodo();
public int updateTodo(TodoDTO dto);
}
todoMapper.xml 설정을 보면
<mapper namespace="com.jdh.spring.repository.TodoMapper">
해당 경로가 설정된 것을 볼 수 있다.
따라서 클래스의 메서드 명과 매퍼 파일내의 id값이 동일해야 해당 메서드를 호출할 수 있다.
DB를 만들었으니 그에 맞는 DTO클래스도 생성
public class TodoDTO {
private String id;
private String name;
private String title;
private String regdate;
private String sequence;
private String type;
.
.
getter // setter 만들어야한다.
controller 작성을 위하여 일단, Repository 클래스 쪽 메서드만 선언한다.
@Repository
public class TodoRepository { // 기존의 DAO역할 (servlet-context.xml의 <annotation-driven />에 의해 등록
@Autowired //컨터이너에 SqlSession와 동일한
SqlSession sqlSession;
public TodoRepository() {
}
public int insertTodo(TodoDTO dto){
return result;
}
public List<TodoDTO> selectTodo(){
List<TodoDTO> result = new ArrayList<TodoDTO>();
return result;
}
public int updateTodo(TodoDTO dto){
int result = 0;
return result;
}
controller 클래스 작성
@Controller
public class TodoController { //servlet-context.xml의 <annotation-driven>에 의해 자동 등록
@Autowired
TodoRepository repo; //DAO 역할
public TodoController() {
}
//TODO 추가작업
@RequestMapping(value="/TodoForm", method = RequestMethod.GET)
public String TodoForm(){
return "todoForm"; //servlet-context.xml의 viewResolver에 의해
//"/WEB-INF/views/todoForm.jsp"로 응답처리
}
//TODO 추가작업
@RequestMapping(value="/TodoAdd", method = RequestMethod.POST)
public String TodoAdd(TodoDTO dto){
//데이터 처리 작업 필요
repo.insertTodo(dto);
return "redirect:/";
}
//TODO AJAX
@RequestMapping(value="/updateTodo",method = RequestMethod.POST)
public @ResponseBody List<TodoDTO> updateTodo(TodoDTO dto){
//데이터 처리 작업 필요
repo.updateTodo(dto);
return repo.selectTodo();
}
}
RequestMapping을 보면 경로가 설정되어있다. 이부분은 JSP파일에서 설정한 경로에 맞게 값이 넘어오게된다.
jsp파일이 완성된 것이 아니기에 일단은 해당 값이 설정된 부분이다.
<form action="TodoForm">
<input type="submit" value="새로운 TODO 등록">
</form>
form 태그 action 경로에 TodoForm이 설정 되어있다.
값을 입력 하고 등록 버튼을 누르게 되면 위의 컨트롤러에서 해당하는 경로 명을 찾게 된다.
/TodoForm => 이 경로로 반환값으로 todoForm이 설정되어있는데 이는
servlet-context.xml의 viewResolver에 의해 /WEB-INF/views/todoForm.jsp"로 응답처리가 된다.
따라서 todoForm.jsp가 실행되게 된다.
** 이번 프로젝트부터는 jsp파일을 직접 실행하게 되면 404에러가 나오게 된다.
따라서 프로젝트를 통하여 실행해야하는데 프로젝트를 처음 생성하게되면 HomeController가 자동적으로 생성되어있다.
이를 현재 우리가 사용할 수 있게 코드를 수정해줘야한다.
HomeController
@Controller
public class HomeController {
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
/**
* Simply selects the home view to render by returning its name.
*/
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
return "main";
}
}
반환값을 main으로 바꿨는데 위에 TodoForm이 실행되는 과정과 똑같이
/main.jsp 로 변환 되어 실행 되게 된다.
기본 생성파일은 home으로 되어있고 views 폴더안에 home.jsp가 생성되어 있다. 그렇기 때문에 바꿔주지 않으면 home.jsp가 실행된다.
http://localhost:9000/
톰캣 서버주소 ==> http://localhost:9000
톰캣 서버 내부의 프로젝트(즉, 컨테이너) ==> /
실제 요청사항 문자열 ==> "/" => DispatcherServlet 에게 전달
DispatcherServlet
1. "/" 와 동일한 값을 갖고 있는 메서드 검색 (없다 => 404)
2. HomeController => @RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
return "main";
}
3. "main" 문자열을 DispatcherServlet 에게 전달
4. DispatcherServlet은 ViewResolver 객체를 찾고
ViewResolver 에게 "main" 에 대한 구체적인 내용을 문의
5. ViewResolver 는 <beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
을 이용하여
"/WEB-INF/views/main.jsp" 라고 DispatcherServlet에게 응답
6. DispatcherServlet는 웹브라우저에게 "/WEB-INF/views/main.jsp" 와 같이 응답
7. 웹 브라우저는 "/WEB-INF/views/main.jsp" 내용을 달라고 다시 요청
8. DispatcherServlet 은 /WEB-INF/views/에 있는 main.jsp 파일을 컴파일 하여 html 형태로 웹 브라우저에게 응답 처리
9. 웹 브라우저는 응답받은 html 형태의 문서를 HTML 엔진을 통해 컴파일 하여 사용자에게 출력
'Learn > KH정보교육원' 카테고리의 다른 글
[KH정보교육원 당산] 75일(AJAX - To-Do-List ) (0) | 2021.07.02 |
---|---|
[KH정보교육원 당산] 연습문제9 (인터페이스 구현) (0) | 2021.07.01 |
[KH정보교육원 당산] 73일( Spring - Mybatis ) (0) | 2021.06.29 |
[KH정보교육원 당산] 73일 (Spring - 파일업로드, 다국어처리, 데이터 변환) (0) | 2021.06.29 |
[KH정보교육원 당산] 72일 ( 어노테이션 방식) (0) | 2021.06.23 |