콘솔에서 구글드라이브에 업로드하기

2012년 10월에 구축한 홈서버가 언제 사망할지 알 수 없는 일이라, 주기적으로 구글 드라이브에 WordPress 데이터와 Confluence 데이터를 백업하고 있다. 홈서버에서 mac으로 파일을 옮긴 후 다시 mac에서 구글드라이브로 업로드하는 과정이 불편해 개선안을 찾다가 구글드라이브 CLI를 발견해 사용해 보았다.

설치 방법은 gdrive github을 참고.

설치 후 처음 명령어를 실행해 보면 아래와 같이 인증을 위한 웹 주소를 준다. 브라우저에 이 주소를 복사해 인증 성공 후 나오는 verification code를 콘솔에 입력하면 모든 준비 과정은 끝.

# ./gdrive list
Authentication needed
Go to the following url in your browser:
https://accounts.google.com/o/oauth2/auth?access_type=offline&client_id=...

Enter verification code: ...

다음으로 할 일은 백업파일을 업로드 할 폴더의 아이디 찾기. 쿼리 문법은 여기를 참고.

# ./gdrive list --query "name contains 'Backups'"
Id                             Name      Type   Size   Created
0B4H4TB17DIyIal9MM0N3MngtMGM   Backups   dir           2016-06-25 04:41:15

업로드는 아래와 같이 간단하다.

# ./gdrive upload --parent 0B4H4TB17DIyIal9MM0N3MngtMGM ./confluence-backup-2017-04-01.zip
Uploading ./confluence-backup-2017-04-01.zip
Uploaded 0B4H4TB17DIyIMTREQUFXZklGU0E at 5.3 MB/s, total 232.9 MB

Android Studio 프로젝트 릴리즈 빌드할때 Proguard를 이용해 로그제거하기

마켓에 애플리케이션을 릴리즈하기 전에 해야할 일 중 하나는 로그를 제거하는 작업입니다.

소스코드를 수정하지 않고 릴리즈 빌드할때 Proguard를 이용해 로그(android.util.Log) 메서드 호출을 간단하게 제거할 수 있습니다.

build.gradle에서 다음과 같이 릴리즈 빌드할때 Proguard를 적용하도록 정의해 줍니다.

    buildTypes {
        release {
            runProguard true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.txt'
        }
    }

그리고 proguard-rules.txt를 아래와 같이 수정해 줍니다.

-assumenosideeffects class android.util.Log {
    public static *** d(...);
    public static *** v(...);
}

이렇게 설정하고 gradle assembleRelease로 빌드하면, Log.d(), Log.v() 호출은 제거됩니다. (리턴값을 참조하지 않는다면)

중요한 포인트는 -assumenosideeffects 옵션은 optimize 단계를 거쳐야만 적용이 된다는 점입니다. 때문에 proguard-android.txt가 아닌 proguard-android-optimize.txt를 사용하도록 설정해야 합니다.

이 파일들은 안드로이드 SDK의 tools/proguard 디렉토리에 위치하고 있습니다.

Console에서 Android SDK 업데이트하기

Jenkins로 안드로이드 프로젝트를 자동으로 빌드하는 시스템을 구축하다보면 UI 없이 Console에서 Android SDK를 업데이트해야 하는 상황이 발생합니다.

먼저 SDK 루트 디렉토리에서 tools 디렉토리로 이동합니다.

cd tools

설치할 수 있는 SDK 목록을 확인합니다.

