취미로 개발하기

많은 개발자의 로망중에 하나가 아마도 취미로 개발하기가 아닐까 생각된다?
아닌가? 나만 그런가?..

그러고 보니 나도 꾀나 오래전부터 프로그래밍을 해온거 같다.
그때가 아마 초등학교 5학년 이었던가?
그 시절은 윈도우즈 3.0이 막 지나던 시절.. 로터스1,2,3을 컴터학원에서 가르치던 시절..
여튼 그 시절부터 GW-BASIC을 만지작 거렸고, 과학동아 였던가?
어떤 잡지를 사면 부록으로 딸려오는 프로그래밍 자습서 같은게 있었다.
그 부록 맨 뒷장에 항상 GW-BASIC 프로그래밍 예제가 있었고, 난 코드들을 따라 해보며 희열을 느꼈다.

그러다가 내가 한계에 부딪친적이 한번 있었는데, 아직도 기억난다.
소방차 그리기 예제였다. 당시 GW-BASIC 의 허접한 그리기 라이브러리가 있었는데..
어쩐일인지,.. 내 컴터에서는 그대로 코드를 따라쳤껀만 소방차는 나오지 않았다.
그 예제코드에 오타가 있지 않았나? 라는 의심은 한번도 해본적이 없다.
그냥 내컴터에서는 안되는건가? 지원 안해주는거야? 그런거야? 꾸진 망할 컴퓨터… 이랬다.

그리고 당시 유행하던 종이책 게임이 있었는데.. 일종의 머그게임이었다.
텍스트를 따라 읽어가면,.. 선택지가 주어지고, 그 선택에 따라 난 또 다른 미션의 선택을 하게 되는
그런 게임이다. 이 게임에 착안해서 베이직으로 머그게임도 만들었던 기억이난다.
사실 그건 프로그래밍이라기 보다는 순전히 소설.. 글짓기에 가까웠다.

여튼 나의 유년시절 프로그래밍은 그랬다.. 당시엔 지금의 나의 모습을 상상할수조차 없었다.
난 프로그래머가 장래희망이 아니었기 때문에… 난 과학자가 꿈이었지..아마..ㅋㅋㅋ

그리고 대학교 4학년때였던가? 그랬다. 학교 다니면서 수많은 프로그램을 만들고,..
공모전 출품도 하고 왕성하게 쓰레기 코드를 작성하던 시절..
문득 그런 생각이 들었다.. “이딴거 만들어서 뭐에 쓰지?”
이런 생각이 스치고 지나자마자 결심을 하나 했다.
“내가 쓰지 않는건 말들지 말자!”

그렇게 시작됐다.. 취미로 개발하기..
내가 필요한걸 만들기 시작했다. 남들이 안 만들어주니까..
그냥 내가 만들었다. 남들에게 보여주려고 한것도 아니고 순전히 나혼자만 쓰려고 만들기 시작했다.
나만 만족하면 그만이고.. 약간 버그는 내가 쓰는데 아무런 문제가 되지 않는다.

그렇게 해서 탄생한것들이
미투머니, 미투알람, JSTools(자바스크립트 지원도구), LiveXE RSS 리더 모듈
그리고 몇개의 홈페이지… 등등..

이중에서 몇개는 오픈소스화 시켰고,.. 나름 서너명의 유저도 확보했다.
혼자 쓸려고 만들었지만 나름 잘 써주는 유저가 생기다보니.. 이거저거 손이 많이 가는것도 사실이다.
그래도 나름 나와 비슷한 불편함을 겪는 사람들이 있다는 것이 많은 위로와 응원이 된다.

그러면서 중2 담임선생님이 늘 강조하시던 말씀이 떠올랐다.
“배워서 남줘라”
그리고 얼마전에 TEDxSeoul 을 다녀오면서 더욱 확고해졌다.
“내가 아는것을 공유하자”

그래서 이런 생각들을 정리하며 글을 쓰기로 맘을 먹었다.
글쓰기 주제는 “취미로 개발하기”

