게임개발자의 vue.js #3

Vue.js의 이벤트 핸들링

게임개발자의 vue.js #1
게임개발자의 vue.js #2

이번 포스팅에서는 Vue.js의 이벤트 핸들링에서 대해서 살펴보겠습니다. 만약 vue.js가 처음이라면 게임개발자의 vue.js 1/2편을 먼저 읽기 바랍니다.

2개의 버튼을 추가하고, 버튼에 클릭 이벤트에 이벤트 핸들러를 추가하여 화면에 표시되는 이미지를 추가/삭제 하겠습니다. 먼저 HTML을 아래와 같이 작성합니다.

스크린샷 2018-12-28 오후 4.22.26.png

Vue 인스턴스와 연결할 app이라는 id를 갖는 div 하위에 div를 생성하고 button 태그로 버튼을 2개 추가합니다. button 태그에 버튼에 표시될 텍스트를 ADD/REMOVE라고 작성하고, @click 이라는 vue용 속성을 추가하고, @click 속성의 값으로 해당 버튼의 클릭 이벤트가 발생 시 호출될 메소드 이름을 입력합니다. 참고로 @click은 v-on:click의 단축 표기법으로, @click 대신 v-on:click 속성을 사용해도 됩니다.

이제 app을 id로 갖는 div와 바인딩되어 있는 Vue 오브젝트를 구현할 차례입니다. Vue 오브젝트에 methods 프로퍼티를 추가하고, button의 @click 속성의 값으로 지정한 메소드를 구현합니다.

스크린샷 2018-12-28 오후 4.30.38.png

ADD 버튼이 클릭되면 호출될 add 메소드를 구현하고, REMOVE 버튼이 클릭되면 호출될 remove 메소드를 구현합니다. add 메소드에서는 표시할 수 있는 이미지 데이터 images와 img 태그와 v-for에 의해 표시되는 icons의 배열 길이를 검사한 후 icons의 길이가 images보다 작다면 icons에 images의 값을 클릭 이벤트가 발생할 때 마다 하나 씩 추가합니다. remove는 icons의 길이가 0보다 작다면 pop 메소드를 호출해 하나 씩 제거합니다. icons는 HTML의 v-for와 img 태그를 통해 뷰에 바인딩되어 있어, 데이터가 변경도면 뷰가 자동으로 자동으로 갱신되도록 Vue 내부가 구현되어 있습니다. 이제 브라우저에서 결과를 확인해 봅시다.

스크린샷 2018-12-28 오후 4.35.24.png

v-on:click 이외에도 폼의 summit 이벤트를 핸들링할 수 있는 v-on:submit, 스크롤 이벤트를 핸들링할 수 있는 v-on:scroll.passive, 키 입력 이벤트를 핸들링할 수 있는 v-on:keyup.{키코드}, 마우스 이벤트를 핸들링할 수 있는 v-on:mouseover 등을 제공합니다. 보다 자세한 내용은 vus.js의 공식 홈페이지에서 확인할 수 있습니다.

https://vuejs.org/v2/guide/events.html

게임개발자의 노드 #1

노드 공식 페이지의 문서>안내 를 보면 개발을 시작하기 위한 도큐먼트를 확인할 수 있습니다. 기존에는 빠르게 만드는 웹서비스와 같이 기반 기술을 이야기하지 않는 문서가 공식 페이지의 메인을 차지 했지만, 현재는 디버깅/배포/코어 등에 관한 이야기를 다루고 있습니다. 공식 페이지의 이러한 변화는 시사하는 바가 있다고 생각됩니다.

https://nodejs.org/ko/docs/guides/debugging-getting-started/
https://nodejs.org/ko/docs/guides/simple-profiling/

이번 포스팅에서는 디버깅에 대해서 살펴보겠습니다. 먼저 디버깅에 대해서 살펴보겠습니다.

–inspect 옵션과 함께 실행된 노드 프로세스나 리눅스/맥에서 SIGUSR1 신호를 보내서 디버깅 메세지를 받을 수도 있습니다. 디버깅 메세지는 v8-inspector 프로토콜을 바탕으로 동작합니다. 자세한 디버깅 프로토콜은 Chrome Debugging Protocol Viewer에서 확인할 수 있습니다. 디버깅 메세지는 인스펙터 클라이언트가 노드 프로세스로부터 웹소켓 인터페이스에 정의된 정보를 주고 받습니다.

즉, 디버깅을 위해서는 인스펙터 클라이언트를 사용해야 하는데, 아래와 같이 다양한 종류의 인스펙터 클라이언트가 있습니다.

먼저 node-inspect는 node 6.3+ 이상부터 기본적으로 설치되어 있어 따로 인스펙터 클라이언트를 설치하지 않아도 됩니다. helloworld.js라는 스크립트 파일을 생성하고, 코드를 작성했다면 터미널에서 node helloworld.js와 같은 커맨드로 노드 프로세스를 시작할 수 있습니다. 이때 node-inspect와 함께 실행하려면 node inspect helloworld.js 와 같이 inspect라는 옵션과 함쎄 실행하면 디버깅 가능한  CLI가 활성화 됩니다.

