2008년 7월 1일 화요일

자바에서 바이트 단위길이로 문자열 자르기

모 이런 거 어딘가 있다는 얘길 들었는데, 내가 만들면 더 잘 할 수 있을 거 같아서 한번 만들어 봤다.
하다가 귀찮아져서 겨우 문제 없이 돌아가는 수준에서 멈췄으니 고쳐 쓸 사람은 얼마든지 고쳐서 쓰시길...

용도는 아무래도 바이트 단위의 길이로 정의된 DB 컬럼에 데이타를 넣을 때,
어쩔 수 없이 잘라서 넣어야 하는 경우가 생기는데 이럴 때 이 메소드 한방으로 해결.
바이트 단위라는 부분에서 냄새를 맡았겠지만, 한글을 비롯한 한글자가 2바이트 이상을 사용할 때에 유용하겠다.
오라클 같은 경우 VARCHAR2 길이가 4000바이트 밖에 안 되다 보니 긴 내용을 넣기에 좀 부족한 느낌이 있는데,
그렇다고 LONG이나 CLOB를 쓰는 건 성능도 안 좋고, 다루기도 불편하니
이런 식으로 잘라서 VARCHAR2 컬럼에 넣는 방법을 종종 사용한다고 한다.

첫번째 메소드는 원하는 바이트수만큼만으로 이루어진 String 리턴
getMaxByteString(str, 400) 을 호출하면 str이라는 문자열에서 앞에서 최대 400바이트만큼 잘라서 문자열으로 만들어 리턴
즉, 리턴한 문자열을 보면 str이 400바이트 이상이면 399바이트 아니면 400바이트이겠고, 그보다 짧으면 통째로 리턴할 것이다.
[code java] // str이라는 문자열의 맨앞부터 최대 maxLen만큼의 바이트수로 이루어진 문자열을 잘라서 리턴 public static StringgetMaxByteString(String str, int maxLen) { StringBuilder sb = new StringBuilder(); int curLen = 0; String curChar; for (int i = 0; i < str.length(); i++) { curChar = str.substring(i, i+1); curLen += curChar.getBytes().length; if (curLen > maxLen) break; else sb.append(curChar); } return sb.toString(); } [/code]
두번 째 메소드는 원하는 바이트수(maxLen)만큼만으로 이루어진 String 배열을 str 문자열 끝까지 처리해서 리턴
getMaxByteStringArray(str, 400) 을 호출하면 str이라는 문자열에서 앞에서 최대 400바이트만큼 잘라서 문자열로 만들고
그 후에 문자열이 또 있으면 또 이어서 최대 400바이트만큼 잘라서 문자열을 만들고. 이걸 문자열 끝까지...
만들어진 문자열을 배열로 만들어서 리턴
별도 테이블을 만들어서 각 문자열을 하나의 로우에 저장하도록 구성한다면 이것도 괜찮을 듯.
[code java] // str이라는 문자열의 맨앞부터 끝까지 최대 maxLen만큼의 바이트수로 이루어진 문자열을 // 연속으로 잘라서 String 배열로 리턴 public static String[]getMaxByteStringArray(String str, int maxLen) { return getMaxByteStringArray(str, maxLen, -1); } [/code]
세번째 메소드는 원하는 바이트수(maxLen)만큼만으로 이루어진 String 배열을 최대 maxArrays만큼 길이로 리턴
getMaxByteStringArray(str, 400, 3) 을 호출하면 str이라는 문자열에서 앞에서 최대 400바이트만큼 잘라서 문자열로 만들고
그 후에 문자열이 또 있으면 또 이어서 최대 400바이트만큼 잘라서 문자열을 만들고. 이걸 문자열을 최대 3번까지...
만들어진 문자열을 배열로 만들어서 리턴
한 테이블의 3개의 컬럼에 문자열을 쪼개서 저장한다면 유용할 듯.
[code java] // str이라는 문자열의 맨앞부터 최대 maxLen만큼의 바이트수로 이루어진 문자열을 // 연속으로 잘라서 최대 maxArrays 길이의 배열로 리턴 public static String[]getMaxByteStringArray(String str, int maxLen, int maxArrays) { StringBuilder sb = new StringBuilder(); ArrayList strList = new ArrayList(); int curLen = 0; String curChar; for (int i = 0; i < str.length(); i++) { curChar = str.substring(i, i+1); curLen += curChar.getBytes().length; if (curLen > maxLen) { if (maxArrays == -1 || strList.size() <= maxArrays-2) { strList.add(sb.toString()); sb = new StringBuilder(); curLen = 0; i--; } else break; } else sb.append(curChar); } strList.add(sb.toString()); String[] strArr = new String[strList.size()]; for (int i = 0; i < strList.size(); i++) { strArr[i] = strList.get(i); } return strArr; } [/code]
아래는 위의 메소드를 호출하는 샘플 코드
이 코드 돌려 보고 싶으면, 위의 메소드들과 아래의 main 메소드 합쳐서 한 클래스에 넣고 돌리면 된다.
[code java] public static void main(String[] args) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < 1000; i++) { sb.append("codePointAt을 사용하면 문자의 유니코드값을 구할 수 있잖아요.. 근데.. 문자의 유니코드값을 다시 문자로 변환해"); } Stringstr = sb.toString(); String newStr = getMaxByteString(str, 4000); System.out.println(newStr + ":" + newStr.getBytes().length); System.out.println("-----------------------------------------"); String[] strArr = getMaxByteStringArray(str, 4000); for (int i = 0; i < strArr.length; i++) { System.out.println(strArr[i] + ":" + strArr[i].getBytes().length); } System.out.println("-----------------------------------------"); String[] strArr2 = getMaxByteStringArray(str, 4000, 3); for (int i = 0; i < strArr2.length; i++) { System.out.println(strArr2[i] + ":" + strArr2[i].getBytes().length); } } [/code]
한 글자씩 잘라 붙이기라서 오래 걸릴 줄 알았는데, 꽤 긴 문자열도 0.1초 이내에 잘라 내니 쓸 만하군...