오늘의 그 첫 시작이다.  앞으로 계속해서 연재할꺼다.
기대하시라~!!
오늘은 이만 끝~!!

Kill Process: Windows 에서 살아있는 포트 찾아서 강제로 죽이기

간혹 이클립스로 개발하다보면 어쩔수없이 강제 종료를 하게되는데, 이때 이클립스 플러그인으로 톰캣을 연동해 사용하다 강제 종료 했다면,톰캣 포트가 죽지않아 강제로 죽여야 하는 상황에 놓이게 된다.

유닉스나 리눅스면 netstat 또는 ps 와 같은 명령어로 PID를 찾고 kill 명령어로 해당 PID 를 찾아 죽일수 있으나, Windows는 당췌 알수없어, 매번 구글링을 하게 된다. 이젠 이 구글링 하는것도 귀찮아 정리해야겠다. ㅇㅎㅎ

열린 포트의 PID 확인하기

netstat -ao

위의 명령어를 사용하면, 아래와 같은 형식으로 출력된다.
Proto  Local Address Foreign Addresss  State       PID
TCP 나의-컴퓨터:10000 somewhere.com     LISTENING   666

죽이고 싶은 포트가 10000번 이었는데,.. 10000번 포트를 차지하고 있는 프로세스는 666 이구나!!

프로세스 강제 종료하기

netstat는 리눅스나 윈도우즈나 동일한데 맥에서는 PID가 보이질 않는다. 그래서 맥은 lsof 라는 명령어를 써야하는데 뒤에서 얘기하기로 하고, 아무튼 일단 프로세스를 죽이는 kill 명령어는 윈도우즈용 명령어에는 없다. 윈도우즈 전용은 taskkill 을 사용한다.

taskkill /F /PID 666

위와 같이 강제종료 옵션 /F 와 같이 사용하면, 죽이지 못할 프로세스가 없다!! 유후!!

맥에서 PID 얻기

맥에서는 netstat 명령을 아무리 입력해도 PID가 보이지 않는다. 이럴땐 lsof를 이용하자.

lsof -iTCP

위와 같이 입력하면 아래와 같이 나온다.
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
Finder 347 realrap 5u IPv4 0xc21f4cc42ae141cd 0t0 TCP localhost:49520->localhost:26164
Finder 347 realrap 7u IPv4 0xc21f4cc42ae141cd 0t0 TCP localhost:49520->localhost:26164

아래와 같이 입력하면 포트번호만 필터링 할수도 있다.

lsof -iTCP:35729

자바스크립트 정규식에 대한 고찰..

오늘 미투머니 테스트 코드를 작성하다가 그동안 안개속에 쌓였던, 정규식의 맘을 헤아리게 되어 몇가지 공유합니다. 제가 말하고자 하는 내용은 사실 이 포스트에 다 있어요.

정규식 RegExp Vs String

정규식은 기본적으로 문자열 패턴을 응용한 놈입니다. 그러니 당연히 String 객체와는 뗄레야 뗄수가 없는 놈입니다. 생각해보면 너무나 당연한 사실을 그동안 크게 신경 안쓰고 있다가 이제야 깨달았네요.

정리하면,

정규식은 2가지 형태로 사용할수있습니다.
1. 정규표현식 객체(RegExp)를 사용하는 방법
2. 문자열 객체(String)의 정규식 메소드를 이용하는 방법

메소드만 정리하면 대강 이렇습니다.

RegExp.test() - Boolean 값을 리턴
RegExp.exec() - 매칭된 값을 Array로 리턴
String.split() -
String.match() -
String.replace() -
String.search() -


RegExp.test() Vs RegExp.exec() 의 성능 차이

일반적으로 두 메소드 중에서, test() 메소드의 성능이 더 좋다고 얘기합니다. 왜 그럴까요? 그냥 성능이 좋다고 하니까 그려려니 하나요? 여기엔 이유가 있습니다. 바로 캡쳐링이라는 기능 때문입니다. 캡쳐링은 패턴으로 찾은 놈을 따로 저장하는걸 얘기합니다.

