Spring day04

유요한·2022년 11월 23일
1

Spring

목록 보기
6/15
post-thumbnail

root-context, servlet-context, web

web.xml

web.xml은 설정을 위한 설정 파일이다.
WAS가 처음 구동될 때 web.xml을 읽어 웹 애플리케이션 설정을 구성한다.

servlet-context.xml & root-context.xml

  1. servlet-context.xml
    이름에서 보듯이 서블릿 요청과 관련된 객체를 정의해야한다.
    주로 View 관련된, Controller 같은 bean을 설정한다고 한다.
    해당 컨텍스트 내에 있는 빈만 사용이 가능한다.

  2. root-context.xml
    root-context 또는 application-context는 view와 관련되지 않은 객체를 정의하고 공유가 가능하다. 그러니까 결국은 Service, Repository(DAO) 등 비지니스 로직 관련 bean을 설정하면 될것 같다.


Mybatis

SQL이 짧고 간결한 경우에는 어노테이션을 이용해서 쿼리문을 작성해 준다.
SQL이 복잡하거나 길어지는 경우에는 어노테이션보다 XML을 이용하는 것이 좋다. MyBatis-Spring의 경우 Mapper 인터페이스와 XML을 연동해서 동시에 이용할 수 있다. 인터페이스객체.메소드()를 호출하는 순간 해당하는 인터페이스의 경로를 namespace로 가지고 있는 XML 파일로 찾아가서 메소드명과 동일한 id를 가지고 있는 쿼리문을 수행하여 결과로 돌려준다.

관련 라이브러리 추가

pom.xml에 설정

pom.xml

<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>3.5.2</version>
</dependency>

<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis-spring</artifactId>
  <version>1.3.2</version>
</dependency>

 <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${org.springframework-version}</version>
 </dependency>
 <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${org.springframework-version}</version>
 </dependency>

root-context.xmlbean을 만들어야 한다.

SQLSessionFactory

MyBatis에서 가장 핵심적인 객체는 SQLSession이라는 존재와 SQLSessionFactory입니다. SQLSessionFactory의 이름에서 알 수 있듯이 내부적으로 SQLSession이라는 것을 만들어내는 존재이다.

개발에서는 SQLSession을 통해 Connection을 생성하거나 원하는 SQL을 전달하고 결과를 리턴받는 구조로 작성된다.

root-context.xml

<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
        <!-- hikariConfig라는 객체를 참고하라는 것 -->
        <constructor-arg ref="hikariConfig"/>
    </bean>

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
    </bean>

스프링에서 sqlSessionFactory를 등록하는 작업은 sqlSessionFactoryBean을 이용한다.

그러면 의문이 생긴다...

    <!-- property를 참고로 hikariConfig라는 bean이 생성된다. -->
    <bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig" >
<!--        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />-->
<!--        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/web0315" />-->
        <property name="driverClassName" value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy" />
        <property name="jdbcUrl" value="jdbc:log4jdbc:mysql://localhost:3306/web0315" />
        <property name="username" value="root" />
        <property name="password" value="1234" />
    </bean>

이거는 왜 root-context에 적어주는 것일까?

package com.example.persistance;


import lombok.extern.log4j.Log4j;
import org.junit.Test;
import org.junit.internal.runners.statements.Fail;

import java.sql.Connection;
import java.sql.DriverManager;