Chrome DevTools 55+에서는 노드 프로세스를 –inspect 옵션과 함께 실행 한 후 브라우저에서 chrome://inspect를 열고, 설정 버튼을 눌러 디버깅할 대상 호스트와 포트 목록을 확인합니다. Remote Target에서 실행되고 있는 노드 프로세스를 확인할 수 있습니다. 이때 노드 프로세스는 실행 후 종료되지 않도록 테스트를 위해 setInterval을 통해 로그를 출력하도록 합니다. 이후 Remote Target의 노드 프로세스 중 inspect를 클릭하면 디버깅을 위한 창이 표시됩니다.

setInterval(()=> {
    console.log(“인터벌!!”);
}, 1000);

스크린샷 2018-09-09 오전 12.14.56.png

VS Code 1.10+에서는 .vscode/launch.json 파일을 생성하는 것 만으로도 디버깅이 가능합니다. VS Code의 디버깅탭에서 Node.js 설정을 선택해 ./vscode/launch.json 파일이 추가되도록 합니다.

스크린샷 2018-09-09 오전 12.17.18.png

Node.js 디버깅을 위한 파일이 추가됐지만, 아직 디버깅은 안됩니다.

{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
“version”: “0.2.0”,
“configurations”: [
    {
       “type”: “node”,
       “request”: “launch”,
       “name”: “Launch Program”,
       “program”: “${file}”
    }
]
}

정상적으로 디버깅이 동작하도록 launch.json 파일을 열었을 때 우측 하단에 표시되는 Add Configuration 버튼을 클릭합니다. 나타난 메뉴 중 {} Node.js: Attach를 선택해 디버깅을 위한 기본 설정이 launch.json에 추가되도록 합니다. 기존에 있던 옵션은 삭제하고 디버깅 시작을 위한 플레이 버튼을 클릭합니다.

{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
“version”: “0.2.0”,
    “configurations”: [
    {
       “type”: “node”,
       “request”: “attach”,
       “name”: “Attach”,
       “port”: 9229
    }
]
}

스크린샷 2018-09-09 오전 12.25.16.png

WebStorm은 Node.js 프로젝트 생성 후 디버깅 버튼을 클릭하면 되고, chrome-remote-interface는 인스펙터 프로토콜의 사용을 도와주는 라이브러리 입니다. 이상 Node.js 디버깅에 대해서 자세히 살펴봤습니다. 다음 포스팅에서는 프로파일링에 대해서 살펴 보겠습니다.

 

게임개발자의 도커 #2

1. 도커(도커엔진)의 구성요소

도커는 서버/클라이언트 그리고 서버와 클라이언트가 통신하는데 사용되는 API를 이용해 어플리케이션이 실행될 수 있는 환경을 개발/구성/배포하도록 도와준다.  도커의 서버를 데몬, 도커의 클라이언트를 CLI라 한다. 아래 이미지가 도커의 구성요소를 이해하는데 도움이 될 것이다.

engine-components-flow.png
<출처 : https://docs.docker.com/engine/docker-overview/#what-can-i-use-docker-for>

 

스크린샷 2018-07-25 오후 11.37.16.png
<출처 : https://docs.docker.com/engine/docker-overview/#docker-architecture>

2. 도커 데몬

long-running 프로그램인 서버를 데몬 프로세스라고 부른다. 도커 데몬은 도커 API의 요청을 수신하고, 이미지/컨테이너/네트워크/저장소와 같은 도커 객체를 관리한다. 또한 도커 데몬은 도커 서비스를 관리하기 위해 다른 도커 데몬과 통신할수도 있다.

3. 도커 클라이언트

도커 클라이언트는 커맨드 라인 인터페이스(CLI)를 제공하는 클라이언트이다. CLI는 도커 데몬을 제어하거나 통신하기 위해 스크립팅이나 커맨드라인을 이용해 도커 API를 사용한다. 예를 들어 docker run이라는 커맨드를 사용하는 도커 클라이언트는 입력된 커맨드를 도커 API를 사용해 도커  데몬에게 전달한다. 도커 클라이언트는 하나 이상의 도커 데몬과 통신할 수 있다. 도커가 설치되어 있다면, 터미널에서 docker version이라고 입력해보자. 이때 출력되는 정보가 도커 클라이언트의 정보이다.

4. 도커 API

도커엔진 API라고도 한다. 도커 데몬은 unix:///var/run/docker.sock를 통해 API 요청을 기다린다. 만약 도커 데몬이 실행되지 않은 상태에서 터미널에서 docker version 라고 입력하면, ‘Cannot connect to the docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?’ 이라는 에러를 만날 수 있다. API는 기본적으로 몇 몇 예외의 API를 제외하고 RESTful하게 설계됐다. https://docs.docker.com/engine/api/latest/ 에서 도커 API의 레퍼런스를 확인할 수 있다. 수많은 API있는데, 도커 클라이언트가 제공하는 CLI를 이용하는게 정신 건강에 좋다.

 

 

golang의 image 패키지

image 패키지는 2d 이미지를 다루는 다양한 구조체/함수 등을 제공합니다.
참고 : https://golang.org/pkg/image/

 

func NewRGBA(r Rectangle) *RGBA

