본문 바로가기
  • 오늘도 한걸음. 수고많았어요.^^
  • 조금씩 꾸준히 오래 가자.ㅎ
IT기술/JAVA

소스 분석하기

by 미노드 2016. 2. 11.

만들어진 자바 프로젝트를 분석하여 내 것으로 만들고 싶다.

그러나 난 자바 개발자가 아닌 관리자로써 프로그램을 이해하고, 필요한 경우 소스코드를 수정하고 싶다.

그러나 자바를 이용해본 경험이 많이 없으며 자바를 하고싶지도 않다.

 

때문에 속성과정으로 원하는 부분을 빠르게 찾을 수 있도록 소스를 분석하는 방법을 익혀야 한다.

난 JAVA로 무언갈 만드는 게 아니라 수정하는 업무를 주로 하기 때문이다.

 

분석을 잘하려면 자바에 대한 많은 경험이 최고라고 하는데... 그걸 누가 모르나?

속성으로 빠르게 하고싶으니까 이런 글을 적는거지.

 

먼저 분석하면서 알아두면 좋은점들을 찾아봤다.

 

1. 관련 지식/정보의 습득과 직관의 획득

 어떤 일을 하기위해서 관련 지식은 반드시 필요하다.

 모르는 상태에서 일을 할 수 없기 때문이다.

 그러나 업무에서 필요료 하는 지식에 대한 모든 정보를 알 필요는 없다.

 원하는 부분에 관련된 지식들만 찾아서 습득한다면 자세히는 모르지만 어느정도는 소스가 보일 것이다.

 여기서 중요한 것이 직관이다.

 "척 보면 아는", "여기가 맞는것 같다" 라는 직관과 어느정도 아는 지식으로 문제가 생긴 부분, 원하는 부분을 찾아야 한다.

 

 그렇다면 직관은 어떻게 획득할까?

 아쉽게도 직관만은 속성이 불가능하다.

 Java 뿐만 아니라 프로그래밍 관련 소스 제작경험이 많으면 많을 수록 능력이 향상된다.

 그러므로 "다양한 경험"을 겪어가면서 힘들더라도 계속 분석에 시도하다보면 어느순간 원하는 항목이 보일 것이다.

 

2. 조급해하지 말 것

 많은 사람들이 소스분석은 소스 제작보다 어렵다고 한다.

 특히 소스의 량이 많으면 많을 수록... 하루 이틀 밤샘으로 끝낼 분량이 아닌 이상 느긋한 마음을 가져야한다.

 개발자들은 하루에 코드 50줄정도 하면 평타는 치는거라고 했다.(복붙 제외)

 하루에 40~50줄 정도 분석완료 했다면 잘한 것이니 느리다고 재촉하지 말자.

 

3. 스타일을 파악하자

 사람마다 코딩하는 스타일이 다르기 때문에 변수나 함수작명법, 소스파일을 에디팅하는 스타일, Indenting같은 기본적인 부분부터 typedef, 매크로, 유틸리티 함수, 에러처리, 헤더파일, 루프 스타일, if문의 조건문 스타일, 메소드별 공동 양식 등등 그 사람의 스타일을 이해하면 소스를 빠르게 분석할 수 있다.

 

4. 소스의 목적을 이해하라

 소스가 무엇을 위해 존재하는지 이해하지 못한다면 소스를 분석한 후에 무엇에 쓸 것인가?

 분석 하기 전 주석, 파일명, 함수명, 호출위치 등 에서 최대한 정보를 얻어내어 "이 소스는 이런 일을 하는구나..."를 예상하고 분석해야 합니다.

 

5. 구조를 파악할 것

 아무리 덩치 큰 소스라고 해도 구조가 있다.

 그 구조를 파악하여 분석한다면 한줄 한줄의 의미를 파악하기 쉬울 것이다.

또한 유틸리티 모듈인지, UI모듈인지, 데이터 모듈인지 또 서로 의존적인 것 끼리 묶어서 그룹화한다.

그렇다면 보기 더 쉬울 것이다.

 

6. 자료구조를 파악할 것

 함수는 어짜피 블랙박스이다.

 한줄 한줄 코드의 분석 보다 함수 단위로 '이 함수는 이런 일을 한다' 정도만 이해해도 좋다.

 자세한 주석도 없는 상황에서 변수명과 데이터 타입만 가지고 자료구조를 정확히 파악하기는 힘들다.

 그러므로 변수의 형식, 어디서 끌어온 메소드, 객체인지 철저하게 들여다보며 들어가야한다.

 

7. 더미파일로 실험하기

 이해가 잘 안가거나 실험해보고 싶은 소스들은

 새로운 프로젝트를 열고 모듈별로 복사 붙이기를 통해 테스트 해보자.

 이를 통해 메소드나 소스의 사용방식을 이해하고 본문에 적용할 수 있다.

 

