Spring4 & MyBatis

[부스트코스] 웹 프로그래밍 기반의 설정에서 추가 설정으로 확장하기 위한 포스팅

  • 목표

    • spring4 pom.xml 설정
    • mybatis dependency 추가
    • mybatis java config
    • domain, mapper, sql 샘플 작성
    • mapper 테스트
  • Spring4의 Java Config 향상 기능을 통해 Mybatis를 구성하는 xml 제외하기

  • mybatis-spring 라이브러리에서 제공하는 @MapperScan 어노테이션을 사용하여 MyBatis Domain Mapper에 대해 패키지단위로 검색이 가능하다.

  • Servlet 3+와 결합하는 경우 XML없이 응용 프로그램을 구성하고 실행할 수 있다.

프로젝트 스펙 및 설정 구조

모듈 버전 설명
Spring Framework 4.3.15.RELEASE
MyBatis 3.5.2
mybatis-spring 2.0.2
- src/main
    - java/com/seok/mybatis/
                    config/
                        * AppConfig.java
                        * DBConfig.java
                        * MybatisConfig.java
                    domain/
                        * Product.java
                    persistence/
                        * ProductMapper.java
    - resources/mapper/
            * ProductMapper.xml
* pom.xml

프로젝트 구조

설정

  • pom.xml

    • MyBatis 사용을 위한 mybatis, mybatis-spring dependency를 추가 한다.

    • 물론 DB를 사용하기 위한 프로젝트이기 때문에 spring-jdbc, mysql-connector-java도 추가

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

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>${spring.version}</version>
    </dependency>

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

    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.2</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.2</version>
    </dependency>
  • AppConfig.java
    • 스프링 설정 클래스
    • DBConfig와 MyBatisConfig를 분리해서 Import하였다.
    • 같은 클래스안에서 설정해도 무관

@Configuration
@Import({DBConfig.class, MybatisConfig.class})
public class AppConfig {
}
  • DBConfig.java
    • DB 설정관련 클래스
    • java 파일 내에 설정 정보를 넣지 않고 properties 파일을 사용하여 보안 및 배포 관리

@Configuration
@EnableTransactionManagement
@PropertySource({ "classpath:db.properties" })
public class DBConfig implements TransactionManagementConfigurer {

    @Value("${spring.datasource.driver-class-name}")
    private String driverClassName;

    @Value("${spring.datasource.url}")
    private String url;

    @Value("${spring.datasource.username}")
    private String userName;

    @Value("${spring.datasource.password}")
    private String password;

    @Override
    public PlatformTransactionManager annotationDrivenTransactionManager() {
        return transactionManager();
    }

    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }

    @Bean
    public DataSource dataSource() {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName(driverClassName);
        dataSource.setUrl(url);
        dataSource.setUsername(userName);
        dataSource.setPassword(password);
        return dataSource;
    }
}
  • MybatisConfig.java

    1. line 2: MyBatis Mapper가 있는 패키지 설정
    2. line 9: MyBatis.xml 파일에서 resultType으로 사용할 수 있는 도메인 개체를 포함하는 패키지 설정
      (SQL쿼리에서 Java 객체를 반환하는 것은 ORM 문제를 해결)
    • MyBatis를 설정하기 위해 SqlSessionFactoryBean를 사용

@Configuration
@MapperScan("com.seok.mybatis.persistence")
public class MybatisConfig {

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) throws IOException {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        sessionFactory.setTypeAliasesPackage("com.seok.mybatis.domain");
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
        return sessionFactory;
    }
}

  • Product.java
    • Product 도메인 객체는 실제 단순한 POJO

public class Product implements Serializable{

    private static final long serialVersionUID = 3690807750019415954L;

    private int id;
    private int categoryId;
    private String desc;
    private String content;
    private String event;
    // constructor, setter, getter

}
  • ProductMapper.java
    • Mapper 클래스는 해당 mapper.xml에 정의된 sql문과 일치하는 메서드를 정의하는 단순한 인터페이스이다.
    • xml로 SQL을 정의하는 대신 어노테이션으로 간단한 SQL문을 작성하는 것이 가능하지만, query가 번거로워져 복잡한 Query는 사용하지 않는다.

import org.apache.ibatis.annotations.Select;
import kr.or.seok.naver.domain.Product;

public interface ProductMapper {

    public List<Product> getAllProducts();

