'Spring'에 해당되는 글 49건

  1. 2020.05.20 [Spring] Spring Security Disabled.
  2. 2020.05.08 [Thymeleaf] Spring boot + Thymeleaf 사용 시 값 전달 주의 사항.
  3. 2020.04.28 [Thymeleaf] <a> onclick 및 modal 에 변수 전달 하기.
  4. 2020.04.24 [Thymeleaf] Springboot + Thymeleaf 사용 시 viewResolver 이슈.
  5. 2020.04.21 [Spring] spring security + javax api 사용에 따른 JDK 버전 문제.
  6. 2020.04.21 [Spring] Spring Cloud Config Client 구성 시 주의점
  7. 2020.04.09 [Spring] WebClient 사용 예제
  8. 2020.04.09 [Spring] Scheduler + WebClient Hello ~
  9. 2020.03.26 [AWS] Spring Cloud Config S3 Backend 사용하기
  10. 2019.10.10 [Reference] How to do in java

[Spring] Spring Security Disabled.

ITWeb/개발일반 2020. 5. 20. 15:10

spring security 를 사용하면서 테스트를 위해 인증을 off 하고 싶을 때 사용 하면 됩니다.

 

Application main)
@SpringBootApplication(exclude = SecurityAutoConfiguration.class)

application.yml)
security:
  enable:
    csrf: false
  basic:
    enabled: false
  ignored: /**
management:
  security:
    enabled: false

위에서 처럼 두 군데에 적용해 주면 됩니다.

:

[Thymeleaf] Spring boot + Thymeleaf 사용 시 값 전달 주의 사항.

ITWeb/개발일반 2020. 5. 8. 16:00

ModelAndView 를 이용해서 Controller 에서 HTML 로 값을 넘길 때 템플릿 엔진으로 Thymeleaf 를 사용하고 있다면 꼭 Model, VO, DTO 등의 객체를 만들어서 넘기도록 합니다.

그냥 Object 로 넘기게 되면 type casting 에 대한 문제가 발생 하거나 삽질을 경험 할 수 있습니다.

 

제가 경험한 내용은 LocalDateTime 값을 잘 못 넘겨서 String to DateTime 을 할 수 없다고 계속 에러를 내거 삽질 했네요.

 

#conversions, #strings, #dates, #temporals 다 그냥 시도해도 Thymeleaf 엔진에서 오류 떨어집니다.

:

[Thymeleaf] <a> onclick 및 modal 에 변수 전달 하기.

ITWeb/개발일반 2020. 4. 28. 21:25

알면 쉽고 모르면 고생하는...

 

<a class="dropdown-item" th:attr="onclick=|alert('${thingCertificate.certificateId}')|">상세정보</a>

<a class="dropdown-item" th:attr="onclick=|alert('${thingCertificate.certificateId}')|">상세정보</a>

 

<a class="dropdown-item"

  data-toggle="modal"

  href="#deleteThingCertificateModal"

  th:data-certid="${thingCertificate.certificateId}"

  th:data-certname="${thingCertificate.certificateInfo == null ? '인증서명이 존재 하지 않습니다.' : thingCertificate.certificateInfo.certName}">

인증서 삭제

</a>

...중략...

<th:block layout:fragment="script">
<script type="text/javascript">
$('#deleteThingCertificateModal').on('show.bs.modal', function (e) {
var certId = $(e.relatedTarget).data('certid');
var certName = $(e.relatedTarget).data('certname'); console.log(certId);
console.log(certName);
}
);
</script>
</th:block>

 

<a class="dropdown-item" 
  data-toggle="modal" 
  href="#deleteThingCertificateModal" 
  th:data-certid="${thingCertificate.certificateId}" 
  th:data-certname="${thingCertificate.certificateInfo == null ? '인증서명이 존재 하지 않습니다.' : thingCertificate.certificateInfo.certName}">
인증서 삭제
</a>

...중략...

<th:block layout:fragment="script">
<script type="text/javascript">
$('#deleteThingCertificateModal').on('show.bs.modal', function (e) {
    var certId = $(e.relatedTarget).data('certid');
    var certName = $(e.relatedTarget).data('certname');
    console.log(certId);
    console.log(certName);
  }
);
</script>
</th:block>

기본 전체는 Modal 창을 띄울 수 있어야 합니다.

그럼 위 내용이 쉽게 이해가 됩니다.

 

 

:

[Thymeleaf] Springboot + Thymeleaf 사용 시 viewResolver 이슈.

ITWeb/개발일반 2020. 4. 24. 08:17

이전 글 참고)

https://jjeong.tistory.com/1386

 

controller 단에서 viewName 작성 시 "/" 를 제거 하고 작성을 하셔야 합니다.

 

Spring framework 의 UrlBasedViewResolver.java 내 코드를 보면,

protected AbstractUrlBasedView buildView(String viewName) throws Exception {
   Class<?> viewClass = getViewClass();
   Assert.state(viewClass != null, "No view class");
   AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(viewClass);
   view.setUrl(getPrefix() + viewName + getSuffix());
   
   ...중략...
}

Thymeleaf 기본 설정을 보면,

spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html

...중략...

 

로컬에서는 되는 viewName 그러나 서버에서 안되는 ...)

    @GetMapping("/list")
    public String list(Model model) {
        return "/list";
    }

 

서버에서 되는 viewName)

    @GetMapping("/list")
    public String list(Model model) {
        return "list";
    }

둘 다 resources/templates/list.html 을 찾습니다.

만약, templates/thing/list.html 로 설정을 해야 한다면,

 

return "thing/list"; 

로 작성을 하시면 됩니다.

로컬에서만 테스트 하다 보면 이런 내용도 왜 안되지 하고 있을 때가 있어서 기록 합니다.

:

[Spring] spring security + javax api 사용에 따른 JDK 버전 문제.

ITWeb/개발일반 2020. 4. 21. 17:56

구글링 하면 많이 나옵니다.

 

spring security 사용 시 JDK 를 11 사용 하게 되면 아래와 같은 오류가 발생 할 떄가 있습니다.

 

에러)

 

Class Not Found javax/xml/bind/DatatypeConverter

 

해당 API 는 JDK 11 에서 삭제 되었기 때문에 그렇습니다.

그래서 해결을 하기 위해서는 두 가지 방법이 있습니다.

 

1. JDK 1.8 을 사용 하거나

2. javax-api dependency 를 잡아 주시면 됩니다.

compile group: 'javax.xml.bind', name: 'jaxb-api', version: '2.3.1'

 

:

[Spring] Spring Cloud Config Client 구성 시 주의점

ITWeb/개발일반 2020. 4. 21. 16:35

주의점 이라기 보다 그냥 설정을 잘 못 하는 경우가 있어서 기록 합니다.

 

Client 쪽 Dependency 등록)

implementation 'org.springframework.cloud:spring-cloud-starter-config'
implementation 'org.springframework.boot:spring-boot-starter-actuator'

 

Client 쪽 bootstrap.yml 등록)

spring:
  cloud:
    config:
      uri: http://${CONFIG_SERVER_HOST}:${PORT}

management:
  endpoints:
    web:
      exposure:
        include: refresh

 

Refresh 호출)

- HTTP Method : POST

- http://${CONFIG_CLIENT_HOST}:${PORT}/actuator/refresh

 

bootstrap.yml 에 등록 하지 않게 되면 client 가 config server 를 localhost:8888 로 찾는 오류가 발생 할 수 있습니다.

또한 refresh 등록을 해줘야 actuator/refresh 사용이 가능 합니다.

 

:

[Spring] WebClient 사용 예제

ITWeb/개발일반 2020. 4. 9. 17:01

https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/function/client/WebClient.RequestHeadersSpec.html#retrieve--

- retrieve()
Perform the HTTP request and retrieve the response body.
This method is a shortcut to using exchange() and decoding the response body through ClientResponse.

- exchange()
Perform the HTTP request and return a ClientResponse with the response status and headers. You can then use methods of the response to consume the body

 

https://spring.io/guides/gs/reactive-rest-service/

 

[Non Blocking]

// retrieve() 예제
  @Override
  public void runner() {
    Mono<String> response = WebClient
        .create(watcherEndpointHost)
        .method(HttpMethod.POST)
        .uri(watcherEndpointUri)
        .contentType(MediaType.APPLICATION_JSON)
        .accept(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML)
        .acceptCharset(Charset.forName("UTF-8"))
        .body(BodyInserters.fromValue(getCheckQuery()))
        .retrieve()
        .bodyToMono(String.class);

    response.subscribe(result -> {
      logger.debug("{}", result);
    }, e -> {
      logger.debug("{}", e.getMessage());
    });
  }
  
// exchange() 예제  
  @Override
  public void runner() {
    Mono<String> response = WebClient
        .create(watcherEndpointHost)
        .method(HttpMethod.POST)
        .uri(watcherEndpointUri)
        .contentType(MediaType.APPLICATION_JSON)
        .accept(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML)
        .acceptCharset(Charset.forName("UTF-8"))
        .body(BodyInserters.fromValue(getCheckQuery()))
        .exchange()
        .flatMap(clientResponse -> clientResponse.bodyToMono(String.class));

    response.subscribe(result -> {
      logger.debug("{}", result);
    }, e -> {
      logger.debug("{}", e.getMessage());
    });
  }

 

[Blocking]

  @Override
  public void runner() {
    String response = WebClient
        .create(watcherEndpointHost)
        .method(HttpMethod.POST)
        .uri(watcherEndpointUri)
        .contentType(MediaType.APPLICATION_JSON)
        .accept(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML)
        .acceptCharset(Charset.forName("UTF-8"))
        .body(BodyInserters.fromValue(getCheckQuery()))
        .exchange()
        .block()
        .bodyToMono(String.class)
        .block();

    logger.debug("{}", response);
  }
  
  @Override
  public void runner() {
    Mono<String> response = WebClient
        .create(watcherEndpointHost)
        .method(HttpMethod.POST)
        .uri(watcherEndpointUri)
        .contentType(MediaType.APPLICATION_JSON)
        .accept(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML)
        .acceptCharset(Charset.forName("UTF-8"))
        .body(BodyInserters.fromValue(getCheckQuery()))
        .retrieve()
        .bodyToMono(String.class);

    String result = response.block();
    
    logger.debug("{}", result);
  }

 

WebClient  는 기본 async 방식으로 동작 합니다.

그리고 어디선가 문서에서 봤는데 Connection Pool 관련 고민을 하지 않고 사용해도 된다고 했던 것으로 기억 합니다.

구글링 하다 걸린 코드 걸어 둡니다.

 

[Timeout 설정]

// TcpClient 이용
TcpClient tcpClient = TcpClient.create()
                 .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000) // Connection Timeout
                 .doOnConnected(connection ->
                         connection.addHandlerLast(new ReadTimeoutHandler(10)) // Read Timeout
                                   .addHandlerLast(new WriteTimeoutHandler(10))); // Write Timeout
WebClient webClient = WebClient.builder()
    .clientConnector(new ReactorClientHttpConnector(HttpClient.from(tcpClient)))
    .build();


// ReactorClientHttpConnector 이용
ReactorClientHttpConnector connector =
            new ReactorClientHttpConnector(options ->
                    options.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 2000));
WebClient webClient = WebClient.builder().clientConnector(connector).build();

 

보시면 아시겠지만 방법은 다양하게 많이 있습니다.

직관적이고 이해 하기 쉬운 방법을 찾아서 사용하시면 될 것 같습니다.

 

:

[Spring] Scheduler + WebClient Hello ~

ITWeb/개발일반 2020. 4. 9. 15:09

필요한 Dependency)

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-quartz'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.springframework.boot:spring-boot-starter-webflux'
}

 

Main Application 에서 @EnableScheduling 을 작성 해 주시면 아주 쉽게 Annotation 설정 만으로 사용 하실 수 있습니다.

예제는 아래 문서 보시면 너무 쉽게 되어 있습니다.

https://spring.io/guides/gs/scheduling-tasks/

 

추가적인 내용을 조금 더 작성 하자면,

- fixedDelay

실행 시간을 포함해서 완료 된 이후 타이머가 동작 하는 방식 입니다.

 

- fixedRate

실행 시간 상관 없이 그냥 지정된 타이머 주기로 동작 하는 방식 입니다.

결국, 실행 시간이 길 경우 중복 실행이 될 수 있습니다.

 

- PeriodicTrigger

특정 주기에 맞춰서 실행 됩니다.

fixedRate 과 유사한 방식 이라고 보시면 됩니다.

 

- CronTrigger

cron 설정 주기에 맞춰서 실행 됩니다.

 

저는 그냥 Web Service 형태로 해서 구성을 해서 아래와 같이 적용했습니다.

 

Scheduler 에 대한 Abstract 를 만들어서 사용했습니다. (구글링 해보면 예제 코드 많이 나옵니다.)

  @PostConstruct
  public void init() {
    this.startScheduler();
  }

  @Override
  public void runner() {
    String response = WebClient
        .create(watcherEndpointHost)
        .method(HttpMethod.POST)
        .uri(watcherEndpointUri)
        .contentType(MediaType.APPLICATION_JSON)
        .accept(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML)
        .acceptCharset(Charset.forName("UTF-8"))
        .body(BodyInserters.fromValue(getCheckQuery()))
        .exchange()
        .block()
        .bodyToMono(String.class)
        .block();

    logger.debug("{}", response);
  }

  @Override
  public Trigger getTrigger() {
    return new PeriodicTrigger(10, TimeUnit.SECONDS);
  }

  @Override
  public String getCheckQuery() {
    ...중략...
    return checkQuery;
  }

GenericScheduler 를 Singleton 으로 만들어서 Abstract 를 하나 구성했습니다.

  public void startScheduler() {
    GenericScheduler.getInstance()
        .schedule(getRunnable(), getTrigger());
  }

  public void stopScheduler() {
    GenericScheduler.getInstance().shutdown();
  }

  private Runnable getRunnable(){
    return new Runnable(){
      @Override
      public void run() {
        runner();
      }
    };
  }

  public abstract void runner();

  public abstract Trigger getTrigger();

  public abstract String getCheckQuery();

대략적인 구성은 아래 처럼 나옵니다.

@SpringBootApplication
@EnableScheduling
public class MegatoiMonitorWatcherApplication { ... }

public class GenericScheduler { ... }

public abstract class AbstractGenericScheduler { ... }

@Component
public class WatcherCpuChecker extends AbstractGenericScheduler { ... }

 

:

[AWS] Spring Cloud Config S3 Backend 사용하기

Cloud&Container/AWS 2020. 3. 26. 10:57

Spring Cloud Config Server 설정)

 

[S3 설정]
- S3 버킷 생성
- 접근권한 부여

[ConfigApplication.java]

@SpringBootApplication
@EnableConfigServer
public class ConfigApplication {
  public static void main(String[] args) {
	SpringApplication.run(ConfigApplication.class, args);
  }
}


[build.gradle]

compile 'com.amazonaws:aws-java-sdk-s3'


[application.yml]

server:
port: 8888

spring:
  profiles:
    active: awss3
  cloud:
    config:
      server:
        awss3:
          region: ap-northeast-2
          bucket: s3config


[Config Server 접근 URL]

http://localhost:8888/${S3-FILE-NAME}/${PROFILE-NAME}

- 생성한 버킷 하위로 설정 파일 목록이 존재 해야 합니다.
- ${S3-FILE-NAME} 는 생성한 Bucket 아래 만든 설정 파일명 입니다.
- backoffice
- ${PROFILE-NAME} 은 설정 파일에 대한 프로필명 입니다.
- app1, app2

예제)
Config Server URL : 
http://localhost:8888/backoffice/app1

S3 :
megatoidiscons3config/backoffice-app1.yml
or
megatoidiscons3config/backoffice-app1.properties


[backoffice-app1.yml]

project.app1="backoffice-app1"


[backoffice-app2.yml]

project.app2="backoffice-app2"

 

Spring Cloud Config Client 설정)

[application.yml]

spring:
  cloud:
    config:
      name: backoffice
      uri: http://localhost:8888
      profile: app1,app2


[HelloController.java]

@RestController
public class HelloController {

  @Value ("${project.app1}")
  String projectApp1;

  @Value ("${project.app2}")
  String projectApp2;

  @RequestMapping("/app1")
  public String helloApp1() {
    return projectApp1;
  }

  @RequestMapping("/app2")
  public String helloApp2() {
    return projectApp2;
  }
}

 

git 사용 하는 건 문서들이 많이 나와 있어서 s3 로 정리해 봤습니다.

:

[Reference] How to do in java

ITWeb/스크랩 2019. 10. 10. 13:04

우연히 Spring Batch 관련 자료를 찾다가 얻어 걸린 사이트 입니다.

그냥 한번 따라만 해봤는데, 정리도 잘 되어 있고 코드도 잘 동작 해서 링크 걸어 봤습니다.

 

https://howtodoinjava.com/

 

Java Tutorials - Java Learning Resources - HowToDoInJava

Simple and easy to follow free Java tutorials on spring framework, spring boot, angular, maven, hibernate, jpa, concurrency, collections and much more.

howtodoinjava.com

그럼에도 불구 하고 모든 예제들은 기본적으로 Spring 에 대한 또는 Java 에 대한 이해를 하고 있어야 문제 없이 따라 할 수 있습니다.

 

만약 실행이 안된다면 그건 당신의 실력이 부족해서 입니다.

: