Java로 몇년간 서버개발을 하고, 안드로이드 개발을 최근에 시작했다.

그래도 자바 개발자인데 GC에 대해서 확실하게 설명할 수 있어야지! 하는 생각에 Java GC를 요약해본다.

요약을 위한 포스트임으로 자세한 내용은 아래 참고를 확인 바란다.

자바의 신 이상민님이 NAVER D2에 올리신 글과 '자바 고양이 Tomcat 이야기' 도서를 참고했다.

Garbage Collection

GC는 Garbage Collection의 약자다. (Garbage Collector 아님ㅋ) Java는 Garbage Collector가 메모리 관리(Garbage Collection)를 자동으로 해주기 때문에 메모리 해제를 위한 별도의 코드가 필요없다. (Java 1.2부터 GC에 관여할 수 있는 클래스가 추가 됨) 하지만 GC에 대한 고민없이 Garbage Collector만 믿고 규모있는 어플리케이션을 서비스하면 어김없이 장애가 발생한다. 왜일까?

STW (Stop-The-World)

Full GC가 발생하면 JVM은 어플리캐이션 실행을 멈추고 GC를 실행하는 쓰레드만 작동한다. 만약 웹서버에서 Full GC가 발생하면 서비스는 중단될 것이고 서비스가 중단 된 동안 각종 Time Out이 발생할 것이고 미뤄진 작업들이 누적되어 또 다른 Full GC를 발생시켜 장시간 장애가 발생 할 수 있다.

메모리 공간(heap)을 늘리면 Full GC를 피할 수 있을까? 메모리공간을 늘리면 Full GC의 첫 수행 시점은 늦출 수 있겠다. 하지만 STW의 시간은 heap 크기에 비례하기 때문에 메모리 공간이 클수록 더 많은 시간과 노력이 필요하다. 때문에 Heap을 많이 할당하는 것이 반드시 좋은 것만은 아니다.

어떤 GC알고리즘을 사용하더라도 Full GC와 STW는 발생한다. Full GC를 어떻게 제거할 것인가!? 하는 질문 보다 Full GC는 왜 발생하는가? 그리고 어떻게 피할것인가? 어떻게 최소화 할 것인가? 라는 질문에 초점을 맞춰 보자.

Garbage Collector

Garbage Colletor는 두 가지 일을 한다.

  • 힙(heap)내의 객체 중에서 garbage를 찾아낸다.
  • 찾아낸 garbage를 처리해서 heap의 메모리를 회수한다.

그리고 두 가지의 전제조건 아래에서 만들어졌다.

  • 대부분의 객체는 금방 접근 불가능 상태(unreachable)가 된다.
  • 오래된 객체에서 젊은 객체로의 참조는 아주 적게 존재한다.

이러한 전제조건 아래에서 garbage를 처리하기 위해 HotSpot Vm)에서는 2개의 물리적 공간을 만들었다.

  • Young Generation 영역 : 새롭게 생성한 객체의 대부분이 이곳에 위치. 대부분의 객체가 금방 접근 불가능 상태가 되기 때문에 대부분의 객체가 Young 영역에 생성되었다가 사라진다. 이 영역에서 객체가 사라질 때 Minor GC가 발생했다고 말한다.
  • Old Generation 영역 : Minor GC이후에도 Young 역역에서 사라남은 객체가 여기로 복사 된다. 대부분 Young 영역보다 크게 할당하며, 크기가 큰 만큼 Young 영역보다 GC는 적게 발생한다. 이 영역에서 객체가 사라질 때 Major GC(혹은 Full GC)가 발생한다고 말한다.

Heap Area 구조 : https://www.journaldev.com/2856/java-jvm-memory-model-memory-management-in-java

위 그림에는 Perm(Permanent Generation) 영역이 추가로 보이는데 이곳은 클래스 로더에 의해 로드되는 클래스, 메소드 등에 대한 메타정보가 저장되는 영역으로 어플리케이션이 아닌 JVM에 의해 사용된다. 리플렉션을 사용하여 동적으로 클래스가 로딩되는 경우에 사용되기도한다.

Young Generation 영역의 구성

위 그림을 보면 Young 영역은 Eden과 S0, S1영역으로 나누어져 있다. S는 Survivor를 뜻한다.

Young 영역에서 객체 처리절차는 이상민님의 글을 그대로 인용하겠다.

  • 새로 생성한 대부분의 객체는 Eden 영역에 위치한다.
  • Eden 영역에서 GC가 한 번 발생한 후 살아남은 객체는 Survivor 영역 중 하나로 이동된다.
  • Eden 영역에서 GC가 발생하면 이미 살아남은 객체가 존재하는 Survivor 영역으로 객체가 계속 쌓인다.
  • 하나의 Survivor 영역이 가득 차게 되면 그 중에서 살아남은 객체를 다른 Survivor 영역으로 이동한다. 그리고 가득 찬 Survivor 영역은 아무 데이터도 없는 상태로 된다.
  • 이 과정을 반복하다가 계속해서 살아남아 있는 객체는 Old 영역으로 이동하게 된다.

이 절차를 확인해 보면 알겠지만 Survivor 영역 중 하나는 반드시 비어 있는 상태로 남아 있어야 한다. 만약 두 Survivor 영역에 모두 데이터가 존재하거나, 두 영역 모두 사용량이 0이라면 여러분의 시스템은 정상적인 상황이 아니라고 생각하면 된다.

이렇게 Minor GC를 통해서 Old 영역까지 데이터가 쌓인 것을 간단히 나타내면 다음과 같다.

Old Generation의 GC

Old 영역은 기본적으로 할당 된 메모리에 데이터가 가득 차면 GC를 실행한다. 기본적으로 Young와 Old 영역크기는 1:2의 비율을 가지기 때문에 Old Generation의 GC가 더 많은 시간을 소요한다.

JDK버전이 올라가면서 GC를 위한 방식들이 추가되었다. GC의 방식은 Young 영역과 Old 역역 모두에 영향을 미치며 JDK7 기준으로 5가지 방식이 있다. (최근 JDK 8은 G1GC 방식을 Default로 한다.)

5가지의 GC에 대한 설명도 이상민님의 글을 그대로 인용하겠다.
다만 해당 이상민님의 글은 2011년에 작성되어서 G1GC방식의 안정성에 대해 걱정하고 있지만, 지금은 G1GC 안정화 과정을 마치고 대부분의 서비스에서 채택하고있다.

Old 영역은 기본적으로 데이터가 가득 차면 GC를 실행한다. GC 방식에 따라서 처리 절차가 달라지므로, 어떤 GC 방식이 있는지 살펴보면 이해가 쉬울 것이다. GC 방식은 JDK 7을 기준으로 5가지 방식이 있다.

  • Serial GC
  • Parallel GC
  • Parallel Old GC(Parallel Compacting GC)
  • Concurrent Mark & Sweep GC(이하 CMS)
  • G1(Garbage First) GC

이 중에서 운영 서버에서 절대 사용하면 안 되는 방식이 Serial GC다. Serial GC는 데스크톱의 CPU 코어가 하나만 있을 때 사용하기 위해서 만든 방식이다. Serial GC를 사용하면 애플리케이션의 성능이 많이 떨어진다.

그럼 각 GC 방식에 대해서 살펴보자.

Serial GC (-XX:+UseSerialGC)

Young 영역에서의 GC는 앞 절에서 설명한 방식을 사용한다. Old 영역의 GC는 mark-sweep-compact이라는 알고리즘을 사용한다. 이 알고리즘의 첫 단계는 Old 영역에 살아 있는 객체를 식별(Mark)하는 것이다. 그 다음에는 힙(heap)의 앞 부분부터 확인하여 살아 있는 것만 남긴다(Sweep). 마지막 단계에서는 각 객체들이 연속되게 쌓이도록 힙의 가장 앞 부분부터 채워서 객체가 존재하는 부분과 객체가 없는 부분으로 나눈다(Compaction).

Serial GC는 적은 메모리와 CPU 코어 개수가 적을 때 적합한 방식이다.

Parallel GC (-XX:+UseParallelGC)

Parallel GC는 Serial GC와 기본적인 알고리즘은 같지다. 그러나 Serial GC는 GC를 처리하는 스레드가 하나인 것에 비해, Parallel GC는 GC를 처리하는 쓰레드가 여러 개이다. 그렇기 때문에 Serial GC보다 빠른게 객체를 처리할 수 있다. Parallel GC는 메모리가 충분하고 코어의 개수가 많을 때 유리하다. Parallel GC는 Throughput GC라고도 부른다.

다음 그림은 Serial GC와 Parallel GC의 스레드를 비교한 그림이다.JavaGarbage4

그림 4 Serial GC와 Parallel GC의 차이 (이미지 출처: "Java Performance", p. 86)

Parallel Old GC(-XX:+UseParallelOldGC)

Parallel Old GC는 JDK 5 update 6부터 제공한 GC 방식이다. 앞서 설명한 Parallel GC와 비교하여 Old 영역의 GC 알고리즘만 다르다. 이 방식은 Mark-Summary-Compaction 단계를 거친다. Summary 단계는 앞서 GC를 수행한 영역에 대해서 별도로 살아 있는 객체를 식별한다는 점에서 Mark-Sweep-Compaction 알고리즘의 Sweep 단계와 다르며, 약간 더 복잡한 단계를 거친다.

CMS GC (-XX:+UseConcMarkSweepGC)

다음 그림은 Serial GC와 CMS GC의 절차를 비교한 그림이다. 그림에서 보듯이 CMS GC는 지금까지 설명한 GC 방식보다 더 복잡하다.

