1. 스프링부트 h2console 관련 설명

스프링부트 1.3.0 부터 h2console 기능을 제공한다.

개발자 개인개발환경(로컬local)에서 개발할 때 h2database를 Database로 사용하면 개발이 매우 용이해진다. 그리고 h2database에서는 데이터베이스에 에 접근할 수 있는 클라이언트를 제공하는데 그게 바로 h2console 이다.

이전버전에서 h2console을 사용하기 위해서는 다음과 같은 형태로 ServletRegistrationBean 으로 h2에서 제공하는 WebServlet을 서블릿빈으로 등록한다.

@Bean
public ServletRegistrationBean h2servletRegistration() {
    ServletRegistrationBean registration = new ServletRegistrationBean(new WebServlet());
    registration.addUrlMappings("/h2console/*");
    return registration;
}

스프링부트 1.3.0 부터는 @WebServlet, @WebFilter @WebListener 애노테이션을 선언한 클래스들을@ServletComponentScan으로 탐색하는 기능도 제공한다.

어쨌든~~ h2console은 기본적으로 애플리케이션의 데이터에 접근하는 부분이기 때문에 보안과 관련되어 있다. 그래서 스프링부트 1.3.0에 추가된 H2ConsoleAutoConfiguration 에서 기본보안에 대한 설정을 하는 부분도 볼 수 있다.

H2ConsoleSecurityConfiguration.java
@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass(WebServlet.class)
@ConditionalOnProperty(prefix = "spring.h2.console", name = "enabled", havingValue = "true", matchIfMissing = false)
@EnableConfigurationProperties(H2ConsoleProperties.class)
@AutoConfigureAfter(SecurityAutoConfiguration.class)
public class H2ConsoleAutoConfiguration {
 
@Autowired
private H2ConsoleProperties properties;
 
@Bean
public ServletRegistrationBean h2Console() {
String path = this.properties.getPath();
String urlMapping = (path.endsWith("/") ? path + "*" : path + "/*");
return new ServletRegistrationBean(new WebServlet(), urlMapping);
}
 
@Configuration
@ConditionalOnClass(WebSecurityConfigurerAdapter.class)
@ConditionalOnBean(ObjectPostProcessor.class)
@ConditionalOnProperty(prefix = "security.basic", name = "enabled", matchIfMissing = true)
static class H2ConsoleSecurityConfiguration {
 
@Bean
public WebSecurityConfigurerAdapter h2ConsoleSecurityConfigurer() {
return new H2ConsoleSecurityConfigurer();
}
 
@Order(SecurityProperties.BASIC_AUTH_ORDER - 10)
private static class H2ConsoleSecurityConfigurer
extends WebSecurityConfigurerAdapter {
 
@Autowired
private H2ConsoleProperties console;
 
@Autowired
private SecurityProperties security;
 
@Override
public void configure(HttpSecurity http) throws Exception {
String path = this.console.getPath();
String antPattern = (path.endsWith("/") ? path + "**" : path + "/**");
HttpSecurity h2Console = http.antMatcher(antPattern);
h2Console.csrf().disable();
h2Console.httpBasic();
h2Console.headers().frameOptions().sameOrigin();
String[] roles = this.security.getUser().getRole().toArray(new String[0]);
SecurityAuthorizeMode mode = this.security.getBasic().getAuthorizeMode();
if (mode == null || mode == SecurityAuthorizeMode.ROLE) {
http.authorizeRequests().anyRequest().hasAnyRole(roles);
}
else if (mode == SecurityAuthorizeMode.AUTHENTICATED) {
http.authorizeRequests().anyRequest().authenticated();
}
}
 
}
 
}
 
}

2. h2console을 사용하기 위한 설정

h2console을 사용하는 방법은 간단하다. application.yml(혹은 application.properties) 파일에spring.h2.console.* 속성을 정의하면 된다. 여기서 정의하는 속성은