$ ./android list sdk
Refresh Sources:
  Fetching https://dl-ssl.google.com/android/repository/addons_list-2.xml
  Validate XML
  Parse XML
  Fetched Add-ons List successfully
  Refresh Sources
  Fetching URL: https://dl-ssl.google.com/android/repository/repository-10.xml
  Validate XML: https://dl-ssl.google.com/android/repository/repository-10.xml
  Parse XML:    https://dl-ssl.google.com/android/repository/repository-10.xml
  Fetching URL: https://dl-ssl.google.com/android/repository/addon.xml
  Validate XML: https://dl-ssl.google.com/android/repository/addon.xml
  Parse XML:    https://dl-ssl.google.com/android/repository/addon.xml
  Fetching URL: https://dl-ssl.google.com/android/repository/addon-6.xml
  Validate XML: https://dl-ssl.google.com/android/repository/addon-6.xml
  Parse XML:    https://dl-ssl.google.com/android/repository/addon-6.xml
  Fetching URL: https://dl-ssl.google.com/glass/gdk/addon.xml
  Validate XML: https://dl-ssl.google.com/glass/gdk/addon.xml
  Parse XML:    https://dl-ssl.google.com/glass/gdk/addon.xml
  Fetching URL: https://dl-ssl.google.com/android/repository/extras/intel/addon.xml
  Validate XML: https://dl-ssl.google.com/android/repository/extras/intel/addon.xml
  Parse XML:    https://dl-ssl.google.com/android/repository/extras/intel/addon.xml
  Fetching URL: https://dl-ssl.google.com/android/repository/sys-img/android/sys-img.xml
  Validate XML: https://dl-ssl.google.com/android/repository/sys-img/android/sys-img.xml
  Parse XML:    https://dl-ssl.google.com/android/repository/sys-img/android/sys-img.xml
  Fetching URL: https://dl-ssl.google.com/android/repository/sys-img/android-wear/sys-img.xml
  Validate XML: https://dl-ssl.google.com/android/repository/sys-img/android-wear/sys-img.xml
  Parse XML:    https://dl-ssl.google.com/android/repository/sys-img/android-wear/sys-img.xml
  Fetching URL: https://dl-ssl.google.com/android/repository/sys-img/android-tv/sys-img.xml
  Validate XML: https://dl-ssl.google.com/android/repository/sys-img/android-tv/sys-img.xml
  Parse XML:    https://dl-ssl.google.com/android/repository/sys-img/android-tv/sys-img.xml
  Fetching URL: https://dl-ssl.google.com/android/repository/sys-img/x86/addon-x86.xml
  Validate XML: https://dl-ssl.google.com/android/repository/sys-img/x86/addon-x86.xml
  Parse XML:    https://dl-ssl.google.com/android/repository/sys-img/x86/addon-x86.xml
Packages available for installation or update: 15
   1- Android SDK Tools, revision 23.0.2
   2- Documentation for Android 'L' Preview SDK, revision 1
   3- SDK Platform Android 4.4W, API 20, revision 1
   4- SDK Platform Android L Preview, revision 1
   5- Samples for SDK API 20, revision 1
   6- Samples for SDK API L Preview, revision 1
   7- Google APIs (x86 System Image), Android API 19, revision 6
   8- Google APIs (ARM System Image), Android API 19, revision 6
   9- Glass Development Kit Preview, Android API 19, revision 8
  10- Android Support Repository, revision 6
  11- Android Support Library, revision 20
  12- Google Play services, revision 18
  13- Google Repository, revision 9
  14- Google Play Billing Library, revision 5
  15- Google Play Licensing Library, revision 2

아래와 같은 커맨드로 원하는 항목만 선택적으로 업데이트 할 수 있습니다.

$ ./android update sdk --no-ui --filter 1,10,11,12

의존성 문제 때문인지 원하는 항목이 한 번에 나타나지 않을 수도 있습니다. 보통은 리스트를 확인하고 업데이트를 요청하는 과정을 반복하게 되더군요.

Android Studio에서 unsigned apk 생성하기

build.gradle에서 다음과 같이 release 빌드용 signing configuration을 정의해 줍니다.

    signingConfigs {
        release {
            storePassword ""
            keyAlias ""
            keyPassword ""
        }
    }

    buildTypes {
        release {
            signingConfig signingConfigs.release
        }
    }

그리고 아래와 같이 console에서 release 빌드하면,

