🎥 AI 시대 옵저버빌리티 전략 웨비나 | 무료 다시보기 (~4/9)
Top
도입문의
테크
2026-06-17

Java 디컴파일이 가능한 이유와 JD-GUI 사용하기

트랜잭션 트레이스를 들여다보다 보면, 가끔 모르는 클래스가 호출 스택에 떠 있을 때가 있습니다. `com.fasterxml.jackson.databind.ser.std.NumberSerializer$Long` 같은 이름이 나타나면, "이게 왜 여기서 응답 시간 200ms를 잡아먹고 있지?" 하고 잠깐 멈추게 됩니다.

이런 순간 가장 확실한 답은 그 클래스를 직접 들여다보는 것입니다. 다행히 Java는 디컴파일이 가능한 언어라, 소스가 없어도 라이브러리 안에서 무슨 일이 일어나는지 확인할 수 있습니다.

이 글은 Java가 왜 디컴파일이 되는지, JD-GUI를 어떻게 쓰는지, 그리고 운영 중 어떤 순간에 이 도구가 결정적인지를 실습과 함께 정리합니다.

Java가 디컴파일 가능한 이유

Java는 `.java` 파일을 컴파일러로 바이트코드(`.class`)로 바꿔 실행합니다. 이 바이트코드는 특정 CPU나 OS가 아니라 JVM이 읽는 중간 형태라, 구조가 잘 보존돼 있습니다. 그래서 `.class` 파일을 거꾸로 돌려 다시 `.java` 형태에 가깝게 복원하는 일이 가능합니다. 이 과정이 디컴파일(역컴파일)입니다.

운영자 입장에서 중요한 점은 하나입니다. 소스 코드를 받지 못한 라이브러리라도, JAR만 있으면 내부 동작을 직접 확인할 수 있다는 것입니다. 운영 환경에서는 소스를 열어 볼 수 없는 상황이 더 많기 때문에, 디컴파일이 사실상 유일한 확인 수단이 되는 경우가 잦습니다.

JD-GUI로 `.class` 들여다보기

JD-GUI는 가장 널리 쓰이는 GUI 기반 Java 디컴파일러입니다. Windows, Linux, macOS를 모두 지원하고, 내려받아 바로 실행할 수 있어 진입 장벽이 낮습니다.

JD-GUI 공식 다운로드

기본 흐름은 단순합니다.

1. JD-GUI 실행

2. `파일 → JAR 열기`로 분석할 라이브러리 선택

3. 패키지 트리에서 클래스 클릭

4. 디컴파일된 Java 코드 확인

소스가 없을 때 IDE에서 보이는 화면

특정 클래스가 어떻게 동작하는지 궁금할 때, IDE에서 선언부로 바로 이동할 수 있습니다. Eclipse는 `F3`, IntelliJ는 `Cmd + B`(Windows는 `Ctrl + B`)입니다. 내가 작성한 `.java`라면 소스로 이동하지만, 소스가 없는 라이브러리라면 아래처럼 바이트코드만 표시됩니다.

IDE에서 소스를 찾지 못해 클래스의 바이트코드만 표시된 화면
소스가 없으면 바이트코드가 그대로 노출됩니다. 사람이 읽고 동작을 파악하기는 어렵습니다.


JD-GUI로 열면 읽을 수 있는 코드로

같은 `.class`를 JD-GUI로 열면 Java 코드 형태로 복원돼 보입니다. 예를 들어 어떤 `Print` 클래스의 `print` 메서드가 `"HelloWorld"`와 현재 시각을 함께 출력하도록 작성됐다는 사실을 바로 확인할 수 있습니다. 더 깊이 들어가고 싶다면 `System.out.println` 내부까지 연쇄적으로 따라 내려가는 것도 가능합니다.

JD-GUI로 .class 파일을 열어 Java 소스 형태로 복원해 보여 주는 화면
JD-GUI는 패키지 구조와 클래스 관계까지 함께 보여 줘, 라이브러리 동작을 빠르게 파악할 수 있습니다.

JAR 파일을 통째로 열기

개별 `.class`뿐 아니라 JAR 파일을 통째로 열 수도 있습니다. JAR을 열면 내부 클래스들이 패키지 단위로 디컴파일되어 트리로 표시됩니다. 밑줄이 있는 클래스는 클릭하면 해당 소스로 이동하고(JAR 내부 클래스 간 이동), 디컴파일된 코드를 대상으로 필터링 검색도 할 수 있어 원하는 메서드를 빠르게 찾을 수 있습니다.