    // Simple SQL
    @Select("SELECT COUNT(*) totalCnt FROM product")
    public int getTotalCnt();

}

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.seok.mybatis.persistence.ProductMapper">
    <cache />

    <select id="getAllProducts" resultType="Product">
        SELECT 
            id, 
            category_id, 
            description, 
            content, 
            event 
        FROM product
    </select>

</mapper>

테스트

  • TestMyBatis.java
    • Test 코드는 Mockito나 Junit을 사용하는 것이 편리

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class TestMyBatis {

    @Autowired
    ProductMapper prodMapper;

    @Test
    public void testMapper() {
        // prodMapper.getAllProducts();

    }

    @Test
    public void testTotalProducts() {
        // productMapper.getTotalCnt();
    }
}

결과

  • TotalCnt()
    • Product의 전체 row 갯수를 확인하기 위한 메서드로 50개가 조회되고 있음을 알 수 있다.

23:53:03.208 [main] DEBUG com.seok.mybatis.persistence.ProductMapper.getTotalCnt - ==>  Preparing: SELECT COUNT(*) totalCnt FROM product 
23:53:03.251 [main] DEBUG com.seok.mybatis.persistence.ProductMapper.getTotalCnt - ==> Parameters: 
23:53:03.276 [main] DEBUG com.seok.mybatis.persistence.ProductMapper.getTotalCnt - <==      Total: 1
23:53:03.282 [main] DEBUG org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3daf7722]
23:53:03.282 [main] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
23:53:03.336 [main] INFO com.seok.mybatis.config.TestMyBatis - 50
  • getAllProducts()
    • Product의 모든 row를 조회하기 위한 테스트 메서드로 50개의 Product리스트가 조회되고 있음을 확인할 수 있다.

23:53:03.348 [main] DEBUG com.seok.mybatis.persistence.ProductMapper.getAllProducts - ==>  Preparing: SELECT id, category_id, description, content, event FROM product 
23:53:03.348 [main] DEBUG com.seok.mybatis.persistence.ProductMapper.getAllProducts - ==> Parameters: 
23:53:03.372 [main] DEBUG com.seok.mybatis.persistence.ProductMapper.getAllProducts - <==      Total: 50
23:53:03.379 [main] DEBUG org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@52066604]
23:53:03.379 [main] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
23:53:03.419 [main] INFO com.seok.mybatis.config.TestMyBatis - [ {
    "id" : 1,
    "categoryId" : 0,
    "desc" : null,
    "content" : "",
    "event" : ""
    },
    ...
}] 

[BoostCourse] Swagger API Document 적용

 - 스프링에 Swagger 적용하는 방법 익히기

Pom.xml spac (Source)

 - spring.version = 4.3.15.RELEASE (5.x.x 버전의 경우 모든 메서드를 구현하지 않아도 됨)

 - jackson.version = 2.9.8

 - swagger.version = 2.9.2

groupId module

version

scope

javax.servlet javax.servlet-api 3.1.0 provided
javax.servlet.jsp javax.servlet.jsp-api 2.3.2-b02 provided
javax.servlet jstl 1.2  
org.springframework spring-webmvc ${spring.version}  
org.springframework spring-test ${spring.version}  
com.fasterxml.jackson.core jackson-core ${jackson.version}  
com.fasterxml.jackson.core jackson-databind ${jackson.version}  
junit junit 4.12 test
io.springfox springfox-swagger2 ${swagger.version}  
io.springfox springfox-swagger-ui ${swagger.version}  

 

config 설정 파일 리스트 (Source)

 1. WebAppInitializer

클래스명 상속 설명
WebAppInitializer AbstractAnnotationConfigDispatcherServletInitializer  
구현 메서드  - getRootConfigClasses()  - Spring 기본 설정파일 클래스를 지정
 - ApplicationConfig.class
 - getServletConfigClasses()  - Spring MVC 설정 파일 클래스를 지정
 - MvcConfig.class, SwaggerConfig.class
 - getServletMappings()  - DispatcherServlet이 동작할 맵핑정보를 설정
 - "/"를 설정한다는 것은 모든 요청을 DispatcherServlet이 처리
 - getServletFilters()  - 인코딩 필터를 설정

 

 2. ApplicationConfig

