'springBoot gradle'에 해당되는 글 25건

  1. 2024.04.11 appScript docx2pdf 사전작업
  2. 2024.04.05 구글appScript를 이용해 pdf로 변환하기
  3. 2024.01.30 springboot 버전정보 api
  4. 2023.09.06 백엔드 리액티브 Stream
  5. 2023.08.31 bean 종류
  6. 2023.02.23 Test에서 docker image사용
  7. 2020.07.22 springboot2 Async통신
  8. 2020.07.13 Test Code 관련
  9. 2020.07.10 spring cloud stream
  10. 2020.03.31 spring session mongo
  11. 2018.09.21 nginx와 springBoot
  12. 2018.06.04 엑셀 다운로드 (xls download)
  13. 2018.04.25 springBoot jar배포
  14. 2018.03.26 몽고db
  15. 2018.01.23 Excel upload
  16. 2017.12.30 web에서 project 생성
  17. 2017.12.29 MVC개념
  18. 2017.07.19 gradle
  19. 2017.05.19 spring-boot-devtools와 @Cacheable
  20. 2017.04.14 spring @Async @Retryable
  21. 2017.03.02 배치 실행 @Scheduled
  22. 2017.02.06 RestOperations
  23. 2017.01.21 @Cacheable
  24. 2016.12.18 spring boot
  25. 2016.09.12 Git advanced.

 

1. Apps Script 컨솔 왼쪽 메뉴에서 "서비스 +" 를 눌러 Drive API 를 추가한다.

 

2. 코드를 아래와 같이 작성하고

3. 배포 -> 새 배포 를 한 후

4. 실행을 누른다.

function convertDocxToPdf() {
  var folderId = 'myFolderID-folder의 URL 뒤쪽 끝부분임.'; // 변환할 파일이 들어 있는 폴더 ID
  var folder = DriveApp.getFolderById(folderId);
  var files = folder.getFilesByType(MimeType.MICROSOFT_WORD);

  while (files.hasNext()) {
    var file = files.next();
    Logger.log('File name:' + file.getName());

    //1. Microsoft Word 파일을 Google Docs로 변환
    var resource = {
      title: file.getName(),
      mimeType: MimeType.GOOGLE_DOCS,
      parents: [{id: folderId}]
    };

    var convertedDocFile = Drive.Files.copy(resource, file.getId());

    //2. Google Docs 형식으로 문서를 연 후 PDF로 변환
    var doc = DocumentApp.openById(convertedDocFile.getId());
    var blob = doc.getBlob().getAs('application/pdf');

    // 특정 폴더에 PDF 파일 저장
    var folder = DriveApp.getFolderById(folderId);
    var pdfFile = folder.createFile(blob.setName(convertedDocFile.getName().replace(/\.[^/.]+$/, "") + ".pdf"));

    // 생성된 PDF 파일의 URL 반환
    return pdfFile.getUrl();
  }
}

 

 

원격호출이 가능한 appScript만들기 => doGet(e)과 filename 파라미터를 추가.

 

function convertDoc2PdfByFilename(filename) {
  var folderId = 'myFOlderID'; // 변환할 파일이 들어 있는 폴더 ID
  var folder = DriveApp.getFolderById(folderId);
  // var files = folder.getFilesByType(MimeType.MICROSOFT_WORD);
  var files = folder.getFilesByName(filename);

  while (files.hasNext()) {
    var file = files.next();
    Logger.log('File name:' + file.getName());

    //1. Microsoft Word 파일을 Google Docs로 변환
    var resource = {
      title: file.getName(),
      mimeType: MimeType.GOOGLE_DOCS,
      parents: [{id: folderId}]
    };

    var convertedDocFile = Drive.Files.copy(resource, file.getId());

    //2. Google Docs 형식으로 문서를 연 후 PDF로 변환
    var doc = DocumentApp.openById(convertedDocFile.getId());
    var blob = doc.getBlob().getAs('application/pdf');

    // 특정 폴더에 PDF 파일 저장
    var folder = DriveApp.getFolderById(folderId);
    var pdfFile = folder.createFile(blob.setName(convertedDocFile.getName().replace(/\.[^/.]+$/, "") + ".pdf"));

    // 생성된 PDF 파일의 URL 반환
    return pdfFile.getUrl();
  }
}

function doGet(e) {
  var filename = e.parameter.filename;
  if (!filename) {
    return ContentService.createTextOutput("Filename parameter missing")
      .setMimeType(ContentService.MimeType.TEXT);
  }

  // 파일 이름을 사용하여 DOCX를 PDF로 변환하고, 생성된 PDF의 URL을 반환
  var pdfUrl = convertDoc2PdfByFilename(filename);
  if (pdfUrl) {
    return ContentService.createTextOutput("PDF 생성됨: " + pdfUrl)
      .setMimeType(ContentService.MimeType.TEXT);
  } else {
    return ContentService.createTextOutput("해당 이름의 파일을 찾을 수 없습니다.")
      .setMimeType(ContentService.MimeType.TEXT);
  }
}
Posted by yongary
,

springboot에서 pdf로 변환하다가 어려움을 겪는다면, 시도해 볼만한 방법 중하나로
구글드라이브와 구글의 appScript를 이용하는 방법이다.

 

