본문 바로가기
Back-end/JAVA & Spring

PMD - Programming Mistake Detector

by 꼬바리 2022. 1. 6.

PMD

Programming Mistake Detector


전자정부 표준 프레임워크

😎 전자정부 표준프레임워크 표준 Inspection 룰셋

PMD를 이용한 Code Inspection 시 기준이 되는 요소는 룰이며, 전자정부 표준프레임워크에서는 PMD가 제공하는 수 많은 룰 중에서 기본이 될 만한 것들을 간추려 전자정부 표준프레임워크 표준 Inspection 룰셋이라는 이름으로 구성하였다.
전자정부 표준프레임워크의 표준 Inspection 룰셋은 다음의 표와 같은 39개의 룰들로 구성된다. 개별 룰에 대한 상세한 설명은 전자정부 표준 inspection 룰셋을 참고합니다.


번호 룰 이름 설명
01 EmptyCatchBlock 내용이 없는 Catch Block이 존재
02 EmptyIfStmt 빈 if 구문의 사용을 피하도록 함
03 EmptyWhileStmt 빈 while 구문이 사용되었음
04 EmptyTryBlock 내용이 없는 try 블록이 존재함
05 EmptyFinallyBlock 내용이 없는 finally 블록이 존재함
06 UnnecessaryConversionTemporary 기본 데이터 타입을 String으로 변환할 때 불필요한 임시 변환 작업을 피하도록 함
07 EmptyStatementNotInLoop 필요없는 문장 (;)이 있음
08 WhileLoopsMustUseBraces 중괄호없이 사용된 while문의 사용은 바람직하지 못한 코딩 습관임
09 AssignmentInOperand 피연산자내에 할당문이 사용됨. 해당 코드를 복잡하고 가독성이 떨어지게 만듬
10 UnnecessaryParentheses 괄호가 없어도 되는 상황에서 불필요한 괄호를 사용할 경우 마치 메소드 호출처럼 보여서 소스 코드의 가독성을 떨어뜨릴 수 있음
11 SimplifyBooleanExpressions boolean 사용 시 불필요한 비교 연산을 피하도록 함
12 SwitchStmtsShouldHaveDefault Switch구문에는 반드시 default label이 있어야 함
13 AvoidReassigningParameters 넘겨받는 메소드 parameter 값을 직접 변경하는 코드 탐지.
14 FinalFieldCouldBeStatic final field를 static으로 변경하면 overhead를 줄일 수 있음
15 EqualsNull null 값과 비교하기 위해 equals 메소드를 사용하였음
16 SimpleDateFormatNeedsLocale SimpleDateFormat 인스턴스를 생성할때 Locale 을 지정하는 것이 바람직함
17 ImmutableField 생성자를 통해 할당된 변수를 Final로 선언하지 않았음
18 AssignmentToNonFinalStatic static 필드의 안전하지않은 사용 가능성이 존재
19 AvoidSynchronizedAtMethodLevel mothod 레벨의 synchronization 보다 block 레벨 synchronization 을 사용하는 것이 바람직함
20 AbstractClassWithoutAbstractMethod Abstract Class내에 Abstract Method가 존재하지 않음
21 UncommentedEmptyMethod 빈 메소드에 빈메소드임을 나타내는 주석을 추가할 것
22 AvoidConstantsInterface Interface는 클래스의 behavior 을 구현하는 데에만 사용해야 함
23 DuplicateImports import문이 중복 선언 되었음
24 ImportFromSamePackage 동일 패키지에 있을 때는 import문을 사용할 필요가 없음
25 SystemPrintln System.out.print 가 사용됨. 전용 로거를 사용할 것을 권장
26 VariableNamingConventions final이 아닌 변수는 밑줄을 포함할 수 없음
27 MisleadingVariableName non-field 이름이 m_ 으로 시작함'
28 AvoidArrayLoops 배열의 값을 루프문을 이용하여 복사하는 것 보다, System.arraycopy() 메소드를 이용하여 복사하는 것이 효율적이며 수행 속도가 빠름
29 UnnecessaryWrapperObjectCreation 불필요한 Wrapper Object가 생성되었음. 탐지된 코드는 삭제하고, 별도의 parse관련 전용 메소드 사용을 권장
30 AvoidThrowingRawExceptionTypes 가공되지 않은 Exception을 throw하는 것은 비추천
31 AvoidThrowingNullPointerException NullPointerException을 throw하는 것은 비추천
32 StringInstantiation 불필요한 String Instance를 생성하는 코드를 탐지. 간단한 형태의 코드로 변경 필요
33 StringToString String 객체에서 toString()함수를 사용하는 것은 불필요함. 해당 코드 제거 필요
34 InefficientStringBuffering StringBuffer 함수내에서 비문자열 연산 이용하여 직접 결합하는 코드 사용을 탐지. append 메소드 사용을 권장
35 InefficientEmptyStringCheck 빈 문자열 확인을 위해 String.trim().length() 을 사용하는 것은 피하도록 함. whitespace/Non-whitespace 확인을 위한 별도의 로직 구현을 권장
36 UselessStringValueOf String 을 append 할 경우, String.valueOf 함수를 사용할 필요 없음
37 UnusedPrivateField 사용되지 않는 Private field의 탐지
38 UnusedPrivateMethod 사용되지 않는 Private Method 선언을 탐지
39 UnusedFormalParameter 메소드 선언 내에사용되지 않는 파라미터를 탐지

