최근 회사에서 storm kafka에 대해 공부하면서 작은 미니 프로젝트를 했는데 공부했던 경험을 정리도 할겸 포스팅을 한다.

먼저 storm과 kafka에 대해 간단히 알아보도록 하자. 


storm 


storm은 실시간 분산 처리 시스템이고, 방대한 양에 데이터 스트림을 안정적으로 처리한다. storm은 실시간 분석, 머신러닝 등에 사용된다. storm 클러스터는 Hadoop 클러스터와 표면적으로 유사한데 Hadoop에서  "MapReduce job"을 실행하는 반면에, storm은 "topology"를 실행시킨다.  "jobs" 와 "topology"는 매우 다른데 한가지 핵심적으로 다른 점은 MapReduce job 은 결국 끝나게 되지만 토폴로지는 kill하지 않는 이상 계속 message를 처리한다. 

storm에 대한 좀 더 자세한 설명은 링크링크2 를 확인해 보기 바란다.








kafka


kafka는 LinkedIn에서 자신들의 내부 데이터 처리를 위해 개발한 Distriubted Processing Message System이다. 전통적인 메세지큐시스템과는 다르게 Apache Kafka는 Broker Cluster를 여러 대의 Machine으로 구성하여 분산처리가 가능하다는 장점을 가지고 있다. Big Data시장의 발전과 함께 가장 주목받고 있는 Queue System이기도 하다. 

Kafka에 대한 좀 더 자세한 설명은 링크를 확인해 보기 바란다.  

아래의 사이트도 설명이 잘 되어있다.

http://epicdevs.com/17 

http://kafka.apache.org/documentation.html#introduction




storm kafka에 대한 간단한 소개를 마쳤으니, 이제 내가 했던 작은 프로젝트에 대해 간단히 설명하고 프로젝트에 대한 step 하나 하나 설명해 보도록 하겠다. 


storm kafka 미니 프로젝트 


producer에서 로그 파일을 읽어  message를 broker(연습 용이므로 broker는 하나만 사용한다)로 publish하고  storm spout에서 consume하여 bolt에서 처리하도록 한는것이 프로젝트 목표이다.




로그 파일의 log는 다음과 같은 형태로 되어 있다고 가정한다.

@timestamp : 2015-11-10T15:32:06.046+09:00; doctype : sns; key : 974cfc83-99e0-420e-bfd1-2262e4e82dbd; appid : com.facebook.katana; appversion : 48


개발환경은 ubuntu 14.04를 기반으로 한다. 


step 1. kafka 준비 (kafka 다운로드 페이지)


다운 받은 kafka 앞축을 풀고 설치된 kafka디렉토리로 이동한다. 


(1)zookeeper 서버 실행 (zookeepr에 대한 자세한 설명은 여기 링크를 참조 하기 바란다.  )

$ bin/zookeeper-server-start.sh config/zookeeper.properties


(2)kafka서버 실행 

$ bin/kafka-server-start.sh config/server.properties


(3)topic 만들기 

kafka의 broker는 topic이라는 기준으로 메시지를 관리한다. producer에서 특정 topic의 메시지를 생성 한 후 broker에 전달하면 broker는 전달받은 메시지를 topic별로 분류하여 쌓아놓는다.  "onlytest"라는 이름의 topic을 생성하도록 하겠다. 

$ bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic onlytest



step 2. producer 준비 



log 파일을 읽어 message를 send하는 간단한 코드이다. 추가 properties 설정에 대한 자세한 사항은 이 링크에서 확인해 보기 바란다.


import java.io.File;

import java.util.Properties;

import org.apache.commons.io.input.Tailer;

import org.apache.commons.io.input.TailerListenerAdapter;

import kafka.producer.KeyedMessage;

import kafka.producer.ProducerConfig;


public class TestProducer {

private static final int SLEEP = 500;

public kafka.javaapi.producer.Producer<String,String> producer;

public void setConfig(){

Properties properties = new Properties();

       properties.put("metadata.broker.list","localhost:9092"); // broker list 필수!

       properties.put("serializer.class","kafka.serializer.StringEncoder"); //메시지를 serialize할때 사용하는 인코더

       ProducerConfig producerConfig = new ProducerConfig(properties);

       producer = new kafka.javaapi.producer.Producer<String, String>(producerConfig);

}

public static void main(String[] args) throws InterruptedException{

      

       TestProducer testProducer = new TestProducer();

       testProducer.setConfig();

       testProducer.run();

   }

private void run() throws InterruptedException {

OnlyLogListenter onlyLogListenter = new OnlyLogListenter(producer);

       Tailer tailer = Tailer.create(new File("your file path"), onlyLogListenter,SLEEP);

       while(true){

        Thread.sleep(SLEEP);

       }

   }

public class OnlyLogListenter extends TailerListenerAdapter{

kafka.javaapi.producer.Producer<String,String> producer;

public OnlyLogListenter(kafka.javaapi.producer.Producer<String,String> producer){

this.producer = producer;

}

@Override

public void handle(String line){

System.err.println(line);

KeyedMessage<String, String> message =new KeyedMessage<String, String>("onlytest",line);

    producer.send(message);

}

}

}

 

step 3. storm 준비 


(1) storm 다운받기 

storm은 요기 링크에서 다운로드 받으면 된다.(source code가 아닌 release 버전으로 다운로드 한다.) 나는 0.9.5버전을 다운로드 받았다.압축을 풀고 해당 디렉토리로 이동한다. 

나의 경우는 local에서 production cluster 모드로 테스트 하려고 하기 때문에  conf 디렉토리에 storm.yaml을 수정하도록 하겠다.

$ vi ~스톰 디렉토리/conf/storm.yaml


(2) storm.yaml 파일 수정하기 

아래와 같은 항목을 storm.yaml 파일에 추가하도록 하자  nimbus라는 것을 발견할 수 있는데 이에 대한 설명은 다음 링크에서 확인해 볼수 있다.

#zookeeper 서버 설정. local에서 production cluster모드로 테스트 해볼 것이니 실제 아이피 주소로 셋팅하자.

storm.zookeeper.servers:

      - 192.168.0.11


#nimbus host 및 seed 설정. local에서 production cluster모드로 테스트 해볼 것이니 실제 아이피 주소로 셋팅하자.

nimbus.host: 192.168.0.11

nimbus.seed: "192.168.0.11"


#storm local dir 설정 

storm.local.dir: "storm local dir path "


#storm ui  

ui.port : 8087


위와 같이 파일을 수정하고 저장한다. 


step 4. storm topology 만들기  


kafka broker에 전달된 메세지를 spout에 consume하여 bolt에서 처리 하도록 하는 코드이다. 




(1) zookeeper url을 설정한다. 

storm을 local cluster 모드로 테스트 해보자고 한다면 zookeeper 는 "localhost:2181"이 될것이다. 만약에 production cluster모드로 local에서 테스트 해보자고 한다면 "127.0.0.1:2181" 이 아닌 자신의 "실제  ip 주소 : 2181"로 셋팅하면 된다. 

String zkUrl = "zookeeper url:2181";


(2)kafkaspout설정을 해준다. 

아래 코드에 대해 좀더 자세히 알아 보고 싶으면 다음 링크에서 확인해 볼수 있다.

ZkHosts hosts = new ZkHosts(zkUrl);

SpoutConfig spoutConfig = new SpoutConfig(hosts, "onlytest", "/onlytest", UUID.randomUUID().toString());

spoutConfig.scheme = new SchemeAsMultiScheme(new StringScheme());

KafkaSpout kafkaSpout = new KafkaSpout(spoutConfig);


(3)spout과 bolt설정을 해준다. 

shuffleGrouping,fieldsGrouping 이라고 보이는데 이것을 stream grouping이라하고 토폴로지에  두개의 컴포넌트사이에서 어떻게 튜플을 send할지 알려주는 것을 말한다. 여기서 쓴 shuffleGrouping은 튜플을 무작위로 동일한 비율로 나눠서 볼트에 task를 할당하는 것이고, fieldsgrouping은 튜플에 있는 필드 값을 기준으로 파티셔닝되어 각 볼트 task에 튜플을 할당한다.  그 외 다른 stream grouping을 살펴보고 싶다면 다음 링크에서 확인해 볼 수 있다. 

TopologyBuilder builder = new TopologyBuilder();

builder.setSpout("spout", kafkaSpout, 1);

builder.setBolt("cutbolt", new CutLogBolt(), 8).shuffleGrouping("spout");

builder.setBolt("classifybolt", new ClassifyKeyBolt(), 8).fieldsGrouping("cutbolt",new Fields("key","doctype"));

builder.setBolt("docbolt", new DoctypeCountBolt(), 8).fieldsGrouping("classifybolt",new Fields("subdoctype"));


(4) nimbus host,storm local dir 등등을 설정해준다.

local mode와 cluster mode를 제량에 따라 설정할 수 있는데 나는 local에서 production cluster mode로 실행 하려고 했기 때문에 아래와 같이 설정했다. 

//=============================

// local mode

//=============================

// LocalCluster cluster = new LocalCluster();

// cluster.submitTopology("log-stat", conf, builder.createTopology());

// Thread.sleep(10000);

// cluster.shutdown();

//=============================

// cluster mode

//=============================

conf.put(Config.NIMBUS_HOST, "nimbus url");

conf.put(Config.STORM_LOCAL_DIR,"your storm local dir");

conf.put(Config.NIMBUS_THRIFT_PORT,6627);

conf.put(Config.STORM_ZOOKEEPER_PORT,2181);

conf.put(Config.STORM_ZOOKEEPER_SERVERS,Arrays.asList(new String[] {"zookeeper url"}));

// conf.setNumWorkers(20);

// conf.setMaxSpoutPending(5000);

StormSubmitter.submitTopology("onlytest", conf, builder.createTopology());


아래는 최종 topology 코드이다. 

import java.util.ArrayList;

import java.util.Arrays;

import java.util.List;

import java.util.UUID;


import soeun.storm.kafka.bolt.ClassifyKeyBolt;

import soeun.storm.kafka.bolt.CutLogBolt;

import soeun.storm.kafka.bolt.DoctypeCountBolt;

import storm.kafka.KafkaSpout;

import storm.kafka.SpoutConfig;

import storm.kafka.StringScheme;

