model에 enum type을 넣고 

mybatis에서 아래 테이블의 data를 받아오려고 한다.

 

 

enum은 여러 형태를 가질 수 있다.

 

아래와 같이 그냥 단건의 데이터만 지정하여 만드는 경우가 있을 수 있고

 

 

db에 쌓이는 데이터유형이 숫자( 1_VERSION, 2_VERSION )

특수문자로 시작할 경우 enum을 아래와 같이 인자를 2개씩 갖게 지정될 수 있다.

 

 

 

그럴경우 여러 형태의 enum 데이터를 바인딩 하지 못해

아래와 같은 오류가 발생 할 수 있다.

 

Caused by: org.apache.ibatis.executor.result.ResultMapException: Error attempting to get column '' from result set.

Caused by: java.lang.IllegalArgumentException: No enum constant

 

 

 

위소스를 보면 상품정보 rest controller에서 service를 거쳐 mapper에 해당하는 쿼리결과를 lists 객체에

담겨야 하는데 데이터를 못받아와 catch절 NOT_FOUND로 빠져  

swagger에서해당 data를 알지 못해 못받아오는 경우도 생긴다.

 

 

 

이럴경우 TypeHandler를 생성하여 해결할 수 있다.

 

 

 

 

그리고 생성한 type handler 위치의 패키지명을 아래와 같이 지정해준다.

type-handlers-package: com.shlee.toy1.common.handler.type

 

 

 

그리고 api를 조회해보면 아래와 같이 enum으로 정의한 키값으로 나온다.

 

 

 

이제 이 반환된 키 값으로 서비스 로직에서 데이터를 가공해서 반환해주면 된다.

 

 

 

 

이제 여러개의 enum type에 대응가능한 mybatis type handler에 대해 작성할 수 있게 되었다. 

  1. 오타감별사 2021.05.31 05:13

    오타 있습니다

    selectProductListsById, selectProductById 둘 중 하나로 통일하세요.

    mapper 는 selectProductById 로 되있고,

    controller 에는 productService.selectProductListsById 로 되어있네요.

    service 쪽 코드는 없기 때문에

    글을 읽던 중 사소한 차이로
    메서드명으로 살짝 딜레이가 되며 혼동이 오기에
    스크롤을 위로 올려 다시 체크하는 시간낭비가 발생합니다.

    설명글이니까 그냥 특정 코드가 생략되도, 독자가 흐름상 추론하기 쉬운 이름으로 통일하는게 가독성이 좋습니다.

    • effortDev 2021.06.06 14:19 신고

      의견 감사합니다.
      소스를 복붙하는 과정에서 발생한 것 같네요. 수정해서 확인해보겠습니다.
      고맙습니다.

프로젝트에서 이미지 api에서 받아온 이미지 url로

 

이미지 파일 N개 혹은

이미지 파일 1개를 같은 width, height 값으로 N개 분할하여

 

여러개의 이미지

이미지 N개를 1개의 연결된 gif 이미지로 보여주려는 작업요건이 필요했다. 

 

그래서 일단 해당 요건을 테스트 해보기 위해 테스트 소스를 작성해보았다.

 

codepen.io/shlee0882/pen/QWGmJbm

 

이미지 gif 효과 만들기

...

codepen.io

위 소스는 캔버스를 이용해 특정영역을 잡고

이미지 데이터 주소를 획득하여 배열에 넣은 다음

마우스 hover시 이미지 정보가 담긴 배열을 0.5초 간격으로 번갈아 가며 보여주며

마우스 out시는 반복을 멈추는 소스코드이다.

 

그럼 이 코드로 기본검증이 끝났으므로

 

응용하여 gif를 생성하는 라이브러리를 사용하지 않고 (not use gif library)

아래와 같은 움직이는 gif이미지를 생성한 것 같은 착각을 일으키는 코드를 작성할 수 있다.

 

 

 

www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png

 

위 이미지는 구글 웹페이지 메인에 로고이다.

위 이미지의 url이 노출된 상태에서 js에서 해당 이미지url로 image 생성자에 담아 

localhost로 해당 이미지를 조작 및 변경 처리를 시도 하면 아래와 같이