😎 PMD 적용대상 Rule Set

총 10개 룰셋이며, 필수인 39개 룰을 적용합니다.


😎 Basic Rules

대부분의 개발자들이 동의하는 규칙: catch 블록들은 비어있어서는 안되고, equals()를 오버라이딩 할 때 마다 hashCode()를 오버라이드한다.

🙄 EmptyCatchBlock

빈 catch문의 사용을 지양한다. 내용이 없는 Catch Block이 존재. Exception 처리가 안될 수 있음.

수정전

  catch (Exception e){
  }

수정후

  catch (Exception e){
    ``catch시의 처리내용을 추가할것``
  }

🙄 EmptyIfStmt

조건에 해당되는 수행문이 없는 if문의 사용을 지양한다.

수정전

  else {
  }

수정후

삭제 또는 반대 조건을 이용하여 처리

🙄 EmptyFinallyBlock

finally block이 비어있음

수정전

  finally {
  }

수정후

빈 블럭 삭제

🙄  EmptyTryBlock

내용이 없는 try 블록이 존재함

수정전

  try{
  }

수정후

삭제

🙄  EmptyWhileStmt

빈 While 구문이 사용되었음.

수정전

  while(true){
  }

수정후

삭제

🙄  EmptyStatementNotInLoop

loop 블럭에서 ";" 만 포함한 구문의 사용을 지양한다. (불필요한 세미콜론은 삭제되어야 함)

수정전

  while(true){
    ;
  }

수정후

삭제

🙄  UnnecessaryConversionTemporary

기본 데이터(primitive type)를 String으로 변환할 때 불필요한 임시 변환 작업을 피하도록 함

수정전

public String convert(int x) {
  // this wastes an object
  String foo = new Integer(x).toString();
  return foo;} 

수정후

public String convert(int x) { 
  // this is better
  return Integer.toString(x);}

😎 Braces Rules

🙄 WhileLoopsMustUseBraces

중괄호({})없이 사용된 while문의 사용은 피하라

수정전

  while(isdigit(c)) c = get();

수정후

  while(isdigit(c)) {
    c = get();
  }

😎 Controversial Rules

🙄 AssignmentInOperand

코드를 복잡하게 하고 가독성이 떨어지는 피연산자에 할당하는 것을 지양한다.

수정전

  while ( (r  = tQuery.fetch() )!= null){
    ... do something
  }

수정후

  while (true){
    r  = tQuery.fetch();
    if(r == null){
      break;
    }
    .. do something ..
  }

🙄  DuplicateImports

import문이 중복 선언 되었음

수정전

수정후

중복 import 삭제

🙄  UnnecessaryParentheses

괄호가 없어도 되는 상황에서 불필요한 괄호를 사용할 경우 마치 메소드 호출처럼 보여서 소스 코드의 가독성을 떨어뜨릴 수 있음. 이에 불필요한 괄호를 삭제토록 함

수정전

  public class Foo {
      boolean bar() {
          return (true);
      }
  }

수정후

  public class Foo {
      boolean bar() {
          return true;
      }
  }