$ gradle assembleRelease

다음 위치에서 unsigned apk를 확인할 수 있습니다.

build/outputs/apk/app-release-unsigned.apk

아이폰5 배터리 충전요령

리퍼 받기 전에 쓰던 아이폰5는 최대충전용량(FullChargeCapacity)이 1000 이하로 떨어졌습니다. 배터리가 빨리 소모되는 것을 쉽게 체감할 수 있는 수준이었죠. 리퍼를 받은 후에는 배터리를 잘 관리하기 위해서 무엇이 문제였는지 돌아보았습니다.

  1. 리튬이온 배터리는 최대한 충전되어 있는 상태를 유지하면 좋다고 들어서 기회가 닿을때마다 자주 충전하고 충전하면서 사용하기도 했습니다.
  2. 회사에서는 비정품 USB 케이블에 다른 스마트폰용 충전기로 충전했습니다.

리퍼를 받은 후에는 충전방식을 바꿔봤습니다.

  1. 열이 발생하면 배터리 수명이 줄어들기 때문에 자주 충전하기 보다는 사용하지 않는 상태에서 한 번에 많이 충전하기
  2. 가급적 정품 충전기에 정품 USB 케이블 사용
  3. 한 달에 한 번 정도는 배터리 잔량이 10~20% 남을때까지 사용하기

스크린샷 2014-02-21 오전 12.14.59

그 결과 Cycle이 126회가 되었지만 최대충전용량이 최초설계용량의 95%를 유지하고 있습니다. 충전 중에 사용하는 일을 자제하고, 충전 횟수를 줄이면 Cycle이 200, 300이 넘어도 배터리는 좋은 상태를 유지할 수 있을 것 같습니다.

Mac OS X 터미널에서 Git 브랜치가 보이는 프롬프트 설정

프로젝트 저장소로 Git을 사용할때 브랜치를 생성하여 개발하다 보면, 실수로 의도하지 않은 브랜치에서 작업하게 되는 경우가 있습니다. 이러한 실수를 방지하기 위해 현재 디렉토리가 Git 저장소라면 현재 브랜치를 프롬프트에 표시하도록 설정하였습니다.

스크린샷 2014-02-05 오후 10.51.27

리눅스에서 Git을 설치하면 현재 브랜치를 출력하는 __git_ps1을 사용할 수 있지만 Mac에서는 사용할 수 없어 다음 링크를 참조하여 사용가능하도록 만들었습니다.

$ curl -o ~/.git-prompt.sh https://raw.github.com/git/git/master/contrib/completion/git-prompt.sh
$ source ~/.git-prompt.sh

~/.git-prompt.sh가 준비되었다면 ~/.profile에 아래 코드를 추가합니다.

source ~/.git-prompt.sh

# Reset
Color_Off="\[\033[0m\]"       # Text Reset

# Regular Colors
Black="\[\033[0;30m\]"        # Black
Red="\[\033[0;31m\]"          # Red
Green="\[\033[0;32m\]"        # Green
Yellow="\[\033[0;33m\]"       # Yellow
Blue="\[\033[0;34m\]"         # Blue
Purple="\[\033[0;35m\]"       # Purple
Cyan="\[\033[0;36m\]"         # Cyan
White="\[\033[0;37m\]"        # White

# Bold
BBlack="\[\033[1;30m\]"       # Black
BRed="\[\033[1;31m\]"         # Red
BGreen="\[\033[1;32m\]"       # Green
BYellow="\[\033[1;33m\]"      # Yellow
BBlue="\[\033[1;34m\]"        # Blue
BPurple="\[\033[1;35m\]"      # Purple
BCyan="\[\033[1;36m\]"        # Cyan
BWhite="\[\033[1;37m\]"       # White

