예전에 Quartz를 이용해서 작업할 때 보다는 @Scheduled annotation을 사용하면 배치 작업을 무척 쉽게 만들 수 있습니다.


spring boot Scheduler @Scheduled batch


얼마나 간단하고 쉬운지 한번 만들어 보겠습니다.


spring boot Scheduler @Scheduled batch


먼저 프로젝트를 하나 생성 합니다.


spring boot Scheduler @Scheduled batch


프로젝트 이름을 대충 적어주고


spring boot Scheduler @Scheduled batch


 

화면을 만들건 아니지만 일단 Web을 추가해서 생성 합니다.

 

이제 설정을 해줍니다.


Application 메인 파일을 열고

package com.copycoding.batch;

 

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.scheduling.annotation.EnableScheduling;

 

@SpringBootApplication

@EnableScheduling

public class BatchApplication {

 

        public static void main(String[] args) {

               SpringApplication.run(BatchApplication.class, args);

        }

}

 

@EnableScheduling 어노테이션을 추가해 줍니다.

 

그리고 실제 스케쥴 작업할 class 파일을 만듭니다.

 

package com.copycoding.batch;

 

import java.time.LocalDateTime;

 

import org.springframework.scheduling.annotation.Scheduled;

import org.springframework.stereotype.Component;

 

@Component

public class ScheduleTask {

 

@Scheduled(fixedDelay = 2000)

public void task1() {

        System.out.println("The current date (1) : " + LocalDateTime.now());

}

       

@Scheduled(fixedDelayString = "${spring.task.fixedDelay}")

public void task2() {

        System.out.println("The current date (2) : " + LocalDateTime.now());

}}

 

중요한건 class 파일에 @Component를 설정해야 합니다이렇게 두개의 어노테이션을 적어주기만 하면 설정은 끝입니다.

이제 메소드를 만들고 @Scheduled를 이용해서 메소드의 작동 시간을 입력하고 코딩을 해줍니다위에 있는 프로그램을 실행하면


spring boot Scheduler @Scheduled batch


 

이런식으로 결과를 얻을 수 있습니다.  annotation만으로 쉽게 배치 작업이 완료 됩니다.

 

@Scheduled() 어노테이션에 들어가는 설정들을 정리해 보았습니다.

 

fixedDelay

@Scheduled(fixedDelay = 1000)

이전 작업이 종료된 후 설정시간(밀리터리세컨드) 이후에 다시 시작

fixedDelayString

@Scheduled(fixedDelay = “1000”)

fixedDelay와 동일 하고 지연시간(the delay in milliseconds)을 문자로 입력

fixedRate

@Scheduled(fixedRate = 1000)

설정된 시간마다 시작을 한다. 즉 이전 작업이 종료되지 않아도 시작.

fixedRateString

@Scheduled(fixedRateString = “1000”)

fixedRate와 동일 하고 지연시간(the delay in milliseconds)을 문자로 입력

initialDelay

@Scheduled(fixedRate = 5000, initialDelay = 3000)

프로그램이 시작하자마자 작업하는게 아닌 시작을 설정된 시간만큼 지연하여 작동을 시작 한다.(예제는 3초 후 부터 5초 간격으로 작업)

initialDelayString

@Scheduled(fixedRate = 5000, initialDelay = “3000”)

initialDelay와 동일 하고 지연시간(the delay in milliseconds)을 문자로 입력

cron

@Scheduled(cron = "* * * * * *")

첫번째 부터 위치별 설정 값은

(0-59)

(0-59)

시간(0-23)

(1-31)

(1-12)

요일(0-7)

zone

@Scheduled(cron = "0 0 14 * * *" , zone = "Asia/Seoul")

미설정시 local 시간대를 사용한다.

oracle에서 제공하는 문서를 참조하여 입력 한다.

https://docs.oracle.com/cd/B13866_04/webconf.904/b10877/timezone.htm

 

- copy coding -


 

프로그램 할 때 query log가 안보이면 제대로 실행이 되고 있는 건지 디버깅도 힘들고 답답하기 때문에 로그 보는 설정을 먼저 진행하게 됩니다오늘 진행하는 작업은 대부분의 Database가 동일한 방법으로 작업이 가능 합니다.

 

먼저 maven을 이용하여 log4j2를 추가 합니다.


pom.xml 

        <dependency>

            <groupId>org.bgee.log4jdbc-log4j2</groupId>

            <artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>

            <version>1.16</version>

        </dependency>

 

그리고 추가한 라이브러리가 작동할 수 있도록 application.properties

