Develop/DevOps

[Intellij] GCP Plugin - Cloud Code 사용하기 in GCS | [Intellij] GCP Plugin - Using Cloud Code in GCS

GCP Storage를 연동하여 사용하는 글을 블로그에 포스팅하려고 Spring initializer을 이용해서 프로젝트를 생성하면서 좋은 플러그인을 발견하여 기록하려 한다.위와 같은 dependency로 새로운 프로젝트를 설정하니, Cloud Code라는plugin 추천이 떠서 받아보았다.Google에서 개발한 플러그인으로, Stackdriver Debugger와 통합되서 GCP에서 실행되는 프로덕션 애플리케이션을 intellij에서 디버깅할 수 있다. 특히 Stackdriver Debugger를 사용하면 Stackdriver Debugger를 사용하면 Compute Engine과 App Engine 표준 환경 및 가변형 환경 모두에 배포된 자바 애플리케이션을 디버깅할 수 있다. 또한 App Engin..

[Intellij] GCP Plugin - Cloud Code 사용하기 in GCS | [Intellij] GCP Plugin - Using Cloud Code in GCS

728x90

GCP Storage를 연동하여 사용하는 글을 블로그에 포스팅하려고 Spring initializer을 이용해서 프로젝트를 생성하면서 좋은 플러그인을 발견하여 기록하려 한다.

Initializer에 Google Cloud Platform 관련 dependency를 추가할 수 있다.

위와 같은 dependency로 새로운 프로젝트를 설정하니, Cloud Code라는plugin 추천이 떠서 받아보았다.

Google에서 개발한 플러그인으로, Stackdriver Debugger와 통합되서 GCP에서 실행되는 프로덕션 애플리케이션을 intellij에서 디버깅할 수 있다. 특히 Stackdriver Debugger를 사용하면 Stackdriver Debugger를 사용하면 Compute Engine과 App Engine 표준 환경 및 가변형 환경 모두에 배포된 자바 애플리케이션을 디버깅할 수 있다. 또한 App Engine 환경 배포를 직접 IDE에서 할 수 있는 장점도 있다.

인텔리제이의 Preference [ ⌘ + , ] > Plugins > clod code 를 입력해서 설치할 수 있고, 설치가 완료될 경우 intellij를 restart 해야 플러그인을 적용할 수 있다.

https://cloud.google.com/code/docs/intellij/how-to

 

안내 가이드  |  IntelliJ용 Cloud Code  |  Google Cloud

Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 4.0 License, and code samples are licensed under the Apache 2.0 License. For details, see the Google Developers Site Policies. Java is a registered trade

cloud.google.com

설명서를 읽어보니 Intellij가 아니더라도 JetBrains 의 여러 IDE에서 가능했다. 다만 Intellij에 최적화된 플러그인이다.

PyCharm / WebStorm / PhpStorm / Rider / RubyMine / GoLand / AppCode / CLion

또한 Cloud code에서 제공하는 Google Cloud Platform에서 지원되는 프로덕트 리스트는 총 6개로 아래와 같으며 개요를 읽어보니 Kubernetes 개발, 배포에서의 편리성이 가장 큰 장점인 듯하다.
근데 나는 Storage 작업을 할 건데,, 여튼 저튼 쨋든

1. Kubernetes : 애플리케이션 만들기, 애플리케이션 배포
2. App Engine : 애플리케이션 만들기 (가변, 표준)
3. Cloud API 및 GCP 라이브러리 관리
4. Cloud Storage : 버킷 안 콘텐츠 탐색
5. Cloud Source Repositories
6. Stackdriver Debugger : logging

storage 연동 첫 시작

플러그인 설치를 완료하면 인텔리제이 우측 바에 google cloud storage, kubernetes Explorer 탭이 생기게 되는데,
현재 내가 개발할 프로젝트는 cloud storage와 관련된 것이라, storage 탭에 대해 알아보려 한다.

storage에 대한 정보를 얻어오려면 GCP의 project를 가져와야 하기 때문에, 그 프로젝트의 권한을 가진 account로 로그인을 진행해준다.
로그인 버튼을 누르면 왼쪽 아래 그림과 같이 로그인 창이 뜨고 허용을 눌러주면 된다.

그리고 다시 Google Cloud Storage 탭으로 돌아와서 project를 선택해주면 되는데, 내가 현재 선택한 account가 권한을 가진 프로젝트 리스트들이 보이므로, storage를 사용할 project를 선택한다. 

나는 daily-commit이라는 개인 프로젝트를 선택하였다. 이렇게 간단하게 연동이 마무리된다.
따로 console.cloud web을 통해 bucket안에 들어있는 파일, 폴더를 찾아보지 않아도, 개발하는 IDE 안에서 볼 수 있다는 장점이 있다.

나는 프로젝트에 두 개의 bucket을 만들었고, ewha-commiters-static-file이라는 버킷 안에 여러 폴더 + 파일들을 저장해둔 상태이다.
이런 내용을 기존에 GCP console Web을 이용해서 봤다면, 플러그인을 이용해서

아래와 같이 Intellij IDE 안에서 bucket안에 있는 파일의 내용을 확인할 수 있다.

다만 아쉬운 게 있다면, Web GCP Console에서는 파일의 URL 및 다운로드 경로도 알 수 있었는데, plugin에서는 지원하지 않는다.
우클릭을 하면 아래와 같이 blob name과 bucket name을 복사할 수 있는데, 이를 이용해서 수제로 URL, 다운로드 경로를 만들어주자.

URL : gs://[bucket-name]/[blob-name]
링크 URL : https://storage.cloud.google.com/[bucket-name]/[blob-name]?authuser=0

 

storage 버켓 확인은 이 플러그인을 효과적으로 사용하는 것 같지 않아서, 다음번엔 Stackdriver의 log를 플러그인을 통해서 확인하는 방법을 찾아봐야겠다. 

I was about to write a blog post about integrating GCP Storage, and while creating a project using Spring Initializer, I discovered a nice plugin that I wanted to document.

You can add Google Cloud Platform related dependencies in the Initializer.

When I set up a new project with the dependencies shown above, a plugin recommendation for Cloud Code popped up, so I gave it a try.

It's a plugin developed by Google that integrates with Stackdriver Debugger, allowing you to debug production applications running on GCP directly from IntelliJ. In particular, with Stackdriver Debugger, you can debug Java applications deployed on both Compute Engine and App Engine standard and flexible environments. It also has the advantage of letting you deploy to App Engine environments directly from the IDE.

You can install it by going to IntelliJ's Preferences [ ⌘ + , ] > Plugins > and searching for "cloud code." Once the installation is complete, you need to restart IntelliJ to apply the plugin.

https://cloud.google.com/code/docs/intellij/how-to

 

How-to Guides  |  Cloud Code for IntelliJ  |  Google Cloud

Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 4.0 License, and code samples are licensed under the Apache 2.0 License. For details, see the Google Developers Site Policies. Java is a registered trade

cloud.google.com

After reading the documentation, I found out it works not only with IntelliJ but also with several other JetBrains IDEs. However, it is optimized for IntelliJ.

PyCharm / WebStorm / PhpStorm / Rider / RubyMine / GoLand / AppCode / CLion

Also, Cloud Code supports a total of 6 Google Cloud Platform products as listed below. After reading the overview, it seems like the biggest advantage is the convenience in Kubernetes development and deployment.
But I'm just going to work with Storage, so... anyway, moving on.

1. Kubernetes : Create applications, deploy applications
2. App Engine : Create applications (flexible, standard)
3. Cloud API and GCP library management
4. Cloud Storage : Browse contents within buckets
5. Cloud Source Repositories
6. Stackdriver Debugger : logging

Getting started with Storage integration

Once the plugin installation is complete, you'll see Google Cloud Storage and Kubernetes Explorer tabs appear on the right sidebar of IntelliJ.
Since the project I'm currently working on is related to Cloud Storage, I'm going to look into the Storage tab.

To retrieve information about storage, you need to fetch the GCP project, so you'll need to log in with an account that has permissions for that project.
When you click the login button, a login window like the one on the left below will appear — just click "Allow."

Then go back to the Google Cloud Storage tab and select your project. You'll see a list of projects that your currently selected account has permissions for, so just pick the project you want to use with Storage. 

I selected my personal project called "daily-commit." And just like that, the integration is done.
The nice thing is that you don't have to go through console.cloud web to look for files and folders inside your buckets — you can see them right inside the IDE where you're developing.

I created two buckets in my project and have stored several folders and files inside a bucket called "ewha-commiters-static-file."
If you used to check this kind of stuff through the GCP Console Web, now you can use the plugin instead.

As shown below, you can view the contents of files inside a bucket right within the IntelliJ IDE.

One downside though — in the Web GCP Console, you could see the file's URL and download path, but the plugin doesn't support that.
If you right-click, you can copy the blob name and bucket name as shown below. You can use these to manually construct the URL and download path.

URL : gs://[bucket-name]/[blob-name]
Link URL : https://storage.cloud.google.com/[bucket-name]/[blob-name]?authuser=0

 

Checking storage buckets doesn't seem like the most effective use of this plugin, so next time I should look into how to check Stackdriver logs through the plugin. 

댓글

Comments

Develop/Springboot

크롤링 테스트를 위한 mock server test 구축 | Mock Server Test Setup for Crawling Tests

Java의 Jsoup을 이용해서 페이지를 크롤링을 하는 코드는 찾아보면 많지만, 크롤링을 테스트하는 코드는 찾기 어려웠다. 따라서 크롤링 테스트를 짜기 위해 직접 nginx 서버를 띄어서 그 서버를 크롤링 하기도 하는 등 JavaBom 스터디원과 구현해본 크롤링 테스트에 대한 포스팅을 하게되었다.참고로 nginx 서버를 띄어서 크롤링 하는것은 실제 서버라서 크롤링 테스트의 의미가 없는 것 같다. 실제 서버가 죽으면 크롤링 테스트도 못하므로 결국은 @Ignore를 걸어야하는 테스트이기 때문이다.내가 생각하기에 크롤링 테스트에서 쟁점은 2가지 이다.1. Parsing 테스트[ 목적 ] 페이지의 html 파일에서 크롤링을 통해 원하는 정보만을 가져왔는지 체크한다.[ 구현 방식 ]크롤링을 원하는 페이지를 ctr..

크롤링 테스트를 위한 mock server test 구축 | Mock Server Test Setup for Crawling Tests

728x90

Java의 Jsoup을 이용해서 페이지를 크롤링을 하는 코드는 찾아보면 많지만, 크롤링을 테스트하는 코드는 찾기 어려웠다. 따라서 크롤링 테스트를 짜기 위해 직접 nginx 서버를 띄어서 그 서버를 크롤링 하기도 하는 등 JavaBom 스터디원과 구현해본 크롤링 테스트에 대한 포스팅을 하게되었다.

참고로 nginx 서버를 띄어서 크롤링 하는것은 실제 서버라서 크롤링 테스트의 의미가 없는 것 같다. 
실제 서버가 죽으면 크롤링 테스트도 못하므로 결국은 @Ignore를 걸어야하는 테스트이기 때문이다.

내가 생각하기에 크롤링 테스트에서 쟁점은 2가지 이다.

1. Parsing 테스트

[ 목적 ]
페이지의 html 파일에서 크롤링을 통해 원하는 정보만을 가져왔는지 체크한다.

[ 구현 방식 ]
크롤링을 원하는 페이지를 ctrl+s 를 이용해서 .html 파일로 받아온 후, 해당 html 파일을 내 springboot project의 resource에 저장한다. 이후 이 파일을 받아와, 내가 만든 크롤링 코드를 돌려서, 내가 파싱한 정보가 원하는 대로 크롤링이 잘 되었는지 확인한다.
  ex ) 네이버 영화 html 에서 찾아온 영화 리스트의 항목이 10개가 맞는가
  ex ) 크롤링 한 영화의 title이 "마션"이 맞는가 등등 (젤 좋아하는 영화ㅋ)

2. connect 테스트

[ 목적 ]
크롤링으로 해당 페이지를 받아오겠다는 요청을 보내고 응답을 받아 내가 만든 크롤링 코드가 잘 동작하는지를 체크한다.

[ 구현방식 ]
test를 위한 Mockserver를 하나 생성한다. 우리가 보는 영화 페이지가 GET 메서드로 URL 요청을 보낼 경우 html 페이지를 리턴하는 것처럼, jsoup connect의 결과를 확인하기 위한 mock server를 만드는 것이다. 이 mock server 역시 내가 크롤링을 원하는 네이버 영화 페이지처럼 특정 URL 을 GET 메소드를 통해 호출할 경우 특정 html 파일을 보내도록 지정한다.

[ 기능 ]
1. 고정된 response를 만들고 return 할 수 있다.
2. request를 다른 서버에 forwarding 한다.
3. callbacks 실행이 가능하다.
4. request를 확인할 수 있다.


오늘 포스팅을 위해 사용할 크롤링 코드 + 페이지는 네이버 영화의 랭킹 페이지를 이용하려 한다. 

https://movie.naver.com/movie/sdb/rank/rmovie.nhn

0. 디렉토리 구조

테스트 디렉토리 구조와 크롤링 디렉토리 구조

[포스팅 관련 코드] https://github.com/mjung1798/Jyami-Java-Lab/tree/master/crawler-mock-server-test

 

mjung1798/Jyami-Java-Lab

Jyami의 Spring boot 및 Java 실험소. Contribute to mjung1798/Jyami-Java-Lab development by creating an account on GitHub.

github.com

1. Jsoup을 이용한 Crawling

네이버 영화의 랭킹페이지

여기서 랭킹 항목의 순위와, 영화명, 그리고 연결 페이지 링크를 크롤링 하겠다.

# build.gradle

dependencies {
	implementation 'org.jsoup:jsoup:1.11.3'
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
}

Jsoup을 이용해 크롤링을 했고, Builder를 사용하려고 lombok을 넣어주었다.

// NaverMovieCrawler.java

public class NaverMovieCrawler {

    public static void main(String[] args) {
        Document document = new NaverMovieCrawler().getCrawlingResult("https://movie.naver.com/movie/sdb/rank/rmovie.nhn");
        TopMovieList topMovieList = new TopMovieList(document);
        System.out.println(topMovieList.toString());
    }

    // url 을 보내면, 해당 페이지를 크롤링하여 Document 타입을 리턴한다.
    public Document getCrawlingResult(String url){
        try {
            return Jsoup.connect(url)
                    .timeout(2000)
                    .get();
        } catch (IOException e) {
            throw new RuntimeException("crawling 실패");
        }
    }
}

스프링 부트로 API를 만들어도 좋지만, 일단은 간단하게 main을 만들어주고 돌려보았다.
여기서 중요한게 Jsoup.connect(url) 이부분인데, mockserver 테스트를 하는 이유라고 볼 수 있다. Jsoup의 connect() 메소드는 기본적으로 get 매핑을 요청하며, 이외에도 Jsoup에서 제공하는 다양한 메서드를 체이닝해서 좀 더 구체적인 요청을 보낼 수 있다.

그러나 크롤링하려는 네이버 영화 페이지의 html 파일을 받는 요청이므로 Get 메서드 + URL 매핑으로 간단하게 connect() 메소드를 마무리 할 수 있다.

@AllArgsConstructor
@Getter
public class TopMovieList {

    private List<Movie> topMovies = new ArrayList<>();

    public TopMovieList(Document document){
        this.topMovies = document.select("table.list_ranking tr").stream()
                .filter(x -> !x.select(".ac").isEmpty())
                .map(Movie::of)
                .collect(Collectors.toList());
    }

    @Override
    public String toString() {
        return topMovies.stream()
                .map(Movie::toString)
                .collect(Collectors.joining(", \n"));
    }
}

main에서 보면 알다싶이 나는 TopMovieList 라는 일급컬렉션 객체를 만들었다. Jsoup을 이용해 받아온 Document객체를 (html 전체를 리턴한다) 파싱하여 List<Movie>를 만든다. 이때 Movie 객체 하나하나를 만들기 위해서 네이버 영화 랭킹의 테이블 row를 하나씩 분리해서 Movie 객체의 of() 메서드를 이용해서 테이블 row 하나하나에서의 데이터를 분리하는 파싱은 Movie 객체에 맡긴다.

// Movie.java

public class Movie {
    private int rank;
    private String title;
    private String detailLink;

    @Builder
    private Movie(int rank, String title, String detailLink) {
        this.rank = rank;
        this.title = title;
        this.detailLink = detailLink;
    }

    public static Movie of (Element element){
        return Movie.builder()
                .rank(Integer.parseInt(element.select(".ac img").attr("alt")))
                .title(element.select(".title").text())
                .detailLink(element.select(".title a").attr("href"))
                .build();
    }

    @Override
    public String toString() {
        return "Movie{" +
                "rank='" + rank + '\'' +
                ", title='" + title + '\'' +
                ", detailLink='" + detailLink + '\'' +
                '}';
    }
}

Movie 객체에서는 List<Movie>의 생성자에서 받아온 네이버 영화 랭킹 테이블의 row 한 줄인 Element를 인자로 받아, 데이터를 바인딩해준다. 개발자도구를 이용해 내가 원하는 데이터만을 분리할 수 있도록 css selector를 적절히 사용한다.

> Task :NaverMovieCrawler.main()
Movie{rank='1', title='남산의 부장들', detailLink='/movie/bi/mi/basic.nhn?code=176306'}, 
Movie{rank='2', title='히트맨', detailLink='/movie/bi/mi/basic.nhn?code=185838'}, 
Movie{rank='3', title='미스터 주: 사라진 VIP', detailLink='/movie/bi/mi/basic.nhn?code=177509'}, 
Movie{rank='4', title='해치지않아', detailLink='/movie/bi/mi/basic.nhn?code=180025'}, 
... // 생략