H2ConsoleProperties.java
@ConfigurationProperties(prefix = "spring.h2.console")
public class H2ConsoleProperties {
 
/**
 * Path at which the console will be available.
 */
@NotNull
@Pattern(regexp = "/[^?#]*", message = "Path must start with /")
private String path = "/h2-console";
 
/**
 * Enable the console.
 */
private boolean enabled = false;
 
public String getPath() {
return this.path;
}
 
public void setPath(String path) {
this.path = path;
}
 
public boolean getEnabled() {
return this.enabled;
}
 
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
 
}

spring.h2.console.* 가 제공하는 속성은 크게 두가지다.

# H2 Web Console (H2ConsoleProperties)
spring.h2.console.enabled=false # Enable the console.
spring.h2.console.path=/h2-console # Path at which the console will be available.

위의 속성에서 볼 수 있듯이 h2console은 기본은 비활성화 되어 있다.

spring.h2.console.enabled=true
spring.h2.console.path=/h2console

로 설정하면 h2console 사용을 위한 기본준비는 끝난다.

스프링부트 애플리케이션을 실행시키면 * h2console 로그인창이 뜨고 ** http://localhost:8080/h2console

h2console-loging

h2console

  • h2console 창이 뜨면 정상적으로 접근이 된 것이다.

h2console-success

h2console-success

3. h2console 로그인을 통과했는데 흰화면만 나온다면?

그런데 스프링시큐리티를 사용하고 있다면 흰화면만 나오고 화면이 뜨지 않을 수도 있다. 브라우저의 개발도구를 열어 콘솔창을 보면 다음과 같은 메시지를 볼 수 있다.

h2console-white

h2console-whitescreen

Load denied by X-Frame-Options: http://localhost:9090/h2console/header.jsp?jsessionid=62e96686014c88a9e644647c7a4bf069 does not permit framing. <알 수 없음>
Load denied by X-Frame-Options: http://localhost:9090/h2console/query.jsp?jsessionid=62e96686014c88a9e644647c7a4bf069 does not permit framing. <알 수 없음>
Load denied by X-Frame-Options: http://localhost:9090/h2console/help.jsp?jsessionid=62e96686014c88a9e644647c7a4bf069 does not permit framing. <알 수 없음>
Load denied by X-Frame-Options: http://localhost:9090/h2console/tables.do?jsessionid=62e96686014c88a9e644647c7a4bf069 does not permit framing. <알 수 없음>

스프링시큐리티에서 headers 에 있는 X-Frame-Options 옵션을 거부했기 때문에 발생한다.

그래서 스프링시큐리티 설정부분에서 아래와 같이 설정해버렸다.

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                //중략 
                .anyRequest().authenticated()
            .and()
                .headers()
                    .addHeaderWriter(new StaticHeadersWriter("X-Content-Security-Policy","script-src 'self'"))
                    .frameOptions().disable();
    }

개발하는 애플리케이션의 보안 정책에 따라서 상세하게 설정하자.

위와 같은 형태로 X-Frame-Options 옵션을 비활성화하면 h2console 화면에 접근이 가능하다.


스프링부트 1.3.0이 나오고 현재 개발중인 애플리케이션의 적용버전을 바로 업그레이드 했다.

스프링부트 1.2.6 에서 1.3.0 으로 변경사항은

  • 스프링 4.1 → 4.2.1

  • 스프링시큐리티 3 → 스프링시큐리티 4

    • thymeleaf-extra-springsecurity3 → thymeleaf-extra-springsecurity4

정도의 차이가 생기겠다.

