$ cat /etc/X11/xorg.conf.d/20-thinkpad.conf
Section "InputClass"
Identifier "Trackpoint Wheel Emulation"
MatchProduct "TPPS/2 IBM TrackPoint|DualPoint Stick|Synaptics Inc. Composite TouchPad / TrackPoint|ThinkPad USB Keyboard with TrackPoint|USB Trackpoint pointing device|Composite TouchPad / TrackPoint"
MatchDevicePath "/dev/input/event*"
Option "EmulateWheel" "true"
Option "EmulateWheelButton" "2"
Option "Emulate3Buttons" "false"
Option "XAxisMapping" "6 7"
Option "YAxisMapping" "4 5"
EndSection
2014년 12월 29일 월요일
페도라에서 pointing stick 휠 사용하기
2014년 10월 8일 수요일
어째서 에러리턴을 예외처리보다 선호하는가.
대체로 아래 링크들에서 하는 이야기들의 재탕이다.
http://blogs.msdn.com/b/oldnewthing/archive/2005/01/14/352949.aspx
http://blogs.msdn.com/b/oldnewthing/archive/2004/04/22/118161.aspx
아래 링크에서는 Joel Spolsky의 코멘트 부분 역시 근본적으로 같은 지적.
http://nedbatchelder.com/text/exceptions-vs-status.html
내 코멘트를 아주 간략하게 덧붙이자면, 예외처리는 "방금 벌어진 일이 무엇인가?"가 아니라 "무슨 종류의 문제가 생겼는가?"를 기반으로 동작하도록 되어 있는데, 이것은 declarative한 코드에 어울리지만 예외처리라는 기능이 그 자체로 코드를 declarative하게 만들 능력은 없으므로, Imperative한 언어에서는 에러리턴이 맞는 방식이라는 것이다.
일전에 트위터 타임라인에서 "어째서 exception이 더 깔끔한가?" 라는 주제로 글이 올라왔었는데, 솔직하게 말하자면 그 예제 자체가 엄밀한 예외처리가 아니었고, 어째서 내가 예외처리를 싫어하는가를 단적으로 보여주는 예였다. 글을 찾기 귀찮아서 거의 equivalent한 유사코드를 써보자면,
try {
file_object = file_open("file_name"); // 파일을 연다. 예외 발생 가능성 있음.
file_object.read(BUFFER_SIZE, buffer); // 파일을 읽는다. 예외 발생 가능성 있음.
// 읽은 내용으로 뭔가를 한다.
file_object.close();
} catch (Exception e) {
handle_file_exception(e);
}
이와 대비되는 에러코드 리턴의 유사코드를 써보자면,
open_error = file_open("file_name", file_object_pointer);
if (open_error != NULL) {
handle_open_error(); // 이 구문만을 보자면 별로 할 일 없음.
return;
}
read_error = file_object_pointer->file_read(BUFFER_SIZE, buffer);
if (read_error != NULL) {
handle_read_error(); // file_close() 동작 포함.
return;
}
// 읽은 내용으로 뭔가를 한다.
file_object_pointer->file_close();
이런 형태가 될 것이다. 따라서 전자가 깔끔하다는 논리인데,
문제는 어차피 handle_open_error()와 handle_read_error()는 다를 수밖에 없다는 점이다. 위의 사례를 가지고 말한다면 handle_open_error()에서는 그냥 나가면 되지만 handle_read_error에서는 이미 파일은 열린 상태이므로 file_close()가 되어야 한다. 저 handle_file_exception() 내부는 에러리턴 방식의 코드보다 훨씬, 훨씬, 훨씬 더 fugly 하게 된다는 점이다.
func handle_file_exception(e) {
if (e instanceof FileOpenExeption) {
handle_open_error();
} else if (e instanceof FileReadExeption) {
handle_file_error();
}
}
...
쳐다 보기도 싫다. -_-
...
instanceof가 둘이나 들어갔다. 그나마도 런타임에 오브젝트에 대한 힌트를 얻을 수 있는 언어라고 가정할 때 이야기다.
여전히 handle_open_error()와 handle_file_error()는 필요하다. 이것이 exception 방식의 깔끔함인가? -_-
조엘이 지적한 같은 문제의 다른 측면도 추가해보자. 예외처리 방식은 어느 라인에서 try 블럭을 빠져나오는 것인지 명시적으로 보이지 않는다. 그리고 그것은 위의 func handle_file_exception(e);의 같은 추함의 다른 측면을 가리키는 것이다.
finally 도 생각보다 무력하다. finally절에 들어갈 코드 역시 코드가 어디까지 진행되었느냐에 따라서 다르게 동작해야 한다. db를 열다가 실패했을 때, db까지는 열렸는데 쿼리를 하다가 실패했을 때, 쿠리까지는 성공했는데 다른 뭔가를 하다가 실패했을 때, 그때마다 해야 할 일이 다르다. 진짜로 무조건 실행되는 구문이라면 겨우 두세줄 이하이거나, 아니면 finally 구문 안에서 코드 진행을 유추할 수 있는 흔적들을 찾아서 if-else로 씨름할텐데 역시 추하기는 마찬가지.
한편 위의 일견 깔끔해보이는 코드를 가지고 exception을 비난하는 것은 부당하다. 예외처리 코드로서도 잘못되었기 때문이다. 잘못 짠 예외처리 구문을 가지고 예외처리를 비난하는 것은 부당하지 않은가. 자바의 경우 catch(Exception e){} 구문은 DO NOT 목록의 대표적인 아이템이다. exception의 방식으로 위의 문제를 다룬다면,
try {
file_object = file_open("file_name"); // 파일을 연다.
file_object.read(BUFFER_SIZE, buffer); // 파일을 읽는다.
// 읽은 내용으로 뭔가를 한다.
file_object.close();
} catch (FileOpenException e) {
handle_file_open_exception(e); // 별로 하는 일 없음.
} catch (FileReadException e) {
handle_file_read_exception(e); // 파일 클로즈 포함해야 함.
}
이 코드 자체는 맞는 코드라고 할 수 있을 것이다. 그러나 여전히 코드 진행 도중에 점프가 일어나는 문제는 그대로이다. 열기 단계에서 문제가 일어났을 때, 읽기 단계에서 문제가 일어났을 때, 코드가 위에서 아래로 라인단위로 진행되지 못하고 goto가 발생한다. 위의 코드는 코드 흐름만을 보이기 위해서, read를 버퍼 사이즈만큼 한 번만 읽고 말 정도로 각 단계가 매우 짧게 축약되어 있다는 점을 상기하자. 위로 다시 올라가지 않는 goto는 경우에 따라서 조심스럽게 쓸 수도 있다고 생각하는 쪽이지만 이 경우에 에러리턴 방식에서는 필요 없었던 점프가 등장해서 얻게 된 실익이 무엇인가? 올바른 에러처리에 오면, 이미 에러리턴 방식보다 코드가 짧다고도 할 수 없다.
상황을 좀 더 복잡하게 만들어보자. 동일한 예외가 다른 두 지점에서 발생할 수 있는 상황을 가정해보자.
http://blogs.msdn.com/b/oldnewthing/archive/2005/01/14/352949.aspx
http://blogs.msdn.com/b/oldnewthing/archive/2004/04/22/118161.aspx
아래 링크에서는 Joel Spolsky의 코멘트 부분 역시 근본적으로 같은 지적.
http://nedbatchelder.com/text/exceptions-vs-status.html
내 코멘트를 아주 간략하게 덧붙이자면, 예외처리는 "방금 벌어진 일이 무엇인가?"가 아니라 "무슨 종류의 문제가 생겼는가?"를 기반으로 동작하도록 되어 있는데, 이것은 declarative한 코드에 어울리지만 예외처리라는 기능이 그 자체로 코드를 declarative하게 만들 능력은 없으므로, Imperative한 언어에서는 에러리턴이 맞는 방식이라는 것이다.
일전에 트위터 타임라인에서 "어째서 exception이 더 깔끔한가?" 라는 주제로 글이 올라왔었는데, 솔직하게 말하자면 그 예제 자체가 엄밀한 예외처리가 아니었고, 어째서 내가 예외처리를 싫어하는가를 단적으로 보여주는 예였다. 글을 찾기 귀찮아서 거의 equivalent한 유사코드를 써보자면,
try {
file_object = file_open("file_name"); // 파일을 연다. 예외 발생 가능성 있음.
file_object.read(BUFFER_SIZE, buffer); // 파일을 읽는다. 예외 발생 가능성 있음.
// 읽은 내용으로 뭔가를 한다.
file_object.close();
} catch (Exception e) {
handle_file_exception(e);
}
이와 대비되는 에러코드 리턴의 유사코드를 써보자면,
open_error = file_open("file_name", file_object_pointer);
if (open_error != NULL) {
handle_open_error(); // 이 구문만을 보자면 별로 할 일 없음.
return;
}
read_error = file_object_pointer->file_read(BUFFER_SIZE, buffer);
if (read_error != NULL) {
handle_read_error(); // file_close() 동작 포함.
return;
}
// 읽은 내용으로 뭔가를 한다.
file_object_pointer->file_close();
이런 형태가 될 것이다. 따라서 전자가 깔끔하다는 논리인데,
문제는 어차피 handle_open_error()와 handle_read_error()는 다를 수밖에 없다는 점이다. 위의 사례를 가지고 말한다면 handle_open_error()에서는 그냥 나가면 되지만 handle_read_error에서는 이미 파일은 열린 상태이므로 file_close()가 되어야 한다. 저 handle_file_exception() 내부는 에러리턴 방식의 코드보다 훨씬, 훨씬, 훨씬 더 fugly 하게 된다는 점이다.
func handle_file_exception(e) {
if (e instanceof FileOpenExeption) {
handle_open_error();
} else if (e instanceof FileReadExeption) {
handle_file_error();
}
}
...
쳐다 보기도 싫다. -_-
...
instanceof가 둘이나 들어갔다. 그나마도 런타임에 오브젝트에 대한 힌트를 얻을 수 있는 언어라고 가정할 때 이야기다.
여전히 handle_open_error()와 handle_file_error()는 필요하다. 이것이 exception 방식의 깔끔함인가? -_-
조엘이 지적한 같은 문제의 다른 측면도 추가해보자. 예외처리 방식은 어느 라인에서 try 블럭을 빠져나오는 것인지 명시적으로 보이지 않는다. 그리고 그것은 위의 func handle_file_exception(e);의 같은 추함의 다른 측면을 가리키는 것이다.
finally 도 생각보다 무력하다. finally절에 들어갈 코드 역시 코드가 어디까지 진행되었느냐에 따라서 다르게 동작해야 한다. db를 열다가 실패했을 때, db까지는 열렸는데 쿼리를 하다가 실패했을 때, 쿠리까지는 성공했는데 다른 뭔가를 하다가 실패했을 때, 그때마다 해야 할 일이 다르다. 진짜로 무조건 실행되는 구문이라면 겨우 두세줄 이하이거나, 아니면 finally 구문 안에서 코드 진행을 유추할 수 있는 흔적들을 찾아서 if-else로 씨름할텐데 역시 추하기는 마찬가지.
한편 위의 일견 깔끔해보이는 코드를 가지고 exception을 비난하는 것은 부당하다. 예외처리 코드로서도 잘못되었기 때문이다. 잘못 짠 예외처리 구문을 가지고 예외처리를 비난하는 것은 부당하지 않은가. 자바의 경우 catch(Exception e){} 구문은 DO NOT 목록의 대표적인 아이템이다. exception의 방식으로 위의 문제를 다룬다면,
try {
file_object = file_open("file_name"); // 파일을 연다.
file_object.read(BUFFER_SIZE, buffer); // 파일을 읽는다.
// 읽은 내용으로 뭔가를 한다.
file_object.close();
} catch (FileOpenException e) {
handle_file_open_exception(e); // 별로 하는 일 없음.
} catch (FileReadException e) {
handle_file_read_exception(e); // 파일 클로즈 포함해야 함.
}
이 코드 자체는 맞는 코드라고 할 수 있을 것이다. 그러나 여전히 코드 진행 도중에 점프가 일어나는 문제는 그대로이다. 열기 단계에서 문제가 일어났을 때, 읽기 단계에서 문제가 일어났을 때, 코드가 위에서 아래로 라인단위로 진행되지 못하고 goto가 발생한다. 위의 코드는 코드 흐름만을 보이기 위해서, read를 버퍼 사이즈만큼 한 번만 읽고 말 정도로 각 단계가 매우 짧게 축약되어 있다는 점을 상기하자. 위로 다시 올라가지 않는 goto는 경우에 따라서 조심스럽게 쓸 수도 있다고 생각하는 쪽이지만 이 경우에 에러리턴 방식에서는 필요 없었던 점프가 등장해서 얻게 된 실익이 무엇인가? 올바른 에러처리에 오면, 이미 에러리턴 방식보다 코드가 짧다고도 할 수 없다.
상황을 좀 더 복잡하게 만들어보자. 동일한 예외가 다른 두 지점에서 발생할 수 있는 상황을 가정해보자.
- 파일1을 특정 지점까지 읽는다.
- 거기까지 읽은 내용에 기반해서 파일2를 읽는다.
- 파일2에 써 있는 내용을 기반으로 파일 1을 계속 읽을지 결정한다.
try {
file_object1 = file_open("file_name1"); // 문제1
read_count = file_object1.read(BUFFER_SIZE, buffer); // 문제2
file_name2 = extract_filename_from_buffer(buffer, read_count);
file_object2 = file_open(file_name2"); // 문제3
read_count = file_object2.read(BUFFER_SIZE, buffer); // 문제4
file_object2.close();
if (should_read_more(buffer, read_count)) {
read_count = file_object1.read(BUFFER_SIZE, buffer); // 문제5
// 추가로 읽은 내용으로 뭔가를 한다.
}
file_object1.close();
} catch (FileOpenException e) {
// 지점 1, 3에서 발생한 문제 해결
} catch (FileReadException e) {
// 지점 2, 4, 5에서 발생한 문제 해결
}
위의 경우에 FileOpenException의 캐치 구문은 문제 1, 3을 해결해야 하고, FileReadException의 캐치 구문은 문제 2, 4, 5를 해결해야 한다.
문제1 발생시 - 할 일 없음.
문제 3 발생시 - 파일1을 닫아줘야 함.
문제2 발생시 - 파일1을 닫아줘야 함.
문제4 발생시 - 파일1, 2를 닫아줘야 함.
문제5 발생시 - 파일1, 2를 닫아줘야 함.
이 지경이 되면, 각각의 catch 블락은 어떤 상황인지를 파악하느라 if-else에 무슨 객체가 널인지 아닌지 등등을 체크하느라 헬이다. 그것도 문제가 일어난 코드와 점프로 멀찍이 떨어져서. 저런 문제를 피하기 위해서 당장 떠오르는 방법은.. try 구문의 중첩이나, 아니면 위의 "올바른 예외처리" 형태처럼 될 수 있도록 try 블락 하나에서 파일을 하나만 여닫는 것이다. 그러면 자주 여닫게 될 것이다. fd가 로컬 파일이 아니라 TCP 통신이거나 한 상태라면 이 옵션은 불가능할 것이다.
수미상관으로, 예외처리가 과연 유용할까? 유용할 수 있다고 생각한다. 고도로 declarative 하게 짜여진 코드라면 그럴 수 있다. 그러나 imperative한 언어에서, 예외처리로 올바르게 예외를 처리하는 것은 극히 어려우며 주로는 잘못 해결한 문제를 감추는데 훨씬, 훨씬 더 유용하다.
// 지점 1, 3에서 발생한 문제 해결
} catch (FileReadException e) {
// 지점 2, 4, 5에서 발생한 문제 해결
}
위의 경우에 FileOpenException의 캐치 구문은 문제 1, 3을 해결해야 하고, FileReadException의 캐치 구문은 문제 2, 4, 5를 해결해야 한다.
문제1 발생시 - 할 일 없음.
문제 3 발생시 - 파일1을 닫아줘야 함.
문제2 발생시 - 파일1을 닫아줘야 함.
문제4 발생시 - 파일1, 2를 닫아줘야 함.
문제5 발생시 - 파일1, 2를 닫아줘야 함.
이 지경이 되면, 각각의 catch 블락은 어떤 상황인지를 파악하느라 if-else에 무슨 객체가 널인지 아닌지 등등을 체크하느라 헬이다. 그것도 문제가 일어난 코드와 점프로 멀찍이 떨어져서. 저런 문제를 피하기 위해서 당장 떠오르는 방법은.. try 구문의 중첩이나, 아니면 위의 "올바른 예외처리" 형태처럼 될 수 있도록 try 블락 하나에서 파일을 하나만 여닫는 것이다. 그러면 자주 여닫게 될 것이다. fd가 로컬 파일이 아니라 TCP 통신이거나 한 상태라면 이 옵션은 불가능할 것이다.
수미상관으로, 예외처리가 과연 유용할까? 유용할 수 있다고 생각한다. 고도로 declarative 하게 짜여진 코드라면 그럴 수 있다. 그러나 imperative한 언어에서, 예외처리로 올바르게 예외를 처리하는 것은 극히 어려우며 주로는 잘못 해결한 문제를 감추는데 훨씬, 훨씬 더 유용하다.
2014년 9월 25일 목요일
안드로이드 환경의 웹브라우저들.
대충 4.0 이후만 취급해서..
1. 웹브라우저 - 런쳐에서 아이콘 클릭해서 열리는 브라우저 얘기. 크롬. 최신 버젼 사용 가능.
2. WebView - android.webkit.WebView. 경우의 수가 나뉨.
1. 웹브라우저 - 런쳐에서 아이콘 클릭해서 열리는 브라우저 얘기. 크롬. 최신 버젼 사용 가능.
2. WebView - android.webkit.WebView. 경우의 수가 나뉨.
- Android API level 19 (킷캣) 이상에서는 최신 크롬 기반.
- Android API level 18 이하에서는 꽤 낡은 버젼의 웹킷.
3. 시스템에 기본으로 들어 있지 않은, chromium 프로젝트에서 자기가 직접 싸제로 WebView 빌드하는 경우 - 최신 버젼의 WebView. 그러나 그래픽 가속이 불가능.
4. 시스템에 기본으로 들어 있지 않은, chromium 프로젝트에서 빌드한 최신버젼의, 그래픽 가속 되는 WebView - 저희 회사에 문의주세요. 한 번 만들어 보죠.
2014년 7월 8일 화요일
Material Design 단상
개인적으로는 언제부터인가 몇 번이나 말해온 내용을 좀 더 공식적이고 권위있는 목소리로 말해줘서 무척이나 감격스럽다.
나는 이 영상을 계기로 ux에 대해서 '직관성', '직관적인' 등의 단어가 무분별하게 남용되지 않기를 바란다. 이 영상은 애플의 스큐어몰피즘이 어째서 작동하는 매커니즘인지를 그런 모호한 단어들보다 훨씬 잘 설명한다.
영상의 최초의 키워드는 메타포(은유)이다. 메타포는 서로 공유하는 경험, 짧은 스토리를 통한 강력하게 함축된 전달이다.
안드로이드의 Material Design이 택한 재질(Material)은 종이였다. 인류가 천 년을 사용해 온 재질을 은유하되 그보다 강력한, 마음대로 늘어나고 줄어들고 붙었다 떨어지는 마법의 종이.
매터리얼 디자인은 그림자 효과와 높낮이를 활용한다. 클릭될 수 있는 것은 돌출되어서 클릭될 수 있다고 알려주어야 한다.
매터리얼 디자인은 애니메이션을 활용한다. 현실세계에 워프와 순간이동은 거의 관측되지 않는다. 사물은 과정을 통해서 변해야 한다.
이러한 디자인 '언어'는 직관을 가능하게 된다. 종이, 누를 수 있을 것 같아 보이는 돌출, 애니메이션, 만질 수 있음 (tangible) 등등은 무엇이 될 것 같은지를 알 수 있게 한다.
따라서 애플의 스큐어모피즘이 작동하는 이유를 설명할 단어가 '직관성'이겠는가 메타포(은유) 이겠는가?
뮤직플레이어에 나오는 버튼을 클릭하자 버튼이 확대되면서 플레이 제어 화면이 되는 장면은 매터리얼 디자인이 스큐어모피즘보다는 좀 더 추상적이고 고차원적인 은유임을 보여준다. 스큐어모피즘은 주로 우리가 아는 사물을 모사하는데서 직관을 발생시킨다. 진짜 오디오처럼 동작함으로써 동작을 예측 가능하게 했을 것이다.
그러나 위의 예시는 현실에서 가능한 동작이 아니다. 그것은 추상적으로, 버튼이 확장된다는 점, 바로 그 버튼이 확장된 종이 위에 UI가 펼쳐졌다는 점에서 해당 버튼과 같은 맥락 위에 있다는 것을 보여준다. 이것은 현실의 모사가 아니고, 추상적, 논리적인 컨텍스트의 발생이다.
얼마나 많은 디자이너들이 이것을 이해할까? 잘 모르겠다. 스스로를 ux 디자이너라고 주장하는 사람들은 이제 매터리얼 디자인의 빌딩블럭을 손에 쥔 이상 정말로 자신이 디자인하는 것이 단순한 겉모양이 아니라 ux임을 증명해야 할 것이다. 그들은 애니메이션을, 그 애니메이션의 컨텍스트를 디자인해야 할 것이다.
ux의 흐름 그 자체에 대해서 별다른 생각을 해보지 않은 채, 그저 iOS 디자인을 열화카피한 디자인을 안드로이드에 적용시키는 기획자와 일하는 것은 꽤나 재미 없는 작업이었다. 난 극단적으로 냉소적인 사람은 아니지만, 솔직히 말하자면 이런 명료한 프레젠테이션이 발표된 이 순간 이후에도, 여전히 그런 기획자들이 대다수를 차지할 것이라고 생각할 정도로는 냉소적이다.
나는 이 영상을 계기로 ux에 대해서 '직관성', '직관적인' 등의 단어가 무분별하게 남용되지 않기를 바란다. 이 영상은 애플의 스큐어몰피즘이 어째서 작동하는 매커니즘인지를 그런 모호한 단어들보다 훨씬 잘 설명한다.
영상의 최초의 키워드는 메타포(은유)이다. 메타포는 서로 공유하는 경험, 짧은 스토리를 통한 강력하게 함축된 전달이다.
안드로이드의 Material Design이 택한 재질(Material)은 종이였다. 인류가 천 년을 사용해 온 재질을 은유하되 그보다 강력한, 마음대로 늘어나고 줄어들고 붙었다 떨어지는 마법의 종이.
매터리얼 디자인은 그림자 효과와 높낮이를 활용한다. 클릭될 수 있는 것은 돌출되어서 클릭될 수 있다고 알려주어야 한다.
매터리얼 디자인은 애니메이션을 활용한다. 현실세계에 워프와 순간이동은 거의 관측되지 않는다. 사물은 과정을 통해서 변해야 한다.
이러한 디자인 '언어'는 직관을 가능하게 된다. 종이, 누를 수 있을 것 같아 보이는 돌출, 애니메이션, 만질 수 있음 (tangible) 등등은 무엇이 될 것 같은지를 알 수 있게 한다.
따라서 애플의 스큐어모피즘이 작동하는 이유를 설명할 단어가 '직관성'이겠는가 메타포(은유) 이겠는가?
뮤직플레이어에 나오는 버튼을 클릭하자 버튼이 확대되면서 플레이 제어 화면이 되는 장면은 매터리얼 디자인이 스큐어모피즘보다는 좀 더 추상적이고 고차원적인 은유임을 보여준다. 스큐어모피즘은 주로 우리가 아는 사물을 모사하는데서 직관을 발생시킨다. 진짜 오디오처럼 동작함으로써 동작을 예측 가능하게 했을 것이다.
그러나 위의 예시는 현실에서 가능한 동작이 아니다. 그것은 추상적으로, 버튼이 확장된다는 점, 바로 그 버튼이 확장된 종이 위에 UI가 펼쳐졌다는 점에서 해당 버튼과 같은 맥락 위에 있다는 것을 보여준다. 이것은 현실의 모사가 아니고, 추상적, 논리적인 컨텍스트의 발생이다.
얼마나 많은 디자이너들이 이것을 이해할까? 잘 모르겠다. 스스로를 ux 디자이너라고 주장하는 사람들은 이제 매터리얼 디자인의 빌딩블럭을 손에 쥔 이상 정말로 자신이 디자인하는 것이 단순한 겉모양이 아니라 ux임을 증명해야 할 것이다. 그들은 애니메이션을, 그 애니메이션의 컨텍스트를 디자인해야 할 것이다.
ux의 흐름 그 자체에 대해서 별다른 생각을 해보지 않은 채, 그저 iOS 디자인을 열화카피한 디자인을 안드로이드에 적용시키는 기획자와 일하는 것은 꽤나 재미 없는 작업이었다. 난 극단적으로 냉소적인 사람은 아니지만, 솔직히 말하자면 이런 명료한 프레젠테이션이 발표된 이 순간 이후에도, 여전히 그런 기획자들이 대다수를 차지할 것이라고 생각할 정도로는 냉소적이다.
2014년 5월 31일 토요일
Android 개발자로 일하기 위해서 필요한 지식들
한동안 안드로이드 앱 개발자로 일하면서 생각했던 단계별로 필요한 지식들, 안드로이드 개발을 막 시작한 부사수가 있다면 이런 과정을 거치도록 유도할 것이라고 생각했던 것들입니다.
1단계 - 아직 일 못 맡기지만 좋은 시작
- 자바의 문법을 대부분 숙지
- http://developer.android.com/guide/index.html 에 있는 내용 중 필수적인 것 숙지. 다음은 그 중에서도 가장 우선순위가 높은 것들
- http://developer.android.com/guide/components/fundamentals.html
- http://developer.android.com/guide/components/intents-filters.html
- http://developer.android.com/guide/components/activities.html
- 기획서를 보고 xml 레이아웃을 어떻게 잡아야 할지 쉽게 떠올릴 수 있음
2단계 - 간단한 작업은 맡겨도 됨
- UI thread를 이해할 것
- race condition, deadlock, visibility 등을 이해하고 위의 세 가지 문제가 없는 AsyncTask를 작성할 수 있을 것.
- 코딩 원칙 체화 - 디자인 패턴, SOLID, 메소드 짧게 짜기, 한 메소드 내에서 각 statement 들의 추상화 정도가 비슷한 수준을 유지하게 하기 등등
- Activity, Service의 라이프사이클을 이해하여 이 부분에서 문제가 되는 해괴한 코드를 짜지 않음. (static 변수에 의한 leakage예시가 대표적)
- http://developer.android.com/guide/index.html 에 있는 내용을 대체로 숙지
3단계 - 같이 일하기 즐거운 동료
- LOGCAT, adb shell top, hprof, 프로파일러 등등을 이용해서 메모리 사용, CPU-배터리 사용을 최적화할 수 있음
- Mark and Sweep 을 이해하고, Activity 내의 static 변수가 발생시킬 수 있는 과도한 메모리 사용, 비트맵의 recycle() 이슈 등 이미 잘 알려진 메모리 관련 이슈들을 알고 있음.
- 최신 유행하는 오픈소스 라이브러리 동향을 파악하고 있음. 이미지 로더 짜놓고 나서 volley를 발견한 멍청하고 아픈 과거가 있음.
- jni 를 이용하여, 네이티브에서 자바 오브젝트와 메소드을 접근하여 사용할 수 있음
4단계 - 내가 배워하는 분들
- Android 내부를 잘 이해하여 zygote, Binder 등을 막힘 없이 설명할 수 있음.
- aidl을 활용함
- GC의 동작 특성을 이해하여 locality를 고려한 코드를 짤 수 있음
2014년 5월 2일 금요일
emacs + Golang
go-settings.el
(add-to-list 'load-path "~/.emacs.d/go-mode" t) (require 'go-mode-load) (add-hook 'go-mode-hook (lambda () (local-set-key (kbd "C-c C-r") 'go-remove-unused-imports))) (add-hook 'go-mode-hook (lambda () (local-set-key (kbd "C-c i") 'go-goto-imports))) (add-hook 'before-save-hook 'gofmt-before-save) (add-to-list 'load-path "~/.emacs.d/go-autocomplete" t) (require 'go-autocomplete) (custom-set-faces ;; custom-set-faces was added by Custom. ;; If you edit it by hand, you could mess it up, so be careful. ;; Your init file should contain only one such instance. ;; If there is more than one, they won't work right. '(magit-item-highlight ((t nil)) t)) ;; go-flymake & go-flycheck (add-to-list 'load-path "~/.emacs.d/go-flymake") (require 'go-flymake)
.emacs 에 추가될 사항
(load "~/.emacs.d/go-settings")
2014년 4월 24일 목요일
TDD 단상
어제밤 중에 TDD 이야기가 나온 김에 그냥 내 삽질 끝에 내린 생각 정리.
테스트를 먼저 작성
켄트 백의 책이 던진 충격 중에 가장 도발적이고 강력한 것이 이것이 아니었을까 생각한다.
"아직 아무 코드도 없는 상태에서, 실패할 테스트를 먼저 작성한다."
이것이 가지는 부수적인 효과는 다음과 같은 것들이라고 생각한다.
1. 코딩에 들어가기 전, 어떤 인터페이스, 다시 말해서 어떤 인풋과 어떤 아웃풋을 가져야 할지를 먼저 생각하고 코딩에 들어가게 한다. 다시 말해서 내부 구현이 아니라 behavior에 의존하는 코드를 짜도록 강요한다.
2. 매 다음 단계마다 테스트가 작성되므로 테스트가 각 '단위별'로 만들어지게 된다.
3. 2의 여파로 각 단위가 고립되서 테스트되어야 하고 결과적으로 디커플링이 중요한 문제가 된다. 나는 mock을 이용하는 테스트가 멋지다고 생각한다.
이것들은 모두 좋은 디자인을 강제하는 방법으로 보였었다. 어떤 면에서는 전통적이라고 볼 수 있는 부분들도 있다. 나는 C를 A book on C, The C Programming Language 등으로 했었는데, 이 시절의 책들에서 실제 코드를 하기 전에 하도록 권유하는 것은 (테스트 작성은 아니고) 주석 작성이었다. 이것이 결국 인터페이스를 먼저 생각하라, 구현보다 인터페이스에 의존해라 등의 가르침이 아닌가? TDD는 이에 더불어 자동화된 테스트를 보유함으로써 리팩토링과 동전의 양면을 이룬다. 테스트될 수 있기 때문에 바꿀 수 있다.
나는 코드의 한 부분에서나마 이것들을 모두 지킬 강인한 의지가 있다면 충분히 경청할만한 방법이라고 생각한다. 그러나 당연히 만능이 아니다. 나는 그 한계를 내적인 것과 외적인 것으로 구분한다.
내적인 것은 TDD 자체가 가진 한계다. TDD는 함수, 프로시져, 메소드 단위에서는 좋은 디자인을 강요한다. 그러나 어떤 메소드가 어느 클래스에 속해야 하는지, 그 관계가 정확한 메타포를 형성하는지를 생각하도록 강제하지는 않는다. Car 클래스에서 drive()를 가져도 되는가? Driver클래스는 따로 만들어서 drive()는 Driver가 하는 것이 나을 것이다. 그러나 drive()의 테스트를 작성하는 순간, 그 사람은 drive()의 인풋과 아웃풋에 대해서 고민하겠지만 drive()가 Car에 속하지 않도록 강제하는 단계가 뚜렸하지는 않다고 본다.
외적인 것은 외부적인 상황 때문에 마주치는 한계이다. 대표적으로 GUI 프로그래밍의 경우 현재까지 사용자 인풋을 자동화하는 뾰족한 방법이 나왔다고는 보기 힘들다. 쓰레드 safety 문제도 TDD가 적절한 해법이 되기 힘들다고 보는데, 사실 이 외적인 한계들은 내적인 한계와 완전히 무관하지는 않다. 하나의 메소드가 synchronous 하게 인풋을 아웃풋으로 전환시키는 상황을 벗어나는 테스트는 본질이 아니기 때문이다.
이러한 "쓰면 되레 손해인 영역들"을 경험으로 확인해 나가고 쌓이는 것이 결국 TDD로 이득 보는 것을 가능하게 할 것 같다.
테스트를 먼저 작성
켄트 백의 책이 던진 충격 중에 가장 도발적이고 강력한 것이 이것이 아니었을까 생각한다.
"아직 아무 코드도 없는 상태에서, 실패할 테스트를 먼저 작성한다."
이것이 가지는 부수적인 효과는 다음과 같은 것들이라고 생각한다.
1. 코딩에 들어가기 전, 어떤 인터페이스, 다시 말해서 어떤 인풋과 어떤 아웃풋을 가져야 할지를 먼저 생각하고 코딩에 들어가게 한다. 다시 말해서 내부 구현이 아니라 behavior에 의존하는 코드를 짜도록 강요한다.
2. 매 다음 단계마다 테스트가 작성되므로 테스트가 각 '단위별'로 만들어지게 된다.
3. 2의 여파로 각 단위가 고립되서 테스트되어야 하고 결과적으로 디커플링이 중요한 문제가 된다. 나는 mock을 이용하는 테스트가 멋지다고 생각한다.
이것들은 모두 좋은 디자인을 강제하는 방법으로 보였었다. 어떤 면에서는 전통적이라고 볼 수 있는 부분들도 있다. 나는 C를 A book on C, The C Programming Language 등으로 했었는데, 이 시절의 책들에서 실제 코드를 하기 전에 하도록 권유하는 것은 (테스트 작성은 아니고) 주석 작성이었다. 이것이 결국 인터페이스를 먼저 생각하라, 구현보다 인터페이스에 의존해라 등의 가르침이 아닌가? TDD는 이에 더불어 자동화된 테스트를 보유함으로써 리팩토링과 동전의 양면을 이룬다. 테스트될 수 있기 때문에 바꿀 수 있다.
나는 코드의 한 부분에서나마 이것들을 모두 지킬 강인한 의지가 있다면 충분히 경청할만한 방법이라고 생각한다. 그러나 당연히 만능이 아니다. 나는 그 한계를 내적인 것과 외적인 것으로 구분한다.
내적인 것은 TDD 자체가 가진 한계다. TDD는 함수, 프로시져, 메소드 단위에서는 좋은 디자인을 강요한다. 그러나 어떤 메소드가 어느 클래스에 속해야 하는지, 그 관계가 정확한 메타포를 형성하는지를 생각하도록 강제하지는 않는다. Car 클래스에서 drive()를 가져도 되는가? Driver클래스는 따로 만들어서 drive()는 Driver가 하는 것이 나을 것이다. 그러나 drive()의 테스트를 작성하는 순간, 그 사람은 drive()의 인풋과 아웃풋에 대해서 고민하겠지만 drive()가 Car에 속하지 않도록 강제하는 단계가 뚜렸하지는 않다고 본다.
외적인 것은 외부적인 상황 때문에 마주치는 한계이다. 대표적으로 GUI 프로그래밍의 경우 현재까지 사용자 인풋을 자동화하는 뾰족한 방법이 나왔다고는 보기 힘들다. 쓰레드 safety 문제도 TDD가 적절한 해법이 되기 힘들다고 보는데, 사실 이 외적인 한계들은 내적인 한계와 완전히 무관하지는 않다. 하나의 메소드가 synchronous 하게 인풋을 아웃풋으로 전환시키는 상황을 벗어나는 테스트는 본질이 아니기 때문이다.
이러한 "쓰면 되레 손해인 영역들"을 경험으로 확인해 나가고 쌓이는 것이 결국 TDD로 이득 보는 것을 가능하게 할 것 같다.
UEFI-GPT 부팅환경에서 페도라가 자동 생성하는 파티션
sda
├─sda1 vfat 8ABA-40EB /boot/efi
├─sda2 ext4 cc7ef53e-c22c-438b-a070-3063822e020a /boot
├─sda3 swap 3e8be53f-78c9-40b7-8f03-603a3b480fc8 [SWAP]
└─sda4 btrfs fedora_isyru-e135 87837358-656a-4cb4-80d7-740e80af83a0 /
Number Start (sector) End (sector) Size Code Name
1 2048 411647 200.0 MiB EF00 EFI System Partition
2 411648 1435647 500.0 MiB 0700
3 1435648 8808447 3.5 GiB 8200
4 8808448 976773119 461.6 GiB 0700
Arch, Gentoo같은 배포판 설치할 때 파티션을 어찌 설치해야 할지 모르는 상황에서 참고하기 위해서 씀.
├─sda1 vfat 8ABA-40EB /boot/efi
├─sda2 ext4 cc7ef53e-c22c-438b-a070-3063822e020a /boot
├─sda3 swap 3e8be53f-78c9-40b7-8f03-603a3b480fc8 [SWAP]
└─sda4 btrfs fedora_isyru-e135 87837358-656a-4cb4-80d7-740e80af83a0 /
Number Start (sector) End (sector) Size Code Name
1 2048 411647 200.0 MiB EF00 EFI System Partition
2 411648 1435647 500.0 MiB 0700
3 1435648 8808447 3.5 GiB 8200
4 8808448 976773119 461.6 GiB 0700
Arch, Gentoo같은 배포판 설치할 때 파티션을 어찌 설치해야 할지 모르는 상황에서 참고하기 위해서 씀.
2014년 4월 13일 일요일
emacs에서 git라면 역시 magit!!
emacs에서 git 사용은 당분간 magit로 쓰는 걸로 결정.
정확한 상황은 모르지만 emacs의 vc인터페이스가 기본적으로 git의
working directory - stage - commit
구조에 딱 들어맞지 않게 생겨 있는 게 아닌가 하는 생각이 들고, vc-git나 emacs-git나 이 문제에 있어서는 매한가지인 것으로 보임.
옵션 뒤지면 다 나올 수도 있겠으나, 뒤져야 나오는 건 그 자체로 문제.
그래서 당분간 magit를 쓰기로 했습니다. 우분투, 페도라 두 배포판 모두에서 제공됩니다.
정확한 상황은 모르지만 emacs의 vc인터페이스가 기본적으로 git의
working directory - stage - commit
구조에 딱 들어맞지 않게 생겨 있는 게 아닌가 하는 생각이 들고, vc-git나 emacs-git나 이 문제에 있어서는 매한가지인 것으로 보임.
옵션 뒤지면 다 나올 수도 있겠으나, 뒤져야 나오는 건 그 자체로 문제.
그래서 당분간 magit를 쓰기로 했습니다. 우분투, 페도라 두 배포판 모두에서 제공됩니다.
2014년 3월 11일 화요일
my .emacs
(require 'color-theme)
(color-theme-initialize)
(color-theme-gnome2)
;;; ~/.emacs 에 다음을 수정 또는 추가하세요
(custom-set-variables
'(default-input-method "korean-hangul390")) ;; 세벌식 390
go-mode 설치 파트는 일단 패스
2014년 2월 20일 목요일
피드 구독하기:
글 (Atom)