이렇게 만들어진 List<Movie> 객체를 toString()을 이용해 main에서 출력하면 위와 같이 확인 할 수 있어, 크롤링이 잘되었음을 확인 할 수 있다.

 

2. MockServer 생성하기

mock-server 생성과 테스트를 위한 모듈을 추가해준다. test는 junit5를 이용해서 했다.

# build.gradle

test {
	useJUnitPlatform()
}

dependencies {
	testCompile group: 'org.mock-server', name: 'mockserver-netty', version: '5.8.1'
}

가장 먼저 크롤링 하려는 네이버 영화 페이지로 가서 html을 다운받는다. 내가 request와 response를 지정한 mock-server가 네이버 영화 페이지의 html 파일을 리턴해야하기 때문이다.

나는 ranking_naver_move.html 이라는 이름으로 저장했다. 그리고 이렇게 다운 받은 파일을 test > resources 폴더 안에 넣는다.
추가로 초반에 말한 parsing test와 관련해서 테스트 코드를 구현하느라 나는 하나의 html 파일이 더 생기게되었다.

mock-server와 관련한 테스트를 할 경우에, 테스트 시작 전, mock-server를 열어주어야한다. 그리고 테스트가 끝나면 mock-server를 닫아주어야한다. 따라서 테스트코드의 시작과 전에 해당 메소드를 써준다.

// NaverMovieCrawlerTest.java

public class NaverMovieCrawlerTest {

    private static final int PORT = 9000;
    private static ClientAndServer mockServer;

    @BeforeEach
    void setUp() {
        mockServer = ClientAndServer.startClientAndServer(PORT);
        System.out.println("mock server start");
    }

    @AfterEach
    void tearDown() {
        mockServer.stop();
        System.out.println("mock server stop");
    }

@Test 어노테이션을 붙인 빈 테스트를 만들고 이 테스트를 돌려보면 아래와 같이 엄청난 로그가 뜬다. 몇몇 눈에 띄는 로그를 읽어보면 java version, cachesize 등을 자동으로 설정함을 알 수 있다.

또한, 인자로 PORT값 즉 9000을 넣은 것은 잠깐 뜨는 mock-server의 포트를 지정한 것인데, 실제로 sout을 찍은 부분 이후 즉, startClientAndServer 메소드가 실행된 이후, 9000포트로 mock-server가 설정되었다는 로그가 출력됨을 알 수 있다. 

이제 mock-server의 자세한 세팅을 해보자. 내가 구현하려는 mock-server는 네이버 영화 랭킹 페이지와 같은 역할을 수행해야한다. 따라서 네이버 랭킹 페이지를 나타내는 path인 "/movie/sdb/rank/rmovie.nhn"을 입력할 경우 html 파일을 리턴하도록 mock-server의 기능을 설정해주어야 한다. 이는 아래 보다 싶이 .when()과 .response() 메소드를 이용해서 설정할 수 있다.

when 부분에는 mock-server에서 받을 request 그리고 respond 부분에는 when에서 정의한 request를 받았을 때 실행할 response 값을 설정해 준다.

가장 먼저 mock-server host인 localhost, 그리고 mock-server를 실행할 port를 적어준다. 이때 mock-server를 가장먼저 시작할 때 사용하는 메서드인 ClientAndServer.startClientAndServer() 에서 만든 mockserver의 port를 적어주었기 때문에, 이때 사용한 port와 똑같이 적어준다.  따라서 PORT(9000)을 할당하였다. 

// NaverMovieCrawlerTest.java

private void createNaverRankingPageServer(String filePath){
        byte[] response = readHtmlFile(filePath);

        new MockServerClient("localhost", PORT)
                .when(
                        request()
                                .withMethod("GET")
                                .withPath("/movie/sdb/rank/rmovie.nhn")
                )
                .respond(
                        response()
                                .withStatusCode(200)
                                .withBody(response)
                );
}

request로는 네이버 영화 랭킹 페이지의 URL인 "/movie/sdb/rank/rmovie.nhn"를 GET 메소드로 호출하도록 정의하고, 이 경우에는
response는 statusCode 200과 함께 랭킹페이지 html을 넘겨주도록 지정하였다. (response 변수)

이때 response 변수는 아까 저장한 네이버 영화 랭킹 페이지의 html 파일이 리턴되어야 하므로, 아래와 같이 Stream 형태로 html 파일의 내용을 받아오는 로직을 짰다. (인자로는 리턴 받을 html 파일을 적어준다)

// NaverMovieCrawlerTest.java

private byte[] readHtmlFile(String filePath) {
        InputStream resourceAsStream = getClass().getClassLoader()
                .getResourceAsStream(filePath);
        try {
            assert resourceAsStream != null;
            return IOUtils.toByteArray(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("file IO 실패");
        }
}

mock-server 세팅까지 한 후에, @Test에 createNaverRankingPageServer() 메서드만 적어서 실행시키면 또 여러 로그가 나오는데, 대충 읽어보면, 내가 설정한 mock-server 세팅에 관한 내용이다.

 

3. MockServer를 이용한 Parsing 및 Connect 테스트

이제 mock-server 설정도 모두 마쳤으니, 테스트할 크롤링 코드를 적어준다.  mock-server의 request와 response를 지정해주는 아까 구현한 createPathNoteServer() 메서드를 호출한다. (이때 인자값은 response 값을 의미하도록 구현했다.)

이후 내가 설정한 mock-server의 주소값 포트 Url에 맞게, Crawling한 결과를 가져오는 코드를 넣어주고, 그 값을 테스트한다.

[GET] http://localhost:9000/movie/sdb/rank/rmovie.nhn
host localhost
port 9000
path /movie/sdb/rank/rmovie.nhn

URL을 적어줄 때 http를 빼고 적어주면 인식하지 못한다! 주의하자

 // NaverMovieCrawlerTest.java
 
    @BeforeEach
    void setUp() {
        mockServer = ClientAndServer.startClientAndServer(PORT);
        System.out.println("mock server start");
    }

    @Test
    public void naverMovieMockServerTest(){

        createNaverRankingPageServer("ranking_naver_movie.html");
        Document document = new NaverMovieCrawler().getCrawlingResult("http://localhost:9000/movie/sdb/rank/rmovie.nhn");
        TopMovieList topMovieList = new TopMovieList(document);
        assertThat(topMovieList.getTopMovies().size()).isEqualTo(50);

    }

    @AfterEach
    void tearDown() {
        mockServer.stop();
        System.out.println("mock server stop");
    }

이렇게 코드를 완성해서 실행한 결과 크롤링한 데이터가 잘 들어와서 Parsing까지 완료되었음을 테스트 성공 표시를 통해 확인할 수 있다.

 

더보기

4. 크롤링 Parsing 테스트

크롤링 Parsing 테스트의 내용은 코드를 구현한 github에서 Movie.test / TopMovieList.test 파일을 이용해서, 크롤링 로직이 데이터를 내가원하는대로 객체에 바인딩 하는 지 여부를 확인하였다.

그러나 읽어보면 어렵지 않은 코드라서 생략하였다. github 를 참고하자 :)

 

[관련 블로그]

javabom 스터디를 통해서 쓰게된 글이며, 팀 블로그가 존재한다 :) 같은 내용의 글을 업로드 한 상태이다.

https://javabom.tistory.com/

 

자바봄

자바 스프링 블로그 입니다.

javabom.tistory.com

 

[참고 페이지]

https://www.programcreek.com/java-api-examples/?class=org.mockserver.integration.ClientAndServer&method=startClientAndServer

https://www.baeldung.com/mockserver 

There are plenty of examples out there for crawling pages using Java's Jsoup, but it was hard to find code for testing the crawling. So I ended up writing this post about crawling tests that I implemented with the JavaBom study group — including spinning up an nginx server and crawling from it.

By the way, crawling from an nginx server doesn't really serve as a proper crawling test since it's an actual server. 
If the real server goes down, the crawling test can't run either, so you'd end up having to slap @Ignore on it anyway.

In my opinion, there are two key points when it comes to crawling tests.

1. Parsing Test

[ Purpose ]
Verify that only the desired information was extracted from the page's HTML file through crawling.

[ Implementation ]
Save the page you want to crawl as an .html file using ctrl+s, then store that HTML file in your Spring Boot project's resources. After that, load the file, run your crawling code against it, and verify that the parsed information matches what you expected.
  e.g.) Does the movie list extracted from the Naver Movie HTML have exactly 10 items?
  e.g.) Is the crawled movie title "The Martian"? etc. (my favorite movie lol)

2. Connect Test

[ Purpose ]
Send a request to fetch the page via crawling, receive a response, and verify that your crawling code works correctly.

[ Implementation ]
Create a mock server for testing. Just like how a movie page returns an HTML page when you send a URL request via GET method, we create a mock server to verify the result of Jsoup's connect. This mock server is configured so that when a specific URL is called via GET method — just like the Naver Movie page we want to crawl — it returns a specific HTML file.

[ Features ]
1. You can create and return a fixed response.
2. It can forward requests to another server.
3. It can execute callbacks.
4. You can inspect the requests.


For today's post, I'll be using the Naver Movie ranking page as the crawling target. 

https://movie.naver.com/movie/sdb/rank/rmovie.nhn

0. Directory Structure

Test directory structure and crawling directory structure

[Code for this post] https://github.com/mjung1798/Jyami-Java-Lab/tree/master/crawler-mock-server-test

 

mjung1798/Jyami-Java-Lab

Jyami's Spring Boot and Java experiment lab. Contribute to mjung1798/Jyami-Java-Lab development by creating an account on GitHub.

github.com

1. Crawling with Jsoup

Naver Movie ranking page

From here, I'll crawl the ranking number, movie title, and the link to the detail page.

# build.gradle

dependencies {
	implementation 'org.jsoup:jsoup:1.11.3'
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
}

I used Jsoup for crawling and added Lombok to use the Builder pattern.

// NaverMovieCrawler.java

public class NaverMovieCrawler {

    public static void main(String[] args) {
        Document document = new NaverMovieCrawler().getCrawlingResult("https://movie.naver.com/movie/sdb/rank/rmovie.nhn");
        TopMovieList topMovieList = new TopMovieList(document);
        System.out.println(topMovieList.toString());
    }

    // url 을 보내면, 해당 페이지를 크롤링하여 Document 타입을 리턴한다.
    public Document getCrawlingResult(String url){
        try {
            return Jsoup.connect(url)
                    .timeout(2000)
                    .get();
        } catch (IOException e) {
            throw new RuntimeException("crawling 실패");
        }
    }
}

It would be fine to build a Spring Boot API, but for now I just created a simple main method and ran it.
The important part here is Jsoup.connect(url) — and this is essentially the reason we need mock server testing. Jsoup's connect() method basically sends a GET request, and you can chain various other methods provided by Jsoup to send more specific requests.

However, since we're just fetching the HTML file of the Naver Movie page, we can keep the connect() method simple with just a GET method + URL mapping.

@AllArgsConstructor
@Getter
public class TopMovieList {

    private List<Movie> topMovies = new ArrayList<>();

    public TopMovieList(Document document){
        this.topMovies = document.select("table.list_ranking tr").stream()
                .filter(x -> !x.select(".ac").isEmpty())
                .map(Movie::of)
                .collect(Collectors.toList());
    }

    @Override
    public String toString() {
        return topMovies.stream()
                .map(Movie::toString)
                .collect(Collectors.joining(", \n"));
    }
}

As you can see in the main method, I created a first-class collection object called TopMovieList. It parses the Document object fetched via Jsoup (which returns the entire HTML) to build a List<Movie>. To create each individual Movie object, I split the table rows from the Naver Movie ranking table and delegate the parsing of each row's data to the Movie object's of() method.

// Movie.java

public class Movie {
    private int rank;
    private String title;
    private String detailLink;

    @Builder
    private Movie(int rank, String title, String detailLink) {
        this.rank = rank;
        this.title = title;
        this.detailLink = detailLink;
    }

    public static Movie of (Element element){
        return Movie.builder()
                .rank(Integer.parseInt(element.select(".ac img").attr("alt")))
                .title(element.select(".title").text())
                .detailLink(element.select(".title a").attr("href"))
                .build();
    }

    @Override
    public String toString() {
        return "Movie{" +
                "rank='" + rank + '\'' +
                ", title='" + title + '\'' +
                ", detailLink='" + detailLink + '\'' +
                '}';
    }
}

The Movie object takes an Element — a single row from the Naver Movie ranking table passed from the List<Movie> constructor — and binds the data. I used appropriate CSS selectors with the browser's developer tools to extract only the data I needed.

> Task :NaverMovieCrawler.main()
Movie{rank='1', title='남산의 부장들', detailLink='/movie/bi/mi/basic.nhn?code=176306'}, 
Movie{rank='2', title='히트맨', detailLink='/movie/bi/mi/basic.nhn?code=185838'}, 
Movie{rank='3', title='미스터 주: 사라진 VIP', detailLink='/movie/bi/mi/basic.nhn?code=177509'}, 
Movie{rank='4', title='해치지않아', detailLink='/movie/bi/mi/basic.nhn?code=180025'}, 
... // omitted

When you print the resulting List<Movie> object using toString() in the main method, you can see the output above, confirming that the crawling worked correctly.

 

2. Creating a MockServer

Add the modules needed for mock-server creation and testing. I used JUnit 5 for the tests.

# build.gradle

test {
	useJUnitPlatform()
}

dependencies {
	testCompile group: 'org.mock-server', name: 'mockserver-netty', version: '5.8.1'
}

First, go to the Naver Movie page you want to crawl and download the HTML. This is because the mock server — where we define our own request and response — needs to return the Naver Movie page's HTML file.

I saved it as ranking_naver_move.html. Then I placed the downloaded file inside the test > resources folder.
Additionally, I ended up with one more HTML file because I implemented test code related to the parsing test I mentioned earlier.

When running tests related to the mock server, you need to start the mock server before each test and shut it down after each test. So we add those methods before and after the test code.

// NaverMovieCrawlerTest.java

public class NaverMovieCrawlerTest {

    private static final int PORT = 9000;
    private static ClientAndServer mockServer;

    @BeforeEach
    void setUp() {
        mockServer = ClientAndServer.startClientAndServer(PORT);
        System.out.println("mock server start");
    }

