Spring/Spring 이야기

[Spring framework] 공부를 해보자 2탄

seungdols 2016. 7. 29. 23:23

Spring framework

스프링은 특정 뷰 기술이 없어도 브라우저에서 렌더링이 가능하도록 하는 View Resolver를 제공한다. 스프링에서 JSP, Velocity(사용 안함), Freemarker, XSLT View, Thymeleaf, ReactJS, AngularJS등이 있으며, NodeJS 렌더링으로 유명한 Jade 또한 사용이 가능하다. (고로, 뷰 템플릿 엔진은 무엇을 쓰든지 가능하다. 자유도가 높다.)

스프링이 뷰를 처리하는데 중요한 기술 2가지 인터페이스가 있다.

  1. View Resolver interface
  2. View interface

view Resolver interface는 뷰 이름 = 실제 뷰를 매핑하는 작업을 하며, View interface는 요청을 준비하고, 뷰 템플릿 엔진을 사용해 요청에 대한 처리를 한다.

ViewResolver

  • AbstractCachingViewResolver
  • XmlViewResolver
  • ResourceBundleViewResolver
  • UrlBasedViewResolver
  • InternalResourceviewResolver
  • VelocityViewResolver/FreeMarkerViewResolver
  • ContentNegotiatingviewResolver

이중에서 AbstractCachingViewResolver의 하위 클래스들을 이용하면, 처리하는 뷰 인스턴스를 캐싱하여, 뷰 기술의 성능을 향상 시킬 수 있다.

스프링은 다중 뷰 리졸버를 지원하며, 리졸버를 체이닝 할 수 있고, 우선순위를 정할 수 있다.

@RequestMapping은 URL을 컨트롤러의 메서드와 매핑할 때 사용한다.

    @RequestMapping(value = {"/", "/hello"}, method = RequestMethod.GET)
    public String printWelcome(ModelMap model) {
        logger.debug("Home Page");
        model.addAttribute("message", "Spring 3 MVC Hello World");
        return "hello";

    }

위 처럼 url이 다중으로 연결 할 수 있다. 또한, 정규식 패턴을 사용 할 수 있어 url 매핑을 특정 정규식으로만 매핑 시킬수도 있다. 그리고 HTTP 메소드 매핑에 따라서 매핑 가능하다.

동적 값 또한 받아 올 수 있다.

    @RequestMapping(value = "/hello/{name:.+}", method = RequestMethod.GET)
    public ModelAndView hello(@PathVariable("name") String name) {

        ModelAndView model = new ModelAndView();
        model.setViewName("hello");
        model.addObject("msg", name);

        return model;

    }

위 코드에서는 {}로 쌓인 name을 @PathVariable로 타입까지 지정하여 가져 올 수 있다.

@RequestParam

@RequestParam은 key = value 형태로 화면에서 넘어오는 쿼리 스트링 혹은 폼 데이터를 메소드의 파라미터로 지정한다. 당연히 파라미터의 수가 적을때만 사용하는게 좋다.

해당 어노테이션을 사용했는데도 불구하고 값이 없다면, 400 Error가 발생한다. 고로, 에러 방지를 해줄 필요가 있다.

@RequestMapping(value="/...", method={RequestMethod.GET, RequestMethod.POST})
public String submit(HttpServletRequest req, 
        @RequestParam(value="num1", defaultValue = "0") int num1, 
        @RequestParam(value="num2", defaultValue = "0") int num2, 
        @RequestParam(value="oper", required=false) String oper) 
        throws Exception {

}

출처

위 처럼 defaultValue를 지정해주던지, 반드시 필요한 값은 아니라는 의미의 require=false를 설정 하는 것이다.

파라미터 수가 많다면, Map 방식을 사용하는 것이 좋다.

@RequestMapping("/Detail")
public String ShopDetail(@RequestParam HashMap<String, String> map){
    String SerchingPages = map.get("searchingPageValue");

    return "/shopping2/detail.nhn";
}

@Controller

컨트롤러에 사용하는 어노테이션으로 클래스 선언을 단순화 시켜준다는 장점이 있다. 또한, bean을 자동으로 생성해준다. (xx-servlet.xml에서 Component-Scan을 통해 bean 등록)

@Repository

이 어노테이션은 일반적으로 DAO에 사용 된다.

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {

    /**
     * The value may indicate a suggestion for a logical component name,
     * to be turned into a Spring bean in case of an autodetected component.
     * @return the suggested component name, if any
     */
    String value() default "";

}

실제 어노테이션 소스를 열어보면 이렇게 구성 되어 있는데, 특별한 일을 해준다는 것에 대해서는 잘 모르겠다. (더 찾아 보기)
나중에 @Autowired를 이용해 자동 빈 주입을 하려고 해도 @Repositoy 어노테이션도 적용을 하지 않고, 빈 등록도 하지 않으면, DAO에 대한 빈 주입을 할 수 없다. 고로, 어노테이션 기반이라면, DAO에 선언을 해주어야 한다.

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {

    /**
     * The value may indicate a suggestion for a logical component name,
     * to be turned into a Spring bean in case of an autodetected component.
     * @return the suggested component name, if any
     */
    String value() default "";

}