# Underline
UBlack="\[\033[4;30m\]"       # Black
URed="\[\033[4;31m\]"         # Red
UGreen="\[\033[4;32m\]"       # Green
UYellow="\[\033[4;33m\]"      # Yellow
UBlue="\[\033[4;34m\]"        # Blue
UPurple="\[\033[4;35m\]"      # Purple
UCyan="\[\033[4;36m\]"        # Cyan
UWhite="\[\033[4;37m\]"       # White

# Background
On_Black="\[\033[40m\]"       # Black
On_Red="\[\033[41m\]"         # Red
On_Green="\[\033[42m\]"       # Green
On_Yellow="\[\033[43m\]"      # Yellow
On_Blue="\[\033[44m\]"        # Blue
On_Purple="\[\033[45m\]"      # Purple
On_Cyan="\[\033[46m\]"        # Cyan
On_White="\[\033[47m\]"       # White

# High Intensty
IBlack="\[\033[0;90m\]"       # Black
IRed="\[\033[0;91m\]"         # Red
IGreen="\[\033[0;92m\]"       # Green
IYellow="\[\033[0;93m\]"      # Yellow
IBlue="\[\033[0;94m\]"        # Blue
IPurple="\[\033[0;95m\]"      # Purple
ICyan="\[\033[0;96m\]"        # Cyan
IWhite="\[\033[0;97m\]"       # White

# Bold High Intensty
BIBlack="\[\033[1;90m\]"      # Black
BIRed="\[\033[1;91m\]"        # Red
BIGreen="\[\033[1;92m\]"      # Green
BIYellow="\[\033[1;93m\]"     # Yellow
BIBlue="\[\033[1;94m\]"       # Blue
BIPurple="\[\033[1;95m\]"     # Purple
BICyan="\[\033[1;96m\]"       # Cyan
BIWhite="\[\033[1;97m\]"      # White

# High Intensty backgrounds
On_IBlack="\[\033[0;100m\]"   # Black
On_IRed="\[\033[0;101m\]"     # Red
On_IGreen="\[\033[0;102m\]"   # Green
On_IYellow="\[\033[0;103m\]"  # Yellow
On_IBlue="\[\033[0;104m\]"    # Blue
On_IPurple="\[\033[10;95m\]"  # Purple
On_ICyan="\[\033[0;106m\]"    # Cyan
On_IWhite="\[\033[0;107m\]"   # White

# Various variables you might want for your PS1 prompt instead
Time12h="\T"
Time12a="\@"
PathShort="\w"
PathFull="\W"
NewLine="\n"
Jobs="\j"

export PS1=$BBlack"\u"$BRed@$BBlack"\h"$Color_Off'$(git branch &>/dev/null;\
if [ $? -eq 0 ]; then \
  echo "$(echo `git status` | grep "nothing to commit" > /dev/null 2>&1; \
  if [ "$?" -eq "0" ]; then \
    # @4 - Clean repository - nothing to commit
    echo "'$Green'"$(__git_ps1 " (%s)"); \
  else \
    # @5 - Changes to working tree
    echo "'$IRed'"$(__git_ps1 " {%s}"); \
  fi) '$BIBlue$PathShort' \$'$Color_Off' "; \
else \
  # @2 - Prompt when not in GIT repo
  echo " '$BIBlue$PathShort' \$'$Color_Off' "; \
fi)'

미리 정의된 컬러코드를 참조하여 간단히 색상을 변경할 수 있으니 참고하세요.

Ubuntu에서 MariaDB 설치 및 활용

설치

다음 링크에서 Ubuntu 버전 및 MariaDB 버전을 선택하면 설치 방법을 알려 줍니다.

Ubuntu 12.04, MariaDB 10.0을 선택한 경우 설치 과정은 다음과 같습니다.

sudo apt-get install python-software-properties
sudo apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0xcbcb082a1bb943db
sudo add-apt-repository 'deb http://ftp.kaist.ac.kr/mariadb/repo/10.0/ubuntu precise main'
sudo apt-get update
sudo apt-get install mariadb-server

설치 과정에서 root 비밀번호를 설정합니다.