import storm.kafka.ZkHosts;

import backtype.storm.Config;

import backtype.storm.StormSubmitter;

import backtype.storm.spout.SchemeAsMultiScheme;

import backtype.storm.topology.TopologyBuilder;

import backtype.storm.tuple.Fields;


public class StormKafakaSimpleTopology {

  

   public static void main(String[] args) throws Exception {


       String zkUrl = "zookeeper url:2181";        // zookeeper url

       String brokerUrl = "localhost:9092";


       if (args.length > 2 || (args.length == 1 && args[0].matches("^-h|--help$"))) {

           System.out.println("Usage: TridentKafkaWordCount [kafka zookeeper url] [kafka broker url]");

           System.out.println("   E.g TridentKafkaWordCount [" + zkUrl + "]" + " [" + brokerUrl + "]");

           System.exit(1);

       } else if (args.length == 1) {

           zkUrl = args[0];

       } else if (args.length == 2) {

           zkUrl = args[0];

           brokerUrl = args[1];

       }


       System.out.println("Using Kafka zookeeper url: " + zkUrl + " broker url: " + brokerUrl);


       ZkHosts hosts = new ZkHosts(zkUrl);

       SpoutConfig spoutConfig = new SpoutConfig(hosts, "onlytest", "/onlytest", UUID.randomUUID().toString());

       spoutConfig.scheme = new SchemeAsMultiScheme(new StringScheme());

       KafkaSpout kafkaSpout = new KafkaSpout(spoutConfig);

       TopologyBuilder builder = new TopologyBuilder();

builder.setSpout("spout", kafkaSpout, 1);

builder.setBolt("cutbolt", new CutLogBolt(), 8).shuffleGrouping("spout");

builder.setBolt("classifybolt", new ClassifyKeyBolt(), 8).fieldsGrouping("cutbolt",new Fields("key","doctype"));

builder.setBolt("docbolt", new DoctypeCountBolt(), 8).fieldsGrouping("classifybolt",new Fields("subdoctype"));

Config conf = new Config();

conf.setDebug(true);

List<String> nimbus_seeds = new ArrayList<String>();

nimbus_seeds.add("nimbus url");


if (args != null && args.length > 0) {

conf.setNumWorkers(3);


StormSubmitter.submitTopologyWithProgressBar(args[0], conf, builder.createTopology());

}

else {


//=============================

// local mode

//=============================

// LocalCluster cluster = new LocalCluster();

// cluster.submitTopology("log-stat", conf, builder.createTopology());

// Thread.sleep(10000);

// cluster.shutdown();

//=============================

// cluster mode

//=============================

conf.put(Config.NIMBUS_HOST, "nimbus url");

conf.put(Config.STORM_LOCAL_DIR,"your storm local dir");

conf.put(Config.NIMBUS_THRIFT_PORT,6627);

conf.put(Config.STORM_ZOOKEEPER_PORT,2181);

conf.put(Config.STORM_ZOOKEEPER_SERVERS,Arrays.asList(new String[] {"zookeeper url"}));

// conf.setNumWorkers(20);

// conf.setMaxSpoutPending(5000);

StormSubmitter.submitTopology("onlytest", conf, builder.createTopology());


}

}

}


CutLogBolt.java

import backtype.storm.topology.BasicOutputCollector;

import backtype.storm.topology.OutputFieldsDeclarer;

import backtype.storm.topology.base.BaseBasicBolt;

import backtype.storm.tuple.Fields;

import backtype.storm.tuple.Tuple;

import backtype.storm.tuple.Values;


public class CutLogBolt extends BaseBasicBolt{

@Override

public void execute(Tuple input, BasicOutputCollector collector) {

String[] splitArray = input.getString(0).split(";");

String key = "";

String doctype = "";

for(int i = 0; i < splitArray.length; i++){

if(splitArray[i].contains("key"))

key  = splitArray[i];

if(splitArray[i].contains("doctype"))

doctype = splitArray[i];

}

collector.emit(new Values(key,doctype));

}


@Override

public void declareOutputFields(OutputFieldsDeclarer declarer) {

declarer.declare(new Fields("key","doctype"));

}


}


ClassifyKeyBolt.java

import backtype.storm.topology.BasicOutputCollector;

import backtype.storm.topology.OutputFieldsDeclarer;

import backtype.storm.topology.base.BaseBasicBolt;

import backtype.storm.tuple.Fields;

import backtype.storm.tuple.Tuple;

import backtype.storm.tuple.Values;


public class ClassifyKeyBolt extends BaseBasicBolt{


@Override

public void execute(Tuple input, BasicOutputCollector collector) {

String[] splitdoctype = input.getStringByField("doctype").split(":");

String[] splitkey = input.getStringByField("key").split(":");

if(splitkey.length == 2 && splitdoctype.length == 2){

String doctype  = splitdoctype[1].trim();

String key  = splitkey[1].trim();

// System.err.println(key + ":" + doctype);

collector.emit(new Values(key + ":" + doctype));

}

}


@Override

public void declareOutputFields(OutputFieldsDeclarer declarer) {

declarer.declare(new Fields("subdoctype"));

}

}


DoctypeCountBolt.java 

import java.util.HashMap;

import java.util.Map;


import backtype.storm.topology.BasicOutputCollector;

import backtype.storm.topology.OutputFieldsDeclarer;

import backtype.storm.topology.base.BaseBasicBolt;

import backtype.storm.tuple.Fields;

import backtype.storm.tuple.Tuple;

import backtype.storm.tuple.Values;


public class DoctypeCountBolt extends BaseBasicBolt {

Map<String,Integer> docMap = new HashMap<String,Integer>();

@Override

public void execute(Tuple input, BasicOutputCollector collector) {

String doctype = input.getStringByField("subdoctype");

Integer count = docMap.get(doctype);

if(count == null)

count = 0;

count++;

docMap.put(doctype, count);

System.out.println(docMap);

collector.emit(new Values(docMap));

}


@Override

public void declareOutputFields(OutputFieldsDeclarer declarer) {

declarer.declare(new Fields("docmap"));

}

}


(5) pom.xml 


pom.xml 설정할 때 주의 할 것은 storm과 kafka의 버전의 따른 dependency 를 꼭 확인해 보고 pom.xml에 추가해야 한다는 것이다.

현재 나는 storm 버전이 0.9.5이기 때문에 

storm-core : 0.9.5

storm-kafka : 0.9.5

앞서 설치한 kafka는  kafka_2.10_0.8.1 이기 때문에 버전에 맞게 설정했다. 버전에 맞지 않으면 엄청난 삽질을 하게 된다. 

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

 <modelVersion>4.0.0</modelVersion>

 <groupId>com.soeun.storm</groupId>

 <artifactId>ministorm</artifactId>

 <version>0.0.1-SNAPSHOT</version>

 <packaging>jar</packaging>

 <name>ministorm</name>

 <url>http://maven.apache.org</url>

 <properties>

   <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

 </properties>

<dependencies>

 <dependency>

   <groupId>junit</groupId>

   <artifactId>junit</artifactId>

   <version>4.11</version>

   <scope>test</scope>

 </dependency>

<dependency>

<groupId>org.apache.storm</groupId>

<artifactId>storm-core</artifactId>

<version>0.9.5</version>

<scope>provided</scope>

</dependency>

 <dependency>

     <groupId>org.apache.storm</groupId>

     <artifactId>storm-kafka</artifactId>

     <version>0.9.5</version>

   </dependency>

   <dependency>

<groupId>org.testng</groupId>

<artifactId>testng</artifactId>

<version>6.8</version>

<scope>test</scope>

</dependency>

<dependency>

<groupId>org.mockito</groupId>

<artifactId>mockito-all</artifactId>

<version>1.9.0</version>

<scope>test</scope>

</dependency>

<dependency>

<groupId>org.apache.kafka</groupId>

<artifactId>kafka_2.10</artifactId>

<version>0.8.1</version>

<exclusions>

       <exclusion>

         <groupId>org.apache.zookeeper</groupId>

         <artifactId>zookeeper</artifactId>

       </exclusion>

       <exclusion>

               <groupId>com.sun.jmx</groupId>

               <artifactId>jmxri</artifactId>

           </exclusion>

           <exclusion>

               <groupId>com.sun.jdmk</groupId>

               <artifactId>jmxtools</artifactId>

           </exclusion>

           <exclusion>

               <groupId>javax.jms</groupId>

               <artifactId>jms</artifactId>

           </exclusion>

     </exclusions>

</dependency>

</dependencies>

<build>

 <plugins>

   <plugin>

     <artifactId>maven-assembly-plugin</artifactId>

     <version>2.2.1</version>

     <configuration>

       <descriptorRefs>

         <descriptorRef>jar-with-dependencies</descriptorRef>

       </descriptorRefs>

       <archive>

         <manifest>

           <mainClass />

         </manifest>

       </archive>

     </configuration>

     <executions>

       <execution>

         <id>make-assembly</id>

         <phase>package</phase>

         <goals>

           <goal>single</goal>

         </goals>

       </execution>

     </executions>

   </plugin>

 </plugins>

</build>

</project>


(6) maven install 


앞에 코드가 작성되어있는 프로젝트 디렉토리에 이동한후 maven clean install해준다.  

$ mvn clean install


target폴더에  ministorm-0.0.1-SNAPSHOT-jar-with-dependencies.jar 파일이 생긴 것을 확인 할 수 있을 것이다. 


step 5. storm nimbus,supervisor,ui 실행 


스톰이 설치 되어 있는 디렉토리로 이동하여 nimbus,supervisor,ui를 실행 시킨다. 

$ bin/storm nimbus


$ bin/storm supervisor


$ bin/storm ui


ui를 실행시키면 http://localhost:8087로 접근하여 topology 상황을 웹으로 확인해 볼 수 있다. 



step 6.  ministorm-0.0.1-SNAPSHOT-jar-with-dependencies.jar summit 


ministorm-0.0.1-SNAPSHOT-jar-with-dependencies.jar 를 아래와 같이 실행 시킨다 


$ bin/storm jar ministorm-0.0.1-SNAPSHOT-jar-with-dependencies.jar {main class path} 


ex)

bin/storm jar ministorm-0.0.1-SNAPSHOT-jar-with-dependencies.jar soeun.storm.kafka.topology.StormKafakaSimpleTopology 


