bit masking

알고리즘 문제 중에 외판원 길찾기 문제를 풀려고하다가 비트마스크란 개념이 나오는데 그 개념을 몰라서 헷갈리는 와중에 차라리 비트마스크를 공부해보자 싶었다. 왜 길을 찾는데 비트마스크란 개념을 쓰냐면, 어떤 곳에 방문했는지 탐색할때마다 배열을 만드는건 비효율 적이니까 int값만 넘기는데 자릿수마다 어떤 마을에 방문했는지 정도로 쓸 수 있는것 이다. 대신 넘기는 자료형의 자릿수보다 크게는 안된다.

백준 문제중에 기능을 한번 씩 써볼 수 있는 문제가 있어서 소스 링크를 달아둔다 link

백준 문제에 있는 기능으로 소개를 한다

n번째 비트를 1로 만들기

1
arr |= (1<<index);

index가 0 일때 맨 오른쪽 비트를 가리킨다.

index 번째 비트를 1로 만든다

1 을 index만큼 left shift 시켜서 or 연산

n번째 비트를 0으로 만들기

1
arr &= ~(1<<index);

index 번째 비트를 0으로 만든다

index위치만 0으로 하고 나머지 1로 해서 and 연산

n번째 비트가 1인지 확인하기

1
2
3
4
5
6
// 연산자 순위 때문에 괄호 순서 중요
if ((arr & (1<<index)) == 0) {
printf("0\n");
} else {
printf("1\n");
}

index 번째 비트만 1로 해서 &연산

자리가 같은곳에 1이 있다면 0이 아닌 수가 나옴

n번째 비트 전환하기

1
arr ^= (1<<index);

index 번째 비트를 1로 만든다

1 을 index만큼 left shift 시켜서 xor 연산(반전)

모든 비트를 1로 만들기

1
2
arr &= 0;
arr--;

0 과 and를 하면 0이 된다

0에서 1을 빼면 모든 비트가 1이된다 (이진수 특성)

모든 비트를 0으로 만들기

1
arr &= 0;

0 과 and를 하면 0이 된다

비트 마스크 알고리즘

  • 이제 다시 외판원 문제를 공부해보자

Retrofit-HTTP-Android

Retrofit 은 안드로이드와 자바에서 HTTP client 에서 REST API통신을 하기 위한 라이브러리이다. 공식 홈페이지. 이번에는 간단한 node server와 android client 의 HTTP 통신을 하는 간단한 예제를 소개하려고 한다. 소스코드는 link를 첨부한다. server repository에는 node server가 들어있고, client repository에는 android client가 들어있다.

이 글에서는 Server 보다는 Retrofit 에 집중 하므로 서버에 관한 내용은 간단하게만 소개한다.

Server

Server는 nodejs로 작성 할 필요는 없다. 단지 HTTP 통신을 확인하기 위한 소스이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
var express = require('express');
var http = require('http');
var path = require('path');
var app = express();
var bodyParser = require('body-parser');
// parse JSON inputs
app.use(bodyParser.json());
// parse URL encoded inputs
app.use(bodyParser.urlencoded());
app.set('port', process.env.PORT || 3000);
app.get('/', function(req, res){
res.sendFile(__dirname + '/index.html');
});
app.post('/ping', function(req, res){
res.status(200).send("pong");
});
app.post('/user/one', function(req, res){
if (req.body.name == "clucle") {
res.status(200).send({name: 'clucle', age: 23});
} else if(req.body.name == "junsu") {
res.status(200).send({name: 'junsu', age: 25});
} else {
res.status(404).send("ERROR");
}
});
app.post('/user/list', function(req, res){
res.status(200).send({users: [
{name: 'clucle', age: 23},
{name: 'junsu', age: 25}
]});
});
http.createServer(app).listen(app.get('port'), function(){
console.log('Express server listeneing on port ' + app.get('port'));
})

이 소스에서 중요한 부분은 app.post 라고 쓰여진 부분 중에서 /user/ 으로 접근하는 부분이다.