    @AfterEach
    void tearDown() {
        mockServer.stop();
        System.out.println("mock server stop");
    }

If you create an empty test with the @Test annotation and run it, you'll see a massive amount of logs like the ones below. Reading through some of the notable ones, you can see it automatically configures things like the Java version and cache size.

Also, the PORT value of 9000 passed as an argument specifies the port for the temporary mock server. After the sout line — that is, after the startClientAndServer method executes — you can see a log confirming that the mock server has been set up on port 9000

Now let's do the detailed setup of the mock server. The mock server I'm building needs to perform the same role as the Naver Movie ranking page. So when the path "/movie/sdb/rank/rmovie.nhn" — which represents the Naver ranking page — is requested, it should return an HTML file. As you can see below, this can be configured using the .when() and .respond() methods.

The when part defines the request the mock server will receive, and the respond part defines the response to return when that request is matched.

First, specify localhost as the mock server host, and then the port to run the mock server on. Since we already specified the port when starting the mock server with the ClientAndServer.startClientAndServer() method, we use the same port here.  So I assigned PORT(9000). 

// NaverMovieCrawlerTest.java

private void createNaverRankingPageServer(String filePath){
        byte[] response = readHtmlFile(filePath);

        new MockServerClient("localhost", PORT)
                .when(
                        request()
                                .withMethod("GET")
                                .withPath("/movie/sdb/rank/rmovie.nhn")
                )
                .respond(
                        response()
                                .withStatusCode(200)
                                .withBody(response)
                );
}

For the request, I defined it to call the Naver Movie ranking page URL "/movie/sdb/rank/rmovie.nhn" via GET method. In that case,
the response is configured to return a status code of 200 along with the ranking page HTML. (the response variable)

The response variable needs to contain the HTML file of the Naver Movie ranking page that we saved earlier, so I wrote logic to read the HTML file contents as a Stream. (The argument specifies which HTML file to return.)

// NaverMovieCrawlerTest.java

private byte[] readHtmlFile(String filePath) {
        InputStream resourceAsStream = getClass().getClassLoader()
                .getResourceAsStream(filePath);
        try {
            assert resourceAsStream != null;
            return IOUtils.toByteArray(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("file IO 실패");
        }
}

After finishing the mock server setup, if you run @Test with just the createNaverRankingPageServer() method, you'll see more logs — which, if you read through them, describe the mock server configuration you set up.

 

3. Parsing and Connect Tests Using MockServer

Now that the mock server setup is all done, let's write the crawling code to test.  Call the createPathNoteServer() method we implemented earlier, which defines the mock server's request and response. (The argument represents the response value.)

Then, plug in the crawling code to fetch results from the mock server's address, port, and URL, and test the values.

[GET] http://localhost:9000/movie/sdb/rank/rmovie.nhn
host localhost
port 9000
path /movie/sdb/rank/rmovie.nhn

If you leave out "http" when writing the URL, it won't be recognized! Watch out for that.

 // NaverMovieCrawlerTest.java
 
    @BeforeEach
    void setUp() {
        mockServer = ClientAndServer.startClientAndServer(PORT);
        System.out.println("mock server start");
    }

    @Test
    public void naverMovieMockServerTest(){

        createNaverRankingPageServer("ranking_naver_movie.html");
        Document document = new NaverMovieCrawler().getCrawlingResult("http://localhost:9000/movie/sdb/rank/rmovie.nhn");
        TopMovieList topMovieList = new TopMovieList(document);
        assertThat(topMovieList.getTopMovies().size()).isEqualTo(50);

    }

    @AfterEach
    void tearDown() {
        mockServer.stop();
        System.out.println("mock server stop");
    }

After completing the code and running it, the test passed — confirming that the crawled data came through properly and parsing was completed successfully.

 

더보기

4. Crawling Parsing Test

For the crawling parsing test, I used the Movie.test / TopMovieList.test files in the GitHub repo to verify that the crawling logic binds the data to objects exactly as I intended.

But the code is pretty straightforward, so I've omitted it here. Check out the GitHub repo :)

 

[Related Blog]

This post was written through the JavaBom study group, and we have a team blog :) The same content has been uploaded there as well.

https://javabom.tistory.com/

 

JavaBom

A Java Spring blog.

javabom.tistory.com

 

[References]

https://www.programcreek.com/java-api-examples/?class=org.mockserver.integration.ClientAndServer&method=startClientAndServer

https://www.baeldung.com/mockserver 

댓글

Comments

Develop/Springboot

springboot 외부 라이브러리 jar 파일 추가 방법 | How to Add External Library JAR Files in Spring Boot

로컬 프로젝트 안에 외부에서 받아온 라이브러리인 jar파일을 추가하는 방법을 찾아보았다.gradle에 존재하지 않는 외부의 .jar 파일을 스프링 부트 안에 의존성을 추가하는 방법이다. /build.gradle 파일 안에 명시를 해주어야 한다. 1. 한 개의 파일을 넣는 방법dependencies { implementation files('libs/something_local.jar')} 2. 디렉토리에 위치한 모든 라이브러리의 의존성을 추가하는 방법dependencies { compile fileTree(dir: 'libs', include: ['*.jar'])} build.gradle에 넣고, build gradle을 하게 되면, 아래와 같이 내가 추가한 외부 라이브러리인 .jar 파일 안에..

springboot 외부 라이브러리 jar 파일 추가 방법 | How to Add External Library JAR Files in Spring Boot

728x90

로컬 프로젝트 안에 외부에서 받아온 라이브러리인 jar파일을 추가하는 방법을 찾아보았다.

gradle에 존재하지 않는 외부의 .jar 파일을 스프링 부트 안에 의존성을 추가하는 방법이다.

/build.gradle 파일 안에 명시를 해주어야 한다.

 

1. 한 개의 파일을 넣는 방법

dependencies {
    implementation files('libs/something_local.jar')
}

 

2. 디렉토리에 위치한 모든 라이브러리의 의존성을 추가하는 방법

dependencies {
   compile fileTree(dir: 'libs', include: ['*.jar'])
}

 

build.gradle에 넣고, build gradle을 하게 되면, 아래와 같이 내가 추가한 외부 라이브러리인 .jar 파일 안에 들어있는 Java 파일을 로드하고, 사용할 수 있게 된다.

 

3. 로컬 디렉토리를 라이브러리로 만들어서 의존성을 추가하는 방법

여기서도 마찬가지로 libs/something_local.jar 파일을 추가하는 방법이다.

repositories {
	mavenCentral()
	flatDir {
         dirs 'libs'
	}
}
dependencies {
	compile( name: 'something_local')
}

 

4. 외부라이브러리 추가시 충돌

java.lang.NoSuchMethodError: 'com.google.api.client.http.HttpRequest com.google.api.client.http.HttpRequest.setResponseReturnRawInputStream(boolean)'
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.buildHttpRequest(AbstractGoogleClientRequest.java:434) ~[google-api-client-1.30.4.jar:1.30.4]

외부 라이브러리로 .jar 파일을 추가하면서 정말 골치를 썩었던 문제였다.

분명, 내가 build.gradle에 추가한 dependency는 가장 상위버전이고, NoSuchMethodError 와 관련한 글을 구글링 해보았을 때 하위 버전에서 발생하는 문제이니 상위버전으로 version을 업데이트 하라는 해결을 준다.

그런데 intellij 상으로 아래 메세지를 보면 1.30.4 버전으로 가장 상위버전으로 표시가 된다.

 

이때 의심해야할 것은 외부 라이브러리에서도 똑같은 dependency를 사용하는데 외부 라이브러리의 버전이 낮을 경우이다.

이 경우에 classPath에 혼동이 생겨서 일어난 에러로 보인다. 내가 build.gradle에 추가한대로 현재 프로젝트의 classpath를 따라야하는데, 외부 라이브러리인 jar 파일의 classpath와의 충돌 때문인 것 같다.

Solution : 나는 이경우에 외부 라이브러리인 jar 파일을 압축 해제후, 해당 모듈을 지우고 다시 jar로 압축하는 방식을 사용했다.

 

jar 파일 압축 / 압축 해제

jar 파일을 라이브러리로 만드려는 과정에서, jar 파일안에 사용되는 라이브러리 gson, slf4j 두개가 내가 사용하는 springboot dependency와 충돌이 나는 상황이 있었다.

그래서 처음에 해당하는 모듈 두개를 아래 방식으로 삭제하려고 했다.

dependencies {
  compile (files('libs/something_local.jar')){
    exclude group: 'org.slf4j';
  }
}

그런데 적용이 되지 않는 방법이라서 결국 포기하고 외부 라이브러리인 jar 파일을 압축 해제하고 duplicate를 일으키는 모듈 그룹을 삭제하기로 결정했다.

 

압축 풀기

shell 열기 > jar 파일이 위치하는 directory로 이동하기

jar파일의 이름이 example.jar 라고 하자.

jar xvf [파일명].jar
jar xvf example.jar

 

압축 하기

shell 열기 > jar 파일이 위치하는 directory로 이동하기

현재 디렉토리를 압축하려고 하며, 압축 파일의 이름을 example.jar 라고 하자.

jar cvf [파일명].jar [디렉토리]
jar cvf example.jar .

 

I looked into how to add an external jar file (a library obtained from outside) to a local project.

This is how to add a dependency for an external .jar file that doesn't exist in gradle into a Spring Boot project.

You need to specify it in the /build.gradle file.

 

1. Adding a single file

dependencies {
    implementation files('libs/something_local.jar')
}

 

2. Adding dependencies for all libraries in a directory

dependencies {
   compile fileTree(dir: 'libs', include: ['*.jar'])
}

 

Once you add this to build.gradle and run build gradle, you'll be able to load and use the Java files inside the external .jar library you added, as shown below.

 

3. Adding a dependency by turning a local directory into a library

This is also a way to add the libs/something_local.jar file.

repositories {
	mavenCentral()
	flatDir {
         dirs 'libs'
	}
}
dependencies {
	compile( name: 'something_local')
}

 

4. Conflicts when adding external libraries

java.lang.NoSuchMethodError: 'com.google.api.client.http.HttpRequest com.google.api.client.http.HttpRequest.setResponseReturnRawInputStream(boolean)'
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.buildHttpRequest(AbstractGoogleClientRequest.java:434) ~[google-api-client-1.30.4.jar:1.30.4]

This was a really frustrating issue I ran into while adding an external .jar file as a library.

The dependency I added to build.gradle was clearly the latest version, and when I googled about NoSuchMethodError, the solutions all said it's an issue with older versions and to update to a newer version.

But looking at the message in IntelliJ below, it shows version 1.30.4, which is the latest version.

 

What you should suspect in this case is when the external library also uses the same dependency but with a lower version.

This error seems to be caused by confusion in the classPath. The current project should follow the classpath I specified in build.gradle, but it appears to conflict with the classpath of the external jar library.

Solution: In my case, I extracted the external jar file, removed the conflicting module, and then repackaged it as a jar.

 

Compressing / Extracting jar files

While trying to turn a jar file into a library, there was a situation where two libraries used inside the jar file — gson and slf4j — were conflicting with my Spring Boot dependencies.

So I initially tried to remove those two modules using the method below.

dependencies {
  compile (files('libs/something_local.jar')){
    exclude group: 'org.slf4j';
  }
}

But this approach didn't work, so I gave up on it and decided to extract the external jar file and delete the module groups causing the duplicates.

 

Extracting

Open shell > Navigate to the directory where the jar file is located

Let's say the jar file is named example.jar.

jar xvf [파일명].jar
jar xvf example.jar

 

Compressing

Open shell > Navigate to the directory where the jar file is located

Let's say we want to compress the current directory and name the archive example.jar.

jar cvf [파일명].jar [디렉토리]
jar cvf example.jar .

 

댓글

Comments

Develop/Web

Image Styling with Web Components - 웹 컴포넌트를 사용한 이미지 스타일링 | Image Styling with Web Components

코드랩 세미나를 준비하기 위해 한글로 정리한 자료 입니다.https://codelabs.developers.google.com/codelabs/image-styling-web-components/#0 Image Styling with Web ComponentsYour Second Custom Element Let's now create a second custom element, codelab-effects. This element will render our image and possibly apply interesting visual effects to it. To start with, this is pretty much the same as the last element—with one extra ..

Image Styling with Web Components - 웹 컴포넌트를 사용한 이미지 스타일링 | Image Styling with Web Components

728x90

코드랩 세미나를 준비하기 위해 한글로 정리한 자료 입니다.

https://codelabs.developers.google.com/codelabs/image-styling-web-components/#0

 

Image Styling with Web Components

Your Second Custom Element Let's now create a second custom element, codelab-effects. This element will render our image and possibly apply interesting visual effects to it. To start with, this is pretty much the same as the last element—with one extra det

codelabs.developers.google.com

0. 소개

Web Component란?

HTML 페이지에 재사용 가능한 요소들을 작성할 수 있는 새로운 기술
새로 사용자가 정의한 이름을 갖는다 : 내가 원하는 태그들을 모아서 캡슐화 할 수 있다.

Custom Elements(codelab-dragdrop)와 shadow DOM(codelab-effects)을 사용해서 WebComponent를 만드는 과정이다. 이것들을 결합하여 페이지로 드래그되는 이미지를 조작 할 수있는 웹 사이트를 만든다.

  • Custom Elements를 선언하는 방법
  • Component에 리스너와 핸들러를 추가하는 방법
  • Custom Design을 캡슐화하기위한 Shadow Root를 만드는 방법
  • 여러 응용 프로그램을 구성하여 작은 응용 프로그램을 만드는 방법

깃허브 저장소 : https://github.com/googlecodelabs/image-styling-web-components

 

googlecodelabs/image-styling-web-components

Image Styling with Web Components. Contribute to googlecodelabs/image-styling-web-components development by creating an account on GitHub.

github.com

1. Custom Elements 만들기

1-1. 기본 HTML 틀 잡기

<!DOCTYPE html>
<html>
<head>
<script>
/* code will go here */
</script>
</head>
<body>

<h1>Image Styling with Web Components</h1>

<!-- elements will go here -->

</body>
</html>

1-2. 나의 첫번째 Custom Element 만들기

이미지를 이 페이지로 드래그앤 드롭을 하기 위한 코드를 작성해 보자.
<codelab-dragdrop></codelab-dragdrop> 태그를 생성할 예정이며, 이 태그는 파일이 드롭되는 위치를 표시하는 곳을 나타낼 것이다.

이에 대한 로직은 Javascript를 이용하여 구현할 예정이다.
1. 새로운 element를 정의
2. element를 사용(인스턴스 화)

<codelab-dragdrop></codelab-dragdrop>

1-3. Element 정의

Custom Element는 HTMLElement라는 ES6의 클래스를 상속받은 것 이다.

ES6는 ECMAScript6의 줄임말으로
ECMAScript6는 자바스크립트 표준 단체인 ECMA가 제정하는 자바스크립트 표준이다.

자바스크립트는 프로토타입 기반(prototype-based) 객체지향 언어다. 프로토타입 기반 프로그래밍은 클래스가 필요없는(class-free) 객체지향 프로그래밍 스타일로 프로토타입 체인과 클로저 등으로 객체 지향 언어의 상속, 캡슐화(정보 은닉) 등의 개념을 구현할 수 있다.

ES6의 클래스는 기존 프로토타입 기반 객체지향 프로그래밍보다 클래스 기반 언어에 익숙한 프로그래머가 보다 빠르게 학습할 수 있는 단순명료한 새로운 문법을 제시하고 있다. 그렇다고 ES6의 클래스가 기존의 프로토타입 기반 객체지향 모델을 폐지하고 새로운 객체지향 모델을 제공하는 것은 아니다. 
[출처] https://poiemaweb.com/es6-class

<script> 태그안에 Javascript 문법을 이용해서 나의 Custom Element를 정의한다.

/* code will go here */
class CodelabDragdrop extends HTMLElement {
  constructor() {
    super();
  }

  connectedCallback() {
    // we'll do stuff here later
    console.info('Element connected!');
  }
}
customElements.define('codelab-dragdrop', CodelabDragdrop);

connectedCallback() : document의 DOM에 정의한 custom element가 맨 처음 호출되었을 때 실행된다.
customElements.defind(DOMString, class, { extends: '[tag-name]' })
- DOMString : 사용자가 element에 전달하려는 이름 (즉, 태그 네임). 이때 커스텀 엘리먼트의 이름들은 dash('-')가 포함된 이름을 사용해야하므로 주의해야한다!
- class : element의 행위가 정의된 object이다.
- extends (optional) : 상속받을 태그를 지정할 수 있다. 만약 { extends: 'p'} 이렇게 지정한다면, p 태그의 inline 성질을 갖고있는 객체가 되는 듯

개발자도구( option+command+i / F12 )를 이용해서 확인해보면 connection이 완료되었다는 메세지를 확인 할 수 있다.

 

2. Drag and Drop

2-1. Target 생성하기

<!-- elements will go here -->
<codelab-dragdrop>
  <div style="width: 200px; height: 200px; background: red;">
  </div>
</codelab-dragdrop>

페이지를 새로 고침하면 큰 빨간색 상자가 나타난다. 
더 중요한 것은 페이지를 개발자 도구로 확인하면 codelab-dragdrop 내부에 빨간색 사각형을 보유하고 있기 때문에 현재 200 x 200 픽셀의 크기를 갖고있음을 알 수 있다.

2-2. Handler 추가하기

codelab-dragdrop에 파일을 끌어다 놓을 수 있도록 확장 해보자.

Web component가 가진 기능중에 하나는 캡슐화이다.
이를 수행할 수 있는 방법은 element defind 안에 코드를 추가하는 것이다.
이전에 작성한 ES6 클래스의 constructor element 자체에 리스너를 추가하여 메소드를 업데이트 할 것이다.

 constructor() {
    super();  // you always need super

    this.addEventListener('dragover', (ev) => {
      ev.preventDefault();
    });
    this.addEventListener('drop', (ev) => {
      ev.preventDefault();
      const file = ev.dataTransfer.files[0] || null;
      file && this._gotFile(file);
    });
  }

super()는 항상 필요하다. Web Element와 관련한 클래스인 HTMLElement 클래스를 상속받고,  그 기능을 온전히 사용하기 위해서.

ev.preventDefault() : 이벤트를 취소할 수 있는 경우, 이벤트의 전파를 막지않고 그 이벤트를 취소한다.
이전에 되어있던 이벤트 내역을 취소해 두는 느낌 인 것 같다
https://developer.mozilla.org/ko/docs/Web/API/Event/preventDefault

https://developer.mozilla.org/samples/domref/dispatchEvent.html

여기서 드래그 앤 드롭과 관련된 두 가지 이벤트를 처리합니다. 
여기서 중요한 것은 drop 핸들러이다. 첫 번째 파일 (있는 경우)을 드래그하여  _getFile() 이라는 메소드를 호출하게 되어있다.

2-3. Event 방출하기

이 코드랩의 목표는 Image를 조작할 수 있는 것(조작기 - manipulator)을 만드는 것이다. 따라서 우리는 이미지를 만들고, 해당하는 조작기에 이미지를 넘겨주는 것을 목적으로 한다.

이 작업을 수행할 일반 인터페이스를 제공하는 가장 좋은 방법은 HTML 자체를 이용해서 우리가 사용 가능할 수 있게 하는 것이다.
이전 단계의 drop과 같이 event를 생성해 볼 것이다. drop된 파일에서 하나의 유효한 이미지를 생성하는 것 같이 이 이벤트는 우리의 목표에 맞게 구체적으로 설정되어있다.
(추가하면 더 많은 작업을 할 수 있다 - 예 : 이미지의 크기를 먼저 조정한 다음 보낼 수 있다.)

이 코드에서 보는 것처럼 _getFile() 이라는 메소드를 작성한다. 이것은 File을 하나의 Image로 Load하며, custom한 Image로 방출한다.