정상적으로 submit 하면 다음과 같은 메세지가 출력된다.

..............


[main] INFO  backtype.storm.StormSubmitter - Finished submitting topology: onlytest


http://localhost:8087로 접근하면 아래와 같은 화면이 나올것이다 Topology summary에 "onlytest"라는 이름이 있으면 성공한 것이다.




step 7.  test

 

이제 위에서 작성하였던 TestProducer를 실행시켜 "onlytest"라는 topic의 메세지가 broker에 전송하여 storm worker 로그에 잘 찍히는지 확인해 보자 log는 storm디렉토리/logs/worker-.. .log에서 확인해 볼수 있다. 

$ tail -f worker-{}.log 

 또한 storm ui 에 Topology Visualization에서도 확인해 볼 수 있다. 




예제 소스는 다음 링크에 존재한다. 


-끝-


p.s storm kafka에 대한 좀더 자세한 사항을  알아보고자 한다면 다음 레퍼런스를 참고하기 바란다. 


참고 레퍼런스:

http://storm.apache.org/index.html

https://storm.apache.org/documentation/Setting-up-development-environment.html

http://bcho.tistory.com/995

https://github.com/apache/storm/blob/master/docs/documentation/Tutorial.md

https://github.com/apache/storm/tree/master/examples/storm-starter

https://storm.apache.org/documentation/Tutorial.html

Posted by 비회원

이번 포스팅에서는 약 2달 반동안 프로젝트를 진행 하면서 처음 접했던 JavaScript 프레임워크 및 라이브러리 소개 중 2편 

 JavaScript 라이브러리인 C3Dagre-D3BigScatterChart에 대한 내용을 다루려고 한다.


이전 포스팅은 주니어 개발자의 경험기 [1편 - AngularJS] 을 참고 하기 바란다.


이번 편은 JavaScript 시각화 라이브러리이다. 시각화 라이브러리는 HTML5의 SVG를 이용한것과 Canvas를 이용한 것으로 나눠진다. SVG를 이용한 라이브러리로 잘 알려진 것은 D3.js가 있다. 프로젝트에 내가 사용한 라이브러리는 SVG를 이용하고 D3.js를 기반으로 한 C3.js 라이브러리와 Dagre-D3.js 라이브러리 그리고 Canvas를 이용한 BIgScatterChart.js 라이브러리 였다. 각 라이브러리를 살펴보기에 앞서 SVG와 Canvas의 차이점에 대해 잠깐 짚고 넘어 갈까 한다.


 

Canvas 

SVG 

그래픽 시스템 

픽셀 기반의 즉시 모드 그래픽 시스템 

모양 기반의 유지모드 그래픽 시스템 

이미지 처리방식 

Bitmap (해상도 의존적) 

Vector (해상도 독립적) 

DOM 

존재하지 않음 (DOM Control 불가) 

존재함 (Script로 Control 가능) 

외부 이미지 편집

Bitmap image 편집 용이 

Vector image 편집 용이 

성능저하 요인 

높은 해상도의 이미지를 사용할 경우 

이미지가 복잡해질 경우 

Animation 

Animation API가 없으므로 Script의 Timer를 사용 

높은 수준의 Animation 지원 

외부이미지로 저장 

jpg, png등으로 저장가능 

불가능 

적합한 서비스  

Graph 구현, Game, 실시간 데이터 출력, 동영상 조작 

Graph구현, 매우 세밀한 해상도를 지원하는 UI 및 Application 

적합하지않은 서비스  

Standalone Application UI 

Game, 실시간 데이터 출력 