JavaGarbage5

그림 5 Serial GC와 CMS GC(이미지 출처)

초기 Initial Mark 단계에서는 클래스 로더에서 가장 가까운 객체 중 살아 있는 객체만 찾는 것으로 끝낸다. 따라서, 멈추는 시간은 매우 짧다. 그리고 Concurrent Mark 단계에서는 방금 살아있다고 확인한 객체에서 참조하고 있는 객체들을 따라가면서 확인한다. 이 단계의 특징은 다른 스레드가 실행 중인 상태에서 동시에 진행된다는 것이다.

그 다음 Remark 단계에서는 Concurrent Mark 단계에서 새로 추가되거나 참조가 끊긴 객체를 확인한다. 마지막으로 Concurrent Sweep 단계에서는 쓰레기를 정리하는 작업을 실행한다. 이 작업도 다른 스레드가 실행되고 있는 상황에서 진행한다.

이러한 단계로 진행되는 GC 방식이기 때문에 stop-the-world 시간이 매우 짧다. 모든 애플리케이션의 응답 속도가 매우 중요할 때 CMS GC를 사용하며, Low Latency GC라고도 부른다.

그런데 CMS GC는 stop-the-world 시간이 짧다는 장점에 반해 다음과 같은 단점이 존재한다.

  • 다른 GC 방식보다 메모리와 CPU를 더 많이 사용한다.
  • Compaction 단계가 기본적으로 제공되지 않는다.

따라서, CMS GC를 사용할 때에는 신중히 검토한 후에 사용해야 한다. 그리고 조각난 메모리가 많아 Compaction 작업을 실행하면 다른 GC 방식의 stop-the-world 시간보다 stop-the-world 시간이 더 길기 때문에 Compaction 작업이 얼마나 자주, 오랫동안 수행되는지 확인해야 한다.

G1 GC

마지막으로 G1(Garbage First) GC에 대해서 알아보자. G1 GC를 이해하려면 지금까지의 Young 영역과 Old 영역에 대해서는 잊는 것이 좋다.

다음 그림에서 보다시피, G1 GC는 바둑판의 각 영역에 객체를 할당하고 GC를 실행한다. 그러다가, 해당 영역이 꽉 차면 다른 영역에서 객체를 할당하고 GC를 실행한다. 즉, 지금까지 설명한 Young의 세가지 영역에서 데이터가 Old 영역으로 이동하는 단계가 사라진 GC 방식이라고 이해하면 된다. G1 GC는 장기적으로 말도 많고 탈도 많은 CMS GC를 대체하기 위해서 만들어 졌다.JavaGarbage6

그림 6 G1 GC의 레이아웃(이미지 출처: "The Garbage-First Garbage Collector" (TS-5419), JavaOne 2008, p. 19)

G1 GC의 가장 큰 장점은 성능이다. 지금까지 설명한 어떤 GC 방식보다도 빠르다. 하지만, JDK 6에서는 G1 GC를 early access라고 부르며 그냥 시험삼아 사용할 수만 있도록 한다. 그리고 JDK 7에서 정식으로 G1 GC를 포함하여 제공한다.

그러나 JDK 7을 실서비스에서 사용하려면 많은 검증 기간(1년은 필요하다는 생각이다)을 거쳐야 할 것으로 보이기 때문에, G1 GC를 당장 사용하고 싶어도 더 기다리는 것이 좋다는 것이 개인적인 생각이다. JDK 6에서 G1 GC를 적용했다가 JVM Crash가 발생했다는 말도 몇 번 들었기에 더더욱 안정화될 때까지 기다리는 것이 좋겠다.

GC 방식 보충

Parallel GC

이름처럼 여러 Thread를 동원해 GC를 처리한다. 정확히 말하면 Young Generation의 GC를 병렬 처리하는 것이며 Parallel Copy 알고리즘을 사용한다. Old Generation은 Serial GC와 같은 Mark-compact 알고리즘을 사용한다. 자바 1.5부터 Parallel Old GC가 소개 됐고, Old Generation도 병렬처리 하게 되었다.

CMS GC

Low Latency GC 혹은 Low Pause GC라고도 부른다. Old Generation GC시 발생하는 STW 시간을 최소화 하는는게 목적이어서 Old Generation 의 GC 알고리즘만 차이가 있다. Young Generation은 Serial GC나 Parallel GC와 같다.

GC를 줄이기 위한 JVM 튜닝

http://d2.naver.com/helloworld/37111 이곳 만한곳이 없다. 하하하!

정리/요약으로 JVM 튜닝작업을 내것으로 만들기는 어려울 것 같다.

GC에 대하여 요약한 내용을 바탕으로 서비스를 해보고 직접 헤딩해보는게 최선일 듯! 

헤딩하게 되면 그 내용을 추가하겠다.

안드로이드 빌드 서버를 구성하면서 메모한 내용을 정리한다.

빌드환경은  CentOS7, Jenkins, Github, Sdkmanager 로 구성하였다.

안드로이드 25 버전부터는 GLIBC_2.14 를 사용하기 때문에 CentOS 6이 아닌 7을 사용해야한다.

CentOS 6에도 GLIBC_2.14를 설치할 수 있지만, 커널쪽 라이브러리를 수정하는건 부담이다. 피하자.

안드로이드 SDK설치

안드로이드 SDK설치 과정이 바뀌었다. 포스트를 쓰게 된 가장 큰 이유다.

안드로이드 빌드환경 구성 방법을 구글링 해보면 adnroid-sdk r24.4.1 압축파일을 다운받아 SDK를 설정하는 방법이 대부분이다.

여기서는 최신버전 SDK로 빌드하기 위해, 안드로이드 공식 홈에 나와있는 sdkmanager를 사용했다.

sdkmanger 설치 & 경로 설정

android 폴더를 만들어 sdkmanger를 다운 받는다.

sdkmanger의 bin 폴더를 PATH에 추가 후 sdkmanger --list 명령어로 테스트 해본다.

안드로이드 패키지 리스트가 나오면 설치완료.

mkdir android
cd android
wget https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip
unzip sdk-tools-linux-3859397.zip
rm sdk-tools-linux-3859397.zip
PATH=$PATH:/절대경로/android/tools/bin
sdkmanager --list

sdkmanger로 sdk 다운로드 

아까 생성한 android 폴더에서 platform-tools와 android-25, 26 platform을 설치한다.

설치가 완료되면 몇몇 폴더들이 추가되어있다.

지금의 android 폴더가 ANDROID_HOME이니 경로를 기억해두자.

sdkmanager "platform-tools" "platforms;android-25" "platforms;android-26"

[bkim@server android]$ ls
build-tools licenses output platforms platform-tools tools

깃 설치

깃허브에서 안드로이드 프로젝트 소스를 받아올것이다. 젠킨스 세팅 전에 git을 설치하자.

설치 후 git 명령어를 날려보면 깃 명령어 리스트가 보인다.

yum update
yum install git
git

젠킨스 세팅

젠킨스 설치

젠킨스 설치 과정은 따로 설명하지 않겠다. jar 파일을 다운받아 바로 실행하는 방법도 좋고, tomcat으로 띄우는 방법도 좋다.

젠킨스 공식 사이트를 참고하면 최신버전을 설치 할 수 있다.

환경변수 설정

ANDROID_HOME을 설정하자. 젠킨스는 편리하게 환경변수를 추가할 수 있는 메뉴를 제공한다.

왼쪽 메뉴 중 Jenkins 관리 -> 시스템 설정 -> Global properties에 추가 -> 이름 ANDROID_HOME -> 값 아까 SDK 설치 경로 (/절대경로/android)

플러그인 설치

github 연동을 위하여 git 관련 플러그인들을 설치해주자.

왼쪽 메뉴 중 Jenkins 관리 -> 플러그인 관리 -> 설치가능 -> git plugin, github plugin 설치

(gradle이 설치되어있지않으면 gradle 플러그인도 추가하자)

빌드 테스크설정

빌드 테스크 설정을 위해 새로운 Item을 추가하자.

왼쪽 메뉴 중 새로운 Item -> Item 이름입력 -> Freestyle project 선택 -> OK

아이템이 생성되었으면, 아이템 설정을 해주자. 단순 빌드를 위한 설정은 단순하다.

 

소스 코드 관리 탭 -> git 선택 -> Repository 주소 입력 -> 깃헙 계정 입력 (나는 서버에 깃헙 키를 등록해두어서 따로 사용자 연동을 하진 않았다)

 

빌드 탭 -> Use Gradle Wrapper 선택 -> Make gradlew executable 체크 -> Tasks 에 clean assembleDebug 입력 (gradle 명령어는 각자에 맞게 수정)

 