golang은 New*류의 함수를 제공하는데, NewRGBA는 RGBA 포맷으로 채울 수 있는 Rectangle 영역(Bounds)을 반환합니다.

 

RGBA

type RGBA struct {
        // Pix holds the image's pixels, in R, G, B, A order. The pixel at
        // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*4].
        Pix []uint8
        // Stride is the Pix stride (in bytes) between vertically adjacent pixels.
        Stride int
        // Rect is the image's bounds.
        Rect Rectangle
}

RGBA 구조체는 위와 같이 uint8 타입의 슬라이스 Pix와 Stride/Rect 프로퍼티를 갖습니다. Pix가 uint8 타입의 슬라이스인걸 보면 RGBA는 하나의 색상코드를 8비트로 표현하며, RGBA 각 색상코드를 담는 8비트 4개로 픽셀을 표현하는걸 알 수 있습니다.  Stride는 새로 다음 이미지의 다음 라인까지의 바이트수를 의미합니다. 4바이트(8비트*4)로 한 픽셀을 표현하기 때문에 Stride의 값은 Rectangle의 width*4가 됩니다.

 

func Draw(dst Image, r image.Rectanglw, src image.Image, sp image.Point, op Op)

메모리 공간에 이미지를 만들려면, image/draw 패키지의 Draw 함수를 호출해야 합니다. Draw 함수는 image.NewRGBA 함수로 만들어진 dst 메모리 공간에 src로 r 영역의 sp 영역을 시작으로 op 방식으로 색을 채우는 함수입니다.

 

image의 Uniform

type Uniform struct {
        C color.Color
}

객체를 생성하며 전달한 하나의 색상으로 만들 수 있는 무한한 크기(infinite-sized)의 이미지를 표현하는 객체입니다. 무한한 크기라 공식 문서에 설명되어 있지만 Uniform 구조체의 프로퍼티는 color.Color 하나만 가지고 있으므로 사실상 사이즈와는 무관하고 유한한 이미지를 그릴 때 사용됩니다.

 

image의 Point

type Point struct {
        X, Y int
}

x/y 쌍을 갖는 구조체입니다. 좌표는 오른쪽/아래 방향으로 증가합니다.

 

image의 Rectangle

type Rectangle struct {
        Min, Max Point
}

Min/Max 2개의 Point로 사각형을 표현합니다. Point/Size로 표현하는 다른 언어와 달리 더 수학적이라 생각됩니다.

 

os.Create/jpeg.Encode

Uniform/Draw/Point/Rectangle 등으로 메모리상의 이미지를 만들었다면 os.Create로 파일 쓰기를 위한 파일포인터를 얻어옵니다.

w, err := os.Create(f.OutputFilePath)

다양한 인코딩 방식이 있겠지만, 파일에 jpeg 인코딩을 수행하는 jpeg.Encode 함수를 사용해 메모리상의 이미지를 jpeg 포맷으로 파일에 인코딩합니다.

jpeg.Encode(w, bgRectangle, quality)

 

기타

기본적인 이미지와 파일 생성만 다뤘는데 image.Rect 함수로 생성한 Rectangle에 Add/Sub 등의 다양한 연산자 함수를 통해 색상을 채울 수 있습니다.

squareWidth := 200
squareHeight := 200
squareColor := image.Uniform{color.RGBA{R: 255, G: 0, B: 0, A: 1}}
square := image.Rect(0, 0, squareWidth, squareHeight)
square = square.Add(image.Point{
   X: (width / 2) - (squareWidth / 2),
   Y: (height / 2) - (squareHeight / 2),
})
squareImg := image.NewRGBA(square)

draw.Draw(bgRectangle, squareImg.Bounds(), &squareColor, origin, draw.Src)

 

Listener 타입의 Listen/Accept/Close/Addr

https://golang.org/pkg/net
https://golang.org/pkg/net/#Listener
https://golang.org/pkg/net/#example_Listener

type Listener

리스너는 stream-oriented 프로토콜을 위한 제네릭 네트워크 리스너이다.  여러 고루틴은 동시에 리스너의 메소드를 호출할 수 있습니다.

Listener 인터페이스는 3개의 인터페이스를 제공합니다.

Accept() (Conn, error)
기다렸다가 다음 연결을 반환

Close() error
리스너를 닫습니다. blocked된 Accept 작업은 unblocked되고, 에러를 반환

Addr() Addr
리스너의 네트워크 주소를 반환

Listener는FileListener, Listen 2개의 함수를 제공합니다.

FileListener(f *os.File) (ln Listener, err error)
파일 리스너는 열린 파일 정보에 접근할 수 있는 네트워크 리스너의 복사본을 반환합니다. 호출자는 끝날 때 파일을 닫을 책임이 있습니다.
Listen(network, address string) (Listener, error)
Listen 함수는 인자로 전달된 로컬 네트워크 주소를 announce 합니다. network 인자는 tcp, tcp4, tcp6, unix, unixpacket이어야 합니다. UDP 등의 프로토콜은 UDPConn 타입의 ListenUDP 등의 함수를 사용하면 됩니다.