[표] SVG vs Canvas  (출처 - http://www.nextree.co.kr/p9307/)



SVG와 Canvas의 차이점에 대해 간단히 살펴 보았다. 그럼 다시 본론으로 들어가 C3.js 라이브러리에 대해 살펴 보자.


1. C3.js


C3.js 는 앞서 말한바와 같이 D3.js를 기반으로 되어 있는 차트 라이브러리 이다. 프로젝트에 해당 라이브러리를 채택하게된 배경은 단순했다. 워낙에 많은 차트 라이브러리가 존재하고 있기 때문에 어떤걸 선택해야될까 고민을 하다가 Google에 'Chart Library' 라고 검색을 해보았다. 많이 쓰이면 그만큼 사용자들의 만족도가 높다고 생각했고, C3.js 라이브러리는 첫페이지에 검색되어 보여졌다. 물론 많이 쓰인다는 이유만으로 선택 한 것은 아니었다. 내가 구현하고자 하는 부분을 충분히 구현 가능한가? 라는 질문에 대해 홈페이지에 있던 Reference를 통해 충분히 확인 해보았다. 당시 내가 원하는 조건은 다음과 같았다.


1. JSON 타입의 Data를 표현 할 수 있어야 된다.

2. 디자인적인 면에서 커스터마이징이 가능할 것 

3. 이벤트 동작을 지원 가능할 것

4. 마우스 오버시 Tooltip으로 정보가 표시될것 


원하는 조건을 충족할 만한 라이브러리 였기에 최종적으로 채택을 하였다. 뿐만아니라 MIT 라이센스라는 점도 선택에 한몫 거들었다. 또한 C3의 장점중 하나로 얘기 하고 싶은 것은 다양한 Example을 제공 한다는 것이었다.

C3.js는 Line chart, Bar chart, Scatter Plot, Pie chart, Donut chart, Gauge chart 등 여러 종류의 차트를 위한 api 및 예제를 제공 하고 있고, Axis, Data, Grid, Region, Interaction, Legend, Tooltip 등과 관련된 설정 및 제어가 가능하다. 공식 홈페이지의 Examples 를 통해 해당 부분에 대한 예제를 직접 수정해보며 적용해 볼 수 있다. 그외에 좀더 상세한 부분은 Reference를 참고 하면 된다. 커스터마이징 하는 것이 어렵지 않도록 되어 있고, Google에도 C3.js에 관련된 많은 정보가 있기 때문에 어렵지않게 원하는 모습의 chart를 그려낼 수 있을 것이다.


그럼 C3.js를 이용한 예제를 한번 살펴보자.

해당 예제는 공식홈페이지에서 제공하는 Example 몇개를 합친 것이다.



차트 라이브러리의 경우 다른 라이브러리도 살펴 보았지만 크게 차이가 나지 않는 선에서 대부분 사용자들의 구미에 맞게 구현이 되어 있다. 다만 개인적인 의견으로 C3의 경우 예제가 잘되어 있어 좀더 쉽게 접근이 가능해 좋았던것 같다.


2. Dagre-d3.js

다음은 Dagre-d3.js 라이브러리를 살펴 보겠다.
해당 라이브러리 역시 D3.js 를 기반으로 되어 있고 Chris Pettitt이라는 사람이 개발한 오픈소스 라이브러리로 Directed Diagram Graph를 표현 할 수 있다.
진행했던 프로젝트에서 Server Map이라고 불리우는 부분을 표현 하기 위해 사용하게 되었는데, 모티브가 된 것은 Naver Pinpoint의 Server Map 이었다. 오픈 소스인 Pinpoint에서 사용한 라이브러리를 사용 하면되지 않을까 하고 처음에 생각하였지만 Go.js라는 유료 라이브러리를 사용했다는 점에서 대체할 수 있는 다른 라이브러리를 찾게 되었다. 라이브러리 선택 조건은 다음과 같았다.

1. 자동으로 Layout을 구성할 수 있을 것
2. Directed Graph 즉, Node간 연결 관계인 간선이 방향성을 가지는 화살표로 표현이 가능할 것
3. Node에는 이미지와 Label을 간선에는 Label을 입력 할 수 있도록 지원 가능 할 것
4. 무료 오픈 소스이면서 라이센스 조건이 이용하기 편리할 것

열심히 구글링을 한 결과 Dagre-d3.js를 알게 되었고 선택하게 되었다. Dagre-d3의 Auto Layout 기능은 Graphviz의 방식을 이용해 구현한 라이브러리이다. 특징을 살펴 보면 자동으로 레이아웃을 구성해 주고, Node에 이미지 삽입이 가능하며, Node와 간선의 스타일링이 가능하고, zoom 및 마우스 이벤트 제어가 가능하고, Node 간 Grouping 이 가능하다. 해당 라이브러리를 처음 사용할 때는 그다지 불편함을 발견 하지 못하였다. 

하지만 프로젝트를 진행 하면서 큰 단점이 발견 되었는데 그건 Grouping 하여 표현할 경우 혹은 일반적인 경우에도 Node수가 많아 지면 Auto Layout 기능으로 인해 오히려 알아보기 힘들게 모양이 변형 되는 것이었다. 단순한 Diagram 을 표현할 때는 좋았지만 Data가 많아지고 복잡해지자 오히려 해당 기능이 독으로 작용을 하였다. 해당 이슈를 해결할 방법을 찾으려 노력했지만 라이브러리 개발자는 잠정적으로 commit을 중단하였고 결국은 해결하지 못하였다.
그 밖에도 나같은 초보 개발자에게는 Example이 많은 도움이 될텐데 기본적인(?) 기능의 예제만 Demo로 제공되어 있어서
활용하기가 조금은 힘이 들었다. 프로젝트를 진행하면서 Node를 클릭하는 이벤트 라던지 클릭 시 관련된 간선과 노드의 CSS를 조작한다던지 하는 부분은 직접 조작하는 코드를 추가 하였다. 이런 부분은 내가 아직 역량이 부족하여 라이브러리를 제대로 이해하지 못했거나 라이브러리 내의 기능을 발견하지 못한걸수도 있다. 

해당 라이브러리로 인해 프로젝트에 난항을 겪었던것은 사실이지만 어찌되었든 처음부터 끝까지 함께한 아래 Dagre-d3.js 라이브러리의 예제 샘플을 한번 살펴보도록 하겠다. 해당 예제는 내가 직접 프로젝트에 사용했던 코드의 일부분만 빼내어 작성한 것이다. 노드를 클릭했을 경우 해당 노드와 노드에 연결된 간선의 색이 하이라이트 되고 toggle방식으로 작동이 된다.


Dagre-d3의 Grouping기능 (cluster)은 썩 추천할만 하지않지만 예제와 같이 기본적인 모습의 graph를 표현 하기에는 나쁘지 않은 라이브러리라고 생각한다. 라이브러리에 대한 업데이트가 이후 언제 이뤄질지 장담할 수 없기때문에 더이상의 발전은 기대할 수 없는 실정이다. 해당 라이브러리를 사용하고자 한다면 이 점을 유의해야 할것 같다.



3. BigScatterChart.js


마지막으로 소개할 라이브러리는 Canvas를 기반으로한 BigScatterChart.js 라는 라이브러리이다. 해당 라이브러리는 Naver의 개발팀이 개발하여 오픈소스로 공개한 라이브러리이고 이름에서도 유추해 볼 수 있듯이 일반 ScatterChart보다는 많은 양의 Scatter를 표현하기위해 개발되었다고 한다. 개발 목적은 Naver의 Pinpoint에 적용할 Scatter Chart라이브러리를 찾다가 본인들의 요구조건에 적합한 라이브러리가 존재하지 않아 개발하게 되었다고 쓰여있다. 해당 라이브러리는 Canvas를 기반으로 개발되었는데 그 이유는 개발자의 요구조건에 쓰여있었다. 포스팅 초반에 살펴보았듯이 SVG의 큰 단점중의 하나는 많은 양의 Data를 렌더링 하게될 경우 속도가 현저히 느려지는 것이다. 해당라이브러리는 수십만개의 점을 표현가능해야 된다는 요구조건을 가지고 있었기 때문에 SVG로는 그 문제점을 해결할 수가 없었다고 한다. 기본적인 배경 소개는 이 정도로 하고 그렇다면 BigScatterChart.js가 가지고 있는 특징이자 강점을 살펴보자. 


1. 수십만개의 Data표현에 적합하다. (대략 50만개의 Data 렌더링 시 약 6초 소요)

( D3.js의 경우 대략 8만개의 Data 렌더링 시 약 33초 소요)

2. 차트영역에서 Drag & Drop으로 셀렉이 가능하다.

3. 셀렉된 부분에 담긴 Data를 검색할 수 있다.

4. 상단에 Type바가 존재하며, 이동 및 toggle이 가능하다.


내가 파악한 특징은 위와 같다. 그 밖에 내가 파악하지 못한 기능들이 더 있을 수도있겠지만, 4가지 특징만으로도 꽤나 매력있다고 생각한다. 해당 라이브러리의 경우는 원청의 요구사항으로 BigScatterChart를 사용해달라고 하여 사용하게 되었었다. 덕분에 좋은 라이브러리 하나를 더 알게 되었고, 사용해볼 수 있는 좋은 계기가 되었다. 


아래는 BigScatterChart라이브러리의 Example 예제를 jsfiddle로 옮겨 작성한 것이다.



이번 프로젝트를 준비하고 진행하면서 라이브러리 선택의 중요성을 새삼 깨달았다. 요구 조건에 맞는 라이브러리를 선택하는 것도 중요하지만 많이 쓰이고 있는 라이브러리는 그 만큼 사용하기 편했었고 그렇지 않다 하더라도 개발자의 능력에 따라 충분히 부족한 부분을 구현함으로써 채워 나갈수도 있는걸 느꼇다. 새로운 라이브러리들은 끊이 없이 쏟아져 나오고있다. 그 라이브러리가 좋다 나쁘다를 판단하기 보다는 내가 원하는 조건에는 맞는지 사용하는데 어려움이 없을지를 판단할 수 있다면 좋을 것 같다고 느꼇다. 물론 그러려면 많은 라이브러리들을 직접 경험해보고 판단하기 위한 안목(?)을 길러야 할 것이다. 

나와 같은 주니어 개발자라면 될 수 있으면 많은 라이브러리들을 사용해보고 직접 느껴보는 것이 좋은 방향이라고 생각한다. 또한 나와 같이 비록 부족하더라도 블로그가 되었든 흔적을 남겨놓는 다면 좋은 자산이 될 것같다. 해당 포스팅을 작성하면서 JSfiddle을 이용해보며 새로운 경험도 하고 사용했던 라이브러리를 다시 한 번 조사 하면서 프로젝트 준비때는 놓쳤던 부분이라던지 잘못 이해했던 부분을 깨달을 수도 있었던 것 같다. 해당 포스팅에는 주관적인 생각이 많이 담겨있고, 주니어 개발자의 수준에서 작성되었기에 잘못된 부분이 있을 수도 있다. 잘못된 부분에 대한 지적은 적극 환영 하고, 이 글을 보는 사람들에게 조금이나마 도움이 되었으면 한다. 






Posted by 한종민

이번 포스팅에서는 약 2달 반동안 프로젝트를 진행 하면서 처음 접했던 JavaScript 프레임워크 및 라이브러리를 간단히 소개 하고 사용하면서 알게된 점과 느낀점을 작성할까 한다.

총 2편의 포스팅으로 나눠서 작성을 할 예정이고, 1편은 JavaScript 프레임워크인 AngularJS에 대한 내용이고, 2편은 JavaScript 라이브러리인 D3, C3, Dagre-D3, BigScatterChart에 대한 내용을 다루려고 한다.


1편에서 다룰 AngularJS는 이번 프로젝트를 통해 처음 접하고 알게된 것이었다.


AngularJS가 무엇인지 먼저 알아보자.

AngularJS는 무엇일까? 


AngularJS의 공식사이트인 https://www.angularjs.org/ 에 들어가면 가장 메인페이지에 다음과 같이 쓰여 있다.

HTML enhanced for web apps!

[웹 앱을 위해 강화된 HTML]

그리고 'AngularJS is a structural framework for dynamic web apps. - AngularJS는 동적인 웹 앱을 위한 프레임워크이다.' 라고 정의 되어 있다. 구글링을 통해 알게된 AngularJS의 몇가지 특징은 MVW (Model-View-Whatever) Architecture 패턴을 이용한 개발이 가능하다. SPA(Single Page Application) 개발에 용이하다. 양방향 Data-Binding이 가능하다. 프레임워크의 사용으로 인해 협업이 용이하다. 등 여러가지 특징이 있었다. 이런 특징을 바탕으로 공홈에 있는 Developer Guide에 명시된 개념설명중 몇가지와 예제를 살펴보도록 하자.

설명에 앞서 AngularJS는 SPA(Single Page Application) 개발에 특화되어 있고 대부분 Module 별로 나누어 구현 하는 방식을 이용한다. 여기서 Module이란 UI Components (ex. Modal, 특정 Button Group, Line Chart, table 등등..)라고 생각하면 이해하기가 쉬울 것 같다.
ng-app이라는 지시어를 통해 AngularJS 코드가 적용 및 시작 된다. ng-app은 HTML코드에서 <html></html>과 같은 느낌이다. 

1. Template : HTML with additional markup (추가된 마크업으로 작성된 HTML)
Template은 HTML로 작성된 Module이라고 이해하면 된다.

2. Direvtives : extend HTML with Custom attributes and elements (커스텀 속성과 요소로 된 확장된 HTML)
Directives는 말그대로 사용자가 만든 HTML 속성 혹은 사용자가 만든 Tag 등이다. 보통 하나의 Directives는 하나의 Module을 담당한다.


3. View : what the user  sees (the DOM) 말그대로 DOM이다.

4. Model : View에서 사용자에게 보여지는 데이타 그리고 사용자와 상호작용하는 것(양방향 Data-binding) 이다. 자바스크립트 객체라고 생각하면 이해하기 편할 것이다.
아래 예제에서 수량과 금액을 수정할 경우 양방향 Data-binding을 통해 수정된 합계가 바로바로 적용되는 것을 확인 할 수 있을 것이다. ng-model을 통해 'qty'  'cost'를 model로 선언해 주었고 Expression인 '{{ }}'를 통해 바라보고 있는 model의 변화를 바로바로 반영 하는 것이다.


5. 양방향 Data-binding

아래는 일반적인 JavaScript에서 볼 수 있는 단방향 Data-binding의 동작 모습과 4.Model의 예시에서 설명한 AngularJS의 양방향 Data-binding 동작 방식을 잘보여 주는 그림이다. 기존의 JavaScript 혹은 JQuery는 Data-Binding 동작을 표현 하기위해서 여러가지 작업을 해주어야 하지만 AngularJS에서는 View와 Model이 서로를 바라보면서 자동으로 Data를 업데이트 하기 때문에 간단하고 쉽게 표현을 할 수 있다. 



[그림 ] One-Way Data Binding in Classical Template Systems (출처 - https://docs.angularjs.org/guide/databinding)



[그림 ] Two-Way Data Binding in AngularTemplates (출처 - https://docs.angularjs.org/guide/databinding)


6. scope : context where the model is stored so that controllers, directives, and expressions can access it

스코프는 angular에서 컨트롤러나 디렉티브의 유효범위내의 저장 공간이라고 생각하면 이해하기 쉬울 것이다. 경험해본 바에 의하면 Angular에서 Scope의 범위는 상당히 중요한것 같다. 처음에 Angular로 코딩을 할 때 Scope에 대해 제대로 이해하지 않은 상태로 진행 하여 많이 애를 먹었었다. angularJs를 처음 접한다면 꼭 Scope에 대해 이해하고 이용하길 바란다. 


아래는 Scope를 저장공간으로 활용한 예제 이다.




아래는 Scope를 컨트롤러나 디렉티브의 유효범위 별 Scope를 표현한 예제이다.

HTML의 Expressions와 Javascript의 Scope를 보면 같은 name 이지만 Scope범위 별로 보여지는 값이 다른 것을 확인 할 수 있다. 



이처럼 Scope의 범위에 따라 컨트롤 할 수 있는 범위가 달라지기 때문에 Angular에서는 항상 내가 사용하는 Scope의 범위를 생각하면서 구현해야한다.



AngularJS 홈페이지에 보면 왜 AngularJS 인가? 라는 질문과 그에 대한 답이 다음과 같이 제시되어 있다.

 - HTML is great for declaring static documents, but it falters when we try to use it for declaring dynamic views in web-applications. AngularJS lets you extend HTML vocabulary for your application. The resulting environment is extraordinarily expressive, readable, and quick to develop.

- HTML은 정적인 documents를 표현하기 위해 훌륭하다. 그러나 HTML은 우리가 그것을 웹 애플리케이션에서 동적인 모습을 표현하려 할때 주저하게 한다. AngularJS는 애플리케이션을 위한 HTML 문법의 확장을 제공한다. 결과적으로 더 나은 표현을 할 수 있게 되고, 더 나은 가독성을 제공하고, 개발을 빠르게 할 수 있게 한다.


AngularJS를 접하기 전까지 나는 정적인 HTML을 동적으로 표현하기 위한 방법으로 JavaScript 혹은 JQuery를 사용하여 Dom을 조작하는 방식을 사용해 왔다. 사실 AngularJS를 사용해보기 전까지는 JavaScript와 JQuery를 사용한 개발 말고 다른 방식으로 동적인 웹 앱을 개발하는 것에 대해 생각을 해보지 못했었다. JavaScript, JQuery 만으로도 충분한 표현이 가능하다고 생각했었기 때문이다.

프로젝트를 진행하기 위한 준비 과정 역시 JavaScript와 JQuery만으로 MockUp을 개발 했었다. 실제 AngularJS를 이용한 개발은 프로젝트 투입 며칠전 요구사항으로 AngularJS를 이용해 개발해달라고 하여 접하게 된 것이다. 

AngularJS에 대한 첫 느낌은 '아... 뭔 소리지 이게?' 였다. 마치 대학교에서 처음 개발 언어를 접했을 때와 같은 기분이었다. 

다른 주니어 개발자들에게는 어떨지 모르겠지만 나에게 AngularJS의 진입 장벽은 꽤나 높게 느껴졌다.

대략 1주일 정도의 학습 기간을 가진 뒤 JQuery로 짜여진 코드를 AngularJS를 적용한 버전으로 바꾸는 작업을 진행 하였다. 

물론 1주일이라는 기간동안 AngularJS를 완벽히 파악하기에는 짧은 기간이었기에 작업에는 많은 어려움이 있었고 개발 속도 또한 더디게 진행 되었다. 당시 어려움을 느꼈던 부분은 크게 3가지가 있었다.


1. 위에서도 언급했던 Scope 사용범위에 대한 이해 부족

2. 단기간에 학습하고 활용하기에는 너무 많은 API들

3. 버전별 비호환성


많은 API 만큼 많은 것을 할 수 있겠지만 프로젝트 기간 동안 학습하고 활용하기에는 너무 많은 양이었다. 때문에 내가 실제 개발에 사용한 API 함수 혹은 Service역시 극히 일부분이었다. 또한 처음 적용할 당시 버전에 따라서 Directive(지시어) 사용법이라던지 Provider 사용법이 다르고 호환이 되지 않는 다는것을 알지 못해서 많은 삽질(?)을 하였다. 


AngularJS가 매력적이고 유용한 점이 많지만 개인적으로 프로젝트 진행중에는 'JQuery를 사용했으면 더 쉽게 해결될 문제였을 텐데..'  라고 생각되는 부분이 많았었다. 물론 학습기간도 짧았고 당시에는 내가 AngularJS의 개념을 완벽히 파악하고 개발을 했던게 아니었기 때문에 발생하는 문제였을 것이다.

아무래도 주니어 개발자로 이제 막 첫걸음을 내딛는 단계이기에 이해하는데 한계와 경험부족으로 인한 어려움이 많았고, 그로인해 포스팅에도 안좋은 점이 많이 부각된 것 같다. AngularJS를 어렵게 느낀 것은 사실이지만 좀 더 공부해 보고싶다. 아직 AngularJS의 매력에 푹 빠져보지도 못했고 내가 경험한 AngularJS는 극히 일부분이기 때문이다. 언젠가 Angular의 매력을 충분히 느끼게 된다면 Angular강좌 같은 포스팅도 가능 하지 않을까 싶다. 강좌 포스팅을 할 수 있는 날을 기대 하며 1편 AngularJS, RequireJS 포스팅은 마치도록 하겠다. 


다음 포스팅인 주니어 개발자의 경험기 [2편 -  JavaScript 시각화 라이브러리] 에서는 프로젝트에 사용했던 JavaScript 시각화 Library들에 대해 알아보도록하겠다.


Posted by 한종민

Deview 2015 Day 1에 참가하게 되었습니다.

요번년도 Deview의 Topic은 웹,모바일,웹스케일 컴퓨팅,빅데이터,선행기술, 테크 스타트업,오픈소스,개발자 문화,로봇,검색 이렇게 10가지로 구성되어 있었는데요.

일단 Deview 참가 신청을 하기 전에 주제를 살펴보았습니다.하나 하나씩 살펴보니 전보다 난이도가 높아서, 제가 발표를 듣고 완전히 이해 할 수 있을까 하는 생각이 들었습니다.하지만 Deview 참가자로 추첨이 되었을 때 어떤 주제를 들을지 고민하는 상황은 앞에 달콤한 사탕을 두고 어떤 것을 고를지 고민하는 상황 같았습니다.


행사날이 다가오고 Deview 행사장에 딱 들어갔을 때 행사장의 느낌은 먼가 제작년 Deview 보다 고급진(?) 느낌이 들었습니다. 자유롭게 앉아서 토의 할 수 있는 인테리어와 볼거리가 많았던 부스까지 더 다양하고 풍부해진 느낌이 들었습니다.

가기전에 어떤 섹션을 들을지 고민을 많이 하긴 했지만 최종적으로 아래 5가지의 섹션을 들었습니다.

1. Developing Android Libraries: Lessons from Realm 

2. Quality without QA

3. Packetbeat 와 Elasticsearch를 이용한 실시간 모니터링

4. React Everywhere

5. 데이터사이언스팀 빌딩 그리고 시스템 구축


Developing Android Libraries: Lessons from Realm 과 Quality without QA는 외국인 연사가 발표를 하셨는데, Deview 측에서 동시통역을 제공해 주셨습니다.

5가지 섹션 중 가장 쉽게 다가갈 수 있었던 섹션은 Quality without QA 입니다.나머지 4가지 섹션의 내용도 좋았지만 아직 부족한게 많은 주니어라 발표를 듣고 완벽히 이해 할 수는 없었던 섹션 이었습니다. 2~3주 뒤에 Deview측에서 영상을 올려 준다고 했기 때문에 다시 들어봐야 겠습니다.


[Quality without QA 섹션을 들으면서]


Quality without QA 섹션의 주제는 개발자 중심에서의 QA에 관한 이야기였습니다. 크게 3가지를 다루었고 그 내용은 아래와 같습니다


1. Git branch를 이용하면 독립적이고, 안정적으로 테스트를 하는데 크게 도움이 된다. 

2. 자동화된 테스트와 코드리뷰가 필요하다.

3. Dogfooding은 regression 버그를 잡는 중요한 도구이다.


Dogfooding의 어원 및 자세한 내용은 다음 링크를 참고해보세요!  


이야기 중 코드리뷰에 관한 이야기가 있어서 반가웠습니다.


[Quality without QA 발표자료 중 코드리뷰에 관한 내용]


제가 다니는 회사에서도 저의 사수님께서 코드리뷰를 해주시는데요.첫 코드 리뷰때 난도질 당하던 저의 코드는 잊을 수가 없었죠;;



하지만 코드리뷰를 하고 나면 주니어 개발자 입장에서는 좋은 코드를 보고 고치고 느낌으로써 성장하고 있다는 것을 몸소 깨닫게 되고 코드 리뷰를 하면서 전달받는 지식과 경험들은 저에게 피가 되고 살이 되더군요.또한 초반엔 코드리뷰를 할 때 보다 점점더 지날수록 배포과정이 좀 더 빠르게 진행되는 느낌이 들었습니다.실제로 발표 내용을 보니 Atlassian에서 조사한 통계에 따르면 코드리뷰를 했을 때와 안했을 때를 비교 했을 때 시간이 지날수록 코드리뷰를 했을때의 배포속도가 더 빨라진다는 조사결과가 나왔다고 합니다


[코드리뷰 유뮤에 따른 배포 속도 비교 그래프]

Tim Pettersen는 Pull Requests를 위한 10가지 팁도 소개 해줬는데요. 이에 대한 자세한 내용은 블로터 기사 에서 확인해 보실 수 있습니다.


섹션 중간중간에 부스투어도 했는데 이것저것 많이 주시더군요. 행사 끝나고 집에 돌아오는 길에 가방을 보니 두둑해져 있었습니다.



Deview 2013,2015 모두 참가해 보았지만 매번 제 자신에게 느끼는 건 '더 공부해야겠구나' 란 생각이 든다는 것입니다. Deview 2013 보다 Deview 2015 에선 세션을 듣고 이해 할 수 있는 비중이 높아 지긴 했지만 그래도 저에게 부족함을 느끼고 있습니다. 다른 대형 개발자 컨퍼런스가 요즘 많이 없어졌던데 Deview는 아직 남아 있어서 새로운 기술들을 알게 되고, 다른 개발자들이 공유해 주는 지식과 경험을 들을 수 있어서 즐거웠습니다. 내년 Deview도 기대합니다

Posted by 비회원
On-ly 2.0 project review pt 자료 입니다.




Posted by 비회원
이번 포스팅에서는 Django Web Framework의 설치와 환경 설정에 대한 알아볼까 한다.

Django (web framework)

From Wikipedia, the free encyclopedia

Django (/ˈæŋɡ/ jang-goh)[4] is a free and open source web application framework, written in Python, which follows the model–view–controller (MVC) architectural pattern.[5][6] It is maintained by the Django Software Foundation (DSF), an independent organization established as a 501(c)(3) non-profit.

Django's primary goal is to ease the creation of complex, database-driven websites. Django emphasizesreusability and "pluggability" of components, rapid development, and the principle of don't repeat yourself. Python is used throughout, even for settings, files, and data models. Django also provides an optional administrative create, read, update and delete interface that is generated dynamically through introspectionand configured via admin models.


위키백과를 참고해 찾아보면 장고 혹은 쟁고라 불리우며 무료 오픈소스 웹 어플리케이션 Framework라고 되어 있다. 또한 MVC ( Model-View-Controller) 아키텍처 패턴을 따른다고 되어있다. 특히 장고는 데이터베이스 기반의 웹 프레임 워크 개발에 용이하게 되어 있다고 한다.

그렇다면 Django를 사용하기 위한 설치 과정과 환경 설정 과정을 알아보도록 하자.

  1. Django Install

  • Django 설치

easy_install을 이용해 Django를 설치 한다.


 $ sudo easy_install Django


  • Django 설치 확인

아래 명령어를 통해 Django가 제대로 설치 되었는지 확인한다.

$ python

>>> import django

>>> print(django.get_version())

1.8.x 


  • Django Project 생성

아래 명령어를 통해 Django Project를 생성한다.

 $ django-admin startproject project_name

mysite/
    manage.py
    mysite/
        __init__.py
        settings.py
        urls.py
        wsgi.py


  • Django app 생성

아래 명령어를 통해 Django app을 생성한다.

 $ python manage.py startapp app_name

mysite/
    manage.py
    mysite/
        __init__.py
        settings.py
        urls.py
        wsgi.py

app_name/ __init__.py admin.py migrations/ __init__.py models.py tests.py views.py





  1. VirtualEnv Install (가상환경)

VirtualEnv를 사용 하는 이유는 아래와 같다.

프로젝트만의 가상 공간을 만들고 필요한 모듈들을 따로 설치 및 관리 할 수 있다.

프로젝트 간 Python 버전 충돌, 모듈 버전 충돌로부터 해방 될 수 있다.

실제 적용할 서버와 동일한 환경을 만들어 작업을 진행 할 수 있다.


  • virtualenv 설치

아래 명령어를 통해 virtualenv를 설치 해준다.


 $ sudo pip install virtualenv

# 만약 pip가 없다면

$ sudo easy_install pip



  • setting

아래 명령어를 통해 virtualenv를 사용하기 위한 설정을 해준다.


$ cd project_name

$ virtualenv virtualenv_name

# “virtualenv_name”이라는 이름의 새로운 디렉터리가 생성된 것 확인.


  • 가상 공간 활성화

해당 프로젝트 내에서 아래의 명령어를 통해 virtualenv 를 활성/비활성화 시켜준다.


$ . virtualenv_name/bin/activate

# 프롬프트가 달라진 것 확인.

(venv)$

# 비활성화 하고 싶을 때는

$ deactivate



  1. Django settings.py 설정 하기

  • Logging(Debug) 사용하기

  • Logging은 디버깅 기능을 사용하고, 로그를 기록하기 위해 제공 되는 모듈 이다.

1. Django Install 에서 생성한 project에 접근 한다.

/project_name/ 경로의 settings.py 파일에 편집기를 통해 아래 코드를 추가한다.



LOGGING = {

   'version': 1,

   'disable_existing_loggers': False,

   'formatters': {                        # message 출력 포맷 형식

       'verbose': {

           'format' : "[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s",

           'datefmt' : "%d/%b/%Y %H:%M:%S"

       },

       'simple': {

           'format': '%(levelname)s %(message)s'

       },

   },

   'handlers': {

       'file': {

           'level': 'DEBUG',

           'class': 'logging.FileHandler',

           'filename': 'file_name.log',        # message가 저장될 파일명(파일명 변경 가능)

           'formatter': 'verbose'

       },

   },

   'loggers': {

       'django': {

           'handlers':['file'],

           'propagate': True,

           'level':'DEBUG',

       },

       'app_name': {                   # Project에서 생성한 app의 이름

           'handlers': ['file'],          # 다른 app을 생성 후 해당 app에서도

           'level': 'DEBUG',          # 사용하고자 할 경우 해당 app 이름으로

       },                                      # 좌측 코드를 추가 작성해서 사용

   }

}





logging을 사용할 app의 파일에 아래의 코드를 추가 한 뒤 사용하면 된다.


import logging

logger = logging.getLogger(__name__)

#       logger.debug()



console 에 아래의 명령어를 통해 logging을 실행 시킨다.

 $ tail -f filename.log


  • Installed_apps 설정

project_name/settings.py 파일에서 'INSTALLED_APPS’에 아래와 같이 app_name을 추가한다.

# Application definition

INSTALLED_APPS = (

   'django.contrib.admin',

   'django.contrib.auth',

   'django.contrib.contenttypes',

   'django.contrib.sessions',

   'django.contrib.messages',

   'django.contrib.staticfiles',

   'app_name',                         # ',' 빠트리지 않도록 주의한다.

) 


  • Templates 설정

project_name/settings.py 파일에서 'DIRS’ 부분을 아래와 같이 templates 파일 경로를 설정해준다.


TEMPLATES = [

 {

       'BACKEND': 'django.template.backends.django.DjangoTemplates',

       'DIRS': [os.path.join(BASE_DIR, 'templates')],

       'APP_DIRS': True,

       'OPTIONS': {

           'context_processors': [

               'django.template.context_processors.debug',

               'django.template.context_processors.request',

               'django.contrib.auth.context_processors.auth',

               'django.contrib.messages.context_processors.messages',

           ],

       },

   },

]


  • Database 설정

project_name/settings.py 파일에서 ‘DATABASES’ 부분을 다음 과 같이 설정해 준다. 아래는 mysql을 사용할 경우 이다.

# Database

# https://docs.djangoproject.com/en/1.8/ref/settings/#databases


DATABASES = {

   'default': {

       'ENGINE': 'django.db.backends.mysql',    # 사용할 DB Engine의 종류

       'NAME': 'database_name',                          # 해당 Engine에서 사용할 DB name

       'USER': 'database_user_id',                       # 해당 Engine에 접근 하기 위한 user_id (ex. root)

       'PASSWORD': 'database_password',        # 해당 Engine에 접근 하기 위한 password

       'HOST': 'localhost',

       'PORT': '',

   }

}

DATABASE_OPTIONS = {'charset': 'utf-8'}       # Database encoding 설정


다른 종류의 DB를 사용 할 경우 Django의 Documentation가 제공하는 아래 링크를 참고 하면 된다.

https://docs.djangoproject.com/en/1.8/ref/settings/#std:setting-DATABASES


  • Language 설정

project_name/settings.py 파일에서 'LANGUAGE_CODE' 부분을 다음과 같이 설정해준다.

LANGUAGE_CODE = 'ko-kr'


TIME_ZONE = 'Asia/Seoul'




  • Static files 설정

project_name/collected_statics 디렉터리를 생성 해준다.

project_name/settings.py 파일에서 'STATIC’  부분을 다음과 같이 설정 해준다.

# Static files (CSS, JavaScript, Images)


# https://docs.djangoproject.com/en/1.8/howto/static-files/


STATIC_ROOT = os.path.join(BASE_DIR, 'collected_statics')     #


STATIC_URL = '/assets/'


STATICFILES_DIRS =(

       os.path.join(BASE_DIR, 'static'),

)




프로젝트에 사용할 js, css파일은 project_name/static/ 의 경로에 넣어준다.

project_name/template 경로에 있는 HTML로 작성된 template 파일에 아래와 같이 적용해 사용한다.

{% load static %}

<link href="{% static 'css/bootstrap.css' %}" rel="stylesheet">

<script src="{% static 'js/jquery-2.1.3.js' %}"></script> 



아래 명령어를 통해 prject_name/static/ 에 있는 js, css 파일을 collected_statics 파일로 모아준다.

 $ python manage.py collectstatic

명령어 실행 시 아래와 같이 이미 존재하는 파일을 덮어씌울 것이냐는 질문을 하게 되면 'yes'라고 입력해주면 된다.

< 그림 Collectstatic 명령어 실행 결과 >



  1. Django url

  • Template에서 Static 파일을 사용하기 위해 4) Django setting의 Static files 설정과 더불어 project_name/urls.py 파일에도 아래와 같이 코드를 추가해 주어야 한다.


from django.conf.urls import include, url, patterns

from django.contrib import admin

from django.conf import settings

from django.conf.urls.static import static

from django.contrib.staticfiles.urls import staticfiles_urlpatterns


urlpatterns = [

       url(r'^admin/', include(admin.site.urls)),

] + static(settings.STATIC_URL, document_root = settings.STATIC_ROOT)


urlpatterns += staticfiles_urlpatterns()


이것으로 Django를 설치하고 환경설정을 위해 Setting 파일의 install_app, Template, DB, Static File, Language, Time Zone을 비롯해 디버깅과 로그 기록을 위한 Logging 모듈설정을 마치도록 하겠다.






'Newbie's Log' 카테고리의 다른 글

Deview 2015 Day 1참가 후기  (0) 2015.09.22
On-ly 2.0 project review  (0) 2015.08.18
Django 설치 및 환경설정  (0) 2015.08.03
Evernote API 사용하기  (0) 2015.07.29
주니어 개발자의 On-ly 2.0 프로젝트 경험기-개발편  (0) 2015.07.21
On-ly 소개 Prezi  (0) 2015.07.16
Posted by 한종민



이번 포스팅은 Evernote 에서 무료로 제공하고 있는 Evernote API를 소개 하고

Evernote-SDK에 포함되어 있는 Sample 코드를 실행해보고자 한다.

Evernote API는 개발자들이 본인들의 프로젝트에 Evernote 연동을 할 수 있도록 제공해주는 오픈소스이다. 

Evernote의 기능 중 note, notebook 과 관련해서 작성, 판독, 갱신 및 삭제 기능등을 모두 연동해 사용할 수 있도록 

Thrift 방식으로 제공 하고 있다.

이번 포스팅에서는 Evernote API를 사용하기 위해 제공되는 Evernote-SDK에 포함된 Sample 코드중 EDAMTest.py 

라는 파일을 이용해 진행을 하려고 한다.


https://dev.evernote.com/

위 URL은 Evernote API 이용을 위한 SDK파일과 Documentation을 제공 하고 있는 에버노트 개발자 홈페이지 이다. 홈페이지에 접속 하게 되면 아래와 같은 페이지를 볼 수 있다. 

<Evernote 개발자 홈페이지>

1. 가운데에 있는   버튼을 통해 다운로드 페이지로 이동 할 수 있다.


Evernote API는 이 포스팅에서 진행할 Python용 외에도 IOS, Android와 같은 모바일 용과 Ruby, C++, Perl 과 같은 다른 언어를 위한 SDK파일도 제공하고 있다. 

우리는 Python을 이용 할 것이기 때문에 가장 위에 있는 Python용 Install SDK 와 Quick-start-Guide 를 이용하면 된다.


2. Install SDK를 클릭하면 Evernote-SDK-Python이 올려져 있는 GitHub로 이동된다. 이곳에서 SDK 파일을 다운로드 해 주도록 한다. 다운로드를 완료 했다면 그 다음 단계는 일종의 Tutorial 과정인 Quick-start-Guide 이다. 

zip파일을 받아 압축을 풀어도 되고 아래와 같이 git명령어를 이용해도 된다.


$ git clone  https://github.com/evernote/evernote-sdk-python.git



3. Quick-Start-Guide를 클릭하면 위에서 다운로드한 Evernote-SDK의 기본적인 사용법을 Tutorial 형식으로 제공하는 페이지로 이동하게 된다.


4. Tutorial 및 Evernote API를 이용하기 위한 준비

첫번째 단계는 준비 과정이다. 

1) https://sandbox.evernote.com (test용 Evernote 샌드박스 사이트) 의 계정을 만들어야 한다. 사이트에 접속하게되면 아래와 같은 설명을 볼 수 있는데 살펴 보면, 해당 사이트는 test용이고, 실제 Evernote와 별개로 운영되는 사이트 임을 알 수 있다. 

계정을 생성하였다면 그다음 단계는 Evernote API KEY를 발급 받아야한다.

2) 두번째 항목의 'get one here'를 클릭하거나 우측 상단의  를 클릭하게되면 아래와 같이 API KEY를 발급 받을 수 있는 화면을 볼 수 있다. 