spring.datasource.driverClassName=net.sf.log4jdbc.sql.jdbcapi.DriverSpy

를 추가하고 source urllog4jdbc 를 추가 해줍니다


application.properties

## PostgreSQL

spring.datasource.driverClassName=net.sf.log4jdbc.sql.jdbcapi.DriverSpy

#spring.datasource.url=jdbc:postgresql://localhost:5432/copydb

spring.datasource.url=jdbc:log4jdbc:postgresql://localhost:5432/copydb

spring.datasource.username=copycoding

spring.datasource.password=copy123

 

다른 데이터 베이스를 사용 하는 경우에도 첫 번째 라인인 DriverSpy는 추가해 주고 url 부분을 다음처럼 변경을 합니다.

spring.datasource.url=jdbc:log4jdbc:oracle:thin:@localhost:1521/xe

spring.datasource.url=jdbc:log4jdbc:mariadb://localhost:3306/testdb

spring.datasource.url=jdbc:log4jdbc:mysql://localhost/testdb

 

그리고 실행을 하면 아래 그림처럼


sql query log


모든 값들이 정리되지 않고 나오게 되는데 이제 정리를 하기 위한 설정을 추가해 줍니다.


 

src/main/resources 폴더에 properties 파일을 하나 생성 합니다.


log4jdbc.log4j2.properties

log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator

log4jdbc.dump.sql.maxlinelength=0

  

src/main/resources 폴더에 추가로 xml 파일을 생성 합니다.


logback.xml

<?xml version="1.0" encoding="UTF-8"?>

 

<configuration>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">

    <encoder>

      <pattern>%d{yyyyMMdd HH:mm:ss.SSS} [%thread] %-3level %logger{5} - %msg %n</pattern>

    </encoder>

  </appender>

 

  <logger name="jdbc" level="OFF"/>

 

  <logger name="jdbc.sqlonly" level="OFF"/>

  <logger name="jdbc.sqltiming" level="DEBUG"/>

  <logger name="jdbc.audit" level="OFF"/>

  <logger name="jdbc.resultset" level="OFF"/>

  <logger name="jdbc.resultsettable" level="DEBUG"/>

  <logger name="jdbc.connection" level="OFF"/>

 

  <root level="INFO">

    <appender-ref ref="STDOUT" />

  </root>

 

</configuration>

 

이제 다시 실행을 하고 console을 보게 되면 


sql query log


보기 좋게 형식을 갖추어 출력이 됩니다.


- copy coding -


Spring Tool Suite 3.9.11.RELEASE를 사용하고 있는데 SVN을 설치하려고 하니 잘 안되는것 같습니다.  Eclipse Marketplace에서 검색을 하니 설치 할만 한건 ContextQuickie 인것 같아 설치를 했으나 사용이 안되는것 같고



spring boot svn


Subversive 을 설치하려고


spring boot svn


Install 버튼을 클릭하면


spring boot svn


호환이 되지 않아 설치가 진행되지 않습니다.  Git는 잘 되는것 같은데 프로젝트에서 SVN을 사용하여 방법을 찾아 보았습니다.



SVN 설치


그냥 예전에 설치하던 Install을 이용한 방법을 사용해서 해결 하였습니다.설치 방법과 project commiteclipse와 동일 합니다잘 아시는 분들은 아래 부분을 읽어볼 필요가 없습니다.

 

Help > Install New Software 메뉴를 선택 하고


spring boot svn


Install 팝업 창이 나오면


spring boot svn


Add 버튼을 클릭 하여 Add Repository 창을 오픈 합니다.


spring boot svn


여기에 Name은 아무거나 구분할 수 있도록 입력 하고 Location에는 https://dl.bintray.com/subclipse/releases/subclipse/4.3.x/ 를 입력하고 Add 버튼을 클릭 합니다.


spring boot svn


잠시 뭔가 찾는 척을 하다가


spring boot svn


설치할 항목을 보여주는데 저는 그냥 모두 선택 했습니다. 전부 설치하기 싫으면 SVNKit, Subclipse 만 선택하고 Next 버튼을 클릭 합니다.


spring boot svn


설치를 진행하려는 내용을 보여줍니다.  Next 버튼을 클릭 합니다


spring boot svn


설치할 상세 정보 입니다.  Next 버튼을 클릭 합니다.


spring boot svn


라이센스 동의를 해주고 Finish 버튼을 클릭 합니다.


spring boot svn


설치 중간에 팝업이 나오면 Install anyway 버튼과 