CORS policy: No 'Access-Control-Allow-Origin 오류가 발생한다.

 

 

CORS를 통하지 않고, 다른 origin으로 부터 가져온 이미지를

canvas를 통해 이미지를 자르고 조작 시 
원본데이터가 오염 및 안전하지 않다고 판단하여

js에서 origin exception이 발생한 것이다.

 

developer.mozilla.org/ko/docs/Web/HTML/CORS_enabled_image

 

교차 출처 이미지와 캔버스 허용하기 - HTML: Hypertext Markup Language | MDN

교차 출처 이미지와 캔버스 허용하기 Table of contentsTable of contentsHTML은 이미지 처리를 위해 CORS header를 포함하고 있는 crossorigin 속성을 제공합니다. 이는 요소에서 정의된, 외부 origin으로 부터 가

developer.mozilla.org

 

이러한 문제를 회피하기 위해 image url이 완전히 노출된 데이터를 인코딩 해서 처리해야한다.

 

 

- js에서 base64 encoding으로 변환 후 처리를 시도할 수 있지만 잘 처리가 되지 않을 수 있다.

 

 

- 이럴경우 java에서 base64 encoding으로 변환 후 처리하면 쉽게 해결 할 수 있다.

 

 

인코딩 된 base64 data 값을 화면에서 가져와 img 태그안에 src attribute로 넣어주면 된다.

 

 

 

img 태그는 해당 이미지값을 읽어 다음과 같이 출력해준다.

 

 

인코딩된 값으로 canvas 등을 활용해 CORS policy: No 'Access-Control-Allow-Origin을 회피하여

DOM을 조작할 수 있게 되었다. 

component로 등록된 것은 @SpringBootApplication 어노테이션을 통해 componentscan을 한다고 한다.

 

docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-using-springbootapplication-annotation

 

Using Spring Boot

This section goes into more detail about how you should use Spring Boot. It covers topics such as build systems, auto-configuration, and how to run your applications. We also cover some Spring Boot best practices. Although there is nothing particularly spe

docs.spring.io

intellij에서는 component로 등록된 것을 @Autowired하여 사용하려면

Could not autowire. No beans of '' type found. 라는 error가 발생한다.

컴파일 및 서버 실행에 문제는 없지만 해당 오류를 수정 할 수 있다.

 

컴포넌트를 사용하려는 클래스 위에 컴포넌트를 스캔할 패키지명을 적어준다.

@ComponentScan(basePackages={"com.shlee.toy1.common.components"}) 

 

참조 

stackoverflow.com/questions/26889970/intellij-incorrectly-saying-no-beans-of-type-found-for-autowired-repository

spring boot 테스트 프로젝트로 swagger를 적용해보았다.

 

1. gradle에 해당 springfox-swagger를 명시해준다.

 

 

 

swagger2, swagger-ui, swagger-annotations, swagger-models를 받아주는데

해당 정보는 mvn repository에서 확인할 수 있다.

 

mvnrepository.com/artifact/io.springfox/springfox-swagger2/2.9.2

mvnrepository.com/artifact/io.springfox/springfox-swagger-ui/2.9.2

mvnrepository.com/artifact/io.swagger/swagger-annotations/1.5.21

mvnrepository.com/artifact/io.swagger/swagger-models/1.5.21

 

gradle 설정이 완료되면

 

ToySwaggerConfig 파일을 하나 생성해준다.

 

 

먼저 WebMvcConfigurer를 implements 받고 addResourceHandlers를 구현해준다.

swagger-ui가 보여질 화면의 리소스 위치를 설정해주는 것이다.

 

다음은 swagger를 만들 Docket을 bean으로 등록하고 생성할 Docket을 만들어준다.

 

.apis는 api를 탐색할 위치(com.shlee.toy1,controller)

.path 경로로 시작되는 위치(controller에서 @RequestMapping("/v1")으로 시작되는 uri)

.directModelSubstitute로 날짜형식을 맞춰주고

.globalResponseMessage로 요청을 받았을때 어떻게 처리할지를 정해준다.

 

.additionalModels의 typeResolver.resolve(ResponseEntity.class)는 ModelRef에서 지정한.