2008년 3월 7일 금요일

ASM - 자바 바이트코드 분석하기

전부터 자바소스코드를 읽어들여 분석할 수 있는 방법이 없을까 하는 생각을 많이 했었다.

그러려면 거의 자바 컴파일러 수준이 되어야 할 것 같아서 엄두를 못 내다가 얼마전에 자바 바이트코드를 다룰 수 있는 라이브러리가 있다는 사실을 알게 되었다. 대표적인 것이 아파치의 BCEL과 지금 살펴 보려고 하는 ASM이다.

근데 이런 게 있다는 걸 알긴 했는데 도무지 사람들이 관심이 없어서인지 쓸만한 참고자료 찾기가 너무 힘들었다.

결국 ASM 사이트에서 이것저것 파일을 다운받아서 보다보니 내가 딱 원하는 기능의 예제까지 찾아낼 수가 있었다.

 

일단, BCEL(Byte Code Engineering Library)과 ASM은 자바소스파일을 컴파일해서 얻은 클래스파일(바이트코드)을 읽어 들여 변경하거나 분석하는 데 사용하는 라이브러리다. 자바소스파일 같은 경우는 코드상에 문제가 있을 수 있으니까 이걸 분석하는 건 문제가 있을 가능성이 있지만, 클래스파일은 자바소스코드상에 오류가 없어야 컴파일을 통해 만들 수 있는 것이므로 클래스파일을 분석하는 것이 보다 신뢰성 있는 분석결과를 얻을 수 있지 않겠는가? (나는 아직 클래스파일을 동적으로 변경하거나 이런 건 별로 관심이 없다 보니...)

여기저기 자료를 찾다 보니 BCEL보다 ASM이 속도가 빠르다고 해서 나는 ASM을 써 보기로 결정했다.

 

1. 라이브러리 준비