@Log4j
public class JDBCTests {
    static {
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testConnection() {
        try(Connection con = DriverManager.getConnection(
                "jdbc:mysql://localhost:3306/web0315",
                "root",
                "1234")) {
            log.info(con);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }
}

이렇게 사용할 때마다 연결해주는 것을 적어야 한다면 불편하다. 그렇기 때문에 위에 처럼 root-context에 따로 빼놔서 사용할 때 불러오는 것이다.

그런 다음

mybatis는 mapper을 해줘야 하는데 mapper은 src/main/java에 만들어 준다.
JDBC 방식으로 해도 되지만 그러면 너무 비효율적이라 Mybatis를 사용해서 자동으로 처리되는 방식이 좋습니다. 이를 위해서 Mybatis의 Mapper이라는 존재를 작성해야 합니다.

Mapper는 쉽게 말해 SQL과 그에 대한 처리를 지정하는 역할을 합니다.

package com.example.mapper;

import org.apache.ibatis.annotations.Select;

public interface TimeMapper {
    // Mapper 인터페이스
    // Mapper를 작성하는 작업은 XML을 이용할 수 있지만
    // 최소한의 코드로 작성하기 위해서는 Mapper 인터페이스를 이용한다.
    @Select("select now() from dual")
    public String getTime();

	// XML을 이용하는 방법
    public String getTime2();
}

src/main/resources에 com.example.mapper패키지를 만들고 거기에 TimeMapper.xml을 만들어준다. Mybatis는 mapper 인터페이스와 XML을 인터페이스의 이름과 namespace 속성값을 가지고 판단합니다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.example.mapper.TimeMapper">
    <select id="getTime2" resultType="string">
        select now() from dual;
    </select>
</mapper>

이제 src/test/java에서 persistence페키지에 MapperTest 클래스를 만들어서 테스트 해준다. 스프링에서는 만들때마다 테스트해서 에러가 있는지 확인하는 것이 좋다.

여기까지만 해준다면 이상한점이 있다. 그것은 여태까지 spring에서 주입해주라고 한다면 spring에게 위치를 알려줘야 하는데 예를들어서 객체에 알아서 주입해줘라고 한다면 root-context에서 scan으로 위치를 알려주고 @Component로 주입해줄 것을 표시해준다. 하지만 여기서는 mapper1이 주입을 받게 해놨는데 spring에게 위치를 알려주고 있지 않다.

그렇기 때문에 spring에게 위치를 알려줄 필요가 있다.

이 scan은 mybatis에서 mapper에서 사용할 때 사용하는 것이고 보통 scan은 다르다.

MyBatis는 내부적으로 JDBC의 PreparedStatement를 이용해서 SQL을 처리한다. 따라서 SQL에 전달되는 파라미터는 JDBC에서와 같이 ?로 치환되어서 처리된다. 복잡한 SQL의 경우 ?로 나오는 값이 제대로 전달되었는지 확인하기가 쉽지 않고 실행한 SQL의 내용을 정확히 확인하기 어렵다. 따라서 log4jdbc-log4j2라이브러리를 사용하여 어떤 값들이 전달되었고, 어떤 퀴리문이 수행되었는지를 확인 할 수 있다.


이것이 아래와 같이 바뀌는 것이 log.info를 했을때 자세히 볼 수 있는
DriverSpy다.

root-context.xml 전체

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
	xsi:schemaLocation="http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
		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">
	
	<!-- property를 참고로 hikariConfig라는 bean이 생성된다. -->
	<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
		<!-- <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
		<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/web0315"></property> -->
		<property name="driverClassName"  value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy" />
		<property name="jdbcUrl" value="jdbc.log4jdbc:mysql://localhost:3306/web0315" />
		<property name="username" value="root" />
		<property name="password" value="1234" />
	</bean>
	
	<!-- 위에서 hikariConfig라는 bean을 참고로 해서 DataSource라는 bean 생성된다. -->
	<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
		<!-- hikariConfig라는 객체를 참고하라는 것 -->
		<constructor-arg ref="hikariConfig"/>
	</bean>
	
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource"/>
	</bean>
	
	<mybatis-spring:scan base-package="com.example.mapper" />
	
	
	<!-- Root Context: defines shared resources visible to all other web components -->
	<!-- 어디서 scan하면 니가 주입할 객체들을 찾을 수 있는지 알려주는 것 -->
	<context:component-scan base-package="com.example.sample"/>	
	<!-- Root Context: defines shared resources visible to all other web components -->
		
</beans>

스프링 MVC 기본 구조

스프링 프레임워크는 하나의 기능을 위해서만 만들어진 프레임 워크가 아닌 코어라고 할 수 있는 여러 서브 프로젝트들을 결합해서 다양한 상황에 대처할 수 있도록 개발되었다. 그 중 하나가 스프링 MVC 구조이다.

-스프링 MVC-
[Spring MVC] : WEB 관련 영역
[Spring Core] : 일반 JAVA 영역(POJO)
[Spring-MyBatis][MyBatis]
[DB]

스프링 구동 순서, 과정

프로젝트 구동은 web.xml에서 시작한다. web.xml 상단에는 가장 먼저 구동되는 Context Listener가 등록되어 있다.

1) ContextLoaderListenr는 해당 웹 어플리케이션을 구동하게 되면 같이 작동이 시작되므로 해당 프로젝트를 실행하면 가장 먼저 로그를 출력하면서 실행된다.

2) root-context.xml이 처리되면 파일에 있는(설정해 놓은) Bean들이 작동한다.