좌측은 직접 작성을 하면 된다. 그리고 우측을 보면 API Permissions가 있다. 이는 Evernote API를 이용해 접근해서 처리할 수 있는 권한에 따라 2가지로 나뉜 것이다. Basic Access의 경우 생성하고 정보를 불러와서 읽는 것만 가능하고, Full Access의 경우 basic 기능에 추가로 업데이트와 공유 노트북의 생성까지 가능하다. 사용하려는 용도에 맞게 선택하면 된다. 우리는 우선 Full Access로 선택 하도록 한다. 

다음으로 바로 아래에 있는 3개의 check box를 살펴보면 첫번째는 사용자Data 동기화와 관련된 것이고 두번째는 Evernote에서 제공하는 업데이트 정보를 email로 구독할 것인지에 대한 것이고 세번째는 API KEY를 발급 받기위해 약관에 동의하는 것과 관련된 것이다. 

우리는 첫번째와 두번째는 제외하고 세번째만 체크 한뒤 Request Key 버튼을 클릭하면된다.

Evernote API KEY인 Consumer KEY와 Consumet Secret이 발급 된 것을 확인 할 수 있을 것이다. 발급 받은 KEY는 이후 진행할 Sample Script에서 사용 해야 되니 따로 적어 놓도록 하자. 해당 KEY는 발급 과정에서 작성한 e-mail을 통해 언제든지 다시 확인 할 수있으니 위 페이지를 벗어 났더라도 걱정 하지 말자.