😎 Design Rules

다양한 좋은 디자인 원리 체크, 이를 테면: switch 문장은 default 블록을 갖고 있어야 하고, 심하게 중첩된 if 블록은 피해야 하고, 매개변수들은 재할당되어서는 안되며, 더블(double)이 동일함(equality)과 비교되어서도 안된다.

🙄  AvoidReassigningParameters

파라미터값을 재할당 하지 않는다.

수정전

  public int WriteObject(int nShapeId, SHPObject theObject) throws Exception{
 
    nShapeId = m_nRecords++;
  }
 

수정후

  public int WriteObject(int nShapeId, SHPObject theObject) throws Exception{
    int newShapeId = m_nRecords++;
  }

🙄 AvoidSynchronizedAtMethodLevel

“mothod 레벨의 synchronization 보다 block 레벨 synchronization 을 사용하는 것이 바람직함. 새로운 코드가 메소드에 추가되었을때, 메소드 레벨 동기화는 역효과를 일으킨다. 블락레벨 동기화는 동기화가 필요한 코드가 동기화될 수 있도록 도울것이다.”

수정전

  protected static synchronized void debug_println(String stText)  {
    System.out.println(stText);
  }

수정후

  protected static void debug_println(String stText){
     synchronized (stText) {
         System.out.println(stText);
     } 
  }

🙄 AssignmentToNonFinalStatic

static 필드의 안전하지않은 사용 가능성

수정전

  public static int PK_IDX = 8;

수정후

  public static final int PK_IDX = 8;

🙄 EqualsNull

null 값과 비교하기 위해 equals 함수를 사용하였음.

수정전

  if (A.equals(null)){
  }

수정후

  if (A == null){
  }

🙄 FinalFieldCouldBeStatic

final field를 Static 으로 변경하면 overhead 를 줄일 수 있음.

수정전

  public final int PK_IDX = 8;

수정후

  public static final int PK_IDX = 8;

🙄 ImportFromSamePackage

동일 패키지에 있을 때는 import문을 사용할 필요가 없음

수정후

import 삭제

🙄 ImmutableField

생성자에서 Assign된 변수를 Final로 선언하지 않았음

수정전

  public class Foo {
    private int x; // could be final
    public Foo() {
      x = 7;
    }  
  }

수정후

  public class Foo {
    private final int x;
    public Foo() {
      x = 7;
    }  
  }

🙄 SimpleDateFormatNeedsLocale

SimpleDateFormat 인스턴스를 생성할때 Locale 을 지정하는 것이 바람직함

수정전

  SimpleDateFormat sdf = new SimpleDateFormat();

수정후

  SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd hh:mm:ss", Locale.KOREA);

🙄 SimplifyBooleanExpressions

boolean 표현에서 불필요한 비교는 사용을 지양한다.

수정전

  if (bShell == true)

수정후

  if (bShell)

🙄 SwitchStmtsShouldHaveDefault

Switch구문에는 반드시 default label이 있어야 함

수정전

  switch(a){
    case SeColumnDefinition.TYPE_BLOB:
      break;
  }

수정후

  switch(a){
    case SeColumnDefinition.TYPE_BLOB:
      break;
    default:
      .. do Something ..
  }

🙄 UncommentedEmptyMethod

Empty Method 가 사용되었음. 의도적으로 Empty Method 를 구현했다면 그에 대항하는 주석을 기재하는 것이 바람직함.

수정전

public static void main(String[] args) {}

수정후

불필요할 경우 삭제, 의도적이면 주석추가

🙄 AbstractClassWithoutAbstractMethod

추상클래스 정의 오류

수정전

Abstract Class내에 Abstract Method가 존재하지 않음

수정후

Class 로 변경

🙄 AvoidConstantsInterface

인터페이스에 상수 적용

수정전

public interface ParserTokens {
  public final static short OR=257;
  public void method();}

수정후

public interface ParserTokens {
  public void method();}

😎 Java Logging Rules

🙄 SystemPrintln

개발 시 디버그 작업을 위해 사용한 System.out.println()문이 남아 있음. 불필요한 System.out.println()문은 시스템에 부하를 발생 시킨다.

수정전

System.out.println("SHPType = " + theInfo.m_nShapeType);