spring boot svn


설치 후 재시작을 하라고 Restart Now 버튼을 클릭 합니다.  Spring Boot가 재시작 하면 SVN이 생성 되어 있고 


spring boot svn


SVN Repositories를 선택하면 탭 창이 활성화 됩니다.


spring boot svn


탭 창에서 SVN 아이콘을 클릭해 줍니다.


spring boot svn


SNV 서버 주소를 입력하고 Finish 버튼을 클릭 합니다. SVN 설치가 완료 되었습니다



프로젝트 Commit

 

프로젝트에 마우스를 대고 우클릭 하여 


spring boot svn


Team > Share Project 메뉴를 선택 합니다처음에만 이런 메뉴가 나옵니다.


spring boot svn


SVN을 선택 해야 겠죠.  Next 버튼을 클릭 합니다.


spring boot svn


기존 Repository를 사용하기 위하여 Next  버튼을 클릭 합니다.


spring boot svn


프로젝트 폴더명도 그대로 사용하기로 하고 Finish 버튼을 클릭 합니다.


spring boot svn


Remember my decision을 클릭하고 Yes 버튼을 클릭 하면


spring boot svn


SVN 서버와 데이터를 비교하여 결과를 보여 주는데 처음 소스를 올리는 것이니 모두 + 표시로 나타납니다.


spring boot svn


프로젝트 명에 마우스를 놓고 우측 버튼을 클릭 해서 모두 Commit 합니다.


spring boot svn


커밋 하는 이유에 대해 comment 를 입력 하고 OK 버튼을 클릭 합니다.


spring boot svn


소스가 모두 서버에 반영되었습니다이제 끝없는 update commit의 늪에서 허우적거리면 됩니다.


- copy coding -


Spring Boot를 이용하여 security를 적용하는 방법을 연습해 봅니다.

먼저 프로젝트를 생성해야 테스트를 진행 하겠죠.


spring boot security


상단 메뉴에서 File > New > Spring Starter Project를 이용하여 프로젝트를 생성해도 됩니다.


spring boot security


프로젝트 정보는 테스트용이라 대충 적었습니다.


spring boot security


필요한 라이브러리는 Spring Security만 설명할거기 때문에 복잡하지 않도록 Spring SecuritySpring Web만 선택 했습니다그리고 Finish 버튼을 클릭 하면 자동으로 필요한 라이브러리를 추가하고 기본 소스를 생성 합니다.

 

자동으로 생성된 프로젝트를 실행 해 볼까요?


spring boot security


Spring Boot App을 선택해서 실행을 합니다.


spring boot security


로그인 화면이 나오는데 IDsecurity에서 제공하는 초기값이 user 이고 비밀 번호는 Console 창에 출력을 해줍니다.


spring boot security


이렇게 console 창에 비밀번호가 나타납니다.  복사해서 비밀번호 입력을 하고  Sign in 버튼을 클릭하면 로그인이 진행 됩니다.


spring boot security


오류 메시지가 나오는군요그럼 로그인 성공 입니다.

가야할 페이지가 없어서 나오는 오류 입니다.  프로젝트만 생성하고 화면 작업을 하나도 안했죠.


그럼 갈곳을 정해주고 다시 해봅니다.

Controller 파일을 하나 생성하고


package com.copycoding.security;


import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.ResponseBody;

import org.springframework.web.bind.annotation.RestController;


@RestController

public class TestController {

@RequestMapping("/")

    public @ResponseBody String home() throws Exception {

return "Spring Boot";

}



@RestController 어노테이션을 사용합니다화면 만들기 귀찮아 텍스트를 바로 화면으로 출력하도록 했습니다. 홈페이지에 가려고 하는 경우 Spring Boot를 출력하도록 했습니다. 이러면 로그인 하고 여기로 오겠죠.

다시 프로젝트를 실행하고 로그인을 해봅니다.


spring boot security


로그인 성공입니다.

 

이번에는 페이지를 여러개 만들어서 테스트 합니다.

 

package com.copycoding.security;

 

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.ResponseBody;

import org.springframework.web.bind.annotation.RestController;

 

@RestController

public class TestController {

       

        @RequestMapping("/")

    public @ResponseBody String home() throws Exception {

 

               return "Spring Boot ";

        }

       

        @RequestMapping("/page1")

    public @ResponseBody String pageNo1() throws Exception {

              

               return "Spring Boot : Page No 1";

        }

       

        @RequestMapping("/page2")