테스트

$ mysql -u root -p
Enter password: 
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 55
Server version: 10.0.7-MariaDB-1~precise-log mariadb.org binary distribution

Copyright (c) 2000, 2013, Oracle, Monty Program Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]>

Node.js Binding

mariasql

var inspect = require('util').inspect;
var Client = require('mariasql');

var c = new Client();
c.connect({
  host: '127.0.0.1',
  user: 'foo',
  password: 'bar',
  db: 'mydb'
});

c.on('connect', function() {
   console.log('Client connected');
 })
 .on('error', function(err) {
   console.log('Client error: ' + err);
 })
 .on('close', function(hadError) {
   console.log('Client closed');
 });

c.query('SELECT * FROM users WHERE id = :id AND name = :name',
        { id: 1337, name: 'Frylock' })
 .on('result', function(res) {
   res.on('row', function(row) {
     console.log('Result row: ' + inspect(row));
   })
   .on('error', function(err) {
     console.log('Result error: ' + inspect(err));
   })
   .on('end', function(info) {
     console.log('Result finished successfully');
   });
 })
 .on('end', function() {
   console.log('Done with all results');
 });

c.query('SELECT * FROM users WHERE id = ? AND name = ?',
        [ 1337, 'Frylock' ])
 .on('result', function(res) {
   res.on('row', function(row) {
     console.log('Result row: ' + inspect(row));
   })
   .on('error', function(err) {
     console.log('Result error: ' + inspect(err));
   })
   .on('end', function(info) {
     console.log('Result finished successfully');
   });
 })
 .on('end', function() {
   console.log('Done with all results');
 });

c.end();

Node.js에서 Redis 사용하기

Node.js에서 Redis 사용하기

모듈 설치

$ npm install redis

API 레퍼런스

https://github.com/mranney/node_redis

프로그래밍

모듈 가져오기

var redis = require("redis");

서버에 접속하기

var client = redis.createClient(6379, "reshout.com");

Redis 명령어 실행하기

모든 Redis 명령어는 client 객체의 함수를 통해 실행할 수 있다. 모든 함수는 args 배열에 이어 callback을 전달 받는다.

두가지 형태로 호출할 수 있다.

client.mset(["test keys 1", "test val 1", "test keys 2", "test val 2"], function (err, res) {});

다른 형태는 다음과 같다.

client.mset("test keys 1", "test val 1", "test keys 2", "test val 2", function (err, res) {});

callback은 생략 가능하다.

client.set("some key", "some val");
client.set(["some other key", "some val"]);

기본으로 제공되는 redis.printcallback으로 사용하여 간단히 결과를 출력할 수 있다.

명령어에 해당하는 함수명은 대소문자 모두 사용 가능하다. client.get() 함수와 client.GET() 함수는 동일하다.

client.get("missingkey", function(err, reply) {
    // reply is null when the key is missing
    console.log(reply);
});

존재하지 않는 key에 대한 get() 함수의 결과로 null이 전달된다.

reply의 타입은 커맨드의 결과에 따라 아래와 같다.

  • single line: JavaScript String
  • integer: JavaScript Number
  • bulk: node Buffer
  • multi bulk: array of node Buffer

이벤트

client 객체는 상황에 따라 다음과 같은 이벤트를 발생(emit) 시킨다.

  • ready
  • connect
  • error
  • end
  • drain
  • idle

error 이벤트에 대한 리스너를 등록하지 않으면 에러가 발생했을때 프로그램이 종료된다.

예제

client.on("error", function (err) {
    console.log("Error " + err);
});

인증

Redis 서버가 인증을 요구하는 경우, client.auth(password, callback) 함수를 호출해 인증을 수행할 수 있다.

접속 종료

close() 함수를 호출해 강제로 접속을 종료할 수 있다. 명령어 실행에 따른 결과가 전달되기까지 기다리지 않고 즉시 접속을 종료하기 때문에 아래 코드는 아무것도 출력하지 않는다.