client 에서 user/one 으로 접근 할 때는 req.body.name 에 이름을 받아서 검색한다. body 는 json 형식으로 {“name”: “something”} 보내주면 파싱한다.

client 에서 user/list 으로 접근 할 때는 파라미터 없이 users 리스트를 전달해준다. body 는 json 형식으로 {“name”: “something”} 보내주면 파싱한다.

res.send(code) 에서 code 는 일반적으로 200번이 성공이고 400대가 넘어가면 관리자가 적용해둔 에러로 처리하고 client에서 200번에 왔을 때 통신을 처리하고, 에러코드가 넘어오면 에러처리를 해주면 된다.

Client

1. gradle setting

  • app의 build.gradle에 다음 소스를 추가한다. 우리가 주목해야 하는 부분은 아래와 같다. 이글의 작성 시점 2017-05-19의 최신 버전 2.3.0 버전을 사용하였다.

    compile ‘com.squareup.retrofit2:retrofit:2.3.0’
    compile ‘com.squareup.retrofit2:converter-gson:2.3.0’

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion "25.0.3"
defaultConfig {
applicationId "com.android_dev.clucle.retrofittutor"
minSdkVersion 18
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
testCompile 'junit:junit:4.12'
}

2. example

우선 통신을 하는 API를 정해서 미리 정의한다.

POST 명령으로 user/one을 실행하면 name을 인자로 줘서 User class에 맞는 json을 받는다. 주는 형식은 {“name”: name} 으로 전달된다

POST 명령으로 user/list를 실행하면 인자 없이 Users class에 맞는 json을 받는다.

@FormUrlEncoded는 json 인자를 서버에 보내줘야할 때 써준다.

1
2
3
4
5
6
7
8
9
10
11
public interface RetrofitAPI {
@POST("user/one")
@FormUrlEncoded
Call<User> getUser(
@Field("name") String name
);
@POST("user/list")
Call<Users> getUserList();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return ("name : " + name + " age : " + age);
}
}
1
2
3
public class Users {
public List<User> users;
}

정의해둔 HTTP API 통신을 하기 위해서 Retrofit 의 Builder로 서버와 통신한다. 이를 편하게 하기 위해서 RetrofitHelper를 제작한다. 아래 url과 port는 내 로컬 pc의 ip와 node서버를 열어둔 포트이다. getInstance() Method를 통해 어디서나 통신을 할 수 있도록 만들고 retrofit.create에서 위에서 만든 API를 넣어주면 서버와 통신한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class RetrofitHelper {
// server address, port
private final static String url = "http://192.168.0.22";
private final static int port = 3000;
private static Retrofit retrofit;
public static RetrofitAPI getInstance() {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(url + ":" + port)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit.create(RetrofitAPI.class);
}
public static boolean returnNetworkState(Context context) {
ConnectivityManager connectivityManager =
((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE));
return connectivityManager.getActiveNetworkInfo() != null &&
connectivityManager.getActiveNetworkInfo().isConnected();
}
}

3. start retrofit

만들어둔 RetrofitHelper와 API로 통신하는 법이다. RetrofitHelper.getInstance().에서 Interface에 정의해둔 함수명을 적는다.

RetrofitHelper.getInstance().Method(parameter).enque(new Callback<인터페이스에서 정한 받을 타입>(){ 여기에서 엔터 치고 명령어들을 override한다. })

그러면 onResponse와 onFailure에 나오는데 onResponse는 서버에서 어떤 값이 넘어온 상태이고 onFailure는 서버에서 값이 넘어오지 않은 상태이거나, 너무 많은 요청이 들어간 등 서버에서 에러가 난 상태이다.

onResponse에서 code가 에러코드가 아니지 체크하고 response.code() == 200

response.body()는 내부에 json파일이 들어있는지 확인한다 response.body() != null

만약 Model Class 여기서는 User 에 getter를 만들어 두었으면 json형식의 파일을 맞춰서 받으면 자동으로 파싱되서 들어간다. 잘 모르겠으면 소스를 위에 링크한 소스를 참고하면 편할 것이다.