예제
listener, error := net.Listen(“tcp”, “:2000”)
if err != nil {
log.Fatal(err)
}
defer listener.Close()
for {
conn, err := listener.Accept()
if err != nil {
log.Fatal(err)
}
go func(c net.Conn) {
io.Copy(c, c)
c.Close()
}(conn)
}

기타
Listener 타입의 Listen 함수는 TCP/Unix 프로토콜에 대한 추상화를 network/address 인자를 바탕으로 ListenTCP/ListenUnix 함수를 호출한 결과를 반환하도록 구현되어 있습니다. 만약 TCP/Unix 외의 네트워크 프로토콜을 사용한다면 OpError 타입의 값이 반환됩니다. Listener 타입의 Accept/Close/Addr 인터페이스는 TCP/UnixListener 각 프로토콜 타입의 Accept/Close/Addr 함수에 의해서 프로토콜 구현됩니다.

참고
https://golang.org/src/net/dial.go?s=17421:17475#L574
https://golang.org/src/net/tcpsock.go?s=7202:7246#L245

 

hello rails_api with scaffold

iOS/Android/Game 클라이언트를 개발하는 엔지니어라 서버/웹에 대한 갈증이 항상 있습니다. Server-less나 (이제는 사라진)Parse를 이용해 개발한 경험도 있지만, 여전히 뭔가 아쉬웠습니다. 그래서 시작한 Ruby on Rails! 오늘은 rails의 API Only 프로젝트를 만들고, 간단히 리뷰를 진행하겠습니다.

rails에서 API Only 프로젝트를만드려면 rails-api라는 gem을 사용해야 합니다. 과거에는 rails 프레임워크와 별도로 존재하던 프로젝트였으나, 2015년 4월 rails 프로젝트에 포함됐습니다. API Only 프로젝트는 API 제공을 위해 경량화된 rails 프로젝트를 만들어 줍니다.

rails의 API Only 프로젝트에 대한 가이드는 아래 URL에서 확인할 수 있습니다. 이 포스팅은 rubyonrails.org의 가이드를 참고해 작성됐습니다.

API Only 프로젝트를 만드려면 “rails new 프로젝트명 –api”라는 커맨드를 터미널에 입력합니다.

$ rails new todo –api

rails로 프로젝트 생성 시 사용된 –api 매개변수가 경량화된 API Only 프로젝트를 만드는 용도로 사용된 매개변수입니다. 만약 API 사용을 안하려면 –no-api 매개변수를 사용합니다.

rails 커맨드로 프로젝트 생성 시 –api 매개변수를 전달하면, 프로젝트 디렉토리의 config/application.rb의 Application 클래스에 config.api_only가 true로 설정된 프로젝트가 만들어 집니다. application.rb 뿐만이 아니라 app/controller/application_controller.rb 역시 –api 매개변수로 만들면 상위 클래스가 ActionController::Base 대신 ActionController::API가 됩니다.

API Only 프로젝트로 생성하며 rails 프로젝트의 middleware가 경량화되는데, middleware 확인을 위해 터미널에서 “rails middleware” 커맨드를 입력해 봅시다.

$ rails middleware

이제 scaffold를 사용해 사용자와 관련된 기본적인 API를 만들어 봅시다. 터미널에서 “rails g scaffold 모델명 모델컬럼:컬럼타입 모델컬럼:컬럼타입”을 입력해 봅시다.

$ rails g scaffold User name:string email:string registration_number:string is_valid:boolean

User 모델을 scaffold로 생성하며, 컬럼을 지정하면 모델과 컨트롤러, 마이그레이션, 테스트 코드와 같은 것이 생성됩니다.

스크린샷 2016-09-07 오전 1.54.55.png

user.rb는 모델 클래스입니다. users_controller.rb는 컨트롤러 클래스로 index/show/create/update 액션 메서드를 포함한 컨트롤러가 생성됩니다. users 컨트롤러는 user 모델을 다루는 기본 로직이 포함되어 있습니다.

class UsersController < ApplicationController
  before_action :set_user, only: [:show, :update, :destroy]

  # GET /users
  def index
    @users = User.all

    render json: @users
  end

  # GET /users/1
  def show
    render json: @user
  end

  # POST /users
  def create
    @user = User.new(user_params)

    if @user.save
      render json: @user, status: :created, location: @user
    else
      render json: @user.errors, status: :unprocessable_entity
    end
  end

  # PATCH/PUT /users/1
  def update
    if @user.update(user_params)
      render json: @user
    else
      render json: @user.errors, status: :unprocessable_entity
    end
  end

  # DELETE /users/1
  def destroy
    @user.destroy
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_user
      @user = User.find(params[:id])
    end

    # Only allow a trusted parameter "white list" through.
    def user_params
      params.require(:user).permit(:name, :email, :registration_number, :is_valid)
    end
end

간단히 코드 리뷰를 마쳤으면, “rails db:migrate” 커맨드를 터미널에서 입력해 마이그레이션을 실행합니다.

$ rails db:migrate

rails의 db:migrate를 실행하면, scaffold를 통해 생성된 마이그레이션을 수행합니다. 마이그레이션 수행이 정상적으로 끝나면, 터미널에서 프로젝트의 ROOT로 이동 후 “rails server” 커맨드를 입력해 WEBrick으로 서버를 실행합니다.