ex) CommonError, CommonResponse, string, ResponseEntity 같은 사용자 지정 모델값을 넣어주면 된다.

필요 하지 않다면 다음과 같이 작성해도 된다.

 

 

마지막으로 .apiInfo를 통해 api정보를 지정해준다.

 

 

그리고 서버를 실행후 http://localhost:8080/swagger-ui.html 에 접속하면

controller와 연동된 api 목록이 나타나게 된다.

 

 

test 시도 시 response데이터가 잘 오는 것을 확인 할 수 있었다.

 

 

 

최근 스프링 부트 기본 설정을 테스트로 잡아보면서 작업하는데

DB 비밀번호를 간단하게 설정(숫자 0882) 하여 application.yml 파일을 작성하였더니 해당 오류가 발생하였다.

 

 

 

패스워드는 대문자, 소문자, 숫자, 특수문자를 포함한 암호 길이 8자 이상으로 설정해야 한다고 한다.

그래서 아래와 같이 패스워드를 조합하여 설정하였더니 해당 오류가 발생하지 않았다. 

 

 

 

또한 스프링 2.3.x 버전에서는 

application-local.yml profile을 local

application-dev.yml profile은 dev

application-prd.yml profile을 prd로 

파일단위별로 관리하여

 

 

 

해당하는 active profiles가 무엇인지 설정한 뒤 실행하였다.

 

2.4.x 버전 부터 profile을 설정할 시 deprecated 오류가 발생하지만

이전 설정을 spring.config.use-legacy-processing=true 값을 통해

2.3.x의 동일한 설정을 사용할 수 있다.

  

 

  1. 지니가던컴공생 2021.05.08 15:14

    오류 내용 구글에 검색해도 전부 비밀번호를 잘못 입력했을 거라고 해서 별짓 다 했는데 이것 때문이었군요 ㅠㅠ 감사합니다!

  2. 역대최고의노섹시노유연개답답프레임워크=스프링 2021.05.31 04:34

    deprecated 된 프로퍼티를 굳이 쓰려고
    spring.config.use-legacy-processing=true 하지 말고

    spring.config.activate.on-profile 쓰는 것이 바람직 합니다.

    스프링 부트의 고질적이고 XX같은 문제 중 하나인 ConfigFileApplicationListener 문제 때문에 맘먹고 버전업 되면서
    수정한 부분입니다.

    • effortDev 2021.06.06 14:18 신고

      의견 주셔서 정말 감사합니다.
      참고해서 사용해보겠습니다.
      고맙습니다.

  3. 한국의 메타몽 2021.09.26 14:31 신고

    와.. 정말 감사합니다. 어떻게 해도 안됐는데 결국 대문자 + 소문자 + 특수기호 + 숫자의 조합으로 비밀번호를 설정해야하는거였군요... ㅠㅠ!!


프론트에 Vuejs를 사용해 사용자가 키워드를 넣어 크롤링 할수 있게 구현해보려고 한다.


이미 Vuejs 기본셋팅하는 방법을 포스팅한 적이 있다.

https://shlee0882.tistory.com/282?category=903333


해당 포스팅을 참고하여  Vuejs 프로젝트 생성하고 온다.



검색 엔진 ID와 API_KEY를 gitignore에 추가하기 위해 따로 API_DATA.js 파일을 뺐다.


Home.vue에는 구글키워드 이미지 크롤링 텍스트와 ImageList 컴포넌트를 갖고 있다.


사용한 오픈소스는

파싱을 위한 url, js를 쉽게 쓸수 있게 도와주는 jquery, 이미지 get을 위한 axios

zip파일 생성 및 체크를 위한 JSZip, JSZipUtils 을 사용했다.


구글이미지를 크롤링하는 js코드가 구현되어 있고 크롤링 진행상황에 대한 progress bar가 추가 되었다.

또한 수집된 url이 리스트형태로 텍스트 출력되는 것과 zip파일로 사용자에게 다운을 제공한다.

그 전 포스팅과 달라진 점은 fs을 사용하지 않는다는 것이다.


프론트에서는 fs를 사용할 수 없어 이미지 api로 json 데이터로 response 받으면

이미지 url을 axios request하여 이미지 데이터를 blob 구조로 받아와 이미지 리스트를 만들고