현재 ViewTemplate로 타임리프Thymeleaf 를 사용중인데, 스프링시큐리티와 연동해서 사용자가 가진 권한에 따른 처리를 하기 위해서 thymeleaf-extra-springsecurity(<https://github.com/thymeleaf/thymeleaf-extras-springsecurity>) 를 추가적으로 사용한다.

3 버전에서 4 버전으로 변경되면서 권한을 가지고 있는지 체크하는 구문이 변경된 것으로 보인다. * 3 버전

<div sec:authorize="hasRole('ROLE_USER')"></div>
<div sec:authorize="hasAnyRole('ROLE_ADMIN', 'ROLE_MANAGER')></div>
  • 4 버전

<div sec:authorize="hasAuthority('ROLE_USER')"></div>
<div sec:authorize="hasAnyAuthority('ROLE_ADMIN', 'ROLE_MANAGER')></div>

Role이 Authority 로 변경된 듯 싶다. ㅡ_-)>

스프링시큐리티 설정에서 URL별 접근권한을 설정하는 부분에서 hasAuthority를 사용하고 있어서 혹시나 하고 써봤더니 적용된다. 스프링시큐리티 문서를 보니 hasRole 등 Role 을 접미사로 사용하는 메서드는 ROLE_이라고 하는 접두사를 사용한 권한을 체크하는 목적으로 사용하는 것으로 보인다.

흠…​ ㅡ_-)> ROLE_ 붙이기 싫어서 빼먹고 권한을 정의했는데, 4버전에서 먹통이 되는 현상을 발생시키다니…​

정리

스프링시큐리티 4 적용 후 hasRole이나 hasAnyRole이 적용되지 않는다면 권한에 ROLE_ 접미사를 붙였는지 확인해보자.

레퍼런스 문서를 보는게 제일 좋다.

참고


스프링부트 1.3.0이 출시되었다.

1.3에서 추가된 부분들의 주요사항들을 살펴보면:

  • Developer Tools

    새롭게 추가된 spring-boot-devtools 모듈은 개발시 경험을 향상시키는데 초점을 두고있다. 모듈이 제공하는 기능은:

    • 민감한 속성 기본(예를 들어 템플릿 캐시를 비활성화)

    • 애플리케이션 자동 재시작

    • LiveReload 지원(살아있는 상태에서 다시 읽기…​?) 애플리케이션을 종료하지 않고도 변경사항을 다시 읽어오는 것이 가능해짐

    • 원격 개발 지원(HTTP 터널을 통한 원격 갱신과 원격 디버그 지원)

    • 재시작 동안 HTTP session 영속화

      여유가 된다면 DevTools 에 관한 짧은 소개영상을 보자.

  • Caching Auto-configuration

    EhCache, Hazelcast, Infinispan, JCache (JSR 107) implementations, Redis 과 Guava을 위한 자동설정을 제공한다. 추가적으로 인-메모리 캐시 기반의 간단한 Map도 지원한다.

  • Fully executable JARs and service support

    메이븐Maven과 그레들Gradle 플러그인을 이용해서 리눅스/유닉스에서 다음과 같은 형태로 실행할 수 있는 완벽한 실행가능한 아카이브를 생성할 수 있다.

    $ ./myapp.jar

    거기서 더 나아가, init.d 혹은 systemd 서비스로 동작한다. init.d 서비스로 설치할 때는 심볼릭만 생성하면 된다.

    $ sudo link -s /var/myapp/myapp.jar /etc/init.d/myapp
  • Color banners

    banner.txt 파일에 ANSI 칼라코드를 포함시킬 수 있다. 이런 미친 짓(?)도 가능하다.

    Meow
    Figure 1. color banner
  • Support for @WebServlet, @WebFilter and @WebListener annotations 내장 서블릿 컨테이너를 사용할 때,@ServletComponentScan을 사용하여 활성화시킨 경우 @WebServlet, @WebFilter  @WebListener 애노테이션을 사용한 클래스가 자동 등록된다.

  • Additional auto-configurations

    다음 항목들에 대한 자동설정을 제공한다. Cassandra OAuth2 Spring Session jOOQ SendGrid Artemis

  • Actuator Metrics

    측정metric 은 반출과 집계를 지원기능이 확장되었다. 추가적으로 자바8에 정의된 GaugeService CounterService 구현체(가능하다면 사용가능한)와 성능의 향상을 제공한다.

  • Update Endpoints and Health Indicators

    /logfile, /flyway  /liquibase 액츄에이터 엔드포인트를 포함하였고 엘라스틱서치, 이메일과 JMS health indicator를 지원한다.

  • Other changes

    다른 변경사항이나 향상된 기능들에 대해서는 릴리즈 노트를 살펴보라. 다음버전에서 삭제예정의 클래스와 메서드들도 찾아볼 수 있다.