$ rails server

이제 웹브라우저에서 url을 입력해 scaffold로 생성한 API를 확인해 봅시다. 사용자 인증과 관련된 비즈니스 로직은 아직 없지만, 기본적인 구현이 완료됐습니다.

 

 

Laravel 시작하기

(사연많지만)뜬금없이 시작한 PHP. PHP의 웹프레임웍 중 단연 으뜸인 Laravel을 시작해보자. 이 포스팅은 Mac OS X El-Capitan, Terminal(oh-my-zsh), PHP 7.0 기준으로 작성되었습니다.

    1. 당연히 PHP가 설치되어 있어야 합니다. 터미널에서 간단히 $ php -v 커맨드를 날려줍니다. PHP를 따로 설치하지 않았다면 5.x 버젼의 PHP를 확인할 수 있습니다.
    2. PHP계의 dependency manager인 composer를 설치해야 합니다. 대략 https://getcomposer.org/download/ 이 URL에 설명되어있는 커맨드를 날려줍니다.
      스크린샷 2016-08-01 오후 1.02.34.png
      “Download Composer” 페이지의 설명대로 커맨드를 날려줬으니 이제 composer를 터미널에서 사용할 수 있도록 환경변수 $PATH에 추가해야 됩니다만, https://getcomposer.org/doc/00-intro.md 페이지의 설명대로 mv 커맨드를 사용해 composer를 /usr/local/bin 경로로 이동시켜 줍니다.
      스크린샷 2016-08-01 오후 1.04.31.png
      mv 커맨드로 composer.phar를 usr/local/bin의 composer로 이동시켰으니 터미널에서 composer -v 커맨드를 날려봅시다. composer 버젼이 잘 표시되면 OK!
    3. composer를 설치했으면 드디어 laravel을 설치할 차례입니다. laravel을 설치하기 위해 터미널에서 composer로 아래 커맨드를 날립니다.
      composer global require "laravel/installer"

      laravel이 설치가 완료되면 composer는 잊고 아래와 같이 laravel로 프로젝트를 생성하고 싶은데 문제가 발생합니다.

      laravel new blog

      laravel 커맨드를 찾을 수 없다는 에러를 뱉어냅니다. 그래서 할 수 없이 아래와 같이 composer 블라블라와 같이 사용해야 합니다.

      composer create-project --prefer-dist laravel/laravel 프로젝트이름

      그래서 방법을 찾다가 ~/.bashrc의 alias를 이용한 방법을 찾았습니다.
      터미널에서 nano ~/.bashrc 커맨드를 이용해 ~/.bashrc에 alias를 추가합니다.
      스크린샷 2016-08-01 오후 1.14.21.png
      nano로 편집한 ~/.bashrc 파일을 저장하고, 반영을 위해 source ~/.bashrc 커맨드를 날립니다. source 커맨드로 laravel alias 변경했으니 터미널에서 laravel -v 커맨드를 날려봅시다.
      스크린샷 2016-08-01 오후 1.20.30.png

 

이제 터미널에서 “$ laravel new 프로젝트명”을 사용해 프로젝트를 생성하고, laravel을 빠르게 학습합시다.
스크린샷 2016-08-01 오후 1.23.43.png

 

 

 

안드로이드 소스코드 빌드하기

안드로이드 소스코드 빌드를 위해 이틀여 시간을 보냈습니다. 특히 기본 준비 과정도 준비 과정이지만, 중요한 절차를 빠트리고 안드로이드 소스코드를 빌드를 하다보면 컴파일 에러가 발생하며 중단됩니다. 빌드에 오랜 시간이 걸리는 안드로이드 소스코드의 특성 상 삽질하지 말라고, 빌드를 위한 순서를 정리했습니다. 이번 포스팅은 Android Source Code 6.1 Master Branch 기준, OS X 10.11 El Capitan 환경에서 작성됐습니다.

소스코드 빌드를 위해서는 안드로이드 소스코드를 받아야 되는데, 그 과정은 아래 링크의 게시물에 정리했으니 먼저 읽고 진행하기 바랍니다.

안드로이드 소스코드 받기

가장 먼저 Xcode를 설치해야 합니다. Xcode는 iOS나 OS X 애플리케이션/라이브러리 개발에 사용되는 도구로 안드로이드 빌드 과정에서는 Xcode에 포함된 커맨드 라인 툴을 사용하기 때문에 필수적으로 필요합니다. Xcode는 OS X의 앱스토어에서 손쉽게 다운로드 받을 수 있습니다.

https://itunes.apple.com/kr/app/xcode/id497799835?mt=12

다음으로 JDK7이 필요합니다. 아래 링크에서 다운로드 후 설치하시기 바랍니다. 설치가 잘 됐다면 터미널을 실행하고 java -version 커맨드를 입력해 설치 및 default로 지정된 java 버젼을 확인하기 바랍니다.

http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html

스크린샷 2015-12-31 오후 4.25.07.png