이미지 리스트를 zip파일로 묶어서 사용자에게 반환한다.


효율적인 크롤링과 acess-control-allow-origin 문제 해결을 위해 

크롬 extention에서 간단한 플러그인을 설치해준다.





https://github.com/shlee0882/vuejs-nodejs-google-image-crawling


배포된 url로 들어가 테스트 해본다.

크롤링이 잘되어 zip파일로 이미지를 다운받아지는 것을 확인 할 수 있다.







마지막으로 해당 소스의 배포된 url과 github repository를 올린다.


클라이언트


https://github.com/shlee0882/vuejs-nodejs-google-image-crawling


https://shlee0882.github.io/vuejs-nodejs-google-image-crawling/#/


서버쪽 


https://github.com/shlee0882/node-crawling-google-image

  1. kdhyun55 2020.11.18 17:13

    유용한 글 정말 잘 보고 가요

  2. zullu70 2020.11.19 00:08

    도움되는 글 매우 잘 배우고 가요


구글 검색엔진 API, Node js, Vue js를 활용하여 간단하게 이미지 크롤링하는 프로젝트를 만들어 보려고 한다.



본인은 구글 이미지를 크롤링 할 수 있는 오픈소스가 있는지 검색하였고

github에서 google-images라는 오픈소스를 발견하여 npm을 이용하여 해당 오픈소스를 설치하여 활용하였다.



1. 일단 제일 먼저 Nodejs프로젝트를 생성해보자. 



폴더를 하나 만들고 cmd에서 해당폴더의 경로로 이동 후 npm init을 한다.




그럼 package.json이 생성되고 해당 폴더경로로 VSC(visual studio code)로 오픈한다.



2. VSC에서 TERMINAL창을 열고 해당 위치에서 필요한 package를 설치한다.





3. 구글 커스텀 검색 엔진을 생성한다.


https://cse.google.com/cse


들어가서 간단하게 구글 검색엔진을 생성할 수 있고 

생성하면 검색엔진 ID가 나오게 된다.


이미지 검색 부분을 가능하게 설정해주고

해당 검색엔진 ID를 보관한다.





4. 구글 개발자 콘솔로 접속하여 프로젝트를 생성 및 연결하기


https://console.developers.google.com/


구글 개발자 콘솔로 접속하여 프로젝트를 생성한다.

본인은 googleSearchApiTest라는 프로젝트를 생성했다. 




프로젝트 생성 이후 라이브러리로 이동하여 custom search api를 검색하여 api 사용해보기를 설정한다.




그다음 사용자 인증정보 탭으로 이동 후 API 키를 하나 생성한다.


여기서 생성한 API_KEY를 보관한다.




우리는 총 2개의 값 (검색엔진 ID와 API_KEY) 를 갖게 되었다.


오픈소스 google-images 레퍼런스

https://github.com/vadimdemedes/google-images



5. 다시 프로젝트로 돌아온다.



getGoogleImg.js라는 자바스크립트 파일을 하나 생성한다.



6. 구글검색엔진으로 이미지 크롤링 하기 위한 코드를 작성한다.


getGoogleImg.js 파일에 해당 코드를 작성한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
const GoogleImages = require('google-images');
let url = require('url');
let request = require('request');
const fs = require('fs');
var zip = new require('node-zip')();
 
// ** 8라인 본인의 검색엔진 ID와 API_KEY로 교체 필수
const client = new GoogleImages('검색엔진 ID''API_KEY');
 
const keyWord = "조현영";
const pageStVal = 1;
const pageEndVal = 201;
 
let saveDir = __dirname + "/"+keyWord;
 
if(!fs.existsSync(saveDir)){
    fs.mkdirSync(saveDir);
}
 