  _gotFile(file) {
    const image = new Image();
    const reader = new FileReader();
    reader.onload = (event) => {
      // when the reader is ready
      image.src = event.target.result;
      image.onload = () => {
        // when the image is ready
        const params = {
          detail: image,
          bubbles: true,
        };
        const ev = new CustomEvent('image', params);
        this.dispatchEvent(ev);
      };
    };
    reader.readAsDataURL(file);
  }

reader가 준비가 된 경우, image객체의 src(경로) 속성에 drop 이벤트를 받은 타겟인 image의 결과값을 저장한다.
그렇게 해서 image가 준비가 잘 되었을 경우에 param이라는 변수를 정의하는데 이때 detail과 bubbles라는 속성을 지정하며.
우리가 image가 잘 들어왔는지 확인을 하기 위해서 CustomEvent라는 객체를 새로 생성한다.
this.dispatchEvent(ev) : 적어도 하나의 이벤트 핸들러가 해당하는 이벤트를 처리하면서 이 메소드를 호출했다면 false를 반환하고, 그렇지 않으면 true를 반환한다.

2-4. 시도하기

개발자 도구를 열고 다음을 붙여 넣는다.

document.querySelector('codelab-dragdrop').addEventListener('image',
    (ev) => console.info('got image', ev.detail));

해당하는 곳에 image가 drop 된다면 console에 해당하는 image에 대한 정보를 보여주는 것이다.
위에서 설정한 CustomEvent가 설정되는 것.

 

3. Connecting Element

3-1. 나의 두번째 Custom Element 만들기

codelab-effects라는 두번째 Custom Element를 만들어 보자. 
이 요소는 이미지를 렌더링하고 흥미로운 시각적 효과를 적용 할 수 있다.

class CodelabEffects extends HTMLElement {
  constructor() {
    super();
    this.root = this.attachShadow({mode: 'open'});
  }
}
customElements.define('codelab-effects', CodelabEffects);

3-2. Shadow Root 생성하기

Shadow DOM을 사용하면 실제로는 페이지에 없는 요소인 custom HTML을 추가하는 것을 허용해준다.
이때 Shadow DOM을 사용하기 위한 method가 attachShadow()인 것이다.

attachShadow()의 모드에 따라서 개발자 도구에서 해당 element의 내부에 있는 html 코드를 볼 수 있는지 없는지 여부를 판단할 수 있다.

실제 개발자 도구에서는 볼 수 없는 Element 이며, 이 것은 일반적으로 document.querySelector() 또는 getElementById()를 이용해서 호출을 할 수 있다.

모든 Custom Element에 Shadow Root가 필요한 것은 아니다.
실제로 <codelab-dragdrop> element는 새롭게 하나를 생성하지 않고도 drop된 파일을 조작하는 복잡한 로직을 수행한다. 그러나 이것은 정말 강력한 API이다.

Shadow DOM 내부의 HTML을 몇개의 코드를 추가해서 템플릿을 정의할 수 있다.

    this.root = this.attachShadow({mode: 'open'});
    this.root.innerHTML = `
<style>
:host {
    background: #fff;
    border: 1px solid black;
    display: inline-block;
}
</style>
<canvas id="canvas" width="512" height="512"></canvas>
<table>
  <tr>
    <td>AMOUNT</td>
    <td><input id="amount" type="range" min="3" max="40" value="10"></td>
  </tr>
</table>
`;

 

마지막으로 이 element를 codelab-dragdrop element 안에 넣는다. 
이때 이전에 있던 빨간색 상자는 제거한다. 
이제 codelab-effect element가 target image를 제공하는 데 도움이 된다.

<!-- elements will go here -->
<codelab-dragdrop>
  <codelab-effects></codelab-effects>
</codelab-dragdrop>

3-3. Putting It Together

이전 단계에 개발자 도구를 이용해서 event를 확인하려고 넣었던 스크립트를 기억하는가?
이 스크립트를 이용해서 이벤트를 연결해 보자!

<!-- elements will go here -->
<codelab-dragdrop id="dragdrop">
  <codelab-effects id="effects"></codelab-effects>
</codelab-dragdrop>
<script>
dragdrop.addEventListener('image', (ev) => {
  effects.image = ev.detail;  // set the image that we got in dragdrop
});
</script>

이제 image 이벤트가 발생하면(즉 image를 drop 했다는 이벤트가 발생하면) codelab-effects 요소에 있는 image 속성을 설정하도록 코드가 장성되어있다. 이제 이렇게 제공한 이미지의 픽셀 데이터를 가져오는 것을 코드에 추가해 보자.

 constructor() {
    super();
    this.root = this.attachShadow({mode: 'open'});
    // Leave the root.innerHTML part alone
  }

  // Add this method
  set image(image) {
    const canvas = this.root.getElementById('canvas');

    // resize image to something reasonable
    canvas.width = Math.min(1024, Math.max(256, image.width));
    canvas.height = (image.height * (canvas.width / image.width));

    // clone buffer to get one of same size
    const buf = canvas.cloneNode(true);
    const ctx = buf.getContext('2d');
    ctx.drawImage(image, 0, 0, buf.width, buf.height);
    this.data = ctx.getImageData(0, 0, buf.width, buf.height).data;
    console.info(this.data);
  }

setter는 클래스 필드에 값을 할당할 때마다 클래스 필드의 값을 조작하는 행위가 필요할 때 사용한다. setter는 메소드 이름 앞에 
set 키워드를 사용해 정의한다. 이때 메소드 이름은 클래스 필드 이름처럼 사용된다.
다시 말해 setter는 호출하는 것이 아니라 프로퍼티처럼 값을 할당하는 형식으로 사용하며 할당 시에 메소드가 호출된다.

이제 이 곳에 이미지를 Drag and Drop 하면 해당하는 이미지의 픽셀 데이터를 console에서 확인 할 수 있다.

3-4. 기본 스타일링

console.log()를 이용해서 데이터 숫자값만 보기 보다는 데이터를 가져와서 실제 캔버스로 그리는 작업을 해보자.
set image 메서드 내부에서 만든 이미지 데이터를 보고 캔버스에 그린다.
이 코드랩은 캔버스 사용 및 이미지 데이터 작업에 관한 것이 아니라 웹 구성 요소를 시연하는데 도움이 되며 흥미로운 효과를 기대할 수 있다.

    // Replace console.info with:
    this.draw();
  }

  // And add this method
  draw() {
    const canvas = this.root.getElementById('canvas');
    canvas.width = canvas.width;  // clear canvas
    const context = canvas.getContext('2d');

    const amount = +this.root.getElementById('amount').value;
    const size = amount * .8;

    for (let y = amount; y < canvas.height; y += amount * 2) {
      for (let x = amount; x < canvas.width; x += amount * 2) {
        const index = ((y * canvas.width) + x) * 4;
        const [r,g,b] = this.data.slice(index, index+3);
        const color = `rgb(${r},${g},${b})`;

        context.beginPath();
        context.arc(x, y, size, 0, 360, false);
        context.fillStyle = color;
        context.fill();
      }
    }
  }

페이지를 새로 고침하고 좋아하는 이미지를 페이지로 드래그하면 점묘 효과가 나타난다.

4. Saving Images

4-1. Click To Download

방금 만든 점묘화의 이미지를 공유하거나 사용하기 위해서는 마우스 오른쪽 버튼을 클릭해서 이미지를 다운로드 해야한다.
대신 canvas 아래에 링크를 추가하여 이미지를 자동으로 다운로드를 할 수 있도록 환경을 만들어 볼 예정이다.

    this.root.innerHTML = `
...
<canvas id="canvas" width="512" height="512"></canvas>
<br /><a href="#" id="link">Download</a>
...
`;

그리고 이 link라는 id값을 받아서 download 할 수 있도록 이벤트 핸들러를 설정한다.

    // And add this handler
    const link = this.root.getElementById('link');
    link.addEventListener('click', (ev) => {
      link.href = this.root.getElementById('canvas').toDataURL();
      link.download = 'pointify.png';
    });

다운로드 완료!!

 

5. Control 추가

5-1. 응답하기

"AMOUNT" 슬라이더를 활용해보자! 이 것을 활용해서 우리가 그리는 점묘화의 점의 크기를 제어할 수 있다. 그러나 현재는 이미지 자체가 드롭될 때 한번만 발생하기 때문에 코드를 수정해보자

CodeLabEffects 클래스의 생성자 안에 리스너를 추가해 보자.
다시 리마인드 하면 이것은 this.root는 AMOUNT 슬라이더를 포함한 모든 Shadow DOM이 있는 곳이다.
이것은 Shadow DOM element에 의해 생긴 모든 변경에 응답하고 draw 메소드를 호출한다.

...
      link.download = 'pointify.png';
    });

    //add these two new listeners
    this.root.addEventListener('input', (ev) => this.draw());
    this.root.addEventListener('change', (ev) => this.draw());

  }

input이나 change와 관련한 이벤트가 들어왔을 경우에 draw()를 다시 실행한다.

5-2. 고급 컨트롤

shadow DOM에 컨트롤을 추가한다.

    this.root.innerHTML = `
... <!-- add some new <tr>'s at the bottom -->
  <tr>
    <td>SIZE</td>
    <td><input id="size" type="range" min="0" max="4" step="0.01" value="1"></td>
  </tr>
  <tr>
    <td>OPACITY</td>
    <td><input id="opacity" type="range" min="0" max="1" step="0.01" value="1"></td>
  </tr>
  <tr>
    <td>ATTENUATION</td>
    <td><input id="attenuation" type="checkbox"></td>
  </tr>

</table>
`;

렌더링 코드인 draw() 역시 컨트롤에 따라 조금 수정해준다.

draw() {
    const canvas = this.root.getElementById('canvas');
    canvas.width = canvas.width;  // clear canvas
    const context = canvas.getContext('2d');

    const attenuation = this.root.getElementById('attenuation').checked;
    const amount = +this.root.getElementById('amount').value;
    const size = this.root.getElementById('size').value * amount;
    const opacity = this.root.getElementById('opacity').value;

    for (let y = amount; y < canvas.height; y += amount * 2) {
      for (let x = amount; x < canvas.width; x += amount * 2) {
        const index = ((y * canvas.width) + x) * 4;
        const [r,g,b] = this.data.slice(index, index+3);
        const color = `rgba(${r},${g},${b},${opacity})`;

        const weight = 1 - ( this.data[ index ] / 255 );
        const radius = (attenuation ? size * weight : size);

        context.beginPath();
        context.arc(x, y, radius, 0, 360, false);
        context.fillStyle = color;
        context.fill();
      }
    }
  }

size : 원의 기본 반경을 제어한다
opacity : 원의 투명도를 제어한다.
attenuation : 각 원의 어두운 정도에 따라 원의 크기를 조정한다.

5-3. 추가 기능

이미지 전체를 색조로 만드는 컬러 필터
다른 모양을 사용
정렬되지 않은 배치 등

이 component element 에는 많은 가능성이 있다!

Polymer와 같은 다양한 Web Component Element 라이브러리도 있다.
이 라이브러리에는 지금 렌더링 수준의 그림 하나하나 생각했던 것과 같은 Low Level의 Component Element보다 좀더 High Level 계층의 추상화된 라이브러리를 제공한다.

This is a summary I put together in Korean to prepare for a codelab seminar.

https://codelabs.developers.google.com/codelabs/image-styling-web-components/#0

 

Image Styling with Web Components

Your Second Custom Element Let's now create a second custom element, codelab-effects. This element will render our image and possibly apply interesting visual effects to it. To start with, this is pretty much the same as the last element—with one extra det

codelabs.developers.google.com

0. Introduction

What is a Web Component?

A new technology that lets you create reusable elements for HTML pages.
They have custom user-defined names: you can bundle the tags you want and encapsulate them.

This is the process of creating a WebComponent using Custom Elements (codelab-dragdrop) and shadow DOM (codelab-effects). By combining these, we'll build a website that can manipulate images dragged onto the page.

  • How to declare Custom Elements
  • How to add listeners and handlers to a Component
  • How to create a Shadow Root to encapsulate Custom Design
  • How to compose multiple components to build a small application

GitHub repository: https://github.com/googlecodelabs/image-styling-web-components

 

googlecodelabs/image-styling-web-components

Image Styling with Web Components. Contribute to googlecodelabs/image-styling-web-components development by creating an account on GitHub.

github.com

1. Creating Custom Elements

1-1. Setting Up the Basic HTML Structure

<!DOCTYPE html>
<html>
<head>
<script>
/* code will go here */
</script>
</head>
<body>

<h1>Image Styling with Web Components</h1>

<!-- elements will go here -->

</body>
</html>

1-2. Creating My First Custom Element

Let's write the code for dragging and dropping an image onto this page.
<codelab-dragdrop></codelab-dragdrop> — we're going to create this tag, and it will indicate the area where files can be dropped.

The logic for this will be implemented using Javascript.
1. Define a new element
2. Use the element (instantiate it)

<codelab-dragdrop></codelab-dragdrop>

1-3. Defining the Element

A Custom Element is an ES6 class that extends HTMLElement.

ES6 stands for ECMAScript 6.
ECMAScript 6 is a JavaScript standard established by ECMA, the JavaScript standards body.

JavaScript is a prototype-based object-oriented language. Prototype-based programming is a class-free object-oriented programming style that can implement concepts like inheritance and encapsulation (information hiding) through prototype chains and closures.

ES6 classes provide a cleaner, simpler syntax that makes it easier for programmers familiar with class-based languages to learn quickly, compared to the traditional prototype-based object-oriented programming. That said, ES6 classes don't replace the existing prototype-based object-oriented model with a new one. 
[Source] https://poiemaweb.com/es6-class

We define our Custom Element using Javascript syntax inside the <script> tag.

/* code will go here */
class CodelabDragdrop extends HTMLElement {
  constructor() {
    super();
  }

  connectedCallback() {
    // we'll do stuff here later
    console.info('Element connected!');
  }
}
customElements.define('codelab-dragdrop', CodelabDragdrop);

connectedCallback(): This is called when the custom element is first inserted into the document's DOM.
customElements.define(DOMString, class, { extends: '[tag-name]' })
- DOMString: The name you want to give the element (i.e., the tag name). Note that custom element names must include a dash ('-'), so keep that in mind!
- class: An object that defines the element's behavior.
- extends (optional): You can specify a tag to inherit from. For example, if you set { extends: 'p'}, the resulting object seems to take on the inline properties of a p tag.

If you check using the developer tools (option+command+i / F12), you can confirm the connection completed message.

 

2. Drag and Drop

2-1. Creating the Target

<!-- elements will go here -->
<codelab-dragdrop>
  <div style="width: 200px; height: 200px; background: red;">
  </div>
</codelab-dragdrop>

When you refresh the page, a big red box appears. 
More importantly, if you inspect the page with developer tools, you can see that the red square is contained inside codelab-dragdrop, so it currently has a size of 200 x 200 pixels.

2-2. Adding Handlers

Let's extend codelab-dragdrop so that files can be dragged and dropped onto it.

One of the features of Web Components is encapsulation.
The way to achieve this is by adding code inside the element definition.
We'll update the constructor of the ES6 class we wrote earlier by adding listeners to the element itself.

 constructor() {
    super();  // you always need super

    this.addEventListener('dragover', (ev) => {
      ev.preventDefault();
    });
    this.addEventListener('drop', (ev) => {
      ev.preventDefault();
      const file = ev.dataTransfer.files[0] || null;
      file && this._gotFile(file);
    });
  }

super() is always required — to inherit from HTMLElement, the class related to Web Elements, and to fully use its features.

ev.preventDefault(): If the event is cancelable, it cancels the event without stopping its propagation.
It feels like it clears out any previously set event behavior.
https://developer.mozilla.org/ko/docs/Web/API/Event/preventDefault

https://developer.mozilla.org/samples/domref/dispatchEvent.html

Here we handle two events related to drag and drop. 
The important one here is the drop handler. It takes the first file (if there is one) from the drag and  calls a method called _getFile().

2-3. Emitting Events

The goal of this codelab is to build something that can manipulate images (a manipulator). So our objective is to create an image and pass it to the corresponding manipulator.

The best way to provide a general interface for this is to make it available to us through HTML itself.
Just like the drop event from the previous step, we're going to create an event. Like generating a single valid image from a dropped file, this event is specifically tailored to our goal.
(You can do more if you add to it — for example, you could resize the image first before sending it.)

As you can see in this code, we write a method called _getFile(). It loads a File as an Image and emits it as a custom Image event.

  _gotFile(file) {
    const image = new Image();
    const reader = new FileReader();
    reader.onload = (event) => {
      // when the reader is ready
      image.src = event.target.result;
      image.onload = () => {
        // when the image is ready
        const params = {
          detail: image,
          bubbles: true,
        };
        const ev = new CustomEvent('image', params);
        this.dispatchEvent(ev);
      };
    };
    reader.readAsDataURL(file);
  }

When the reader is ready, the result of the drop event target image is stored in the image object's src (path) property.
Once the image is successfully loaded, we define a variable called params with detail and bubbles properties.
To verify that the image was received correctly, we create a new CustomEvent object.
this.dispatchEvent(ev): Returns false if at least one event handler that handled the event called this method, and true otherwise.

2-4. Trying It Out

Open the developer tools and paste the following:

document.querySelector('codelab-dragdrop').addEventListener('image',
    (ev) => console.info('got image', ev.detail));

If an image is dropped onto the designated area, it shows the image information in the console.
This is where the CustomEvent we set up earlier kicks in.

 

3. Connecting Element

3-1. Creating My Second Custom Element

Let's create a second Custom Element called codelab-effects. 
This element will render the image and can apply interesting visual effects to it.

class CodelabEffects extends HTMLElement {
  constructor() {
    super();
    this.root = this.attachShadow({mode: 'open'});
  }
}
customElements.define('codelab-effects', CodelabEffects);

3-2. Creating a Shadow Root

Shadow DOM allows you to add custom HTML that isn't actually part of the page's main DOM.
The method used to enable Shadow DOM is attachShadow().

Depending on the mode of attachShadow(), you can control whether the HTML code inside the element is visible in the developer tools or not.

It's an element that isn't visible in the actual developer tools, and it can typically be accessed using document.querySelector() or getElementById().

Not every Custom Element needs a Shadow Root.
In fact, the <codelab-dragdrop> element performs complex logic for manipulating dropped files without creating a new one. But it's a really powerful API.