http://asm.objectweb.org/download/index.html에 가서 ASM 관련 파일을 다운받는다.

여기서는 asm관련 모든 패키지가 들어 있는 asm-all-3.1.jar를 사용하는데, 그러려면 asm-3.1-bin.zip을 다운받아서 압축을 풀면 그 안의 lib/all에 이 파일이 있다.

이 파일 외에도 다른 가이드나 예제 같은 것이 많으니 필요에 따라 함께 다운받으면 된다.

 

2. 분석프로그램 생성

※ 이 클래스가 컴파일되려면 컴파일할 때 클래스패스에 1에서 받은 asm-all-3.1.jar를 등록시켜야 한다.


[code java] import java.util.List; import org.objectweb.asm.ClassReader; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.MethodNode; public class ClassInfo implements Opcodes { public static void main(final String[] args) throws Exception { ClassReader cr = new ClassReader("test.Test"); ClassNode cn = new ClassNode(); cr.accept(cn, ClassReader.SKIP_CODE); System.out.println("Class Name : " + cn.name + "\n"); System.out.println("Super Class : " + cn.superName + "\n"); System.out.println("Interfaces :"); List interfaces = cn.interfaces; for (int i = 0; i < interfaces.size(); i++) { System.out.println(interfaces.get(i)); } System.out.println("\nMethods :"); List methods = cn.methods; for (int i = 0; i < methods.size(); ++i) { MethodNode method = (MethodNode) methods.get(i); System.out.println(method.name + method.desc); } } } [/code]

ClassInfo.java


 

코드설명 :

9: 분석하려는 클래스의 패캐지를 포함한 full-name을 인자로 ClassReader 객체를 생성한다.

    여기서 읽기 위해서는 분석하려는 클래스가 클래스패스 경로 안에 반드시 있어야 한다.

12: 아마 클래스파일을 파싱하는 작업이 아닐까 생각이 된다.(아직 잘 몰라서 그만...)

14: 클래스 이름 출력

16: 클래스가 상속한 클래스 출력

18~22: 클래스가 구현한 인터페이스 리스트 출력

24~29: 클래스 내의 메소드 정보 출력

 

3. 분석 대상 클래스를 생성 및 컴파일

위의 ClassInfo 클래스가 test.Test라는 클래스를 분석하려고 하는데 그러려면 아래와 같이 미리 Test를 만들어서 컴파일해서 클래스파일이 위의 클래스가 실행하는 클래스패스 안에 들어가 있어야 한다.

[code java] package test; import java.io.Serializable; import java.util.HashMap; public class Test extends HashMap implements Serializable { public static String str; static { str = "abced"; } public Test() { super(); } public static void main(String[] args) throws Exception { String str = "abc.dfd.efef.fjskdl"; System.out.println(str.replace(".", "/")); Test test = new Test(); String msg = "message to be printed"; int count = 5; double d = 1.3; float f = 1.3f; long l = 170343702; HashMap map = new HashMap(); test.printMsg(msg, count, d, f, l, map); } public void printMsg(String msg, int count, double d, float f, long l, HashMap map) { for (int i = 0; i < count; i++) System.out.println(msg); } public String getString(int i, String str) { return "abc"; } } [/code]

Test.java


 

4. 분석 실행 및 결과 확인

 

이렇게 한 다음 ClassInfo 클래스를 실행하면 아래와 같은 결과가 나온다.