클래스명 상속 설명
ApplicationConfig @Configuration
@ComponentScan(basePackages = {"service"})
 - Spring MVC에서 사용할 Bean들을 설정하는 스프링 설정

 

 3. MvcConfig

클래스명 상속 설명
MvcConfig @Configuration
@EnableWebMvc
WebMvcConfigurer
@ComponentScan(basePackages = { "controller" })
 
구현 메서드  - configureDefaultServletHandling()  - DefaultServlet에 대한 설정
 - DispatcherServlet이 처리하지 못하는 URL은 DefaultServlet이 처리
 - 해당 설정이 없으면 자동 생성된 Swaager 페이지를 볼 수 없다.

 

4. Swagger

클래스명 상속 설명
Swagger @Configuration
@EnableSwagger2
 
구현 메서드 @Bean
Docket api()
 - Swagger 사용 시에는 Docket Bean 을 품고있는 설정 클래스 1개가 기본으로 필요
 - Spring Boot 에서는 기본적인 설정파일 1개로 Swagger 와 Swagger UI를 함께 사용가능하지만 Spring MVC 의 경우 Swagger UI를 위한 별도의 설정이 필요
 - Swagger UI 를 ResourceHandler 에 수동으로 등록해야 하는 작업인데, SpringBoot 에서는 이를 자동으로 설정해주지만 Spring MVC 에서는 그렇지 않기 때문이다.

 

Controller, Service, Dto

 - Controller (Source)

클래스명 Swagger Annotation
CalculatorApiController  
CalculatorResult plus(value1, value2) @ApiOperation(value = "덧셈 구하기") 
@ApiResponses({  
            @ApiResponse(code = 200, message = "OK"), 
            @ApiResponse(code = 500, message = "Exception") 
})
CalculatorResult minus(value1, value2)
@ApiOperation(value = "뺄셈 구하기")
@ApiResponses({  // Response Message에 대한 Swagger 설명
            @ApiResponse(code = 200, message = "OK"),
            @ApiResponse(code = 500, message = "Exception")
})

 

Swagger Page

 - 설정 후 http://localhost:8088/swagger-ui.html로 접근 시 Swagger UI로 확인 가능

 

Dynamic Web Project 3.1 spec

 - Servlet 클래스 작성 시 web.xml 변화 확인 3.x spec부터는 annotation으로 설정

 - 프로젝트 생성

 - 다음 페이지

 - 다음 페이지 web.xml 생성

 - 프로젝트 구조 확인

 - web.xml 확인

 - Servlet 클래스 작성

 - URL Mapping 수정

 - 메서드 생성

 - 생성된 서블릿 확인 @WebServlet("URL")이 생기는 점이 2.5 spec과 3.x spec의 차이점
(web.xml에 Servlet 등록 태그로 설정하지 않고 annotation으로 설정)

 - Servlet 생성 후 web.xml 확인 ( 변화없음 )

 

Dynamic Web Project 2.5 spec

 - 해당 페이지 내용은 2.5 spec과 3.0이상 web.xml 변화 차이를 확인하기 위하여 작성하였습니다.

 - Dynamic Web Project 프로젝트 만들기

 - 다음 화면


 - 다음 화면 (Generate web.xml... 체크)

 - 생성된 프로젝트 및 web.xml 확인
폴더 구조

web.xml 생성 확인

 - Servlet 클래스 작성

 - URL Mapping 수정 ("/MainServlet")

 - 

 - servlet 클래스 작성으로 자동으로 수정되는 web.xml 확인

[Java Config] xml 설정 대신 Java 설정으로 바꿔보기

 

BoostCourse 프로젝트 설정 수정해보기

 

Convert web.xml to WebApplicationInitializer
 - 프로젝트에 필요한 내용만 web.xml에 설정을 했으나 그마저도 Java Config로 변경하고자 함

web.xml
convert web.xml to Java Config

 

참고 사이트

https://www.baeldung.com/spring-xml-vs-java-config

[웹 프론트엔드 취약점]

 - View에서 Controller로 보낸 데이터의 유효성 검사 필요

 - Spring Filter를 사용한 XSS(Cross-Site Scripting) 처리 필요

 


 

한줄평 400자 이상 입력 후 제출 시 백엔드에서도 체크할 수 있도록 처리

이미지 확장자 jpg, png외에 다른 확장자의 파일을 업로드를 시도할 경우, 한줄평 등록 기능에 대한 방어로직 필요