updateUserList에서는 updateUser와 다르게 json이 배열 형식으로 오는데 받는 json 형식이 현재 {users: [{“name”:”name1”,”age”:”age1”},{“name”:”name2”,”age”:”age2”}]}
배열에 담겨서 온다. 이럴 때는 response.body().users로 해주면 json의 users에 담긴 배열을 받아와서 List에 넣을 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public class RetrofitPresenter {
private View view;
private User user;
private List<User> userList;
// Constructor
public RetrofitPresenter(View view) {
this.view = view;
}
// Search One User and Show View
public void updateUser(String name) {
Log.e("[Client]", "Too many request?");
RetrofitHelper.getInstance().getUser(name).enqueue(new Callback<User>() {
@Override
public void onResponse(@NonNull Call<User> call, @NonNull Response<User> response) {
if (response.code() == 200 && response.body() != null) {
user = response.body();
view.updateTextView(user.toString());
} else {
onFailure(call, new Throwable("may be response something but error"));
}
}
@Override
public void onFailure(@NonNull Call<User> call, @NonNull Throwable t) {
Log.e("[Server]", t.getLocalizedMessage());
view.updateTextView("Error! at Search One");
}
});
}
// Search User List and Show View
public void updateUserList() {
RetrofitHelper.getInstance().getUserList().enqueue(new Callback<Users>() {
@Override
public void onResponse(@NonNull Call<Users> call, @NonNull Response<Users> response) {
if (response.code() == 200 && response.body().users != null) {
userList = response.body().users;
// 임시로 2번 째 사람의 이름만 출력해보자
view.updateTextView(userList.get(1).getName());
} else {
onFailure(call, new Throwable("may be response something but error"));
}
}
@Override
public void onFailure(@NonNull Call<Users> call, @NonNull Throwable t) {
Log.e("[Server]", t.getLocalizedMessage());
view.updateTextView("Error! at Search List");
}
});
}
public interface View {
void updateTextView(String text);
}
}

Retrofit REST API Android 후기

  • 이번에 해커톤 대회에서 새로 배워온 내용이라 써보면서 편하다고 느꼇고, 글만 봐서는 어떤 내용인지 잘 모를 것 같고 한번 써보면 쉽게 사용 할 수 있는 좋은 라이브러리 인 것 같다.

MVP-Pattern-Android

MVP Pattern 이란 MVC 아키텍처 패턴에서 파생된 것으로 User Interface를 작성하는데 사용된다. MVP는 (Model-View-Presenter)의 약자이다. 기존에 작성했던 소스를 예시로 MVP Pattern의 예시를 들어보려고 한다. 소스코드는 링크를 첨부한다. source

MVP Pattern flow

flow

위의 그림을 토대로 설명을 해보겠다.

View

MVP Pattern에서 View란 activity 혹은 Fragment 에서 사용자가 보고있는 화면을 의미한다. View를 보는 사용자는 굳이 내부에서 실행 될 로직이나, 변수에는 전혀 의존성 없이 할 행동만 있으면 된다.

Presenter

Presenter 란 View와 Model 사이를 연결해주는 곳이다. View에서 User의 Action이 들어오면 데이터에 의존성이 있다면 데이터를 수정하고 없다면 데이터를 수정 하지 않는다. User Action 에 맞는 Method를 실행하고 View에 모든 명령을 다 처리 한 후 사용자에게 보여질 View를 udpate 해준다.

Model

Model 이란 사용자 저장되어 있는 정보가 필요 할 때 사용한다. 예를 들면 사람 정보를 저장한다고 하면, 사람에 관한 데이터를 DB로 저장하던지 Local 변수로 저장 할 것이다.

Simple source

이 소스는 View에서 사용자의 입력을 받아 Model(user) 에 이름을 저장하는 간단한 예제이다.

Presenter

