블로그 이미지
평범하게 살고 싶은 월급쟁이 기술적인 토론 환영합니다.같이 이야기 하고 싶으시면 부담 말고 연락주세요:이메일-bwcho75골뱅이지메일 닷컴. 조대협


Archive»


 
 


Android View와 ViewGroup의 개념



View와 ViewGroup 객체를 이용해서 만듬.

View는 버튼이나 텍스트필드와 같은  UI Widget 이다. 

ViewGroup은 여러개의 View 또는 ViewGroup을 포함할 수 있는 컨테이너의 개념이며, ViewGroup을 상속하여 화면 배치 속성을 갖는 Layout을 가지고 있는 ViewGroup이 있다. ex)LinearLayout 등






<그림. ViewGroup과  View의 상하 관계>

출처 : https://developer.android.com/training/basics/firstapp/building-ui.html


이 ViewGroup은 res/layout/*.xml 에 XML 형태로 정의할 수 있다.

다음은 LearLayout 을 선언한 예이다.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="horizontal" >

</LinearLayout>

View의 경우에는 보통 다음과 같은 형태로 표현되는데, 

<TextView

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:textAppearance="?android:attr/textAppearanceMedium"

    android:text="Medium Text"

    android:id="@+id/textView2" />

해당 뷰 컴포넌트를 식별하기 위한 id, 그리고 사이드를 정의하기 위한 layout_width,layout_height으로 정의한다. 위에서는 wrap_content로 정의했는데, 절대 크기가 아니라 화면에 나타난 뷰 크기만큼 맞추겠다는 정의이다. (안드로이드 디바이스는 해상도가 다양하기 때문에 절대 크기 보다는 위와 같은 상태 크기를 사용할 수 있도록 지원한다.)





Couchbase Server

#5. node.js를 이용하여 뷰를 호출하기

조대협 http://bcho.tistory.com


앞서 설명한 뷰를 node.js 카우치베이스 SDK를 이용해서 호출해보자, 

카우치베이스는 node.js와 몽고DB만큼이나 궁합이 잘 맞는 것 같다. 매우쉽게 뷰를 호출할 수 있다. 뷰 호출 메서드는

var viewQuery = bucket.view(“디자인도규먼트 이름”,”뷰 이름”,{옵션1:값,옵션2:값,…})

로 뷰쿼리를 생성한 후에, 

viewQuery.query(function(err,results){…} );

로 호출하면, 호출 성공시, 결과가 result 변수로 리턴된다.

그러면 간단한 예제를 보자. “mybucket”이라는 버킷이 있고, 안에 다음과 같은 데이타가 있다.

[ { id: 'user0002', key: [ 'us', '75', 'female' ], value: 1 },

  { id: 'user0004', key: [ 'us', '14', 'female' ], value: 1 },

  { id: 'user0003', key: [ 'us', '08', 'female' ], value: 1 },

  { id: 'user0001', key: [ 'korea', '75', 'male' ], value: 1 },

{ id: 'user0005', key: [ 'canada', '69', 'male' ], value: 1 } ]


뷰의 경로는 _desgin/dev_sample 디자인 도규먼트아래에, filter라는 이름으로 다음과 같이 정의되어 있다.


function (doc, meta) {

  var year = doc.ssn.substring(0,2);

  emit([doc.country,year,doc.sex],1);

}


이제, node.js와 Express 프레임웍을 이용해서, 이 뷰를 호출 하는 간단한, REST API를 만들어보자.

 

var express = require('express');

var routes = require('./routes');

var user = require('./routes/user');

var http = require('http');

var path = require('path');

var couchbase = require('couchbase');

 

var bucket = new couchbase.Connection({

    'bucket':'mybucket',

    'host':'127.0.0.1:8091'

},function(err){

    if(err){

        console.error('couchbase server connection error');

        throw err;

    }

})

var app = express();

 

// all environments

app.set('port', process.env.PORT || 3000);

app.set('views', path.join(__dirname, 'views'));

app.set('view engine', 'jade');

app.use(express.favicon());

app.use(express.logger('dev'));

app.use(express.json());

app.use(express.urlencoded());

app.use(express.methodOverride());

app.use(express.session({ secret: 'your secret here' }));

app.use(app.router);

app.use(express.static(path.join(__dirname, 'public')));

 

// development only

if ('development' == app.get('env')) {

  app.use(express.errorHandler());

}

 

app.get('/', routes.index);

app.get('/myview',function(req,res){

    console.log('myview');

    var viewQuery =  bucket.view('dev_sample','filter'

        ,{reduce:false,descending:true,startkey: [ 'us', '75', 'female' ],endkey:[ 'korea', '75', 'male' ]});

    viewQuery.query(function (err,results){

       console.log(results);

        console.log(typeof (results));

       if(err) {

            console.error(err);

       }else{

           res.send(results);

        }// else

    });

 

});

 

http.createServer(app).listen(app.get('port'), function(){

  console.log('Express server listening on port ' + app.get('port'));

});

 


앞부분에 카우치베이스를 연결하는 부분은 http://bcho.tistory.com/926 의 예제와 동일하다.

var viewQuery =  bucket.view('dev_sample','filter'

        ,{reduce:false,descending:true,startkey: [ 'us', '75', 'female' ],endkey:[ 'korea', '75', 'male' ]});

와 같이 뷰 쿼리를 만들었는데, 여기서 주목할 부분은 option 부분이다.

  • reduce:false로 하여, 리듀스함수를 실행하지 않고, 맵 함수만 수행하도록 하였으며
  • descending:true로 하여, 내림차원 정렬이 되도록 하였다.
  • startkey,endkey를 정의하여, 내림 차순으로 정리한 뷰에서 키의 값이 [‘us’,’75’,’female’]~[‘korea’,’75’,’female’]인 결과만 쿼리하도록 설정하였다.

다음으로 viewQuery.query(function (err,results){ 를 호출하여, 뷰쿼리를 호출한후에, 마지막으로 res.send(results); 를 이용하여 리턴된 결과값을 출력하였다.

※ 결과값은 JSON 도큐먼트로 리턴되기 때문에 바로 JSON 형태로 출력된다.

다음은 예제에 대한 실행 결과 화면이다.

 




Couchbase Server

#4. 뷰(View) 이해하기

조대협 http://bcho.tistory.com


뷰는 카우치베이스의 아주 강력한 기능중의 하나이다. RDBMS의 뷰의 개념과 유사한 개념으로, 원본 데이터로부터, 필터링을 통하여 원하는 형태의 데이터로 변환하여 보여주는 일종의 읽기 전용 테이블과 유사한 개념으로 보면 된다. 이를 통해서 키-밸류 스토어 기능만 제공하는 일반 NoSQL에 비해서 filtering 뿐만 아니라, Indexing,grouping,ordering과 같은 다양한 기능을 이 뷰를 이용하여 사용할 수 있다.

카우치베이스의 뷰는 원본 데이터에서 자바스크립트로된 맵&리듀스(Map&Reduce) 함수를 통해서 데이터를 정재한 후에, 뷰로 만들어낸다. 간단하게 개념을 잡아보면 다음과 같다.

 


좌측 그림과 같이 이름을 ID로 하고, Country,Role,Age라는 필드를 가지고 있는 값을 VALUE로 저장하고 있다고 하자, 필터링해서 보고 싶은 데이터는 여기서 Country와 Role만을 필터링 하고 싶다. 그래서 맵&리듀스 함수(여기서는 맵 함수만을 사용하였음)를 이용해서 오른쪽과 같은 뷰를 만들어낼 수 있다.


맵&리듀스(Map&Reduce)함수


뷰를 이해하려면 맵&리듀스함수에 대해서 정확하게 이해해야 한다. 맵함수는 버킷에 저장된 모든 데이터에 대해서 맵함수를 실행한다. 뷰를 정의할 때 맵함수는 반드시 정의해야 한다.

function(doc,meta){

 emit(doc.role,doc.country);

}

맵함수는 두개의 인자를 전달 받는다. “doc”는 버킷내의 저장된 개별 데이타로 각 데이터별로 id와 JSON 도큐먼트의 값을 갖는다. “meta” 는 그 데이터에 대한 메타 데이터 (flag,cas 값등)을 리턴한다.

맵함수에서는 이렇게 받은 개별 데이터를 emit이라는 함수로 가공하여 리턴한다. emit 이라는 함수에 필터링등의 로직을 적용할 수 가 있다. emit(인자1,인자2)의 인자1은 뷰의 KEY값을 리턴하고, 인자2는 뷰의 밸류값을 리턴한다.


 emit 함수를 거쳐 맵함수를 끝내면 ID,KEY,VALUE 형식의 데이터 셋이 나오는데, 이를 카우치베이스에서는 “Index”라고 부른다. ID는 원래 데이터에 대한 ID이고, KEY는 뷰의 키값이다. Index는 이 KEY에 따라서 정렬(sorting)된 형태로 리스팅 된다.  그리고 마지막에는 뷰의 값(VALUE)가 저장된다. 이렇게 생성된 Index는 디스크에 저장된다.


저장된 index를 이용해서 리듀스함수(Reduce)를 실행할 수 있는데, 리듀스 함수를 이용해서는 Grouping 기능을 사용할 수 있다. RDBMS의 group by sum이나 group by count와 같은 간단한 기능에서부터 복잡한 계산을 자바스크립트를 이용해서 구현하는 것이 가능하다.



정리하자면 뷰에는 각 버킷내의 개별 데이터를 변환하는 맵함수와, 변환된 개별 데이터를 그룹별로 모아서 처리할 수 있는 리듀스 함수를 갖는다.


Design Document


하나의 버킷에는 여러 개의 뷰를 생성할 수 있다. 아래 그림과 같이 뷰는 Design document라는 단위로 묶이게 되고 각 뷰는 그에 대칭 되는 맵&리듀스 함수를 가지고 있다.

 

 

카우치베이스의 문서를 보면 http://blog.couchbase.com/10-things-developers-should-know-about-couchbase 성능상 무리가 없게 하기 위해서는 하나의 버킷당 4개의 Design Document 정도가 적절하며, 하나의 Design Document당 최대 10개 정도의 뷰를 구현하는 것이 적절하다고 권고하고 있다. (즉 버킷당 최대 40대 정도의 뷰가 적절)


콘솔에서 뷰 만들기


개념을 이해 했으면 이제 간단한 뷰를 만들어보자, 카우치베이스는 웹콘솔에서 뷰생성 및 개발 테스트를 할 수 있도록 지원한다. 카우치베이스 웹콘솔로 이동한후 상단메뉴에서 “Views” 메뉴를 선택한후 아래에 나오는 리스트박스에서 뷰를 생성하고 싶은 버킷을 선택한다. 

다음으로, 아래에 나오는 “Create Development View” 버튼을 선택하면 뷰를 생성할 수 있다.


 

생성 버튼을 누르면 아래와 같이 대화상자가 뜨는데, 첫번째는 Design Document 명이다. 그리고 다음 텍스트 상자가 뷰 이름을 넣는 상자이다.

 


이때, design document를 넣는 텍스트박스에서 Design Document 이름이 “dev_”로 시작하는 것을 볼 수 있는데, Design document는 두 가지 타입이있다. 개발을 위한 개발용 “Development view”와 실제 운영에서 사용할 수 있는 “Production view”가 있다. 이 둘의 차이점은 다음과 같다.

  • Development View : 처음에 뷰를 생성하면 Development View로 생성된다. Design document 이름은 “_design/dev_”로 시작된다. Development View의 경우 버킷의 모든데이타에 대해서 Index를 만드는 것이 아니라 개발 목적이기 때문에 일부의 데이터에 대해서만 Index를 생성한다.
  • Production View : Development View를 이용해서 개발과 테스트를 완료하면 Publish하여 Production View로 전환한다,  Production View는 버킷의 전체 데이터로 Index를 생성한다. 

※ Development View에서 개발 및 테스트가 완료되었으면 반드시 Production View로 배포를 진행해야 한다.

뷰가 생성되었으면 콘솔에서 아래와 같이 뷰의 맵&리듀스 코드를 작성하고 테스트를 해볼 수 있다.



위에 "▼cath”라는 창에 맵&리듀스 코드 작성을 돕기위해서 버킷에 저장된 데이터 중 random으로 출력해서 샘플로 보여준다. “cath”는 샘플로 출력되는 도큐먼트의 ID이다.

아래 “▼View Code” 창 부분에는 왼쪽에는 맵 함수를 오른쪽에는 리듀스 함수를 직접 코딩해볼 수 있다. 그리고 맨아래 부분에는 이 맵&리듀스 함수를 실행했을 때의 결과를 출력해준다.

그 아래 부분에는 Filter Result라는 버튼이 있는데, 이 버튼을 통해서 맵&리듀스에 의해 생성된 결과를 필터링 할 수 있다. 도큐먼트 ID나 KEY의 범위등 지정해서 pagination등에 활용할 수 있다. 자세한 필터에 대한 내용은 향후에 설명하도록 한다.

 





뷰를 이용하여 다양한 기능 구현하기

이러한 뷰를 이용해서 카우치베이스는 다양한 데이터 조작이 가능한데, 대부분의 NoSQL은 키밸류 형태로 RDBMS에서 지원하는 Indexing, Grouping, Sorting 등 강력한 기능을 지원하지 않는 경우가 많다. 카우치베이스에서는 뷰를 이용해서 이러한 기능등을 구현할 수 가 있다.


Indexing 

RDBMS의 Index와 같은 개념으로, 도큐먼트 ID이외에 다른 필드로 검색이 가능하게 해주는 기능이다. NoSQL에서는 이를 Secondary Index라고도 부르는데, Secondary Index를 지원하는 다른 NoSQL의 경우, 제대로 성능이 나도록 설계되어 있는 경우도 있지만, Secondary Index를 기반으로 검색을 할 경우, 분산된 전체 노드를 FULL SCAN해서 (전체 목록을 뒤져서) 검색 결과를 한 노드로 합쳐서 리턴하는 형태로 구현되어 있는 경우가 있다. (Riak의 예전 Secondary Index의 구조) 이 경우, 전체 노드가 O(N)으로 전체 레코드를 검색해야 하기 때문에, 전체적인 성능 저하가 매우 심하다. 앞에서도 언급했지만, 카우치베이스는 이를 Incremental view의 개념을 사용해서, Secondary Index에 해당 하는 필드를 키로 잡은 새로운 물리적인 테이블을 내부적으로 만들기 때문에, Index를 이용한 검색도 타 NoSQL에 비해서 성능이 좋은 편에 속한다.


Extracting & Filtering

필터링은 SQL문장에서 select where라고 보면 된다. 조건에 맞는 특정한 도큐먼트만 select하는 것이 가능하다.

다음과 같은 도규먼트들이 버킷에 저장되어 있다고 할때

{

   "name": "yuna",

   "country": "us",

   "ssn": "140515-1234123",

   "sex": "female"

}

여기서 country="us" 인 것만을 쿼리하고, 전체 필드중 일부 필드(컬럼)만을 가지고 리턴하는 뷰를 만들려고 하면, 다음과 같은 맵 함수를 이용하면 된다.
function (doc, meta) {
  if(doc.country=="us") emit(doc.name,doc.country,doc.ssn);
}
emit에 오는 첫번째 필드가 KEY로 사용된다. 
단 뷰 함수에 의해서 필터링이 된 경우에는 물리적인 Index가 생기기 때문에, country를 Korea,Canada등과 같이 동적으로 변경하는 것이 불가능하다.
이런 동적인 필터링은 카우치베이스의 필터를 이용해서 가능한데, 동적으로 나라에 따라서 필터링을 하고 싶다면, 조건을 적용할 필드를 키로 리턴한 후, 키에 대한 필터링을 하면 되는데, 다음과 같이 맵 함수를 작성한 후에 뷰를 생성하고
function (doc, meta) {
  emit(doc.country,doc.name,doc.ssn);
}
아래와 같이 필터 조건에 key를 "us"로 지정하면 doc.country가 "us" 인 도큐먼트만 쿼리가 된다.
※ 문자열은 반드시 "" 을 사용해서 지정해야 한다.


Sorting
카우치베이스에서 소팅은 뷰의 키값을 기반으로 한다. 소팅의 기준으로 하고자하는 필드를 KEY로 잡으면, 뷰에서는 디폴트로 오름차순으로 키에 따라 정렬을 해준다. 내림 차순으로 정렬을 하기 위해서는 위의 필터에서 descending이라는 옵션을 체크 해주면 된다.

리듀스(Reduce)함수를 이용한 Grouping 연산
Grouping은 RDBMS의 group by와 유사한 기능으로 같은 country인 도큐먼트 수를 카운트하거나, 태어난 해별 사용자 수를 하는 것과 같은 그룹 단위의 연산을 할 수 있다.
먼저 Grouping의 개념을 사용하기 위해서는 Compound Key라는 개념을 이해해야 하는데, 카우치베이스의 Grouping 연산은 이 Compound Key를 기본으로 한다. Compound Key랑 뷰의 키를 [] (배열) 형태로 정의한 경우이다. 예를 들어 키를
["Country","생년","성별"] 과 같은 형태의 배열 형태로 정의하는 것이다. Grouping은 이 Compound Key의 Level 단위 (즉 배열의 첫번째 값으로만 그룹을 만들것인가? 아니면 첫번째,두번째 값을 포함해서 그룹으로 잡을 것인가) 로 처리한다.
실제 예제를 살펴보자. 다음과 같은 JSON 도큐먼트가 있을때,
{
   "name": "yuna",
   "country": "us",
   "ssn": "140515-1234123",
   "sex": "female"
}
앞에서 언급한 형태의 Compound Key를 만드는 함수는 다음과 같다, 
function (doc, meta) {
  var year = doc.ssn.substring(0,2);
  emit([doc.country,year,doc.sex],1);
}
이 맵함수를 이용하면 뷰는 다음과 같아진다.
["canada","69","male"] 1
["korea","75","male"] 1
["us","08","female"] 1
["us","14","female"] 1
["us","75","female"] 1
그러면 국가별로 사용자 수를 통계를 내려면, 리듀스 함수에 "_count" 라고 정의하고
필터에 group 옵션을 선택한후에, group level을 1로 지정한다.

이렇게 하면, Compound Key의 첫번째 필드 (country)를 기반으로 그룹핑을 하고, 리듀스 함수에서 이 그룹단위로 _count 연산을 수행한다.

카우치베이스에는 리듀스함수에서 사용할 수 있는 기본적인 함수를 미리 정의해놨는데, 아래와 같다.

  • _sum : 밸류값을 더한다. (밸류 타입이 Integer라야 함)
  • _count : 그룹별로 묶인 값들의 수를 카운트 한다.
  • _stats : 각종 그룹값을 계산해준다, 위에서 나온 sum뿐만 아니라, sum,count,min(최소값),max(최대값),sumsqr(Sum of square : 제곱합) 값을 출력해준다.
Pagination 
뷰에서 만들어진 데이타는 쿼리시에 pagination을 지원할 수 있다. 필터를 통해서, 뷰의 start/end key값을 정의하거나, start/end 도큐먼트ID를 지정하면 그 범위내의 도큐먼트만 쿼리할 수 있다. (위의 필터 설정 그림 참고) 콘솔에서는 나와 있지 않지만 필터에 추가로 limit라는 옵션을 주면, 최대한 읽어올 수 있는 도큐먼트 수를 정의할 수 있다.
예를 들어 뷰의 키로 1~100까지 100개의 도큐먼트가 있을때, start key=11,end key=50으로 하면 11~50까지의 50개의 레코드가 리턴된다. 여기에 limit=10을 추가하면 11~20까지의 레코드만 리턴되낟. start/end와 limit를 적절하게 사용하면 페이징 기능을 구현할 수 있다. limit를 설정하는 방법은 SDK를 이용하여 뷰를 호출하는 방법에서 알아보도록 한다.

다음번에는 SDK를 이용해서 뷰를 생성하고, 뷰에 대한 쿼리를 수행하는 방법에 대해서 알아보도록 하겠다.









Django Template

프로그래밍/Python | 2013.11.21 02:07 | Posted by 조대협


Django의 MVC 구조


원본- littlegreenriver.com

Template 란?


Template은 쉽게 이야기 하면 jsp 파일과 같다고 보면된다.

template은 string으로 파일에서 로딩할 수 도 있고 몇개의 notation을 통해서 구성된다.

마치 JSP의 <% %> 처럼

time.html 파일이 다음과 같을때 

current time is {{ current_time }}


이는 

current time is <%=current_time%> (JSP에서)와 같은 의미라고 보면된다.

실제 view 모듈에서 이 템플릿을 렌더링 하는데, 이때, tempalte에 있는 인자들을 대입한다.

    template = loader.get_template('time.html')

    current_time = datetime.datetime.now()

    context = Context({'current_time' : current_time })

    return HttpResponse(template.render(context))


Context 객체는 인자를 넘기는 일종의 컨테이너라고 보면되고,

Template에 정해진 변수 이름에, 실제 값을 맵핑 시킨후에,

Template을 이 Context로 렌더링 한다.



Template 위치에 대해서.


https://docs.djangoproject.com/en/1.4/ref/templates/api/#loader-types

django.template.loaders.app_directories.Loader

Loads templates from Django apps on the filesystem. For each app in INSTALLED_APPS, the loader looks for a templates subdirectory. If the directory exists, Django looks for templates in there.


This means you can store templates with your individual apps. This also makes it easy to distribute Django apps with default templates.


템플릿을 로딩하는 방법은 여러가지 Loader 설정에 따라 변경할 수 가 있는데,

그중에서 filesystemLoader와 app_directories.loader가 디폴트 설정되어 있다.

이때, app_directories.loaderㅢ 경우에는 app directory 아래의 /templates/ 디렉토리에서 template을 찾는다.


#views.py - mysite/reader/views.py

from django.http import HttpResponse

from django.template  import Context, loader

import datetime



def what_time_is_it(request):

    template = loader.get_template('time.html')

    current_time = datetime.datetime.now()

    context = Context({'current_time' : current_time })

    return HttpResponse(template.render(context))


이파일은 mysite라는 프로젝트의 reader라는 애플리케이션이다.

위의 파일 경로가 mysite/reader/views.py 이다

template의 파일명만 지정했는데, 이 경우

mysite/reader/templates/time.html을 찾게 된다.


Template 사용시 Dictionary 접근 방법

JSON을 변환하여 dictionary를 접근하면, 일반 python code에서

item['name']['firstname'] 이었다면

Template에서는 item.name.firstname 으로 해야 한다.


참고 : https://docs.djangoproject.com/en/1.4/ref/templates/api/#loader-types