3) Quick-start-guide의 What you need 에는 설명되어 있지 않지만 한가지 더 준비해야 될 것이 있다. 

바로 Developer Token을 발급 받는 것이다. Developer Token은 Evernote 계정에 접근해 작업을 할 수 있도록 승인 받기 위해 꼭 필요한 것이다. Developer Token은 아래 링크를 통해 발급 받을 수 있다. 

두개의 링크 중 우리는 Sample Script를 실행해볼 것이기 때문에 sandbox용을 발급 받도록 한다. Production용은 실제 개발에 적용할 경우 이용면 된다.

https://sandbox.evernote.com/api/DeveloperToken.action - Sandbox 용

https://www.evernote.com/api/DeveloperToken.action - Production 용

Developer Token의 경우 반드시 따로 적어서 저장해놓도록 하자. 해당 토큰은 잃어 버렸을 경우 재발급 받는 방법 밖에 없다. 아래 그림에서 가운데 위치한 Developer Token 을 저장해 놓으면 된다.

이것으로 준비과정은 끝이 났다. 이제부터는 위에서 준비한 1)Evernote-SDK 2) API KEY 3) Developer Token 을 가지고 Sample Script를 진행해 보자.


5. 우리는 위에서 Evernote-SDK를 이미 다운 받았기 때문에 다운 받은 Evernote-SDK-Python 파일이 있는 곳으로 이동해 진행을 하면 된다.