3) root-context.xml이 처리된 후에는 DispatcherServlet이라는 서블릿과 관련된 설정이 작동한다. MVC구조에서 가장 핵심적인 역할을 하는 클래스이며 내부적으로 앱 관련 처리의 준비 작업을 진행한다. 내부적으로 웹 관련 처리의 준비 작업을 진행하기 위해 사용하는 파일이 있고 servler-context.xml이다.

4) DispatcherServlet에서 XmlWebApplicationContext를 이용해서 servlet-context.xml을 로딩하고 해석한다. 이 과정에서 등록된 객체(Bean)들은 기존에 만들어진 객체(Bean)들과 같이 연동하게 된다.

Front-controller 패턴


스프링 MVC 프로젝트를 구성해서 사용한다는 의미는 내부적으로는 root-context로 사용하는 일반 Java영역(흔히 POJO)과 servlet-context로 설정하는 Web관련 영역을 같이 연동해서 구동하게 됩니다.

  1. 사용자의 Reaquest는 Front-Controller인 DispatcherServlet을 통해서 처리합니다. 생성된 프로젝트의 web.xml을 보면 모든 Request를 DispatcherServlet이 받도록 처리하고 있습니다.

2, 3. HandlerMapping은 Request의 처리를 담당하는 컨트롤러를 찾기 위해서 존재합니다. HandlerMapping 인터페이스를 구현한 여러 객체들 중 RequestMappingHandlerMaping같은 경우는 개발자가 @RequestMapping어노테이션이 적용된 것을 기준으로 판단하게 됩니다. 적절한 컨트롤러를 찾았다면 HandlerAdapter를 이용해서 해당 컨트롤러를 동작시킵니다.

  1. Controller는 개발자가 작성하는 클래스로 실제 Request를 처리하는 로직을 작성하게 됩니다. 이 때 View에 전달해야 하는 데이터를 Model이라는 객체에 담아서 전달합니다. Controller는 다양한 타입의 결과를 반환하는데 이에 대한 처리를 ViewResolver를 이용하게 됩니다.

  2. ViewResolver는 Controller가 반환한 결과를 어떤 View를 통해서 처리하는 것이 좋을지 해석하는 역할입니다. 가장 흔하게 사용하는 설정은 servlet-context.xml에 정의된 InteralResourceViewResovler입니다.

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

6, 7. View는 실제로 응답 보내야 하는 데이터를 JSP등을 이용해서 생성하는 역할을 하게 됩니다. 만들어진 응답은 DispatcherServlet을 통해서 전송됩니다.

  1. 사용자의 Request는 Front-Controller인 DispatcherServlet을 통해 처리한다.

  2. HandlerMapping은 Request의 처리를 담당할 컨트롤러를 찾기 위해 존재한다.

  3. HandlerMapping 인터페이스를 구현한 여러 객체중 @RequestMapping이라는 어노테이션이 적용된 것을 기준으로 판단하며, 적절한 컨트롤러를 찾았다면 HandlerAdapter를 이용해서 해당 컨트롤러를 동작시킨다.

  4. Controller의 처리가 완료되었다면 어디로, 어떻게 갈 것인지 라는 결과가 나오고 그 결과를 ViewResolver가 리턴을 통해 받아서 어떤 View에서 처리하는 것이 좋을지 해석을 해준다.

  5. 해석된 결과를 가지고 실제 응답을 보내야 하는 데이터를 JSP등을 이용해서 생성해준다.

  6. 만들어진 응답(페이지)을 DispatcherServlet을 통해서 응답해준다.

여기서 작성해야 하는 것은 Controller, Service, DAO, DTO만 만들어주면 된다.

스프링 MVC의 Controller

스프링 MVC를 이용하는 경우 작성되는 Controller는 다음과 같은 특징이 있습니다.

  • HttpServletRequest, HttpServletResponse를 거의 사용할 필요 없이 필요한 기능 구현

  • 다양한 타입의 파리미터 처리, 다양한 타입의 리턴 타입 사용 가능

  • GET방식, POST방식 등 전송 방식에 대한 처리를 어노테이션으로 처리 가능

  • 상속/인터페이스 방식 대신에 어노테이션만으로도 필요한 설정 가능

profile
발전하기 위한 공부

0개의 댓글