Presenter 부터 살펴보자. Presenter 는 Model과 View 모두 연결 되있다. Presenter에 View라는 interface를 만들어두고 View에서 Presenter.View를 implements 하는 것으로 View에서 rendering 될 View에대한 Method를 override할 의무를 준다. updateName 은 View에서 사용자가 요청할 Method이고 요청이 들어오면 Model에 데이터를 저장하고 View에 update를 요청한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class MainPresenter {
private User user;
private View view;
public MainPresenter(View view) {
this.view = view;
user = new User();
}
public void updateName(String name) {
user.setName(name);
view.updateUserInfoTextView(user.toString());
}
public interface View {
void updateUserInfoTextView(String info);
}
}

Model

Model 에서는 사용자가 필요한 정보를 저장할 class가 필요하다

1
2
3
4
5
6
7
8
9
10
11
12
13
public class User {
private String name;
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Name : " + name;
}
}

View

View 에서는 Presenter의 View를 implements받아 View에서 사용해야 하는 Method를 override 한다. onCreate시 presenter를 등록 해 주고, 사용자의 행동은 Presenter로 보내준다. 여기에서는 text가 변경 될 때 Presenter에 사용자의 행동을 보낸다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class MainActivity extends AppCompatActivity implements MainPresenter.View{
private MainPresenter mainPresenter;
private TextView userInfoTextView;
private EditText name;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mainPresenter = new MainPresenter(this);
userInfoTextView = (TextView) findViewById(R.id.userInfo);
name = (EditText) findViewById(R.id.name);
name.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
mainPresenter.updateName(s.toString());
}
@Override
public void afterTextChanged(Editable s) {
}
});
}
@Override
public void updateUserInfoTextView(String info) {
userInfoTextView.setText(info);
}
}

MVP-Pattern 후기

  • 사용자 입장과 개발자 입장을 나누어서 볼 수 있다는 점이 편한 것 같다.

  • 다른 여러 디자인 패턴에 대한 정보도 궁금하다.

Data Binding? ButterKnife? 뭐하는 거에요?

Data Binding과 ButterKnife, 안드로이드를 처음 접하는 사람에겐 생소한 이름일 것이다. Data Binding과 ButterKnife 안드로이드에서 데이터 바인딩를 도와주는 library이다. 안드로이드에서 데이터 바인딩이란 무엇일까? 바로 UI와 데이터간을 서로 연결하는 프로세스이다. 그렇다면 왜 이러한 라이브러리를 사용하는 것일까? 아래 라이브러리를 사용하지 않았을 때와 그렇지 않을 때의 차이점을 간단하게 살펴보자.

데이터 바인딩을 사용하지 않았을 경우

라이브러리를 사용하지 않았을 때의 코드를 살펴보자. 아래 예시는 안드로이드에 처음 접할 때 만들었던 소스 중 일부를 첨부한다. source

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// xml 소스 중 일부
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="38"
android:background="#8B4513"
android:orientation="vertical"
android:paddingBottom="2dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="2dp">
<Button
android:id="@+id/btn_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_marginLeft="4dp"
android:text="Add"
android:textStyle="bold" />
<Button
android:id="@+id/btn_del"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginRight="4dp"
android:text="Del"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="Select Coffee"
android:textSize="18dp"
android:textStyle="bold" />
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// activity 소스 중 일부
Button btnReset;
Button btnAdd;
Button btnDel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.your_view);
btnReset = findViewById(R.id.btn_reset);
btnAdd = findViewById(R.id.btn_add);
btnDel = findViewById(R.id.btn_del);
btnReset.setOnClickListener(mClickListener);
btnAdd.setOnClickListener(mClickListener);
btnDel.setOnClickListener(mClickListener);
}
Button.OnClickListener mClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
//pass
}
};

소스를 보기만 해도 답답하다. class 내부에서 변수를 선언해주고 id를 가져와서 연결해주고 listener를 연결한다. 하나의 element마다 3줄을 써야되는 것이다. 그렇다면 이와 같은 것을 제거하기 위해 라이브러리를 사용한다면 어떻게 될까?

-
-

ButterKnife를 사용하였을 경우

이 글의 작성 시점 2017-05-11 에는 버터나이프보다 Data Binding이 좀 더 좋은 평을 얻고있다. android 자체적으로 지원하는 라이브러리에 Data Binding이 추가되어 있는 상태이다.

