Quantcast
Channel: Uncle Bae's Story
Viewing all 50 articles
Browse latest View live

[Apache Spark] 아파치 스파크 요약

$
0
0

1. Apache Spark

- Big Data 분석 엔진이다.
- 메모리에서 수행 혹은 Disk에서 수행이 가능하다.
  - 기존 Map Reduce 보다 100배 빠르다.
  - 디스크보다 10배 더 빠르다.
- 배치프로세스 지원, 반복적인 분석이 가능하다.
- Python, Scala, Java 등 다양한 API를 지원한다.
- HDFS 프렌들리한 인터페이스
- Real-Time 프로세싱 지원(스톰과 유사하다)
- Graph Analytics, Machine Learning, SQL지원
- 형식화된 툴셋 지원
- 파이프라인 기반의 처리 지원

2. 스파크 구조

3. RDD (Resilient Distributed Dataset)

3.1 RDD란 ? 

- 스파크에 핵심 코어 데이터 셋
- 스파크에서 RDD를 위한 인터페이스 제공
- 2개의 RDD타입이 있음
  - Transformations
  - Actions
그림 설명 :
- 파티션된 객체의 참조를 가지고 있음
- 각 파티션 객체는 데이터의 subset을 참조한다.
- 파티션들은 클러스터 상의 각 노드에 할당되어 있다.
- 각 파티션은 기본적으로 RAM에 분리되어 존재하게 된다.

3.2 RDD의 활용

- Lineage (Required)
  - 각 HDFS블록당 파티션들의 셋이다. 
  - dependencies의 리스트이다. 
  - 각 파티션의 함수를 수행한다. 이는 HDFS 블록으로 부터 읽어들인 데이터이다. 

- Execution optimizations (Optional)
  - 파티셔너 (해시, 범위 등등..) 을 통해서 파티션을 수행함
  - NameNode로 부터 HDFS블록 위치가 선택이 되어 파티셔닝된다. 

3.3 파티션된 데이터셋의 처리과정

- 데이터셋은 파티션으로 분리된다. 
- 이 파티션들은 각 워커의 메모리에 저장이 된다. 

4. RDD 생성방법 

hdfsData = sc.textFile("hdfs://data.txt")

5. Spark에서 I/O 지원

- HDFS
- CSV, Tab-delimited, TXT
- JSON
- SequenceFile
- Hive
- JDBC
- HBase, S3, Cassandra

6. RDD Transformation

  - RDD의 서브셋을 의미한다. 
  - 새로운 RDD의 포인터를 반환한다. 



6.1 Single RDD Transformation

6.1.1 Filter 
  male1, male2, female1 --> female1
6.1.2 map :
2, 5, 6 --> 4, 25, 36
6.1.3 flatMap :
my name is ray --> my, name, is ray
6.1.4 distinct : 
apple, apple, banana --> apple, banana
6.1.5 sample :
apple, banana, guava --> banana, apple

6.2 Multiple RDD Transformation

Sample : 
A Set :
  apple, orange, banana
B Set :
  guava, banana, pear

6.2.1 Union :
  apple, orange, banana, guava, banana, pear
6.2.2 Intersection : 
banana
6.2.3 substract (A - B)
apple, orange
6.2.4 cartesian
(apple, guava), (apple, banana), ...
6.3 Pair RDD Transformation Example
Sample : myRDD
name : age
joe     : 12
sara   : 31
sara   : 40
juan   : 85
Example : groupBykey()
myRDD.groupByKey()
joe     : [12]
sara   : [31, 40]
juan   : [85]
6.3.1 pair RDD의 종류
6.3.1.1 reduceByKey
6.3.1.2 groupByKey
6.3.1.3 combineByKey
6.3.1.4 mapValues
6.3.1.5 flatMapValues
6.3.1.6 keys
6.3.1.7 values
6.3.1.8 substractByKey
6.3.1.9 join
6.3.1.10 rightOuterJoin
6.3.1.11 leftOuterJoin
6.3.1.12 cogroup
6.3.1.13 sortByKey

7. Laziness Concept

  - Lazy 실행 프레임워크를 지원하여 효과적으로 프로세스를 처리한다. 
  - 21세 이상되는 남자수를 카운트 하라고 하는 경우 스파크는 매우 스마트하게 동작한다. 
    모든 male 취합하고 21세 이상을 걸러낼 필요 없이 결과를 도출한다. 

8. RDD Actions

  - 계산식을 말한다. 
  - 결과를 다시  HDFS로 보내거나 드라이버로 보낼 수 있다. 


8.1 Common RDD Action
8.1.1 count
8.1.2 countByValue
8.1.3 collect
8.1.4 take(n)
8.1.5 reduce(func)
8.1.6 foreach(func)
8.1.7 saveAsTextFile

8.2 Common "Pair" RDD Action
8.2.1 countByKey
8.2.2 collectAsMap
8.2.3 lookup(key)

9. Persistence

  - 기본적으로 스파크는 액션이 호출되는 매번 RDD를 재 계산한다. (재 계산되지 않는 것은 Persisted된다.)
  - RDD가 메모리에 캐시된 상태로 존재하거나 디스크에 있게 할 수 있다. 캐시된 RDD는 파티션 되어 있다. 
  - 정말 대단한 것은 동일한 RDD를 많이 계속해서 수행할 수 있다.

10. The Lineage Concept (혈통, 계보)

- RDD 장애에 대한 처리를 위한 대응 시스템이다. 
-  최종 ShuffledRDD는 2갈래 계보를 따라 생성된 것을 확인할 수 있다. 

11. Accumulator

-  MapReduce의 Counter와 유사하다.
- 글로벌 변수는 스파크 프로그램을 디버깅을 위해서 트래킹 할 수 있다. 
- Executors는 각각 커뮤니케이션 할 수 없기 때문에 이러한 Accumulator가 필요하다. 
- 즉, 글로벌 값을 말한다. 

12. Broadcast Variables

- MapReduce의 Distributed Cache와 유사하다. 
- 이는 워커 노드에게 읽기 전용의 변수값을 전달한다. 
- 룩업테이블, 딕셔너리 등을 위해 사용한다. 
- 즉, 다양한 노드들이 이 설정값 등을 읽을 수 있으면 이를 참조해서 계산에 이용될 수 있다. 

13. StatsCounter(통계 카운터)

- 프로세스를 마치고 나면 통계 카운터가 생성이 된다. 
13.1 count
13.2 mean
13.3 sum
13.4 min, max
13.5 variance
13.6 stddev

14. DataFrames

- 2015년 2월 부터 릴리즈된 API이다. 
- MySQL의 테이블과 유사함, R에서 DataFrame임
- 파이썬의 DataFrame는 Python RDD에 비해서 5배나 빠르다. 
- Hive, HDFS, MySQL, PostgreSQL, JSON, S3, Parquet, OpenStack...

15. YARN-based Architecture



16. Tuning with SparkConf

- Configures를 위한 클래스와 스파크 잡을 튜닝할 수 있는 클래스
- 키/밸류 쌍을 가진다.
- 드라이버 내에서 코드로 존재하거나 커맨드 라인으로 튜닝 가능

16.1 튜닝 예제 : 
cons = new SparkConf()
conf.set(“spark.executor.memory”, “1g”)
sc = SparkContext(conf)

17. MLib를 이용한 Machine Learning

- Native Machine Learning Framework
- Classification, Regression, Clustering
- Chi-Square, Correlation, Summary Stats
- Automatic Algorithm Parallelization
- Pipeline API (train --> test --> eval)

17.1 MLib Example
model = LinearRegressionWithSGC.train(data, iterations = 100, intercept = True)

18. Spark SQL

- Native SQL Interface to Spark
- Hive, DataFrame, JSON, RDD
- JDBC Server(B.I Interface)
- UDF(User Define Function)s (Spark SQL and Hive)
- Columnar storage, Predicated Pushdown, Tuning options

18.1 Spark SQL Example
hiveCtx = HiveContext(sc)
allData = hiveCtx.jsonFile(filein)
allData.registerTempTable(“customers”)
query1 = hiveCtx.sql(“SELECT last, first FROM customers ORDER BY last LIMIT 50”)

19. GraphX Example (PageRank)

- Native Graph Processing Framework
- Pregel, Giraph, GraphLab와 유사
- 네트워크 기반의 분석을 위해 설계됨
  - 트위터 Analysis
  - PageRank
- Spark보다 16배 빠름
- MapReduce보다 60배 빠름
- 표준 스파크의 RDD Transforms를 이요함

19.1 GraphX Example (PageRank)
graph = GraphLoader.edgeListFile(sc, “followers.txt”)
ranks = graph.pageRank(precision).vertices

20. Spark Streaming

- RealTime 분석 (Apache Storm과 유사)
- Shopping Cart추천 (아마존)
- Micro-Batch 아키텍쳐
- Windowing
- Fault tolerance를 위한 체크포인트

20.1 Spark Streaming Word Count예
lines = sac.socketTextStream(host, port)
words = lines.flatMap(lambda line: line.split(“ “))
pairs = words.map(lambda word: (word, 1))
wc = pairs.reduceByKey(lambada x, y: x + y)
wc.pprint()
ssc.start()
ssc.awaitTermination()

출처 : 
-- https://www.youtube.com/watch?v=rvDpBTV89AM

앞으로 공부해야할 사이트 :
-- http://backtobazics.com/big-data/spark/apache-spark-reducebykey-example/





[Spark] Spark Install Guide and Standalone mode start

$
0
0

Apache Spark Install Guide 

1. 스파크 다운로드 및 구성요소 

1. Apache-Spark 다운로드

http://spark.apache.org/downloads.html로 이동한다.

2. 다운로드 버젼 설정 

스파크 최신 버젼을 선택했다.

사전빌드(Pre-build)이고, 대부분의 하둡 배포판에서 이용할 수 있다고 한 두번째 항목은 선택했다.
필요한경우 이미 설치되어 있는 적합한 하둡 버젼을 다운받으면 된다. 

# 참고 : 하둡은 반드시 설치 되어 있지 않아도 된다.

3. 다운로드 받은 파일 압축 풀기. 

txr -xvf spark-1.6.1-bin-without-hadoop.tgz
ln -s spark-1.6.1-bin-without-hadoop spark
vim ~/.bash_profile
export SPARK_HOME=/Users/KIDO/Documents/00.TOOLS/spark
export PATH=$JAVA_HOME:$MYSQL_HOME:$ACTIVATOR_HOME:$SPARK_HOME/bin:$PATH
상기 항목을 입력하고 :wq로 빠져나온다.

source .bash_profile

4. 스파크 디렉토리 둘러보기


bin
  - 실행 명령이 들어있다.
conf
  - 스파크 설정을 위한 설정파일
  - 스파크 환경설정, log설정, docker관련 설정, slave관련 설정등 다양하다.
examples
  - 스파크를 사용한 다양한 예제를 살펴볼 수 있다.
lib
  - 스파크에 필요한 라이브러리들이 존재한다.
  - spark-yarn 셔플 라이브러리, 하둡 라이브러리등이 있다.
python
  - 파이선 스파크를 위한 다양한 라이브러리와 스크립트가 있다.
R
  - spark R을 위한 라이브러리
sbin
  - 스파크 마스터서버 및 슬레이브 서버 실행 명령

2. Spark Standalone Mode 

출처 : http://spark.apache.org/docs/latest/spark-standalone.html

스파크는 Mesos혹은 YARN 상에서 수행이 된다. 그러나 Standalone모드를 지원하고 있다.
스탠드 얼론 모드는 하나의 클러스터에서 마스터와 워커를 수동으로 실행 할 수 있도록 제공한다.

1. 클러스터 수동 실행 

1.1 마스터 서버 실행하기
./sbin/start-master.sh
마스터를 실행하면 스파크는 ://HOST:PORT의 형식으로 URL을 프린트한다.
이 정보를 이용하여 마스터 서버에 접근하거나 SparkContext로 해당 정보를 전달 할 수 있다.
또한 마스터의 WEB-UI는 http://localhost:8080으로 접근할 수 있다.

1.2 슬레이브 워커 실행하기.
./sbin/start-slave.sh <master-spark-URL>
슬레이브 워커를 수행하면 master WEB-UI에서 신규 노드가 접속되었음을 확인할 수 있다.
또한 CPU와 메모리 정보등을 직접 확인할 수 있다.

1.3 마스터와 슬레이브 설정 파라미터
ArgumentMeaning
-h HOST--host HOST리슨할 호스트명을 설정한다.
-i HOST--ip HOST리슨할 호스트 이름을 설정한다. (deprecated됨, -h나 --host이용)
-p PORT--port PORT리슨할 서비스의 포트번호 (기본 : 7077 마스터, worker은 랜덤)
--webui-port PORTweb UI를 위한 포트 (기본 마스터 : 8080, 워커 : 8081)
-c CORES--cores CORES스파크 어플리케이션이 사용할 CPU코어의 총 개수,
머신에 따라 다르게 설정될 수 있다. (기본 : all available)
이것은 worker에만 설정한다. 
-m MEM--memory MEM스파크 어플리케이션이 사용할 총 메모리 양,
머신에 따라 다르게 설정될 수 있다. (기본 : 머신 총 RAM - 1GB)
형식 : 1000M 혹은 2G 등으로 설정
이것은 worker에만 설정한다. 
-d DIR--work-dir DIR작업에 사용할 공간과 job가 출력할 로그를 저장할 디렉토리
(기본 : SPARK_HOME/work)이며 worker에만 설정한다. 
--properties-file FILE로드할 커스텀 Spark 프로퍼티 패스를 지정한다.
(기본 : conf/spark-defaults.conf)

2. 클러스터 런치 스크립트 

스파크를 스탠드 얼론 모드로 스크립트로 수행하기 위해서는, conf/slave 라고 불리는 파일을 생성해야한다. 이는 spark디렉토리 내부에 존재한다.
이 파일에는 스파크 워커가 실행할 모든 머신의 호스트 명이 기술 되어있어야 한다.
만약 conf/slave가 존재하지 않는다면  lunch script는 기본적으로 단일 머신에서 수행된다. (localhost)
이는 테스팅을 위해서 유용하게 사용된다.
마스터 머신은 각 워커 머신에 ssh를 이용하여 접근하는것이 일반적이다. 기본적으로는 ssh를 이용하며, 이를 이용하면 password없이 서버간 access가 가능하다.
만약 password-less설정을 하지 않았다면 SPARK_SSH_FOREGROUND 변수값에 각 워커를 위한 password를 설정할 수 있다.

설정을 완료하고 나면 launch나 stop를 다음 쉘 스크립트를 통해서 수행할 수 있다. 이 쉘들은 SPARK_HOME/sbin에 들어있다.
  • sbin/start-master.sh - 마스터 인스턴스를 실행한다. 
  • sbin/start-slaves.sh - conf/slaves 파일에 지정된 머신에 슬레이브를 실행한다. 
  • sbin/start-slave.sh - 머신내에 슬레이브 인스턴스를 실행한다. 
  • sbin/start-all.sh - 상단에 기술한 모든 마스터와 슬레이브를 실행한다. 
  • sbin/stop-master.sh - bin/start-master.sh스크립트를 통해 실행한 마스터 인스턴스를 종료한다. 
  • sbin/stop-slaves.sh - conf/slaves파일에 지정된 머신에서 슬레이브를 종료시킨다. 
  • sbin/stop-all.sh - 마스터와 슬레이브 모두 종료한다. 
주의할 것은 상기 스크립트는 마스터 노드에서 실행해야한다. 슬레이브 노드에서 각각 실행해서는 안된다.

클러스터의 설정은 conf/spark-env.sh에 있는 환경 변수에 값을 설정하여 선택적으로 수행할 수 있다.
이 파일은 conf/spark-env.sh.template를 복사하여 모든 워커 머신에 설정값을 지정하면 된다.

환경변수 목록 : 
Environment VariableMeaning
SPARK_MASTER_IP특정 IP주소에 마스터를 바인딩한다. 
SPARK_MASTER_PORT다른 포트로 마스터를 실행한다. (기본 : 7077)
SPARK_MASTER_WEBUI_PORT마스터 web UI용 포트 (기본:8080)
SPARK_MASTER_OPTS마스터에 설정될 옵션들을 설정한다.
형식 : "-Dx=y"의 형식으로 설정한다.
아래 가능한 옵션 참조. 
SPARK_LOCAL_DIRS스파크가 사용할 디렉토리를 지정한다. 이것은 map 출력파일, RDD 등을 저장한다. 성능을 위해서는 시스템 내의 로컬 디스크에 저장하는것이 좋다. 콤마를 이용하여 서로다른 디스크상의 디렉토리를 나열할 수 있다. 
SPARK_WORKER_CORES스파크 어플리케이션이 머신내에서 사용할 총 코어의 개수를 지정한다. (기본값 : all avaliable cores)
SPARK_WORKER_MEMORY스파크 어플리케이션이 머신내에서 사용할 총 메모리의 양을 지정한다.
형식 : 1000m, 2G (기본적으로 총 메모리 - 1G를 설정한다.)
각 어플리케이션들의 개별적인 메모리 설정은 spark.executor.memoryproperty를 이용하여 설정할 수 있다. 
SPARK_WORKER_PORT특정 포트로 스파크 워커를 실행한다. (기본값 : random)
SPARK_WORKER_WEBUI_PORT워커의 web UI를 수행할 포트를 지정한다. (기본값 : 8081)
SPARK_WORKER_INSTANCES각 머신에서 수행할 워커 인스턴스의 개수를 지정한다. (기본 : 1)
1 이상을 설정할 수 있으며, 매우 큰 머신에서 이용하거나, 복수개의 워커 프로세스를 두고자 할때 이 값을 증가 시킬 수 있다.
이 값을 변경한경우에는 SPARK_WORKER_CORES 를 함께 확인할 필요가 있다. 이것은 각 워커의 워커를 명시적으로 제한하기 때문이다.
혹은 각 워커는 모든 코어를 사용할 수 있다. 
SPARK_WORKER_DIR어플리케이션들을 수행할 디렉토리를 지정한다. 이곳에는 로그와 워커 작업 공간으로 이용된다. (기본 : SPARK_HOME/work)
SPARK_WORKER_OPTS워커에 대한 설정값을 지정하고자 할때 사용한다.
형식 : "-Dx=y"
아래 옵션을 참조하자. 
SPARK_DAEMON_MEMORY스파크 마스터와 워커 데몬 자체에 할당할 메모리를 지정한다. (기본 : 1g)
SPARK_DAEMON_JAVA_OPTSJVM옵션으로 스파크 마스터와 워커 데몬 자체에 설정하는 옵션값이다.
형식 : "-Dx=y"의 형태이다. 
SPARK_PUBLIC_DNSpublic DNS명을 지정한다. 
* 참고 : launch script는 window에서는 적용되지 않는다. 윈도우에서 실행하기 위해서는 수동으로 직접 설정해야한다.