아래와 같이 해당 폴더로 이동했다면 setup.py 라는 파일이 있는 것을 확인 할 수 있을 것이다.

아래의 명령어를 실행한다.

해당 명령어는 Evernote SDK 에있는 Library들을 python directory에 위치 시켜 사용할 수 있게 해준다.

$ python setup.py install


6.  위의 install 이 제대로 진행 되었는 지 확인 하기위해  아래의 명령어를 실행해본다. Error 메세지가 없이 조용히 종료 된다면 제대로 install된 것이다.

$ python -c 'from evernote.api.client import EvernoteClient'


7.  ./sample/client/ 경로로 이동한다.

EDAMTest.py 파일이 있는 것을 확인 할 수 있다. 해당 파일을 열어서 아래와 같이 첫번째 auth_token 부분에 위에서 발급 받은 dev_token 을 입력한 뒤 저장 해준다.

다음 명령어를 실행해준다.

$ export PYTHONPATH=../../lib; python EDAMTest.py

명령어를 실행하게 되면 1개의 노트북을 발견하였고 노트북의 이름은 test1 이고 새로운 노트를 생성하였다는 메세지와 함께 새로 생성한 노트의 GUID를 출력하는 것을 볼 수 있을 것이다. 단지 콘솔에 메세지만 출력된 것이 아니고

https://sandbox.evernote.com/    샌드박스용 Evernote에 로그인하면 아래와 같이 새로운 노트가 생성된 것을 확인 할 수 있다.

노트가 생성되는 것 까지는 확인 하였지만 어떻게 활용을 해야될지 감이 잘 안올 것이다. 아래의 코드는 노트생성을 위해 가장 기본이 되는 형식이다. 

client = EvernoteClient(token=auth_token, sandbox=True)

note_store = client.get_note_store()

notebooks = note_store.listNotebooks()


# To create a new note, simply create a new Note object and fill in

# attributes such as the note's title.

note = Types.Note()

note.title = "Test note from EDAMTest.py"

note.content = '<?xml version="1.0" encoding="UTF-8"?>'

note.content += '<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">'

note.content += '<en-note>Here is the Evernote logo:<br/>'

note.content += '<en-media type="image/png" hash="' + hash_hex + '"/>'

note.content += '</en-note>'

created_note = note_store.createNote(note)


노트생성을 위해서는 사용자의 정보가 우선 있어야 한다. 이때 사용되는 것이 auth_token (dev_token)이다. 토큰을 이용해 노트를 작성할 계정의 사용자임을 확인시킨다.

client = EvernoteClient(token=auth_token, sandbox=True)

그다음 필요한 것은 노트저장소의 정보이다. 위에서 확인한 사용자의 노트저장소의 정보를 가져온다.

note_store = client.get_note_store()

노트 저장소에는 노트북이 있을 것이다. 한개일 수도 있고 여러개 일 수도 있다. Evernote-sandbox 계정을 생성하고 로그인 했을 때 노트북을 한개 만들었을 것이다. 해당 노트북이 기본 노트북으로 지정되고 노트를 생성할 노트북을 우리가 따로 지정해주지 않는다면 기본 노트북에 자동으로 생성이 될것이다.

notebooks = note_store.listNotebooks()

노트를 생성할 위치와 계정 정보를 확인 했다면 노트를 만들어보자. 노트를 만들기 위해 노트 객체를 가져온다.

note = Types.Note()

노트의 제목을 설정해준다.

note.title = "Test note from EDAMTest.py"

노트의 본문내용은 Evernote 에서 자체적으로 사용 하는 ENML 이라는 markup language를 사용해야된다. 때문에 3번째 줄의 '본문 내용' 을 제외한 나머지 코드의 형식을 맞춰 주어야 생성이 가능하다.

note.content = '<?xml version="1.0" encoding="UTF-8"?>'

note.content += '<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">'

note.content += '<en-note>본문 내용<br/>'

note.content += '</en-note>'

이제 위에서 작성한 제목과 본문내용을 가진 노트를 생성하면 된다.

created_note = note_store.createNote(note)


Evernote API 사용에 있어서 가장 기본이 되는 노트 생성과정을 알아보았다. Evernote API를 사용하기 위해 기억해야될 것은
auth_token (developer_token)과 모든 기능 사용에 기본이 되는 사용자의 정보를 확인시켜주고 노트저장소의 정보를 가져오는 것이다. 이 세가지를 잘 활용한다면 Evernote API 사용에 많은 도움이 될 것이다.





'Newbie's Log' 카테고리의 다른 글

On-ly 2.0 project review  (0) 2015.08.18
Django 설치 및 환경설정  (0) 2015.08.03
Evernote API 사용하기  (0) 2015.07.29
주니어 개발자의 On-ly 2.0 프로젝트 경험기-개발편  (0) 2015.07.21
On-ly 소개 Prezi  (0) 2015.07.16
Ubuntu에서 OracleJDK설치하기  (0) 2015.06.23
Posted by 한종민

오늘은 "주니어 개발자의 On-ly 2.0 프로젝트 경험기-개발편"을 포스팅한다.

"주니어 개발자의 On-ly 2.0 프로젝트 경험기-기획편"이 궁금하면 이곳을 클릭하길 바란다.


다사다난했던 On-ly 2.0 기획 단계를 거치고 직접 기획했기에 순조로울 것만 같았던 개발 과정은 결코 순조롭지 못했다.


그중 첫번째. 버블 차트

우리가 많은 시간을 잡고 있었던 과정 중 하나가  친구와 나의 게임 플레이 시간 데이터를 버블차트로 보여주는 부분이였다. 

초기에 기획 했을 때 아래 그림과 같은 버블차트 + 알파 를 원했다.


<나와 사용자의 게임 플레이 시간 버블 차트>  