You can define a template by adding a few lines of code for the HTML inside the Shadow DOM.

    this.root = this.attachShadow({mode: 'open'});
    this.root.innerHTML = `
<style>
:host {
    background: #fff;
    border: 1px solid black;
    display: inline-block;
}
</style>
<canvas id="canvas" width="512" height="512"></canvas>
<table>
  <tr>
    <td>AMOUNT</td>
    <td><input id="amount" type="range" min="3" max="40" value="10"></td>
  </tr>
</table>
`;

 

Finally, we place this element inside the codelab-dragdrop element. 
Remove the red box from before. 
Now the codelab-effects element helps provide the target image.

<!-- elements will go here -->
<codelab-dragdrop>
  <codelab-effects></codelab-effects>
</codelab-dragdrop>

3-3. Putting It Together

Remember the script we pasted in the developer tools to check the event in the previous step?
Let's use this script to connect the events!

<!-- elements will go here -->
<codelab-dragdrop id="dragdrop">
  <codelab-effects id="effects"></codelab-effects>
</codelab-dragdrop>
<script>
dragdrop.addEventListener('image', (ev) => {
  effects.image = ev.detail;  // set the image that we got in dragdrop
});
</script>

Now the code is set up so that when an image event fires (i.e., when an image is dropped), it sets the image property on the codelab-effects element. Let's now add code to grab the pixel data from the provided image.

 constructor() {
    super();
    this.root = this.attachShadow({mode: 'open'});
    // Leave the root.innerHTML part alone
  }

  // Add this method
  set image(image) {
    const canvas = this.root.getElementById('canvas');

    // resize image to something reasonable
    canvas.width = Math.min(1024, Math.max(256, image.width));
    canvas.height = (image.height * (canvas.width / image.width));

    // clone buffer to get one of same size
    const buf = canvas.cloneNode(true);
    const ctx = buf.getContext('2d');
    ctx.drawImage(image, 0, 0, buf.width, buf.height);
    this.data = ctx.getImageData(0, 0, buf.width, buf.height).data;
    console.info(this.data);
  }

A setter is used when you need to manipulate the value of a class field every time a value is assigned to it. A setter is defined by placing the 
set keyword before the method name. The method name is then used as if it were a class field name.
In other words, a setter isn't called like a regular method — it's used in the format of assigning a value like a property, and the method is invoked upon assignment.

Now if you drag and drop an image here, you can see the pixel data of that image in the console.

3-4. Basic Styling

Rather than just looking at data numbers via console.log(), let's actually grab the data and draw it on the canvas.
We take the image data created inside the set image method and draw it on the canvas.
This codelab isn't about working with canvas or image data — it's about demonstrating Web Components, and you can look forward to some interesting effects.

    // Replace console.info with:
    this.draw();
  }

  // And add this method
  draw() {
    const canvas = this.root.getElementById('canvas');
    canvas.width = canvas.width;  // clear canvas
    const context = canvas.getContext('2d');

    const amount = +this.root.getElementById('amount').value;
    const size = amount * .8;

    for (let y = amount; y < canvas.height; y += amount * 2) {
      for (let x = amount; x < canvas.width; x += amount * 2) {
        const index = ((y * canvas.width) + x) * 4;
        const [r,g,b] = this.data.slice(index, index+3);
        const color = `rgb(${r},${g},${b})`;

        context.beginPath();
        context.arc(x, y, size, 0, 360, false);
        context.fillStyle = color;
        context.fill();
      }
    }
  }

Refresh the page and drag your favorite image onto it — you'll see a pointillism effect appear.

4. Saving Images

4-1. Click To Download

To share or use the pointillism image we just created, you'd have to right-click and download the image.
Instead, we're going to add a link below the canvas so that the image can be downloaded automatically.

    this.root.innerHTML = `
...
<canvas id="canvas" width="512" height="512"></canvas>
<br /><a href="#" id="link">Download</a>
...
`;

Then we set up an event handler using the link id to enable the download.

    // And add this handler
    const link = this.root.getElementById('link');
    link.addEventListener('click', (ev) => {
      link.href = this.root.getElementById('canvas').toDataURL();
      link.download = 'pointify.png';
    });

Download complete!!

 

5. Adding Controls

5-1. Responding to Input

Let's make use of the "AMOUNT" slider! We can use it to control the size of the dots in our pointillism image. But right now, the drawing only happens once when the image is dropped, so let's fix the code.

Let's add a listener inside the constructor of the CodelabEffects class.
As a reminder, this.root is where all the Shadow DOM lives, including the AMOUNT slider.
This will respond to any changes made by Shadow DOM elements and call the draw method.

...
      link.download = 'pointify.png';
    });

    //add these two new listeners
    this.root.addEventListener('input', (ev) => this.draw());
    this.root.addEventListener('change', (ev) => this.draw());

  }

When an input or change event comes in, it re-executes draw().

5-2. Advanced Controls

Let's add controls to the shadow DOM.

    this.root.innerHTML = `
... <!-- add some new <tr>'s at the bottom -->
  <tr>
    <td>SIZE</td>
    <td><input id="size" type="range" min="0" max="4" step="0.01" value="1"></td>
  </tr>
  <tr>
    <td>OPACITY</td>
    <td><input id="opacity" type="range" min="0" max="1" step="0.01" value="1"></td>
  </tr>
  <tr>
    <td>ATTENUATION</td>
    <td><input id="attenuation" type="checkbox"></td>
  </tr>

</table>
`;

The rendering code, draw(), also needs a slight update to accommodate the controls.

draw() {
    const canvas = this.root.getElementById('canvas');
    canvas.width = canvas.width;  // clear canvas
    const context = canvas.getContext('2d');

    const attenuation = this.root.getElementById('attenuation').checked;
    const amount = +this.root.getElementById('amount').value;
    const size = this.root.getElementById('size').value * amount;
    const opacity = this.root.getElementById('opacity').value;

    for (let y = amount; y < canvas.height; y += amount * 2) {
      for (let x = amount; x < canvas.width; x += amount * 2) {
        const index = ((y * canvas.width) + x) * 4;
        const [r,g,b] = this.data.slice(index, index+3);
        const color = `rgba(${r},${g},${b},${opacity})`;

        const weight = 1 - ( this.data[ index ] / 255 );
        const radius = (attenuation ? size * weight : size);

        context.beginPath();
        context.arc(x, y, radius, 0, 360, false);
        context.fillStyle = color;
        context.fill();
      }
    }
  }

size: Controls the base radius of the circles.
opacity: Controls the transparency of the circles.
attenuation: Adjusts the size of each circle based on how dark it is.

5-3. Additional Features

A color filter that tints the entire image
Using different shapes
Randomized placement, etc.

There are so many possibilities with this component element!

There are also various Web Component element libraries like Polymer.
These libraries provide higher-level abstractions rather than the low-level component elements we were working with here, where we had to think about each individual rendering detail.

'Develop > Web' 카테고리의 다른 글

Thrift 뽀개기 | Cracking Thrift  (0) 2023.02.23
web & server - DSC Ewha 세션 | web & server - DSC Ewha Session  (0) 2019.10.15

댓글

Comments

Develop/Springboot

springboot Junit5 + assertJ TestCode | springboot Junit5 + assertJ TestCode