이들을 활용한다면 소스 분석이 막히더라도 쉽게 풀어나갈 수 있으며

작업속도도 빨라질 것이다.

 

그리고 분석기법에 대해 조사해봤다.

 

1. 중단점_ Breakpoint들과 단계별 실행

중단점을 설정하고 실행 코드를 추적하는 방법이다.

   장점

 - 설정하고 사용하기 매우 단순함, 디버거를 실행하기만 하면 되기 때문

 - 어느 부분에서 실행을 멈출지 결정할 수 있다.(선택적 분석)

 - 코드를 수정하지 않고도 수행 가능하다.

   단점

 - 코드를 한 단계씩 추적하는 것이기에 오래 걸림

 - 더 깊이 들어가야 하는지 얕게 들어가야하는지 계속 시도해야하므로 실용적이지 않음

 - 이벤트 핸들러를 디버깅 할 때 어떤 메소드가 호출될지 모른다면 메소스들에 중단점을 설정해야 한다.

 

 이 방법은 어느 부분에서 멈춰야 하는지 알았을 때 또는 특정 위치의 자세한 정보(전달 인수, 지역 변수 등)가 필요할 때 사용하면 좋다.

 

자신의 코드를 디버깅할때 사용하면 좋으나 다른 코드의 실행 추적에서는 사용하더라도 이해하기 어려울 수 있다.

 

2. 디버그 메세지 사용하기

 System.out.print 명령을 사용하여 콘솔로 메세지를 출력하는 것이다.

 로깅(logging)을 사용해도 상관없다.

   장점

 - 원하는 부분을 정확히 선택 가능

 - 실행이 보다 빠르며, 관심있는 정보를 정확히 확인할 수 있다.

   단점

 - print문으로 출력되게끔 소스를 변경해야 하는 경우가 있다.

 - 어느 부분을 살펴보아야 하는지 확실히 모를 경우 여러 메소드에 디버그 메세지를 삽입해야한다.

 - 코드가 지저분해지고 후에 디버그 메세지들을 제거하지 않을 경우 실행시 부하가 생긴다.

 

 이 방법은 코드를 가지고 있으며 어디를 살펴보아야 할 지에 대해 좋은 생각이 있는 경우 시도해보면 좋다.

 복잡한 흐름이 실행되는 중에 어떤 이벤트가 발생하는지 이해하는데 효율성이 높아 실용적이다.

팁: 지금 실행중인 메소드를 출력해주는  제너릭 메소드를 만드는 것은 쉽다. 내가 즐겨쓰는 가장 좋은 방법은 스텍 트레이스를 얻는 것이다. 호출 스택 중 마지막 엔트리가 디버그 메세지 메소드가 될 것이다. 그 엔트리 전의 것들은 그것을 호출하는 메소드들이 된다. 스택 트레이스를 얻는 방법은 다음과 같다.

JDK 5.0 이후: Thread.currentThread().getStackTrace()를 사용한다.

이전 (JDK 1.4): 새로운 익셉션 인스턴스를 만들고 이 새 인스턴스에서 getStackTrace를 사용해서 호출 스택을 얻는다. 이 인스턴스를 throw 할 필요는 없다..

 

3. 동적 프록시(Dynamic Proxy)

 동적 프록시는 자바의 특별한 기능으로 개발자가 프록시 클래스를 도입해 기존 클래스 앞에 놓고 기존 인터페이스로 들어오는 호출들을 가로챌 수 있게 해준다. 호출을 가로채고 나면 필요한 모든 정보가 포함된 적절한 디버그 메세지를 출력할 수 있을 것이다.

   장점

 - 디버그 메세지로 코드를 어지럽힐 필요가 없으며, 디버그 메세지를 한곳에 모아놓을 수 있다.

 - 작업이 끝나면 간단히 한번에 제거할 수 있어 편리하다.

 

   단점

 - 인터페이스를 통해서만 동작한다. 퍼블릭 메소드이건 아니건 인터페이스에 정의 되지 않으면 사용할 수 없다.

 - 선택적으로 메세지를 출력할 수 없다.

 

 이벤트 핸들러에서 최고의 해법이다. 이벤트 핸들러들을 이해라려면 가장 단순하면서도 빠른 방법이다.

 

팁:Sun의 사이트에서 제너릭 동적 플락스 코드를 찾을 수 있다. 이것은 대부분 좋지만 한가지 작은 제약이 있는데 프록시로 감싸게 될 인스턴스의 클래스가 직접 구현했던 인터페이스들만을 임플리먼트 한다. 그 조상들의 어떤 인터페이스도 임플리먼트하지 않는다.  이 문제는 조상들의 목록을 따라가며 모든 인터페이스들을 뽑아내는 것으로 쉽게 수정할 수 있다. 앞으로 올릴 포스트들 중 하나에서 좀 더 나은 방법을 제시하겠다.

 

 