[code] Class Name : test/Test Super Class : java/util/HashMap Interfaces : java/io/Serializable Methods : <clinit>()V <init>()V main([Ljava/lang/String;)V printMsg(Ljava/lang/String;IDFJLjav getString(ILjava/lang/String;)Ljava [/code]

클래스나 수퍼클래스, 인터페이스는 무슨 내용인지 바로 알겠는데, 메소드 부분은 바로 안 들어온다.

짧은 지식으로 대강 설명을 드리자면,

9: <clinit>는 클래스의 static 블럭. ()는 인자가 없음. V는 void로 리턴타입이 없다는 뜻

10: <init>는 constructor. 나머지는 위와 동일

11: main은 main 메소드.[Ljava/lang/String;에서 [로 시작하니까 배열이고, L로 시작하니까 자바클래스일 것 같고, V니까 void

      다 합치면 void main(String[]) 이런 형태가 되려나?

12: 인자에서 L로 시작하는 String과 HashMap은 감이 오는데, IDFJ는 뭔가?

        Test.java 소스를 보면서 맞추면 I는 int, D는 double, F는 float, J는 long가 될 것 같네.

       primitive type은 바이트코드에서 한글자로 줄여서 표현하는 모양이다.

13: 맨 마지막이 V가 아니고 Ljava/lang/String; 이니까 String을 리턴하는 메소드로군...

 

 

이렇게 해서 간단하게 클래스파일에서 간단한 정보를 뽑아 보는 방법을 알아 보았다.

여기서 Test.java를 클래스패스에 연결이 영 안 되는 분은 ClassInfo.java의 8라인에 "test.Test" 대신 "java.lang.String"을 입력해서 실행해 보면 String 클래스의 정보가 쭉 나오는 것을 확인할 수 있다.

 

클래스파일을 이용한 분석의 좋은 점은 JSP 파일을 분석할 수도 있다는 것이다.

JSP를 실행하려면 웹컨테이너가 java파일로 변환하고, 이걸 다시 클래스파일로 컴파일해서 실행하는데 JSP를 실행하고 나면 웹컨테이너 디렉토리 어딘가에 클래스파일이 남아 있다. 그러니까 이 경로만 찾아서 적당히 클래스패스로 잡아 주면 JSP도 분석이 가능한 것이다.

참고로 톰캣의 경우는 {Tomcat_Root}/work/Catalina/localhost 디렉토리 아래에 생기니 클래스패스 잘 잡아서 테스트해 보는 것도 재미있을 수 있겠다.

 

사실, 이 정도 정보 외에도 메소드 내의 각 코드 라인별로도 분석이 가능하다.

그런데 이 정도까지 가게 되면 거의 어셈블리 언어 수준까지 읽을 수 있는 능력이 필요하다.

(ASM이 무엇의 약자인지 아무리 해도 못 찾겠는데, 내 생각에는 ASM이 어셈블리의 약자가 아닌가 하는 생각도 든다.)

나도 학교 다닐 때 보던 기억을 더듬어 어느정도 읽어내긴 했지만 그런 걸 모르는 사람은 거의 암호 수준일 것 같다.

모 어려운 것도 있고, 호기심이 발동한 분들이 열심히 공부하길 바라는 마음도 있고, 이걸 보면서 머리 속에 굉장한 아이디어가 떠오른 것도 있고 해서 더 이상 심화된 내용은 다루지 않아야겠다.

 

궁금하면 공부합시다!

2008년 1월 17일 목요일

DB 테이블 복사 유틸

간단하게 어떤 DB에 있는 테이블 내용을 다른 DB에 있는 테이블로 그대로 복사하는 유틸입니다.

일단은 오라클만 되게 만들었는데, 테이블의 스키마, 데이터타입, 컬럼길이만 맞는다면 다른 DB도 가능할 것 같네요. 아직 미확인 ^^;

컴파일한 클래스파일과 실행스크립트를 첨부할까 하다가, 간단히 이클립스에서 돌리시라고 그냥 소스를 올립니다.

뭐, 오류 생기면 알아서 고쳐 쓰시라는 의미도 되겠네요. 일단 이건 잘 돌아갔던 소스입니다.

정 못 고치시겠으면 저한테 메일이나 쪽지 남겨 주시면 해결해 드리겠습니다. 이건 유료일지도.. ㅋㅋ

 

 

장점

 

1. 데이터 내용에 작은따옴표(')가 있어도 옮길 수 있다.

   어떤 방법이든 export 받아서 insert문 만들면 작은따옴표 때문에 안 되더군요. 제가 아는 한도 내에서는... ^^

   그런데, 이건 JDBC 방식에 PreparedStatement로 처리해서 insert문을 안 만드니까 문제가 없어요.

   -> 이건 방법이 있었네요. ^^; 작은따옴표를 연속 2개 사용하면 하나인 걸로 인식해서 DB에 들어가네요.

       예를 들어, sometable 테이블의 somecol 컬럼에 123'456 이라는 값을 넣으려면 아래와 같이 하면 돼요.

       insert into sometable(somecol) values ('123''456');

 

2. LONG 타입도 옮길 수 있다.

   LONG 타입은 덤프 뜰 때 안 된다고 하더군요. 이것도 역시 그냥 들은 내용 ^^

   이 프로그램엔 LONG 타입도 옮길 수 있도록 처리해 놨습니다.

   INTEGER와 LONG 타입 외에는 모두 STRING으로 처리하게 만들었으니까 혹시 에러나면 다른 타입 처리코드를 추가해서 사용하세요.

 

3. DB연결정보만 알면 빠르게 옮길 수 있다.

   아래에 설명할 세팅정보만 넣고 실행하면 export, import 과정없이 즉석에서 옮길 수 있어요.

 

 

환경세팅

 

사용자 삽입 이미지

 

1. allColumns : 테이블의 모든 컬럼을 복사할지 설정한다.

true로 하면 모든 컬럼을, false로 하면 아래의 colArray에 설정한 컬럼만 복사한다.

 

2. colArray : 모든 컬럼을 복사하지 않을 경우, 복사할 컬럼을 정의하는 배열

이 값을 적용시키려면 allColumns를 false로 해야 한다.

 

3. source 테이블 연결정보

source로 시작하는 변수들에는 원본 데이터가 있는 테이블에 관한 정보를 입력한다.

 

4. target 테이블 연결정보

target로 시작하는 변수들에는 복사한 데이터를 insert할 테이블에 관한 정보를 입력한다.

 

 

※ 주의사항

source 테이블과 target 테이블의 스키마, 데이타타입, 컬럼길이는 호환가능해야 합니다.

target에는 무조건 insert를 하기 때문에, 원래 있던 데이터는 전혀 건드리지 않습니다.

JDBC 사용하니까 실행할 때 클래스패스에 DB JDBC 드라이버는 당근 포함되어 있어야겠죠.

예전에 VARCHAR2에 2,000 바이트 넘는 데이터 읽고 쓸 때 getString, setString으로 안 됐는데 지금은 되나 모르겠네요.

혹시 이런 케이스로 안 되는 분 있으시면 알려 주세요.

 

마지막으로,

마음껏 배포 가능합니다. 단, 소스 맨위의 주석은 건드리지 말아 주세요.

 

 

편리하게 이용하시길...

 

2007년 10월 24일 수요일

Server Alive Check

원래 이 포스트에는 AJAX로 웹주소만 체크하는 프로그램만 있었는데,

이번에 자바로 웹주소 뿐만 아니라 JDBC로 오라클(다른 DB는 안 됨)까지 체크하는 프로그램을 만들었습니다.

그래서, 원래 내용은 아래로 밀려나고 자바로 만든 프로그램이 윗자리를 차지하게 되었습니다.

 

-------------------------------------------------------------------------------------------------

 

 

사용자 삽입 이미지

 

svrchk.zip을 받아서 압축을 풀면, 5개의 파일이 나타나는데 그중에서 servercheck.bat를 실행하면 위와 같은 프로그램이 뜹니다.

 

1. 서버추가 : 체크할 서버를 추가합니다.

   - 웹주소는 서버명과 URL만 입력합니다.(URL은 서버의 부하를 줄이기 위해 꼭 떠야 하는 페이지 중에 금방 뜨는 주소로 합니다)

   - 오라클이면 URL에 JDBC 연결에 사용하는 connection-url(SID포함)을 입력하고, ID와 PW칸에 오라클 아이디, 패스워드를 입력합니다.

2. 서버삭제 : 등록된 서버중 선택된 하나를 삭제합니다.

3. ▲ : 선택한 서버를 한칸 위로 올립니다.

4. ▼ : 선택한 서버를 한칸 아래로 내립니다.

5. 체크주기 : '체크시작' 버튼을 클릭하면 체크주기에 입력한 시간주기로 체크합니다.

6. 체크시작 : 웹과 오라클을 체크하기 시작합니다.

7. 체크중지 : 웹과 오라클에 대한 체크를 중지합니다.

 

프로그램을 종료하면 등록되어 있는 서버정보를 server.lst라는 파일에 저장합니다.

오라클 패스워드는 읽기 어렵게(암호화까진 아니고 ^^;) 변환해서 저장합니다.

참, 서버에서 응답이 없으면 삐뽀삐뽀하고 사이렌 소리가 납니다. 끄기 전까지 계속 납니다. ㅋㅋ

 

중요> 여기 올리는 프로그램들은 누구나 자유롭게 사용하시라고 올리는 거니까 자유롭게 사용하세요.

       이게 오라클만 되니까 다른 DB의 경우 추가기능이 필요할지도 모르는데, 그럴 경우 요청해 주세요.

       단, 디컴파일해서 변형을 가하거나 상업적으로 이용하는 것은 저와 미리 상의해 주세요.

 

================================================================================================

 

별로 대단한 것은 아니고

AJAX를 주기적으로 돌려서 주어진 URL 리스트가 정상적으로 응답하는지 확인하는 자바스크립트이다.

여러 WAS에 여러 웹애플리케이션을 관리한다면 유용할 것 같다.

그냥 이 페이지만 웹브라우저에 띄워 놓으면 주기적으로 돌다가 응답이 없을 때 알려 준다.

 

사용자 삽입 이미지
 

 

정상적일 때는 이렇게 모두 OK로 뜨고,

 

 

사용자 삽입 이미지

제대로 응답이 없을 때는 위와 같이 에러 메시지가 뜬다.

 

 

다음은 설정방법...

 

사용자 삽입 이미지

 

1. 체크할 URL을 차례로 써 놓는다. 웹애플리케이션 주소나 JSP나 HTML을 해도 괜찮을 것 같다.

2. 몇 초 주기로 서버를 체크할지 변수를 설정한다. 너무 짧게 하면 서버에 부하가니까 넉넉하게...

 

이렇게 설정을 해서 WAS에 올려 놓고 웹브라우저로 열면 정상적으로 동작한다.

그냥 로컬 파일시스템에 있는 HTML을 그냥 열면 제대로 동작하지 않는다고 하던데...

만들고 보니 쓸 사람은 아주 조금일 거 같지만, 유용하게 쓰시길.

아니면 소스 보면서 AJAX 공부하는 것도 괜찮고.

 

2007년 8월 15일 수요일

클래스 위치 찾아 주는 프로그램

 


WAS에 jar 파일과 class 파일들을 만들어 올려서 사용하다 보면, 서버에서 에러가 나면서 어떤 클래스 파일에 문제가 있다고 메시지가 뜨는 경우가 많다.

그런데 이 클래스가 도무지 어디에 박혀 있는지 찾기가 쉽지 않다.

그냥 클래스파일이면 경로를 뒤지다 보면 나올 수도 있지만 jar 파일 안에라도 들어가 있으면 하나씩 열어 가면서 확인해야 한다.

그래서, jar 파일과 class 파일이 있는 디렉토리만 입력해 주면 포함된 모든 클래스 파일을 뒤져서 해당 클래스 파일을 찾아 주는 프로그램을 만들어 보았다.

본 S/W Architect 카테고리의 이전 포스트 중 '클래스 중복 확인 프로그램'과 기본 로직은 비슷하고 단지 이것은 그냥 필요한 것만 찾아 줄 뿐이다.

대신, 해당 클래스 파일을 모두 찾아 주기 때문에 같은 경로의 클래스 파일이 결과에 여러번 반복되어 나타난다면 중복되어 올라갔다고 볼 수 있으며 이런 것은 되도록 하나만 남도록 하는 것이 찾기 어려운 에러가 발생하는 것을 방지하는 좋은 방법이다.


사용법 :

1. 첨부된 zip 파일의 압축을 푼다.

2. lib.properties에 WAS 동작시 로딩되는 jar 파일 디렉토리(lib)와 class 파일 디렉토리(classes)를 모두 입력한다.

   jar를 포함한 디렉토리는 jar로 시작해서 일련번호를 붙이고, class파일을 포함한 디렉토리는 class로 시작해서 일련번호를 붙인다.

   단, 경로는 절대경로로 입력하며 경로구분자는 반드시 "/"로 해야 한다. "\"일 경우는 바꿔 주어야 한다.(이건 귀차니즘 때문에.. ^^;)

   웹애플리케이션을 개발할 경우, 보통 jar를 포함하는 lib 디렉토리들은 WAS 루트의 lib, server/lib, 웹애플리케이션의 WEB-INF/lib 정도가 되고,

   클래스파일 디렉토리는 웹애플리케이션의 WEB-INF/classes 정도가 된다.

3. runFinder.bat를 실행한다.

   C:\> runFinder [클래스명 전체 또는 일부]

    단, 여기서 클래스명은 대소문자를 구분한다.

4. 생성되는 result.txt 파일을 열어 클래스들의 위치를 확인한다.


프로젝트마다 lib.properties 하나만 세팅해 놓으면, 끝날 때까지 편리하게 사용할 수 있을 것이다.

클래스 중복 확인 프로그램과 형식이 똑같으므로 같은 디렉토리에 놓고 공유해서 사용해도 괜찮다.



이것도 역시, 아무쪼록 많은 도움이 되길...


추가>

이것도 원래 JDK1.5에서만 돌아가서 추가로 1.4로 컴파일한 프로그램을 올립니다.


2007년 8월 12일 일요일

놀라운 발견

오늘 코딩을 하다가 놀라운 사실을 발견했다.

아래와 같은 코드를 디버깅용으로 만들어 넣고 테스트해 보았다.

 

System.out.println("ignore:"+ignore+",characterencoding:"+request.getCharacterEncoding());

 

그런데, 분명히 콘솔에 찍혀야 되는데 안 찍히길래 컴파일이 제대로 된 건가 확인하기 위해 디컴파일러로 소스를 다시 보니 아래와 같은 코드가 보이는 것이다.

 

System.out.println((new StringBuilder("ignore:")).append(ignore).append(",characterencoding:").append(request.getCharacterEncoding()).toString());

 

흠, String concatenation 때문에 하도 문제가 생기니 JDK 1.5부턴 컴파일할 때 아예 이렇게 바이트코드를 만드는 모양이군...

요 몇달동안 String concatenation해야 할 게 많아서 그때마다 StringBuffer에 append하느라고 눈 빠질 뻔 했는데, 이게 언제 바뀐 거야.

바꾸면 나한테 전화라도 미리 해 줬어야 되는 거 아닌감? ㅋㅋ

 

2007년 8월 4일 토요일

웹개발을 위한 Power Tool - Firefox + Firebug

한동안 X-Internet 프로젝트를 하다 보니 순수 웹 개발에 대해서 잘 몰랐었는데

올해 순수 웹 개발을 하다 보니 말로만 듣던 Firefox + Firebug의 기능을 확인하게 되었다.

진작 이런 게 있었으면 웹개발 진짜 쉽게 할 수 있었는데, 왜 이제야 나온 거니? 왜 그런 거니?

 

1. Firebug 설치

Firefox만으로는 안 되고, Firebug라는 플러그인을 추가해야 강력한 무기를 완성하게 되는 것이다.

Firefox의 메뉴에서 "도구 > 부가기능"으로 들어간다.

 

사용자 삽입 이미지

 

이 다이얼로그는 이미 설치되어 있는 것이고, 없다면 아래의 "유용한 확장 기능 찾기"로 들어가 Firebug를 찾아 설치해야 한다.

설치되면 위와 같은 다이얼로그를 볼 수 있다.

설치된 후에 Firebug와 관련된 설정은 "도구 > Firebug"에서 조정할 수 있는데, Firefox를 오직 개발용으로 사용한다면 문제가 없지만 웹서핑용으로도 사용한다면 자신이 디버그를 원하는 주소를 등록하는 것이 좋다. 아무래도 디버깅툴이다 보니 Firebug가 동작하면 사이트가 뜨고 동작하는데 시간이 좀 걸린다.

 

 

2. Inspect Element

나를 반하게 한 기능 중의 하나가 Inspect Element이다. 이건 특정 element의 현재 속성을 HTML로 표현해 준다. 중요한 건 자바스크립트 등으로 변경되거나 추가된 각종 속성을 모두 보여 준다는 것이다. 그러니까, 서버에서 받은 코드가 아닌 현재 웹브라우저가 갖고 있는 실제 내용을 직접 확인할 수 있다는 얘기다.

 

이것은 화면상의 특정 부분에 오른쪽 클릭을 하고 메뉴에서 "Inspect Element"를 선택한다.

 

사용자 삽입 이미지

 

여기서는 맨 위의 플러스 이미지에서 오른쪽 클릭을 했다.

그러면, Firefox 하단에 아래와 같은 내용이 나타난다.

 

사용자 삽입 이미지

 

왼쪽 부분에는 현재 메모리상의 HTML 코드가 나오고, 오른쪽에는 이 element에 적용된 스타일이 표시된다.

HTML 코드에 마우스를 올려 놓으면 그 코드가 화면에서 어떤 부분인지 화면에 하늘색으로 표시가 되니 코드가 실제 화면에서 어떻게 나타나는지 확인하는 데도 유용하다.

 

 

3. 자바스크립트 디버그

이리저리 Firebug를 사용해 보다가 가장 놀란 기능이다. 자바스크립트를 디버그하다니...

먼저 위의 그림에서 Script 탭을 선택하고, 바로 위에서 디버그할 js나 자바스크립트를 포함한 html을 선택하면 아래에 코드가 나타난다.

코드에서 실행을 멈출 설정할 라인을 골라 클릭하면 breakpoint가 표시된다.

여기서 화면의 버튼을 클릭해서 자바스크립트를 실행시키거나, 화면을 다시 로딩하거나 하면 설정해 놓은 breakpoint에서 멈춰서 다음과 같은 화면이 나타난다.

 

사용자 삽입 이미지

 

여기부터는 이클립스에서 디버그를 해 본 사람이라면 편하게 사용할 수 있다.

오른쪽에 변수의 값들을 확인할 수 있고, New watch expression...에서 표현식을 입력하고 중간중간 확인해 볼 수도 있다.

프로그램 진행은 오른쪽 위의 각종 화살표를 누르면서 진행해 볼 수 있다. 단, 이클립스와 모양은 같지만 기능이 다르니 잘 확인해서 눌러야 한다.

 

 

이 포스트에선 내가 가장 유용하다고 생각되는 기능만을 다뤘는데 그 외의 버튼을 눌러보다 보면 정말 강력한 플러그인이라는 걸 느끼게 될 것이다.

단, Firefox는 IE에서 사용하는 비표준 태그나 자바스크립트 함수, ActiveX 등이 동작하지 않으므로 제한이 있다고 할 수도 있는데, 요즘 세상의 흐름이 표준을 준수하는 모든 브라우저를 사용할 수 있도록 사이트를 구성하는 것이기 때문에 이 기회에 이런 흐름을 따르는 것도 좋을 것 같다.