수정후

protected static Logger logger = Logger.getLogger(Main.class.getName());
logger.info("SHPType = " + theInfo.m_nShapeType);

 


😎 Naming Rules

표준 자바 네이밍 규약을 위한 테스트: 변수 이름들은 너무 짧아서는 안된다; 메소드 이름은 너무 길어서는 안된다; 클래스 이름은 대문자로 시작해야 하고, 메소드와 필드 이름들은 소문자로 시작해야 한다.

🙄 MisleadingVariableName

변수명에 잘못된 prefix 사용. non-field 이름이 m_ 으로 시작하지 말아야 함

수정전

public void bar(String m_baz) {  // 메소드명이므로 X
    int m_boz = 42; // 지역변수이므로 X
  }

수정후

public void bar(String baz) { 
    int boz = 42; 
}

 

🙄 VariableNamingConventions

다음 2가지 경우 중 하나입니다.

  1. final 변수 이름이 대문자가 아님.
  2. non-final 변수에 underscore 가 포함되어 있음.

수정전

public static final String m_stEnc = "euc-kr";protected FileIO m_theShp = null;

수정후

public static final String ST_ENC = "euc-kr";protected FileIO mTheShp = null;

😎 Optimizaion Rules

🙄 AvoidArrayLoops

배열의 값을 루프문을 이용하여 복사하는 것 보다 System.arraycopy() 메소드를 이용하여 복사하는 것이 효율적이며 수행 속도가 빠름

🙄 UnnecessaryWrapperObjectCreation

불필요한 Wrapper Object가 생성되었음. Parsing 메소드를 직접적 호출하는 것이 바람직함.

수정전

  int i;
  i = Integer.valueOf(s).intValue(); // this wastes an object

수정후

  int i2;
  i2 = Integer.parseInt(s); // this is better

😎 Strict Exception Rules

예외 테스트: 메소드는 java.lang.Exception을 던지도록 선언되어서는 안되고, 예외는 플로우 제어에 사용되어서는 안되며, Throwable은 잡혀서는 안된다

🙄 AvoidThrowingRawExceptionTypes

단순히 RuntimeException, Throwable, Exception 또는 Error를 전달하지말고 더욱 명확한 오류 타입들로 전달해야 합니다.

수정전

if (StringUtils.isEmpty(props.getString("smtp.config"))) {
  throw new RuntimeException("Bad Configuration for smtp.config");}

수정후

if (StringUtils.isEmpty(props.getString("smtp.config"))) {
     throw new BadConfigurationException("smtp.config");}
 
// Exception 처리를 위한 클래스를 생성합니다.public class BadConfigurationException extends Exception {
     public BadConfigurationException (String message) {
             ... 처리...  
     }}

🙄 AvoidThrowingNullPointerException

NullPointerException을 throw하는것을 지양한다. 대신 IllegalArgumentException 사용을 고려하라.

수정전

public class Foo {
  void bar() {
    throw new NullPointerException();
  }}

수정후

public class Foo {
  void bar() {
    throw new IllegalArgumentException();
  }}

😎 String and StringBuffer Rules

스트링 관련 작업을 할 때 발생하는 일반적인 문제들 규명. 스트링 리터럴 중복, String 구조체 호출, String 객체에 toString() 호출하기 등.

🙄 StringToString

String object에서 toString() 호출을 지양한다.

수정전

sb.append(" Where pnu = '").append(pnuSgg.toString()).append("';");

수정후

sb.append(" Where pnu = '").append(pnuSgg).append("';");

🙄 InefficientStringBuffering

“StringBuffer 함수에서 nonliteral 을 직접 concatenate 하지 말 것. Nonliteral conatenation 는 별도로 처리할 것.”

수정전

public class Foo {
 void bar() {
  // Avoid this
  StringBuffer sb=new StringBuffer(""tmp = ""+System.getProperty(""java.io.tmpdir""));
 }}

수정후

public class Foo {
 void bar() {
  // use instead something like this
  StringBuffer sb = new StringBuffer(""tmp = "");
  sb.append(System.getProperty(""java.io.tmpdir""));
 }}

🙄 StringInstantiation

필요없는 Instance가 생성되어 있음

수정전

public class Foo {
  private String bar = new String("bar");}