SPARK_MASTER_OPTS 옵션 : 
Property NameDefaultMeaning
spark.deploy.retainedApplications200디스플레이할 완료된 어플리케이션의 총 개수를 지정한다. 오래된 어플리케이션은 UI에서 이 제한에 걸려 나타나지 않을 것이다. 
spark.deploy.retainedDrivers200디스프레이할 완료된 드라이버들의 최대 개수를 지정한다. 오래된 드라이버는 UI에서 이 제한에 걸려 나타나지 않을 것이다. 
spark.deploy.spreadOuttrue스탠드 얼론 클러스터 매니저는 노드들로 어플리케이션을 분산하게 될 것이다. 아니면 가능하면 몇개의 노드들로 통합해서 처리하게 된다. 분산 하는 작업은 보통 HDFS에서 더 낳은 데이터 로컬리티를 위해 이용된다. 그러나 통합 작업은 계산에 더 집중적인 작업을 위해서 효과적이다. 
spark.deploy.defaultCores(infinite)만약 spark.cores.max를 지정하지 않은경우라고 하면 스파크 스탠드 얼론에서 어플리케이션에 할당할 기본 코어수를 지정하도록 한다. 설정되지 않았다면 어플리케이션은 항상 모든 가능한 코어를 spark.cores.max에 할당한다. 공유된 클러스에 이 값을 낮게 지정하여 전체 클러스터를 사용자가 잡아버리는 것을 막기 위해 이용할 수 있다. 
spark.worker.timeout60만약 마스터가 허트비트를 수힌하지 못한경우 얼마의 시간이 지나면 워커가 유실 되었다는 것을 파악할지 지정하는 시간이다. (초단위)
SPARK_WORKER_OPTS 옵션 : 
Property NameDefaultMeaning
spark.worker.cleanup.enabledfalseworker의 /application 디렉토리를 주기적으로 클린업 할지 여부를 설정한다. 이것은 오직 standalone모드에서만 효과가 있는 설정값이다. WARN은 다르게 동작한다. 이것은 오직 정지된 어플리케이션의 디렉토리만을 클린업 하게 된다. 
spark.worker.cleanup.interval1800 (30 minutes)로컬머신에서 오래된 어플리케이션 작업의 디렉토리를 클린업 할 인터벌 시간을 나타낸다. 초단위이다. 
spark.worker.cleanup.appDataTtl7 * 24 * 3600 (7 days)각 워커에서 유지해야할 작업 디렉토리의 TTL을 지정한다. 초단위이며 이것은 Time To Live이며, 가능한 디스크의 용량에 의존하게 된다. 어플리케이션 로그와 jars들이 어플리케이션 워크 디렉토리에 다운로드 된다. 시간이 지나면 work dirs들은 빠르게 디스크 공간을 차지해 버리게 된다. 특히 job들을 매우 빈번하게 실행하면 말이다. 

3. 어플리케이션에서 Cluster로 커넥팅 하기

스파크 클러스터에서 어플리케이션을 수행하는 것은 단순히 spark://IP:PORT 를 SparkContext constructor에 전달하는 것으로 가능하다.
스파크 쉘이 클러스터와 상호 인터렉션 하는 것은 다음 커맨드로 가능하다.

./bin/spark-shell --master spark://IP:PORT
또한 옵션을 전달 할 수 있으며 --total-executor-cores <numCores> 를 지정하여 스파크 쉘이 클러스터에서 사용할 코어의 개수를 설정하게 할 수 있다.

4. Spark Application들 런칭하기. 

spark-submit 스크립트는 직관적으로 컴파일된 스파크 어플리케이션을 클러스터로 서밋 하게 할 수 있다.
standalone 클러스터들에서는 현재 제공되는 2가지 모드가 있다. client모드에서 드라이버는 동일한 프로세스에서 런치 되며, 클라이언트 어플리케이션에서 서밋이 된다. cluster모드에서 드라이버는 클러스터내의 하나의 워커 프로세스들의 하나로 부터 런치되며, 클라이언트 프로세스는 바로 어플리케이션의 서밋 작업에 대한 응답을 수행한다. 이는 어플리케이션이 종료되기 까지 대기하지 않고 수행된다.

만약 어플리케이션이 Spark 서밋을 통해서 런치되며 어플리케이션 jar는 자동적으로 모든 워커 노드로 배포된다. 어플리케이션에 의존된 다른 jar들은 --jars옵션에 콤마로 분리하여 (ex --jars jar1,jar2) 참조 가능하다.
어플리케이션의 설정 혹은 실행 환경을 컨트롤 하는 것은 Spark Configuration을 참조하자.

추가적으로 스탠드 얼론 클러스터 모드는 0이 아닌 값으로 종료 되었을때 자동으로 재 시작 하는 모드를 제공한다. 이러한 기능을 이용하기 위해서는 --supervise플래스에서 spark-submit를 전달하면 되며, 이는 어플리케이션 실행시에 전달된다. 그리고 반복되는 실패때문에 어플리케이션을 종료하고자 한다면 다음과 같이 수행하면 된다.

./bin/spark-class org.apache.spark.deploy.client kill <master url> <driver ID>
드라이버 아이디는 http://<master url>:8080에서 web UI를 이용하여 해당 아이디를 찾을 수 있다.

5. Resource Scheduling 

standalone 클러스터 모드에서는 현재 단순 FIFO 스케줄러만을 제공하고 있다.
그러나 복수의 동시 이용자를 허용하기 위해서 각 어플리케이션 이 사용할 최대 리소스의 개수를 조정할 수 있다. 기본적으로 이것은 클러스터 내의 모든 코어를 필요로 하게 되지만 한번에 하나의 어플리케이션만을 실행할때에만 유효하다. 이 설정은 spark.cores.max 를 SparkConf내에서 지정해줄 수 있다.

예제 )
val conf = new SparkConf()
            .setMaster(...)
            .setAppName(...)
            .set("spark.cores.max", "10")
val sc = new SparkContext(conf)
추가적으로 spark.deploy.defaultCores를 클러스터 마스터 프로세스에서 설정할 수 있다.
이 설정은 conf/spark-env.sh에서 다음과 같이 설정할 수 있다.
export SPARK_MASTER_OPTS="-Dspark.deploy.defaultCores=<value>"
이것은 공유 클러스터 상에서 유용하다. 사용자가 개인적으로 최대수의 cores를 지정하지는 않을때 말이다.

6. Monitoring and Logging

Spark 스탠드얼론 모드는 웹 기반의 사용자 인터페이스를 제공하여 클러스터 모니터링을 수행할 수 있도록 한다. 마스터와 각 워커들은 각자의 web UI를 가지고 있으며 각 클러스터와 잡들을 볼수 있다. 기본적으로 웹 UI에 8080을 통해서 접근할 수 있다. 포트는 설정 파일에서 변경하거나, 수행시 파라미터를 통해서 설정이 가능하다.
추가적으로 각 잡의 상세 로그는 각 슬레이브 노드의 워크 디렉토리에 저장된다. 기본적으로 SPARK_HOME/work디렉토리에 저장된다. 또한 잡들에 대해서 2개의 파일이 존재하며 stdout과 stderr로 저장이 된다.

7. Running Alongside Hadoop

존재하는 하둡을 이용하고자 하는 경우에는 단순히 동일한 머신에서 분리된 서비스로 이용하면 된다. Hadoop 데이터를 스파크에서 엑세스 하기 위해서는 hdfs://URL (보통 : hdfs://<namenode>:9000/path 이다. ) 반대로 완젼 다른 서버에서 하둡과 연동할 수 있다. 이때에는 네트워크를 통해서 접속할 수 있으며, 로컬 위치의 데이터 엑세스에 비해서 성능은 떨어진다.

8. Configuring Ports for Network Security

 스파크는 네트워크 트래픽을 많이 발생 시킨다. 또한 어떠한 시스템에서는 파이어월 설정을 이용하기 위해서 제한된 요구사항을 가지기도 한다. 이러한 설정을 확인하려면 다음 경로를 찾아가자. http://spark.apache.org/docs/latest/security.html#configuring-ports-for-network-security


9. High Availability

기본적으로 스탠드얼론 스케줄링 클러스터는 Worker의 실패들에 대해서 회복력이 있다. 그러나 스케줄러는 Master를 이용하여 스케줄링 결정을 내린다. 그리고 기본적으로 이것은 단일 실패지점 (SPOF)가 된다. 만약 마스터가 크래쉬되면, 어떠한 새로운 어플리케이션도 생성되지 않는다. 이러한 문제를 극복하기 이ㅜ해서 2개의 high availability 스키마를 아래와 같이 구성한다.

10. Standby Masters with ZooKeeper

Overview
zookeeper의 주 용도는 리더의 선출과 상태를 저장하는 것이다. 사용자는 복수개의 마스터를 클러스터에 설정하여 동일한 ZooKeeper인스턴스에 연결할 수 있다. 한대는 "leader"로 선출되고 나머지는 standby mode로 마스터를 구성한다. 만약 현재 leader가 죽으면, 다른것이 Master가 된다. 그리고 마스터의 상태를 복구하게 되고 동일한 스케줄링을 다시 수행하게 된다.
전체 복구 프로세스는 아마도 처음 마스터가 죽고 나서 부터 약 1 ~ 2분 정도 소요된다.
이 딜레이는 새로운 어플리케이션 스케줄링에만 영향을 준다. 어플리케이션들은 마스터 failover를 수행하는 동안 계속해서 수행되고 있는 상태가 된다.

Zookeeper에 대해서 더 많은 것을 보려면 http://zookeeper.apache.org/doc/trunk/zookeeperStarted.html 을 참조하자.

Configuration
스파크 복구 모드는 다음과 같이 spark-env스크립트 내에 SPARK_DAEMON_JAVA_OPTS 에 설정할 수 있다.
System propertyMeaning
spark.deploy.recoveryModezookeeper를 standby 모드로 설정하도록 한다. (기본 : NONE)
spark.deploy.zookeeper.urlZooKeeper 클러스터 url을 설정한다.
예) 192.168.1.100:2181,168.1.101:2181
spark.deploy.zookeeper.dirThe directory in ZooKeeper to store recovery state (default: /spark).
멀티플 마스터를 가지고 있는 상태에서 마스터가 주키퍼를 이용하는 설정에 실패를 하게 되면 마스터는 서로를 확인하는 작업에 실패하게 되며, 모든 마스터들이 리더라고 생각하게 된다. 이때에는 올바른 클러스터 상태를 이용하기 어렵게 된다. (모든 마스터들은 독립적으로 스케줄링 하게 된다.)

Details
주키퍼 설정을 완료하고 나면 high availability를 기동할 수 있다. 단순히 복수개의 마스터를 서로다른 노드에서 실행하고 동일한 주키퍼 설정을 가지고 있으면 된다. (ZooKeeper URL과 디렉토리 설정). 마스터들을 언제든지 추가나 제거 할 수 있다.
새로운 어플리케이션을 스케줄 하거나 클러스터에 worker를 추가할때에는 현재 Master의 IP를 알아야 한다. 이것은 단순히 마스터 리스트에서 하나에다가 전달 하는 것으로 가능하다. 예를 들어 SparkContext를 다음과 같이 마스터의 위치를 포인트 할 수 있다. spark://host1:port1,host2:port2 로 지정하는 것이다.
이렇게 하면 SparkContext는 두개의 마스터에 접근을 시도한다. host1이 내려가 있으면 이 설정은 새로운 리더를 찾을 것이며 이것이 host2가 됨을 알려 줄 것이다.

"registering with a Master"과 normal operation사이에는 중요한 구별 되는 점이 있다. 어플리케이션이나 워커가 실행될때 현재의 마스터를 찾거나 등록하는 작업이 필요하다. 이 작업이 한번 성공적으로 수행되고 나면 이 내용을 Zookeeper에 저장한다. 만약 failover가 발생하면 새로운 리더가 모든 등록된 어플리케이션들과 Workers에 접근을 시도하며, 이후 모든 클러스터에게 새로운 리더를 알린다.

이러한 속성으로 인해서 새로운 마스터는 언제든지 생성될 수 있다, 고민해야할 것은 오직 새로운 어플리케이션과 워커들이 새로운 리더를 찾고, 이를 등록하는 작업만 하면 된다. 한번 등록하면 언제든지 이용할 수 있다.

11. Single-Node Recovery with Local File System

Overview
주키퍼는 high availability를 위한 가장 좋은 방법을 제공한다. 그러나 마스터가 다운되었을때 단지 리스타트를 하기를 원할 수 있다. FILESYSTEM모드는 이러한 작업을 할 수 있다. 어플리케이션과 워커가 등록될때 제공된 디렉토리에 상태를 저장하고, 이것은 마스터 프로세스가 리스타트 될때 이용 할 수 있다.

Configuration
복구 모드를 기동하기 위해서는 spark-env 설정파일의 SPARK_DAEMON_JAVA_OPTS에 지정할 수 있다.
System propertyMeaning
spark.deploy.recoveryModeFILESYSTEM을 설정하여 단일 노드 복구 모드를 실행할지 지정한다.
(기본 :  ㅜNONE)
spark.deploy.recoveryDirectorySpark가 복구 상태를 저장하고 접근할수있는 상태를 저장할 디렉토리를 지정한다. 
Detail 
  • This solution can be used in tandem with a process monitor/manager like monit, or just to enable manual recovery via restart.
  • While filesystem recovery seems straightforwardly better than not doing any recovery at all, this mode may be suboptimal for certain development or experimental purposes. In particular, killing a master via stop-master.sh does not clean up its recovery state, so whenever you start a new Master, it will enter recovery mode. This could increase the startup time by up to 1 minute if it needs to wait for all previously-registered Workers/clients to timeout.
  • While it’s not officially supported, you could mount an NFS directory as the recovery directory. If the original Master node dies completely, you could then start a Master on a different node, which would correctly recover all previously registered Workers/applications (equivalent to ZooKeeper recovery). Future applications will have to be able to find the new Master, however, in order to register.






[Mac] ssh 자동로그인처리

$
0
0

Mac에서 ssh기능 실행하기.


1. Mac에서 ssh기능 실행하기. 

- 우선 Mac에서 ssh기능을 실행하기 위해서는
"시스템 환경설정"> "공유"> "원격 로그인"을 실행해주자.



2. 인증키 생성하기. 

- 원격 로그인을 자동으로 실행하기 위해서는 인증키를 생성하여 클라이언트에 공개 인증키를 배포하면 된다. 

2.1 인증키 생성하기. 

ssh-keygen -t rsa
mac에서는 위와 같이 ssh-keygen을 이용하면 된다. 

Enter file in which to save the key (/Users/KIDO/.ssh/id_rsa):
생성할 파일을 입력하는 것으로 기본값을 이용하기 위해서 enter키를 치고 넘어간다. 

Enter passphrase (empty for no passphrase):
Enter same passphrase again:
생성할 비밀번호를 입력한다. 
이때 비밀번호를 입력하지 않으면, 공개키만 가지고 해당 서버에 접근할 수 있으므로 보안에 위험이 생길 수 있으므로 비밀번호를 입력하자. 
비밀번호는 10자에서 30자까지 넣으면 된다. 

The key's randomart image is:
+---[RSA 2048]----+
|o.  .oo=oo       |
|o. o oo ++       |
| oo =   o+.      |
|o  oooo.E.=      |
|. . .+  .. o     |
|   o  ... .      |
|  .   ...o       |
+----[SHA256]-----+
결과로 위와같은 내용이 출력되고 다음과 같은 파일이 생성된다. 
-rw-------   1 KIDO  staff  1766  5 13 22:57 id_rsa
-rw-r--r--   1 KIDO  staff   404  5 13 22:57 id_rsa.pub

2.2 인증 퍼미션 설정하기. 

인증 퍼미션은 중요도에 따라 다음과 같이 설정한다. 
id_rsa는 private_key이므로 타인에게 공개해서는 안되는 중요한 파일이다. 

chomd 700 .ssh
chmod 600 .ssh/id_rsa
chmod 644 .ssh/id_rsa.pub

2.3 클라이언트용 공개키항목에 추가하기 

cat id_rsa.pub >> authorized_keys
보통 클라이언트는 authorized_keys에 공개키들을 나열하고 있다. 그러므로 상기 명령어로 공개키를 추가해주자. 





[Spark] Quick Start

$
0
0

Spark Quick Start

Interactive Analysis with the Spark Shell

기본 : 

스파크 쉘을 다음과 같이 실행하자. 이는 대화형 데이터 분석을 위한 강력한 툴이다.
./bin/pyspark
스파크의 중요한 추상화는 Resilient Distributed Dataset(RDD)라고 부르는 분산 컬렉션이다.
RDD는 Hadoop 입력 포맷 이나 transformation등을 통해서 생성된다. 스파크 패키지에 존재하는 README 파일을 읽어 들이는 예제를 보자.
>>> textFile = sc.textFile("README.md")
RDD는 actions과 transformations를 가진다. action은 반환 값을 가지며, transformation은 새로운 RDD를 생성한다.
다음 몇개의 액션들을 보자.
>>> textFile.count()       # 이 RDD에서 아이템의 총 개수를 센다.
126
>>> textFile.first()          # 이 RDD에서 첫번째 아이템을 출력한다.
u'# Apache Spark'
다음은 transformation을 사용한 예제이다.
이것은 filter transformation을 통해서 새로운 RDD를 생성한다. 이는 file의 subset이다.
>>> linesWithSpark = textFile.filter(lambda line: "Spark" in line)
그리고 transformations과 actions의 체인을 걸어줄 수 있다.
>>> textFile.filter(lambda line: "Spark" in line).count()   # "Spark"를 포함하는 라인이 몇개인가?
15

RDD 연산 더보기 

RDD actions와 transformations는 더 복잡한 계산을 위해서 사용이 가능하다.
가장 긴 단어의 라인을 찾는다고 해보자.
>>> textFile.map(lambda line: len(line.split())).reduce(lambda a, b: a if (a > b) else b)
15
이 첫번째 맵은 라인을 정수 값으로 하는 새로운 RDD를 생성한다. reduce는 가장 긴 라인 카운트를 찾는데 호출된다.map 아규먼트와 recude아규먼트는 (lambda)로 anonymous functions이다. 우리는 최상위 레벨의 파이선 함수를 전달할 수도 있다. 예를 들어 우리는 max함수를 이해하기 쉽게 다음과 같이 만들 수 있다.