별점 값이 설정되어 있지 않음에도 한줄평이 제출될 경우, 한줄평이 등록 기능에 대한 방어로직 필요

별점 값의 range를 제한하여 체크하는 로직이 필요

[Verification & Validation]

Verification (검증)

 - Verification Process는 문서, 설계, 코드, 프로그램을 체크하는 것.

 - 제품이 올바르게 만들어지고 있는지 체크, 요구사항, 설계 명세서에 따라 만들어지고 있는지 확인

Validation (유효성 검사)

 - Validation이란 소프트웨어 테스트의 동적 메커니즘이며, 실제 제품을 검증하는 것.

 - 올바른 제품을 만들었는지 체크, 비즈니스 요구사항을 충족하는지 여부를 결정

Difference Verification & Validation
  Verification Validation
정의 Verfication Process에는 문서, 설계, 코드 및 프로그램 점검이 포함된다. 실제 제품을 테스트하고 검증하는 동적 메커니즘
코드실행 여부 코드 실행을 포함하지 않는다. 코드 실행을 항상 포함한다.
실행 검증은 review, walkthroughs, inspections, 그리고 desk-checking 등과 같은 방법을 사용한다. Black Box 테스트, White Box 테스트, 비기능 테스트와 같은 방법을 사용한다.
질문 사항 소프트웨어가 사양에 부합한가? 소프트웨어가 고객의 요구사항 및 기대 요건을 충족하는가 ?
  개발 주기 초기에 버그를 발견 검증 프로세스에서 잡을 수 없는 버그를 찾을 수 있다.
평가 대상 대상은 어플리케이션소프트웨어 아키텍처, 사양, 전체 설계, 상위 수준데이터베이스 설계 등이다. 대상은 실제 제품
  QA 팀은 SRS 문서의 요구 사항에 부합하는지 검증한다. 테스트 팀의 참여와 함께 소프트웨어 코드에 대한 유효성 검사가 실행된다.
순서 유효성 확인 전에 제공 검증 후에 제공

 

 


프로젝트 적용 내용

 - Verification 

 - Validation 

 

 

[프로젝트 작성 후 보완해야 하는 비기능성 요소]

 - 수정 및 수정이 필요한 근거


레이어 팝업 컨텐츠(Layer Popup Content)
 * 팝업 창 차단 기능이 있는 브라우저에서 시작적으로 팝업 창과 같은 효과를 내도록 구현한 컨텐츠
 레이어 팝업 컨텐츠는 같은 페이지의 일부 영역을 가리고 그 위에 표시되므로 그 뒤의 컨텐츠를 보기 위해서는 반드시 레이어 팝업을 화면에서 사라지도록 해야 한다. 레이어 팝업은 웹 페이지에 포함되는 컨텐츠이므로 초점 이동과 컨텐츠의 선형구조를 위반하지 않도록 구현해야 한다.

 

초점 이동
 * 키보드에 의한 초점은 논리적으로 이동해야 하며, 시각적으로 구별할 수 있어야 한다.

 웹 페이지에서 제공하는 모든 기능을 키보드만으로 사용하는 경우에도 사용자 입력 간의 초점 이동은 적절한 순서를 따라야 하며, 이 과정에서 컨텐츠 조작이 불가능한 상태가 되거나 갑작스러운 페이지의 전환 등이 일어나지 않아야 한다.

 또한 초점을 받은 컨텐츠는 저시력 장애인과 지체 장애인들이 인지할 수 있도록 시각적으로 구별되어야 한다.

 (1) 초점 이동 순서 유지 : 사용자가 키보드를 이용하여 초점을 이동하는 경우 이동 순서가 관례를 벗어나면 사용자에게 혼란을 주기 때문에 초점 이동 순서는 사용자가 예측하는 이동 순서와 일치하여야 한다. 바람직한 방법은 기존의 관례를 따르도록 콘텐츠를 제공하는 것이다. 관례와 달리 초점 이동 순서를 결정해야 하는 경우에는 사용자 입력 간의 이동 순서가 논리적이 되도록 구현해야 한다. 예를 들어, 사용자 아이디, 비밀번호를 입력하는 입력 창과 로그인 버튼 간의 초점 이동 순서는 사용 자 아이디, 비밀번호, 로그인 버튼의 순서이어야 한다. 