Spring Boot 애플리케이션에서 Google 계정에 로그인하여 Google Apps Script를 실행하고, 결과로 생성된 PDF를 가져오는 것은 가능합니다. 이 과정은 크게 세 부분으로 나눌 수 있습니다: Google 계정 인증, Google Apps Script 실행, 생성된 PDF 파일 가져오기. 이 과정은 Google Cloud Platform(GCP)의 여러 서비스와 API를 사용하며, OAuth 2.0을 통한 인증이 필요합니다.

1. Google 계정 인증 (OAuth 2.0 사용)

  1. Google Cloud Console에서 새 프로젝트를 생성합니다.
  2. API 및 서비스 대시보드로 이동하여 OAuth 동의 화면을 구성합니다.
  3. 사용자 인증 정보를 클릭하여 새 OAuth 클라이언트 ID를 생성합니다. 애플리케이션 유형으로 웹 애플리케이션을 선택합니다.
  4. 승인된 리디렉션 URI로 애플리케이션의 리디렉션 엔드포인트를 추가합니다. 예를 들어, http://localhost:8080/oauth2/callback과 같습니다.
  5. 클라이언트 ID와 클라이언트 비밀을 안전하게 저장합니다.

2. Google Apps Script 실행

  1. Google Apps Script를 사용하여 PDF를 생성하고 Google Drive에 저장하는 스크립트를 작성합니다.
  2. Apps Script 프로젝트에서 공유 및 배포 > 배포를 위한 앱 만들기 > API 실행 가능을 활성화하고, 생성된 스크립트 ID를 저장합니다.

3. Spring Boot에서 Google API 사용

  1. Spring Boot 프로젝트에 google-api-client, google-oauth-client, google-api-services-script 라이브러리를 의존성에 추가합니다.
implementation 'com.google.api-client:google-api-client:1.30.9'
implementation 'com.google.oauth-client:google-oauth-client-jetty:1.30.6'
implementation 'com.google.apis:google-api-services-script:v1-rev20200827-1.30.10'
gradleCopy code
implementation 'com.google.api-client:google-api-client:1.30.9' implementation 'com.google.oauth-client:google-oauth-client-jetty:1.30.6' implementation 'com.google.apis:google-api-services-script:v1-rev20200827-1.30.10'
  1. 사용자를 Google로 리디렉션하여 인증하고, Google로부터 리디렉션된 요청을 처리하는 컨트롤러를 구현합니다. 이 과정에서 GoogleAuthorizationCodeFlow를 사용하여 액세스 토큰을 얻습니다.
  2. 얻은 액세스 토큰을 사용하여 Apps Script API를 호출하고, 스크립트를 실행하여 PDF를 생성합니다.

4. 생성된 PDF 가져오기

  1. Google Drive API를 사용하여 Apps Script가 생성한 PDF 파일을 검색하고 다운로드합니다.
  2. 파일의 ID나 이름을 알고 있다면, Files.get 메서드로 파일 메타데이터를 가져온 후 GoogleCredential을 사용하여 인증된 Drive 서비스 객체로 파일을 다운로드할 수 있습니다.
Posted by yongary
,

springboot 버전정보를 api로 제공하는 2가지 방법.

 

1. 1 build.gradle에서 processResources 태스크를 사용하여 애플리케이션 속성 파일에 버전 정보를 주입

 

// build.gradle
processResources {
    filesMatching('application.properties') {
        expand(project: project)
    }
}

 

1.2 

# application.properties
app.version=${version}

# application.yml
app:
  version: ${version}

 

1.3

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class VersionController {

    @Value("${app.version}")
    private String appVersion;

    @GetMapping("/api/version")
    public String getVersion() {
        return appVersion;
    }
}

 

 

2. @Autowired BuildProperties buildProperties;를 사용하여 애플리케이션의 빌드 정보에 접근하는 것은 Spring Boot 2.0 이상에서 가능합니다. BuildProperties는 Spring Boot의 org.springframework.boot.info 패키지에 포함된 클래스로, 빌드 시 생성된 META-INF/build-info.properties 파일의 정보를 제공

2.1  build.gradle 1

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    // 기타 의존성들...
}

 

2.2 build.gradle 2

springBoot {
    buildInfo()
}

 

2.3 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.info.BuildProperties;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class VersionController {

    @Autowired
    private BuildProperties buildProperties;

    @GetMapping("/api/version")
    public String getVersion() {
        return buildProperties.getVersion();
    }
}

 

 

클라이언트에서 사용 방법.