>>> def max(a, b) :
...            if a > b:
...                return a
...            else:
...                return b
>>> textFile.map(lambda line: len(line.split())).reduce(max)
15
하나는 Hadoop에 잘 알려진 데이터 흐름 패턴인 MapReduce이다. Spark는 다음과 같이 쉽게 MapReduce를 구현할 수 있다.
>>> wordCounts = textFile.flatMap(lambda line: line.split()).map(lambda word: (word, 1)).reduceByKey(lambda a, b: a+b)
여기에서 우리는 flatMap, map과 reduceByKey transformations를 서로 연결하여 RDD에 존재하는 각 단어의 개수를 새로운 RDD로 (string, int) 쌍으로 만들어 낸다.
우리에 쉘에서는 단어 수를 세는 작업은 collect 액션을 이용하여 구현한다.
>>> wordCounts.collect()
[[(u'and', 9), (u'A', 1), (u'webpage', 1), (u'README', 1), (u'Note', 1), (u'"local"', 1), (u'variable', 1), ...]

Caching

Spark는 또한 여러 클러스들에 넓게 메모리 캐시에 데이터를 넣을 수 있는 기능을 제공한다. 이것은 데이터를 반복적으로 접근할때 매우 유용한 기술이다. 작은 "hot"데이터셋을 쿼리 할때나, PageRank와 같은 알고리즘을 반복적으로 수행하고자 할때 유용하다. 다음은 linesWithSpark데이터 셋을 캐시 하는 예이다.

>>> linesWithSpark.cache()
>>> linesWithSpark.count()
19
>>> linesWithSpark.count()
19

만약 100라인짜리 텍스트 파일을 캐시한다면 매우 우스운 일일 것이다. 흥미로운 것은 이러한 동일한 함수를 통해서 매우 큰 데이터 셋을 사용할 수 있다는 것이다. 비록 이들이 10개에서 수백개의 노드에 흩어져 있더라도 말이다. 또한 bin/pyspark를 통해서 클러스터에 연결하여 이러한 작업을 수행할 수 있다. 이는 프로그래밍 가이드를 참조하자. https://spark.apache.org/docs/latest/programming-guide.html#initializing-spark

Self-Contained Applications

우리는 Spark API를 이용하여 self-contained application을 작성할 수 있다. 
다음 SimpleApp.py 파일을 살펴보자. 

"""SimpleApp.py"""
from pyspark import SparkContext
logFile = "YOU_SPARK_HOME/README.md"
sc = SparkContext("local", "Simple App")
logData = sc.textFile(logFile).cache()
numAs = logData.filter(lambda s: 'a' in s).count()
numBs = logData.filter(lambda s: 'b' in s).count()
print("Lines with a: %i, lines with b: %i" % (numAs, numBs))
이 프로그램은 텍스트 파일에서 각 라인중에 a와 b를 포함한 라인의 수를 카운트 한다. YOUR_SPARK_HOME은 인스톨된 스파크의 위치를 지정하면 된다. SparkContext는 RDD를 생성하기 위해서 사용된다. 또한 Spark에 Python 함수를 전달할수 있다. 이것은 자동으로 해당 함수를 참조하도록 시리얼라이즈 된다. 어플리케이션에서 사용하기 위해 커스텀 클래스혹은 서드파티 라이브러리를 이용할 수 있다. 또한 이 외부 어플리케이션을 실행하기 위해서 spark-submit를 호출할 수 있다. 자세한 내용은 spark-submit --help를 통해서 사용법을 확인해보자. 

다음 과 같이 함수를 실행해보자. 
# spark-submit 를 실행하여 어플리케이션을 실행한다. 
$ YOUR_SPARK_HOME/bin/spark-submit \
   --master local[4] \
   SimpleApp.py
...
Lines with a: 46, Lines with b: 23



[Spark] Cluster Mode

$
0
0

Cluster Mode Overview

from : https://spark.apache.org/docs/latest/cluster-overview.html

클러스터 모드에서 어떻게 스파크가 수행되는지 보여주는 문서이며, 컴포넌트들이 수행하는 일에 대해서 쉽게 이해할 수 있다.

Components

스파크 어플리케이션은 크러스터 상에서 독립된 처리 단위로 수행된다. 이는 SparkContext 객체에 의해서 코디네이트 된다. (이를 driver program이라고 한다.)

특히, 클러스터에서 수행될때 SparkContext는 몇가지 타입의 클러스터 관리자와 연결할 있다. (스파크의 스탠드 얼론 클러스터 관리자, Mesos, YARN등이 있다.) 이것은 어플리케이션 상호간의 자원을 할당한다. 한번 접속이 되면 스파크는 클러스터 내의 노드들에서 수행하며, 이 처리는 계산을 하거나 데이터를 저장하는 작업을 수행한다. 다음으로 어플리케이션 코드를 실행하는 executors에 전달한다. (JAR혹은 Python파일을 SparkContext에 전달한다.) 최종적으로 SparkContext는 태스크를 executors에 전달하여 실행한다.

Spark cluster components
이 아키텍쳐에는 주요 노트 몇가지가 있다.
1. 각 어플리케이션은 소유한 executor processes를 획득한다. 이것은 전체 어플리케이션의 기간동안 유지되며, 태스크를 멀티 쓰레드로 실행한다.
이것은 어플리케이션들 각각을 독립적으로 고립시키는 이점이 있다. 여기에는 스케줄링 사이드(각 드라이버는 자신의 태스크를 스케줄링한다.) 와 executor 사이드(서로다른 어플리케이션의 태스크들은 서로다른 JVM상에서 동작한다.)가 있다. 그러나 이 의미는 외부 스토리지 시스템에 해당 정보를 쓰는 것을 통하지 않고서는, 서로다른 스파크 어플리케이션 상호간에 데이터를 쉐어할 수 없다는 의미가 된다.

2. 스파크는 클러스터 매니저를 알지 못한다. 가능한 오랬동안 executor processes를 가질 수있으며, 이들 상호간에 서로 커뮤니케이션 하며, 비교직 쉽게 클러스터 매니저 상에서 실행이 가능하다. 이것은 또한 다른 어플리케이션을 지원한다. (Mesos/YARN)

3. 드라이버 프로그램은 executors의 라이프타임 내에서 들어오는 접근을 반드시 listen해야하며, 받아 들여야 한다. 드라이버 프로그램은 반드시 워커 노드들로 부터 네트워크 접근을 할 수 있도록 해야한다.

4. 클러스터에서 드라이버가 스케줄하기 때문에 워커노드 가까운 곳에서 수행되어야한다. 가급적이면 동일한 LAN내에 있어야 한다. 만약 원격지에 요청을 보내고자 한다면 드라이버에 대한 RPC를 오픈하고 오퍼레이션을 워커노드에서 가급적 가까운 곳에서 수행할 수 있도록 해야한다.

Cluster manager Types

현재 지원되는 클러스터 매니저는 다음과 같다.
Standalone - 단순 클러스터 매니저로 Spark를 포함하며, 클러스터를 쉽게 설정할 수 있다.
Apache Mesos - 일반적은 클러스터 관리자이며, Hadoop MapReduce와 서비스 어플리케이션을 수행할 수 있다.
Hadoop YARN - Hadoop2에 있는 리소스 매니저이다.

추가적으로 Spark의 EC2 launch scripts는 Amazone EC2에서 스탠드 얼론 클러스터에 런치하기 쉽게 해준다.

Submitting Applications

어플리케이션은 spark-submit스크립트를 이용하여 다양한 타입으로 서밋 할 수 있다.
다음 참조 : https://spark.apache.org/docs/latest/submitting-applications.html


Monitoring

각 드라이버 프로그램은 web UI를 가지고 있다. 일반적으로 4040포트를 이용하며 실행되는 태스크, executors, storage사용 등을 볼 수 있다. 단순히 http://<driver-node>:4040 에 접근하여 볼 수 있다.
다음을 참조 : https://spark.apache.org/docs/latest/monitoring.html

Job Scheduling

스파크는 어플리케이션들 간에 리소스 할당을 컨트롤 하도록 해준다. (클러스터 매니저의 역할에서) 그리고 어플리케이션 내부의 계산들은 동일한 SparkContext내에서 수행된다.
다음을 참조 : https://spark.apache.org/docs/latest/job-scheduling.html

Glossary

다음 표는 클러스터 개념을 나타내는 용어들을 보여준다.
TermMeaning
Application스파크내 사용자 프로그램, 드라이버 프로그램과 클러스터 내의 executors로 구성된다. 
Application jar사용자 스파크 어플리케이션을 포함하는 jar파일, 사용자는 "uber jar"를 생성하기를 원할수 있다. 이때 이 내부에는 의존성 패키지를 포함한다.
사용자의 jar는 Hadoop 혹은 Spark라이브럴리를 포함해서는 안된다. 이는 실행시점에 포함된다. 
Driver program어플리케이션의 main() 함수를 실행하며, SparkContext를 생성한다. 
Cluster manager클러스터 내의 필요한 리소스를 위한 외부 서비스이다. (standalone manager, Mesos, YARN등)
Deploy mode드라이버 어디에서 프로스세를 실행하는지를 구별한다. 클러스터 모드에서 프레임워크는 클래서터의 내부에 드라이버를 실행한다. 클라이언트 모드에서는 클러스터의 외부에서 드라이브를 런치한다. 
Worker node클러스터 내에서 어플리케이션 코드를 실행할 수 있는 특정 노드
Executor워커노드에서 어플리케이션을 위해서 린치된 프로세스이다. 이것은 태스크를 실행하고, 메모리에 데이터를 저장하거나 디스크에 접근하는 역할을 한다. 각 어플리케이션은 자신의 executors를 가지고 있다. 
Task하나에 executor에 전달될 작업의 단위
Job복수개의 태스크들로 구성된 병렬 계산처리를 하는 것으로 스파크 액션의 응답에서 만들어진다. (save, collect) 이것은 드라이버의 로그에서 이 용어를 사용한 것을 볼 수 있을 것이다. 
Stage각 job은 스테이지라고 불리는 태스크의 셋들로 나누어 진다. 이것은 서로 의존적이며 (이는 map, reduce와 유사하다.) 드라이버 로그에서 이 용어를 사용한 것을 볼 수 있을 것이다. 



[Spark] Programming-guide

$
0
0

Spark Programming Guide

from : https://spark.apache.org/docs/latest/programming-guide.html

개요 : 

각 스파크 어플리케이션은 main함수인 driver program과 각 클러스터에서 다양한 병렬 오퍼레이션으로 구성되어 실행된다.

스파크의 메인 추상화는 resilient distributed dataset(RDD)이다. 이것은 클러스터의 노드들간에  파티션된 엘리먼트의 컬렉션들이며 이들은 병렬로 처리되어진다.
RDD는 HDFS 에서 생성되어지며 (혹은 다른 하둡 지원의 파일 시스템) 혹은 드라이버 프로그램 내에서 스칼라 컬렉션으로 존재하는 데이터이며, 이들은 transforming되기도 한다.
사용자는 RDD를 메모리에 persist하도록 요청할 수 있으며, 병렬 처리를 위해 상호간에 효과적으로 재 사용 할 수 있도록 해준다.
마지막으로 RDD는 노드가 실패하는 경우 자동으로 복구 해준다.

두번째 추상화는 병렬 처리에서 사용될 수 있는 shared variables이다. 기본적으로 스파크가 서로다른 노드에서 태스크의 집합을 병렬로 수행할때 각 태스크의 함수에서 사용되는 각 변수들을 복제하여 옮긴다.
가끔 변수들은 태스크들 끼리 공유되거나 태스크와 드라이버 프로그램 사이에 공유된다.
스파크는 2개의 공유 변수를 제공한다.
broadcast variables : 모든 노드의 메모리에 캐시되어 사용될 수 있다.
accumulators : 이 변수는 counters와 sums와 같은 작업을 위해서 추가되는 변수이다.

# 참고 : 이문서는 python만을 정리하였다.
bin/pyspark 
상기 스크립트를 실행해보자.

Linking with Spark

Spark 1.6.1 은 Python 2.6 + 와 Python 3.4+ 에서 동작한다.
또한 CPython 인터프리터를 이용할 수 있다. NumPy와 같은 C라이브러리가 사용될 수 있다. 또한 이는 PyPy 2.3+ 버젼과 호환된다.

Spark어플리케이션을 Python에서 실행하기 위해서는 bin/spark-submit 스크립트를 이용할 수 있다.
이 스크립트는 Spark의 Java/Scala라이브러리를 로드할 것이다. 그리고 클러스터에 어플리케이션을 submit하게 해준다.
또한 bin/Pyspark 스크립트는 대화영 Python shell이다.

만약 HDFS데이터를 이용하고자 한다면, PySpark linking 빌드를 HDFS버젼과 연동 해야한다.
이는 Prebuilt packages에서 해당하는 HDFS 버젼에 맞게 설치하면 된다.

마지막으로 다음과 같이 프로그램에 Spark 클래스를 import할 필요가 있다.
from pyspark import SparkContext, SparkConf
PySpark 는 Python의 마이너 버젼과 동일한 버젼을 필요로 한다.
기본 파이선 버젼을 PATH에 걸어주는 작업이 필요하다. 특정 버젼을 설정하고자 한다면 PYSPARK_PYTHON의 값을 다음과 같이 설정하자.
$ PYSPARK_PYTHON=python3.4 bin/pyspark
$ PYSPARK_PYTHON=/opt/pypy-2.5/bin/pypy bin/spark-submit examples/src/main/python.pi.py

Initializing Spark

스파크 프로그램의 시작은 SparkContext를 생성하는 것으로 시작한다. 이것은 Spark에게 어떻게 클러스터에 접근할지를 알려준다.
SparkContext를 생성하기 위해서는 어플리케이션에 대한 정보를 포함하는 SparkConf를 빌드 해야한다.
conf = SparkConf().setAppName(appName).setMaster(master)
sc = SparkContext(conf=conf)
appName은 어플리케이션의 이름으로 cluster UI에 나타난다. master는 Spark, Mesos혹은 YARN클러스터의 URL이거나 혹은 "local"(로컬모드 지원)이 올 수 있다.
실제에서는 클러스터에서 수행할때 프로그램에서 master를 하드코딩 하지 않을 것이다. 그러나 대신에 spark-submit으로 어플리케이션을 런치 하고, 결과를 수신 받을 것이다. 그러나 로컬 테스트와 유닛 테스트를 위해서는 "local"을 전달해서 스파크를 실행할 수 있다.

Using the Shell

PySpark쉘은 SparkContext가 이미 생성되어 있는 인터프리터이다. 이 변수는 sc라고 불린다.
자신의 SparkContext를 만들면 동작하지 않는다. 또한 master 아규먼트를 이용하여 마스터 컨텍스트 커넥션을 설정할 수 있다. 그리고 Python에 .zip, .egg, .py파일을 --py-files에 콤마로 나누어진 리스트를 전달할 수 있다.
또한 의존성을 쉘 세션에 전달할 수 있다. 이는 메이븐과 연동될 리스트를 콤마로 분리된 값을 전달하면 가능하며 --packages아규먼트에 실어서 보내면 된다. 만약 의존성에 필요한 추가적인 repository가 필요한경우라면 --repositories 아규먼트에 기술한다. 그리고 python에 필요한 스파크 의존성은 pip를 이용하여 필요한 의존성을 추가하자. 예를 들면 다음과 같이 할수 있다.

$./bin/pyspark --master local[4]
혹은 code.py 를 추가할 수있다.
$./bin/pyspark --master local[4] --py-files code.py
전체 옵션 항목을 확인해보기를 원한다면 pyspark --help 를 입력하자.
pyspark 는 더 많은 일반 spark-submit script를 호출한다.

IPython에서  PySpark 쉘을 런칭하는 것도가능하다. 이는 Python 인터럽터기능을 향상 시킨다. PySpark는 IPython 1.0.0과  이후 버젼에 호환한다. IPython을 사용하는 것은 PYSPARK_DRIVER_PYTHON 변수에 ipython 값을 지정하면된다.
$PYSPARK_DRIVER_PYTHON=ipython ./bin/pyspark
또한 ipython커맨드를 PYSPARK_DRIVER_PYTHON_OPTS 를 설정하여 커스터마이징 할 수 있다. 예를 들어 IPython Notebook 을 런칭할때 PyLab plot를 지원하도록 할 수 있다.
$PYSPARK_DRIVER_PYTHON=ipython PYSPARK_DRIVER_PYTHON_OPTS="notebook" ./bin/pyspark
IPython Notebook 서버가 런칭 된 이후에 "Python 2"노트북을 "Files"탭으로부터 생성할 수 있다. notebook 내에서 %pylab inlines 커맨드를 IPython 노트북에서 Spark를 실행하기 전에 입력할 수 있다.

Resilient Distributed Datasets(RDDs)

스파크는 RDD(Resilient Distributed Datasets)의 개념을 바탕으로 수행된다. 이것은 엘리먼트들의 컬렉션으로 fault-talerant 한 컬렉션이다. 이것은 병렬로 수행될 수 있다.
RDD를 생성하기 위한 2가지 방법으로 드라이버 프로그램 내부에 존재하는 컬렉션을 parallelizing을 통해서 생성하는 것과 외부 저장 시스템에서 데이터 셋 혹은 공유 파일시스템, HDFS, HBase, 하둡 인풋 형식을 따르는 다양한 데이터 소스등을 참조하는 방법으로 생성이 가능하다.

Parallelized Collections

Parallelized Collections는 SparkContext의 parallelize메소드를 통해서 생성된다. 이것은 드라이버 프로그램 내부에 존재하는 반복가능한 데이터나 컬렉션을 이용한다. 컬렉션 엘리먼트들은 분산된 데이터 셋으로 부터 복제되어 병렬로 수행될 수 있다. 예를 들어 다음은 숫자 1 에서 5까지 값을 가지고 있는 컬렉션을 생성하는 것이다.

data = [1, 2, 3, 4, 5]
distData = sc.parallelize(data)
일단 생성이 되면 분산된 데이터셋(distData)는 병렬로 수행이 가능하다. 예를 들어 우리는 distData.reduce(lambda a, b: a + b)를 호출하여 각 엘리먼트의 리스트를 더할 수 있다.

중요한 파라미터중에 하나는 데이터를 잘라낼 파티션의 개수를 지정하는 것이다. 스파크는 각 클러스터의 파티션마다 하나의 태스크를 수행하게 된다. 일반적으로 CPU당 2 - 4개의 파티션을 생성한다. 보통 스파크는 클러스터에 따라 자동으로 파티션을 설정하는 작업을 시도한다. 그러나 수동으로 두번째 파라미터로 전달이 가능하다.

예)
sc.parallelize(data, 10)

External Datasets

PySpark는 Hadoop에 의해서 분산저장되어 있는 데이터셋을 생성할 수 있다. 로컬파일 시스템이나, HDFS, Cassandra, HBase, Amazon S3등으로 생성이 가능하다. Spark는 text 파일, SequenceFile, 그리고 다른 하둡 입력 포맷등을 지원한다.