// 이미지 검색
const searchFunc = (pageStVal) =>{
    client.search(keyWord,  {page: pageStVal, size: 'large'}).then(images => {
        images.forEach(img => {
            console.log(img);
            let filePath = url.parse(img.url).pathname;
            let newFilePath = filePath.replace(/[^a-zA-Z0-8\.]+/g, '_');
            let localFilePath = saveDir + "/" + newFilePath;
            let pattern = /\.(jpg|png|gif)\b/
            
            // 파일길이가 200 미만이고 이미지 파일인지 체크
            if(newFilePath.length<200 && pattern.test(newFilePath)){
                try {
                    request.get(img.url).on('error'function(err) {
                        console.log('request error1:', err);
                    }).pipe(
                        fs.createWriteStream(localFilePath).on('close'function() {})
                    );
                } catch (err) {
                    console.log('request error2:', err);
                }
            };
        });
        compareTwoVal(pageStVal, pageEndVal);
    }).catch(error => {
        console.log(">>>>>>>>>>>>>>>>>>>"+error);
        console.log("모든 이미지를 수집했습니다.");
        makeImgToZip();
        return;
    });
}
 
// 이미지 압축파일 만들기
const makeImgToZip = () =>{
    var zipName = keyWord+".zip";
    var someDir = fs.readdirSync(__dirname+"/"+keyWord);
    var newZipFolder = zip.folder(keyWord);
    
    for(var i = 0; i < someDir.length; i++){
        newZipFolder.file(someDir[i], fs.readFileSync(__dirname+"/"+keyWord+"/"+someDir[i]),{base64:true});
    }
    var data = zip.generate({base64:false,compression:'DEFLATE'}); 
    fs.writeFile(__dirname +"/"+ zipName, data, 'binary'function(err){
        if(err){
            console.log(err);
        }
    });
}
 
// 페이징 검색
const compareTwoVal = (pageStVal, pageEndVal) =>{
    if(pageStVal<=pageEndVal){
        setTimeout(function() {
            pageStVal += 10;
            console.log("pageStVal: >>>>>>>>"+pageStVal);
            searchFunc(pageStVal);
        }, 500);
    }else{
        console.log("모든 이미지를 수집했습니다.");
        makeImgToZip();
        return;
    }
}
 
// 이미지 수집 시작
searchFunc(pageStVal);
cs



코드를 작성 및 저장 후 

termial에서 node getGoogleImg.js 로 실행한다.





정상적으로 동작한다면 

크롤링한 이미지 데이터를 json형태로 return받게 된다.


이제 이 많은 데이터가 

키워드의 폴더 하위로 로컬컴퓨터에 저장하게 되고 zip파일도 만든다.


레인보우 조현영의 이미지 데이터가 잘 크롤링 된 것을 확인 할 수 있다. 






Nodejs로 크롤링 테스트가 완료되었으니


프론트에 Vuejs를 사용해 사용자가 키워드를 넣어 크롤링 할수 있게 구현해보자.


[수정된 코드 github url]


https://github.com/shlee0882/node-crawling-google-image


  1. 코린이 2020.12.20 21:21

    안녕하세요 포스팅 해주시는 유용한 글들 너무 잘 보고있습니다.

    다름이 아니라 본 포스트를 따라 하며 배우는 과정에서 이미지 크롤링의 정확도 및 크롤링 되는 이미지의 양이 직접 구현하신 것보다 많이 떨어져 질문드립니다.

    혹시 어떤 부분을 개선하면 더 질적과 양적으로 향상된 크롤링이 가능할까요?

    • effortDev 2020.12.27 17:43 신고

      안녕하세요.
      현재 프론트에서는 CORS 문제로 인해 크롤링이 제대로 안되는 경우가 있습니다.
      그래서 서버쪽만 구현해서 크롤링해보는것도 좋을것 같습니다.
      구글 검색엔진에서는 최대 20페이지 까지 총 200개까지 제공해주고 있는것 같습니다.
      그래서 그 이상의 데이터를 요청하면 받아올 수 없습니다.
      또한 오픈 소스인 google-images에 받아온
      크롤링된 데이터를 제 logic에서 jpg, png, gif인것만 필터 해서 가져와
      이미지가 적게 받아진 것이었습니다.
      이 필터를 제거하고 URL에서 받아온 데이터가 이미지확장자가 아니더라도
      이미지파일로 변환해버리는 작업을 코드수정을 통해 추가하였습니다.
      해당 사항은 포스팅 한 곳에 github url로 올려놓았습니다.
      의견주셔서 감사하고 재확인해주시면 감사하겠습니다.

+ Recent posts