@Service 어노테이션 또한 비슷하다. 소스 코드 내의 서비스라는 것을 명시해준다는 것뿐인지. 또 다른 이면의 기능을 하는지 모르겠다.
@Repository처럼 서비스에 해당하는 빈에 해당 어노테이션을 추가해주어야 한다. 그렇지 않으면, 스프링 컨테이너인 IoC컨테이너가 빈을 찾을 수 없다.
(참조할 XML 설정 정보에도 없는데 무슨 수로 찾을까…)

@Resource

특정 Bean의 기능 수행을 위해 다른 Bean을 참조해야 할 때, 사용하는 어노테이션 중 하나 이며, 표준 어노테이션으로 Spring Framework 2.5부터 지원 가능하다.
필드, 입력 파라미터가 한 개인 Bean 프로퍼티 setter 메소드에 적용이 가능하다.
@Resource는 이름으로 연결한다.

사용하려면,

<bean class="org.springframework.beans.factory.annotation.CommonAnnotationBeanPostProcessor"/>

을 등록 시켜주어야 한다.
하지만, <context:annotation-config/>를 사용해도 된다.
참고1 , 참고2

@Autowired

특정 Bean의 기능 수행을 위해 다른 Bean을 참조 할 때, 사용하는 어노테이션이며(의존 관계를 자동 설정할 때 사용한다.), Type-driven Injection 기반이다. 고로, 참조할 빈을 검색할 때, 같은 타입의 빈이 여러 개 혹은 없을 경우 오류가 난다. 그래서 @Qualifier 어노테이션을 같이 사용하여, 구분을 할 수 있게 해준다.

@Autowired(required=false)를 설정하여, 꼭 설정 하지 않아도 되면, 이렇게 설정함으로 예외를 발생시키지 않을 수 있다.

@Qualifier를 정의하는 방법

  • XML
    <bean class="anyframe.sample.springmvc.annotation.web.SimpleProductCategory">
      <qualifier value="electronics"/>
      <!-- inject any dependencies required by this bean -->
    </bean>
    
  • Annotation
    @Component
    @Qualifier("electronics")
    public class ElectronicsProductCategory implements ProductCategory {
      //...
    }
    

@Qualifier

@Autowired 사용시 같은 타입이 여러 개 중에서 특정 빈을 지정할 때, @Qualifier를 사용 할 수 있다. 고로, 이를 이용해 @Autowired의 예외 상황을 막을 수 있다.

참고


Spring + Mybatis 연동하기

흔한 개발자

필요 라이브러리

  • spring-jdbc : Spring 에서 지원하는 JDBC
  • mysql-connector-java : MySQL 커넥션 드라이브를 제공
  • mybatis : myBatis
  • mybatis-spring : Spring 에서 연동을 지원하는 myBatis
  • commons-dbcp : 커넥션 풀을 담당하는 Apache Commons DBCP
        <!-- spring-jdbc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.1</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.0</version>
        </dependency>
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>1.4</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>6.0.3</version>
        </dependency>
    </dependencies>

이렇게 라이브러리를 추가하면 된다. 하지만, 여기서 부터 문제는 Spring + Mybatis 연동 방식이 각양각색이다. properties로 한다거나, xml로 한다거나 web.xml에 직접 jdbc 설정 정보를 넣거나 하는 방식이 여러가지이다. (핵심만 본다면, 결국 모든 방식이 Spring과 Mybatis를 연동하는 것 일뿐이다.)

프로젝트의 설정 하는 사람 마음대로 연동법이 각기 다르게 존재한다. (이것은 어떤 방식으로 학습을 해야 할지 갈피를 잡을 수 없다…)

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:config/spring/context/context-*.xml</param-value>
    </context-param>

web.xml에 해당 내용을 추가하게 되면, 경로에 있는 context-로 시작하는 모든 xml 파일을 스프링 컨테이너가 첫 로딩시 web.xml을 읽으면서 같이 로딩 하도록 하는 것이다. 해당 경로에는 mybatis 연동 파일들이 존재한다.

context-datasource.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 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                        http://www.springframework.org/schema/jdbc  http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd">

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/board"/>
        <property name="username" value="board_user"/>
        <property name="password" value="1234"/>
    </bean>
</beans>

context-mapper.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 http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- name은 위에서 등록한 sqlSession bean에서 사용 할 이름-->
        <!-- ref의 dataSource는 context-datasource.xml에서 정의한 bean을 참조하는 것을 의미한다.-->
        <property name="dataSource" ref="dataSource" />
        <!-- sql mapper 파일 위치의 *_sql.xml 모두 등록하기 위함이다. -->
        <property name="mapperLocations" value="classpath*:/mapper/**/*_SQL.xml" />
    </bean>

    <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg index="0" ref="sqlSession"/>
    </bean>
</beans>

위 두 파일의 내용을 찾아봐야 할 듯 하다. 무슨 의미를 하는지, 무엇을 설정하는 지에 대해 알 필요가 있을 듯 하다.

이어서 찾아 보기
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

위 내용도 추가를 해주었는데, 이는 계층별로 xml 파일이 있으면, web.xml에서 모두 로딩 될 수 있도록 등록 할 때, 사용하며 서블릿 이전에 서블릿 초기화하는 용도로 쓰이고, contextConfigLocation이라는 파라미터를 사용시, Context Loader가 로딩할 수 있는 설정파일을 여러 개 쓸 수 있다. 참고1, 참고2


반응형