빌드 후 조치 탭 -> 빌드 후 조치 추가에서 Archive the artifacts 선택 -> Files to archive에 빌드 완료시 apk 결과물이 떨어지는 경로 입력 (따로 세팅 안했으면 app/build/outputs/apk/*.apk 로 입력하면 된다.)

 

빌드 후 조치에서 Jenkins 저장 이외에 메일로 apk를 보내준다던지, 배포서비스로 apk를 전달하는 것 같은 태스크를 추가 할 수 있다.

또한 빌드 유발 탭에서 깃헙 push 할때마다 빌드도록 세팅해두고, 빌드 후 조치 탭에서 빌드 결과를 메일이나 메신저로 전달하면... 좋다.

 

설정 저장 -> Build Now 클릭 -> 빌드 결과로 들어가면 빌드된 이미지를 확인할 수 있다

마치며

젠킨스를 이용해서 안드로이드 빌드 서버를 구성했다.

빌드 서버를 구성하는 이유는 대부분 CI 환경을 만들기 위함일 것이다.

이제 젠킨스에 Findbugs 같은 코드 분석툴을 붙이고, 그래들 스크립트로 테스트 검증도 할 수 있다.

Deploy Gate라는 서비스를 이용하면 apk를 배포하고, 무선으로 테스트폰에 설치까지 해준다고한다.

결국에는 편하자고 하는 일들이니 우리들 모두 화이팅!!!

 

 

  

Maven vs Gradle

스프링 기반의 프로젝트를 시작하면서 Maven을 처음 접했다.

Ant를 사용한적도 없었고 의존성 관리와 빌드 스크립트에 대한 지식도 없었기에 이런게 있나보다 하고 사용했었다.

Maven 책을 한권 보고나서야 프로젝트 구성, 빌드툴이 무었인지 이해할 수 있었고, 편리한 의존성 관리에 감사하며 부족함을 느끼지 못했다.

하지만 프로젝트의 단위가 커지면서 빌드와 테스트에 소요되는 시간이 길어졌고,

여러 모듈에서 설정을 상속받기 시작하면서 Gradle이라는 녀석이 계속 눈에 뛰었다.

Gradle이 Maven의 단점을 보완해주고 사용해본 사람들이 좋다고들 하니 Maven이랑 비교해서 얼마나 좋은지 알아보려 한다.

Maven

Apache의 이름 아래 2004년 출시

Ant를 사용하던 개발자들의 불편함을 해소 + 부가기능 추가

Maven은 무엇인가?

  • 빌드를 쉽게 (Making the build process easy)
  • pom.xml을 이용한 정형화된 빌드 시스템 (Providing a uniform build system)
  • 뛰어난 프로젝트 정보 제공 (Providing quality project information_
    • Change log document created directly from source control
    • Cross referenced sources
    • Mailing lists
    • Dependency list
    • Unit test reports including coverage
  • 개발 가이드 라인 제공 (Providing guidelines for best practices development)
    • Keeping your test source code in a separate, but parallel source tree
    • Using test case naming conventions to locate and execute tests
    • Have test cases setup their environment and don’t rely on customizing the build for test preparation.
  • 새로운 기능을 쉽게 설치할 수 있고 업데이트할 수 있음 (Allowing transparent migration to new features)

Gradle

Ant와 Maven의 장점을 모아모아 2012년 출시

Android OS의 빌드 도구로 채택 됨

Gradle이란 무엇인가?

  • Ant처럼 유연한 범용 빌드 도구 (A very flexible general purpose build tool like Ant.)
  • Maven을 사용할 수 있는 변환 가능 컨벤션 프레임 워크 (Switchable, build-by-convention frameworks a la Maven. But we never lock you in!)
  • 멀티 프로젝트에 사용하기 좋음 (Very powerful support for multi-project builds.)
  • Apache Ivy에 기반한 강력한 의존성 관리 (Very powerful dependency management (based on Apache Ivy))
  • Maven과 Ivy 레파지토리 완전 지원 (Full support for your existing Maven or Ivy repository infrastructure.)
  • 원격 저장소나, pom, ivy 파일 없이 연결되는 의존성 관리 지원
    (Support for transitive dependency management without the need for remote repositories or pom.xml and ivy.xml files.)
  • 그루비 문법 사용 (Groovy build scripts.)
  • 빌드를 설명하는 풍부한 도메인 모델 (A rich domain model for describing your build.)

Maven VS Gradle

Maven에는 gradle과 비교 문서가 없지만, gradle에는 비교문서가 존재. (비교에 자신있는 모습 ..ㅋ)

Gradle이 시기적으로 늦게 나온만큼 사용성, 성능 등 비교적 뛰어난 스펙을 가지고있다.

Gradle이 Maven보다 좋은점

  • Build라는 동적인 요소를 XML로 정의하기에는 어려운 부분이 많다.
    • 설정 내용이 길어지고 가독성 떨어짐
    • 의존관계가 복잡한 프로젝트 설정하기에 부적절
    • 상속구조를 이용한 멀티 모듈 구현
    • 특정 설정을 소수의 모듈에서 공유하기 위해서는 부모 프로젝트를 생성하여 상속하게 해야 함 (상속의 단점 생김)
  • Gradle은 Groovy를 사용하기 때문에, 동적인 빌드는 Groovy 스크립트로 플러그인을 호출하거나 직접 코드를 짜면 된다.
    • Configuration Injection 방식을 사용해서 공통 모듈을 상속해서 사용하는 단점을 커버했다.
    • 설정 주입 시 프로젝트의 조건을 체크할 수 있어서 프로젝트별로 주입되는 설정을 다르게 할 수 있다.

 

Gradle vs Maven: Performance Comparison

  • 600명의 엔지니어가 1년동안 1분걸리는 빌드를 매주 42번 빌드를 진행할 때 들어가는 비용은
    600 engineers * $1.42/minutes * 42 builds/week * 44 work weeks/year = $1,600,000/year
    이때 빌드 속도가 90% 빨라진다면?
  • Gradle은 메이븐 보다 최대 100배 빠르다.
  • 어떻게?
    • the Gradle Daemon is a long-lived process that keeps build information “hot” in memory
    • incremental task inputs and outputs for various types of tasks makes it unnecessary to run clean ever again
    • incremental compilation analyzes the dependencies between your sources and classes and recompiles only those which are affected by changes
    • the build cache fetches results from a cache when switching branches or running a clean build and the same output has already been produced somewhere else in the organization.
    • Gradle’s smart classpath analyzer avoids unnecessary compilation when the binary interface of a library hasn’t changed
    • better modelling of dependencies using the Java Library plugin reduces the size of the compile classpath which has a large, positive impact on performance

예제

스프링부트를 이용하여 같은 기능과 라이브러리 의존성을 가지는 Maven, Gradle 프로젝트를 생성해 보았다. (Java 1.8, Spring Boot 1.5.4)

  1. 스크립트 길이와 가독성 면에서 Gradle(groovy)이  앞선다.
  2. 빌드와 테스트 실행 결과 Gradle이 더 빠르다. (Gradle이 캐시를 사용하기 때문에 테스트 반복 시 차이가 더 커진다.)
  3. 의존성이 늘어날 수록 성능과 스크립트 품질의 차이가 심해질 것이다.
Maven
<?
xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.example</groupId>
<artifactId>demo-maven</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>demo-maven</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>


</project>
Gradle
buildscript {
ext {
springBootVersion = '1.5.4.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}

apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'

version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
mavenCentral()
}


dependencies {
compile('org.springframework.boot:spring-boot-starter')
testCompile('org.springframework.boot:spring-boot-starter-test')
}

결론

지금 시점에서 Gradle을 사용하지 않을 이유는 '익숙함' 뿐인 것 같다.

Gradle이 출시되었을 때에는 Maven이 지원하는 Scope를 지원하지 않았고 성능면에서도 앞설것이 없었다.

Ant의 유연한 구조적 장점과 Maven의 편리한 의존성 관리 기능을 합쳐놓은 것만으로도 많은 인기를 얻었던 Gradle은 버전이 올라가며 성능이라는 장점까지 더해지면서 대세가 되었다.

 

물론 그동안 사용해왔던 Maven과 이제는 익숙해진 XML을 버리고 Gradle과 Groovy문법을 배우는 것은 적지않은 비용이 든다.

특히 협업을 하는 경우, 프로젝트 구성과 빌드만을 위해 모든 팀원이 Groovy 문법을 익여야 한다는 사실은 Gradle를 사용하는데 큰 걸림돌이 된다.

실제로 여전히 Maven의 사용률은 Gradle을 앞서고 있으며 구글 트랜드 지수도 Maven이 Gradle을 앞선다.

 

협업과 러닝커브를 고려하여 여전이 Maven를 사용하는 팀이 많고 부족함 없이 잘 사용하고 있지만,

앞서 요약했듯 프로젝트의 빌드타임이 비용문제로 이어질 경우 Gradle을 사용해야 할 것 같다.

리드미컬?하게 테스트를 진행하고 민첩한? 지속적 배포를 생각하고 있다면 새로움 배움이 필요하더라도 Gradle을 사용해보자. 아자.

 

출처

Nginx는 컴파일된 바이너리를 제공하지 않는다.

apt-get이나 yum을 통해 설치하면 설정 관리나, 이중화같은 작업이 어렵기 때문에 컴파일 설치를 선호하는 편이다.

2017년 6월 23일 기준 최신 Stable version인 nginx-1.12.0 버전 설치 스크립트를 아래 작성했다.

 

설치 기준 위치는 /home/username/apps/ 로 한다.

 

1. NGINX 다운로드

cd /home/username/apps/
wget https://nginx.org/download/nginx-1.12.0.tar.gz
tar -xvf nginx-1.12.0.tar.gz
rm nginx-1.12.0.tar.gz

2. PCRE 다운로드

cd /home/username/apps/nginx-1.12.0
wget http://downloads.sourceforge.net/project/pcre/pcre/8.37/pcre-8.37.tar.gz
tar -zxf pcre-8.37.tar.gz

3. zlib 다운로드

cd /home/username/apps/nginx-1.12.0
wget http://zlib.net/zlib-1.2.11.tar.gz
tar -zxf zlib-1.2.11.tar.gz

4. OpenSSL 다운로드

cd /home/username/apps/nginx-1.12.0
wget http://www.openssl.org/source/openssl-1.0.2f.tar.gz
tar -zxf openssl-1.0.2f.tar.gz

5. NGINX 설치

cd /home/username/apps/nginx-1.12.0
./configure --prefix=/home/username/apps/nginx --with-zlib=./zlib-1.2.11 --with-pcre=./pcre-8.37 --with-openssl=./openssl-1.0.2f --with-http_ssl_module --with-http_stub_status_module
make install
cd /home/username/apps/
rm -rf nginx-1.12.0

6. 실행권한 설정

cd /home/username/apps/nginx/sbin
sudo chown root nginx
sudo chmod +s nginx

7. 실행 및 테스트

cd /home/username/apps/nginx/sbin
./nginx //시작
//포트 80 이 사용중이면, /home/username/apps/nginx/conf/nginx.conf 에서 liten 80 을 변경해주면 된다.

//자신의 서버 ip:prot 로 접근해보면 nginx index페이지 뜬것을 확인 할 수 있다. 

./nginx -s stop  //종료

 

HTML5 Video Tag

HTML5 HTML의 완전한 5번째 버전으로 월드 와이드 웹 (World Wide Web)의 핵심 마크업 언어이다. 2004년 7월 Web Hypertext Application Technology Working Group(WHATWG)에서 웹 애플리케이션 1.0이라는 이름으로 세부 명세 작업을 시작하였다.

HTML5는 HTML 4.01, XHTML 1.0, DOM 레벨 2 HTML에 대한 차기 표준 제안이다. 비디오, 오디오 등 다양한 부가기능과 최신 멀티미디어 콘텐츠를 액티브X 없이 브라우저에서 쉽게 볼 수 있게 하는 것을 목적으로 한다.
 W3C는 2014년 10월 28일 HTML5 표준안을 확정했다고 발표했다. - https://ko.wikipedia.org/wiki/HTML5

 

HTML4에서 5로 넘어오면서 audio, video, canvas라는 멀티미디어 관련 태그가 추가되었다.

W3C는 2004년 새로운 표준을 제안하였고, 단계적으로 HTML5를 지원하는 범위를 넓혀가던 2007년까지 HTML5에 대한 시선이 곱지만은 않았다.

이제는 레거시 시스템이 된 Silver Light와 Flash, Active X가 라이벌을 물리치고 평정한 시대였고, 브라우저가 HTML5를 랜더링하는 성능도 눈에 뛰게 부족했기 때문이었다.

 

하지만 HTML5가 있으면 모든 클라이언트 서비스(읽고, 쓰고, 보고, 듣는)를 지원할 수 있다는 희망이 생겨났고, 이를 증명하듯 수많은 웹앱이 태어났다. 스마트폰과 하드웨어의 발전이 계속되고 복잡한 웹생태계를 표준화하고자 하는 사람들의 열망이 모여 현재 Flash와 Silver Light, Actice X는 레거시를 넘어 웹생태계를 해치는 악의 축으로 밀려 나기 까지 했는데, 이러한 HTML5 내용 중 Video에 대해서 살펴보고자 한다.

 

Video element 는 Medeia element를 구현하고 있으며, 속성으로 src, poster, preload, autoplay, mediagroup, loop, muted, controls, width, height 속성을 가진다. DOM생성시 해당 옵션을 지정할 수 있으며 이름을 통해서 알 수 있듯이 플레이어가 지원해야할 기본적인 옵션들이다.

Video element의 DOM interface로는 width, height, videoWidth, videoHeight, poster가 있고, Media elemet에 current와 duration 같은 좀 더 상세한 interface가 있다.  상세내용은 아래에 첨부하였다.

 

W3C  Video 정의

Video Element

Content attributes

src
poster
preload
autoplay
mediagroup
loop
muted
controls
width
height

DOM interface
interface HTMLVideoElement : HTMLMediaElement {
           attribute unsigned long width;
           attribute unsigned long height;
  readonly attribute unsigned long videoWidth;
  readonly attribute unsigned long videoHeight;
           attribute DOMString poster;
};

Media elements (audio and video, in this specification) implement the following interface:

interface HTMLMediaElement : HTMLElement {

  // error state
  readonly attribute MediaError error;

  // network state
           attribute DOMString src;
  readonly attribute DOMString currentSrc;
  const unsigned short NETWORK_EMPTY = 0;
  const unsigned short NETWORK_IDLE = 1;
  const unsigned short NETWORK_LOADING = 2;
  const unsigned short NETWORK_NO_SOURCE = 3;
  readonly attribute unsigned short networkState;
           attribute DOMString preload;
  readonly attribute TimeRanges buffered;
  void load();
  DOMString canPlayType(in DOMString type);

  // ready state
  const unsigned short HAVE_NOTHING = 0;
  const unsigned short HAVE_METADATA = 1;
  const unsigned short HAVE_CURRENT_DATA = 2;
  const unsigned short HAVE_FUTURE_DATA = 3;
  const unsigned short HAVE_ENOUGH_DATA = 4;
  readonly attribute unsigned short readyState;
  readonly attribute boolean seeking;

  // playback state
           attribute double currentTime;
  readonly attribute double initialTime;
  readonly attribute double duration;
  readonly attribute Date startOffsetTime;
  readonly attribute boolean paused;
           attribute double defaultPlaybackRate;
           attribute double playbackRate;
  readonly attribute TimeRanges played;
  readonly attribute TimeRanges seekable;
  readonly attribute boolean ended;
           attribute boolean autoplay;
           attribute boolean loop;
  void play();
  void pause();

  // media controller
           attribute DOMString mediaGroup;
           attribute MediaController controller;

  // controls
           attribute boolean controls;
           attribute double volume;
           attribute boolean muted;
           attribute boolean defaultMuted;

  // tracks
  readonly attribute MultipleTrackList audioTracks;
  readonly attribute ExclusiveTrackList videoTracks;
  readonly attribute TextTrack[] textTracks;
  MutableTextTrack addTextTrack(in DOMString kind, in optional DOMString label, in optional DOMString language);
};

Youtube, Twitch, Pooq

실서비스 되고 있는 비디오 플레이어를 살펴보면, Html5 Video 태그로 플레이어 기본 기능을 구현한 것을 알 수 있다.

각종 버튼과, 광고노출과 같이 플레이어와 겹쳐서 보여지는 부분들은 DIV형태로 플레이어 위에 덧씌어져서 사용자에게 노출하고,

외부 Html요소를 이용하여 사용자로부터 전달되는 이벤트를 Video Eelement로 전달하는 방식으로 플레이어를 구현했다.

 

You Tube

 

Twitch

 

Pooq

 

Open Source

여러 종류의 Html5 Video Player 비교1, 비교2, 추천들이 있다.

다수의 Html5 Video Player 중에서 눈에 뛰는 두가지 오픈소스 플레이어를 소개한다.

 

두가지 플레이어 모두 플레이어 기본기능을 구현하고 있고, YouTube와 Vimeo완 연동이 가능하다.

또한 공통적으로 자막, 반응형, 스트리밍, 풀스크린, 다국어와 같은 사용자 편의기능을 포함하고있다.

 

Video.js의 Github Start는 16,549개다. Jquery가 45,000개 인것을 감안하면 엄청난 숫자이다.

사용자가 많은만큼 레퍼런스가 풍부하고, 대부분의 플러그인이 개발되어 있다.

커스터마이징이 필요없고, 하나의 솔루션처럼 Html5 Video Player가 필요하다면 가장 추천할만한 플레이어다.

 

Plyr의 Github Star는 9,469개다. 유명도에 비해 높은 Star 수를 보유하고 있으며,

경량화된 Html5 Video Player에 대한 개발자들의 요구를 반영하여 경량과, 커스터마이징에 초점을 맞추어 개발했다.

비교적 부족한 플러그인과 Owner가 육아에 돌입했다는 단점이 있지만 커스터마이징이 필수인 경우 가장 추천할만한 플레이어다. 

 

Video.js

  • 가장 사용자가 많은 Video Player Open Source (40만 사이트 이상)
  • HTML5와 Flash video까지 지원
  • 활발한 PR과 다수의 플러그인
  • js간 의존성이 높고, 파편화(좋은말로 모듈화) 됨
  • Apache License

Plyr

  • 경량, 커스터마이징에 초점을 맞춤
  • js 파일 하나, 10kb
  • 비교적 부족한 플러그인
  • Owner가 최근 육아에 전념함
  • MIT License

Solution

유료 솔루션 형태의 Html5 Video Player도 많이 있다.

유료형태의 솔루션은 HLS(스트리밍)지원과 광고 연동, 사용자 통계, DRM지원, Preview Thumbnail, 360 View, 고객지원과 같은 부가기능을 무기로 가격을 책정한다. 

하지만 Video.js 와 플러그인, hls.js와 같은 오픈소스들로 유료 기능들을 구현할 수 있기 때문에 플레이어만 판매하는 것을 넘어서 인코딩과 CDN, 그리고 스트리밍을 위한 서버를 함께 제공해주는 업체가 많다. 또는 플레이어를 무료로 풀고 고개지원에만 가격을 책정하는 업체도 있다.

자체적으로 스트리밍 서버와 CDN, 인코딩서버를 구축하기 힘든 경우 해당 업체를 사용하면 좋을것 같지만, 최근 AWS에서 워낙 친절한 솔루션을 내놓았기 때문에 개발자 관점에서 매력을 찾기는 어려울 것 같다.

OpenSource Issue (DRM, Streaming, Mobile, Ad)

오픈소스를 사용하여 Html5 Video Player를 구현할 때 어려운 점이 무엇일까 고민해보았다.

(유료 솔루션들이 해당 기능을 해결해주고 돈을 받는 것이기 때문에 유료 솔루션의 피처리스트들이 구현 시 어려운 점이었다.)

 

HLS Streaming : hls.js를 사용하면 된다.

DRM : hls.js를 사용하면 되지만, 스트리밍이 아닌경우 생각해봐야할 문제.

Mobile : 반응형 개발로 Web View 재생을 하면 되지만, 성능이 받쳐줄지는 미지수. 성능이슈라면 유료 솔루션도 어쩔수 없다.

Ad : 광고를 심는 시점과 고객 행동 데이터를 수집해야한다. 광고 노출기능은 Video.js에서 videojs-contrib-ads라는 플러그인 형태로 구현해 두었지만, 광고 전환율, 사용자당 클릭 퍼센트와 같은 통계데이터 수집은 따로 구현해야할 부분이다. 이 부분은 플레이어 자체의 기능이라기보다 서비스 플랫폼의 관점으로 바라봐야 할 것 같다.

Change Detection 란 무엇인가?

Change Detection(이하 CD)는 뷰와 모델을 동기화 할 수 있도록 컴포넌트의 변경을 감지하고 값을 동기화 하는 프로세스다.

 

Angular JS (1.x)

뷰단의 변경을 감지하는 것 뿐만 아니라, 비동기로 돌아가는 자바스크립트(ajax, setTimeout 등..)가 데이터 변경하는 시점을 감지하여 모델의 변경의 뷰단에 반영해야 하는데 Angular 1에서는 이런 로직을 Digest Loop로 구현 했었다. 

Digest Loop로 감시할 대상을 지정하기 위해 scope마다 watcher을 등록하여 감지할 목록들을 만들어야 했는데 이런 상황 때문에, 데이터의 변경을 감지하기 위해서는 Angular 도구만을 사용해야 했다. ajax call 또는 click 같은 이벤트도 $http와 ng-click으로 구현해야만 모델 변경이 감지되었다.

이를 보완하기 위해 명시적 호출($diest, $apply, $timeout)을 지원했지만 개발자가 직접호출해야한다는 이질감이 있었다.

또한 Angular 1은 양방향 데이터 바인딩으로 구현했기 때문에 watcher들이 늘어날 수록 Digest Loop은 걷잡을 수 없이 혼란스러워졌다.

Angular

Angular2에서는 $scope기능을 없애고 Zone.js를 이용하여 CD를 구현하였는데, Zone.js 덕분에 Angular 도구 뿐만 아니라, 순수 자바스크립트로 컴포넌트를 구성할 수 있게 되었다. 또한 Angular 2는 단방향 바인딩을 구현하였기 때문에 (ngModel을 사용해서 양방향 바인딩을 구현할때도 데이터의 흐름은 단방향) CD는 어플리케이션 전체를 변경할 필요 없이 데이터가 바인딩 되어있는 곳만 변경을 감지하고 Angular2는 변경된 부분만 다시 그려주면 되었다. 이를 통해 Angular는 성능을 높일 수 있었고 강제되던 Angular 도구들을 제거할 수 있었다.


Figure: File StructureFigure: File Structure 출처 - https://angular-2-training-book.rangle.io


어떻게 변화를 감지하는가? (Zone.js - 동영상)

Angular는 Angular도구가 아닌 순수 자바스크립트로 변경한 데이터 변경 또한 감지 할 수 있다고 했다.

우리가 웹에서 ajax로 데이터로 가져오거나, Timer로 함수를 실행하거나, click 이벤트를 발생시키는것은 모두 비동기로 이루어지는데 사용자가 명시적으로 호출하지 않고도 어떻게 Angular는 어플리케이션 상태 변화를 감지할 수 있을까?

답은 Zone.js에 있었다. (Angular는 자체적인 Zone을 가지고 있고 ngZone이라고 한다.)

Zone.js는 JAVA의 ThreadLocal 개념과 비슷해서 각각의 비동기 기능에서 kick on과 kick off를 설정하지 않아도 Zone이 이를 감지하여 후킹할수 있게 해준다.

 

 

위 코드를 실행해보면 콘솔 창에 다음과 같이 나타단다.

1번에서 click event listener가 등록된것을

2번에서 setTimeout이 발생하여 Invoike 한것을 상세하게 알 수 있다.

이렇게 Zone.js는 비동기 코드들을 컨트롤하면서 사용자에게 편리한 후킹인터페이스를 제공해주는 것이고,

Angular는 ngZone을 이용해서 컴포넌트의 변화를 감지하는 것이다.

 

 

Angular에서 구현된 소스는 다음과 같다.

this.zone.onMicrotaskEmpty
.subscribe(() => {
this.zone.run(() => this.tick()
})
})

tick()
{
this.changeDetectorsRefs
.forEach((ref) => ref.detectChanges())
}

어떻게 변경하는가?

그렇다면 앞서 ngZone이 감지한 변화를 Angular는 어떻게 View에 적용할까?

Angular의 컴포넌트들은 각자 자신만의 CD를 가지고 있어서, 각자 CD 수행하고 컨트롤할 수 있다. 예를 들어 클릭 이벤트가 발생하면, Zone.js가 이벤트를 감지하여 알려주고 Angular는 CD를 실행한다. 이 때 컴포넌트는 각자 자신의 CD를 가지고 있고 컴포넌트는 트리구조로 이루어져 있기 때문에 CD의 구조도 방향이 있는 트리구조로 구성된다. 방향성이 있는 트리구조 때문에 CD는 항상 TOP에서 ROOT로 전파된다.

 

http://pascalprecht.github.io/slides/angular-2-change-detection-explained/#/59http://pascalprecht.github.io/slides/angular-2-change-detection-explained/#/59

 

Angular 컴포넌트들은 자신만의 Change Detector를 가진다?

Runtime에 Angular는 각 컴포넌트마다 자신이 가진 속성들에 대하여 Change Detector 클래스를 생성한다.

Change detector들은 컴포넌트의 각 속성에대한 Old Value를 가지고 있기때문에 독립적으로 자신의 컴포넌트에 대한 변경 여부를 확인 할 수 있고,

감지 속도를 높이기 위해 개발자가 전략을 작성하거나 속성값 등을 전달 할 수 있다.

/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* @stable
*/
export declare abstract class ChangeDetectorRef {
abstract markForCheck(): void;
/**
* Detaches the change detector from the change detector tree.

*/
abstract detach(): void;
/**
* Checks the change detector and its children.
*/
abstract detectChanges(): void;
/**
* Checks the change detector and its children, and throws if any changes are detected.
*/
abstract checkNoChanges(): void;
/**
* Reattach the change detector to the change detector tree.
*/
abstract reattach(): void;
}

Change Detection을 빠르게

그렇다면 어떻게 Chage Detection을 효율적으로 실행할 수 있을까?

앞서 설명했듯이 CD는 Runtime시 각 컴포넌트마다 생성되기 때문에 다양한 형태를 가질 수 있다. 각 컴포넌트의 구조에 맞게 CD 클래스를 정의 하고 생성하기 때문에, CD가 감시해야할 대상을 줄이면 성능을 높일 수 있다. Angular는 CD가 감시해야 할 대상을 줄이기 위해 Immutable 객체와 Observable 객체를 알아?보고 감시 대상에서 제외해 할 수 있는데, 이런 최적화는 Angular의 OnPush 전략(컴포넌트에 바인딩된 Input 값의 변화가 없는 경우에는 Subtree 에 대해 Change Detection을 하지 않음)으로 설정하여 처리한다.

 

Immutable

Immutable 객체는 값의 변화가 없음을 보장한다. 새로운 값을 지정하고 싶으면 객체를 변경하는 것이 아니라 새로 생성 해야만한다.

객체를 새로 생성한다는 것은 Angular 입장에서 값(상태) 비교가 필요 없다는 것을 뜻한다. Immutable 객체를 사용하는 바인딩된 컴포넌트들은 변경된 객체의 상태를 비교해 볼 필요 없이 객체의 레퍼런스만으로 변화를 감지할 수 있다. 

(Javascript Immutable lib들을 이용하여 손쉽게 Immutable 객체를 생성해 볼 수 있는다. Immutable.js Github)

 

http://blog.thoughtram.io/angular/2016/02/22/angular-2-change-detection-explained.htmlhttp://blog.thoughtram.io/angular/2016/02/22/angular-2-change-detection-explained.html

 

Observable

관찰할 수 있는, 식별할 수 있는 객체를 의미한다. 관찰할 수 있는 객체를 구독(subscribe)하면서 값을 참조하는 방법이다.

예를 들어 다음과 같은 코드가 있을 때, firstName만 변경하여도 fullName까지 함께 변경 되는 것이다.

var firstName = Observable('BK');
var lastName = Observable('KIM');

var fullName = Observable(function () {
return firstName.value + ' ' + lastName.value;
});

Angular에서는 Immutable 객체 반대 방식으로 CD를 생략한다. Immutable 객체의 레퍼런스 참조만으로 CD를 생략할 수 있었다면 반대로 Observabel 객체는 값의 변경점에서 이벤트를 발생시켜서 TOP CD까지 변경을 전파한다. 따라서 Angular는 ROOT에서 TOP으로 가는 길목에 있는 CD만 수행하면 되는 것이다. 이때 OnPush전략을 함께 사용하면 Observable 객체의 레퍼런스는 변경되지 않기 때문에, 이벤트 컴포넌트 Subtree의 CD가 생략 된다. 하위 컴포넌트들도 함께 업데이트 해야한다면 디텍터의 markForCheck()라는 API를 이용하여 Subtree까지 CD를 수행 할 수 있다.

 

http://blog.thoughtram.io/angular/2016/02/22/angular-2-change-detection-explained.htmlhttp://blog.thoughtram.io/angular/2016/02/22/angular-2-change-detection-explained.html

마치며

AngularJs 1.x에서 Angular 2로 넘어오면서 가장 관심이 갔던 부분은 성능이었다.

Change Detection에 대해서 자세히 알아보기 전까진 ReactJS의 Virtual DOM과 비슷한 방식이라 생각했었는데, 깊이 들여다보니 Framewrok로서 장점을 잘 이용한 것 같다. 성능 저하와 복잡도 상승의 주 원인이던 양방향 바인딩을 없애고, Zone.js를 도입 함으로써 라이브러리 사용에 비해 자유롭지 못한 Framework의 단점을 보완하려 한 부분도 흥미로웠다. 들여다 볼수록 매력있는 Angular지만 제대로 사용하지 않으면 이러한 장점들의 존재조차 모르고 사용하게 될것이란 게 두렵기도 하다. 

참조

 

 

 

'ANGULAR' 카테고리의 다른 글

IntelliJ 에서 Angular 2 프로젝트 시작하기  (499) 2017.05.16

결론

JavaScript개발시 어떤 프레임워크 또는 라이브러리는 사용할것인가? 라는 질문에 대답하기 위해 기술조사를 했다.

사용할 기술을 선택할 때는  언제나 트레이드 오프가 발생한다. 

따라서 개발하려고 하는 서비스와 팀의 규모를 고려해서 결정 하는 것이, 트렌드와 유행에따라 결정하는것보다 타당하다 생각한다.


기술조사를 마치고 느낀점은 Angular는 개발자의 자유도를 제한하는 대신, 우리가 마주할 수 있는 이슈에 대하여 기본적인 방법을 제시해고 있었고,

React는 스스로 라이브러리임을 밝히며 몸집을 최소화하며 개발자의 선택을 존중하고 있었다. 개발자의 자유도를 제한하지 않는면에서 잘나가는? 분야에서 조예가 깊은? 개발자는 React Family를 좋아하겠다는 생각을 여러번 했다. 실제로 기술조사를 하면서 React를 추천하는 곳에서 geek함과 개발자스러운 냄새를 맡을 수 있었다.


부족한 견해일지 모르지만 서버개발 프레임워크와 비교해보았을 때 Angular를 보면서 Spring같다는 생각을, React Family를 보면서 Node.js 같다는 생각을 했다. 또한 진정으로 POJO와 닮은 Vanilla JS의 간단한 페이지를 돌아보며 이것 또한 좋은 방법 같았다. Jquery 또한 여전히 사용성에서 1등을 고수하고 있었으며, 사용 추세가 전혀 줄어들지 않고 있었다.


다만 우리가 Spring을 사용하듯이, 팀단위로 일정한 수준 이상의 JavaScript 생산성과 품질을 유지하려면 Angular의 프레임 안에서 개발해나가는게 좋을 것 같다. 개발자에게 주어지는 자유도가 높을수록 성능은 높아질 수 있지만, 유지 보수와 협업의 관점에서의 성능은 어플리케이션의 규모가 커질수록 낮아질 것이다. 


만약 우리가 Front개발에 익숙하지 않은 팀이라면 Angular를, Front개발에 익숙한 팀이라면 React Family를 사용하는 것이 좋겠다.

소규모의 팀이거나 규모가 작은 서비스는 Front개발의 숙련도를 떠나 React Family를 이용하는것이 좋을 것 같다.

Framework

프레임워크란 무엇일까? 자바스크립트 프레임워크를 비교하기 전에 프레임워크에 대한 정의부터 분명히 해야겠다.

GoF의 디자인패턴 저자 랄프존슨은 "프레임워크란 소프트웨어의 구체적인 부분에 해당하는 설계와 구현을 재사용이 가능하게끔 일련의 협업화된 형태로 클래스를 제공하는 것" 라고 말한다. 혹자는 디자인 패턴 + 라이브러리 = 프레임워크라고 주장한다.


다양한 프레임워크가 존재하는 만큼 다양한 의미를 포함하고 있지만, 자바스크립트 프레임워크는 Inversion of control을 가능케하고 Modularity, Reusability, Extensibility 를 높이기 위한 목적을 가진 라이브러리 + 구조화 된 뼈대의 조합이라고 생각하면 좋을 것 같다. 우리가 흔히 사용하는 Jquery는 뼈대를 제공하지 않고 라이브러리만 제공하기때문에 프레임워크라고 부르지 않는다. Jquery를 사용해서 MVC 패턴을 입히면 그것은 프레임워크라 부를 수 있을 것이다.


최근 JavaScript가 사용되는 분야가 많아지고 인기가 높아지면서 오픈소스로 개발되는 자바스크립트 프레임워크들 또한 다양해지고 있다.

그 중에서 Angular 와 React를 비교해보고 이러한 상황을 비꼬는 듯한 용어인 Vanilla JS에 대하여 알아보자.

Angular

Angular는 MVC Framework로 최근 4.0버전을 출시했다. AngularJs는 1.x 버전을 지칭하고 Angular는 2.0 이후 버전을 지칭하는데, 1버전과 2버전은 패러다임의 변화가 있었고, 2버전 이후부터는 하위버전 호환을 유지하고있다. AngularJs에서 성능 문제의 주요원이었던 2 way data binding을 Angular에서는 빌트인으로 제공하지 않으며 서로 호환되지도 않음으로 분리해서 보아야 한다.


Angular는 구글 자체적으로 개발하던 AtScript를 사용하려 했으나, Microsoft와 협력하여 TypeScript를 채택했다.

한편에선 Facebook의 React와 달리 구글이 Angular를 사용하는 비중이 낮다는 점을 우려하고 있다. (YouTube를 새로 개편할 때 Polymer 사용)- 링크1, 링크2

React.js

UI 컴포넌트를 만들기 위한 라이브러리로, UI 컴포넌트만을 지원함으로 지원하는 범위가 작지만 다향한 조합으로 사용 할 수 있다.

React.js로 Angular의 directives를 구현하여 프레임워크를 구성할 수 있지만, 보통 Redux를 이용하여 프레임워크를 구축한다.

JavaScript와 TypeScript를 지원하지만 JSX라는 ECMAScript 친화적인 XML 문법을 사용할 것을 추천한다.

VanillaJS

VanillaJS는 자바스크립트 라이브러리와 프레임워크 홍수를 풍자하는 단어이다. 

Java에서 POJO와 비슷한 개념이라 볼 수 있다.


순수한 JavaScript만을 이용하여 구현한?(구현해야 할) 프레임워크다. 

별도의 라이브러리 없이 순수한 JavaScript만을 사용했기 때문에 가장 빠르고, 가벼운 장정이 있다.


http://vanilla-js.com/ 에서 사용하고 싶은 컴포넌트를 선택 후 다운받아 소스를 열어보면 그 의미를 더욱 잘 알 수 있다.



Angular VS React.js ? (2016 데뷰 정리 - 영상 , 발표자료)

위에서 설명했듯이 Angular는 프레임워크고 React.js는 라이브러리다. 

둘을 1:1로 비교하는것은 무리가 있고 Angular VS React Family(React.js + Redux + React router + Babel) 정도로 비교해야할 것이다.

2016년 데뷰발표자료에 사견을 더해 둘을 비교해 봤다.

1. 성능

https://github.com/CoderK/js-framework-benchmark 로 성능 평가 해본 결과 

 

 Angular

React 

Vanilla 

 상대 시간

 2.12

2.00 

 1.00

 메모리 사용량

 2.67

1.79 

 1.00

의미있는 속도차이는 없었음. 엔터프라이즈급 서비스 개발을 위해서 생산성에 좀 더 초점을 두어야 할것 같다.

학습 비용은 비슷하다고 생각함.

2. 언어 생산성

최근 Angular CLI 까지 나오면서 개발 환경과 학습 비용은 Angular가 낮다고 생각한다. 

또한, Angular가 사용되는 곳이 많아지면서 (웹앱, 앱, 데으크탑 앱) 사용성도 증가되고 있다. 


 Angular

 React

  •  구글은 웹표준을 철저히 지키는 회사. 
  •  새로운 언어를 배운다는 개념보다 Next Es 라고 생각하자
  •  구글이 만든게 표준이 아니면 표준을 추가하려고한다...?!
  •  TypeScript는 지나친 비용을 야기한다. 
  •  타입 체커 라이브러리인 FLOW면 충분하다.
  •  언어 생산성 측면에서 JavaScript 언어의 한계가 존재 했고 TypeScript가 탄생했다. 
  •  Angular 와 React 모두 TypeScript를 사용할 수 있지만, React는 타입을 지정해 주는 체커형태 라이브러리인 FLOW를 더 많이 사용하는 편

3. 컴포넌트

 Angular

 React

  • Angular는 HTML 과 JS CSS 를 잘 분리했고 CSS 캡슐화를 내부적으로 지원해서 높은 이식성과 재사용성을 보장한다.
  • JSX를 쓰면 마크업이 JS안으로 들어가는 형태. 코드를 실행하기 전까지 마크업 미리보기가 안된다. 또한 자바스크립트를 모르는 마크업 개발자와 협업할 경우 협업 자체가 매우 어렵다.
  • Angular는 표준 HTML에 디렉티브를 확장해나가는 개념이라 기존 HTML 문법을 그대로 사용할 수 있다.(React는 className, defaultValue 같은 문법 사용)
  • React역시 Webpack를 사용하면 CSS 캡슐화를 지원할 수 있지만 네이티브로 지원하지 않은 것은 조금 아쉬운 부분인건 사실.
  • 협업이 어려운점은 동의한다. 하지만 Angular도 Html에 논리코드(디렉티브)를 끼워넣어야 함으로 구조 안에 행위를 집어 넣는 순간 순수하지 못하다.
  • 사실 구조와 기능을 분리할 수 없다. 그래서 통합하는 것을 목적으로 했고, JSX 는 기존 javascript에서 DOM을 생성하던 것의 단점을 보안하고 가독성을 높인 결과물이다.
  •  컴포넌트 = HTML + JS + CSS
  •  CSS분리에 대해서는 동의하지만 JSX의 존재에 대하여선 대립 

4. 데이터 동기화

 Angular

 React

  • Angular의 경우 양방향 바인딩으로 성능이슈가 있었지만 Angular2와 React는 상당히 닮아있고 성능부분에서도 비슷해짐.
  • Angular는 React의 Virtual DOM을 보고? 체인지 디렉터라는 것을 만들었다. 마찬가지로 모델이 바뀌면 트리구조의 DOM을 훑으면서 달라진 부분만 다시 그린다.
  • Angular는 조금 더 나아가서 Zone(실행영역)이란 것을 이용해서 DOM 변경시 setState, setValue를 개발자가 명시하지 않아도 자동으로 모델을 변경시켜준다. (React 는 DOM -> Model 은 명시해줘야함)
  • React는 Virtual DOM을 이용하여 모델이 바뀌면 새로운 Virtual DOM을 그리고 기존 Virtual DOM 과 비교해서 Diff 된 부분만 새로 그려준다.
  • 뷰와 모델의 분리 후 데이터 동기화 문제 등장. 
  • Angular의 경우 양반향 바인딩으로 성능 이슈가 있었지만, Angular2로 넘어오면서 개선된 상태

5. 비동기 처리

 Angular 

 React 

  • 비동기 처리를 위해 RxJS를 품었다. ZONE을 이용한 DOM -> MODEL 로 데이터 전송처럼 Angular 자체적으로 RxJS는 잘 품었다.
  • Javascript가 가진 다른 대안도 많은 와중에 왜 RxJS를 품은 것인지는 더 지켜봐야할 일
  •  React는 라이브러리인 만큼 Angular보다 자유롭게 비동기 라이브러리를 쓸 수 있다.
  • 페이스북은 리엑트를 라이브러리로 지원하기 때문에 RxJS를 포함하려고 하는 움직임은 없지만 커뮤니티에서 활발히 진행 중. 
  • 또한 React와 함께 쓰는 Redux와 RxJS 는 궁합이 좋고 NetFilx에서 사용한 예가 있다.
  • Angular가 RxJS를 플레임워크로 품은것은 관심가지고 볼 사항이다.




Node.js의 탄생과 JavaScript라는 언어의 범용성이 커짐에 따라 JavaScript의 테스트 환경은 점점 더 중요해졌다.

JavaScript가 서버언어로 사용되면서 JavaScript 테스트는 e2e 테스트 뿐만아니라 BDD 또는 TDD가 가능한 유닛 테스트가 필요했고,

기존 테스트 환경은 발전했고, 새로운 테스트 프레임워크들이 탄생했다.

 

여러종류의 테스트 프레임워크 중 Mocha, Jasmine, QUnit을 비교해보자.

결론

테스트는 개발을 돕기위한 도구일 뿐 목적이 아님으로 개발 환경에 맞고 사용하기 편한 툴을 선택하면 된다.
테스트 속도 차이는 미비하고, 대부분의 툴들이 비동기 테스트를 지원함으로 테스트 툴간 성능비교는 무의미 한 것 같다.

자바스크립트 프레임워크를 사용하지 않고 Jquery만 사용한다면 Qunit으로 간단하게 테스트 환경을 구축하면 되고
Angular와 같은 자바스크립트 프레임워크를 사용한다면 Mocha와 Jasmine중 선택하면 될 것이다.

TDD를 생각한다면 DOM 이 필요 없는 Jasmine을 확장성 높은 테스트 환경을 생각한다면 Mocha를 선택하면 좋겠다.
나는 테스트 기능에만 충실하고 별도의 Dependency 설정이 필요없어 사용하기 쉬운 Jasmine을 선택하고 싶다.

테스트 비교

 

 Mocha 

 Jasmine

 Qunit 

 버전

 3.4.0

 2.6.1

 2.3.2

 인기

 중간 

 높음 

 낮음 

 assertion 라이브러리

 chai 라는 외부 라이브러리 사용

 내장

 내장

 러너

 Karma 가능 

 Karma 가능 (Python, Ruby)  Karma 가능 

 난이도

 보통 (3rd party library 필요, 유연함)

 쉬움   가장 쉬움 

 커뮤니티

12.3K github Stars, 4.45K stack over flow 

 12.4K github Stars, 8.01K stack over flow

 3.63K github Stars, 1K stack over flow

 특징

 Simple, flexible, fun javascript test framework for node.js & the browser  DOM-less simple JavaScript testing framework

 A JavaScript Unit Testing framewor

테스트 문법

 Mocha 

 

var assert = require('assert');
describe('Array', function() {
describe('#indexOf()', function() {
it('should return -1 when the value is not present', function() {
assert.equal(-1, [1,2,3].indexOf(4));
});
});
});
 Jasmine
 

describe("A suite is just a function", function() {
var a;
it("and so is a spec", function() {
a = true;
expect(a).toBe(true);
});
});

 Qunit 
 


<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>QUnit Example</title>
<link rel="stylesheet" href="https://code.jquery.com/qunit/qunit-2.3.2.css">
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<script src="https://code.jquery.com/qunit/qunit-2.3.2.js"></script>
<script src="tests.js"></script>
</body>
</html>

 

QUnit.test( "hello test", function( assert ) {
assert.ok( 1 == "1", "Passed!" );
});

사람들이 좋아하는 이유(출처 https://stackshare.io/stackups/jasmine-vs-mocha-vs-qunit)

 Mocha

 Jasmine 

 Qunit 

 105 오픈소스

 50 TDD 로 사용할 수 있음

 5 단순함

 77 단순함

 39 오픈소스

 3 오픈소스

 62 Promise 지원

 14 RSpec 표준

 3 세팅하기 쉬움

 32 유연함  11 DOM조차 필요없는 독립성

 2 Promise 지원

 18 사용하기 쉬움

 10 훌류한 커뮤니티

 
 7 브라우저와 서버 테스트  5 세팅하기 쉬움  

 2 다른 좋은 대안이 없음

 3 단숨함  
 

 2 Pivotal-Labs에서 개발함

 

Stackshare 특징비교 (https://stackshare.io/stackups/jasmine-vs-mocha-vs-qunit)

구글 트렌드
























Angular를 시작하기 위해 문서를 찾아보면 대부분 편집 툴로 Visual Studio Code 를 추천한다.

처음 접해보는 용어와 Node라는 런타임, 그리고 npm 사이에서 새로운 편집툴은 Angular의 진입 장벽을 더욱 높인다.

평소 사용하고 있는 IDE인 IntelliJ에서 Angular2(이하 Angular) 프로젝트를 생성할 수 있으면 조금이나마 진입 장벽을 낮출 수 있을것 같아서 내용을 정리한다.

해당 문서는 Window 환경을 기준으로 작성했다.


1. Node.js 설치

https://nodejs.org/ko/ 에 접속하여 stable 버전인 v6.10.3 LTS 를 다운받고 설치한다.


Node.js라는 개념이 쉽게 와닿지 않는다.  노드란 무엇일까?

"Node.js 는 Chrome V8 JavaScript 엔진으로 빌드된 JavaScript 런타임" 이라고 공식페이지에서 말한다.

런타임이란 프로그램이 실행되고 있을 때 존재하는 곳을 뜻하는데, 우리가 익숙하게 사용하고 있는 JRE를 떠올리면 쉽다.

JRE는 Java Runtime Environment의 준말로 우리가 개발한 JAVA 어플리케이션을 구동하기 위한 환경인 것이다.

이처럼 노드는 JavaScript로 개발된 어플리케이션을 구동하기 위한 환경이다.


또한 Node.js의 패키지 생태계를 지원하기 위해 npm 이란 것이 있다.

리눅스에서 흔히 사용하는 yum, apt-get, rpm과 비슷한 개념의 패키지 매니져이며 npm은 JavaScript를 위한 패키지 매니저라고 보면 된다.

Node.js를 설치할때 npm도 같이 설치 된다.


2. Angular CLI 설치 (https://cli.angular.io/)


cmd 창에서 

npm install -g @angular/cli@latest 

npm install --save-dev @angular/cli@latest


여기까지 설치 후 cmd 창에서 ng -v를 날려보면 다음과 같은 버전 정보가 나와야한다.

#최근 angular 패키지의 이름이 변경되었고, 이전버전을 디프리케이트 했다. 

골뱅이가 붙어서 시작하는게 변경된 버전이니 책이나 구버전 문서들을 볼때 주의해야겠다.


Angular 프로젝트 생성, 빌드, 테스트를 도와주는 command line interface(CLI)이다.

Angular CLI 로 프로젝트를 생성하면 각각 따로 설정해줘야하는 polyFill, tsConfig, typings, karma 등등 각종 의존성등을 설정해준다.

NodeJs를 처음 접하면 온갖 의존성 관리에 좌절하기 마련인데, 이러한 문제를 

Angular CLI는 가장 최적화된 상태로 프로젝트를 생성함으로써 진입 문턱을 낮춰준다.


이밖에 쉽게 테스트할 수 있는 로컬 서버과 테스트까지 제공해주니 각각 따로 세팅하는것 보다 Angular CLI 사용하는것을 추천한다.


3. IntelliJ 플러그인 설치

 

IntelliJ에는 Angular 프레임워크가 없다. (Web Storm 에는 있음)

플러그인으로 설치 하기 위해 IntelliJ Settings -> Plugins -> Browse Repositories -> AngularJS 를 검색 설치한다.

재시작 후 File -> New -> Project -> Static Web 을 보면 Angular CLI 를 확인할 수 있다. 


4. 프로젝트 생성


File -> New -> Project -> Static Web 을 보면 Angular CLI 후 Next를 클릭하면,

프로젝트 이름과 위치를 지정할 수 있으며, Node Interpreter 와 Angular CLI를 선택 할 수 있다. 

Node 는 1번 단계에서 설치한 node.exe.의 위치를, Angular CLI 는 npm으로 설치한 Angular CLI 패키지 위치를 지정해주면된다.

프로젝트 생성을 마치면 IntelliJ가 자동으로 Angular CLI에 new 명령어를 날려 필요한 라이브러리들을 다운받고 설치한다.

 

 

 

Angular CLI 위치가 목록에 보이지 않는다면 다음 사이트를 참조해보자.

https://www.jetbrains.com/help/idea/2017.1/using-angular.html

http://revf.tistory.com/entry/IntelliJ%EC%97%90%EC%84%9C-Angular2-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-with-Angular-CLI


5. output 세팅


AngularJs는 타입스크립트를 사용하기 때문에 타입스크립트를 자바스크립트로 컴파일 하는 빌드과정이 필요하다.

IntelliJ 터미널 또는 좌측 하단 npm 창에서 build 를 클릭하면 명시적으로 컴파일할 수 있다. 

이때 컴파일 결과물들을 임의의 위치에 저장하려면  angular-cli.json 설정을 수정하면 된다.

추후 스프링과 연동할때 필요할것으로 보이므로 관심이 있으면 수정해보자.

"outDir": "dist",  --> "outDir": "output",  

 

빌드 시작

 

빌드 성공

 

빌드 결과물

 

6. 프로젝트 실행


생성한 프로젝트를 실행하기 위해 Angular CLI 는 serve 명령어를 지원해준다.

IntellJ의 npm 창에는 start로 표시되어있는데, 클릭해보면 터미널 창에서 ng serve 명령어를 날리는 것을 볼 수 있다.

ng serve 를 하면 콘솔창에서 연결정보 (localhost:4200)를 확인 할 수있다.

(ng serve 는 ng build 를 포함한다.)

 



7. 실행 확인


별다른 설정을 하지 않았다면, ng serve로 시작한 어플리케이션은 라이브 코딩을 지원한다. (자동 빌드)

src/app/app.component.ts 의 title 변수를 변경하고 저장하면 자동으로 업데이트 되는것을 확인 할 수 있다.

 




'ANGULAR' 카테고리의 다른 글

Angular Change Detection & Zone.js  (455) 2017.06.01

목적



윈도우 로컬환경에서 셀레늄 테스트 모듈을 구현하는 것은 비교적 쉽게 구현할 수 있다.

문제는 커맨드 환경인 리눅스 서버에서 가상 모니터를 띄우고 젠킨스와 연동하여 테스트를 자동화 하는것이다.

때문에 CentOS 환경에서 셀레늄 테스트를 자동화 하는데 초점을 맞추어 가이드를 썼다. 

해당 포스트는 아래 내용을 포함한다.

  • Selenium을 이용하여 웹 테스트 모듈을 구현.
  • CentOS와 Jenkins를 이용하여 셀레늄 웹 테스트를 자동화.

테스트 환경



최대한 최신환경을 사용하려 노력했다.


셀레늄 : 3.2.0 (selenium-java, selenium-chrome-driver, selenium-server)

브라우저 : Chrome 56.0.2924.87 (64-bit)

크롬 드라이버 : ChromeDriver 2.27

스프링 : 3.2.17.RELEASE

CentOS : CentOS Linux release 7.1.1503 (Core)

Jdk : 1.8.0_101 (Oracle)

테스트 구현



1.Spring Maven 프로젝트를 생성하여, 테스트환경을 구성한다.


2.셀레늄 테스트를 위하여 pom.xml에 라이브러리 의존성을 설정한다.

<!-- pom.xml selenium -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-chrome-driver</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-server</artifactId>
<version>3.2.0</version>
</dependency>


3.테스트를 작성한다.


  setUp() 에서 크롬 드라이버 위치 지정

  테스트 내용은 구글에서 "어쩌다, 블로그"를 검색 

  -> 검색 결과중  "어쩌다, 블로그"라는 문자열을 가진 링크를 10초간 대기하면 찾기

  -> 클릭

public class SeleniumSampleTest {

private WebDriver driver;

@After
public void tearDown() throws Exception {

Thread.sleep(5000);
driver.quit();
}

@Before
public void setUp() throws Exception {

System.setProperty("webdriver.chrome.driver", "D:\\gecko\\chromedriver.exe");
driver = new ChromeDriver();
}

@Test
public void testGetGoogle() throws Exception {

driver.get("http://www.google.com");
WebElement searchBar = driver.findElement(By.cssSelector("#lst-ib"));
searchBar.sendKeys("어쩌다, 블로그");
driver.findElement(By.cssSelector("#_fZl")).click();
WebElement bkim = new WebDriverWait(driver, 10).until(ExpectedConditions.elementToBeClickable( linkText("어쩌다, 블로그"))); bkim.click();
} }


4.테스트 정상작동확인.

리눅스에서 셀레늄 환경 구성 하기



CentOS 메이븐과 젠킨스가 설치되어 있다는 가정하에 진행한다.

젠킨스가 아니더라도 메이븐 테스트를 진행하는 방법은 많다. 

아래 가이드하는 환경 설정 후 메이븐 테스트를 위해 다른 툴을 사용하여도 무방하다.


1. 혹시 GCC가 깔려있지 않으면 설치해주자. 크롬 실행 시 필요한 라이브러리들을 가지고있다.


2. 크롬 설치 (https://tecadmin.net/install-google-chrome-in-centos-rhel-and-fedora/)


위 링크에서 크롬을 설치하여도 GUI 환경이 준비되지 않은채 google-chrome 명령어를 날리면

browser_main_loop.cc(272)] Gtk: cannot open display 와 같은 에러를 뱉으며 크롬이 실행 되지 않는다. 우선 설치만 해두자.


3. 크롬 드라이버 다운로드 (https://chromedriver.storage.googleapis.com/index.html?path=2.27/)


위 링크에서 환경에 맞는 드라이버를 다운로드 후 적절한 경로에 다운 받은 후,

테스트 소스에서 chrome.driver 경로를 맞춰주자.


4. Xwindows 설치 (http://chandrewz.github.io/blog/selenium-on-centos)


위 링크를 참고하거나 아래 명령어를 수행한다.


설치

$ sudo yum install Xvfb libXfont Xorg
$ sudo yum groupinstall "X Window System" "Desktop" "Fonts" "General Purpose Desktop"

99번 포트로 Xwindows 실행 

$ Xvfb :99 -ac -screen 0 1280x1024x24 &

 99번 포트로 XWindows display 포트 지정

$ export DISPLAY=:99

 이제 다시 google-chrome 명령어로 크롬을 실행해보면 cannot open display  와같은 에러가 사라진것을 알 수 있다.

젠킨스 연동



1. 테스트로 만든 maven 프로젝트를 서버로 업로드한다.

   (메이븐으로 테스트를 돌릴것이기 때문에 따로 빌드하여 올릴 필요는 없다.)


2. Jenkins에서 Freestyle project를 하나 생성한다.


3. 프로젝트 이름과 로그 로테이션을 각각 설정한다.


4. 빌드 환경에서 테스트 전후로 Xwindow를 사용할 수 있도록 Start Xvfb before the build, and shut it down after. 를 반드시 체크한다.




5. Build 단계로 이동하여 Execute shell 항목에 테스트를 실행할 쉘 명령어를 입력한다.

  2번단계에서 소스 업로드한 폴더로 이동 (cd /home1/irteam/deploy/test)

  메이븐으로 테스트 실행  -Dtest=클래스명#메소드명 test  (mvn -Dtest=SeleniumSampleTest#testGetGoogle test)

cd /home1/irteam/deploy/test
mvn -Dtest=SeleniumSampleTest#testGetGoogle test

6. 테스트 수행 및 테스트 성공 확인.

마치며



셀레늄은 브라우저를 포함한 사용자 환경을 그대로 재현하여 테스트하기 때문에, 클라이언트에 가장 가까운 테스트 툴이라 할 수 있다.
그 특성때문에 리눅스 서버에서 수행하기에는 어려움이 따른다. 이를 해결하고자 삽질했던 내용들을 블로그에 남겼다.

리눅스에서 셀레늄을 돌리고자하는 분들의 삽질에 도움이 되길바란다.


☞ 관련하여 어떤 내용이듯 댓글로 달아주시면 답글 드리겠습니다. 감사합니다.

+ Recent posts