[SpringBoot] 로깅 처리

1. SpringBoot Logging

스프링부트는 Commons Logging을 사용한다. Java Util Logging, Log4J, Log4J2 and Logback에 대한 기본설정을 제공한다.

2. 로그 출력형태

스프링부트에서 출력하는 기본로그 형태
2014-03-05 10:57:51.112  INFO 45469 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/7.0.52
2014-03-05 10:57:51.253  INFO 45469 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2014-03-05 10:57:51.253  INFO 45469 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1358 ms
2014-03-05 10:57:51.698  INFO 45469 --- [ost-startStop-1] o.s.b.c.e.ServletRegistrationBean        : Mapping servlet: 'dispatcherServlet' to [/]
2014-03-05 10:57:51.702  INFO 45469 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]

출력되는 내용은 다음과 같다. * 날짜와 시간: 밀리세컨드단위로 되어 정렬가능 * 로그레벨: ERROR,WARN, INFO, DEBUG or TRACE * 프로세스ID * ---: 를 통해서 실제 로그 메시지와 구분 * 스레드 네임 * 로거 네임: 로그가 찍히는 위치 * 로그 메시지


로그백Logback에서는 FATAL 레벨이 없다(ERROR로 매핑됨)

3. logback.xml 설정

스프링부트에서는 클래스패스classpath 상에서 루트에 logback.xml이 존재하면 해당설정파일을 로깅설정에 적용한다. 그래서 스프링부트에서는 logback-spring.xml으로 설정하는 것을 권장한다. 이렇게 해두고 설정파일에서 logging.config: classpath:logback-spring.xml과 같은 형태로 설정하면 로그설정을 추가등록한다.

스프링부트에서 생성하는 로그 설정은 대략 다음과 같다.

스프링 환경시스템 속성설명
logging.exception-conversion-wordLOG_EXCEPTION_CONVERSION_WORD로깅 예외발생시 사용할 관례적인 단어
logging.fileLOG_FILE정의되어 있다면 기본로그 설정에 사용됨
logging.pathLOG_PATH정의되어 있다면 기본로그 설정에 사용됨
logging.pattern.consoleCONSOLE_LOG_PATTERN콘솔에 출력되는 로그 패턴(stdout). (JDK 로거는 지원하지 않음)
logging.pattern.fileFILE_LOG_PATTERN파일에 사용될 로그 패턴(LOG_FILE 활성화된 경우). (JDK 로거는 지원하지 않음)
logging.pattern.levelLOG_LEVEL_PATTERN출력 로그레벨 형태 사용(기본%5p). (logging.pattern.level형태는 로그백에서만 지원)
PIDPID현재 프로세스 ID(OS 환경 변수로 정의되지 않은 경우에는 발견)

4. 로그백Logback 확장

스프링부트에서는 logback-spring.xml을 설정하길 권장한다. logback.xml로 설정하면 스프링부트가 설정하기 전에 로그백 관련한 설정을 하기 때문에 제어할 수가 없게 된다.

5. 스프링부트의 로그 설정을 유지하면서 로그백을 통해서 일일 로그파일 남기기

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml"/>
 
<appender name="dailyRollingFileAppender"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<prudent>true</prudent>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>applicatoin.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
 
<encoder>
<pattern>%d{yyyy:MM:dd HH:mm:ss.SSS} %-5level --- [%thread] %logger{35} : %msg %n</pattern>
</encoder>
</appender>
 