JAR 파일을 열어 내부 클래스가 패키지 단위로 디컴파일되어 트리로 표시된 JD-GUI 화면
JAR만 있으면 내부 코드를 패키지 구조 그대로 훑어볼 수 있습니다.

운영에서 디컴파일이 필요한 3가지 순간

디컴파일을 알아두면 좋은 이유는 개발이 아니라 장애 상황에서의 속도입니다. 다음 세 장면에서 특히 그렇습니다.

1. 트레이스에 모르는 라이브러리 클래스가 떴을 때

트랜잭션 트레이스를 따라가다 보면 내 코드가 아니라 라이브러리 메서드가 병목으로 잡히는 경우가 있습니다. 메서드 이름만으로 동작을 추측하기 어렵다면, 해당 JAR을 JD-GUI로 열어 메서드 내부를 직접 확인하는 것이 가장 빠릅니다. 락을 잡고 있는지, 내부에서 외부 호출이 일어나는지 확인하면 다음 조치로 바로 이어집니다.

2. CVE 발생 시 영향 범위 확인

취약점(CVE)이 공개됐을 때 버전 정보만으로 영향 여부를 단정하기 어려운 경우가 있습니다. 특정 메서드 경로에서만 취약하다면, 디컴파일로 실제 코드 흐름을 확인하는 편이 가장 정확합니다. 문서만 보고 안전하다고 판단했다가 실제로는 취약 경로를 타고 있었던 사례도 드물지 않습니다.

3. 서드파티 SDK 동작 검증

결제나 인증 같은 외부 SDK가 문서와 다르게 동작할 때, 설명서만으로는 답이 안 나오는 경우가 많습니다. 이럴 때 디컴파일로 실제 구현을 들여다보면 예상과 실제의 차이를 바로 좁힐 수 있습니다.

난독화된 코드라는 한계

변수명과 클래스명이 a, b, c처럼 의미 없는 문자열로 치환된 난독화 코드를 디컴파일한 화면
난독화된 JAR도 디컴파일 자체는 됩니다. 다만 읽어서 이해하기는 훨씬 어렵습니다.

소스 공개를 막으려고 난독화를 적용한 코드도 디컴파일은 됩니다. 하지만 클래스명과 변수명이 `a`, `b`, `c` 같은 의미 없는 문자열로 치환돼 있어 흐름은 보여도 의도를 읽기는 어렵습니다.

참고로 사람이 일부러 읽기 어렵게 짠 코드의 극단적인 예로 국제 난독화 C 코드 대회(IOCCC) 출품작들이 있습니다. 실제로 컴파일되고 동작하지만 사람이 해석하기는 거의 불가능한 코드들입니다. 난독화된 디컴파일 결과도 이와 비슷한 막막함을 줍니다.

정리하면 이렇습니다.

> 디컴파일은 가능하지만, 항상 이해 가능한 것은 아니다.

역컴파일한 소스를 다시 컴파일할 때 주의점

디컴파일한 코드를 다시 컴파일하려는 경우라면, 컴파일 당시 참조하던 라이브러리 의존성을 반드시 함께 확인해야 합니다. 클래스 코드를 거의 그대로 복원했더라도 필요한 의존성이 빠지면 컴파일 오류가 납니다.

JD-GUI 사용 시 알아둘 점

- Java 8 람다 표현식 등 일부 최신 문법은 정확히 복원되지 않을 수 있습니다.

- 난독화된 코드의 가독성은 기대하기 어렵습니다.

- 디컴파일 결과가 원본과 100% 동일하지는 않습니다.

- 다시 컴파일하려면 원본 의존성이 필요합니다.

- 개발 중 자주 역컴파일해야 한다면 standalone 도구보다 IDE 디컴파일 플러그인이 더 편할 수 있습니다.

마치며

운영 환경에서 디컴파일은 매일 쓰는 도구는 아닙니다. 하지만 한 번 필요한 순간에는 다른 방법으로 대체하기 어렵습니다. 트레이스에서 모르는 클래스가 잡혔을 때 그 코드가 실제로 무엇을 하는지 빠르게 확인할 수 있으면, 문제 해결 속도가 크게 달라집니다.

도구의 구조와 동작을 이해하는 데는 매우 유용하지만, 디컴파일로 들여다본 다른 개발자의 코드를 무분별하게 가져다 쓰는 것은 지양해야 합니다.

트레이스에서 잡힌 느린 라이브러리 호출을 메서드 단위로 추적하고 싶다면, WhaTap APM으로 트랜잭션 흐름을 직접 확인해 볼 수 있습니다.

 👉 와탭 도입 문의하기

와탭 모니터링을 무료로 체험해보세요!