버블차트에 버블 하나하나가 생동감이 있길 바랬고, 거슬리지 않을 만큼의 애니메이션 효과도 들어가길 원했다. 또한 난 최근에 본부장님의 소개로 D3.js라는 것을 알게 되었고(참고,D3 소개), D3.js를 On-ly에서 사용하고 싶었다. 하지만 회사내에서 D3.js 말고 안드로이드 차트 라이브러리를 쓰자는 의견이 나왔고,  Hellocharts 라이브러리 를 사용하게 되었다. 이 라이브러리를 사용하기에는 먼가 아쉬움이 많이 남았지만 그래도 최대한  잘 시각화해서 보여주고 싶었다. 아래 그림과 같이 말이다.


<D3.js를 사용한 버블 차트의 예>



아래는 버블차트를 이용한 1차 버전이다. 일주일간의 나의 게임플레이시간 데이터와 친구의 게임플레이시간의 데이터를 seekbar를 통해 보여주도록 했는데 컬러의 일관성이 없어 산만하고 한 눈에 들어오지 않는다는 평을 받았다. 


    

<3일치 버블차트 1차 버전>


그래서 데이터를 어떻게 표현 할 것인지 오랜 시간 동안 고민을 했는데 최종적으로 선택한 것이 아래 그림과 같다. 


    

<3일치 버블차트 최종 버전>


컬러를 덜 사용하고, top1 사용자와 친구 그리고 나에 집중하여 1차 버전보다는 눈에 잘 들어온다 . 하지만 아쉬운건 사실이었다. 좀 더 생동감있고, 전체적으로 앱과 어울리는 느낌이 부족해 보였다. 버블차트 작업으로 인해 일정을 많이 잡아먹어서 현 상태에 일단 만족해야했고 더 발전된 방향에 대해서는 다음 버전을 기약해야 했다. 


두번째는. 파이차트


On-ly 개발을 하면서 갑자기 문득 들었던 생각이 "내가 가장 많이 하는 게임은 무엇일까?"라는 원초적인 질문에 대해서는 On-ly에서 아무런 데이터를 보여주고 있지 않다는 것을 깨달았다. 

그래서 우리는 사용자가 많이 하는 top6 게임에 대해 파이차트로 보여주는 기능을 추가 했고, 이 기능을 구현하기 위해 Hellocharts라이브러리를 제외한 다른 차트 라이브러리를 찾아보았다.

그러다 발견한 것이 MPAndroidChart !  

이 차트라이브러리에 대한 소개는 아래 영상과 같다. 




차트의 x축, y축 조정 부터, label기능, 다양한 에니메이션 효과,거기다 오픈소스인 MPAndroidChart가 상당히 마음에 들어서 MPAndroidChart github에 별을 쏴주고, 바로 On-ly에 적용하였다. 

요렇게!


(MPAndroidChart를 이용한 파이차트)


세번째, weibo연동하기. 


On-ly가 샤오미와 이야기가 오고가서 샤오미 앱스토어에 On-ly를 배포해야 했다. 일단 중국에선 facebook이 막혀있으니 weibo연동이 필요했다. weibo api문서는 영문을 지원해 줬지만 나머진 죄다 중국어로 되어 있으니 난감 했다(중국어를 빨리 공부해야겠다는 생각이 가장 먼저 앞섰다.). 거기다 weibo sdk version이 froyo여서 안드로이드 버전차이로 인한 삽질을 하게 되었다. 언제적 froyo를...


친절하신 카네기멜론 대학 학생이 Sina Weibo API GUIDE를 작성했는데 개발하는데 상당히 많이 도움이 되어서 그 링크를 공유한다.현재 중국시장에서는 weibo 친구들과 함께 On-ly를 즐길 수 있으며, On-ly는 샤오미 스토어에 올라가 있다.

>>>>>>>On-ly 샤오미 스토어로 이동


마무리하며..

On-ly2.0을 기획하고 배포하는데 5개월의 기간이 지나갔다.회사분들과 함께 기획하고, 개발하고, 테스트 하며 한땀한땀 정성을 쏟아부었다. 이 앱에 애정을 많이 쏟아 부어서 그런지 On-ly 2.0을 Release하고 나서 아쉬운 점과 보완 해야 할 점(User Interface변경 등)이 속속히 생겨났다. 다음 버전에는 그러한 아쉬움들을 반영하고 싶다.

간단한 On-ly 자료를 소개로 주니어 개발자의 On-ly 2.0 프로젝트 경험기를 마무리한다.


(On-ly 소개자료)

-끝-     



Posted by 비회원

On-ly 소개 Prezi

Newbie's Log 2015.07.16 14:49
Posted by 비회원

우분투에서 기본으로 제공해주는 OpenJDK 가 있지만, OracleJDK가 필요할 때가 있다.

이전에는 우분투에서 OracleJDK Repository가 기본으로 제공되어 apt-get으로 쉽게 설치가 가능하였지만, 이제는 더이상 지원하지 않기때문에 다른 방법을 찾아야 한다. 몇군데 찾아보다 보았더니 OracleJDK 설치를 어렵지 않게 할 수 있었다

오늘은 쉽고 빠른 OracleJDK설치에 대해 설명해 보겠다.


PPA등록으로 빠른 설치하기.

1. 새로운 repository PPA를 추가

$ sudo add-apt-repository ppa:webupd8team/java

2. repository index 업데이트

$ sudo apt-get update

3. JDK설치

- Java 8

$ sudo apt-get install oracle-java8-installer

- Java 7

$ sudo apt-get install oracle-java7-installer

4. 설치된 Java version 확인

$ java -version
java version "1.7.0_80"
Java(TM) SE Runtime Environment (build 1.7.0_80-b15)
Java HotSpot(TM) 64-Bit Server VM (build 24.80-b11, mixed mode)


수동 설치하기

1. OS 및 커널 bit 확인

$ file /sbin/init
/sbin/init: ELF 64-bit LSB  shared object, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=7a4c688d009fc1f06ffc692f5f42ab09e68582b2, stripped

2. Java 정보 확인

$ java -version
java version "1.7.0_79"
OpenJDK Runtime Environment (IcedTea 2.5.5) (7u79-2.5.5-0ubuntu0.14.04.2)
OpenJDK 64-Bit Server VM (build 24.79-b02, mixed mode)

3. 다른 Vendor와 충돌 방지를 위하여 OpenJDK 삭제

$ apt-get purge openjdk*

4. OracleJDK 바이너리를 저장할 폴더 생성

$ sudo mkdir -p /usr/local/java

5. OracleJDK 바이너리 다운로드

OracleJDK 7 다운로드

OracleJDK 8 다운로드

6. 다운로드한 압축파일 풀기

- Java 8

$ cp -r ~/Download/jdk-8u45-linux-x64.tar.gz /usr/local/java/
$ cd /user/local/java
sudo tar xvzf jdk-8u45-linux-x64.tar.gz

- Java 7

sudo cp -r ~/Download/jdk-7u79-linux-x64.tar.gz /usr/local/java/
$ cd /user/local/java
sudo tar xvzf jdk-7u79-linux-x64.tar.gz

7. 시스템 PATH 파일 수정 후 저장

- Java 7

$ sudo vi /etc/profile

JAVA_HOME=/usr/local/java/jdk1.7.0_79
JRE_HOME=$JAVA_HOME/jre
PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin
export JAVA_HOME
export JRE_HOME
export PATH

- Java 8

$ sudo vi /etc/profile

JAVA_HOME=/usr/local/java/jdk1.8.0_20
JRE_HOME=$JAVA_HOME/jre
PATH=$PATH:$HOME/bin:$JAVA_HOME/bin
export JAVA_HOME
export JRE_HOME
export PATH

8. /etc/profile 리로드

$ sudo source /etc/profile

9. 시스템에 Oracle Java 사용 알리기

시스템에 Oracle Java JRE 사용 알리기

$ sudo update-alternatives --install "/usr/bin/java" "java" "/usr/local/java/jdk1.7.0_79/bin/java" 1
update-alternatives: using /usr/local/java/jdk1.7.0_79/bin/java to provide /usr/bin/java (java) in 자동 모드

시스템에 Oracle Java JDK 사용 알리기

$ sudo update-alternatives --install "/usr/bin/javac" "javac" "/usr/local/java/jdk1.7.0_79/bin/javac" 1
update-alternatives: using /usr/local/java/jdk1.7.0_79/bin/javac to provide /usr/bin/javac (javac) in 자동 모드

시스템에 Oracle Java Web 사용 알리기

$ sudo update-alternatives --install "/usr/bin/javaws" "javaws" "/usr/local/java/jdk1.7.0_79/bin/javaws" 1
update-alternatives: using /usr/local/java/jdk1.7.0_79/bin/javaws to provide /usr/bin/javaws (javaws) in 자동 모드

10. OracleJDK Default로 지정하기

$ sudo update-alternatives --set java /usr/local/java/jdk1.7.0_79/bin/java
$ sudo update-alternatives --set javac /usr/local/java/jdk1.7.0_79/bin/javac
$ sudo update-alternatives --set javaws /usr/local/java/jdk1.7.0_79/bin/javaws

11. Java 버전 확인하기 

$ java -version
java version "1.7.0_79"
Java(TM) SE Runtime Environment (build 1.7.0_79-b15)
Java HotSpot(TM) 64-Bit Server VM (build 24.79-b02, mixed mode)


Java 관리하기

여러 버전의 Java가 설치 되어있는 경우, Default로 사용할 버전을 선택하여 사용할 수 있다.

$ sudo update-alternatives --config java

대체 항목 java에 대해 (/usr/bin/java 제공) 2개 선택이 있습니다.
  선택       경로                                        우선순위      상태
--------------------------------------------------------------------------

* 0   /usr/lib/jvm/java-7-oracle/jre/bin/java           1073      자동 모드
  1   /usr/lib/jvm/java-7-openjdk-amd64/jre/bin/java    1071      수동 모드
  2   /usr/lib/jvm/java-7-oracle/jre/bin/java           1073      수동 모드

기본 사항[*]을 사용하려면 엔터, 다른 것을 사용하려면 번호를 입력하십시오: 


이제 우분투에서 OracleJDK를 사용할 수 있게 되었다. 

오늘의 포스팅은 여기서 끝~

참고 자료 : 

https://www.digitalocean.com/community/tutorials/how-to-install-java-on-ubuntu-with-apt-get

http://www.wikihow.com/Install-Oracle-Java-JDK-on-Ubuntu-Linux

Posted by jjangAh