안드로이드 소스코드는 case sensitive file system이 사용되도록 지정된 Volume에 위치해야 합니다. 안드로이드 소스코드가 위치한 Volume이 case sensitive file system인지 아닌지를 확인하기 위해서는 touch abc; touch abC; 라는 커맨드를 입력해 abc파일과 abC파일이 각각 생성됐는지 확인하면 됩니다. case sensitive file system으로 지정되어 있다면 대소문자를 구분하는 파일 시스템으로, abc 파일과 abC 파일은 명시적으로 다른 파일로 간주합니다. 만약 repo를 사용해 받은 안드로이드 소스코드가 case sensitive file system을 사용하지 않는 곳이라면 OS X에서는 “디스크 유틸리티”를 사용해 파티션을 만들고, 파티션을 “case sensitive file system”으로 변경 후 소스코드를 받도록 합니다.
(저 역시 이것 때문에 삽질을 했네요.)

스크린샷 2015-12-31 오후 4.31.49

안드로이드 소스코드 빌드를 위해 gmake도 사용됩니다. 그런데 gmake 설치를 위해 여러가지 방법이 있겠지만 Macport를 사용해야 하므로, Macport를 먼저 설치합니다.

http://www.macports.org/install.php

Macports를 설치했다면, 맥포트가 설치된 경로인 /opt/local/bin 경로를 .bash_profile에 추가해야 합니다. Macport 설치 파일이 환경변수에 자동으로 추가할 것입니다. .bash_profile을 열어서 확인해 봅시다.

스크린샷 2015-12-31 오후 4.41.50.png

Macport가 잘 설치되고, 환경 변수에 경로 설정이 완료됐다면, Macport를 이용해 gmake 및 기타 패키지를 설치해야 합니다. 터미널에서 아래와 같이 관리자 권한으로 커맨드를 입력해 gmake 및 기타 패키지를 설치합니다.

스크린샷 2015-12-31 오후 4.44.29.png

gmake 및 기타 패키지가 잘 설치됐다면, 이런 메시지가 출력됩니다. 맥북프로 레티나 기준 20여분이 걸렸습니다.

—>  Cleaning readline
—>  Fetching archive for gnupg
—>  Attempting to fetch gnupg-1.4.19_0.darwin_15.x86_64.tbz2 from http://packages.macports.org/gnupg
—>  Attempting to fetch gnupg-1.4.19_0.darwin_15.x86_64.tbz2 from http://mse.uk.packages.macports.org/sites/packages.macports.org/gnupg—&gt;  Attempting to fetch gnupg-1.4.19_0.darwin_15.x86_64.tbz2 from http://lil.fr.packages.macports.org/gnupg
—>  Fetching distfiles for gnupg
—>  Attempting to fetch gnupg-1.4.19.tar.bz2 from http://cjj.kr.distfiles.macports.org/gnupg
—>  Verifying checksums for gnupg
—>  Extracting gnupg
—>  Applying patches to gnupg
—>  Configuring gnupg
—>  Building gnupg
—>  Staging gnupg into destroot
—>  Installing gnupg @1.4.19_0
—>  Activating gnupg @1.4.19_0
—>  Cleaning gnupg
—>  Updating database of binaries
—>  Scanning binaries for linking errors
—>  No broken files found.

이제 파티션 후 대소문자 구분 설정으로 만들어진 디스크에 repo를 초기화하고, repo sync를 이용해 소스코드를 받습니다.

안드로이드 소스코드 받기

터미널에서 안드로이드 소스코드 저장소로 이동합니다. 터미널에서 디스크 이동을 위해서는 cd /Volumes/android 와 같이 /Volumes 뒤에 이동할 디스크의 이름을 지정합니다. 안드로이드 소스코드 빌드를 위한 환경 변수를 등록해주는 쉘스크립트인 source build/envsetup.sh를 실행해 환경 변수에 빌드에 필요한 변수를 등록합니다.

스크린샷 2015-12-31 오후 4.52.53.png

환경 변수 등록을 했다면, 터미널에서 lunch를 입력해 빌드할 타겟을 입력합니다.스크린샷 2015-12-31 오후 4.54.03

빌드 타겟을 입력하고, 터미널에서 sudo make -j4를 입력하면 빌드를 진행합니다. 대략 1시간 이상의 시간이 소요됐습니다. 모든 설정이 정상적으로 지정되어 있었다면, 다음과 같은 메시지를 만날 것 입니다.