const AppVersionChecker = () => {
  const [clientVersion, setClientVersion] = useState('1.0.0'); // 클라이언트 버전-> localStorage에 기록해 놓으면 좋을듯.

  useEffect(() => {
    const checkVersion = async () => {
      try {
        // 서버에서 현재 버전을 가져옵니다.
        const response = await axios.get('https://yourserver.com/api/version');
        const serverVersion = response.data.version;

        // 서버 버전과 클라이언트 버전이 다르면 페이지를 새로고침합니다.
        if (serverVersion !== clientVersion) {
          window.location.reload();
        }
      } catch (error) {
        console.error('버전 확인 중 오류 발생:', error);
      }
    };
Posted by yongary
,

프런트로 Stream을 리턴하는 방식은 프런트 처리시 난이도가 높아서, 특수한 경우에 한해 사용하면 좋다.

1. Flux나 Observable 생성: WebFlux나 RxJava에서 Flux 또는 Observable을 사용하여 데이터 스트림을 생성합니다. 이 스트림은 비동기적으로 데이터를 생성하거나 가져올 수 있습니다.
    dependencies { implementation 'org.springframework.boot:spring-boot-starter-webflux' }

2. 예제코드  : 보통 Flux를 리턴하고 데이터가 1/0 개일경우는 Mono 리턴. 

import reactor.core.publisher.Flux;

    @RestController
    public class StreamController {

        @GetMapping(value = "/stream-data", produces = "text/event-stream")
        public Flux<String> streamData() {
            // 스트림 생성 예제 (여기서는 간단한 문자열 스트림)
            return Flux.just("Data 1", "Data 2", "Data 3")
                    .delayElements(java.time.Duration.ofSeconds(1)); // 1초마다 데이터 전송
        }
    }

 

 

Posted by yongary
,

bean 종류

springBoot gradle 2023. 8. 31. 23:12

spring에서 bean은 아무 생각없이 만들면

spring container에서 singleton 으로 만들어 준다.

(즉, 전체 sping container에서 항상 동일한 하나의 global instance로 bean을 만들어 준다)

 

따라서 immutable이고 status도 없이 만드는 것이 좋음.

 

만약, 다른 용도로 만드려면

 

Prototype Bean - 매번 생성

Session Bean  - WEB 세션별로 생성

Request  Bean - WEB Request별로 생성

이 존재하며..

 

 

@Component

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)     과 같이 annotate 한다.

    - @Scope("prototype") 만 해도 되지만.. 위에 방식이 더 안전.

 

 

xml에서 할 경우에는
<baen id="note" class="my.Note" scope="prototype"> 으로 한다.

 

Posted by yongary
,

spring test에서 간단히 새로운 DB로 연결하는 방법.

 

@AutoConfigureMockMvc

class MyTest{
	
    @Container
    static MongoDbContainer mongoDBContainer = new MongoDBContainer("mongo:4.4.2"); //docker Image Name
    
    @DynamicPropertySource
    static void setProperties(DynamicPropertyRegistry dymDynamicPropertyRegistry) {
    	dymDynamicPropertyRegistry.add("spring.data.mongodb.uri", mongoDBContainer::getReplicaSetUrl);
    }

		

}
Posted by yongary
,

spring boot2에서부터는  controller에서 @Async에서도

completableFuture를 리턴할 수 있다..

( 그 전에도 Async와 EnableAsync는 있었는데 completableFuture를 리턴하지는 못한것 같은데, 확인 중)

 

<사전지식>

1. Future(Java5 에 등장) 와 Observable간에 전환.  (마찬가지로 CompleatbleFuture와  Single간에 전환도 유사)

Observable<String> source = Observable.fromFuture(future);

source.subscribe(System.out::println);

 

2. Future vs CompletableFuture (Java8에 등장)  : ref
    CompletableFuture.complete() 함수를 호출해  Future를 수동으로 종료할 수 있음. 

    그 외 runAysnc, supplyAsync, thenApply, thenAccept, thenRun 등 함수들이 제공됨.

  

 


출처: https://12bme.tistory.com/570 [길은 가면, 뒤에 있다.]

 

[RxJava] RxJava 프로그래밍(1) - 리액티브 프로그래밍

서버 다수와 통신하게 되면 API 호출 각각에 콜백을 추가하게 된다. 콜백이 늘어나면 애플리케이션의 복잡성도 증가(callback hell)하게 된다. RxJava는 자바로 리액티브 프로그래밍을 할 수 있는 라이

12bme.tistory.com

https://12bme.tistory.com/570 

 

 

 

참고: https://howtodoinjava.com/spring-boot2/rest/enableasync-async-controller/#:~:text=1.-,Spring%20%40Async%20rest%20controller,application%20classes%20for%20asynchronous%20behavior.&text=The%20%40Async%20annotated%20methods%20can,result%20of%20an%20asynchronous%20computation.

 

Spring @Async rest controller example - Spring @EnableAsync

Spring @Async non blocking rest controller example. Learn to use spring async behavior with @EnableAsync annotation. Spring async completablefuture example.

howtodoinjava.com

 

Posted by yongary
,

Test Code 관련

springBoot gradle 2020. 7. 13. 17:19

 

 Stub/ Mock/Fake 등을 묶어‘테스트 더블(Test Double)' 이라고 한다. (여기서 Double=대역)

 

SpringBoot에선 BDDMockito를 지원해 B(Behavior) Driven Test를 지원한다.

 

 

MockMVC 과 Mockito의 차이.

- mockito가 좀 더 general하고 더 많은 case를 지원하고, 여러가지 mock도 지원한다. (mockObject.when()  등등...)

- MockMVC는 spring 전용.  

 

Posted by yongary
,

간단한 @StreamListener  annotation만으로 stream 통신을 할 수 있으므로, MSA에서 유리하다.

 

https://coding-start.tistory.com/139

 

Kafka - Spring cloud stream kafka(스프링 클라우드 스트림 카프카)

Kafka - Spring cloud stream kafka(스프링 클라우드 스트림 카프카) 이전 포스팅까지는 카프카의 아키텍쳐, 클러스터 구성방법, 자바로 이용하는 프로듀서,컨슈머 등의 글을 작성하였다. 이번 포스팅은 �

coding-start.tistory.com

 

그 외에
 SpringCloud Eureka : MSA환경에서 서버들을 registry 해놓고, 로드밸런서와 같은 역할 가능.

 

 springcloud zuul : MSA환경에서 G/W를 이용해 token 체크등을 한 곳에서  모아서 할 수 있다.

 

 

 

Posted by yongary
,

 

redis를 이용해서 httpSession을 공유하는 방식이 좀 더 많이 활용되지만,

 

이미 mongoDB를 사용하고 있다면, mongoDB를 활용한 session공유가 더 좋을 수 있다.

특히 요즘엔 HDD대신에 SSD를 많이 사용하므로  SSD기반의 mongoDB라면 redis에 가까운 속도를 낼 것으로 예상된다.

 

 

spring-boot에서 사용하려면

gradle방식의 경우 dependency를 아래와 같이 추가하고

      compile('org.springframework.session:spring-session-data-mongodb')

 

 

 

HttpSessionConfig 클래스만 만들면 된다. ( default가 30분이라서 1시간으로 바꾼 예제이다. )

@EnableMongoHttpSession(maxInactiveIntervalInSeconds = 3600) //default 1800
public class HttpSessionConfig {
    @Bean
    public JdkMongoSessionConverter jdkMongoSessionConverter() {
        return new JdkMongoSessionConverter(Duration.ofMinutes(60)); //default 30
    }
}
Posted by yongary
,

일반적으로 WAS서버만으로는 성능이 부족하므로,


웹+WAS 서버 결합해서 서비스를 하게 되는데,

에전에는 Apache+ Tomcat의 결합이 대세였다고 하면

요즘에는 nginx + SpringBoot(tomcat 포함)이 대세이다.



<nginx의 특징 및 주요 기능>


1. apache대비 빠르고 가볍다.

  - apache가 클라이언트별로 thread를 할당하는 구조인데 반해,

    nginx는 메시지방식(혹은 event-driven) 방식이다.  당연히 비동기식 메시징 방식이 훨씬 효율적이다.

    자세한 비교는  REF

  


2. reverse PROXY가 강력하다.

  - apace에서도 PROXY기능 외에 Reverse Proxy가 지원되었으나,

    nginx의 reverse Proxy는 ID/비번도 부여할 수 있고.. 더 안전/강력해 보인다. 


    참고: PROXY vs Reverse_PROXY   REF

      - proxy는 웹브라우저 쪽:  웹브라우저 여러개가 하나의 proxy를 이용해 cache된 데이터를 받도록 해서

      - reverse Proxy는 웹서버 쪽: 마치 L4처럼 하나의   ReverseProxy서버가 요청을 받아 여러 웹서버로 분리할 수 있다.



<nginx + springBoot 사용방식>


1. 기본설정    /nginx/conf/nginx.conf 를 편집하고 reload하면 됩니다.

   

   http { ... ... 

             server {

                          listen 80; 

                           ... ...

                          location / { 

                               proxy_pass http://localhost:8080; 

                          }

             ... }



2. nginx + 2개 springBoot (각각 다른포트)를 이용한  무중단 배포 방식: REF   gitREF



참고: 

  nginx를 centOs7에 설치하는 법 -  REF

  nginx를 이용한 reverseProxy 설정 - REF

Posted by yongary
,

아래 세 군데의 Reference사이트를 참고하여, 간단하게 xls 다운로드 하는 spring기능을 만들어 보았다.

기존 3가지가 다 약간씩 장점이 있으나, 복잡한 면이 있어서 이들을 섞으니 간단하게 된다.



    참고0: https://okky.kr/article/421321

   참고1: http://pkbad.tistory.com/26

   참고2:  http://heowc.tistory.com/66 



1. xml 설정.


<!-- 엑셀 Download용 View  -->

<beans:bean class = "org.springframework.web.servlet.view.BeanNameViewResolver">

    <beans:property name = "order" value = "1" />

</beans:bean>

<!-- 엑셀 Download View는 컨트롤러 리턴시 다른것들보다 먼저 읽혀야 하기 때문에 order를 0과 1로 지정-->

<beans:bean class = "org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">

    <beans:property name = "order" value = "0" />

    <beans:property name = "defaultErrorView" value = "error" />

    <beans:property name = "exceptionMappings">

        <beans:props>

            <beans:prop key = "RuntimeException">error</beans:prop>

        </beans:props>

    </beans:property>

</beans:bean>

<beans:bean name="ExcelXlsView" class="my.common.view.ExcelXlsView" />



2.  ExcelXlsView class 작성.


@Component

public class ExcelXlsView extends AbstractXlsView {


@Override

protected Workbook createWorkbook(Map<String, Object> model, HttpServletRequest request) {

return (HSSFWorkbook)model.get("workbook");

}

 

@Override

protected void buildExcelDocument(Map<String, Object> model, Workbook workbook, HttpServletRequest request, HttpServletResponse response) {


    response.setCharacterEncoding("UTF-8");

            response.setContentType("application/vnd.ms-excel");

            response.setHeader("Pragma","public");

            response.setHeader("Expires","0");

        

//file이름 및 확장자

response.setHeader("Content-Disposition",

                 "attachment; filename=\"" + model.get("fileName") +".xls" + "\"");

}

}



3. Controller에서 excel 다운로드 수행.


@RequestMapping(value="/ExcelDownload.do")

public ModelAndView ExcelDownload(ModelMap modelMap, @ModelAttribute("modelVO")myVo infoVO,  

HttpServletResponse response, HttpServletRequest request, HttpSession session) throws Exception {

//화면의 List와 똑같은 Query수행.

List<MyVo> resultList = service.selectList(infoVO);


//Excel 생성

Workbook xlsWb = new HSSFWorkbook();

Sheet sheet1 = xlsWb.createSheet("sheet1");

Row row = null;

Cell cell = null;

    //Header Row생성

    row = sheet1.createRow(0);

    

    //필드별 헤더설정.

    int k=0;

    

    cell = row.createCell(k++);   

    cell.setCellValue("번호");


    

    sheet1.setColumnWidth(k, 4000); //좀 넓은 column은 width설정. 4000정도하면 날짜가 여유있게 들어감.

    cell = row.createCell(k++);   

    cell.setCellValue("생년월일");

    

    

    

 //Data 출력

        for(int i = 0; i < resultList.size(); i++) {

        row = sheet1.createRow(i+1);

        JdnumBunJadonVo oneRow = resultList.get(i); 

        int j = 0;

        //필드별 값설정.

        cell = row.createCell(j++);

        cell.setCellValue(oneRow.getNo());

        

        cell = row.createCell(j++);

        cell.setCellValue(oneRow.getBirthday());

        }


//model에 data넣어서, modelAndView 리턴.

        modelMap.put("workbook", xlsWb);

        modelMap.put("fileName", "excelList_" + CommonUtil.getCurrentDate()); //파일네임 앞부분 설정(.xls는 자동추가됨)

        

return new ModelAndView("ExcelXlsView");


}




4. 마지막으로, jsp에서는 아래와 같이 실행.


      var url = '/ExcelDownload.do'; //+parameter  를 get방식으로 설정.

window.open(url, '_blank' ); 

Posted by yongary
,

springboot에 포함된 tomcat과 함께 배포해서 바로 서비스로 돌릴 수 있다.


jar파일을 묶어서 배포하면 되고, java -jar xxx.jar 로도 실행가능하며 tomcat까지 돌아간다.


혹시 서비스로 설정을 해서 돌리고 싶다면 여기참조: REF

Posted by yongary
,

몽고db

springBoot gradle 2018. 3. 26. 23:14

springBoot2.0에서

spring-boot-starter-data-mongodb 등장.

   (localhost 27017포트의 test DB로 자동 접속.)  SpringBoot2.0 tutorial


@Document나 @Id는 기존과 동일.


출처:

https://m.youtube.com/watch?v=UJn_aTbmk9o



윈도우용 mongoDB설치는 : http://solarisailab.com/archives/1605 


약간예전꺼지만 spring Query예제들:  mkyong



2014년 몽고db 문제점 한글로 적은 글: REF

Posted by yongary
,

Excel upload

springBoot gradle 2018. 1. 23. 11:46

 poi 이용 : 한글 REF 

    영문: REF



1. maven dependency 추가.

<dependency>

<groupId>org.apache.poi</groupId>

<artifactId>poi</artifactId>

<version>3.15</version>

</dependency>

         <dependency>

            <groupId>org.apache.poi</groupId>

            <artifactId>poi-ooxml</artifactId>

            <version>3.15</version>

        </dependency>

<dependency>

            <groupId>org.apache.poi</groupId>

            <artifactId>poi-ooxml-schemas</artifactId>

            <version>3.15</version>

        </dependency>

        <dependency>

            <groupId>org.apache.poi</groupId>

            <artifactId>poi-contrib</artifactId>

            <version>3.6</version>

        </dependency>


2. JSP

function fnCheckUpload() {

var file = $("#excel").val();

if(file == "" || file == null){

alert("먼저 파일을 선택하세요.");

return false;

}

var fileFormat = file.split(".");

var fileType = fileFormat[1];

if(confirm("업로드 하시겠습니까?")){

$('#excelUpForm').attr('action','/uploadMoveinExcel.do');

var options = {

fail:function(data) {

        alert('저장 오류가 발행하였습니다.');

        $('#popup').bPopup().close();

        },

success:function(data){

alert(data + "건 업로드 완료");

$('#popup').bPopup().close();

},

type: "POST",

data : {"excelType" : fileType}

};

$('#excelUpForm').ajaxSubmit(options);

}

}

-----HTML--------

<form id="excelUpForm" method="post" action="" role="form" enctype="multipart/form-data">

                                    <input  id="excel" name="excel" class="file" type="file"  >


</form>



3. controller

  @ResponseBody

@RequestMapping(value="/uploadMoveinExcel.do")

public String uploadExcel( MultipartHttpServletRequest request, HttpSession session) {

List<ExcelVo> list = new ArrayList<>();

String excelType = request.getParameter("excelType");

log.info("uploadExcel.do + excelType:" + excelType);

if(excelType.equals("xls")){

list = moveinService.xlsExcelReader(request);

}else if(excelType.equals("xlsx")){

//IN_STUDY:  list = adminAccountsMngService.xlsxExcelReader(req);

}

int cnt = service.uploadList(list);

return String.valueOf(cnt);//success cnt : media처리의 경우, JSON포맷으로 잘 안변함(config필요), 따라서 간단히 숫자만 리턴.

}


4. service

  public List<MoveinExcelVo> xlsExcelReader(MultipartHttpServletRequest request) {

// 반환할 객체를 생성

List<MoveinExcelVo> list = new ArrayList<>();


MultipartFile file = request.getFile("excel");

HSSFWorkbook workbook = null;


try {

// HSSFWorkbook은 엑셀파일 전체 내용을 담고 있는 객체

workbook = new HSSFWorkbook(file.getInputStream());


// 0번째 sheet 반환

HSSFSheet curSheet = workbook.getSheetAt(0);  //workbook.getNumberOfSheets()

// row 탐색 for문

boolean isInTable = false;

for (int rowIndex = 0; rowIndex < curSheet.getPhysicalNumberOfRows(); rowIndex++) {

//헤더 Cell 찾기.

String firstCellValue = curSheet.getRow(rowIndex).getCell(0).getStringCellValue();

if ("구분".equals(firstCellValue.trim())) { //table헤더 발견.

isInTable = true;

continue; //헤더는 pass.  (다움줊부터 paring시작.)

}

if (isInTable) { //in Table 파싱 시작. ///////////////////////////////////////////

HSSFRow curRow = curSheet.getRow(rowIndex);

//필수 Cell들.

String cellStr = curRow.getCell(2).getStringCellValue(); //String 읽기

String cellDate =  StringUtil.toYYMMDD(curRow.getCell(5).getDateCellValue()); //날짜 읽기

String cellNum = curRow.getCell(7).getNumericCellValue() + ""; //산차.

  String cellLongNUm = StringUtil.toIntString(curRow.getCell(12).getNumericCellValue() ;   //엄청 긴 숫자 읽기

. . . . 

} catch (IOException e) {

e.printStackTrace();

}


// vo로 만들면서 db에 삽입코드는 생략.

return list;

}


public class StringUtil {


    private static SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");

    public static String datePattern = "^\\d{4}[\\-](0[1-9]|1[012])[\\-](0[1-9]|[12][0-9]|3[01])$";

    public static boolean isDate(String str) {

    return Pattern.matches(datePattern, str);

    }

    //return YYYY-MM-DD String

    public static String toYYMMDD(Date date) {

    return formatter.format(date);

    }

    public static boolean isNumericOrFloat(String str) {

        if (str == null) {

            return false;

        }

        int sz = str.length();

        for (int i = 0; i < sz; i++) {

            if (Character.isDigit(str.charAt(i)) == false) {

            if (str.charAt(i) != '.')  //Added KY. '.'도 numeric으로 간주. float때문에.

            return false;

            }

        }

        return true;

    }

    

    //For Excel processing.

    // 엑셀에선 숫자를 항상 double로 읽기 때문에 int형태의 String으로 변환하면 유용.

    public static String toIntString(double doubleVal) {

    try {

    BigInteger k = new BigDecimal(doubleVal).toBigInteger();

    

    return String.valueOf(k);

   

    } catch (Exception e) {

    return null;

    }

    }

Posted by yongary
,

spring boot로 Web Project생성하기.


1. https://start.spring.io/  에 접속해서  maven/gradle을 선택하고 이름만 넣으면  springboot프로젝트를 생성할 수 있다.  

   => 일반 application이 생성된다.


  1.1  IDEA나 eclipse에서 import project를 해서 열어서 Run만 시키면 돌아간다. (아래과 같이 spring을 볼수 있다)

    .   ____          _            __ _ _

 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \

( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \

 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )

  '  |____| .__|_| |_|_| |_\__, | / / / /

 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::        (v1.5.9.RELEASE)



2. Build.gradle을 열어서  아래 한줄을 추가한다.   (JDK1.8기준)


compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '1.4.7.RELEASE'
(version: '1.4.7.RELEASE')

 

   ==> 자동으로 Web Application이 된다. 실행도 되고 localhost:8080 접속도 된다.


3.  jsp처리기능이 아직없으므로 이를 위해 2개 더 추가한다. (REF1, ) 

    

compile group: 'javax.servlet', name: 'jstl', version: '1.2'
compile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-core', version: '8.5.24'
compile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-jasper', version: '8.5.24'

   

     (그리고, resources밑에 있는 application.property에  아래 4번 경로와 suffix를 추가한다)

     


spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp



4.  src/main  밑에  /webapp/views  밑에    jsp생성  (REF2-jsp) 

     
  jsp첫줄에는 아래 한줄이 필요하고..

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

 그 후 <html> tag야 있으면 좋고..





별첨: 간단한 Junit4 Test코드.... + RestTemplate는 여기 참조:   REF

         Lombok사용시 - 설정에서 컴파일러 옵션 중 Annotation Process 체크필요.

Posted by yongary
,

MVC개념

springBoot gradle 2017. 12. 29. 21:56

      USER가 url을 요청한다던지 하는 action을 하면, controller가 받아서 

     Model을 조회/변경하고 그걸 다시
     View에서 그려준다.  


    3가지가 분리되어서 업무분리 개발분리가 가능해진다. 

Posted by yongary
,

gradle

springBoot gradle 2017. 7. 19. 14:58

REF


compileOnly : 컴파일타임에만 사용됨-예)어노테이션라이브러리등   REF

    - maven의 provided와 동일.



  build.gradle  good_REF

        (gradlew build시)

      - jar, assemble  task을 통해 최종 jar파일을 생성할 수 있다.   REF  
           위치(build/libs)  및  jar 단독실행(java -jar xxxx.jar)   - ref

      



멀티 프로젝트: GOOD_REF   REF

 - 상위 프로젝트의 build.gradle에 모든 설정을 다 넣는다.


 - 상위 프로젝트의 settings.gradle파일에 sub프로젝트들을 지정한다.

   include "my:cms"

   project(':my:cms').name = 'my-cms'


- bootRepackage 는 보통 main이 없는 공통모듈용.





Posted by yongary
,

개발의 편의상 spring-boot-devtools라는 걸 사용하는게 좋다. REF

아래와 같은 5가지 기능이 지원된다.



Automatic Restart


LiveReload 

  - 특히 이부분은 개발을 할 때는 편하나, cache(@Cacheable)가 바뀌어도 자동 reload해주는 등 편하지만

    개발환경에서 ClassCastException이 발생할 수 있다.

    즉, cache에서 복원된 Object가 Class로 변환이 잘 안되는 문제가 있다.


   그래서 Cache를 테스트할 때는, 이부분을 pom.xml등에서  코멘트아웃을 하고 하는게 좋다.



Remote Debug Tunneling


Remote update and restart


Video Privew


Posted by yongary
,

spring 에서 async기능구현 필요가 있다면 메쏘드에 @Async를 사용하면 된다.

이 때, Application에는 @EnableAsync가 필요하다.


 REF



Retryable은 몇번의 실패을 다시 해주게 해주는데, 아래와 같이 사용하면된다.

정해진 실패 회수를 초과할 경우 @Recover함수가 불리게 되는데, exception + 파라미터가 동일하다는 점에 주의! 


Application에는  @EnableRetry가 필요하다.  


private static final int MAX_ATTEMPTS = 4;
private static final long BACK_OFF_DELAY = 2000;  //msec
@Retryable(value = DataAccessResourceFailureException.class,
maxAttempts = MAX_ATTEMPTS,
backoff = @Backoff(delay = BACK_OFF_DELAY))
public void checkAndProcess(String userKey, String itemId) {

@Recover
public void recover(DataAccessResourceFailureException e, String userKey, String itemId) {
log.error("All retries have failed!, userKey:{} " + e.toString(), userKey);
}


Posted by yongary
,

스프링으로 주기적으로 도는 함수를 만들고 싶으면... 


아주 간단하게


public 함수위에 

@Scheduled(cron = "0 39 */2 * * *")와 같이 선언하면 된다.



아래는 매일 2시 49분에 도는 함수!


@Scheduled(cron = "0 49 2 * * *")

참고:  제일앞자리는 cron에 없는거네요.. 용도는?  초.   */30 이러면 매 30초마다임.  REF

   

초 0-59 , - * / 

분 0-59 , - * / 

시 0-23 , - * / 

일 1-31 , - * ? / L W

월 1-12 or JAN-DEC , - * / 

요일 1-7 or SUN-SAT , - * ? / L # 

년(옵션) 1970-2099 , - * /

* : 모든 값

? : 특정 값 없음

- : 범위 지정에 사용

, : 여러 값 지정 구분에 사용

/ : 초기값과 증가치 설정에 사용

L : 지정할 수 있는 범위의 마지막 값

W : 월~금요일 또는 가장 가까운 월/금요일

# : 몇 번째 무슨 요일 2#1 => 첫 번째 월요일



<cron 은    REF 참조>


crontab 파일 형식
------    --------  ---------------------------------------------------
필  드    의  미    범  위
------    --------  ---------------------------------------------------
첫번째    분        0-59
두번째    시        0-23
세번째    일        0-31
네번째    월        1-12
다섯번째  요일      0-7 (0 또는 7=일요일, 1=월, 2=화,...)
여섯번째  명령어    실행할 명령을 한줄로 쓴다.
------    --------  ---------------------------------------------------


예) 5  */2 *  *  * 명령어 => 매일 2시간간격으로 5분대에 

Posted by yongary
,

RestOperations

springBoot gradle 2017. 2. 6. 21:59

spring 컨트롤러나 서비스 등의 안에서, 웹 서비스를 접속할 필요가 있을경우 RestTemplate(혹은 RestOperation)을 이용하면 편리하다.

REF


get/post

    For

Object/Entity 의 형태로 함수명이 존재한다.


즉 결과를 Object형태로 받을 수도 있고, Entity형태로 받을 수도 있다. 


파라미터로

-Object, Map, ClassType 등을 다양하게 이용할 수 있다.



patch/update/delete/option 함수들도 존재한다. 

Posted by yongary
,

@Cacheable

springBoot gradle 2017. 1. 21. 22:42

REF   REF한글


REF_NEW(good config)


spring에서

@Cacheable


@Cacheevict


@Cacheput 


을 이용해서 각종 조건을 지정하면서... 함수 리턴결과 등을 cache할 수 있다.


단, Cacheable등은 proxy형태의 AOP이므로, 별도 class를 만들어서 추가해야 한다.


@CacheConfig(cacheNames = CacheManagerConfig.DEFAULT_CACHE_NAME) public class ForCacheService {

@Cacheable(key = "'myCache'")
public Optional<Something> findSomething(){
return db.findSomething();
}

@CacheEvict(key = "'myCache'")
public void clearCache() {
log.info("clear cache: key=myCache");
}

}

 ==> 음 Optional은 serialize가 안되서 Cache가 안되는 문제 발견.

일반 class 일 경우에도 implements Serializable을 해야 한다.




null Value가 cache가 안되도록 하려면  다음과 같이 #unless를 이용하면 된다.  REF

  (unless는 함수가 실행된 이후에 평가됨.  즉, cache가 있을때는 상관없이 pass.)


@Cacheable( key="'myCache'", unless="#result == null")
public Person findPerson(int pk) {
   return getSession.getPerson(pk);
}


null Value도 cache가 되도록 하고 싶으면? ==>안됨.


Posted by yongary
,

spring boot

springBoot gradle 2016. 12. 18. 01:07

설치는 SDKMAN 이나 Brew등으로 가능.

$spring --version 으로 버전확인 가능.

$spring shell  :  sh도 제공


하지만, 대부분 IDEA를 이용하므로,

IntelliJ를 이용할 경우, gradle프로젝트를 만들고, springboot는 dependency로 삽입하면 된다. REF




<어노테이션>

@SpringBootApplication : component-scan 과 자동config제공.

  - @Conditional 도 가능( jdbcTemplate과 같이 dataSource존재여부에 따라...조건부 Bean제공 가능)

한데 자동 config은 이런식으로 Tomcat, ORM, web매핑(/static) 등으로 classpath등을 보고 자동판단한다.


*물론 이러한 자동config도 override를 통해  수정할수 있다.(@Configuration)


<빌드 & run - gradle 사용시>

 $ gradle bootRun : 빌드와 run동시 수행.

    = $gradle build + $java -jar build/libs/..-0.0.1-SNAPSHOT.jar(or war)


maven :  $spring-boot:run

               

             ($ mvn package ) : just package



 <설정>

application.properties는 첨엔 비어있으므로 필요한걸 추가하면 됨.

dependency가 2개가 있는데(gradle.plugin 등)

  - 위에껀 spring boot plugin dependency이고

  - 밑에껀 starter dependency 이다.   (참고로 maven은  순서가 바뀌어있지만.. plugin tag보면 알수 있음)


  - dependency 일부를 제거하고 싶을 땐,  exclude group등으로 가능.


 


 <TEST용 어노테이션>

@WithMockUser

@WithUserDetails


@WebIntegrationTest     


@Value (${local.server.port}) 등도 spring에서부터 사용가능.


물론 웹은 Sellenium과 연동테스트 된다.




<groovy> - C L I 와 궁합이 잘 맞음

Grabs.groovy 파일에 아래 3줄만 넣으면 spring-boot가 자동으로  못한 것들도, startup 시에 dependency를 자동으로 넣으면서 구동 가능. 

  @Grab("h2")

  @Grab("spring-boot-starter-thymeleaf")

  class Grabs {}  


==> 그 후에  $ spring run .  


 

Groovy로 test unit작성 시에는.. 

 $ spring test tests/myTest.groovy   로 하나 실행 혹은

 $ spring test tests  로 폴더통째로 실행 가능.



<패키징>

 $ spring jar MyProject.jar 하면 jar로 패키징 됨.. 


   war는 ?

   -gradle 의 경우 

      war {

baseName = 'readinglist'

version = '0.0.1-SNAPSHOT'

}

  - maven 의 경우

    <packaging> war </packaging>  

  

 만 추가하면 된다.



<Actuator>

CLI 환경에선 @Grab('spring-boot-starter-actuator') 추가하는 방법이 간단함.


그리고 Actuator중에선 아래 3가지가 특히 유용하다. . 

https://localhost:8080/beans  - 모든 bean과 dependency.  

https://localhost:8080/dump  - 모든thread 현황 

https://localhost:8080/trace   


그 외 enable을 시킨후에

curl -X POST http://localhost:8080/shutdown 명령어로 CLI상에서 shut-down도 가능하다. 



SSH 로도 actuator접속이 가능한데

@Grab("spring-boot-starter-remote-shell")  이런형태로나 maven/gradle에 dependency추가 후

$ ssh user@localhost -p 2000 하면 된다.






Posted by yongary
,

Git advanced.

springBoot gradle 2016. 9. 12. 15:21

git기본은: http://yongary.tistory.com/151




http://learngitbranching.js.org/   graphical 공부site


  - git의 commit은 매우 가볍다. 이전버전과 차이점 delta만 관리. 

  - local 관리버전을 master라고 부름.. (remote가 origin 인 듯)

  - branch는 단지 commit으로의 pointer이다. (storage/memory overhead없음)

git branch branchName해서 만들고 + git checkout branchName 한 다음에 git commit해야 함. 

: or  git checkout -b branchName 하면 branch를 만들면서 checkOut도 같이 함.

  - git merge 방법:

: git branch bugFix

:    git checkout bugFix

:    수정후 git commit

: git checkout master

:    git commit  (이 단계가 왜 필요한지?? 연구 중)

:    git merge bugFix

   

  - git rebase : 패러랠 branch작업을 sequential한 history로 변환시켜 줌. (대단하네요)

              현재 bugFix* checkout된 상태에서, 

: git rebase master 하면 sequential로 되고 그 후,  (현재 bugFix*가 계속유지됨.)

: git rebase bugFix   (master*가 current로 바뀜. sequential상에서의 merge효과가 생김. ) 

      


           

<기억할 명령어>

git checkout FileName  : 파일 하나 Revert

git checkout -- FileName  :  파일 하나 Revert from -- branch. (need test)  REF



git branch -v :  branch상태 확인가능.. 로컬이 앞서가면(commit 이 많으면 ahead 2 이런식으로 나옴)  : REF

==>  $ git fetch --all; git branch -w   이렇게 사용해야 최신 정보가 비교가 됨.



Posted by yongary
,