수정후

public class Foo {
  private String bar = "bar";}

🙄 InefficientEmptyStringCheck

Empty String 을 체크하기 위해 String.trim().length() 을 사용하는 것은 피하도록 함. Character.isWhitespace()를 사용하는 것이 바람직함

수정후

isWhitespace를 이용하여 공백을 체크하는 로직으로 변경처리

🙄 UselessStringValueOf

“String 을 append 할 경우, String.valueOf 함수를 사용할 필요 없음. Valueof() 대신 직접 사용하는 것이 바람직함.”

수정전

return (String.valueOf(obj)+"\n").getBytes();

수정후

return (obj+"\n").getBytes();

😎 Unused Code Rules

결코 읽히지 않은 프라이빗 필드와 로컬 변수, 접근할 수 없는 문장, 결코 호출되지 않는 프라이빗 메소드 등을 찾기

🙄 UnusedFormalParameter

사용하지 않는 파라미터는 메소드의 파라미터에서 제외한다.

수정전

private static Geometry getLineGeometryFromSeShape(GeometryFactory tFactory, int[] parts, int[] subParts, SDEPoint[] points){
  // parts 와 subParts 를 사용하지 않음.}

수정후

private static Geometry getLineGeometryFromSeShape(GeometryFactory tFactory, SDEPoint[] points){}

🙄 UnusedPrivateField

사용되지 않는 값이 할당된 private field를 지양한다.

🙄 UnusedPrivateMethod

사용되지 않는 private method 사용을 지양한다.


😝 PMD 룰 적용 방법 (이클립스) 😝

😎 플러그인 설치

이클립스 실행 후

  1. Help → Install New Software
  2. Add

입력

Name: PMD
Location: http://sourceforge.net/projects/pmd/files/pmd-eclipse/update-site/

대상 이클립스 선택

이후 설치 진행하시면 됩니다.


😝룰셋 등록 😝

  1. Window → Preferences
  2. PMD → Rule Configuration
  3. Clear all 클릭 → 기존 내용을 모두 지움
  4. Import rule set 클릭
  5. 파일선택 ( pmd-rule_전자정부 )
  6. Import by Copy 체크 박스 선택
  7. OK 버튼 클릭
  8. Apply 버튼 클릭
임포트시에 OK 버튼이 활성화 안되는 것은 이클립스 버전이 다르기 때문입니다. 신버전 이클립스를 사용하는경우에는
<rule ref="rulesets/basic.xml/UnusedNullCheckInEquals"> 부분을
<rule ref="rulesets/java/basic.xml/UnusedNullCheckInEquals"> 이렇게 변경해야 합니다.

😝 PMD 실행 😝

😎 PMD Check

  1. 프로젝트 마우스 우클릭
  2. PMD → Check Code with PMD

 

Violations Overview 창에서 PMD 결과를 확인하실 수 있습니다.

 

Violations Outline 창에서 Error Message 와 Line 을 확인하실 수 있습니다.

 

 

😎PMD 해제

PMD로 위반 부분을 모두 수정 하기전에, 빌드를 해야 할 상황이라면 PMD 해제를 합니다.

  1. 프로젝트 마우스 우클릭
  2. PMD → Clear PMD Violation

😝 PMD 룰 제외 방법 😝

😎 Annotation

JDK 1.5 이상에서 사용

전체 룰 적용

// This will suppress all the PMD warnings in this class
@SuppressWarnings("PMD")public class Bar {
 void bar() {
  int foo;
 }}

룰 한개 적용

// This will suppress UnusedLocalVariable warnings in this class
@SuppressWarnings("PMD.UnusedLocalVariable")public class Bar {
 void bar() {
  int foo;
 }}

룰셋 적용

// This will suppress UnusedLocalVariable and UnusedPrivateMethod warnings in this class
@SuppressWarnings("unused")public class Bar {
 void bar() {
  int foo;
 }
 private void foobar(){}}

😎 NOPMD

NO PMD

public class Bar {
 // 'bar' is accessed by a native method, so we want to suppress warnings for it
 private int bar; //NOPMD}

supress warnings

public class Foo {
  void bar() {
    int x = 2; // TURN_OFF_WARNINGS
  }}

 

 

728x90
반응형

댓글