3 warnings generated.
[ 99% 17900/18021] host Executable: libaapt2_tests (out/host…j/EXECUTABLES/libaapt2_tests_intermediates/libaapt2_tests64
clang: warning: argument unused during compilation: ‘-pie’
[ 99% 17988/18021] host Executable: libsplit-select_tests (o…libsplit-select_tests_intermediates/libsplit-select_tests64
clang: warning: argument unused during compilation: ‘-pie’
[ 99% 17995/18021] host Executable: libaapt2_tests_32 (out/h…2/EXECUTABLES/libaapt2_tests_intermediates/libaapt2_tests32
clang: warning: argument unused during compilation: ‘-pie’
[100% 18021/18021] host Executable: libsplit-select_tests_32…libsplit-select_tests_intermediates/libsplit-select_tests32
clang: warning: argument unused during compilation: ‘-pie’
Codyui-MacBook-Pro:android cody$

수고 하셨습니다. 안드로이드를 깊게 살펴볼 준비를 마쳤습니다. 삽질없이 한번에 성공하길 바랍니다.

 

 

 

 

 

 

 

 

 

Android Multithread Programming 3

Executors, ExecuteService, Runnable을 이용해 간단한 멀티스레드 예제를 만들어 봤습니다.

  1. https://goodmorningcody.wordpress.com/2015/08/18/android-multithread-programming-1/
  2. https://goodmorningcody.wordpress.com/2015/08/19/android-multithread-programming-2/

병렬 처리 시 Thread는 스레드 컨트롤을 위한 다양한 메소드를 제공합니다. 하나 씩 살펴봅시다.

  • Thread.currentThread()
    현재 실행되고 있는 코드의 스레드 인스턴스를 가져옵니다.
  • Thread.sleep(milliseconds)
    현재 스레드를 잠시 멈추도록 합니다. TimeUnit의 SECONDS, MINUTES의 sleep를 사용하면 milliseconds가 아닌 다른 시간 값을 사용할 수 있습니다.
  • instanceOfThread.getName() / instanceOfThread.getId()
    스레드의 디버깅 시 유용하게 사용됩니다.
  • instanceOfExecutorService.execute(Runnable)
    태스크 큐에 Runnable 인터페이스를 사용한 태스크를 추가합니다.
  • instanceOfExecutorService.shutdown()
    현재 태스크를 종료합니다.
  • instanceOfExecutorService.shutdownNow()
    shutdown과 차이점은 거의 없습니다. 대신 InterruptedException을 catch할 수 있습니다.
  • instanceOfExecutorService.awaitTermination()
    모든 태스크가 종료될 때 까지 block합니다. 반드시 shutdown을 먼저 호출해야 합니다.
  • instanceOfExecutorService.submit, invokeAny, invokeAll
    Runnable 대신 Callable을 사용할 때 유용한 메소드입니다. Callable과 Runnable의 차이점에 대해서 이제 살펴봅시다.

Runnable은 run 메소드가 백그라운드에서 실행됩니다. 리턴값이 없지만, run은 부작용이 발생(?)할 수 있습니다. Callable은 call 메소드가 백그라운드에서 실행됩니다. call 메소드는 리턴값이 있으며, 리턴값은 종료된 이후 “get”에 의해 획득됩니다. execute에 의해 Task List에 put되는 Runnable과 달리 Callable은 submit에 의해 Task List에 put됩니다. invokeAny와 invokeAll은 값을 사용할 수 있을 때 까지 멈출 수(?) 있습니다. 예를 들어 웹페이지로부터 상태를 체크(404 or Good)해야 한다면, Callable과 submit을 사용해 비동기로 체크하도록 구현할 수 있습니다. 이때 invokeAll은 모든 링크의 체크가 완료되면 반환값(404 or Good)을 받을 수 있도록 합니다.

Thread 클래스를 사용하면 저수준(Low Level)에서 병렬 처리를 구현할 수 있습니다. 아래 코드를 살펴봅시다.

public class LowLevelThreadActivity extends Activity implements Runnable {

    @Override
    public void run() {
        System.out.printf("print message on thread - %s%n", Thread.currentThread().getName());
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_low_level_thread);

        Thread thread1 = new Thread(this);
        thread1.start();

        Thread thread2 = new Thread(this);
        thread2.start();
    }
}

LowLevelThreadActivity의 onCreate 메소드는 Thread 인스턴스를 생성합니다. 인스턴스 생성 시 생성자로 Runnable 인스턴스를 전달하는데, LowLevelThreadActivity가 Runnable 인터페이스를 구현하도록 했습니다. 실행하면 아래와 같은 결과를 확인할 수 있습니다.

스크린샷 2015-08-19 오후 5.37.22

Thread 인스턴스를 생성하고, start 메소드를 호출하는 것은 ExecutorService의 execute 메소드를 호출하는 것과 동일합니다.

Executors를 사용해 Task Queue(Task List, ExecutorService)를 생성할 때 newFixedThreadPool을 사용했습니다. Task Queue를 인스턴스를 생성하거나 가져오는 메소드는 여러 개가 있는데 각 차이점을 살펴봅시다.

  • Executors.newFixedThreadPool(nThreads)
    가장 흔하고 간단하게 사용할 수 있는 방식입니다. 태스크 리스트를 만들고, 백그라운드에서 태스크(Runnable, Callable)를 실행합니다.
  • Executors.newScheduledThreadPool
    일정 시간 뒤나 주기적으로 실행될 태스크를 위해 사용됩니다. pre-Java5의 Timer 클래스가 대치되었습니다.
  • Executors.newCachedThreadPool
    짧게 많은 스레드와 함께 시작되는 앱을 위해 최적화된 버젼입니다. 스레드 인스턴스를 재사용합니다.
  • Executors.newSingleThreadExecutor
    Task Queue의 생성과 함께 한 번의 태스크를 실행합니다.

다음 주제로 넘어가 Thread를 멈추려면 어떻게 해야 할까요? while문과 volatile로 선언한 boolen 타입의 변수를 사용하면 됩니다.

public class LowLevelThreadActivity extends Activity implements Runnable {

    private volatile boolean running = false;

    @Override
    public void run() {
        running = true;
        System.out.printf("start the thread - %s%n", Thread.currentThread().getName());
        while (running) {
            System.out.printf("running the thread - %s%n", Thread.currentThread().getName());
        }
        System.out.printf("cleanup the thread - %s%n", Thread.currentThread().getName());
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_low_level_thread);

        Button btnTest = (Button)findViewById(R.id.btn_test);
        btnTest.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                stopAndStartThread();
            }
        });
    }

    private void stopAndStartThread() {
        if( running ) {
            running = false;
        }
        else {
            Thread thread = new Thread(this);
            thread.start();
        }
    }
}