<logger name="org.springframework.web" level="INFO"/>
<logger name="org.thymeleaf" level="INFO"/>
<logger name="org.hibernate.SQL" level="INFO"/>
<logger name="org.quartz.core" level="INFO"/>
<logger name="org.h2.server.web" level="INFO"/>
 
<root level="INFO">
<appender-ref ref="dailyRollingFileAppender" />
</root>
</configuration>

위의 형태로 logback-spring.xml을 만들어서 ${project}/src/main/resources 에 위치시키고 application.properties 혹은 application.yml에 다음과 같이 설정한다.

  1. 애플리케이션 내 로그레벨 설정

spring.profiles: logging-info
logging:
  file: logs/application.log
  level:
    org.thymeleaf: INFO
    org.springframework.web: INFO
    org.hibernate.SQL: INFO
    org.quartz.core: INFO
    org.h2.server.web: INFO
 
---
spring.profiles: logging-debug
logging:
  file: logs/application.log
  level:
    org.thymeleaf: DEBUG
    org.springframework.web: DEBUG
    org.hibernate.SQL: DEBUG
    org.quartz.core: DEBUG
    org.h2.server.web: DEBUG
 
---
spring.profiles: logging-daily
logging:
  config: classpath:logback-spring.xml

6. 로그파일 실행

$ java -jar application.jar --spring.profiles.active=logging-debug,logging-daily

위의 형태로 실행하면, 실행한 위치에는 logback-spring.xml 설정에 의해 생성된 로그는application.2015-11-03.log 파일이 생성되고, application.yml에 설정한 logging.* 관련한 로그는 logs/application.log이 생성된다.



Spring application development and analysis process