앞의 두 메소드의 리턴값이 하나는 Boolean 이고 하나는 Array 인 점이 바로 여기에 있습니다. 당연히, test 메소드가 리턴값이 Boolean 이므로, 성능이 더 좋겠죠? 네! 맞습니다. 하지만 단순히 리턴값의 사이즈가 작다고 성능이 더 좋다고 얘기할수는 없습니다. 왜냐면, 결국 RegExp 객체가 찾은 패턴을 모두 가지고 있기 때문이죠. 따라서, test() 메소드만을 사용한다고 성능이 좋아지는 것은 아니랍니다. 정확히 얘기하면, 어떻게 패턴을 정의하냐에 따라 달라지겠죠!!

예를 들어보겠습니다.

var str = "테스트 테스트1 테스트2 테스트3 테스트4";
var regx = /테스트\d/;

regx.test(str); // true
regx.exec(str);  // ["테스트1"]

위와 같이 실행하면 test()는 true, exec()는 [“테스트1”] 배열을 반환합니다. 그리고 여기서 하나더! 위에서도 잠깐 언급했지만 두 메소드 모두 결국 RegExp 전역 객체를 사용하게 됩니다.

그러니까, 각 메소드를 실행하고 아래와 같이 RegExp 객체의 $_ 와 $1 값을 확인해보면,
모두 같음을 확인할수있습니다.

RegExp.$_  // 테스트 테스트1 테스트2 테스트3 테스트4"  - 테스트할 문자열
RegExp.$1  // ""  - 캡쳐링된 문자열

이 얘기는 결국, test() 메소드와 exec() 메소드의 내부 구현은 같되, 리턴값만 다름을 의미합니다. 즉, 현재 까진 리턴 사이즈 말고는 성능이 같다는 얘기죠~!!

이제 좀 변형해봅시다. RegExp 객체에 패턴을 저장하기 위한 캡쳐링 옵션을 줘보도록 하지요. regx = /(테스트)/ 로 바꿔서 해봅시다.

var str = "테스트 테스트1 테스트2 테스트3";
var regx = /(테스트\d)/;

regx.test(str);  // true
regx.exec(str);  // ["테스트1", "테스트1"]
RegExp.$1;       //"테스트1"    - test(), exex() 모두 같음.

뭐가 다른지 감이 오나요? exec()가 뱉어내는 리턴값을 유심히 보세요. 아직 모르시겠나요?
캡쳐링은 매핑된 결과를 RegExp 객체가 내부에 저장한다고 앞서 말씀 드렸습니다.
바로 그 캡쳐링된 결과를 RegExp.$1, RegExp.$2 등으로 읽어올수있습니다.
물론 캡쳐링은 하나의 패턴안에서 괄호를 여러번 사용함으로써 저장할수 있습니다.

가령, 이렇게 쓸수도 있다는거죠!!

var str = "테스트 테스트1 테스트2 테스트3";
var regx = /(테스트)\s(테스트\d)/;

regx.exec(str);   // ["테스트 테스트1", "테스트", "테스트1"]

자 이제 exec()의 리턴값의 구성을 이해하시겠죠?

[매칭된 문자열, 캡쳐링된 첫번째값, 캡쳐링된 2번째값, 캡쳐링된 3번째 값, … , 캡쳐링된 N번째 값]

이젠 RegExp에 저장하지 않도록 비캡쳐링(?:xxx) 옵션을 주고 하나 더 해보죠.

var str = "테스트 테스트1 테스트2 테스트3";
var regx = /(?:테스트\d)/;

regx.test(str);  // true
regx.exec(str);  // ["테스트1"]
RegExp.$1;       //  ""    - test(), exex() 모두 같음.

자~~ 이젠 어떤 차이가 있지는 아시나요? 여전히 모르시겠다구요? 비캡쳐링(?:) 옵션을 사용해서 RegExp 에 찾은 패턴값을 저장하지 않았습니다. 그러니까 exec() 리턴값에도 찾은 패턴값이 넘어오지 않게 되죠?