0. build.gradletest { useJUnitPlatform()}dependencies { testImplementation 'org.springframework.boot:spring-boot-starter-test' testCompile("org.assertj:assertj-core:3.11.1")}1. assertAllUser user = new User();assertAll( () -> asssertThat(user.getId()).isEqualTo(1L), //1 () -> asssertThat(user.getName()).isEqualTo("jyami"), //2 () -> asssertThat(user.getEmail()).isEqualTo(..

springboot Junit5 + assertJ TestCode | springboot Junit5 + assertJ TestCode

728x90

0. build.gradle

test {
    useJUnitPlatform()
}

dependencies {
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testCompile("org.assertj:assertj-core:3.11.1")
}

1. assertAll

User user = new User();
assertAll(
    () -> asssertThat(user.getId()).isEqualTo(1L),    //1
    () -> asssertThat(user.getName()).isEqualTo("jyami"),    //2
    () -> asssertThat(user.getEmail()).isEqualTo("mor222293@gmail.com")    //3
);

이전의 Junit4에서는
assertEquals을 검증할 때, 위에부터 하나씩 실행하는데, 위에서 실패하면 아래에있는 assertEquals를 실행하지 않는다.즉, 주석의 1번의 assertEquals에서 실패했으면 2, 3번의 assertEquals는 실행하지 않는다.
그러나 assertAll을 사용하면, 1번에서 실패했어도 2번 3번도 실행한다.

2. @DisplayName

@DisplayName("유저 테스트")
public class UserTest {

    @DisplayName("유저의 이름을 테스트 해보자!")
    @Test
    void someTest() {
        User user = new User();
        assertThat(user.getName()).isEqualTo("jyami");
    }

이렇게 할 경우에는, DisplayName을 이용해서 test의 목적을 명확히 명시할 수 있다.

3. assertThrows

@Test
void checkThrow() {
	Assertions.assertThrows(
		Exception.class, () -> {int a = 10/0;}
	);
}

익셉션을 처리할 때 junit5의 assertThrow 안에 throw 체크를 하려고하는 로직을 담으면 된다.

 


AssertJ

1. contains()

@Test
void containsTest() {
	List<Integer> integers = Arrays.asList(1, 2, 3);
	assertThat(integers).contains(1, 2, 3);	//테스트 통과
	assertThat(integers).contains(2, 1, 3);	//테스트 통과
}

contains는 순서와 상관 없이 실제 그룹이 주어진 값들을 포함하고 있는지를 테스트한다.
그래서 위 두 줄의 테스트는 모두 통과한다.

2. containsExactly()

@Test
void containsExactlyTest() {
	List<Integer> integers = Arrays.asList(1, 2, 3);
	assertThat(integers).containsExactly(1, 2, 3);	//테스트 통과
	assertThat(integers).containsExactly(2, 1, 3);	//테스트 통과 X
	assertThat(integers).containsExactly(1, 2);	//테스트 통과 X
}

contains는 순서까지 고려해서 실제 그룹이 주어진 값들을 포함하고 있는지를 테스트한다
그래서 첫번째 줄의 테스트는 통과하지만, 두번째 줄의 테스트는 통과하지 못한다.

이때 주의할 점은 원소 하나라도 빠지면 테스트를 통과하지 못한다. 정말로 정확하게 일치하는 list여야 하는 것!

 


업데이트 예정인 게시글 입니다.

0. build.gradle

test {
    useJUnitPlatform()
}

dependencies {
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testCompile("org.assertj:assertj-core:3.11.1")
}

1. assertAll

User user = new User();
assertAll(
    () -> asssertThat(user.getId()).isEqualTo(1L),    //1
    () -> asssertThat(user.getName()).isEqualTo("jyami"),    //2
    () -> asssertThat(user.getEmail()).isEqualTo("mor222293@gmail.com")    //3
);

In the previous JUnit4,
when verifying with assertEquals, it executes them one by one from the top, and if one fails, it doesn't execute the ones below it. In other words, if assertEquals #1 in the comments fails, assertEquals #2 and #3 won't run.
However, if you use assertAll, even if #1 fails, #2 and #3 still get executed.

2. @DisplayName

@DisplayName("유저 테스트")
public class UserTest {

    @DisplayName("유저의 이름을 테스트 해보자!")
    @Test
    void someTest() {
        User user = new User();
        assertThat(user.getName()).isEqualTo("jyami");
    }

By doing this, you can use DisplayName to clearly state the purpose of each test.

3. assertThrows

@Test
void checkThrow() {
	Assertions.assertThrows(
		Exception.class, () -> {int a = 10/0;}
	);
}

When handling exceptions, you just need to put the logic you want to check for throws inside JUnit5's assertThrows.

 


AssertJ

1. contains()

@Test
void containsTest() {
	List<Integer> integers = Arrays.asList(1, 2, 3);
	assertThat(integers).contains(1, 2, 3);	//테스트 통과
	assertThat(integers).contains(2, 1, 3);	//테스트 통과
}

contains tests whether the actual group contains the given values regardless of order.
So both lines of tests above will pass.

2. containsExactly()

@Test
void containsExactlyTest() {
	List<Integer> integers = Arrays.asList(1, 2, 3);
	assertThat(integers).containsExactly(1, 2, 3);	//테스트 통과
	assertThat(integers).containsExactly(2, 1, 3);	//테스트 통과 X
	assertThat(integers).containsExactly(1, 2);	//테스트 통과 X
}

containsExactly tests whether the actual group contains the given values while also considering the order.
So the first line's test passes, but the second line's test does not.

One thing to note here is that if even a single element is missing, the test will fail. The list really has to match exactly!

 


This is a post that will be updated in the future.

댓글

Comments

Develop/DevOps

[Cloud OnBoard] 4 - 빅데이터 및 머신러닝 | [Cloud OnBoard] 4 - Big Data and Machine Learning

2019년 11월 26일 세종대학교에서 있었던 Google Cloud OnBoard에서 나누어준 자료집의 정리본입니다모듈4 빅데이터 및 머신러닝0. 추가자료Google 빅데이터 플랫폼 : https://cloud.google.com/products/big-dataGoogle AI Platform : https://cloud.google.com/products/ai1. Google Cloud 빅데이터 플랫폼1-1.Google Cloud의 빅데이터 서비스확장가능한 완전 관리형 서비스Cloud Dataproc : 관리형 Hadoop 맵리듀스, Spark, Pig, Hive 서비스Cloud Dataflow : 스트리밍 및 일괄 처리, 파이프라인 통합 및 관소화BigQuery : 분석 데이터베이스, 데이터 스트리..

[Cloud OnBoard] 4 - 빅데이터 및 머신러닝 | [Cloud OnBoard] 4 - Big Data and Machine Learning

728x90

2019년 11월 26일 세종대학교에서 있었던 Google Cloud OnBoard에서 나누어준 자료집의 정리본입니다


모듈4 빅데이터 및 머신러닝

0. 추가자료

Google 빅데이터 플랫폼 : https://cloud.google.com/products/big-data

Google AI Platform : https://cloud.google.com/products/ai

1. Google Cloud 빅데이터 플랫폼

1-1.Google Cloud의 빅데이터 서비스

  • 확장가능한 완전 관리형 서비스
  1. Cloud Dataproc : 관리형 Hadoop 맵리듀스, Spark, Pig, Hive 서비스
  2. Cloud Dataflow : 스트리밍 및 일괄 처리, 파이프라인 통합 및 관소화
  3. BigQuery : 분석 데이터베이스, 데이터 스트리밍 속도 초당 100,000원
  4. Cloud Pub/Sub : 확장 가능하고 유연한 엔터프라이즈 메시징
  5. Cloud Datalab : 대화형 데이터 탐색

1-2. Cloud Dataproc

  • 특징
    • 관리형 Hadoop
    • GCP Hadoop 및 Spark/Hive/Pig를 관리형으로 빠르고 쉽게 실행하는 방법
    • 클러스터 생성 시간 평균 90초 이하
    • 작업 실행 중에도 클러스터 규모 확장 및 축소
  • 사용해야하는 이유
    • 온프레미스 Hadoop 작업을 클라우드로 손쉽게 마이그레이션 합니다.
    • Cloud Storage에 저장된 로그 등의 데이터를 빠르게 분석하고, 평균 90초 이내에 클러스터를 생성하고, 즉시 삭제 합니다.
    • Spark/Spark SQL을 사용하여 데이터 마이닝 및 분석을 빠르게 수행합니다.
    • Spark 머신러닝 라이브러리(MLlib)를 사용하여 분류 알고리즘을 실행합니다.

1-3. Cloud Dataflow

  • 특징
    • 관리형 데이터 파이프라인
    • Compute Engine 인스턴스를 사용하여 데이터 처리
      • 클러스터 크기 자동 조절
      • 자동화된 확장, 인스턴스 프로비저닝이 필요하지 않음
    • 코드를 한 번만 작성하여 일괄 처리 및 스트리밍
      • 변환 기반 프로그래밍 모델
    • Dataflow 파이프라인으로 변환을 통해 소스의 데이터가 이동
  • 사용해야하는 이유
    • ETL (추출/변환/로드) 파이프라인으로 데이터 이동, 필터링, 다변화 및 형성
    • 데이터 분석: 일괄 연선 또는 스트리밍을 사용한 연속 연산
    • 오케스트레이션 : 외부 서비스를 포함하여 여러 서비스를 조율하는 파이프라인 작성
    • Cloud Storage, Cloud Pub/Sub, BigQuery, Bigtable 등의 GCP 서비스와 통합
      • 오픈 소스 Java 및 Python SDK

1-4. BigQuery

  • 완전 관리형 데이터 웨어하우스
    • 방대한 데이터세트 (수백 TB)에 대한 실시간에 가까운 대화형 분석 제공
    • SQL 구문(SQL 2011)을 사용하는 쿼리
    • 클러스터 유지보수가 필요하지 않음
  • Google의 고성능 인프라에서 실행
    • 컴퓨팅과 스토리지가 테라비트급 네트워크로 분리됨
    • 사용된 스토리지 및 처리에 대해서만 지불
    • 장기 데이터 스토리지 자동할인

1-5. Cloud Pub/Sub

  • 특징
    • 확장가능하고 안정적인 메시징
    • 다대다 비동기 메시징 지원 : 애플리케이션 구성요소에서 주제에 대한 push/pull 구독 작성
    • 오프라인 소비자 지원 포함
    • 검증된 Google 기술 활용
  • 사용해야하는 이유
    • Dataflow, 사물인터넷(IoT), 마케팅 분석의 데이터 수집을 위한 구성 요소
    • Dataflow 스트리밍의 기반
    • 클라우드 기반 애플리케이션의 푸시 알림
    • Google Cloud Platform에 속하는 여러 애플리케이션 연결(Compute Engine과 App Engine 사이에 push/pull)

1-6. Cloud Datalab

  • 특징
    • 대화형 데이터 탐색 제공
    • 대규모 데이터 탐색, 변환, 분석, 시각화를 위한 대화형 도구
    • 통합, 오픈 소스 : Jupyter(Ipython)를 기반으로함
  • 사용해야하는 이유
    • 코드, 문서, 결과, 시각화를 직관적인 메모장 형식으로 생성 및 관리
      손쉬운 시각화를 위해 Google Charts 또는 matplotlib 사용
    • Python SQL, 자바스크립트를 사용하여 BigQuery, ComputeEngine, Cloud Storage에서 데이터를 분석
    • BigQuery에 모델을 손쉽게 배포

2. Google Cloud AI Platform

2-1. Cloud AI Platform

  • 신경망 모델을 빌드 및 실행하는 오픈 소스 도구
    • 폭넓은 플랫폼 지원 : CPU 또는 GPU, 모바일, 서버, 클라우드
  • 완전 관리형 머신러닝 서비스
    • 익숙한 메모장 기반 개발자 환경
    • Google 인프라에 최적화, BigQuery 및 Cloud Storage와 통합
  • Google에서 빌드한 선행 학습된 머신러닝 모델
    • 음성: 결과를 실시간으로 스트리밍 하고, 80개의 언어를 이해함
    • 비전: 객체, 랜드마크, 텍스트, 콘텐츠 식별
    • 번역: 언어 감지 및 번역
    • 자연어: 텍스트의 의미, 구조
  • 구조화된 데이터 : 분류 및 회귀 / 추천 / 이상감지
  • 구조화되지 않은 데이터 : 이미지 및 동영상 분석 / 텍스트 분석

2-2. Cloud Vision API

  • 단순한 REST API로 이미지 분석
    • 로고 감지, 라벨 인식 등
  • Cloud Vision API에서 제공하는 기능
    • 이미지에서 유용한 정보 확보
    • 부적절한 콘텐츠 감지
    • 정서 분석
    • 텍스트 추출

2-3. Cloud Speech API

  • 80개 이상 언어 및 변형어 인식
  • 실시간으로 텍스트 반환 가능
  • 소음이 심환 환경에서도 높은 정확도 제공
  • 모든 기기에서 액세스
  • Google 머신러닝 기반

2-4. Cloud Natural Language API

  • 머신러닝 모델을 사용하여 텍스트의 구조와 의미를 파악합니다
  • 텍스문서, 뉴스기사, 블로그 글에서 언급된 사안에 관한 정보를 추출합니다.
  • 요청시 업로드된 텍스트를 분석하거나 Cloud Storage와 통합합니다.

2-5. Cloud Translation API

  • 수많은 언어 쌍 사이에서 임의 문자열 번역
  • 문서의 언어를 프로그래매틱 방식으로 감지
  • 수십 개의 언어 지원

2-6. Cloud Video Intelligence API

  • 동영상 콘텐츠에 특수효과 적용
  • 장면 변화 감지
  • 부적절한 콘텐츠 신고
  • 다양한 동영상 형식 지원

This is a summary of the materials distributed at the Google Cloud OnBoard event held at Sejong University on November 26, 2019.


Module 4: Big Data and Machine Learning

0. Additional Resources

Google Big Data Platform: https://cloud.google.com/products/big-data

Google AI Platform: https://cloud.google.com/products/ai

1. Google Cloud Big Data Platform

1-1. Google Cloud Big Data Services

  • Scalable, fully managed services
  1. Cloud Dataproc: Managed Hadoop MapReduce, Spark, Pig, Hive service
  2. Cloud Dataflow: Stream and batch processing, unified pipeline integration and simplification
  3. BigQuery: Analytics database, data streaming at 100,000 rows per second
  4. Cloud Pub/Sub: Scalable and flexible enterprise messaging
  5. Cloud Datalab: Interactive data exploration

1-2. Cloud Dataproc

  • Features
    • Managed Hadoop
    • A fast and easy way to run GCP Hadoop and Spark/Hive/Pig as a managed service
    • Average cluster creation time under 90 seconds
    • Scale clusters up and down even while jobs are running
  • Why you should use it
    • Easily migrate on-premises Hadoop workloads to the cloud.
    • Quickly analyze data such as logs stored in Cloud Storage, create clusters in under 90 seconds on average, and delete them immediately.
    • Perform data mining and analysis quickly using Spark/Spark SQL.
    • Run classification algorithms using the Spark machine learning library (MLlib).

1-3. Cloud Dataflow

  • Features
    • Managed data pipelines
    • Processes data using Compute Engine instances
      • Automatic cluster size adjustment
      • Automated scaling, no instance provisioning required
    • Write code once for both batch and stream processing
      • Transform-based programming model
    • Data moves from sources through transformations in a Dataflow pipeline
  • Why you should use it
    • Move, filter, enrich, and shape data with ETL (Extract/Transform/Load) pipelines
    • Data analytics: Batch computation or continuous computation using streaming
    • Orchestration: Build pipelines that coordinate multiple services, including external services
    • Integrates with GCP services such as Cloud Storage, Cloud Pub/Sub, BigQuery, and Bigtable
      • Open source Java and Python SDKs

1-4. BigQuery

  • Fully managed data warehouse
    • Provides near real-time interactive analysis on massive datasets (hundreds of TB)
    • Queries using SQL syntax (SQL 2011)
    • No cluster maintenance required
  • Runs on Google's high-performance infrastructure
    • Compute and storage separated by a terabit-class network
    • Pay only for storage used and processing performed
    • Automatic discounts for long-term data storage

1-5. Cloud Pub/Sub

  • Features
    • Scalable and reliable messaging
    • Supports many-to-many asynchronous messaging: Create push/pull subscriptions to topics from application components
    • Includes offline consumer support
    • Leverages proven Google technology
  • Why you should use it
    • A building block for data ingestion in Dataflow, Internet of Things (IoT), and marketing analytics
    • Foundation for Dataflow streaming
    • Push notifications for cloud-based applications
    • Connect multiple applications within Google Cloud Platform (push/pull between Compute Engine and App Engine)

1-6. Cloud Datalab

  • Features
    • Provides interactive data exploration
    • An interactive tool for large-scale data exploration, transformation, analysis, and visualization
    • Integrated, open source: Built on Jupyter (IPython)
  • Why you should use it
    • Create and manage code, documentation, results, and visualizations in an intuitive notebook format
      Use Google Charts or matplotlib for easy visualization
    • Analyze data from BigQuery, Compute Engine, and Cloud Storage using Python, SQL, and JavaScript
    • Easily deploy models to BigQuery

2. Google Cloud AI Platform

2-1. Cloud AI Platform

  • An open source tool for building and running neural network models
    • Broad platform support: CPU or GPU, mobile, server, cloud
  • Fully managed machine learning service
    • Familiar notebook-based developer environment
    • Optimized for Google infrastructure, integrated with BigQuery and Cloud Storage
  • Pre-trained machine learning models built by Google
    • Speech: Streams results in real time and understands 80 languages
    • Vision: Identifies objects, landmarks, text, and content
    • Translation: Language detection and translation
    • Natural Language: Meaning and structure of text
  • Structured data: Classification and regression / Recommendations / Anomaly detection
  • Unstructured data: Image and video analysis / Text analysis

2-2. Cloud Vision API

  • Analyze images with a simple REST API
    • Logo detection, label recognition, and more
  • Features provided by Cloud Vision API
    • Extract useful information from images
    • Detect inappropriate content
    • Sentiment analysis
    • Text extraction

2-3. Cloud Speech API

  • Recognizes over 80 languages and variants
  • Can return text in real time
  • Provides high accuracy even in noisy environments
  • Accessible from any device
  • Powered by Google machine learning

2-4. Cloud Natural Language API

  • Uses machine learning models to understand the structure and meaning of text
  • Extracts information about topics mentioned in text documents, news articles, and blog posts.
  • Analyzes uploaded text on request or integrates with Cloud Storage.

2-5. Cloud Translation API

  • Translates arbitrary strings between numerous language pairs
  • Programmatically detects the language of a document
  • Supports dozens of languages

2-6. Cloud Video Intelligence API

  • Annotate video content
  • Detect scene changes
  • Flag inappropriate content
  • Supports various video formats

댓글

Comments

Develop/DevOps

[Cloud OnBoard] 3 - 컨테이너 및 앱 개발, 배포, 모니터링 | [Cloud OnBoard] 3 - Container and App Development, Deployment, Monitoring

2019년 11월 26일 세종대학교에서 있었던 Google Cloud OnBoard에서 나누어준 자료집의 정리본입니다 모듈3 컨테이너 및 앱 개발, 배포, 모니터링0. 추가자료Kubernetes Engine : https://cloud.google.com/kubernetes-engine/docsKubernetes : https://kubernetes.ioGoogle Cloud Build : https://cloud.google.com/cloud-build/docsGoogle Container Registry : https://cloud.google.com/container-regitry/docsGoogle App Engine : https://cloud.google.com/appengine/docsGoo..

[Cloud OnBoard] 3 - 컨테이너 및 앱 개발, 배포, 모니터링 | [Cloud OnBoard] 3 - Container and App Development, Deployment, Monitoring

728x90

2019년 11월 26일 세종대학교에서 있었던 Google Cloud OnBoard에서 나누어준 자료집의 정리본입니다

 


모듈3 컨테이너 및 앱 개발, 배포, 모니터링

0. 추가자료

Kubernetes Engine : https://cloud.google.com/kubernetes-engine/docs

Kubernetes : https://kubernetes.io

Google Cloud Build : https://cloud.google.com/cloud-build/docs

Google Container Registry : https://cloud.google.com/container-regitry/docs

Google App Engine : https://cloud.google.com/appengine/docs

Google App Engine 가변형 환경 : https://cloud.google.com/appengine/docs/flexible

Google App Engine 표준 환경 : https://cloud.google.com/appengine/docs/standard

Google Cloud Endpoints : https://cloud.google.com/endpoints/docs

Apigee Edge : https://cloud.google.com/api-services/content/what-apigee-edge

Cloud Source Repositories : https://cloud.google.com/source-repositories/docs

Deployment Manager : https://cloud.google.com/deployment-manager/docs

Google Stackdriver : https://cloud.google.com/stackdriver/docs

1. 복습 : IaaS와 PasS

 

IaaS: Infrastructure as a Service - AWS EC2

인프라 스트럭쳐 레벨을 제공하는 서비스이다. 고객이 OS와 어플리케이션을 직접 관리한다.

PaaS : Platform as a Service - heroku

개발자가 어플리케이션을 개발, 서비스하기위해 사용가능한 기능들이 제공되는 클라우드 서비스. 사용자는 어플리케이션과 데이터만 관리

2. 컨테이너 소개

  • IaaS : 하드웨어를 가상화 하고, 리소스를 공유할 수 있다.
  • 하지만 유연성에는 부팅시간(분)과 리소스(GB)가 부과된다.
  • App Engine
    • 프로그래밍 서비스에 대한 액세스를 제공
    • 앱 수요가 늘어날 수록 워크로드 및 인프라에 따라 독립적으로 앱을 신속하게 확장하는 플랫폼

2-1. 컨테이너

  • 컨테이너에서 제공하는 사항
    • IaaS와 PaaS의 확장성을 제공한다.
    • 하드웨어 및 OS의 추상화 레이어
    • 격리된 파티션으로 나눈 파일 시스템, RAM 네트워킹에 대한 구성 가능한 액세스를 제공하는 보이지 않는 상자
    • 빠른 시작
  • 컨테이너의 기능
    • 구성이 가능하며 독립적이고 이식성이 우수하다.
    • 자체 하드웨어, OS, 소프트웨어 스택 구성 정의
    • OS 및 하드웨어를 블랙박스처럼 이용하여 개발에서 스테이징, 프로덕션에 이르기까지 또는 노트북에서 클라우드로 마이그레이션 하는 과정에서 아무것도 변경하거나 다시 빌드할 필요가 없다.

컨테이너는 앱 + 라이브러리 : 컨테이너 인터페이스를 구현한게 OS/하드웨어

2-2. 클러스터

  • 클러스터의 기능
    • 공동의 호스트 구성으로 컨테이너를 서버 그룹에 배포가 가능하다.
    • 네트워크 연결을 사용해서 여러 컨테이너를 연결
    • 모듈식 코드 작성
    • 손쉬운 배포
    • 컨테이너 및 호스트의 독립적인 확장으로 최대 효율과 절약 달성

3. Kubernetes 및 Kubernetes Engine

3-1. Kubernetes

여러 호스트의 많은 컨테이너를 쉽게 조정한다.

 

  1. 앱을 컨테이너로 빌드해 실행해보기
    • Docker : 앱, 종속항목, 시스템 설정을 번들로 묶는다
    • Google Cloud Build 등의 다른 도구도 사용이 가능하다. 코드 예시 : hello world를 표시하는 python flask 앱
[app.py]

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "helloworld!"

if __name__ == "__main__":
    app.run(host='0.0.0.0');

 

  1. 앱을 Kubernetes로 가져오기 - Docker 파일을 사용해 4가지 지정

    • Flask 종속 항목의 requirements.txt 파일
    • Python의 OS 이미지 및 버전
    • Python 설치 방법
    • 앱 실행 방법
[requirement.txt]
Flask==0.12
uwsgi==2.0.15

 

FROM ubuntu:18.10
RUN apt-get update -y && \
    apt-get install -y python3-pip python3-dev
COPY requirements.txt /app/requirements.txt
WORKDIR /app
RUN pip3 install -r requirements.txt
COPY ./app
ENDPOINT ["python3", "app.py"]

 

  1. 컨테이너를 이미지로 빌드해 실행하기

    • docker build로 컨테이너를 빌드하여 로컬에 실행 가능한 이미지로 저장
    • 공유를 위해 레지스트리 서비스 (Google Container Registry 등)에 이미지 업로드 기능
    • docker run으로 컨테이너 이미지를 시작
$> docker build -t py-server .
$> docker run -d py-server

 

  1. Kubernetes API를 사용해 컨테이너를 클러스터라고 부르는 노드 모음에 배포하기

    • 마스터는 제어영역을 실행
    • 노드는 컨테이너를 실행
    • 노드는 VM(GKE에서는 GCE인스턴스로 사용됨)
    • 사용자가 앱을 설명하면 Kubernetes가 구현방법 파악
  2. Kubernetes Engine 부트 스트랩하기
    • GKE 클러스터에서 다음 사항을 지정 가능 > 머신 유형 > 노드수 > 네트워크 설정 등
$> gcloud container clusters create k1
  1. 컨테이너를 노드에 배포할때 Pod라고 부르는 래퍼 사용하기
  1. Kubectl run을 사용해 Pod에서 컨테이너 실행하기

    • Kubectl은 Kubernetes API에 대한 명령줄 클라이언트임
    • 이 명령어로 Pod에서 실행 중인 컨테이너에 배포를 시작
    • 이 경우 컨테이너는 NGINX 서버의 이미지임
$> kubectl run nginx --image=nginx:1.15.7
      1. 배포
        • 앱 또는 워크로드의 복제본 Pod 모음을 관리하고 원하는 수의 Pod가 실행되고 정상상태를 유지하도록 한다
$> kubectl get pods
  1. 기본적으로 클러스터 안에서만 사용되며, 임시 IP를 가져오는 Pod

    • 고정 IP에서 공개적으로 사용할 수 있도록 Kubectl expose를 실행하여 부하 분산기를 배포에 연결가능
    • Kubernetes에서 Pod의 고정 IP를 사용해 서비스를 만들며 컨트롤러에 'I need to attach an external load balancer with a public IP address'라는 메세지가 표시됨
$> kubectl expose deployments nginx --port=80 --type=LoadBalancer
  1. 이 IP에 도달한 클라이언트는 서비스 뒤에 있는 Pod로 라우팅 됨
    • 예를 들어 프런트엔드 및 백엔드라는 이름의 Pod 모음을 2개 만들어 자체 서비스 뒤에 배치할 경우 백엔드 Pod에서 변경이 발생해도 프런트엔드 Pod에서 이를 알지 못한다. 백엔드 서비스를 참조할 뿐
  1. kubectl get services를 실행해 서비스의 공개 IP를 가져오기
$> kubectl get services
NAME     TYPE             CLUSTER-IP     EXTERNAL-IP     PORT(S) AGE
nginx    LoadBalancer    10.0.65.118    104.198.149.140    80/TCP    5m
  1. kubectl scale을 실행해 배포 확장하기
$> kubectl scale nginx ==replicas=3
  1. 각종 매개변수를 사용해 자동확장을 실행하거나 지능적 관리를 위해 프로그래밍 로직 뒤에 자동 확장 배치 가능
$> kubectl autoscale nginx --min=10 --max=15 --cpu=80

 

  1. 선언적 방법을 사용할 때 Kubernetes의 진정한 강점이 발휘
    • 예 : 구성 파일을 가져오는 방법
$> kubectl get pods -l "app=nginx"

 

[nginx-development.yaml]
apiVersion: v1
kind: Deployment
metadata:
    name: nginx
    labels:
        app: nginx
spec:
    replicas: 3
    selector:
        matchLabels:
            app: nginx
        template:
            metadata:
                labels:
                    app: nginx
            spec:
                containers:
                    - name: nginx
                      image: nginx:1.15.7
                      ports:
                          - continerPort: 80
  1. kubectl apply -f 를 실행해 변경사항을 선언적으로 적용하기
$> kubectl apply -f nginx-deployment.yaml

 

  1. kubectl get replicasets를 실행해 업데이트 상태 확인하기
$> kubectl get replicasets

 

NAME                DESIRED    CURRENT    READY    AGE
nginx-2035384211    5        3        3        2s

 

  1. kubectl get pods를 실행해 Pod가 온라인으로 전환되는 것 확인하기
$> kubectl get pods

 

NAME                    READY    STATUS    RESTARTS    AGE
nginx-203584211-7ci7o    1/1        Running    0            18s
nginx-203584211-he3h3    1/1        Running    0            18s
nginx-203584211-qqcnn    1/1        Running    0            18s
nginx-203584211-abbcc    1/1        Running    0            18s
nginx-203584211-knlen    1/1        Running    0            18s

 

  1. kubectl get deployments 실행으로 배포를 설명해 적절한 수의 복제본 실행하기
$> kubectl get deployments

 

NAME    DESIRED    CURRENT    UP-TO-DATE    AVAILABLE    AGE
nginx    5        5        5            5            18s

 

  1. 컨테이너를 빌드하고 이미지 실행하기
$> kubectl get services

 

NAME     TYPE             CLUSTER-IP     EXTERNAL-IP     PORT(S) AGE
nginx    LoadBalancer    10.0.65.118    104.198.149.140    80/TCP    5m

 

4. Google App Engine

  • 확장 가능한애플리케이션을 빌드할 수 있는 PaaS
  • App Engine으로 배포 유지보수, 확장이 쉬워지므로 혁신에만 집중할 수 있음
  • 확장가능한 웹 애플리케이션 및 모바일 백엔드를 빌드하는데 특히 적합함

5. Google App Engine 표준환경

  • 손쉬운 애플리케이션 배포
  • 수요에 대응하여 워크로드 자동 확장
  • 경제성
    • 무료 일일 할당량 / 사용량 기준 가격 책정
  • 개발, 테스트, 배포용 SDK
  • 특정 버전의 자바, Python, PHP, Go가 지원됨
  • 애플리케이션이 샌드박스 제약을 준수해야함
    • 로컬 파일 시스템에 쓰기 금지
    • 모든 요청에 타임아웃 60초가 적용됨
    • 타사 소프트웨어 설치가 제한됨

예시 웹 애플리케이션

6. Google App Engine 가변형 환경

  • 클릭 한번으로 컨테이너형 앱 빌드 및 배포
  • 샌드박스 제약 없음
  • App Engine 리소스에 액세스 가능
  • 표준 런타임 : Python, 자바, Go, Node.js
  • 커스텀 런타임 지원: HTTP요청을 지원하는 모든 언어
  • 런타임을 Dockerfile로 패키지화

App Engine 환경 비교

7. Google Cloud Endpoints 및 ApiGee Edge

  • 애플리케이션 프로그래밍 인터페이스로 세부정보를 숨기고 계약을 시행 [그림]

7-1. Cloud Endpoints

  • API의 생성 및 유지보수를 지원
  • API 콘솔을 통해 분산된 API 관리
  • RESTful 인터페이스를 사용하여 API 노출
  • JSON 웹 토큰 및 Google API 키를 사용하여 액세스 제어 및 호출 유효성 검사
    -> Auth0 및 Firebase 인증을 통해 웹, 모바일 사용자 신원 확인
  • 클라이언트 라이브러리 생성
  • 지원되는 플랫폼 [그림]

7-2. Apigee Edge

  • API의 보안과 수익 창출을 지원
  • 곡객과 파트너가 API를 사용할 수 있는 플랫폼
  • 분석, 수익 창출, 개발자 포털 제공

8. 클라우드에서 개발, 배포 모니터링

8-1. Cloud Source Repositories

  • Google Cloud Platform에 호스팅된 완전한 Git 저장소
  • 클라우드 앱의 공동 개발 지원
  • Stackdriver Debugger 와의 통합 기능 제공

8-2. Cloud Functions

  • 서버 또는 런타임 없이 이벤트에 응답하는 단일 목적의 함수 생성
    이벤트 예시: 새로운 인스턴스가 생성됨. 파일이 Cloud Storage에 추가됨
  • Javascript로 작성됨, Google Cloud Platform의 관리형 Node.js 환경에서 실행함

8-3. Deployment Manager

  • 인프라 관리 서비스
  • 환경을 설명하는 .yaml 템플릿을 만들고 Deployment Manager를 사용하여 리소스 생성
  • 반복 가능한 배포 제공

8-4. Stackdriver

  1. Monitoring
    • 플랫폼, 시스템, 애플리케이션 측정항목
    • 업타임/상태 확인
    • 대시보드 및 알림
  2. Logging
    • 플랫폼, 시스템, 애플리케이션 로그
    • 로그 검색, 뷰, 필터, 내보내기
    • 로그 기반 측정 항목
  3. Trace
    • 지연 시간 보고 및 샘플링
    • URL별 지연 시간 및 통계
  4. Error Reporting
    • 오류 알림
    • 오류 대시보드
  5. Debugger
    • 애플리케이션 디버깅
  6. Profiler
    • CPU 및 메모리 사용량에 대한 지속적인 프로파일링

This is a summary of the materials handed out at the Google Cloud OnBoard event held at Sejong University on November 26, 2019.

 


Module 3: Containers and App Development, Deployment, Monitoring

0. Additional Resources

Kubernetes Engine : https://cloud.google.com/kubernetes-engine/docs

Kubernetes : https://kubernetes.io

Google Cloud Build : https://cloud.google.com/cloud-build/docs

Google Container Registry : https://cloud.google.com/container-regitry/docs

Google App Engine : https://cloud.google.com/appengine/docs

Google App Engine Flexible Environment : https://cloud.google.com/appengine/docs/flexible

Google App Engine Standard Environment : https://cloud.google.com/appengine/docs/standard

Google Cloud Endpoints : https://cloud.google.com/endpoints/docs

Apigee Edge : https://cloud.google.com/api-services/content/what-apigee-edge

Cloud Source Repositories : https://cloud.google.com/source-repositories/docs

Deployment Manager : https://cloud.google.com/deployment-manager/docs

Google Stackdriver : https://cloud.google.com/stackdriver/docs

1. Review: IaaS and PaaS

 

IaaS: Infrastructure as a Service - AWS EC2

A service that provides the infrastructure level. The customer directly manages the OS and applications.

PaaS : Platform as a Service - heroku

A cloud service that provides developers with the capabilities needed to develop and serve applications. Users only manage applications and data.

2. Introduction to Containers

  • IaaS : Virtualizes hardware and allows resource sharing.
  • However, flexibility comes at the cost of boot time (minutes) and resources (GB).
  • App Engine
    • Provides access to programming services
    • A platform that rapidly scales apps independently based on workload and infrastructure as app demand grows

2-1. Containers

  • What containers provide
    • Offers the scalability of both IaaS and PaaS.
    • An abstraction layer over hardware and OS
    • An invisible box that provides configurable access to file systems, RAM, and networking divided into isolated partitions
    • Fast startup
  • Container capabilities
    • Configurable, independent, and highly portable.
    • Define your own hardware, OS, and software stack configuration
    • By treating the OS and hardware as a black box, there's no need to change or rebuild anything when migrating from development to staging to production, or from a laptop to the cloud.

A container is app + libraries: the OS/hardware implements the container interface

2-2. Clusters

  • Cluster capabilities
    • Allows deploying containers to server groups with a shared host configuration.
    • Connects multiple containers using network connections
    • Write modular code
    • Easy deployment
    • Achieve maximum efficiency and savings through independent scaling of containers and hosts

3. Kubernetes and Kubernetes Engine

3-1. Kubernetes

Easily orchestrates many containers across multiple hosts.

 

  1. Build and run an app as a container
    • Docker : Bundles the app, dependencies, and system settings together
    • Other tools like Google Cloud Build can also be used. Code example: a Python Flask app that displays hello world
[app.py]

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "helloworld!"

if __name__ == "__main__":
    app.run(host='0.0.0.0');

 

  1. Bring the app to Kubernetes - Specify 4 things using a Docker file

    • The requirements.txt file for Flask dependencies
    • The OS image and version for Python
    • How to install Python
    • How to run the app
[requirement.txt]
Flask==0.12
uwsgi==2.0.15

 

FROM ubuntu:18.10
RUN apt-get update -y && \
    apt-get install -y python3-pip python3-dev
COPY requirements.txt /app/requirements.txt
WORKDIR /app
RUN pip3 install -r requirements.txt
COPY ./app
ENDPOINT ["python3", "app.py"]

 

  1. Build the container into an image and run it

    • Use docker build to build the container and save it as a locally runnable image
    • Upload images to a registry service (such as Google Container Registry) for sharing
    • Start the container image with docker run
$> docker build -t py-server .
$> docker run -d py-server

 

  1. Deploy containers to a collection of nodes called a cluster using the Kubernetes API

    • The master runs the control plane
    • Nodes run the containers
    • Nodes are VMs (used as GCE instances in GKE)
    • You describe the app, and Kubernetes figures out how to implement it
  2. Bootstrapping Kubernetes Engine
    • In a GKE cluster, you can specify the following > Machine type > Number of nodes > Network settings, etc.
$> gcloud container clusters create k1
  1. Use a wrapper called a Pod when deploying containers to nodes
  1. Run a container in a Pod using Kubectl run

    • Kubectl is a command-line client for the Kubernetes API
    • This command starts a deployment with a container running in a Pod
    • In this case, the container is an image of an NGINX server
$> kubectl run nginx --image=nginx:1.15.7
      1. Deployment
        • Manages a set of replica Pods for an app or workload, ensuring the desired number of Pods are running and remain healthy
$> kubectl get pods
  1. Pods are only accessible within the cluster by default and have ephemeral IPs

    • Run Kubectl expose to attach a load balancer to the deployment so it's publicly accessible at a static IP
    • Kubernetes creates a service using the Pod's static IP, and the controller displays a message saying 'I need to attach an external load balancer with a public IP address'
$> kubectl expose deployments nginx --port=80 --type=LoadBalancer
  1. Clients reaching this IP are routed to the Pods behind the service
    • For example, if you create two sets of Pods named frontend and backend and place them behind their own services, changes in the backend Pods won't be noticed by the frontend Pods. They simply reference the backend service.
  1. Run kubectl get services to get the public IP of the service
$> kubectl get services
NAME     TYPE             CLUSTER-IP     EXTERNAL-IP     PORT(S) AGE
nginx    LoadBalancer    10.0.65.118    104.198.149.140    80/TCP    5m
  1. Run kubectl scale to scale the deployment
$> kubectl scale nginx ==replicas=3
  1. Use various parameters to enable autoscaling, or place autoscaling behind programming logic for intelligent management
$> kubectl autoscale nginx --min=10 --max=15 --cpu=80

 

  1. The true power of Kubernetes shines when using the declarative approach
    • Example: How to use a configuration file
$> kubectl get pods -l "app=nginx"

 

[nginx-development.yaml]
apiVersion: v1
kind: Deployment
metadata:
    name: nginx
    labels:
        app: nginx
spec:
    replicas: 3
    selector:
        matchLabels:
            app: nginx
        template:
            metadata:
                labels:
                    app: nginx
            spec:
                containers:
                    - name: nginx
                      image: nginx:1.15.7
                      ports:
                          - continerPort: 80
  1. Run kubectl apply -f to declaratively apply changes
$> kubectl apply -f nginx-deployment.yaml

 

  1. Run kubectl get replicasets to check the update status
$> kubectl get replicasets

 

NAME                DESIRED    CURRENT    READY    AGE
nginx-2035384211    5        3        3        2s

 

  1. Run kubectl get pods to verify the Pods are coming online
$> kubectl get pods

 

NAME                    READY    STATUS    RESTARTS    AGE
nginx-203584211-7ci7o    1/1        Running    0            18s
nginx-203584211-he3h3    1/1        Running    0            18s
nginx-203584211-qqcnn    1/1        Running    0            18s
nginx-203584211-abbcc    1/1        Running    0            18s
nginx-203584211-knlen    1/1        Running    0            18s

 

  1. Run kubectl get deployments to describe the deployment and verify the correct number of replicas are running
$> kubectl get deployments

 

NAME    DESIRED    CURRENT    UP-TO-DATE    AVAILABLE    AGE
nginx    5        5        5            5            18s

 

  1. Build the container and run the image
$> kubectl get services

 

NAME     TYPE             CLUSTER-IP     EXTERNAL-IP     PORT(S) AGE
nginx    LoadBalancer    10.0.65.118    104.198.149.140    80/TCP    5m

 

4. Google App Engine

  • A PaaS for building scalable applications
  • App Engine makes deployment, maintenance, and scaling easy, so you can focus solely on innovation
  • Particularly well-suited for building scalable web applications and mobile backends

5. Google App Engine Standard Environment

  • Easy application deployment
  • Automatic workload scaling in response to demand
  • Cost-effective
    • Free daily quota / usage-based pricing
  • SDKs for development, testing, and deployment
  • Supports specific versions of Java, Python, PHP, and Go
  • Applications must comply with sandbox constraints
    • No writing to the local file system
    • A 60-second timeout applies to all requests
    • Third-party software installation is restricted

Example web application

6. Google App Engine Flexible Environment

  • Build and deploy containerized apps with a single click
  • No sandbox constraints
  • Access to App Engine resources
  • Standard runtimes: Python, Java, Go, Node.js
  • Custom runtime support: Any language that supports HTTP requests
  • Package the runtime as a Dockerfile

App Engine Environment Comparison

7. Google Cloud Endpoints and Apigee Edge

  • Application programming interfaces hide implementation details and enforce contracts [diagram]

7-1. Cloud Endpoints

  • Supports API creation and maintenance
  • Distributed API management through the API console
  • Expose APIs using RESTful interfaces
  • Access control and call validation using JSON Web Tokens and Google API keys
    -> Verify web and mobile user identity through Auth0 and Firebase Authentication
  • Client library generation
  • Supported platforms [diagram]

7-2. Apigee Edge

  • Supports API security and monetization
  • A platform where customers and partners can use APIs
  • Provides analytics, monetization, and developer portal

8. Development, Deployment, and Monitoring in the Cloud

8-1. Cloud Source Repositories

  • Fully-featured Git repositories hosted on Google Cloud Platform
  • Supports collaborative development of cloud apps
  • Provides integration with Stackdriver Debugger

8-2. Cloud Functions

  • Create single-purpose functions that respond to events without a server or runtime
    Event examples: A new instance is created. A file is added to Cloud Storage.
  • Written in Javascript, runs in a managed Node.js environment on Google Cloud Platform

8-3. Deployment Manager

  • Infrastructure management service
  • Create .yaml templates that describe your environment and use Deployment Manager to create resources
  • Provides repeatable deployments

8-4. Stackdriver

  1. Monitoring
    • Platform, system, and application metrics
    • Uptime/health checks
    • Dashboards and alerts
  2. Logging
    • Platform, system, and application logs
    • Log search, view, filter, and export
    • Log-based metrics
  3. Trace
    • Latency reporting and sampling
    • Per-URL latency and statistics
  4. Error Reporting
    • Error notifications
    • Error dashboard
  5. Debugger
    • Application debugging
  6. Profiler
    • Continuous profiling of CPU and memory usage

댓글

Comments

Develop/DevOps

[Cloud OnBoard] 2 - 가상머신 및 스토리지 | [Cloud OnBoard] 2 - Virtual Machines and Storage

2019년 11월 26일 세종대학교에서 있었던 Google Cloud OnBoard에서 나누어준 자료집의 정리본입니다모듈2 가상머신 및 스토리지0. 추가 자료Google Compute Engine : https://cloud.google.com/compute/docsGoogle Cloud Platform VPC : https://cloud.google.com/compute/docs/vpcGoogle Cloud Stackdriver : https://cloud.google.com/stackdriver/docsGcloud 도구 가이드 : https://cloud.google.com/source-repositories/docsCloud Storage 개요 : https://cloud.google.com/sto..

[Cloud OnBoard] 2 - 가상머신 및 스토리지 | [Cloud OnBoard] 2 - Virtual Machines and Storage

728x90

2019년 11월 26일 세종대학교에서 있었던 Google Cloud OnBoard에서 나누어준 자료집의 정리본입니다


모듈2 가상머신 및 스토리지

0. 추가 자료

Google Compute Engine : https://cloud.google.com/compute/docs

Google Cloud Platform VPC : https://cloud.google.com/compute/docs/vpc

Google Cloud Stackdriver : https://cloud.google.com/stackdriver/docs

Gcloud 도구 가이드 : https://cloud.google.com/source-repositories/docs

Cloud Storage 개요 : https://cloud.google.com/storage

Google Cloud SQL 시작하기 : https://cloud.google.com/sql/docs/quickstart

Cloud Bigtable : https://cloud.google.com/stackdriver/docs

Cloud Spanner : https://cloud.google.com/spanner/docs

Cloud Datastore : https://cloud.google.com/datastore/docs

1. Virtual Private Cloud (VPC) 네트워크

  • 각 VPC 네트워크는 하나의 GCP 프로젝트에 포함
  • Cloud Platform 리소스를 프로비저닝하고 서로 연결하거나 격리할 수 있다.
  • VPC 네트워크는 글로벌 수준으로, 서브넷은 리전별 구성으로

2. Compute Engine

  • 관리형 가상머신 제공

    • 고성능 CPU, 대용량 메모리, 표준 및 공유 코어 머신 유형
    • 영구 디스크 (HD와 같은 느낌)
    • 표준, SSD, 로컬 SSD
    • 스냅샷
    • 다운타임없이 디스크 크기 조절
    • 인스턴스 메타데이터 및 시작 스크립트
  • 고객에게 유리한 가격 제시

    • 초단위 청구, 지속 사용 할인, 약정 사용 할인
    • 선점형 인스턴스
    • 추가 비용없이 스토리지 처리량 상향
    • 커스텀 머신 유형 : 필요한 하드웨어에 대해서만 지불
  • 수평확장 또는 수직 확장

    • 메모리 및 컴퓨팅 집약적 애플리케이션에 대규모 VM 사용
    • 탄력적인 확장형 애플리케이션에 자동 확장 사용

3. 중요한 VPC 기능

  • VCP 네트워크의 토폴로지 제어

    • 경로 테이블을 사용해 트래픽을 네트워크 안에서는 물론 서브넷 간에 전달
    • 방화벽을 사용해 네트워크 트래픽이 허용되는 항목을 제어
    • 공유 VPC를 사용해 네트워크 또는 개별 서브넷을 다른 GCP 프로젝트와 공유
    • VPC 피어링을 사용해 GCP 프로젝트의 네트워크의 상호연결
  • 전역 Cloud Load Balancing으로 전 세계에 단일 프런트 엔드로 제공되는 애플리케이션

    • 사용자가 단일 글로벌 Anycast IP 주소를 사용
    • Google 백본을 통해 가장 가까운 접속 지점에서 사용자에게로 트래픽 전달
    • 부하를 기준으로 백엔드 선택
    • 정상 백엔드만 트래픽 수신
    • 가동 준비가 필요 없음
  • Cloud DNS
    • 관리형 영역을 생성한 후 DNS 레코드를 추가, 편집, 삭제
    • RESTful API 또는 명령줄 인터페이스를 사용해 영역과 레코드를 프로그래매틱 방식으로 관리
  • Cloud CDN (Content Delivery Network)

    • 전세계에 분산된 Google의 에지 캐시를 사용하여 콘텐츠를 사용자에게 가깝게 캐싱
    • 다른 CDN의 사용을 원하는 경우 CDN Interconnect 사용
  • 다양한 상호 연결 옵션을 제공

    • VPN : VPN 터널을 통한 Gbps 단위의 연결 확보
    • 다이렉트 피어링 : 하이브리드 클라우드 워크로드를 위한 사용자와 Google간 비공개 연결
    • Dedicated Interconnect : Google 접속 지점에서 Google Cloud로 전달되는 프라이빗 클라우드 트래픽을 위한 N X 10G 전송 회로 연결
    • 이동통신사 피어링 : 서비스 제공업체의 대규모 파트너 네트워크를 통한 연결
    • Partner Interconnect : 지원되는 서비스 제공업체를 통한 온프레미스 네트워크 및 VPC 네트워크 간 연결
  • 부하 분산 옵션 제품군을 제공하는 Google VPC

 

글로벌 HTTP(S) 글로벌 SSL 프록시 글로벌 TCP 프록시 리전 리전 내부
부하를 기준으로한 Layer 7 부하 분산 부하를 기준으로 한 HTTPS외 SSL 트래픽의 Layer 4 부하 분산 SSL외 TCP 트래픽의 Layer 4 부하 분산 모든 트래픽(TCP, UDP)의 부하 분산 VPC내 트래픽의 부하 분산
각종 URL을 다양한 백엔드에 라우팅 할 수 있음 특정 포트 번호에서 지원됨 특정 포트번호에서 지원됨 모든 포트번호에서 지원됨 다중 계층 애플리케이션의 내부 계층에 사용

4. GCP - 스토리지

5. Cloud Storage

  • Cloud Storage의 특징

    • BLOB (binary large-object) 스토리지
    • 고성능 인터넷 규모 : 단순한 관리
    • 용량을 관리할 필요가 없음
    • 저장 데이터 암호화
    • 기본적으로 Google에서 엔드포인트로 전송 중인 데이터 암호화
    • 온라인 및 오프라인 가져오기 서비스 사용 가능
  • Cloud Storage 파일은 버킷으로 구성
  • Cloud Storage 등급 선택하기
  • Cloud Stoarge로 데이터를 가져오는 3가지 방법


    • 온라인 전송 : 명령줄 도구 또는 드래그인 드롭을 사용한 자체 관리형 사본
    • Storage Transfor Service : 일정에 따른 관리형 일괄 전송
    • Transfer Appliance : 데이터를 안전하게 전송하는, 랙 장착 가능한 어플라이언스
  • 다른 GCP 서비스와 함께 사용이 가능하다.

6. Cloud Bigtable

  • Cloud Bigtable의 특징


    • 관리형 NoSQL
    • 테라바이트 규모의 애플리케이션을 위한 완전 관리형 NoSQL, 광폭 열 데이터베이스 서비스
    • HBase API로 액세스
    • 빅데이터, Hadoop 에코시스템과 기본적으로 호환
    • 복제 스토리지
    • 가동 여부와 관계없이 상시 데이터 암호화
    • 역할 기반 ACL
    • google Analytics, Gmail등의 주요 애플리케이션 실행
  • Bigtable 액세스 패턴

    • Application API
      관리형 VM, HBase REST 서버, HBase 클라이언트를 사용하는 자바 서버와 같은 데이터 서비스 계층을 통해 Cloud Bigtable에서 데이터를 읽고 쓸 수 있다. 일반적으로 애플리케이션, 대시보드, 데이터 서비스에 데이터를 제공한다.
    • 스트리밍
      Cloud Dataflow Streaming, Spark Streaming, Storm과 같은 다양한 인기 스트림 처리 프레임워크를 통해 데이터를 스트리밍(이벤트별로 쓰기) 할 수 있다.
    • 일괄 처리
      Hadoop 맵 리듀스, Dataflow, Spark와 같은 일괄 프로세스를 통해 Cloud Bigtable에서 데이터를 읽고 쓸 수 있다. 요약 데이터 또는 새로 계산괸 데이터가 Cloud Bigtable 또는 다운스트림 데이터베이스에 다시 기록되는 경우가 많다.

 

7. Cloud SQL 및 Cloud Spanner

7-1. Cloud SQL

 

  • Cloud SQL의 특징

    • 관리형 RDBMS
    • MySQL및 PostgreSQL 데이터베이스를 서비스로 제공
    • 자동 복제
    • 관리형 백업
    • 수직 확장 (읽기 및 쓰기)
    • 수평 확장 (읽기)
    • Google 보안
  • 다른 GCP 서비스와 함께 사용 가능

    • Cloud SQL + App Engine
      표준 드라이버를 통해 Cloud SQL과 App Engine을 함께 사용할 수 있다.
      App Engine 애플리케이션을 따르도록 Cloud SQL인스턴스를 구성할 수 있다.
    • Cloud SQL + Compute Engine
      Compute Engine 인스턴스에서 외부 IP 주소를 사용하여 Cloud SQL 인스턴스에 액세스 하도록 승인할 수 있다.
      Cloud SQL 인스턴스를 선호 영역에 구성할 수 있다.
    • Cloud SQL + 외부 서비스
      Cloud SQL을 외부 애플리케이션 및 클라이언트와 함께 사용할 수 있다.
      표준 도구를 사용하여 데이터베이스를 관리할 수 있다.
      외부 읽기 복제본을 구성할 수 있다.

7-2. Cloud Spanner

 

  • 수평으로 확장 가능한 RDBMS
  • 자동 복제
  • 강력한 전역 일관성
  • 가용성이 높은 관리형 인스턴스
  • SQL (ANSI 2011, 확장 포함)

8. Cloud Datastore

 

 

  • 수평으로 확장 가능한 NoSQL DB
  • 애플리케이션 백엔드용으로 설계된 NoSQL
  • 완전 관리형 : 분산 아키텍처를 사용해 확장을 자동 관리
  • 내장형 중복 기능
  • ACID 트랜잭션 지원
  • 스키마 없는 액세스 : 기본 데이터 구조를 고려할 필요 없음
  • 로컬 개발 도구
  • 무료 일일 할당량 포함
  • RESTful 인터페이스를 통해 어디서나 액세스

9. 스토리지 옵션 비교

9-1. 기술 세부 정보

  Cloud Datastore Cloud Storage Cloud Bigtable Cloud SQL Cloud Spanner BigQuery
유형 NoSQL 문서 Blobstore NoSQL
광폭 열
OLTP용 관계형 SQL OLTP용 관계형 SQL OLAP용 관계형 SQL
트랜잭션 O X 단일 행 O O X
복잡한 쿼리 X X X O O O
용량 TB+ PB+ PB+ 10TB PB PB+
단위 크기 1MB/항목 -10MB/셀 -100MB/행 5TB/객체 DB엔진 결정 10.240MiB/행 10MB/행

9-2. 사용 사례

  Cloud Datastore Cloud Storage Cloud Bigtable Cloud SQL Cloud Spanner BigQuery
유형 NoSQL 문서 Blobstore NoSQL 광폭 열 OLTP용 관계형 SQL OLTP용 관계형 SQL OLAP용 관계형 SQL
권장용도 반구조화 애플리케이션 데이터, 내구성 높은 키-값 데이터 '플랫' 데이터, 집중적인 읽기 /쓰기, 이벤트, 분석 데이터 구조화 및 비구조화 바이너리 또는 객체 데이터 웹 프레임워크, 기존 애플리케이션 대규모 데이터 베이스 애플리케이션 (약 2TB이상) 대화형 쿼리, 오프라인 분석
사용사례 입문용, App Engine 애플리케이션 AdTech, 금융, IoT 데이터 이미지, 대용량 미디어 파일, 백업 사용자 인증 정보, 고객 주문 높은 I/O, 전역적인 일관성이 필요한 모든 경우 데이터 웨어하우스
단위크기 1MB/항목 -10MB/셀 -100MB/행 5TB/객체 DB엔진 결정 10.240MiB/행 10MB/행

This is a summary of the handout distributed at the Google Cloud OnBoard event held at Sejong University on November 26, 2019.


Module 2: Virtual Machines and Storage

0. Additional Resources

Google Compute Engine : https://cloud.google.com/compute/docs

Google Cloud Platform VPC : https://cloud.google.com/compute/docs/vpc

Google Cloud Stackdriver : https://cloud.google.com/stackdriver/docs

Gcloud Tool Guide : https://cloud.google.com/source-repositories/docs

Cloud Storage Overview : https://cloud.google.com/storage

Getting Started with Google Cloud SQL : https://cloud.google.com/sql/docs/quickstart

Cloud Bigtable : https://cloud.google.com/stackdriver/docs

Cloud Spanner : https://cloud.google.com/spanner/docs

Cloud Datastore : https://cloud.google.com/datastore/docs

1. Virtual Private Cloud (VPC) Network

  • Each VPC network belongs to a single GCP project
  • You can provision Cloud Platform resources and connect or isolate them from each other.
  • VPC networks are configured at the global level, while subnets are configured at the regional level

2. Compute Engine

  • Provides managed virtual machines

    • High-performance CPUs, large memory, standard and shared-core machine types
    • Persistent disks (similar to a hard drive)
    • Standard, SSD, Local SSD
    • Snapshots
    • Resize disks with no downtime
    • Instance metadata and startup scripts
  • Customer-friendly pricing

    • Per-second billing, sustained use discounts, committed use discounts
    • Preemptible instances
    • Higher storage throughput at no additional cost
    • Custom machine types: pay only for the hardware you need
  • Scale out or scale up

    • Use large VMs for memory and compute-intensive applications
    • Use autoscaling for elastic, scalable applications

3. Key VPC Features

  • Control the topology of VPC networks

    • Use route tables to forward traffic within the network as well as between subnets
    • Use firewalls to control what network traffic is allowed
    • Use Shared VPC to share a network or individual subnets with other GCP projects
    • Use VPC Peering to interconnect networks across GCP projects
  • Global Cloud Load Balancing provides applications served by a single frontend worldwide

    • Users use a single global Anycast IP address
    • Traffic is routed to the user from the nearest point of presence via the Google backbone
    • Backends are selected based on load
    • Only healthy backends receive traffic
    • No pre-warming required
  • Cloud DNS
    • Create managed zones, then add, edit, and delete DNS records
    • Programmatically manage zones and records using a RESTful API or command-line interface
  • Cloud CDN (Content Delivery Network)

    • Cache content close to users using Google's globally distributed edge caches
    • Use CDN Interconnect if you prefer to use another CDN
  • Offers a variety of interconnect options

    • VPN: Secure Gbps-level connectivity through VPN tunnels
    • Direct Peering: Private connection between you and Google for hybrid cloud workloads
    • Dedicated Interconnect: N x 10G transport circuits for private cloud traffic from a Google point of presence to Google Cloud
    • Carrier Peering: Connectivity through a service provider's extensive partner network
    • Partner Interconnect: Connectivity between on-premises networks and VPC networks through a supported service provider
  • Google VPC provides a suite of load balancing options

 

Global HTTP(S) Global SSL Proxy Global TCP Proxy Regional Regional Internal
Layer 7 load balancing based on load Layer 4 load balancing for non-HTTPS SSL traffic based on load Layer 4 load balancing for non-SSL TCP traffic Load balancing for all traffic (TCP, UDP) Load balancing for traffic within a VPC
Can route various URLs to different backends Supported on specific port numbers Supported on specific port numbers Supported on any port number Used for internal tiers of multi-tier applications

4. GCP - Storage

5. Cloud Storage

  • Features of Cloud Storage

    • BLOB (binary large-object) storage
    • High performance, internet scale: simple management
    • No need to manage capacity
    • Data at rest encryption
    • Data in transit encryption by default from Google to endpoints
    • Online and offline import services available
  • Cloud Storage files are organized into buckets
  • Choosing a Cloud Storage class
  • 3 ways to bring data into Cloud Storage


    • Online transfer: Self-managed copies using command-line tools or drag and drop
    • Storage Transfer Service: Managed batch transfers on a schedule
    • Transfer Appliance: A rackable appliance for securely transferring data
  • Can be used together with other GCP services

6. Cloud Bigtable

  • Features of Cloud Bigtable


    • Managed NoSQL
    • Fully managed NoSQL, wide-column database service for terabyte-scale applications
    • Access via HBase API
    • Natively compatible with big data and the Hadoop ecosystem
    • Replicated storage
    • Data encryption at all times, whether in use or not
    • Role-based ACLs
    • Powers key applications like Google Analytics, Gmail, and more
  • Bigtable access patterns

    • Application API
      You can read and write data in Cloud Bigtable through a data service layer such as managed VMs, an HBase REST server, or a Java server using an HBase client. This typically serves data to applications, dashboards, and data services.
    • Streaming
      You can stream data (write per event) through various popular stream processing frameworks such as Cloud Dataflow Streaming, Spark Streaming, and Storm.
    • Batch processing
      You can read and write data in Cloud Bigtable through batch processes such as Hadoop MapReduce, Dataflow, and Spark. Summary data or newly computed data is often written back to Cloud Bigtable or a downstream database.

 

7. Cloud SQL and Cloud Spanner

7-1. Cloud SQL

 

  • Features of Cloud SQL

    • Managed RDBMS
    • Offers MySQL and PostgreSQL databases as a service
    • Automatic replication
    • Managed backups
    • Vertical scaling (read and write)
    • Horizontal scaling (read)
    • Google security
  • Can be used with other GCP services

    • Cloud SQL + App Engine
      You can use Cloud SQL with App Engine through standard drivers.
      You can configure a Cloud SQL instance to follow an App Engine application.
    • Cloud SQL + Compute Engine
      You can authorize access to a Cloud SQL instance using the external IP address from a Compute Engine instance.
      You can configure a Cloud SQL instance in a preferred zone.
    • Cloud SQL + External Services
      You can use Cloud SQL with external applications and clients.
      You can manage databases using standard tools.
      You can configure external read replicas.

7-2. Cloud Spanner

 

  • Horizontally scalable RDBMS
  • Automatic replication
  • Strong global consistency
  • Highly available managed instances
  • SQL (ANSI 2011, with extensions)

8. Cloud Datastore

 

 

  • Horizontally scalable NoSQL DB
  • NoSQL designed for application backends
  • Fully managed: automatically handles scaling using a distributed architecture
  • Built-in redundancy
  • Supports ACID transactions
  • Schemaless access: no need to worry about the underlying data structure
  • Local development tools
  • Includes free daily quota
  • Accessible from anywhere via a RESTful interface

9. Comparing Storage Options

9-1. Technical Details

  Cloud Datastore Cloud Storage Cloud Bigtable Cloud SQL Cloud Spanner BigQuery
Type NoSQL Document Blobstore NoSQL
Wide-column
Relational SQL for OLTP Relational SQL for OLTP Relational SQL for OLAP
Transactions O X Single-row O O X
Complex queries X X X O O O
Capacity TB+ PB+ PB+ 10TB PB PB+
Unit size 1MB/entity -10MB/cell -100MB/row 5TB/object Determined by DB engine 10,240MiB/row 10MB/row

9-2. Use Cases

  Cloud Datastore Cloud Storage Cloud Bigtable Cloud SQL Cloud Spanner BigQuery
Type NoSQL Document Blobstore NoSQL Wide-column Relational SQL for OLTP Relational SQL for OLTP Relational SQL for OLAP
Best for Semi-structured application data, durable key-value data 'Flat' data, heavy read/write, event and analytics data Structured and unstructured binary or object data Web frameworks, existing applications Large-scale database applications (roughly 2TB+) Interactive queries, offline analytics
Use cases Getting started, App Engine applications AdTech, finance, IoT data Images, large media files, backups User credentials, customer orders Any case requiring high I/O and global consistency Data warehousing
Unit size 1MB/entity -10MB/cell -100MB/row 5TB/object Determined by DB engine 10,240MiB/row 10MB/row

댓글

Comments