ButterKnife로 소스를 간결하게 하는 법을 소개한다. 사용하는 소스는 ButterKnife 공식 홈페이지 ButterKnife을 참고하자.

1. gradle setting

  • app의 build.gradle에 다음 소스를 추가한다. 우리가 주목해야 하는 부분은 아래와 같다.

    apply plugin: ‘android-apt’
    compile ‘com.jakewharton:butterknife:8.5.1’
    apt ‘com.jakewharton:butterknife-compiler:8.5.1’ testCompile ‘junit:junit:4.12’

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// app\build.gradle 중 일부
apply plugin: 'android-apt'
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
compile 'com.jakewharton:butterknife:8.5.1'
apt 'com.jakewharton:butterknife-compiler:8.5.1'
testCompile 'junit:junit:4.12'
}
  • project의 build.gradle에 다음 소스를 추가한다. 우리가 주목해야 할 부분은 아래와 같다.

    classpath ‘com.neenbedankt.gradle.plugins:android-apt:1.8’

1
2
3
4
5
6
7
8
9
10
11
12
13
// build.gradle 중 일부
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.1'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}

2. example

아래 소스를 보면 ButterKnife.bind(this);라는 부분에서 지금 사용하는 Activity의 layout(xml)에 해당하는 부분을 바인딩하고 @BindView에서 id를 연결해 주는 모습이다. 이는 class 내부에서 사용할 때 변수 선언을 해주고 안드로이드의 생명 주기상 onCreate할 때 데이터를 바인딩 해줘야하는 두줄의 소스를 한줄로 줄이는 것으로 볼 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
class ExampleActivity extends Activity {
@BindView(R.id.title) TextView title;
@BindView(R.id.subtitle) TextView subtitle;
@BindView(R.id.footer) TextView footer;
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.simple_activity);
ButterKnife.bind(this);
// TODO Use fields...
}
}

아래와 같이 여러 view의 listener도 bind한번으로 가능하게 한다.

1
2
3
4
5
6
7
8
@OnClick({ R.id.door1, R.id.door2, R.id.door3 })
public void pickDoor(DoorView door) {
if (door.hasPrizeBehind()) {
Toast.makeText(this, "You win!", LENGTH_SHORT).show();
} else {
Toast.makeText(this, "Try again", LENGTH_SHORT).show();
}
}

-
-

Data Binding 사용하였을 경우

Data Binding은 AndroidSDK에서 지원하기 때문에 공식 API문서를 참고한다 Data Binding

1. gradle setting

  • app의 build.gradle에 다음 소스를 추가한다.
1
2
3
4
5
6
7
// app\build.gradle 중 일부
android {
....
dataBinding {
enabled = true
}
}

2. example

아래 xml파일에서 기존 xml과 다른 부분을 찾자면 tag tagandroid:text=”@{user.firstName}” 이 될 것이다. Data Binding을 사용하기 위해서는 LinearLayout과 같은 사용자의 View를 으로 감싸고 그 뒤에 data요소와 view요소를 추가한다. 레이아웃 내에서 @{}를 통해서 속성을 불러올 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="user" type="com.example.User"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.firstName}"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.lastName}"/>
</LinearLayout>
</layout>
1
2
3
4
5
6
7
8
public class User {
public final String firstName;
public final String lastName;
public User(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}

이제 activity에서 바인딩을 해주면 된다.

1
2
3
4
5
6
7
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
User user = new User("Test", "User");
binding.setUser(user);
}

더 자세한 내용은 위에 링크한 안드로이드 공식 문서를 찾아보면 된다.

Data Binding, ButterKnife 후기

  • 데이터 바인딩을 해주는 것으로 소스를 좀더 간결하게 사용 할 수 있다.

  • 아직까지는 버터나이프를 사용하고 있는데, Data Binding도 익숙해지도록 노력해야겠다.

Hexo를 이용한 GitHub Blog 만들기