Text 파일 RDD는 SparkContext의 textFile 메소드를 이용하여 생성이 가능하다. 이 메소드는 파일의 URI(hdfs://, s3n://, etc URI)를 획득한다. 그리고 라인의 컬렉션을 읽어 들인다.

>>> distFile = sc.textFile("data.txt")
일단 생성이 되면 dataFile는 dataset오퍼레이션에 의해서 동작된다. 예를 들어 우리는 모든 라인의 길이를 더하는 작업을 할 것이다. 이때 map과 reduce를 이용하는 예는 다음과 같다.
distFile.map(lambda s : len(s)).reduce(lambda a, b : a + b)
스파크에서 파일을 읽는 몇가지 주의 사항

1. 로컬 파일 시스템에서 패스를 이용하는 경우 파일은 반드시 워커 노드에서 동일하게 접근이 가능해야한다, 각 파일 복제본은 모든 워커들 혹은 네트워크 마운트로 공유된 파일 시스템으로 복제 된다.

2. 파일 기반의 입력 메소드는 모두 textFile를 포함한다. 디렉토리, 압축파일, 그리고 와일드카드 모두 가능하다. 예를 들면 다음과 같다.
textFile("/my/directory")
textFile("/my/directory/*.txt")
textFile("/my/directory/*.gz")
3. textFile메소드는 또한 추가적인 아규먼트를 가지며 이는 파일의 파티션 넘버를 지정하는 것이다. 기본적으로 스파크는 파일의 각 블록마다 하나의 파티션을 지정한다. (한 블록은 HDFS에서 기본적으로 64MB이다.) 더 큰 값을 지정할 수 있다. 그러나 주의할 점은 블록보다 더 적은 파티션을 지정하면 안된다.

텍스트 파일로 부터 스파크 파이선 API는 몇가지 데이터 포맷도 지원하고 있다.
1. SparkContext.wholeTextFiles
- 복수개의 작은 텍스트 파일들을 포함하는 디렉토리를 읽도록 한다. 그리고 그것들의 반환한다. (filename, context)쌍을 반환한다. 이것은 textFile과는 대조적으로 각 파일에서 각 라인은 하나의 레코드를 반환하게 된다.

2. RDD.saveAsPickleFile, SparkContext.pickleFile
- 파이선 객체의 피클로 구성된 단순 포맷으로 RDD를 저장한다. 이는 배치로 pickle serialization을 이용하며 기본 배치 크기는 10이다.

3. SequenceFile 그리고 Hadoop Input/Output 포맷

주의 : 이 기능은 현재 Experimental(실험용)로 마크 되어 있다. 그리고 advanced user을 대상으로 제공한다. 앞으로는 아마도 SparkSQL을 기준으로 read/write를 지원하게 될것이다. 앞으로는 Spark SQL을 선호되어질 것이다.

Writable Support
PySpark SequenceFile는 key-value 쌍의 RDD를 로드한다. 이는 또한 Java 타입으로 쓰기 가능한 형태로 변환되며 pickle들은 Java객체로 Pyrolite를 이용하여 변환된다. RDD의 key-value쌍을 SequenceFile로 저장할때에는 PySpark는 역으로 동작한다. Python object는 unpickle를 통해서 Java객체로 변환되고, 이 값은 writable 하도록 변환이 된다. 다음은 상호 자동 변환을 보여주는 표이다.
Writable TypePython Type
Textunicode str
IntWritableint
FloatWritablefloat
DoubleWritablefloat
BooleanWritablebool
BytesWritablebytearray
NullWritableNone
MapWritabledict
Arrays는 처리되지 않는다. 사용자는 특정 커스텀 ArrayWritable 서브 타입을 필요로 하게 된다. 쓰기를 할때 사용자들은 특정 커스텀 컨버터들을 필요로 하며 이는 arrays를 커스텀 ArrayWritable서브 타입으로 변환한다. 읽기시에 기본 컨버터는 커스텀 ArrayWritable서브타입을 Java객체 Object[]로 변환한다. 이 것은 pickle된 값을 Python tuples로 변환한다. Python array.array에서 프리미티브 타입의 배열을 획득하는 경우에도 사용자는 custom converter가 필요하다.

Saving and Loading SequenceFiles

텍스트파일과 마찬가지로 SequenceFiles는 특정 경로에 저장 및 로드할 수 있다. 키와 값의 클래스들이 지정될 수 있다. 그러나 표준 writables들은 필요하지 않다.

>>> rdd = sc.parallelize(range(1, 4)).map(lambda x : (x, "a" * x))
>>> rdd.saveAsSequenceFile("path/to/file")
>>> sorted(sc.sequenceFile("path/to/file").collect())
[(1, u'a'), (2, u'aa'), (3, u'aaa')]
Saving and Loading Other Hadoop Input/Output Formats

PySpark는 또한 다른 Hadoop InputFormat으로부터 읽거나, Hadoop OutputFormat으로 부터 쓰기를 할 수 있다. new와 old하둡의 MapReduce API들 둘다 가능하다. 만약 필요한경우에 Hadoop설정은 Python dict에 전달될 수 있다. 다음 예제는 Elasticsearch ESInputFormat를 이용한 예제이다.
$ SPARK_CLASSPATH=/path/to/elasticsearch-hadoop.jar ./bin/pyspark
>>> conf = {"es.resource" : "index/type"}  #참고 elasticsearch가 로컬에 수행되고 있다고 가정
>>> rdd = sc.newAPIHadoopRDD("org.elasticsearch.hadoop.mr.EsInputFormat", \
            "org.apache.hadoop.io.NullWritable", "org.elasticsearch.hadoop.mr.LinkedMapWritable", conf=conf)
>>> rdd.first() # 결과는 MapWritable이며 이는 Python dict로 변환된다.
(u'Elasticsearch ID',
    {u'field1' : True,
      u'field2' : u'Some Text',
      u'field3' : 12345} )
만약 InputFormat가 단순히 Hadoop 설정 input path에 의존되거나, key, value클래스가 쉽게 상단 테이블로 변환되어진다면, 해당 케이스에서 잘 동작 할 것이다.

만약 커스텀 serialized 바이너리 데이터를 가지고 있다면 (Cassandra / HBase로 부터 읽어들인 데이터같은..), 우선 데이터를 Scala/Java 진영의 객체로 변환될 필요가 있다. 이러한 작업은 Pyrolite의 pickler에서 처리 되도록 해준다. Converter 의 특징은 이것을 위해 제공된다. 단순히 이 특징과 convert메소드에서 변환된 코드들로 확장이 가능하다. 기억할 것은 이 클래스는 InputFormat로 접근할때 어따한 의존성이 필요하지 않아야 한다. 그리고 PySpark의 클래스 패스에 해당 경로의 jar파일을 포함하고 있어야 한다.

Cassandra / HBase의 InputFormat과 OutputFormat에 대한 커스텀 컨버터의 예제를 보려면 다음을 참조하자.
Python example, Converter example

RDD Operations

RDD는 2가지 타입의 오퍼레이션을 지원한다.
1. transformation
- 하나의 RDD에서 새로운 데이터 셋을 생성해 내는 작업을 말한다.
- map은 transformation이다. 이것은 각 데이터 엘리먼트를 특정 함수로 처리하여 새로운 RDD를 결과로 반환한다.

2. actions
- 데이터셋에서 계산을 마치고 드라이버 프로그램으로 값을 반환한다.
- reduce는 action으로 특정 function을 이용하여 RDD의 엘리먼트의 전체를 aggregate하고, 최종 결과를 driver 프로그램으로 반환한다. (참고, reduceByKey는 분산된 데이터 셋을 반환한다. 병렬로...)

모든 transformation은 lazy이다. 이것은 해당 결과가 바로 처리되지 않는다는 것이다. 대신에 변환이 적용되어야 한다는 것을 단지 기억만 한다. transformation은 action이 호출되는 경우에만 수행이 되며, 이는 드라이버 프로그램으로 최종값이 전달된다.
이러한 설계는 spark를 더욱 효과적으로 동작하게 만들어 준다. 예를 들어 우리는 map를 통해서 생성된 데이터는 reduce에 사용하기 위해서 만들어 진다는 것을 알고 있다. 그리고 reduce는 결국 driver로 전달되기 위해서 사용된다.

기본적으로 각 transform 처리된 RDD는 action이 호출될때 매번 재 계산이 된다.  그러나 RDD를 메모리에 저장할 수 있다. 이때에는 persist 나 cache메소드를 호출하면 된다. 이것은 다음 실행때 스파크를 더욱 빠르게 동작하도록 해준다. 또한 Disk에 RDD를 저장하거나 복수개의 노드에 복제하도록 할 수 도 있다.

Basis

RDD의 기본을 위해서 다음 프로그램을 살펴보자.
lines = sc.textFile("data.txt")
lineLength = lines.map(lambda s : len(s))
totalLength = lineLengths.reduce(lambda a, b : a + b)
첫번재 라인은 외부 파일로 RDD를 정의한다. 이 데이터셋은 action이 호출되기 전에는 로드되지 않는다. lines는 단지 파일의 포인터일 뿐이다.
두번째 라인은 lineLengths를 정의하고 잇으며 이는 map 변환을 수행하고 있다. 다시한변 lineLengths는 즉시 수행되지 않는다. 이는 laziness 때문이다.
최종적으로 reduce를 수행하며 이것은 action이다. 이 포인트에서 Spark는 태스크들을 몇몇 머신으로 작업을 분산 시키고, 각 머신은 map의 부분을 수행한다. 그리고 local reduction작업을 수행한다. 결과적으로 최종 결과는 driver 프로그램으로 반환된다.

만약 lineLengths라는 것을 이후에도 사용하고자 한다면 다음과 같이 하면 된다.
lineLengths.persist()
reduce이전에 lineLengths로 인해서 처음 수행이 끝이나면 메모리로 저장이 된다.

Passing Functions to Spark

Spark의 API는 driver 프로그램에서 클러스터로 수행되기 위해서 전달된다. 여기에는 3가지 추천 방법이 있다.
1. Lambda expressions
- 단순 함수를 위해서 표현식을 사용할 수 있다. (Lambda는 복수개의 함수들 혹은 스테이트먼트를 지원하지 않는다. 이것은 값을 바환하지 않는다.)

2. Local defs로 Spark로 호출되는 함수이다. 더 긴 코드

3. 모듈로 구성된 최상위 레벨의 함수들

예를 들어 lambda를 이용하는 것 보다 더 긴 함수를 전달하는 것은 다음과 같다.
"""MyScript.py"""
if __name__ == "__main__" :
    def myFunc(s) :
        words = s.split("")
        return len(words)
    sc = SparkContext(...)
    sc.textFile("file.txt").map(myFunc)
주의 : 클래스 인스턴스에 메소드의 참조를 전달할 수 있다. ( singleton object 에 반대하는것과 같이) 이것은 객체를 전달하는 것을 필요로 한다. 이러한 내용은 클래스가 메소드를 포함하고 있다는 것이다.
다음예를 보자.
class MyClass(object) :
    def func(self, s) :
        return s
    def doStuff(self, rdd) :
        return rdd.map(self.func)
만약 우리가 MyClass를 생성하고, doStuff를 호출한다면 map 내부에서는 MyClass인스턴스의 func메소드를 참조하고 있게 된다. 그래서 전체 객체가 클러스터에 전달 되어야 하는 문제가 생긴다.

유사한 방법으로 외부 객체의 접근은 전체 객체를 참조하게 된다.
class MyClass(object) :
    def __init__(self) :
        self.field = "Hello"
    def doStuff(self, rdd) :
        return rdd.map(lambda s : self.field + s)
이러한 이슈를 피하기 위해서는 단순한 방법으로 로컬 변수에 필드 값을 복사해서 해당 값을 전달하는 것이다. 전체 객체를 전달하는 것 대신에.
def doStuff(self, rdd) :
    field = self.field
    return rdd.map(lambda s : field + s)

Understanding closures

Spark에서 어려운 작업중에 하나는 scope의 이해와 변수의 라이프사이클, 그리고 클러스터들 사이에서 코드가 실행될때 메소드 들이다.
RDD작업은 자신의 scope의 외부 변수들을 수정한다. 이것은 자주 혼란한 소스를 만들어 낸다. 아래 예제에서 우리는 foreach를 이용하여 counter를 증가시키는 예를 그러한 내용을 확인해볼 것이다. 그러나 유사한 이슈가 다른 작업을 위해 발생할 수 있다.

Example
Native RDD 엘리먼트의 sum을 생각해보자. 이것은 동일한 JVM상에서 실행이 되고 있는것인지에 따라서 차이가 난다. 공통적인 예로 Spark가 local모드로 수행되고 있을때와 (--master = local[n]) Spark가 클러스터에서 수행될때(spark-submit로 YARN에서 수행될때)가 그것이다.

counter = 0
rdd = sc.parallelize(data)
# 오류 : 이렇게 하지말라.
def increment_counter(x)
    global counter
    counter += x
rdd.foreach(increment_counter)
print("Counter value: ", counter)
 Local Vs Cluster modes
상단코드의 행동은 정의되지 않았다. 그리고 의도되로 동작하지 않을 것이다. 작업 수행을 위해서 Spark는 RDD오퍼레이션 처리를 태스크로 분리하게 된다. 각 태스크는 executor에 의해서 수행된다. 이전 실행은 Spark 는 task의 closure를 계산한다. Clouser는 변수들이며, 메소드들이다. 이것은 RDD상에서 수행되기 위해서 visible하게 되어야 한다. (이 케이스에서는 foreach()임). 이 clouser는 serialized되며 각 executor에 전달된다.

closure와 함께 변수들은 각 executor에 전달된다. 이때 복사로 진행이 된다. counter가 foreach함수들을 수행하는 것을 참조하면 더이상 counter는 driver note상에 있지 않게 된다. 이것은 여전히 counter가 메모리에 있다. 그러나 이것은 execturos에게 더이상 나타나지 말라고 하는 것이다. executors는 오직 serialized closure로 구번 목제본을 보고 수행한다. 그러므로 counter의 최종 변수는 여전히 zero가 될것이다.

In local mode, in some circumstances the foreach function will actually execute within the same JVM as the driver and will reference the same original counter, and may actually update it.
To ensure well-defined behavior in these sorts of scenarios one should use an Accumulator. Accumulators in Spark are used specifically to provide a mechanism for safely updating a variable when execution is split up across worker nodes in a cluster. The Accumulators section of this guide discusses these in more detail.
In general, closures - constructs like loops or locally defined methods, should not be used to mutate some global state. Spark does not define or guarantee the behavior of mutations to objects referenced from outside of closures. Some code that does this may work in local mode, but that’s just by accident and such code will not behave as expected in distributed mode. Use an Accumulator instead if some global aggregation is needed.

Printing elements of an RDD









작업중......








[Spark] Apache Spark Examples

$
0
0

Apache Spark Examples

from : http://spark.apache.org/examples.html

이 예제들은 Spark API의 전체를 간략하게 보여준다. Spark는 분산 데이터셋의 개념으로 만들어 져있다. 이는 Java, Python 객체들을 포함하고 있다. 외부 데이터로 부터 데이터셋을 생성하고 병렬로 그것들을 처리할 수 있다. Spark API의building block을 확인해보자. RDD API에는 2가지 타입의 오퍼레이션을 제공하고 있다. transformation으로 이전것으로 부터 새로운 데이터셋을 만들어 내는 작업을 한다. 다음으로 actions로 클러스터 상에서 작업을 시작하는 것이다. Spark의 RDD API의 최상위에는 고수준의 API를 제공하며 DataFrame API와 Machine Learning API가 그것이다. 이 고수준 API는 특정 데이터 오퍼레이션에 대한 간단한 방법으로 작업을 수행할 수 있도록 제공한다. 이 페이지에서는 RDD 고수준 API를 이용한 간단한 예제를 살펴볼 것이다.

RDD API Examples

Word Count

이 예제는 몇가지 transformation으로 데이터셋을 만들고 난 후, (String, Int)쌍의 데이터셋을 만들어 내는 작업을 한다. 이는 count라고 부른다. 그리고 다음으로 결과를 파일로 저장한다.

1. Python 버젼 : 


text_file = sc.textFile("hdfs://...")
counts = text_file.flatMap(lambda line: line.split("")) \
.map(lambda word: (word, 1)) \
.reduceByKey(lambda a, b : a + b)
counts.saveAsTextFile("hdfs://...")

2. Scala 버젼 : 

val textFile = sc.textFile("hdfs://...")
val counts = textFile.flatMap(line => line.split(""))
        .map(word => (word, 1))
        .reduceByKey(_ + _)
counts.saveAsTextFile("hdfs://...")

3. Java 버젼 : 

JavaRDD<String> textFile = sc.textFile("hdfs://...");
JavaRDD<String> words = textFile.flatMap(new FlatMapFunction<String, String>() {
    public Iterable<String> call(String s) { return Arrays.asList(s.split("")); }
});
JavaPairRDD<String, Integer> pairs = words.mapToPair(new PairFunction<String, String, Integer>() {
    public Tuple2<String, Integer> call(String s) { return new Tuple2<String, String>(s, 1); }
});
JavaPairRDD<String, Integer> counts = pairs.reduceByKey(new Function2<Integer, Integer, Integer>(){
    public Integer call(Integer a, Integer b) {return a + b;}
});
counts.saveAsTextFile("hdfs://...");

Pi Estimation

Spark는 계산에 집중적인 태스크들에 사용된다. 이 코드는 원에 "throwing darts"를 수행해서 파이를 계산하는 예제이다. 우리는 ((0, 0) to (1, 1)) 사각 정방영역에 랜덤 포인트를 찍고, 얼마나 많이 단위 단위 원 내에 들어가는지 검사하고자 한다. fraction은 Pi / 4이며, 우리는 이 공식을 우리의 계산에 이용할 것이다. 

1. Python 버전 : 

def sample(p) :
    x, y = random(), random()
    return 1 if x*x + y*y < 1 else 0
count = sc.parallelize(xrange(0, NUM_SAMPLES)).map(sample) \
        .reduce(lambda a, b: a + b)
print "Pi is roughly %f" % (4.0 * count / NUM_SAMPLES)

2. Scala 버젼 : 

val count = sc.parallelize(1 to NUM_SAMPLES).map{ i =>
    val x = Math.random()
    val y = Math.random()
    if (x*x + y*y < 1) 1 else 0
}.reduce(_ + _)
println("Pi is roughly " + 4.0 * count / NUM_SAMPLES)

3. Java 버젼 :

List<Integer> l = new ArrayList<Integer>(NUM_SAMPLES);
for (int i = 0; i < NUM_SAMPLES; i++) {
    l.add(i);
}
long count = sc.parallelize(l).filter(new Function<Integer, Boolean>() {
    public Boolean call(Integer i) {
        double x = Math.random();
        double y = Math.random();
        return x * x + y * y < 1;
    }
}).count();
System.out.println("Pi is roughly " + 4.0 * count / NUM_SAMPLES);

DataFrame API Examples

Spark에서 DataFrame는 이름으로 된 칼럼 조합의 분산 데이터 집합이다. 사용자는 DataFrame API를 이용하여 외부 데이터 소스와 스파크의 내장 분산 컬렉션에 대해서 다양한 관계 연산을 수행할 수 있다. 이때 데이터 처리를 위한 별도의 프로시저 없이 이러한 연산이 가능하다. 또한 DataFrame API기반의 프로그램은 자동적으로 스파크 내장 옵티마이저에 의해서 최적화 된다. 

Text Search

이 예제에서는 로그파일에서 에러 메시지를 찾는 예제이다. 

1. Python 버젼 

textFile = sc.textFile("hdfs://...")
# Creates a DataFrame having a single column named "line"
df = textFile.map(lambda r : Row(r)).toDF(["line"])
errors = df.filter(col("line").like("%ERROR%"))
# Counts all the errors
errors.count()
# Counts errors mentioning MySQL
errors.filter(col("line").like("%MySQL%")).count()
# Fetches the MySQL errors as an array of strings
errors.filter(col("line").like("%MySQL%")).count()

2. Scala 버젼 

val textFile = sc.textFile("hdfs://...")
// Creates a DataFrame having a single column named "line"
val df = textFile.toDF("line")
val errors = df.filter(col("line").like("%ERROR%"))
// Counts all the errors
errors.count()
// Counts errors mentioning MySQL
errors.filter(col("line").like("%MySQL%")).count()
// Fetches the MySQL errors as an array of Strings
errors.filter(col("line").like("%MySQL%")).collect()

3. Java 버젼

// Creates a DataFrame having a single column named "line"
JavaRDD<String> textFile = sc.textFile("hdfs://...");
JavaRDD<Row> rowRDD = textFile.map(
  new Function<String, Row>() {
    public Row call(String line) throws Exception {
      return RowFactory.create(line);
    }
  });
List<StructField> fields = new ArrayList<StructField>();
fields.add(DataTypes.createStructField("line", DataTypes.StringType, true));
StructType schema = DataTypes.createStructType(fields);
DataFrame df = sqlContext.createDataFrame(rowRDD, schema);
DataFrame errors = df.filter(col("line").like("%ERROR%"));
// Counts all the errors
errors.count();
// Counts errors mentioning MySQL
errors.filter(col("line").like("%MySQL%")).count();
// Fetches the MySQL errors as an array of strings
errors.filter(col("line").like("%MySQL%")).collect();

Simple Data Operations

이번 예제는 데이터베이스에 있는 테이블을 읽고 각 나이별 사람의 수를 계산하는 예제이다. 최종적으로 우리는 계산된 결과를 JSON타입으로 S3에 저장한다. 단순한 MySQL테이블 "people"에는 "name"과 "age" 2개의 칼럼이 있다. 

1. Python 버젼

# Creates a DataFrame based on a table named "people"
# stored in a MySQL database
url = \
    "jdbc:mysql://yourIP:yourPort/test?user=yourUsername;password=yourPassword"
df = sqlContext \
    .read \
    .format("jdbc") \
    .option("url", url) \
    .option("dbtable", "people") \
    .load()
# Looks the schema of this DataFrame
df.printSchema()
# Counts people by age
countsByAge = df.groupBy("age").count()
countsByAge.show()
# Save countsByAge to S3 in the JSON format.
countsByAge.write.format("json").save("s3a://...")

2. Scala 버젼

// Creates a DataFrame based on a table named "people"
// stored in a MySQL database.
val url =
  "jdbc:mysql://yourIP:yourPort/test?user=yourUsername;password=yourPassword"
val df = sqlContext
  .read
  .format("jdbc")
  .option("url", url)
  .option("dbtable", "people")
  .load()
// Looks the schema of this DataFrame.
df.printSchema()
// Counts people by age
val countsByAge = df.groupBy("age").count()
countsByAge.show()
// Saves countsByAge to S3 in the JSON format.
countsByAge.write.format("json").save("s3a://...")

3. Java 버젼 

// Creates a DataFrame based on a table named "people"
// stored in a MySQL database.
String url =
  "jdbc:mysql://yourIP:yourPort/test?user=yourUsername;password=yourPassword";
DataFrame df = sqlContext
  .read()
  .format("jdbc")
  .option("url", url)
  .option("dbtable", "people")
  .load();
// Looks the schema of this DataFrame.
df.printSchema();
// Counts people by age
DataFrame countsByAge = df.groupBy("age").count();
countsByAge.show();
// Saves countsByAge to S3 in the JSON format.
countsByAge.write().format("json").save("s3a://...");

Machine Learning Example

MLib는 Spark 머신러닝 라이브러리이다. 이는 많은 ML알고리즘을 제공해주고 있다. 이 알고리즘들은 extraction, classification, regression, clustering, recommendation, 등을 제공한다. MLlib 는 또한 워크플로우 구성을 위한 파이프라인을 제공하며, 튜닝 파라미터를 위한 CrossValidator, 그리고 모델의 저장 및 로드를 위한 퍼시스턴스 모델을 제공한다.

Prediction with Learning Example

이 예제에서는 labels과 feature 벡터들의 데이터셋을 획득하고, feature 벡터들에서 부터 labels를 예측하는 학습 수행할 것이다. 이는 Logistic Regression 알고리즘을 이용한다. 

1. Python 버젼

# Every record of this DataFrame contains the label and
# features represented by a vector.
df = sqlContext.createDataFrame(data, ["label", "features"])
# Set parameters for the algorithm.
# Here, we limit the number of iterations to 10.
lr = LogisticRegression(maxIter=10)
# Fit the model to the data.
model = lr.fit(df)
# Given a dataset, predict each point's label, and show the results.
model.transform(df).show()

2. Scala 버젼

// Every record of this DataFrame contains the label and
// features represented by a vector.
val df = sqlContext.createDataFrame(data).toDF("label", "features")
// Set parameters for the algorithm.
// Here, we limit the number of iterations to 10.
val lr = new LogisticRegression().setMaxIter(10)
// Fit the model to the data.
val model = lr.fit(df)
// Inspect the model: get the feature weights.
val weights = model.weights
// Given a dataset, predict each point's label, and show the results.
model.transform(df).show()

3. Java 버젼

// Every record of this DataFrame contains the label and
// features represented by a vector.
StructType schema = new StructType(new StructField[]{
  new StructField("label", DataTypes.DoubleType, false, Metadata.empty()),
  new StructField("features", new VectorUDT(), false, Metadata.empty()),
});
DataFrame df = jsql.createDataFrame(data, schema);
// Set parameters for the algorithm.
// Here, we limit the number of iterations to 10.
LogisticRegression lr = new LogisticRegression().setMaxIter(10);
// Fit the model to the data.
LogisticRegressionModel model = lr.fit(df);
// Inspect the model: get the feature weights.
Vector weights = model.weights();
// Given a dataset, predict each point's label, and show the results.
model.transform(df).show();







[NPM] npm install시 package.json 사용하기.

$
0
0
npm 의존성 패키지를 설치하는 방법으로 package.json을 생성하여 이용하는 방법이 있다.

package.json 파일을 사용하면 얻는 이점
1. package.json 자체로 프로젝트에서 어떠한 의존성을 이용하고 있는지 설명해준다.
2. semantic version rules를 이용하여 프로젝트의 패키지 버젼을 지정할 수 있다.
3. 빌드를 다시 수행할 수 있다. 이는 다른 개발자와 협업을 쉽게 해준다.

필수사항 : 

package.json의 최소 요건은 다음과 같다.

1. "name"
- 모두 소문자로 작성 되어야한다.
- 공백 없이 한단어가 되어야한다.
- 대쉬(-), 언더스코어(_) 는 허용된다.

2. "version"

예제 :
{
  "name": "test-package",
  "version": "1.0.0"
}

package.json 생성하기

package.json을 생성하는 2가지 방법은 다음과 같다.

1. 대화형 
// 대화형식으로 물어보면서 package.json 파일을 생성한다.
npm init
대화형 패키지 생성 과정 :
KIDOui-iMac:~ KIDO$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.
See `npm help json` for definitive documentation on these fields
and exactly what they do.
Use `npm install <pkg> --save` afterwards to install a package and
save it as a dependency in the package.json file.
Press ^C at any time to quit.
name: (KIDO) test-package
version: (1.0.0)
description: my_test_package
git repository:
author: test_author
license: (ISC) ISC
About to write to /Users/KIDO/package.json:
{
  "name": "test-package",
  "version": "1.0.0",
  "description": "my_test_package",
  "main": "index.js",
  "dependencies": {
    "connect": "^3.4.1"
  },
  "devDependencies": {},
  "scripts": {
    "test": "echo \"Error: no test specified\"&& exit 1"
  },
  "keywords": [],
  "author": "test_author",
  "license": "ISC"
}

Is this ok? (yes) yes 

 2. 기본 생성형 
// 디폴트 값을 이용하여 package.json파일을 생성한다.
npm --yes
npm -y

디폴트 생성결과
KIDOui-iMac:~ KIDO$ cat package.json
{
  "name": "KIDO",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "dependencies": {
    "connect": "^3.4.1"
  },
  "devDependencies": {},
  "scripts": {
    "test": "echo \"Error: no test specified\"&& exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"

필드 설명 : 

name
- 기본적으로 author의 이름이며, 지정하지 않는경우 git의 리포지토리 이름이 세팅된다.

version
- 항상 1.0.0이다.

main
- 항상 index.js이다.

scripts
- 입력하지 않으면 기본적으로 test 스크립트를 생성한다.

keywords
- 빈값이 기본으로 들어간다.

author
- CLI가 제공된경우 해당 내용이 표시된다.

license
- ISC이다.

repository
- git등이 존재하면 현재 디렉토리의 정보를 뽑아올 것이다.

bugs
- 현재 디렉토리로 부터 정보를 뽑아온다.

homepage
- 현재 디렉토리로 부터 정보를 뽑아온다.

기본적으로 몇가지 설정 옵션을 init command에 설정할 수 있다.
> npm set init.author.email "wombat@npmjs.com"
> npm set init.author.name "ag_dubs"
> npm set init.license "MIT"

패키지 의존성 지정하기. 

패키지 의존성을 지정하는 타입은 2가지가 있다.
"dependencies" : 이 패키지들은 프로덕션에서 당신의 어플리케이션에 의해 필요한 패키지를 나열한다.
"devDependencies" : 이 패미지들은 오직 개발과 테스팅에서만 필요한 패키지를 나열한다.


수동으로 의존성 지정하기. 

- 수동으로 의존성을 지정하기 위해서는 package.json 파일을 열고 dependencies와 devDepencencies 영역을 추가하면 된다.
{
  "name": "my_package",
  "version": "1.0.0",
  "dependencies": {
    "my_dep": "^1.0.0"
  },
  "devDependencies" : {
    "my_test_framework": "^3.1.0"
  }
}

자동으로 의존성 지정하기.

1. depencencies 영역에 추가하기.
npm install <package_name> --save
2. devDepencencies 영역에 추가하기.
npm install <package_name> --save-dev

참고 : https://docs.npmjs.com/getting-started/using-a-package.json





[handlebars] 핸들바스 설치

$
0
0
핸들바스 설치 방법 :

NPM 

Handlebars의 npm 패키지는 CommonJS 감지 시스템을 위한 라이브러리와 내장 브라우저 라이브러리 둘다 포함하고 있다.

npm install --save handlebars

Require 감지 시스템은 Node와 Webpack등의 시스템에서는
require('handlebars');
 혹은 런타임에서 바로 사용
require('handlebars/runtime');

# 참고 :
브라우저 빌드는 node_modules/handlebars/dist/ 디렉토리에 위치한다.
이 방식은 precompiler를 이용하여 프리컴파일된 템플릿을 이용할때 매우 유용한 방법이다. 이는 항상 동일한 버젼에서 동작하도록 해준다.

Bower

Handlebars 는 bower compoment를 관리한다. 다음과 같이 설치가 가능하다.
bower install --save handlebars 
기본 bower 라이브러리는 UMD 스타일이며 이는 global, CJS 그리고 AMD 모듈 시스템에 적합하게 돌아간다. handlebars.js와 handlebars.runtime.js는 주요 소스 파일이다. 그러나 이 컴포넌트에는 다른 옵션들도 있다.

Manual Download

모든 handlebars릴리즈 버젼과 CI빌드들은 S3인 builds page에서 다운로드 받을수 있다.
가장 마지막에 빌드된 handlebars버젼은 handlebars-latest.js이다.

노트 : S3 빌드버젼은 커뮤니티를 위해서 제공되는 기능이다. 이것을 실제 제품에 이용하는 것은 좋지않다.

CDN

Handlebars는 CDN에서 받을수 있다.


2. handlebars 체험하기

















[handlebars] Getting Started

$
0
0

Getting Started handlebars

from : http://handlebarsjs.com/

Getting Started

Handlebars는 아래와 같이 일반 HTML과 같이 보인다.
내부에는 내장된 handlebars 표현식 {{ 와 }}로 표현하고 있다.

<div class="entry">
  <h1>{{title}}</h1>
  <div class="body">
    {{body}}
  </div>
</div>
그리고 <script>태그 안에 포함시켜 브라우저로 전달할 수 있도록 한다.
다음과 같다.

<script id="entry-template" type="text/x-handlebars-template">
  <div class="entry">
    <h1>{{title}}</h1>
    <div class="body">
      {{body}}
    </div>
  </div>
</script>
JavaScript에서 컴파일 하기 위해서 Handlebars.compile를 이용할 수 있다.
var source   = $("#entry-template").html();
var template = Handlebars.compile(source); 
또한 미리 컴파일 해 둘 수 있다.

- 실행시 필요한 라이브러리 최소화
- 브라우저에서 컴파일 하는 자원낭비를 막을 수 있다.
- 모바일 디바이스에서 수행할때 꼭 필요한 작업이라고 할 수 있다.

context 값을 이용하여 template를 실행하는 방법은 다음과 같다.
var context = {title: "My New Post", body: "This is my first post!"};
var html    = template(context);
결과는 다음과 같다.
<div class="entry">
  <h1>My New Post</h1>
  <div class="body">
    This is my first post!
  </div>
</div>
즉 {{ }} 표현식으로 된 내용이 context의 내용으로 변환되었다.

HTML Escaping

Handlebars HTML-escapes값은 {{표현식}} 으로 반환된다. 만약 escape를 원하지 않는다면 {{{ }}} 표현식을 이용하면 된다. 이는 triple-stash라고 부른다.
<div class="entry">
  <h1>{{title}}</h1>
  <div class="body">
    {{{body}}}
  </div>
</div>
context는 다음과 같다.
{
  title: "All about <p> Tags",
  body: "<p>This is a post about &lt;p&gt; tags</p>"
}
결과는 다음과 같다.
<div class="entry">
  <h1>All About &lt;p&gt; Tags</h1>
  <div class="body">
    <p>This is a post about &lt;p&gt; tags</p>
  </div>
</div>
Handlebars.SafeString을 이용하면 escape하지 않는다. 만약 helper를 작성하고 이것이 HTML을 생성하는 것이라면 new Handlebars.SafeString(result)를 이용하면 된다.
이런 환경에서 수동적으로 파라미터를 이스케이프 할 수 있다.
Handlebars.registerHelper('link', function(text, url) {
  text = Handlebars.Utils.escapeExpression(text);
  url  = Handlebars.Utils.escapeExpression(url);
  var result = '<a href="' + url + '">' + text + '</a>';
  return new Handlebars.SafeString(result);
});
이 코드는 파라미터로 전달된 내용을 이스케이프 할 것이다. 그러나 결과 값으로 safe로 마크하게 된다. 그러므로 Handlebars는 "triple-stash"를 기술하지 않더라도 이스케이프 처리를 하지 않게 된다.

Block Expressions

Block Expressions는 헬퍼를 지정하도록 해준다. 이것은 현재와 다른 서로다른 컨텍스트에서 템플릿 섹션을 invoke하게 해준다.
이 블록 헬퍼들은 # 시작하고 마지막으로는 /로 끝이 나는 블록을 가진다.

다음 HTML내용을 살펴보자. 이는 HTML리스트를 생성하게 된다.
{{#list people}}{{firstName}} {{lastName}}{{/list}}
context : 
{
  people: [
    {firstName: "Yehuda", lastName: "Katz"},
    {firstName: "Carl", lastName: "Lerche"},
    {firstName: "Alan", lastName: "Johnson"}
  ]
}
이 헬퍼 이름을 list로 생성하고자 한다고 하자.
헬퍼는 people을 첫번째 파라미터로 받는다. 그리고 옵션들의 해시는 두번째 파라미터로 받는다.
옵션 해시는 fn으로 이름된 속성을 가지고 있다. 이는 컨텍스트와 함께 호출할 수 있으며, 일반적으로 Hanglebars template처럼 invoke하게 된다.

Handlebars.registerHelper('list', function(items, options) {
  var out = "<ul>";
  for(var i=0, l=items.length; i<l; i++) {
    out = out + "<li>" + options.fn(items[i]) + "</li>";
  }
  return out + "</ul>";
});
템플릿을 실행하면 다음과 같은 결과가 나온다.
<ul>
  <li>Yehuda Katz</li>
  <li>Carl Lerche</li>
  <li>Alan Johnson</li>
</ul> 
Block helper는 더 많은 기능이 있다. else섹션 생성 등...
options.fn(context)를 호출할때 블록헬퍼의 컨텐츠는 escape된다. 그리고 Handlebars는 블록헬퍼의 결과를 escape하지 않는다. 만약 Handlebars가 escape 처리를 하게 된다면 두번 escape되기 때문이다.

Handlebars Paths

Handlebars는 단순 패스를 지원한다. Mustache와 같이..
<p>{{name}}</p>
다음과 같은 중첩 path도 지원한다. 
<div class="entry">
  <h1>{{title}}</h1>
  <h2>By {{author.name}}</h2>
  <div class="body">
    {{body}}
  </div>
</div>
context :
var context = {
  title: "My First Blog Post!",
  author: {
    id: 47,
    name: "Yehuda Katz"
  },
  body: "My first post. Wheeeee!"
};
이는 Handlebars가 좀더 raw한 JSON 객체와 함게 템플릿을 처리할 수 있다는 것을 의미한다.

또한 ../세그먼트를 포함할 수 있으며, 이는 부모로 부터 상대적인 경로를 나타낸다.
<h1>Comments</h1>
<div id="comments">
  {{#each comments}}
  <h2><a href="/posts/{{../permalink}}#{{id}}">{{title}}</a></h2>
  <div>{{body}}</div>
  {{/each}}
</div>
../ 는 호출하는 블록마다 다르게 해석이 될 것이다.
../를 이용하는 것은 컨텍스트가 변경될때 필요하다. 그러므로 each와 같은 헬퍼들의 자식은 ../의 사용이 if와 같은 헬퍼의 자식에 따라 사용하거나 하지않게 작업이 필요할 것이다.

{{permalink}}
{{#each comments}}
  {{../permalink}}
  {{#if title}}
    {{../permalink}}
  {{/if}}
{{/each}}
이 예제에서 서로다른 블록에서 permalink 값이 존재하더라도 같이 사용될 것이다.
이러한 행위는 Handlebars4에서 지원이 된다.

Handlebars는 this키워드를 이용하여 helpers와 data fields에 대해서 이름 충돌을 해결할 수 있도록 해준다.

<p>{{./name}} or {{this/name}} or {{this.name}}</p>
상기 예제는 각각 방식 모두 헬퍼에 있는 동일한 name을 이용하지 않고, 현재 컨텍스트의 name 필드를 이용하게 된다. 

Template comments with {{!-- --}} or {{! }}.

커멘트는 다음과 같이 사용할 수 있다. 
<div class="entry">
  {{!-- only output author name if an author exists --}}
  {{#if author}}
    <h1>{{firstName}} {{lastName}}</h1>
  {{/if}}
</div>
커멘트는 출력에 나타나지 않는다.

<div class="entry">
  {{! This comment will not be in the output }}
  <!-- This comment will be in the output -->
</div>
어떠한 커멘트든 반드시 }} 을 포함하거나 다른 핸들바스의 토큰들은 {{!-- --}}을 사용해야한다.

Helpers

Handlebars의 헬퍼는 템플릿의 어떠한 컨텍스트로 부터든 접근이 가능하다. 헬퍼는 Handlebars.registerHelper 메소드를 이용하여 등록 가능하다.
<div class="post">
  <h1>By {{fullName author}}</h1>
  <div class="body">{{body}}</div>
  <h1>Comments</h1>
  {{#each comments}}
  <h2>By {{fullName author}}</h2>
  <div class="body">{{body}}</div>
  {{/each}}
</div>
컨텍스트와 헬퍼 사용
var context = {
  author: {firstName: "Alan", lastName: "Johnson"},
  body: "I Love Handlebars",
  comments: [{
    author: {firstName: "Yehuda", lastName: "Katz"},
    body: "Me too!"
  }]
};
Handlebars.registerHelper('fullName', function(person) {
  return person.firstName + "" + person.lastName;
});
결과는 다음과 같다.
<div class="post">
  <h1>By Alan Johnson</h1>
  <div class="body">I Love Handlebars</div>
  <h1>Comments</h1>
  <h2>By Yehuda Katz</h2>
  <div class="body">Me Too!</div>
</div>
헬퍼들은 함수내에서 this를 통해서 현재 컨텍스트에 접근이 가능하다.
<ul>
  {{#each items}}
  <li>{{agree_button}}</li>
  {{/each}}
</ul>
컨텍스트와 헬퍼의 사용
var context = {
  items: [
    {name: "Handlebars", emotion: "love"},
    {name: "Mustache", emotion: "enjoy"},
    {name: "Ember", emotion: "want to learn"}
  ]
};
Handlebars.registerHelper('agree_button', function() {
  var emotion = Handlebars.escapeExpression(this.emotion),
      name = Handlebars.escapeExpression(this.name);
  return new Handlebars.SafeString(
    "<button>I agree. I " + emotion + "" + name + "</button>"
  );
});
결과는 다음과 같다.
<ul>
  <li><button>I agree. I love Handlebars</button></li>
  <li><button>I agree. I enjoy Mustache</button></li>
  <li><button>I agree. I want to learn Ember</button></li>
</ul> 
만약 헬퍼가 HTML을 반환하는 경우 escape처리 되지 않기를 원할수 있다. 이때에는 Handlebars.SafeString을 이용하자.

Literals

Helper는 또한 자신들에거 전달된 literal values 를 가질 수 있을 것이다. 이것이 파리미터 아규먼트이든지, 해시 아규먼트이든지. 지원된 리터럴은 numbers, strings, true, false, null 그리고 undefined 를 포함한다.

{{agree_button "My Text" class="my-class" visible=true counter=4}}

Partials

Handlebars partials는 공유 템플릿을 생성하여 코드를 재사용 할 수 있다. 이 템플릿을 렌더링은 다음과 같이 한다.
<div class="post">
  {{> userMessage tagName="h1" }}
  <h1>Comments</h1>
  {{#each comments}}
    {{> userMessage tagName="h2" }}
  {{/each}}
</div>
partial과 context를 시용하는 경우는 다음과 같다.
Handlebars.registerPartial('userMessage',
    '<{{tagName}}>By {{author.firstName}} {{author.lastName}}</{{tagName}}>'
    + '<div class="body">{{body}}</div>');
var context = {
  author: {firstName: "Alan", lastName: "Johnson"},
  body: "I Love Handlebars",
  comments: [{
    author: {firstName: "Yehuda", lastName: "Katz"},
    body: "Me too!"
  }]
};
결과는 다음과 같다.
<div class="post">
  <h1>By Alan Johnson</h1>
  <div class="body">I Love Handlebars</div>
  <h1>Comments</h1>
  <h2>By Yehuda Katz</h2>
  <div class="body">Me Too!</div>
</div>

1. handlebars 설치
















[handlebars] Expression 알아보기.

$
0
0
from : http://handlebarsjs.com/expressions.html
Handlebars 의 expression은 Handlebars 템플릿의 기본유닛이다.
{{mustache}}를 이용할 수 있으며, 헬퍼에 이를 전달할 수 있으며 또는 해시 아규먼트에서 값처럼 사용할 수 있다.

기본사용법 : 

다음은 가장 단순한 표현식이다.
<h1>{{title}}</h1>
이 표현식의 의미는 현재 컨텍스트 내에서 title라는 프로퍼티를 찾아서 대체하라는 의미이다.
Block helper들은 현재 컨텍스트를 다룬다. 그러나 표현식의 기본적인 의미는 변경하지 않는다.

Handlebars표현식은 dot 로 분리된 표현식으로 경로 탐색을 할 수 있다.
<h1>{{article.title}}</h1>
이 표현식은 현재 컨텍스트에서 article를 찾고, title속성을 찾아서 대체하라는 의미이다.
Handlebars는 / 문법을 이용할 수 있다.  이를 이용하여 위 코드를 다음과 같이 기술 할 수 있다.
<h1>{{article/title}}</h1>
식별자는 다음에 열거된 유니코드 문자를 제외하고 어떤것이든 될 수 있다.
Whitespace ! " # % & ' ( ) * + , . / ; < = > @ [ \ ] ^ ` { | } ~

프로퍼티를 참조하는 것은 유효한 식별자가 아니다. 세그먼트 리터럴을 이용하기 위해서 [ 표기를 이용할 수 있다.
{{#each articles.[10].[#comments]}}
    <h1>{{subject}}</h1>
    <div>
        {{body}}
    </div>
{{/each}}
상단 예에서 templage는 each파라미터를 통해서 자바스크립트의 다음 고드와 같이 처리된다.
articles[10]['#comments']
Handlebars HTML escapes 값은 {{expression}}에 의해서 반환된다. 만약 이를 원하지 않는다면 "triple-stash" {{{ 를 이용하자.
{{{foo}}}

Helpers

Handlebars의 헬퍼 콜은 단순한 식별자이며, 0개 이상의 파라미터를 받을 수 있다. 이는 공백으로 나누게 된다.  Handlesbars는 상단에 기본 사용법과 완전히 동일한 방법으로 사용된다.
Handlebars.registerHelper('link', function(object) {
    var url = Handlebars.escapeExpression(object.url),
        text = Handlebars.escapeExpression(object.text);
    return new Handlebars.SafeString(
        "<a href='" + url +"'>" + text + "</a>"
    );
 });
헬퍼로 부터 HTML을 반환할때, Handlebars의 SafeString을 이용하여 기본적으로 escape하지 않을 수 있다. SafeString을 이용하면 모든 unknown 혹은 unsafe한 데이터들은 수동으로 escape된다. 이는 escapeExpression메소드를 이용한다.

또한 단순 스트링, number, boolean과 같은 타입의 파라미터를 Handlebars helper에 전달 할 수 있다.

{{{lilnk "See more..."" story.url}}
이 케이스에서 Handlebars는 2개의 파라미터를 전달한다. "See more..."와 현재 컨텍스트에서 store.url의 처리결과 값이 전달된다.

Handlebars.registerHelper('link', function(text, url) {
    url = Handlebars.escapeExpression(url);
    text = Handlebars.escapeExpression(text);
    return new Handlebars.SafeString(
        "<a href='" + url + '''>" + text + "</a>"
    );
});
이는 story.text의 값을 기반으로 동적 텍스트 값을 헬퍼에 이용할 수 있다.

{{{link story.text story.url}}}
Handlebars헬퍼는 선택적인 시퀀스인 key-value쌍들을 받을수 있다. 이는 final파리미터로 사용되며 (도큐먼테이션에서 해시 아규먼트와 같이 참조된다.)

{{{link "See more..." href-story.url class="story"}}}
해시 아규먼트에서 키들은 단순 식별자이어야 한다. 그리고 값은 Handlebars의 표현식이어야 한다.
이 의미는 값은 단순 식별자, paths, String이 될 수 있다는 의미이다.

Handlebars.registerHelper('link', function(text, options) {
    var attrs = [];
    for(var prop in options.hash) {
        attrs.push(
            Handlebars.escapeExpression(prod) + '="' + Handlebars.escapeExpression(options.hash[prop]) + '"');
    }
 
    return new Handlebars.SafeString (
        "<a " + attrs.join("") + ">" + Handlebars.escapeExpression(text) + "</a>"
    );
});
Handlebars는 추가적인 메타데이터를 제공한다. Hash argument와 같은 것으로 final parameter과 같이 helper에 제공된다.

Handlebars또한 템플릿의 블록과 함께 헬퍼를 호출하기 위한 메커니즘을 제공한다. 블록 헬퍼는 어떠한 컨텍스트에서든지 선택된 블록을 0번 이상 호출할 수 있다.

Subexpressions

Handlebars 는 subexpression을 제공한다. 이는 단일 mustache를 이용하여 복합 헬퍼를 호출할 수 있다. 그리고 내부 헬퍼의 결과를 외부 헬퍼 아규먼트로 전달 할 수 있다. Subexpression은 ( )로 구분한다.
{{outer-helper (inner-helper 'abc') 'def' }}
이 케이스에서 inner-helper는 스트링 아규먼트 'abc'를 받는 헬퍼로 호출이 되며 이 결과는 outer-helper의 첫번째 아규먼트로 전달된다. 그리고 'def'는 두번째 아규먼트로 전달된다.

Whitespace Control

템플릿의 공백은 특정 mustache스테이트먼트의 각 부분에서 ~ 문자를 브레이스{의 의해서 생략이 될 수 있다.
{{#each nav ~}}
    <a href="{{ur}}">
        {{~#if test}}
            {{~title}}
        {{~^~}}
            Empty
        {{~/if~}}
    </a>
{{~/each}}
컨텍스트 :
{
    nav: [
        {url: 'foo', test: true, title: 'bar'},
        {url: 'bar'}
    ]
}

출력결과 뉴라인과 포매팅된 공백처리 결과이다.
<a href="foo">bar</a><a href="bar">Empty</a>
이 확장은 라인 처리의 기본적인 동작이며 "standalone"헬퍼이다.
{{#each nav}}
    <a href="{{url}}">
        {{#if test}}
            {{title}}
        {{^}}
            Empty
        {{/if}}
    </a>
{{~/each}}
결과는 다음과 같다.
<a href="foo">
    bar
</a>
<a href="bar">
    Empty
</a>

Escaping

Handlebars컨텐츠는 하나 혹은 두개의 방법으로 이스케이프 될 수 있다. 이는 인라인 이스케이프 혹은 raw block helpers이다. 인라인 escape는 prefixing mustache블록으로 \으로 시작하는 블록이다. Raw block은 {{{{ mustache 브레이스로 생성된다.

\{{escaped}}
{{{{raw}}}}
    {{escaped}}
{{{{/raw}}}}



























 


[handlebars] precompiler이용하기.

$
0
0
from : http://handlebarsjs.com/precompilation.html

Handlebars프리컴파일러를 이용하면 핸들바 컴파일이 클라이언트 브라우저에서 컴파일되는 시간을 줄여주며, 필요한 핸들바스 라이브러리를 처리하는 시간을 감소 시켜 준다.

Getting Started

우선 OS X에서 node를 인스톨 하거나, npm을 인스톨 할 필요가 있다.
$ brew install node
이 가정은 Homebrew가 미리 설치 되어 있다는 것을 가정한다. 설치를 위해서는 다음을보자.

다음으로 npm패키지로 핸들바스를 설치하자.
$ npm install handlebars -g
-g옵션을 이용하면 패키지를 글로벌 속성으로 설치하겠다는 의미이다. 이러게 하면 어떠한 프로젝트에서든지 사용이 가능하다.

이제 precompiler를 사용할 준비가 되었다.
$handlebars <input> -f <output>
컴파일러는 Handlebars.template에 템플릿을 인서트 할 것이다. 만약 입력 파일이 person.handlebars라면 컴파일러는 Handlebars.template.person에 추가한다. 이 템플릿은 다음과 같은 장식으로 직접 호출 될 수 있다.

Handlebars.templates.person(context, options);
만약 precompile된 template로 작업을 한다면 디플로이된 어플리케이션에 컴파일러를 추가할 필요가 없다. 대신에 더 가벼운 "runtime"빌드를 이용할 수 있다.
<script src="/libs/handlebars.runtime.js"></script>
추가적으로 다운로드 사이즈를 줄여주기 위해서는 클라이언트 사이드의 컴파일을 제거하면 된다. 이러면 부트 타임을 극적으로 줄여준다. 컴파일 타임은 핸들바의 사용에 있어 매우 비싼 작업이다.

Optimizations

precompile를 수행할때 몇가지 최적화를 수행할 수 있다.
첫번째로 알려진 헬퍼 리스트를 지정할 수 있다.
handlebars <input> -f <output> - each -k if -k unless
handlebars 컴파일러는 이러한 헬퍼에 접근하기 위한 최적화를 할 수 있도록 해준다.

그리고 컴파일 타임에 알려진 헬퍼만 필요하다면 --knownOnly 옵션을 추가하여 최소한의 코드를 생성하도록 할 수 있다. 이는 더 빠르게 수행되도록 해준다.

Usage

Usage: node ./bin/handlebars [template|directory]...

Options:
  -f, --output         Output File
  --map                Source Map File                                                    [string]  [default: undefined]
  -a, --amd            Exports amd style (require.js)
  -c, --commonjs       Exports CommonJS style, path to Handlebars module                                 [default: null]
  -h, --handlebarPath  Path to handlebar.js (only valid for amd-style)                                     [default: ""]
  -k, --known          Known helpers
  -o, --knownOnly      Known helpers only
  -m, --min            Minimize output
  -n, --namespace      Template namespace                                              [default: "Handlebars.templates"]
  -s, --simple         Output template function only.
  -N, --name           Name of passed string templates. Optional if running in a simple mode. Required when operating
                       on multiple templates.
  -i, --string         Generates a template from the passed CLI argument.
                       "-" is treated as a special value and causes stdin to be read for the template value.
  -r, --root           Template root. Base value that will be stripped from template names.
  -p, --partial        Compiling a partial template
  -d, --data           Include data when compiling
  -e, --extension      Template extension.                                                       [default: "handlebars"]
  -b, --bom            Removes the BOM (Byte Order Mark) from the beginning of the templates.
  -v, --version        Prints the current compiler version
  --help               Outputs this message

precompiler의 노멀 모드를 이용한다면 결과 템플릿은 Handlebars.templates객체에 저장된다. 이 템플릿들은 동일한 템플릿 호출 방법으로 이용이 가능하다.

만약 단순 모드 프리컴파일을 한다면 이는 단일 자바 스크립트 메소드를 생성해낸다. 이는 Handlebars.template메소드를 거쳐야 하며, 결과 객체는 노멀로 이용될 수 있다.




[handlebars] Block Helper

$
0
0
from : http://handlebarsjs.com/block_helpers.html

Block Helper는 커스텀 iterator를 정의하고 다른 기능을 수행할 수 있도록 해준다. 그리고 새로운 컨텍스트함께 전달된 블록을 호출할 수 있다.

Basic Blocks

보여주기를 목적으로 블록 헬퍼를 정의해보다. 이는 블록을 호출하며 이는 헬퍼가 존재하지 않는다. 
<div class="entry">
    <h1>{{title}}</h1>
    <div class="body">
        {{#noop}}{{body}}{{/noop}}
    </div>
</div>

noop 헬퍼 (줄임말로 no operation이다.) 옵션 해시를 받는다. 이 옵션 해시는 (options.fn)함수를 가지고 있으며 이는 일반적인 컴파일된 Handlebars template와 같은 행동을 한다. 특히 이 함수는 컨텍스트를 받고 스트링을 반환한다. 

Handlebars.registerHelper('noop', function(options) {
    return options.fn(this);
});
Handlebars는 항상 this와 같은 현재 컨텍스트 포함하는 헬퍼를 호출한다. 그래서 this를 블록에서 호출할 수 있다. 이는 현재 컨텍스트를 의미한다. 

어떤 헬퍼들은 이러한 방법으로 컨텍스트에 전의된 전체 필드들을 획득할 수 있다. 필드에 접근하는 것은 helper에 의해서 씌여진다. 이때 패스 레퍼런스가 사용될 것이다. 상단 예제어서 noop 는 컨텍스내의 필드이며 다음과 같이 레퍼런싱 된다. 
{{./noop}}

Basic block Variation

더욱 낳은 문법을 살펴보기 위해서 다른 블록 헬퍼 정의를 살펴보자. 이는 몇가지 마크업으로 둘러쌓인 텍스트를 추가하는 것이다. 
<div class="entry">
    <h1>{{title}}</h1>
    <div class="body">
        {{#bold}}{{body}}{{/bold}}
    </div>
</div>

bold헬퍼는 텍스트를 볼드로 만들어주는 마크업을 추가할 것이다. 이때 함수는 컨텍스트를 받아 들이고 스트링을 반환한다. 
Handlebars.registerHelper('bold', function(options) {
    return new Handlebars.SafeString(
        '<div class="mybole">'
        + options.fn(this)
        + '</div>');
});

The with helper

with 헬퍼는 헬퍼에게 파라미터를 어떻게 전달하는지 보여준다. 헬퍼가 호출될때 파라미터를 전달하기 위해서는 context와 함께 전달한다. 
<div class="entry">
    <h1>{{title}}</h1>
    {{#with story}}
        <div class="intro">{{{intro}}}</div>
        <div class="body">{{{body}}}</div>
    {{/with}}
</div>
아마도 JSON객체 섹션에서 깊게 중첩된 프로퍼티 부분이 있고 반복적인 부모 이름을 피하여 사용할 것이다. 
{
    title: "First Post",
    story: {
        intro: "Before the jump"
        body: "After the jump"
    }
}
이러한 헬퍼의 구현은 noop헬퍼의 구현과 매우 많이 닮았다. 헬퍼들은 파라미터를 받을 수 있다. 그리고 파라미터들은 내부적으로 {{mustache}}블록을 통해서 직접적으로 사용할 수 있다. 
handlebars.registerHelper('with', function(context, options) {
    return options.fn(context);
});
파라미터들은 헬퍼에 전달되며 기술한 순서대로 전달된다. 

Simple Iterators

block 헬퍼의 공통적인 유즈케이스는 커스텀 iterator를 정의하는 것이다. 사실 모든 핸들바스의 내장 헬퍼들은 일반적인 핸들바스 블록 헬퍼로 정의 되어 있다. each 헬퍼가 어떻게 동작하는지 보자. 

<div class="entry">
    <h1>{{title}}</h1>
    {{#with story}}
        <div class="intro">{{{intro}}}</div>
        <div class="body">{{{body}}}</div>
    {{/with}}
</div>
<div class="comments">
    {{#each comments}}
        <div class="comment">
            <h2>{{subject}}</h2>
            {{{body}}}
        </div>
    {{/each}}
</div>
이 케이스에서 우리는 comments의 배열의 각 엘리먼트가 each에 전달된 블록을 호출하기를 원할 것이다. 

Handlebars.registerhelper('each', function(context, options) {
    var ret = '';
    for (var i=0, j=context.length; i<j; i++) {
        ret = ret + options.fn(context[i]);
    }
    return ret;
});
이 예제에서 우리는 전달된 파라미터를 iterate를 통해서 접근하며 각 아이템들을 한번씩 호출한다. 우리는 스트링을 만들고 그것을 반환한다. 

이 패턴은 더 향상된 iterator를 구현을 사용할 수 있도록 한다. 예를 들어 <ul>로 래핑된 내용을 만들고, 내부의 각 결과는 <li>로 래핑하고자 할수도 있다. 
{{#list nav}}
    <a href="{{url}}">{{title}}</a>
{{/list}}
컨텍스트는 다음과 같다. 
{
    nav: [
        {url: "http://www.yehudakatz.com", title: "Katz Got Your Tongue" },
        {url: "http://www.sproutcore.com/block", title: "SproutCore Blog" },
    ]
}
헬퍼는 원래 each헬퍼와 유사하다. 

Handlebars.registerHelper('list', function(context, options) {
    var ret = "<ul>";
    for (var i=0, j=context.length; i<j; i++) {
        ret = ret + "<li>" + options.fn(context[i]) + "</li>";
    }
    return ret + "</ul>";
});
라이브러리는 underscore.js 혹은 SproutCore의 런타임 라이브러리와 같이 이용하며 이는 쫌 이쁘게 만들어 준다. 예를 들어 여기에는 SproutCore의 런타임 라이브러리와 같다. 

Handlebars.registerHelper('list', function(context, options) {
    return "<ul>" + context.map(function(function(item) {
        return "<li>" + options.fn(item) + "</li>";
    }).join("\n") + "</ul>";
});

Conditionals

다른 일반적인 유즈케이스는 조건문을 수행하는 것이다.  iterator 과 같이 핸들바스는 내장된 if와 unless 컨트롤 구조를 가지고 있으며 이는 일반적인 핸들바스 헬퍼이다. 

{{#if isActive}}
    <img src="start.gif" alt="Active">
{{/if}}
컨트롤 구조는 일반적으로 현재 컨텍스트를 변경하지 않는다. 대신에 이는 몇가지 변수에 대해서 어떠한 블록을 수행해야할지를 결정하게 된다. 

Handlebars.registerHelper('if', function(conditional, options) {
    if (conditional) {
        return options.fn(this);
    }
});
조건을 작성할때 조건에 만족하지 않을때 다른 구문을 실행하고자 할 수 있다. 이럴때는 else 블록 헬퍼를 이용할 수 있다. 

{{#if isActive}}
    <img src="star.gif" alt="Active">
{{else}}
    <img src="cry.gif" alt="Inactive">
{{/if}}
Handlebars는 options.inverse와 같은 else를 위한 프라그먼트 블록을 제공한다. else 프라그먼트의 존재여부를 위한 체크를 할 필요가 없다. Handlebars는 자동적으로 찾아낼 것이며, "noop"함수를 등록한다.

Handlebars.registerHandler('if', function(conditional, options) {
    if (conditional) {
        return options.fn(this);
    } else {
        return options.inverse(this);
    }
});
Handlebars는 추가적인 메타데이터를 제공하여 블록헬퍼에게 전달할 옵션 헬퍼 프로퍼티를 추가할 수 있도록 해준다.

조건은 else mustache에 추가적으로 검사를 진행 할 수 있다.
{{#if isActive}}
    <img src="star.gif" alt="Active">
{{else if isInactive}}
    <img src="cry.gif" alt="Inactive">
{{/if}}
이후 처리를 위한 헬퍼와 동일하게 unless 헬퍼는 else위치에 사용할 수 있다. 헬퍼 값들이 다른경우 닫는 mustache는 여는 헬퍼의 이름과 매치 시켜주면 된다.

Hash Arguments

보통의 헬퍼와 같이 블록 헬퍼는 옵셔널 해시를 final 아규먼트로 전달 할 수 있다. 이전의 list헬퍼를 살펴보고 다른 선택적인 속성을 추가해보나. 이는 <ul>엘러먼트가 생성될 것이다.

{{#list nav id="nav-bar" class="top"}}
    <a href="{{url}}">{{title}}</a>
{{/list}}
handlebars final hash로 options.has형태로 제공한다. 이것은 다양한 개수의 파라미터를 수용할 수 있도록 해준다. 이는 또한 optional해시를 받아들일 수 있다. 만약 템플릿이 hash 아규먼트를 제공하지 않는다면 Handlebars는 자동적으로 빈 객체를 전달하며 ( {} )으로 전달한다.  그러므로 해시 아규먼트가 존재하는지 여부를 검사할 필요가 없다.

Handlebars.registerHelper('list', function(context, options) {
    var attrs = Em.keys(options.hash).map(function(key) {
        return key + '="' + options.hash[key] + '"';
    }).join("");
    return "<ul " + attrs + ">" + context.map(function(item) {
        return "<li>" + options.fn(item) + "</li>";
    }).join("\n") + "</ul>";
});
Hash 아규먼트들은 강력한 초가적인 파라미터들을 제공할 방법을 가지고 있다. 이는 위치 멪가 아규먼트를 제공해야하는 복잡함 없이 제공한다.

Block 헬퍼들은 또한 자식 템플릿에 private 변수를 인젝트 할 수 있다. 이것은 원래 컨텍스트에 들어있지 않는 데이터와 같은 추가적인 정보를 추가하기 위한 매우 유용한 방법이다.

예를 들어 list에 작업을 할때현재 엔덱스의 값을 제공할 수 있다.
{{#list array}}
    {{@index}}. {{title}}
{{/list}}
Handlebars.registerHelper('list', function(context, options) {
    var out = "<ul>", data;
    if(options.data) {
        data = Handlebars.createFrame(options.data);
    }
    for(var i=0; i<context.length; i++) {
        if(data) {
            data.index = i;
        }
        out += "<li>" + options.fn(context[i], { data: data }) + "</li>";
    }
    out += "</ul>";
    return out;
});
private변수는 data옵션을 이용하여 제공된다. 이것은 모든 파생된 스콥에서 지정이 가능하다.

부모 스콥에서 정의된 Private변수는 path된 쿼리를 통해 접근되어진다. index필드를 접근하기 위해서 @../index가 사용되어진다.

각 헬퍼에서 새로운 데이터 프레임을 생성하는 것을 보장하는 것은 자신의 데이터에 assign하는 것이다. 그렇지 않으면 다운스트림 헬퍼는 의도하지 않은 업스트림 변수를 변경할 것이다.

또한 데이터 필드는 이전에 존재하는 데이터 객체와 상호작용 하기 위해서 추가된 것임을 보장한다. private 변수의 행동은 조건적으로 컴파일 되고 몇몇 템플릿들은 이 필드로 생성되지 않는다.

Block Parameters

Handlebars 3.0에서는 제공하고 있는 헬퍼로 부터 이름있는 파라미터를 수신받을 수 있다.
{{#each users as | user userId}}
    Id : {{userId}} Name: {{user.name}}
{{/each}}
이 예제에서 user는 현재 컨텍스트와 같은 동일한 값을 가진다. 그리고 userId는 iteration을 위한 인덱스 변수를 가진다.

이는 중첩된 헬퍼를 제공하여 이름 충돌을 회피한다. 이는 private변수들에 발생할 수 있다.
{{#each users as |user userId}}
    {{#each user.book as |book bookId}}
        User Id: {{userId}} Book Id: {{bookId}}
    {{/each}}
{{/each}}
내장 헬퍼의 넘버는 블록 파라미터들을 젝오하고 다른 커스텀 헬퍼는 blockParams옵션 필드를 통해서 제공된다.
Handlebars.registerHelper('block-params', function() {
    var args = [],
        options = arguments[arguments.length - 1];
    for(var i=0; i < argument.length - 1; i++) {
        args.push(arguments[i]);
    }
    return options.fn(this, {data: options.data, blockParams: args});
});
{{#block-params 1 2 3 as |foo bar baz|}}
    {{foo}} {{bar}} {{baz}}
{{/block-params}}
 헬퍼의 구현은 주어진 블록의 이름지어진 변수의 선언을 허용한다. 이 예제는 출력으로 1, 2, 3을 렌더시에 수행한다.

헬퍼들은 블록 파라미터들의 number를 결정할 수 있다. 이는 options.fn.blockParams 필드 템플릿을 통해서 가능하며 이는 정수 카운터이다. 이 값은 블록 파라미터의 수를 표현하며 자식 템플릿에 의해서 참조될 수 있다. 이 카운트 뒤에 있는 파라미터들은 절대 참조되지 않는다. 그리고 필요한경우 헬퍼에 의해서 안전하게 제거된다. 이것은 선택적이고 다른 추가적인 파라미터들이며 이는 템플릿에 전달된 파라미터들로 조용히 무시된다.

Raw Blocks

Raw 블록들은 처리되지 않는 mustache블록들을 처리하기 위해 필요한 템플릿으로 이용가능하다.
{{{{raw-helper}}}}
    {{bar}}
{{{{/raw-helper}}}}
이것은 컨텐츠의 인터프리터를 수행하지 않고 raw-helper 헬퍼를 실행할 것이다.
Handlebars.registerHelper('raw-helper', function(options) {
    return options.fn();
});
이는 다음과 같이 렌더링된다.
{{bar}}

[handlebars] Partials 알아보기

$
0
0
from : http://handlebarsjs.com/partials.html

Handlebars는 partials를 이용하여 템플릿을 재사용 가능하게 한다. Partials는 일반적인 Handlebars 템플릿으로 다른 템플릿을 직접적으로 호출할 수 있다.

Basic Partials

partials를 이용하기 위해서 반드시 Handlebars.registerPartial로 등록이 되어야 한다. 
Handlebars.registerPartial('myPartial', '{{name}}')
이는 myPartial이라는 partial을 등록한다. Partial들은 사전에 컴파일 되며 사전에 컴파일된 템플릿에 두번째 파라미터로 전달된다. 

partial을 호출하는 것은 partial 문법을 이용하여 호출할 수 있다. 
{{> myPartial }}
이는 myPartial로 이름지어진 partial을 렌더링한다. partial을 실행하면 이는 현재 수행되는 컨텍스트 하위에 존재하게 된다.

Dynamic Partials

sub expression문법을 이용하연 선택된 partial을 동적으로 실행할 수 있다.
{{> (whichPartial) }}
이것은 whichPartial을 실행하고 이 함수에 의해서 반환된 이름으로 된 partial을 렌더링한다.

subexpression은 변수들을 resolve하지 않는다. 그러므로 whichPartial은 반드시 함수이어야한다. 만약 단순 변수가 partial이름을 가니면 lookup 헬퍼를 이용하여 resolve할 수 있다.
{{> (lookup . 'myVariable') }}

Partial Contexts 

partial을 custom context에서 수행이 가능하며 이는 partial call을 위한 컨텍스트를 전달함으로 해서 가능하다.
{{> myPartial myOtherContext }} 

Partial Parameters

커스텀 데이터는 해시 파라미터를 통해서 partial에 전달될 수 있다.
{{> myPartial parameter=value }}
이는 parameter를 parar이 실행될때 value 값으로 전달한다.
이는 부모 컨텍스트의 데이터를 partial로 전달해서 노출하고자 할때 유용하다.

{{> myPartial name=../name }}

Partial Blocks 

partial을 렌더하기 위한 시도를 할때 보통의 행동은 not found는 error를 throw 하기 위한 구현을 하는 것이다. 만약 failover가 필요한경우, partial은 블록 구문을 이용하여 해당 처리를 한다.
{{#> myPartial }}
    Failover content
{{/myPartial}}
이것은 만약 myPartial에 대한 partial이 등록되지 않은경우 Failover content가 렌더될 것이다.

이 블록 문법은 partial에 템플릿을 전달하기 위해서 사용된다. 이는 @partial-block으로 이름지어진 특별한 partial에 의해서 실행될 수 있다.
{{#> layout }}
    My Content
{{/layout}}
layout partial은 다음을 포함한다.
Site Content
{{> @partial-block }}
이런경우 다음을 렌더한다.
Site Content
My Content
이러한 방식으로 호출될때 블록은 호출되는 시점에 partial의 컨텍스트 하위에서 수행이 된다. 깊어진 path와 블록 파라미터들은 partial템플릿 대신에 partial block에 연관되어 수행이 된다.
{{#each children as |child|}}
    {{#> childEntry}}
        {{child.value}}
    {{/childEntry}}
{{/each}}
 이것은 이 템플릿으로 부터 child.value를 렌더링한다. partial이 아니다.

Inline Partials

템플릿들은 inline 데코레이터를 통해서 블록으로 스콥된 partial들을 정의할 것이다. 
{{#*inline "myPartial"}}
    MyContent
{{/inline}}
{{#each children}}
    {{> myPartial}}
{{/each}}
이것은 myPartial 로 이름지어진 partial을 렌더링 한다. 각 child마다.

각 inline partial은 현재 블록과 모든 자식에 가능하며 이는 다른 partial의 실행을 포함한다. 이것은 레이아웃 템플릿을 포함하며 기능적으로 유사하다.
{{#> layout}}
    {{#*inline "nav"}}
        My Nav
    {{/inline}}
    {{#*inline "content"}}
        My Content
    {{/inline}}
{{/layout}}

layout partial은 다음과 같을 것이다. 
<div class="nav">
    {{> nav}}
</div>
<div class="content">
    {{> content}}
</div>

[handlebars] Built-In Helpers

$
0
0
from : http://handlebarsjs.com/builtin_helpers.html

The if block helper

if헬퍼를 이용하여 조건에 따라 블록을 렌더할 수 있다. 만약 아규면트가 false, undefined, null, "", 0혹은 []을 반환하는 경우 Handlebars는 블록을 렌더하지 않는다.
<div class="entry">
    {{#if author}}
        <h1>{{firstName}} {{lastName}}</h1>
    {{/if}}
</div>
비어있는 ( {} )컨텍스트를 이용하였다면 author은 undefined로 되며 결과는 다음과 같다.
<div class="entry">
</div>
block expression을 이용하는경우 expression이 false와 같은 값을 반환하는 경우 특정 템플릿 섹션을 실행할수 있다. 섹션은 {{else}}로 된 부분이며 이는 "else section"이라 부른다.
<div class="entry">
    {{#if author}}
        <h1>{{firstName}} {{lastName}}</h1>
    {{else}}
        <h1>Unknown Author</h1>
    {{/if}}
</div>

The unless block helper

unless블록을 이용하여 if헬퍼의 inverse 처리를 할 수 있다. 이 블록은 만약 표현식이 false 값인경우 렌더링한다.
<div class="entry">
    {{#unless license}}
    <h3 class="warning">WARNING: This entry does not have a license!</h3>
    {{/unless}
</div>
만약 현재 컨텍스트에서 license를 살펴보고 결과가 false 값이라면 핸들바스는 warning을 렌더링한다. 그렇지 않은경우 렌더링 하지 않는다.

The each block helper

each를 이용하여 반복을 처리할 수 있다. 블록 내부에서는 this를 이용하여 각 엘러먼트를 레퍼런스 할수 있다.
<ul class="people_list">
    {{#each people}}
        <li>{{this}}</li>
    {{/each}}
</ul>
컨텍스트는 다음과 같다.
{
    people: [
        "Yehuda Katz",
        "Alan Johnson",
        "Charles Jolley"
    ]
}
결과는 다음과 같다.
<ul class="people_list">
    <li>Yehuda Katz</li>
    <li>Alan Johnson</li>
    <li>Charles Jolley</li>
</ul>
this 표현식을 이용하여 특정 컨텍스트를 현재 컨텍스트를 기준으로 참조할 수 있다.

또한 선택적으로 {{else}} 섹션을 제공하여 리스트가 비어있는경우 처리를 할 수 있다.
{{#each paragraphs}}
    <p>{{this}}</p>
{{else}}
    <p class="empty">No content</p>
{{/each}}
 추가적으로 객체를 위한 iteration은 {{@key}} 를 통해서 현재 key를 참조할 수 있다.
{{#each object}}
    {{@key}}: {{this}}
{{/each}}
iterator의 첫번째와 마지막 스텝은 @first와 @last를 통해서 접근이 가능하다.

중첩된 each블록은 path를 기반으로 깊이값을 통해서 반복에 접근할 수 있다. parent인덱스에 접근하기 위해서는 {{@../index}}를 이용할 수 있다.

each헬퍼는 또한 block parameters를 제공한다. 이는 이름 지어진 참조를 블록 내부 어디서든지 접근하게 해준다.
{{#each array as |value key|}}
    {{#each child as |childValue childKey|}}
        {{key}} - {{childKey}}. {{childValue}}
    {{/each}}
{{/each}}
이는 key와 value변수를 생성하고 자식은 depth 변수를 참조하지 않고서도 접근이 가능하다. 이 예제에서는 {{key}}는 {{@../key}}를 나타낸다. 그러나 많은 케이스에서 더 읽기 쉽다.

The with Block Helper

보통 핸들바스 템플릿은 컴파일된 메소드로 전달된 컨텍스트에 대해서 처리를 수행한다.
var source = "<p>{{lastName}}, {{firstName}}</p>";
var template = Handlebars.compile(source);
template({firstName: "Alan", lastName: "Johnson"});
결과는 다음과 같다.
<p>Johnson, Alan</p>
내장된 with블록 헬퍼를 이용하여 template의 섹션을위한 컨텍스트를 옮길 수 있다.
<div class="entry">
    <h1>{{title}}</h1>
    {{#with author}}
        <h2>By {{firstName}} {{lastName}}</h2>
    {{/with}}
</div>
컨텍스트는 다음과 같다.
{
    title: "My first post!",
    author: {
        firstName: "Charles",
        lastName: "Jolley"
    }
}
결과는 다음과 같다.
<div class="entry">
    <h1>My first post!</h1>
    <h2>By Charles Jolley</h2>
</div>
with는 또한 block parameters와 함께 사용된다. 이는 현재 블록내의 참조를 정의한다.
다음 예제는 위 예제를 변형한 것이다.

<div class="entry">
    <h1>{{title}}</h1>
    {{#with author as |myAuthor|}}
        <h2>By {{myAuthor.firstName}} {{myAuthor.lastName}}</h2>
    {{/with}}
</div>
이 코드는 ../와 같은 깊이 참조자를 이용하는 것보다 잠재적으로 복잡한 템플릿을 이용할때 더 명확하게 해준다.

전달된 값이 비어있는 경우를 위해서 선택적으로 {{else}}를 이용할 수 있다.
{{#with author}}
    <p>{{name}}</p>
{{else}}
    <p class="empty">No content</p>
{{/with}}

The lookup helper

lookup 헬퍼는 handlebars변수를 이용하여 동적 파라미터를 참조하기 위한 헬퍼이다. 이는 배열의 인덱스를 해석하기 위해 유용하다.
{{#each bar}}
    {{lookup ../foo @index}}
{{/each}}

The log block helper

log 헬퍼는 템플릿이 수행될때 컨텍스트의 상태를 로깅하기 위한 목적으로 사용된다.
{{log "Look at me!"}}
Handlebars.logger.log를 위임하여 커스텀 로깅을 오버라이딩 할 수 있다.
여러개의 아규먼트를 이 메소드에 전달하여 로거를 찍을 수 있다.

{{log "This is logged" foo "And so is this"}}
log level은 해시 파라미터를 이용하여 지정이 가능하며 debug, info, warn, error를 지정할 수 있다. 이 값이 없으면 기본적으로 warn으로 설정된다.

{{log "Log!" level="error"}}
로깅은 선택적으로 Handlebars.logger.level에 설정한 값에 따라 출력이 된다. 기본값은 info이다. 모든 로그 스테이트먼트는 현재 레벨을 출력시 설정할 수 있다.

The blockHelperMissing helper

environment's helpers hash에서 직접적으로 resolve할 수 없는 경우에 암묵적으로 호출된다.
{{#foo}}{{/foo}}
이것은 이 헬퍼를 호출하며 현재 컨텍스트 상에서 foo의 값을 해석한다. 그리고 options.name 필드에 "foo"를 설정한다.

이것은 사용자에 의해서 오버라이드 될것이다. 다음과 같이 블록 실행을 통해 변경이 가능하다.
Handlebars.registerHelper('blockHelperMissing', function(context, options) {
    throw new Handlebars.Exception('Only if or each is allowed');
});
이는 mustache-style블록 실행의 사용을 막기위해 사용될 수 있다. 이는 더 효과적인 if와 each 헬퍼들을 위한 목적이다.

The helperMissing helper

내부적 헬퍼는 특정 황경에서 헬퍼가 발견되지 못하는 경우 혹은 현재 컨텍스트 내에서 발견되지 못하는 경우에 호출된다. 양쪽 케이스에서 이것은 blockHelperMissing 헬퍼를 우선적으로 수행한다.
{{foo}}
{{foo bar}}
{{#foo}}{{/foo}}
각각은 이 헬퍼를 호출하고, 특정 아규먼트들을 전달한다. 이 헬퍼는 knownHelperOnly모드에서는 호출되지 않는다.

이것은 어플리케이션에서 오버라이딩 될수 있다.
Handlebars.registerHelper('helperMissing', function(/* [args, ] options */) {
    var options = arguments[arguments.length - 1];
    throws new Handlebars.Exception('Unknown field: ' + options.name);
});

































[Tabata Training] 타바타 트레이닝이란?

$
0
0
일반적으로 운동은 지속적으로 오랫동안 해야한다고 알려져 있다.

아마도 운동을 함으로써 힘의 증가, 살 빼기, 유연성 증가, 근육 키우기 등을 목표로 할 것이다.
대부분의 운동 프로그램은 가능한 오랫동안 고정적인 플랜으로 운동을 하라고 한다.
그러나 이러한 운동은 오랜 시간 그리고 지루함, 무엇보다 지속적으로 하기가 어려운 단점이 있다.

연구끝에 Tabata라는 새로운 운동 방법이 등장하였고, 이는 high-intensity interval training (HIIT)로 불리는 운동이다.

Tabata의 역사 : 

Tataba트레이닝은 일본의 과학자 Izumi Tabata와 그의 연구팀에 의해서 도쿄에 있는 National Insitute of Fitness and Sport 연구소에서 탄생하였다.

Tabata와 그의 팀은 2개의 팀으로 나누어 연구를 하였고
첫번째 그룹은 평이한 강도의 운동을 하고, 두번째 그룹은 고강도의 운동을 하도록 나누었다.

첫번째 그룹

  • 매주 5일 운동, 6주간 수행
  • 평이한 강도의 운동
  • 매 운동은 1시간 수행

두번째 그룹

  • 매주 4일 운동, 6주간 수행
  • 고강도 운동
  • 매 운동은 4분동안 수행하며 20초 운동 10초 휴식을 반복.

운동의 결과
첫번째 그룹

  • 에어로빅 시스템 향상 (cardiovascular 심폐)
  • 비 에어로빅 시스템 (muscle 근육)은 작거나 변화 없음


두번째 그룹

  • 첫번재 그룹보다 더 향상된 에어로빅 시스템
  • 비 에어로빅 시스템 28% 향상

즉 결론은  HIIT (고강도 인터벌 트레이닝)이 에어로빅 시스템과 비 에어로빅 시스템 둘다에 더 큰 효과가 있음을 보였다.

Tabata 프로그램

각 훈련은 오직 4분간만 수행한다. 그러나 주어진 4분동안 최대한 극복하여 운동을 수행해야한다.
타바다 프로그램은 다음과 같다.

  • 20초동안 고강도로 운동
  • 10분 휴식
  • 8라운드 반복

20초동안은 본인이 생각하기에 가능하면 더 힘들고 어렵게 운동을 하고, 10초를 쉰다 이것이 한 세트이다. 이렇게 해서 8세트를 수행한다.

그리고 이러한 훈련에서 원하는 어떠한 운동이든 상관없다.
스쾃, 푸시업, 버핏 혹은 다른 대근육을 키우는 운동을 수행한다. 케틀벨 운동역시 매우 좋다.

Tabata 프로그램 운동은 다음과 같이 할 수 있다. 

  1. Push-ups (4분)
  2. Bodyweight Squats (4분)
  3. Burpeets (4분)
  4. Mountain Climbers (4분)

푸시업을 시작으로 20초동안 수행하고 10초 쉬기를 한다. 그리고 다시 20초동안 푸시업을 한다. 8세트를 끝내고 나면 1분을 쉰다.

다음으로 스쾃을 동일하게 반복한다. 끝나고 나면 1분을 쉬고, 다시 버핏을 한다. 그리고 마운틴 클라이밍을 수행한다.

Tabata는 짧은 시간에 매우 신속하게 수행하는 운동이다. 인내성과 스피드를 향상시키기를 원한다면 루틴을 변경하면 된다.

Tabata Workouts and Exercises 샘플 : 




[handlebars] Reference

$
0
0

Base

from : http://handlebarsjs.com/reference.html

Handlebars.compile(template, options)

template 을 컴파일 하는 작업은 다음과 같이 수행하면 즉시 수행된다.
var template = Handlebars.compile('{{foo}}');
template({});

  • template :를 수행할때 어떻게 수행할지에 대한 다양한 옵션들이 다음과 같이 있다. 
  • data : false로 설정하면 @data 트래킹을 disable한다. 
  • compat : true로 설정하면 recursive 필드 룩업을 수행한다. 
  • knownHelpers : Hash로 헬퍼의 리스트를 포함하고 있다. 이는 템플릿이 실행되는 타임에 존재하는지 파악하게 된다. 이때 이 값을 전달하면 옵티마이즈된 결과로 컴파일러가 컴파일한다. 내부 헬퍼는 자동적으로 포함되어진다. 값을 false로 설정하는 것으로 제거 할 수 있다. 
  • knownHelpersOnly : true로 설정하면 알려진 헬퍼의 리스트를 기반으로 최적화 한다. 
  • noEscape : true로 설정하면 어떠한 내용도 escape하지 않는다. 
  • strict : strict모드로 실행한다. 이 모드에서는 템플릿은 유실된 필드에 대해서 무시하지 않고 오류를 던진다. 이것은 {{^foo}}{{/foo}} 와 같은 inverse오퍼레이션을 disable하는 사이드 이펙트가 있다. 그렇지 않으면 소스 객체내에서 명시적으로 포함된다. 
  • assumeObjects : path를 트래버싱 할때 체크된 객체를 제거한다. 이는 strict모드의 서브셋이며, 데이터 입력들이 안전하다고 판단되면 옵티마이즈된 템플릿을 생성한다. 
  • preventIndent : 기본적으로 인덴트된 partial콜을 수행한다. 이는  pre태그를 patial이 작성할때 기대하지 않는 행동을 하는 것을 막아준다. 이 옵션을 true로 지정하면 auto-indent 기능이 disable된다. 
  • ignoreStandalone : true로 지정하면 standalone태그를 제거하는 작업을 disable하게 한다. block과 partial에 설정하면 이는 해당 라인에서 공백을 제거하지 않는다. 
  • explicitPartialContext : partial을 위한 컨텍스트를 disable한다. enabled된 경우 partial은 컨텍스트 값이 전달되지 않으며 비어있는 객체로 실행이 된다. 

Handlebars.precompile(template, options) 

주어진 템플릿을 precompile한다. 이를 통해서 클라이언트에 전달하게 되면 클라이언트는 컴파일 없이 수행된다.
var templateSpec = Handlebars.precompile('{{foo}}');
이는 Handlebars.compile에서 제공하는 모든 옵션을 이용할 수 있다. 추가적으로 다음 값을 전달할 수 있다.

  • srcName : 입력 파일을 위한 소스 맵을 생성하기 위해 전달한다. 이 방법으로 수행이 되면 반환되는 구조는 {code, map}으로 code에는 템플릿 정의가, map에는 소스 맵이 들어간다. 
  • destName : 선택적인 파라미터로 srcName과 결합된다. 이는 소스 맵을 생성할때 목적지 파일 이름을 제공한다. 

Handlebars.template(templateSpec)

Handlebars.precompile와 함께 precompile된 템플릿을 설정한다. 
var template = Handlebars.template(templateSpec);
template({});

Handlebars.registerPartial(name, partial)

environment 에서 특정 템플릿에 의해 접근 가능한 partial을 등록한다.
Handlebars.registerPartial('foo', partial);

또한 한번에 복수개의 partial을 등록할 수 있다.
Handlebars.registerPartial({
    foo: partial,
    bar: partial
});
전체 라이브러리가 모두 로딩되면 partial은 필요한경우 컴파일 될 것이다. 만약 실행타임에 로딩하는 경우라면 partial은 사전에 컴파일된 템플릿 이어야한다. 이는 Handlebars.template 메소드를 이용하여 적절하게 설정할 수 있다.

Handlebars.unregisterpartial(name)

이전에 등록한 partial을 등록 해제한다.
Handlebars.unregisterPartial('foo');

Handlebars.registerHelper(name, helper)

environment에서 특정 템플릿에 의해서 접근할 수 있는 헬퍼를 등록한다.
Handlebars.registerHelper('foo', function() {
});
또한 복수개의 헬퍼를 한번에 등록할 수 있다.
Handlebars.registerHelper({
    foo: function() {
    },
    bar: function() {
    }
});

Handlebars.unregisterHelper(name)

사전에 등록된 헬퍼를 등록 해제한다.
Handlebars.unregisterHelper('foo');

Handlebars.registerDecorator(name, helper)

environment에서 특정 템플릿에서 접근이 가능한 decorator를 등록한다.
Handlebars.registerDecorator('foo', function() {
});
또한 한번에 decorator를 여러개 등록할 수 있따.
Handlebars.registerDecorator({
    foo: function() {
    },
    bar: function() {
    }
});

Handlebars.unregisterDecorator(name)

이전에 등록된 decorator를 해제한다.
Handlebars.unregisterDecorator('foo');

Handlebars.SafeString(string)

template가 렌더된경우 스트링이 escape되는 것을 막는다.
new Handlebars.SafeString('<div>HTML Content!</div>')
스트링이 생성될때 safe로 마크된다. 다른 외부 컨텐츠는 handlebars.escapeExpression메소드를 이용하여 escape된다. 이는 잠재적인 보안적인 문제를 회피한다.

Handlebars.escapeExpression(string)

전달된 스트링의  HTML을 escape한다. 이는 HTML컨텐츠에서 text렌더링시 안전하게 수행한다.
 Handlebars.Utils.escapeExpression(string)
 &<>"'`=
상기 엔티티는 적합한 값으로 변경이 된다. SafeString 값은 수정되지 않고 남아 있는다.

triple braced 표현식 {{{ }}} 을 제외하고 모든 표현식은 이 메소드를 통과하게 되어 있다. 추가적으로 헬퍼들은 이 메소드를 이용하여 SafeString  을 통과한 HTML컨텐츠를 안전하게 처리한다. 이는 code injection 가능성을 막아준다.

이 메소드는 Handlebars.Utils.escapeExpression 로 alias되어 있다.

Handlebars.createFrame(data)

block helper에 의해서 사용되며, 자식 데이터 객체를 생성한다.
if (options.data) {
    var data = Handlebars.createFrame(options.data);
    data.foo = 'bar';
    options.data = data;
}
헬퍼들은 데이터 스테이트를 변경하며 이때 새로운 프레임을 생성한다. 이때 그 자체로 고립된 상태로 그리고 다른 부모의 상태를 깨트리지 않고자 하는경우 새로운 프레임을 생성한다. 일반적으로 하나의 헬퍼가 수행될때 오직 하나의 프레임만 생성될 필요가 있다. each iterator은 오직 하나의 프레임을 생성하며 이는 모든 자식들이 수행될때 재사용 된다.

Handlebars.create()

고립된 핸들바스 환경을 생성한다.
var OtherHandlebars = Handlebars.create();
각 환경은 자신의 헬퍼들과 partials를 가진다. 이것은 구별되는 헬퍼와 partials가 필요한 케이스에서 사용하기 위해 필요하다. 대부분의 유즈케이스는 root Handlebars 환경을 직접적으로 사용할 수 있다.

주어진 환경에 대해서 생성된 템플릿은 해당 환경에 연결이 된다. 이 의미는 복수의 환경에서 수행될 필요가 있는 템플릿들은 Handlebars.template 를 이용하여 매 환경마다 새롭게 컴파일하고 생성되어야할 필요가 있다는 의미이다. 이것은 partial에도 동일하게 적용된다.

Handlebars.noConflict()

Handlebars 인스턴스를 글로벌 스페이스에서 제거하고, 이전에 등록되어진 다른 라이브러리를 저장한다.
var myHandlebars = Handlebars.noConflict();
이는 버젼 충돌에 대한 고민없이 글로벌에 로드된 라이브러리의 구분되는 버젼을 이용하도록 해준다.

Handlebars.log(level, message)

log헬퍼에 의해서 로거 기능을 사용하도록 해준다.
필요하다면 오버라이드 된다.

Utilities

Handlebars는 다양한 유틸리티 메소드를 제공하며 Handlebars.Utils객체를 통해서 사용가능하다.

Handlebars.Utils.isEmpty(value)

주어진 값이 비어있는지 확인한다.
Handlebars.Utils.isEmpty(value)
이는 if와 with헬퍼에서 사용되며 해당 컨틀로 플로우를 결정하기 위해 사용된다. Handlebar의 empty에 대한 정의는 다음과 같다.

  • 길이가 0인 배열
  • false성격의 값이나 0

Handlebars.Utils.extend(obj, value)

단순한 유틸리티 메소드로 아규먼트 obj 객체에 특정 키와 값으로 구성된 value를 적용한다.
Handlebars.Utils.extend(foo, {bar: true})
이는 객체 foo에 키가 bar인 값을 세팅하며 그 값은 true이다.

Handlebars.Utils.toString(obj)

일반적인 toString메소드이다.

Handlebars.Utils.isArray(obj)

객체가 배열인지 검사한다.

Handlebars.Utils.isFunction(obj)

객체가 함수인지 검사한다.

@data Variable

다음 @data 변수는 Handlebars와 내장된 헬퍼에 의해서 구현되었다.

@root

초기화된 컨텍스트로 템플릿이 수행된 것이다.
{{#each array}}
    {{@root.foo}}
{{/each}}
명시적으로 변경하지 않는한 이 값은 모든 페이지 렌더링에서 일관적으로 동작한다. 이 의미는 사용된 partials가 depth로된 파라미터를 통해서 그들의 부모 템플릿을 참조 할 수 없다는 것이다.

@first

각 iteration이 첫번째 엘리먼트인경우에만 해당 헬퍼가 수행된다.
{{#each array}}
    {{#if @first}}
        First!
    {{/if}}
{{/each}}

@index

0기반의 인덱스이며 이는 현재 스텝의 인덱스를 나타난다. 이는 each헬퍼에 의해서 설정한다.
{{#each array}}
    {{@index}}
{{/each}}

@key

현재 반복 스텝의 엘리먼트의 키 이름을 나타낸다. 이는 each헬퍼에 의해서 설정된다.
{{#each array}}
    {{@key}}
{{/each}}

@last

each헬퍼에 의해서 설정하며 iteration스텝의 마지막 스텝을 의미한다.
{{#each array}}
    {{#if @last}}
        Last :(
    {{/if}}
{{/each}}

@level

로그 레벨을 할당한다.
template({}, {data: {level: Handlebars.logger.WARN}})
 로그레벨은 다음과 같다.

  • Handlebars.logger.DEBUG
  • Handlebars.logger.INFO
  • Handlebars.logger.WARN
  • Handlebars.logger.ERROR

로거를 통한 로그 출력은 Handlebars.logger.level에 설정된 값 이상인경우에 출력된다.
기본값은 error모드로 설정된다.











[Sublime Text] Babel Package 추가하기

$
0
0

Install Babel Syntext Package on Sublime Text

React를 사용할때 간단한 에디터 툴로 Sublime Text를 이용하는 경우가 많다.
테스트를 할때든, 공부를 할때든 Sublime Text는 좋은 툴이다.

React 문법을 이쁘게 보여주거나, 자동완성을 위한 Sublime Text 패키지가 있어 설치방법을 소개한다.
설치방법은 다음과 같이 선택할 수 있다.

  • Sublime React
  • Babel Sublime
여기서는 Babel Sublime만을 설치한다. 사실 Babel이 더 보기가 이쁘고 좋다. 


Sublime Text에서 패키지 Controller 설치하기. 

패키지 컨트롤러를 설치하는 가잔 간단한 방법은 Sublime Text console에서 명령어를 이용하여 설치하는 방법이 있다.

sublime에서 Ctrl + ` 를 누르거나
View > Show Console 메뉴를 선택한다.

창이 열리면 다음 설치 명령어를 입력한다.
Sublime Text 3용  :
import urllib.request,os,hashlib; h = '2915d1851351e5ee549c20394736b442' + '8bc59f460fa1548d1514676163dafc88'; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); by = urllib.request.urlopen( 'http://packagecontrol.io/' + pf.replace('', '%20')).read(); dh = hashlib.sha256(by).hexdigest(); print('Error validating download (got %s instead of %s), please try manual install' % (dh, h)) if dh != h else open(os.path.join( ipp, pf), 'wb' ).write(by)
Sublime Text 2용 : 
import urllib2,os,hashlib; h = '2915d1851351e5ee549c20394736b442' + '8bc59f460fa1548d1514676163dafc88'; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); os.makedirs( ipp ) if not os.path.exists(ipp) else None; urllib2.install_opener( urllib2.build_opener( urllib2.ProxyHandler()) ); by = urllib2.urlopen( 'http://packagecontrol.io/' + pf.replace('', '%20')).read(); dh = hashlib.sha256(by).hexdigest(); open( os.path.join( ipp, pf), 'wb' ).write(by) if dh == h else None; print('Error validating download (got %s instead of %s), please try manual install' % (dh, h) if dh != h else 'Please restart Sublime Text to finish installation')
설치가 자동으로 완료 된다.

문제가 있다면 다음 사이트에서 설치 명령어를 다시 확인하자.

만약 오류가 있다면 Sublime Text를 닫았다가 다시 열고 명령어를 한번더 입력해보자.

Babel Sublime 설치하기. 

Cmd + Shift + P를 선택한다.
입력창에서 Package Controll: Install Package 를 선택한다.



여기서 Babel을 찾아서 설치한다.

설치 이후에는 다음과 같이 Babel 신텍스를 선택한다.


변경된 내용을 확인해보자.
신텍스가 정상으로 동작함을 확인할 수 있다.



더 자세한 설정을 원하면 다음 사이틀르 찾아가자.
https://github.com/babel/babel-sublime










[IntelliJ] IntelliJ에서 정적 Import 설정하기.

$
0
0

InttlliJ 정적 Import 설정 


테스트 코드를 작성하다보면 정적 import처리에서 애를 먹는다.
주로 등록해서 사용하는 패키지를 나열해본다.

org.hamcrest.Matchers
org.hamcrest.CoreMatchers
org.junit
org.junit.Assert
org.junit.Assume
org.junit.matchers.JUnitMatchers
IntelliJ에서는 어떻게 설정하는지 찾아보자.
참고로 Eclipse에서는 링크참조.

설정방법 : 

Cmd + , 혹은 상단 메뉴인 IntelliJ IDEA > Preference 를 클릭한다.

검색창에서 "Code Style"로 검색한다.
그러면 Editor > Code Style 가 검색이 된다. (직접 찾아가도 된다.)

Java를 클릭하고. Package to Use Import with '*'부분에서 '+'버튼을 클릭한다.

상기 나열된 패키지를 하나씩 등록한다.
(이때 Static 체크 박스를 체크하면 정적 import가 된다.)

이제 소스에서 자동완성을 클릭하면 원하는 정적 import가 자동으로 삽입된다.


끝 ~




[Java] Static Import 즉, 정적 import를 왜 하는가?

$
0
0

Static Import

from : http://docs.oracle.com/javase/1.5.0/docs/guide/language/static-import.html

static 멤버에 접근하기 위해서 우리는 Qualify 하게 레퍼런스를 참조해야한다. 이 멤버가 어디에서 왔는지 지정하기 위해서 필요하다.
예를 들면 다음과 같다.
double r = Math.cos(Math.PI * theta);
이를 위해서 사람들은 정적 멤버를 인터페이스에 기술하고, 이 인터페이스를 상속 받는다. 그러나 이것은 좋은 방법이 아니다. 사실 이런 나쁜 방법들에는 그것을 위한 이름이 있다. Constant Interface Antipattern( Effective Java Item 17참조). 이 문제가 되는 것은 다른 클래스의 정적 멤버를 사용하는 클래스는 상세 구현의 일부라는 것이다. 클래스가 인터페이스를 구현할때 이것은 클래스의 public API의 부분이 된다. 구현의 상세는 공개 API에 노출 되어서는 안된다.

static import 생성자는 정적 멤버에 대해서 qualified 하지 않아도 되며, 정적 멤버를 포함하고 있는 타입으로 부터 상속을 받지 않아도 된다. 대신에 프로그램은 멤버를 임포트 할 수 있다.

import static java.lang.Math.PI;
혹은
import static java.lang.Math.*;
를 이용할 수 있다.
그리고 정적 임포트를 통해서 정적 멤버에는 다음과 같이 qualification을 사용하지 않고 이용가능하다.
double r = cos(PI * theta);
정적 import 선언은 일반적인 import와 유사하다.
normal import 선언은 클래스를 package로 부터 임포트한다. 이는 패키지의  qualification없이 사용가능하도록 해준다.

정적 import선언은 클래스로 부터 정적 멤버를 임포트 한다. 이는 클래스의 qualification없이 사용 가능하도록 해준다.

언제 static import를 이용할까? 매우 드물게 (Very sparingly!). 오직 상수의 로컬 복사를 시도하는 경우 혹은 상속을 통해서 어뷰징 하고 있을때 (the Constant Interface Antipattern) 사용한다. 다른  말로 하나 혹은 두개의 클래스에서 정적 멤버를 빈번하게 접근해야할때 이용한다. 만약 static import 기능을 남용하면 코드를 매우 읽기 어렵게 하고, 유지보수 하기 어렵게 한다. namespace를 오염 시켜버리게 된다.
코드를 읽는 독자들은 정적 멤버들이 어디서 왔는지 알지 못하게 될 것이다. 클래스로 부터 정적 멤버 전체를 임포트 하는 것은 읽기를 어렵게 할 수 있다. 만약 하나 혹은 2개의 멤버를 이용하고자 한다면 그것들을 각각 이용하기 바란다.

적절하게만 사용한다면 static import는 당신을 코드를 더 읽기 쉽게 할 수 있으며, 클래스 이름을 중복으로 쓰는 boilerplate한 코드를 제거할 수 있다.

















Viewing all 50 articles
Browse latest View live