4. 실행 시 프로파일러

 프로파일러는 특별한 JVM 훅으로 모든 호출을 추적하는 강력한 도구이다. 좌우간, 이것은 왠지 반 인치짜리 못에 10 파운드짜리 해머를 쓰는 것 같다.

   장점

 - 코드 변경이 필요없다.

 - 소스코드가 없어도 모든 실행코드를 추적할 수 있다.

 - 대부분의 도구에 관심 밖의 데이터를 선별해서 보는 기능이 있다.(JDK 호출, getter, setter 등)

   단점

 - 대부분의 좋은 도구들이 비싸다

 - 설치하고 사용법을 배우는데 시간이 필요하다

 - 프로파일링은 시스템 성능을 떨어트릴 것이다. 때문에 프로파일러가 정보를 수집하는동안 실행하는 것은 좋지 않다.

 - 대부분 성능 측정에 맞추어진 기능들이므로 실제 수행 시간에 따른 실행 경로는 무시한다.

 

 매우 특별한 동작에 대한 완전한 그림을 원할 때 사용하면 좋다.

 

이 작업을 위한 최고의 무료 오픈소스 도구는 Eclipse Testing and Performance Tools Platform(TPTP)이다.

이것은 어떻게 실행되는지 이해하기에 가장 좋은 방법 중 하나인 sequence diagram을 제공한다.

다만 맥에서는 호환되지 않는다.

 

 5. Aspects

  Aspects Oriented Programming (AOP)는 단순하지 않은 아이디어이다. Aspects의 개념은 빼고 단순히 결론만 한마디로 말한다면, '빠르고 쉽게 코드의 실행을 가로채는 방법'이다. 기존의 소스코드의 변경 없이 메소드, 생성자, 필드 접근등의 주위에 후크를 선별적으로 걸 수 있다.

 이 후크안에서 디버그 메세지를 출력 할 수 있다.

   장점

 - 매우 빠르고 쉽게 설정할 수 있다.

 - 원하는 클래스나 메소드만을 추적 할 수 있다. (선별적)

 - 인수를 포함한 모든 정보를 제공한다.

 - 소스코드를 변경하지 않으며 작업이 끝난 후 제거하기도 매우 쉽다.

 - 성능이 좋아 장시간 실행에도 사용 할 수 있다.

   단점

 - AOP에 대한 기본적인 이해를 필요로 한다.

 - 복잡한 필터를 추가하기 위해 코드를 다시 구축해야 한다.

 - JAR 패키지에 있는 코드를 추적하려 할 때 문제가 될 수 있다.

 

그러므로 다시 구축할 수 있는 실행 코드를 추적하려고 할 때 사용하면 유용하다.

 

 AOP는 아직까지는 주로 사용하지 않는다. 품질이나 사양에 대한 논쟁을 차지하고 대부분의 개발자들은 한시간 안에 AOP 기반의 로깅을 설정해서 실행할 수 있다.

 이클립스 사용자라면 Aspect Java Development Tools(AJDT)를 추천한다.

 

5가지 기법들을 나열해 봤으며 정리하자면 두가지 다른 경우로 나눌 수 있다.

 ⓐ 실행 코드의 짧은 부분을 완벽하게 추적할 것이냐

 ⓑ 주어진 클래스나 계층으로 들어가거나 여기서 발생하는 이벤트를 추적할 것이냐

 

 ⓐ 실행 코드의 짧은 부분을 완벽하게 추적할 것이냐

  1. 이 경우는 프로파일러가 가장 단순한 방법이다. 프로파일러를 작동할 줄 알고 군수창고에 운영할 수 있게 준비된 것 하나를 가지고 있어야 한다.

  2. 다음 선택은 AOP가 될 수 있다. 이것이 두번째 선택인 이유는 JAR 패키지로 작업하기 쉽기 때문이다.

  3. 디버그 메세지를 사용하라.

 

 ⓑ 주어진 클래스나 계층으로 들어가거나 여기서 발생하는 이벤트를 추적할 것이냐

  1. AOP를 사용하여 관심있는 호출만 선별해서 잡아내자.

  2. 동적 프록시를 사용. 디버그 메세지를 사용하는 것과 유사하지만 보다 멋진 해법이다. AOP가 처음에는 복잡할 수 있지만 오랫동안 실행하는 경우에는 AOP가 낳다.

  3. 디버그 메세지를 사용하라.

  4. 중단점(Break Point)를 사용하라.

 

위의 알아두면 좋은 점들과 기법들을 활용하여 소스 분석을 하더라도 쉽게 할 수 있기를 바란다.