자 그럼, exec() 리턴값의 구성을  한번더 정리해보죠~

[매칭된 문자열, RegExp.$1, RegExp.$2, RegExp.$3, … , RegExp.$N]

마지막으로 그럼,

도대체 성능과는 무슨 관계가 있는거냐?

이 질문에 답할 시간입니다. 이미 눈치채고 계신분이라면, 조용히 닫기버튼을 누르셔도 됩니다. test()와 exec() 메소드의 성능차이는 간단합니다. 이렇게 정리하도록 하죠!

“간단한 패턴 문자가 있는지 없는지를 확인할 땐, 비캡쳐링과 test() 메소드 조합을 사용해라!

되셨나요? 이렇게 얘기 할수도 있습니다.

“test() 메소드를 사용할때는 반드시 비캡쳐링 패턴으로 정의해라!”

그 이유는 앞서 주구장창 설명한 캡쳐링되어 저장된 값 때문입니다.

패턴 플래그 g 에 대한 고찰

이제 오늘 깨달은 것의 하일라이트!! 바로 g 플래그 옵션입니다. 패턴 플래그는 총 3가지가 있죠. i, g, m 뭐 다 아실꺼라 생각하고 각각의 설명은 생략합니다. 모르면 검색해보아요~

이제 g에 일반적으로 알려진 사실을 흔히 쓰는 예제로 보면, 아래와 같습니다.

var str1 = "No pain, No gain!";
var str2= str1.replace(/ain/g, "XXX"); // str2 = "No pXXX, No gXXX!"

즉, “g 옵션을 쓰면, 모든 패턴을 찾게 된다.” Global 의 G 가 바로 그 g 옵션인거죠..

그러면, 얼핏 이런 사실을 알고 있을때, 지금껏 제가 착각해왔던 것은 아래와 같은 겁니다.
앞에서 해왔던 예제를 이어봅니다.

var str = "테스트 테스트1 테스트2 테스트3";
var regx = /(테스트\d)/g;

regx.exec(str);  // ["테스트1", "테스트2", "테스트3"] 일까요???????

g는 글로벌 옵션이니까,.. 저렇게 나와야 하는게 아닐까?.. 하는거죠!! 결론부터 얘기하면 아닙니다! 왜인지는 아시겠죠? 아직도 그 이율 모르겠다면 앞에서 설명한 exec() 메소드의 리턴값 구성을 다시한번 보세요.

그러면 이렇게 해보죠..

var str = "테스트 테스트1 테스트2 테스트3";
var regx = /(테스트\d)/g;

regx.exec(str);  // 1번 실행, ["테스트1", "테스트1"]
regx.exec(str);  // 연속해서 두번 실행, ["테스트2", "테스트2"]
regx.exec(str);  // 연속해서 세번 실행, ["테스트3", "테스트3"]
RegExp.$1;  // "테스트3"

오잉? 저렇게 나올껄 예상 하셨나요? 올~~ 예상했다면, 당신은 규식이를 잘 하는 분입니다.
아직도 모르겠다구요? 글로벌 옵션이긴 하지만, 적어도 제가 기대했던 것과는 사뭇 다릅니다.

자 그럼 g 옵션의 비밀을 정리해보죠.. 정리하면,

패턴 플래그 g 옵션은 연속해서 패턴을 찾을때
다음 패턴 검색을 위해 RegExp 객체에 패턴 검색의 시작 위치를 저장해 둔다.


이해되셨나요? 앞에 예제를 곱씹어보세요~ ^^

test() 메소드와 exec() 메소드는 리턴값이 다른 만큼 분명 그 쓰임도 다릅니다.
따라서, 패턴을 어떻게 정의하느냐에 따라서 RegExp 객체에 얼마나 많은 정보가 캡춰링 되어 저장되는지 그 비효율성도 고민하셔야 합니다. 여기까집니다.

도움이 되셨나요? 도움이 되셨다면, 리플이나 광고라도 한번 눌러주세요.