스크린샷 2015-08-19 오후 6.20.04

역시나 지금까지 살펴본 내용은 아래 Github에서 프로젝트 소스를 다운로드 받을 수 있습니다.

https://github.com/GoodMorningCody/android-study/tree/thread-sample/AndThreadSample

맥에서 Django(장고) 시작하기

1. terminal을 실행하고, python을 입력하고 엔터를 칩니다. python이 잘 설치되어 있다면 아래와 같은 메세지가 표시되며 python repl이 실행됩니다. 아래와 같이 표시되지 않는다면, https://www.python.org/downloads/ 에서 python 설치 파일을 다운로드 받아 설치합니다.

Codyui-MacBook-Pro:~ cody$ python
Python 2.7.6 (default, Sep  9 2014, 15:04:36)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.39)] on darwin
Type “help”, “copyright”, “credits” or “license” for more information.
>>>

2. 다음 스텝으로 Django를 설치하기 위한 pip를 설치해야 합니다. pip는 Python으로 작성된 패캐지 관리 시스템입니다. python 3.4 이후의 sudo easy_install pip 라는 커맨드를 입력하고, OS X의 암호를 입력합니다.

odyui-MacBook-Pro:~ cody$ sudo easy_install pip
Searching for pip
Reading https://pypi.python.org/simple/pip/
Best match: pip 7.0.3
Downloading https://pypi.python.org/packages/source/p/pip/pip-7.0.3.tar.gz#md5=54cbf5ae000fb3af3367345f5d299d1c
Processing pip-7.0.3.tar.gz
Writing /tmp/easy_install-8xoRb6/pip-7.0.3/setup.cfg
Running pip-7.0.3/setup.py -q bdist_egg –dist-dir /tmp/easy_install-8xoRb6/pip-7.0.3/egg-dist-tmp-HcJ3C9
warning: no previously-included files found matching ‘.coveragerc’
warning: no previously-included files found matching ‘.mailmap’
warning: no previously-included files found matching ‘.travis.yml’
warning: no previously-included files found matching ‘pip/_vendor/Makefile’
warning: no previously-included files found matching ‘tox.ini’
warning: no previously-included files found matching ‘dev-requirements.txt’
no previously-included directories found matching ‘.travis’
no previously-included directories found matching ‘docs/_build’
no previously-included directories found matching ‘contrib’
no previously-included directories found matching ‘tasks’
no previously-included directories found matching ‘tests’
Adding pip 7.0.3 to easy-install.pth file
Installing pip script to /usr/local/bin
Installing pip2.7 script to /usr/local/bin
Installing pip2 script to /usr/local/bin
Installed /Library/Python/2.7/site-packages/pip-7.0.3-py2.7.egg
Processing dependencies for pip
Finished processing dependencies for pip

3. pip 설치가 완료됐다면 터미널에서 pip를 이용하여 Django를 설치합니다. 터미널에서 sudo pip install Django 라고 입력합니다. sudo와 함께 사용했으므로, 맥북의 암호를 요구하는데 암호를 입력하고 나면 Django 설치가 진행됩니다. 완료되면 아래와 같은 메세지가 출력될 것 입니다.

Codyui-MacBook-Pro:~ cody$ sudo pip install Django
The directory ‘/Users/cody/Library/Caches/pip/http’ or its parent directory is not owned by the current user and the cache has been disabled. Please check the permissions and owner of that directory. If executing pip with sudo, you may want sudo’s -H flag.
/Library/Python/2.7/site-packages/pip-7.0.3-py2.7.egg/pip/_vendor/requests/packages/urllib3/util/ssl_.py:90: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.  InsecurePlatformWarning
The directory ‘/Users/cody/Library/Caches/pip/http’ or its parent directory is not owned by the current user and the cache has been disabled. Please check the permissions and owner of that directory. If executing pip with sudo, you may want sudo’s -H flag.
Collecting Django
/Library/Python/2.7/site-packages/pip-7.0.3-py2.7.egg/pip/_vendor/requests/packages/urllib3/util/ssl_.py:90: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.  InsecurePlatformWarning
Downloading Django-1.8.2-py2.py3-none-any.whl (6.2MB)
    100% |████████████████████████████████| 6.2MB 91kB/s
Installing collected packages: Django
Successfully installed Django-1.8.2

4. 터미널에서 pip와 python을 통해 Django의 설치가 완료되었습니다. Django에 대한 내용은 https://www.djangoproject.com/에서 살펴봅시다.