최근 스프링개발환경은 많은 변화가 있었다. 고전적인 XML 설정방식외에도 JavaConfig 방식이 가능해졌고, 기존에 설정의 복잡함을 줄이기 위한 관례적인 설정을 자동으로 제공하는 자동설정Auto-configuration을 제공하는 스프링부트(http://projects.spring.io/spring-boot/) 프로젝트를 기반으로 한 애플리케이션 개발환경이 제공되고 있다.


1. 스프링애플리케이션 개발순서

  1. 스프링애플리케이션 기능설계

  2. 스프링 프로젝트 빌드

  3. web.xml 설정

    1. servlet 3.0+ 이상을 적용하는 경우에는 ServletInitializer를 통해서 web.xml 설정 대체가능

  4. 스프링 애플리케이션 컨텍스트ApplicationContext 설정

    1. application-context.xml(DAO, 서비스 영역)

      1. datasource 설정

      2. AOP 설정

    2. web-application-context.xml(웹 영영)

    3. security-context.xml(웹접근 인증 설정)

      1. URL과 사용자 권한을 기준으로 접근제어

  5. 애플리케이션 컨텍스트 정상설정 테스트

  6. MyBatis 를 사용하느냐, 하이버네이트등의 ORM 을 사용하느냐에 따라 DB 사용방식 달라짐

    1. MyBatis 인 경우에는 Database 에 스키마와 테이블들이 생성되어 있어야 함

      1. Mapper 클래스 및 SQL 을 설정해줘야 함

      2. iBatis 말고 MyBatis 사용을 권장: iBatis는 개발지원이 끝난지 꽤 되었음

      3. MyBatis와 관련된 부분은 MyBatis를 참고

    2. ORM인 경우에는 개발단계에서 ddl-auto 의 설정을 통해서 테이블 및 컬럼의 자동생성처리가 가능

      1. Spring Data JPA를 사용할 경우 기본적인 CRUD 처리를 JPARepository를 통해서 처리가 가능함

      2. 이와 관련된 자세한 내용은 자바 ORM 표준 JPA 프로그래밍 을 살펴보기

  7. 이후 애플리케이션의 패키지를 구성하고 기능별로 계층을 나눠 개발

io.honeymon.spring
  configuration
    - WebConfiguration.java
  domain
    - Member.java
    - Project.java
  repository
    - MemberRepository.java
    - ProjectRepository.java
  service
    - MemberService.java
    - DefaultMemberService.java
    - ProjectService.java
    - DefaultMProjectService.java
  web
    - MemberController.java
    - ProjectController.java
  common
    - FileUtils.java
    - DateUtils.java

위와 같은 형태로 기능별로 계층을 나눠 개발하기도 하고

io.honeymon.spring
  configuration
    - WebConfiguration.java
  member
    - Member.java
    - MemberRepository.java
    - MemberService.java
    - DefaultMemberService.java
    - MemberController.java
  project
    - Project.java
    - ProjectRepository.java
    - ProjectService.java
    - DefaultProjectService.java
    - ProjectController.java

와 같은 기능별 패키지로 나눠 개발하는 방식도 있습니다. 저도 최근에 이 방식으로 변환을 시도하고 있다.

위와 같은 형태로 개발을 진행합니다.

  1. 애플리케이션 개발에 있어서 MyBatis 보다는 JPA 사용을 선호함. MyBatis 는 4년전 이후로 사용해본 경험없음

  2. ORM을 기반으로 개발하면 엔티티 객체를 기반으로 해서 DB와의 매핑처리도 쉽고 기능구현도 쉬움.


스프링부트를 기반으로 개발하게 될 경우, 스프링과 관련된 설정의 부담감이 줄고 개발에 필요한 라이브러리들을 `Starter-POM`s 를 통해서 쉽게 처리가 가능하기 때문에 스프링 프레임워크에 대한 이해가 어느정도 있는 개발자에게는 스프링부트 사용을 권함

1.1. 화면개발

  1. 기본설정이 완료된 후에는 개발하는 화면에 따라 기능 구현 시작

  2. JSP 페이지 등의 템플릿 파일 작성

  3. Controller ModelAndView 에 템플릿 페이지 등록

    1. ajax 를 활용할 경우 화면처리를 담당할 컨트롤러와 데이터를 JSON으로 처리해줄 컨트롤러를 분리하면 좋음

    2. Spring 4.0 이후 @RestController 애노테이션이 생겨서 요청한 컨텐트타입으로 반환해주는 컨트롤러를 만들 수 있음

  4. 화면에 필요한 데이터를 담을 데이터들을 ModelAndView 혹은 템플릿엔진을 사용하는 경우에는 Model에 담아주면 ViewResolver에 의해서 처리됨

  5. 화면에 필요한 데이터에 따라서 Controller에서 Model에 담아주는 데이터가 달라지고 이는Service의 구현이 필요해짐

    1. 화면데이터에 따라 데이터, 서비스 가 달라짐

2. 스프링 애플리케이션 분석

2.1. 고전적인 XML 설정을 기반으로 한 경우

  1. web.xml 찾아서 애플리케이션 필터 설정들을 확인

  2. application-context.xml 혹은 *-context.xml 파일 분석

    1. application-context.xml: 파일은 보통 src/main/resource/META-INF에 위치

    2. web-application-context.xml: 파일은 보통 WEB-INF 에 위치

    3. 프로젝트를 설정한 사람에 따라 파일명이나 위치는 다를 수 있으므로 파일찾기를 통해서 찾아보기 바람

  3. DB 설정 확인

  4. 애플리케이션 패키지 구성 확인

  5. 애플리케이션을 구동하면서 찍히는 로그를 통해서 동작순서 확인

  6. 관련한 설정 확인

2.2. JavaConfig 인 경우

  1. @Configuration 애노테이션을 사용한 클래스 탐색

  2. 나머지 항목들은 고전적인 XML 설정을 기반으로 한 경우 와 동일

2.3. STS를 이용한 경우


STS: Spring Tool Suite http://spring.io/tools/sts
  1. spring explorer view 를 활용해서 각 설정빈을 확인 가능함

3. 참고자료


+ Recent posts