Hexo란 간편하게 만들 수 있는 블로그 프레임 워크다. 더 많은 정보는 공식 홈페이지 Hexo 에서 볼 수 있다. Hexo로 블로그를 만드는 방법을 소개 하려 글을 쓴다. Hexo를 사용해서 깃에 블로그를 만들기 위해서는 github 계정이 있어야 하고, node가 깔려한다. 링크를 타고 가서 nodejs를 다운받을 수 있다.

github Repository 만들기

GitHub 에서는 GitHub Repository에서 웹을 호스팅 할수 있는 기능을 제공 한다. 더 많은 정보는 공식 홈페이지 GitHub Pages 에서 볼 수 있다. 우선 Repository를 만들어야 한다. 이미 주소가 있기 때문에 빨간 경고창이 나오지만 처음이라면 경고창이 나오지 않을테니 [username].github.io 로 Repository로 만들면 된다. Public, Private는 블로그에는 영향을 주지 않는다. 이제 Create repository를 눌러서 Repository를 생성한다.
make_repo

Hexo 설치

Hexo를 설치하기 위해 윈도우에서는 Git 에서 다운받을 수 있는 GitBash를 사용할 것이다. 링크 한 홈페이지에 들어가서 Download를 하고 끝가지 설치 하면 GitBash가 깔릴 것이다. 아래는 gitbash 화면이다.
gitbash

이제 hexo를 다음 명령어를 입력해서 설치한다. git bash에서 가만히 멈춰 있는 것 처럼 보여도 다운받는 과정이다.

1
$ npm install -g hexo-cli

아래는 설치가 완료된 모습이다.
install_hexo

이제 다운받은 hexo를 이용해서 블로그의 내용이 담길 폴더를 생성하고 초기화 해준다.

1
$ hexo init (your_blog_name)

아래는 초기화가 완료된 모습이다.
init_hexo

hexo init 까지 마치면 명령을 실행한 폴더 안에 (your_blog_name) 이라는 폴더가 생긴다. 이 폴더에 들어가서 로컬에서 hexo가 어떻게 보여지는지 확인 할 수 있다. hexo server를 실행 후 localhost:4000 으로 들어가서 확인해보자.

1
2
$ cd your_blog_name
$ hexo server

hexo_server

Deploy 설정하기

Deployt란 배포를 뜻하고 방금 로컬에서 테스트한 서버를 github 블로그에 들어갔을 때 보여지게 하는 과정이다. Hexo에서는 Deploy기능을 제공해 주는데 우리는 위에서 만든 github Repository 주소를 지정해주면 Deploy시 자동으로 commit 과 push가 되면서 블로그가 갱신된다. 그래서 개인 설정을 해야하는데 개인 설정은 (your_blog_name)의 _config.yml 을 수정하면 된다.

1
$ vi _config.yml

우리가 수정할 부분은 #Site, #URL, #Deployment 이다.

#Site 에서는 title에 블로그 제목, subtitle에 문구, description에 설명, author에 이름, language에 언어를 적어준다. 이 부분은 홈페이지에 보이는 내용들을 수정 할 수 있다.

#URL 에서는 url 부분을 github Repository의 git 주소를 적어둔다 https://github.com/[username]/[username].github.io.git

config1

#Deployment 에서는 github주소와 어떤 브랜치에 올릴지 정하는데 우린 master브랜치로 하면 된다.
config2

GitHub에 Deploy하기

이제 설정은 마쳤고 github에 배포만 해주면 된다. 배포를 하기 위해서 hexo-deploy-git을 설치한다. 작업하고 있던 (your_blog_name) 폴더 안에서 명령어를 실행한다.

1
$ npm install --save hexo-deployer-git

설치를 마친 후 배포하는 명령을 실행한다.

1
2
$ hexo generate
$ hexo deploy

deploy 후 [username].github.io 에 들어가면 GitHub Blog가 완성된다.

Hexo 후기

  • 블로그 포스팅은 github에 저장 되지만 블로그 내용을 담고 있는 폴더 (your_blog_name) 을 따로 관리해 주어야 한다.

  • 블로그 테마 바꾸는 법은 후에 포스팅 하겠다.