(2) 함정 또는 오류 방지 : 웹 콘텐츠는 더 이상 키보드 조작이 불가능한 상태가 되어 다음 사용자 입력 또는 컨트롤 등으로 초점을 이동할 수 없거나 이전 페이지로 초 점을 이동할 수 없는 상태가 되지 않도록 구현하여야 한다.

(3) 초점의 시각화 : 사용자 입력 등이 위치 지정 도구(마우스)나 키보드 조작을 통해 초점을 받았을 때, 해당 컨트롤이 초점을 받았음을 시각적으로 구별할 수 있음을 의미한다. 대표적인 예로 키보드 조작을 통해 버튼이 초점을 받았을 때 이 버튼의 주위에 점선의 테두리가 표시되는 것을 들 수 있다. 위치 지정 도구에 의한 초점과 키보드에 의한 초점의 표시 방법이 다른 것도 허용한다. 

검사 항목 6.1.2 절을 준수함으로써 얻을 수 있는 기대 효과는 다음과 같다. 

(1) 화면 낭독 프로그램을 이용하는 사용자의 경우, 사용자 입력 주변의 상하좌우에 위치한 콘텐츠에 대한 정보를 알 수 없다. 따라서 웹 콘텐츠를 사용하는 과정에서 키보드 조작에 의한 사용자 입력 간의 이동 순서는 관례를 따라야 한다. 그렇지 않으면 사용자 입력의 조작 과정에서 혼란을 주게 된다. 

(2) 마우스나 키보드 조작을 통해 특정 영역으로 컨트롤을 이동하였을 경우에 해당 영역이 초점을 받았음을 시각적으로 알려준다면 저시력 장애인, 노인, 지체 장애인뿐 만 아니라 비장애인들도 어느 컨트롤이 선택되었고 활성화시킬 수 있는지 쉽게 인지할 수 있다. 

 

컨텐츠의 선형구조
웹 페이지의 모든 컨텐츠는 시각적인 2차원 공간의 상하좌우로 배치되어 있어서 원하는 곳을 바로 찾아가거나 그 기능을 바로 선택하여 실행할 수 있다. 그러나 화면 낭독 프로그램 사용자는 모든 컨텐츠를 순차적으로 접근할 수 있기 때문에 시각적인 배치가 아닌 읽어주는 순서가 중요하다. 여기서 컨텐츠가 보조 기술로 제공되는 순서를 컨텐츠의 선형 구조라고 하며, 이 구조는 논리적이어야 한다. 마크업 언어로 제작된 컨텐츠의 선형 구조는 스타일 시트와 테이블 구조들을 제거하면 얻을 수 있다. 
컨텐츠의 논리성
 * 컨텐츠는 논리적인 순서로 제공해야 한다.

콘텐츠는 보조 기술 사용자가 맥락을 이해할 수 있도록 논리적인 순서로 제공해야 한다.

(1) 콘텐츠의 선형 구조 유지 : 웹 페이지를 구성하는 모든 콘텐츠는 사용자가 그 내용을 이해할 수 있도록 선형 구조로 작성되어야 한다. 

(2) 내용, 표현 및 기능 분리 : 브라우저 화면에 표시되는 콘텐츠의 순서는 웹 페이지에 수록된 콘텐츠의 선형 구조와 항상 같은 것은 아니다. 예를 들어 스타일 시트를 사용하면 웹 페이지를 구성하는 콘텐츠의 순서를 변경하지 않고도 화면에 표시되는 콘텐츠의 배치를 임의로 변경할 수 있다. 따라서 웹 페이지를 구성하는 콘텐츠의 나열 순서는 그 맥락을 이해할 수 있도록 논리적으로 구성해야 한다. 시각적으로 배치를 변경해야 하는 경우에도 콘텐츠의 선형 구조는 유지되어야 한다.

검사 항목 7.3.1 절을 준수함으로써 얻을 수 있는 기대 효과는 다음과 같다. 

(1) 콘텐츠의 선형 구조가 논리적인 콘텐츠는 지적 장애, 언어 장애 및 학습 장애가 있는 사용자들이 콘텐츠를 이해하는 데 도움을 준다. 

(2) 콘텐츠의 선형 구조가 논리적인 웹 콘텐츠는 스타일 시트(style sheet)를 바꾸거나 기능을 제거하더라도 그 내용을 순서대로 읽어 문서의 의미를 이해하기가 쉽다.

 

+ Recent posts