    public @ResponseBody String pageNo2() throws Exception {

              

               return "Spring Boot : Page No 2";

        }

}

 

프로젝트를 실행해서 로그인 하고 각 페이지에 접근해 봅니다.


spring boot security


spring boot security


또 성공입니다로그인만 되면 아무 페이지나 막 갈 수 있습니다이러려고 Spring Security를 사용하는게 아니죠?


이번엔 갈 수 있는 페이지와 없는 페이지를 한정해 봅니다그러려면 class 파일을 하나 만들어야 합니다파일에 Security Config를 설정해서 테스트를 진행 합니다.

클래스를 하나 생성 하는데 상속을 받아서 생성을 해야 합니다.


spring boot security


바로 WebSecurityConfigurerAdapter를 찾아서 상속 관계를 만들어 줍니다생성하는 파일명은 편하게 만들면 되고 파일이 만들어 지면 상속된 클래스로 부터 Method 리스트를 찾아 추가해야 합니다.


spring boot security


Method 중에 configure(HttpSecurity http)를 선택하여 추가해 줍니다.  찾기 귀찮으면 그냥 직접 입력 해서 사용 합니다.


 package com.copycoding.security;


import org.springframework.context.annotation.Configuration;

import org.springframework.security.config.annotation.web.builders.HttpSecurity;

import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;

import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;


@Configuration

@EnableWebSecurity

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {


@Override

protected void configure(HttpSecurity http) throws Exception {

http.authorizeRequests()

        .antMatchers("/").permitAll()

        .antMatchers("/page2").hasRole("ADMIN")

        .antMatchers("/page1").hasRole("USER")

        .anyRequest().authenticated();

}

}


Method가 추가 되면 페이지별 권한을 설정 합니다.

“/”는 로그인 하면 접근이 가능하고 다른 페이지 “page1”, “page2”는 아래와 같이 접속 하려면 오류가 발생 합니다.


spring boot security


잠시 Postman으로 설명을 하면


spring boot security


“/”는 접근이 가능 하지만


spring boot security


이렇게 “/page1”, “/page2”ADMIN USER 권한이 있어야 접속이 가능하므로 “Access Denied” 오류가 발생 합니다.

 

이제 ADMIN USER 권한이 있는 로그인 정보를 source에 추가해서 테스트 해보겠습니다.


 package com.copycoding.security;


import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;

import org.springframework.security.config.annotation.web.builders.HttpSecurity;

import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;

import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import org.springframework.security.crypto.password.PasswordEncoder;


@Configuration

@EnableWebSecurity

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {


@Override

protected void configure(HttpSecurity http) throws Exception {

http.httpBasic().and().authorizeRequests()

        .antMatchers("/").permitAll()

        .antMatchers("/page2").hasRole("ADMIN")

        .antMatchers("/page1").hasRole("USER")

        .anyRequest().authenticated()

        .and().logout().permitAll()

        .and().formLogin()

        .and().csrf().disable();

}

@Autowired

public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

auth.inMemoryAuthentication()

.withUser("copycoding").password(passwordEncoder().encode("copycopy")).roles("ADMIN");

auth.inMemoryAuthentication()

.withUser("honggil").password(passwordEncoder().encode("hoho")).roles("USER");

}

@Bean

    public PasswordEncoder passwordEncoder() {

        return new BCryptPasswordEncoder();

    }

}


“/”는 아무나 접근 가능 합니다.

“/page2”“ADMIN” 권한이 있어야 하는데 아래에 id/pw“copycoding”, ”copycopy” 이고

“/page1” “USER” 권한으로 로그인 정보가 “honggil”, “hoho” 이면 된다고 했습니다.

Postman으로 id password를 전송해서 테스트를 해 봅니다.


spring boot security


ADMIN 권한으로 로그인이 되어서 “/page2”에 잘 접속이 됩니다.


spring boot security


ADMIN 권한으로는 “/page1” 접근은 어림 없군요.

 

logout을 하고 honggil로 로그인 하면 반대의 결과가 나옵니다.

결과는 생략 하겠습니다.

 

logout“.and().logout().permitAll()” 이렇게 설정을 해놓았으므로 사용이 가능하고 사용하는 방법은 다음처럼 http://localhost:9090/logout 을 입력 하면 됩니다.


spring boot security


회원 로그인과 권한을 메모리에 넣고 사용하는 방법으로 테스트를 했는데 이걸 Database에 넣어서 관리하면 되겠네요.  그건 다음에 시간이 되면 테스트 해보겠습니다.


- copy coding -


1

+ Recent posts