var redis = require("redis"),
    client = redis.createClient();

client.set("foo_rand000000000000", "some fantastic value");
client.get("foo_rand000000000000", function (err, reply) {
    console.log(reply.toString());
});
client.end();

Ubuntu에서 Redis 설치, 빌드, 실행

Ubuntu에서 Redis 설치, 빌드, 실행

다운로드

$ wget http://download.redis.io/releases/redis-2.8.4.tar.gz
$ tar xvzf redis-2.8.4.tar.gz
$ cd redis-2.8.4/

빌드

$ make
$ make test

설치

$ make install

바이너리 파일들이 /usr/local/bin에 설치된다. 시스템에 설치하지 않고 src 디렉토리에 위치한 바이너리를 사용해도 무방하다.

서버에서 정식으로 redis를 운영하고자 한다면 Ubuntu나 Debian 시스템에서 init script 설정을 도와주는 스크립트를 활용할 수 있다.

$ cd utils
$ ./install_server

이 스크립트는 몇가지 질문을 통해 Redis가 백그라운드에서 데몬으로 동작하며, 재부팅 되었을때 자동으로 실행되도록 설정하는데 도움을 준다.

Redis 서버 시작

기본 설정

$ cd src
$ ./redis-server

기본 설정은 6379포트를 사용함

설정 파일 활용

$ cd src
$ ./redis-server ../redis.conf

설정 파라미터 활용

$ cd src
$ ./redis-server --port 9999 --slaveof 127.0.0.1 6379
$ ./redis-server /etc/redis/6379.conf --loglevel debug

Redis 사용해 보기

서버를 시작한 후, redis-cli를 통해 Redis 서버에 명령을 보내고 답을 얻을 수 있다.

% cd src 
% ./redis-cli
redis> ping
PONG
redis> set foo bar 
OK  
redis> get foo 
"bar"
redis> incr mycounter
(integer) 1
redis> incr mycounter
(integer) 2
redis> 

Redis Tutorial

Redis Tutorial

http://try.redis.io

Key-Value

SET

SET server:name "fido"

GET

GET server:name

INCR

SET connections 10
INCR connections

DEL

DEL connections

EXPR

SET resource:lock "Redis Demo 1"
EXPIRE resource:lock 120
TTL resource:lock

List

RPUSH

RPUSH friends "Alice"
RPUSH friends "Bob"

LPUSH

LPUSH friends "Sam"

LRANGE

LRANGE friends 0 -1
LRANGE friends 1 2

A value of -1 for the second parameter means to retrieve elements until the end of the list.

LLEN

LLEN friends

LPOP

LPOP friends

RPOP

RPOP friends

Set

SADD

SADD superpowers "flight"

SREM

SREM superpowers "reflexes"

SISMEMBER

SISMEMBER tests if the given value is in the set.

SISMEMBER superpowers "flight"

SMEMBERS

SMEMBERS returns a list of all the members of this set.

SMEMBERS superpowers

SUNION

SUNION combines two or more sets and returns the list of all elements.

Sorted Set

ZADD

ZADD hackers 1940 "Alan Kay"
ZADD hackers 1906 "Grace Hopper"
ZADD hackers 1953 "Richard Stallman"
ZADD hackers 1965 "Yukihiro Matsumoto"
ZADD hackers 1916 "Claude Shannon"
ZADD hackers 1969 "Linus Torvalds"
ZADD hackers 1957 "Sophie Wilson"
ZADD hackers 1912 "Alan Turing"

ZRANGE

ZRANGE hackers 2 4

Hash

HSET

HSET user:1000 name "John Smith"
HSET user:1000 email "john.smith@example.com"
HSET user:1000 password "s3cret"

HGET

HGET user:1001 name

HGETALL

HGETALL user:1000

HMSET

HMSET user:1001 name "Mary Jones" password "hidden" email "mjones@example.com"