반응형

 

최근에 꽤 큰 지름을 했다. JetBrains사의 상용 IDE인 IntelliJ IDEA를 구매한 것이다. 그것도 단일 

제품이 아닌 All 패키지로 해서 다양한 툴들이 포함된 라이센스로 구매를 했다. 대략 240달러의 가격

이었는데…카드 명세서 보니까 이놈에 환율이 어떻게 돌아가고 있는지 31만원 가량이 결재되었다…

ㅠ.ㅠ

 

사실 이클립스로도 충분히 개발할 수 있고, 아직 현역에서도 충분한 위력을 발휘하고 있는 IDE인 만큼

굳이 큰 돈을 주고 구매해야 하는가에 대해 꽤 긴 시간 고민을 하긴 했었다. 하지만 현재 내가 Mac을 쓰고

있는데, 이 Mac용 이클립스의 인터페이스가 꽤나 조잡하다. 일단 기본적으로 표시되는 폰트가 너무 작고

가끔은 폰트와 퀵메뉴 아이콘들이 깨져보일 때도 있을 정도다.

 

슬슬 노안이 친해지려고 하는 이 때, 다른 것은 다 제쳐두고라도 가독성이 떨어진다는 것은 적지 않은 

핸디캡이다. 그에 비해 안드로이드 스튜디오는 구글을 통해 무료로 배포되기는 하지만 인터페이스가 

상당히 깔끔하여 IntelliJ에도 관심이 가게 된 것이다.

 

물론 주변 사람들의 IntelliJ에 대한 긍정적인 평가도 한몫 했다.

 

위에 이클립스가 현역에서도 아직 충분한 위력을 발휘하고 있다고 쓰긴 했는데 2020년 10월 14일자
JetBrains의 블로그를 보니 java 개발자 대상 IDE 점유율에서 IntelliJ는 72%, 이클립스는 겨우 13%
라는 자료가 있다. 어느정도 신빙성이 있는지는 모르겠으나 IntelliJ의 점유율이 많이 높아지긴 했나보다.

2020년 Java를 그려보다 | The IntelliJ IDEA Blog

 

이클립스와의 비교…를 하려고 했다가…

 

그래도 나름 라이벌 관계이니만큼 IntelliJ와 이클립스를 비교해볼까 하다가 IntelliJ는 이제 겨우 

만져보기 시작했고 이클립스도 속속들이 아는 것이 아니다보니 이것저것 살펴보다 보면 또 시간만 낭비

하게 될 것 같아 자세한 비교는 생략한다.

 

그런데 처음에 비교를 해볼까 하고 프로젝트 생성 과정을 정리하다보니 Spring boot는 IntelliJ나 이클립스가

큰 차이가 없었는데 Spring Framework를 이용하는 Spring Legacy Project를 생성하는 경우에는 오히려

이클립스가 훨씬 쉬웠다. 이클립스에서 Spring Legacy Project를 생성하기 위해서는 플러그인 하나를 설치해야

하지만 이후 프로젝트를 생성하면 바로 Tomcat에 올려 샘플 페이지를 볼 수 있을 정도로 프로젝트를 만들어준다.

 

하지만 IntelliJ는 설정과 샘플 소스를 일일이 직접 코딩을 해주어야 샘플 페이지를 띄울 수 있었다.

공부하기에는 좋지만 학습용 툴이 아닌만큼 조금 더 쉽게 생성할 수 있었으면 어땠을까 싶다.

 

하지만 일단 프로젝트 생성 후에는 설정이라든지 여러가지 부가적인 기능에 있어서 IntelliJ가 압도적으로 유용한 기능이 많다. 특히나 감동받은 기능은 스프링 클래스간의 관계도를 보여주는 부분,

클래스다이어그램을 생성해주는 부분은 내가 개발하고 있는 프로젝트의 구조를 수시로 확인할 수 있게

해주는 아주 유용한 기능이라 생각된다. 이클립스도 플러그인을 설치하면 클래스다이어그램을 얻을 수

있지만 그 품질의 차원이 다르다.

 

여기서 더 깊이 들어가면 (이라기보다 더이상 아는바가 없으므로) 그 내용으로만 하세월이므로 일단

이클립스와의 비교는 이정도로 마치고 앞서 말한 것 처럼 IntelliJ에서 Spring Legacy Project를

생성하는 것이 쉽지 않으므로 이 포스팅에서는 이 내용만 조금 정리해보고자 한다.

 

IntelliJ에서 Spring Legacy Project 생성하기 - Maven 버전

 

우선 IntelliJ를 실행시키고 웰컴 화면에서 New Project를 클릭한다. 만일 IntelliJ를 최초로 실행한 

경우 화면 구성이 약간 다르겠으나 New Project, Open, Get from VCS 3개의 버튼은 동일하게

보일 것이다.

 

 


 

New Project 창의 좌측 프로젝트 타입에서 Maven을 선택해 주자. Project SDK만 잘 설정이 되어

있는지 확인 후 바로 Next 클릭~

 

사족이지만 최근 트렌드는 gradle이라고 하나 늘 그렇듯 프로젝트 현장에는 항상 예전의 잔재들이 강한

영향력을 행사하고 있고 그런 고로 Maven을 무시할 수 없다.

 

 


 

다음으로 적절하게 프로젝트 정보를 입력해 준다. 입력할 것이 많지 않아서 좋다. Artifact쪽은 입력 폼

아래에 간략한 설명도 있어 참고하여 입력하면 된다.

 

 


 

깡통 프로젝트가 만들어졌다. IDE 좌측의 프로젝트 윈도우를 보면 기본적인 뼈대가 만들어져있으나 

당장에 뭔가를 할 수 있는 것은 전혀 없다. 일단 웹 시스템의 구조도 제대로 갖춰져 있지 않고, 라이브러리

역시 기본 JDK 라이브러리밖에 없으며, pom.xml 역시 며느리 집나간 시골집마냥 휑하다.

 

 


 

일단 Spring 프로젝트를 만들기로 했으니 Maven을 이용하여 Spring 라이브러리들을 추가해보자.

pom.xml 파일에서 마우스 우측 버튼을 누르고 컨텍스트 메뉴에서 Generate...를 선택하고 이어

Dependency를 선택하자.

 

 


 

Maven Artifact Search 창이 열리고 의존성 추가할 라이브러를 검색하여 추가할 수 있다. 이런 점이 

바로 IntelliJ가 돈값 하는 부분 중 하나일 것이다^^.

 

현재 Spring Framework는 5.3.7 버전이 릴리즈되어 있지만 IntelliJ에서 자동으로 추가 가능한 것은

5.2.14버전이 최고이다. 일단 선택하고  Add 버튼을 눌러주자.

 

같은 방식으로 로그 관련 라이브러리들까지 추가를 해주었다.

 

 


 

기본적인 라이브러리들이 포함되었으니 이제 슬슬 web 환경을 만들어보도록 하자. 기본적인 구조는 

이클립스로 프로젝트를 생성했을 때 만들어지는 디렉토리 구조를 따라 진행하겠다.

 

먼저 프로젝트 이름 위에서 마우스 오른쪽 버튼을 클릭하여 Add Framework Support…메뉴를 클릭

하자. 

 

 


 

Add Framework Support 창이 열리면 좌측 프레임워크 목록에서 Java EE 아래 있는 Web Application (4.0)을 선택하고 OK 버튼을 누른다.

 

 


 

웹과 관련된 디렉토리와 파일들이 프로젝트 아래 web이라는 디렉토리에 만들어졌다. 하지만 이 위치는

우리가 원하는 위치가 아니다. 우리는 생성된 내용들을 scr > main > webapp 아래로 옮길 것이다.

 

 


 

우선 scr > main > webapp 디렉토라를 만들고 앞서 만들어진 web 디렉토리 아래에 있는 WEB-INF

통째로 scr > main > webapp 아래로 옮기자. 복붙을 해도 좋고 그냥 드래그앤 드롭을 해도 좋다. 

이렇게 옮기면 IntelliJ는 리팩토링 창을 띄워 확인할 것이다. 그냥 Refactor를 누르면 이동이 완료된다.

 

Index.jsp 파일은 옮겨진 위치의 WEB-INF 아래에 views 디렉토리를 만들고 그 곳으로 이동시켜 주자.

마지막으로 자동 생성된 web 디렉토리는 제 역할을 다했으니 장렬하게 보내주면 된다.

 


 

이제 Spring 프로젝트에서 필요한 설정파일을 만들어주자. root-context.xml과 servlet-context.xml

2개의 파일을 만들 것이다.

 

우선 WEB-INF 아래 spring, spring/appServlet 디렉토리를 만들자. 각각의 디렉토리 위에서 마우스

오른쪽 버튼을 클릭하여 New > XML Configuration File > Spring Config를 차례대로 클릭한다.

 

spring 디렉토리 아래에는 root-context.xml 파일을, 

spring/appServlet 아래에는 servlet-context.xml 파일을 만들어준다.

 

그리고 web.xml 파일에 새로만든 두 파일에 대한 경로가 잘 설정되어 있는지 확인한다.

 


 

spring 설정파일 중 root-context.xml 파일은 아직 쓸 일이 없으니 관심 끄고 우선 ViewResolver를

설정하기 위해 servlet-context.xml 파일을 열자.

 

 


 

이제 디렉토리와 파일은 모두 준비가 된 것 같으니 IntelliJ의 프로젝트 설정에서 마무리를 지어주자.

프로젝트 이름 위에서 마우스 오른쪽 버튼을 클릭하고 Open Module Setting를 선택한다.

 

 


 

Project Structure 창에서 좌측 목록 중 Project Settings > Modules를 선택해보면 황량한 풍경이

펼쳐진다. 아직 뭔가 덜 되었다는 얘기다. 가장 오른쪽 컨텐츠 창의 왼쪽 윗부분에 있는 + 버튼을 누르자.

새롭게 New Application Context 창이 열리고 우리가 새로 만든 2개의 context.xml 파일을 선택할 

수 있다. 

 

모두 선택하고 OK를 누르자. 계속 OK를 누르면서 빠져나오면 우리의 프로젝트에 context.xml

파일들이 연결된 것을 확인할 수 있다.

 

 


 

이번에는 Web을 선택하여 web.xml 파일이 제대로 연결되어 있는지 확인해보자.

상단의 Deployment Descriptor에는 web,xml의 경로가, 하단의 Web Resource Directories에는

webapp 디렉토리가 정상적으로 설정되어 있는지 확인한다.

 


 

이제 Project Settings 항목 아래의 Facets로 자리를 옮겨보자.

앞서 설정한 내용들이 모두 동일하게 설정되어 있는 것을 확인할 수 있다.

 

 


 

마지막으로 Artifacts로 이동해보자. 가장 우측의 창 하단 부분이 두 부분으로 나뉘어 있고 그 오른쪽은

Available Eliments라고 되어있다. 이곳에 있는 라이브러리들 중 필요한 것이 있으면 더블클릭을 

해보자. 더블클릭한 라이브러리들은 좌측 화면의 WEB-INF 아래 lib 폴더가 생기면서 그곳으로 

들어간다.

 


IntelliJ의 Project Structure는 프로젝트를 관리하는 중요한 곳인데 생소한 용어들이 많이 보인다. 간략하게 정리하면 다음과 같다.

 

- Project : 현재 설정의 글로벌한 내용들을 볼 수 있다. 최초 프로젝트 생성시 입력한 내용들이다.
- Modules : 일종의 서브프로젝트 개념이다. 현재는 서브프로젝트가 없기 때문에 하나의 항목만 보인다.
- Libraries : Maven으로 추가된 라이브러리 목록들이 보인다. 라이브러리의 소스와 문서까지 관리한다.
- Facets : 모듈을 구성하고 있는 요소들을 보여준다. 모듈에서 하위 항목을 선택했을 때와 동일하다.
- Artifacts : Deploy와 관련된 내용들이 보여진다. 프로젝트를 발드했을 때 어떤 형태로 저장되는지, 어느 위치에 저장되는지, 어떤 내용들이 포함되는지 등을 설정한다.

 


 

이제 빌드를 준비해보자.

우선 IntelliJ 가장 우측 벽에 붙어있는 Maven 버튼을 눌러보자. Maven의 여러가지 task 들이 표시

된다. 우리는 패키징을 하여 배포할 것이므로 Lifecycle의 package를 더블클릭한다. 

 

package task로 빌드가 진행된다. 다행이 SUCCESS란다!

 

 


 

매번 이렇게 할 수 없으니 Run/Debug Configuration에 등록을 해놓자.

화면 상단의 초록색 망치(Build Project) 버튼 옆의 App Configuration…이라고 된 부분을 클릭하면

Run/Debug Configuration 창이 뜬다.

 

화면 좌측 상단의 + 버튼, 좌측의 Add New…, 한복판의 Add new run configuration…어디를 

눌러도 설정을 추가할 수 있는 팝업 메뉴가 열린다. 우리는 Maven 프로젝트이니 Maven을 선택하자.

 

이름을 간단히 적어주고 중앙에 있는 Command line 항목에는 package war:war를 입력해준다.

그리고 Apply, OK 하고 빠져나오면 이제 IntelliJ 상단의 Build 버튼(초록 망치)과 Run 버튼(초록 

플레이) 버튼 사이에서 우리가 만든 Run Configuration을 선택할 수 있다.

 

 


 

다음은 톰캣 연동이다. 톰캣은 이미 PC에 설치되어 있고 IntelliJ에 아래와 같이 설정이 되어 있다는 전제 

하에 설명한다.

 

 

처음 Run Configuration을 등록한 후 App Configuration…은 셀렉트박스가 되어 있을 것이다.

설정을 추가해야 하므로 다시 App Configuration…을 선택한다.

 

Maven 설정을 추가할 때와 동일한 진행을 하고 Add new run configuration에서 Tomcat Server > local을 선택한다.

Tomcat 설정 창에서 Deployment 탭을 선택하고 + 버튼을 눌러 Artifact…를 선택하면 아까

Project Structure의 Artifacts에서 우리가 보았던 이름이 추가될 것이다.

 

그리고 화면 아랫쪽에 있는 Application context도 잊지말고 수정해 주자. URL 치는데 귀한 노동력 

소모하고 싶지 않으면…

 

이상태에서 App Configuration…을 Tomcat으로 선택하고 Run 하면 톰캣이 로드되면서 우리가 

만든 프로젝트가 디플로이 되고 드디어 브라우저에서 화면을 볼 수 있게 된다.

 

 


 

정리

 

스크린샷이 많다보니 글이 너무 길어졌다. 원래는 gradle 프로젝트도 함께 설명을 하려 했으나 그 역시

이정도 분량의 스크린샷이 있다보니(상당부분 중복이지만…) 아무래도 따로 나누어 포스팅을 해야겠다.

 

일단 잠깐의 경험으로는 거금을 치룬 것이 아깝지는 않다. 이클립스 역시 훌륭한 개발툴이지만 이클립스를

쓰다보면 알게모르게 자잘한 오류들이 많이 발생을 한다. 그리고 서두에도 썼다시피 일단 이터페이스가

IntelliJ와 비교해보면 조악하기 그지 없다.

 

처음 맛보기로 이정도인데… 앞으로 하나하나 알아나가야 할 IntelliJ의 기능들이 기대가 될 뿐이다.

앞으로 개발을 하면서 새롭게 알게되는 IntelliJ의 기능들을 틈나는대로 공유해보도록 하겠다.

반응형




AddEmptyString


우선순위 : 3

The conversion of literals to strings by concatenating them with empty strings is inefficient. It is much better to use one of the type-specific toString() methods instead.

 

문자열로 형변환을 할 때 빈 문자열로 + 연산을 하는 것은 비효율적이다.
이럴 경우에는 각 타입별로 구현된 toString() 메소드를 이용하는 것이
더 낫다.

 

샘플 코드



AvoidArrayLoops


우선순위 : 3

Instead of manually copying data between two arrays, use the efficient System.arraycopy method instead.

 

두 배열간에 복사를 할 때는 루프를 통해 수동적으로 하지 말고
System.arraycopy를 이용하는 것이 더 효율적이다.

 

샘플 코드

 

부연설명

 

System.arraycopy 사용법
System.arraycopy는 다음과 같은 5개의 인자를 받는다.

 

Object src : 복사할 원본 소스 배열
int srcPos : 소스에서 복사를 시작할 index
Object dest : 소스를 복사할 대상 배열
int destPos : 대상 배열에서 쓰기 시작할 index
int length : 원본에서 몇개의 요소를 읽어올지에 대한 길이

 

예제

결과

destStrArr1 : null
destStrArr1 : null
destStrArr1 : 요소1
destStrArr1 : 요소2
destStrArr1 : null

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

destStrArr2 : 요소1
destStrArr2 : 요소2
destStrArr2 : 요소3
destStrArr2 : 요소4
destStrArr2 : 요소5




RedundantFieldInitializer


우선순위 : 3

Java will initialize fields with known default values so any explicit initialization of those same defaults is redundant and results in a larger class file (approximately three additional bytecode instructions per field).

 

자바의 멤버 변수들은 잘 알려진 값으로 자동 초기화 되므로 명시적으로 이러한
기본값으로 초기화 할 필요가 없으며 초기화할 경우 오히려 용량만 많이 차지하게 된다.

 

샘플 코드

 

부연 설명

아래 추가 샘플코드에 보면 FieldTest의 멤버변수들인 b,s,i,l,d,f,o 등은
아무런 초기화를 하지 않았지만 실제로 프린트를 해보면 기본값일이 출력되는 것을
알 수 있다.

 

FieldTest.java

PMDTestMain.java

 

결과

b = false
s = null
i = 0
l = 0
d = 0.0
f = 0.0
o = null


반응형



변수명 맞추기...


잘 만들어지고 있던 사이트에 마지막 기능을 추가하는 중...
갑자기 등록과 수정(POST,PUT) 과정에서 400 Bad Request 오류가
발생을 한다.


다른 메뉴에서는 잘 되는 것으로 보아 설정의 문제는 아니고...
새로 구현된 기능에 국한된 문제인 듯싶은데...


반나절 이상을 날리면서 오늘 아침 겨우 찾아낸 문제의 원인은...
서버측 domain 클래스의 변수명과 등록/수정 시 넘기는
JSON 문자열의 변수명이 일치하지 않았던 것...ㅠ.ㅠ


서버쪽에서 변수명에 오타가 하나 있어 수정을 했는데
클라이언트쪽에서 수정을 안한 것이다.


참으로 소박한 실수였다...ㅠ.ㅠ

반응형




AvoidInstantiatingObjectsInLoops


우선순위 : 3

New objects created within loops should be checked to see if they can created outside them and reused.

 


Loop 안에서 객체의 인스턴스를 생성할 때는 Loop 밖에서 생성한 후 재사용 가능한지
확인하라


 

샘플코드

 


부연설명

 

잘 아다시피 instance를 생성한다는 것은 메모리를 사용한다는 의미다.
루프 안에서 객체의 인스턴스를 생성한다는 것은 곳 루프가 도는 만큼 메모리를
소비하게 된다는 이야기다. 하지만 이 룰에 강제성을 두지 않은 이유는 참조타입의
변수 각각에 서로 다른 값을 가지게 해야 할 경우 루프 밖에서 인스턴스를 생성한 후
루프 안에서 값을 할당하게 되면 모든 인스턴스가 동일한 값을 가지게 되어
의도한 대로 작동하지 않게 되기 때문이다. 따라서 이 룰은 잘 판단을 하여 사용해야 한다.




SimplifyStartsWith


우선순위 : 3

Since it passes in a literal of length 1, calls to (string).startsWith can be rewritten using (string).charAt(0) at the expense of some readability.

 


문자열의 길이가 1이라면 startsWith 메소드를 사용하는 것보다 charAt(0)을 사용하는 것이
더 효율적이다.


 

샘플 코드





UseStringBufferForStringAppends


우선순위 : 3

The use of the ‘+=’ operator for appending strings causes the JVM to create and use an internal StringBuffer. If a non-trivial number of these concatenations are being used then the explicit use of a StringBuilder or threadsafe StringBuffer is recommended to avoid this.

 


문자열을 합치기 위한 ‘+=‘ 연산은 JVM에서 내부적으로 StringBuffer를 생성하여
사용한다. 따라서 통상적인 범위를 넘어서는 문자열 연결이라면 명시적으로
StringBuilder나 혹은 thread에 안정적인 StringBuffer(threadsafe한 대신
조금 느림)를 사용하도록 권고한다.


 

샘플코드

 


부연설명
PMD에서 뿐만 아니라 일상적인 코딩에서도 논란이 많은 룰입니다.
이미 내부적으로 StringBuffer를 사용하므로 그냥 += 연산자를 사용해도 되지
않느냐는 말들도 참 많이 합니다. 룰 설명에도 ‘통상적인 범위를 넘어서는’이라는
전제를 달고 있고…
현재까지의 가장 합리적인 절충안은 적어도 loop안에서는 += 사용을 지양하는 정도가
되겠네요.
오히려 += 연산자를 쓰느냐 마느냐보다는 StringBuffer와 StringBuilder를 선별해서
사용하는데 주의를 기울여야 할 것 같습니다.




UseArraysAsList


우선순위 : 3

The java.util.Arrays class has a “asList” method that should be used when you want to create a new List from an array of objects. It is faster than executing a loop to copy all the elements of the array one by one.

 

배열(Arrays)을 List로 변환하는 경우 Array 클래스에 있는 asList 메소드를 이용하는 것이
루프문을 통해 하나하나 복사하는 것보다 더 빠르므로 asList 메소드를 사용하는 것이 좋다.

 

샘플 코드


반응형



PositionLiteralsFirstInComparisons


우선순위 : 3

 

Position literals first in comparisons, if the second argument is null then NullPointerExceptions can be avoided, they will just return false.

 

equals()를 통한 비교를 할 때는 항상 상수 값을 앞에 두어야 한다.
만일 두번째 인자가 null인 경우에는 NullPointerException이 발생하지 않으며
단순히 false를 리턴하게 된다.

 

하지만 상수가 아닌 참조타입의 변수가 앞에 오게 되면 이 변수가 null인 경우
NullPointerException이 발생하게 된다.

 

샘플 코드




UnnecessaryLocalBeforeReturn


우선순위 : 3

Avoid the creation of unnecessary local variables

 

불필요한 지역변수를 만들지 말라.

 

모든 변수는 메모리를 사용하는 만큼 가급적이면 꼭 필요한 변수만
필요할 때 만들어서 사용하는 것이 좋을 것이다. 이 것이 힘들다면
절대 필요하지 않을 것 같은 변수는 만들지 않는 것이 좋을 것이다.

 

샘플 코드




AvoidConstantsInterface


우선순위 : 3

An interface should be used only to characterize the external behaviour of an implementing class: using an interface as a container of constants is a poor usage pattern and not recommended.

 

인터페이스란 클래스가 외부와 어떤 행위를 하도록 하는 내용으로 구현되어야만 한다.
단지 상수를 저장해놓는 컨테이너로만 사용되는 인터페이스는 권장되지 않는다.

 

샘플 코드 (나쁜 예)





UseCollectionIsEmpty


우선순위 : 3

The isEmpty() method on java.util.Collection is provided to determine if a collection has any elements. Comparing the value of size() to 0 does not convey intent as well as the isEmpty() method.

 


컬렉션 객체에 엘리먼트들이 없다는 것을 확인하기 위해서는 size() == 0 보다는
isEmpty() 사용이 권장된다. size()를 통한 비교는 isEmpty()에 비해 그 의도가
명확하게 전달되지 않는다.

 

샘플코드

 

부연설명

일반적으로 isEmpty() 메소드와 size() 메소드의 성능상의 차이는 없는 것으로 보아도
좋습니다. 다만 메소드 명에서 드러나는 ‘의도’가 isEmpty쪽이 좀 더 분명하다는
장점이 있습니다. 참고로 ArrayList에 구현된 isEmpty() 메서드와 size() 메서드의
내용은 다음과 같습니다.





ReturnEmptyArrayRatherThanNull


우선순위 : 1

For any method that returns an array, it is a better to return an empty array rather than a null reference. This removes the need for null checking all results and avoids inadvertent NullPointerExceptions.

 


배열을 리턴하는 메소드에서 배열에 엘리먼트가 없더라도
null 보다는 빈 배열을 리턴하는 것이 좋다.
이렇게 하는 것은 불필요한 null 체크를 피할 수 있을 뿐만 아니라
의도하지 않은 NullPointerException이 발생하지 않도록 할 수도 있다.

 

샘플 코드


반응형



FinalFieldCouldBeStatic


우선순위 : 3

 

컴파일 타임에 상수가 할당되는 final 필드는 자동으로 static으로 변환된다.
따라서 이러한 필드들은 runtime시의 부하를 줄여주기 위해 static을 명시적으로
지정해주는 것이 좋다.

 

샘플 코드



OptimizableToArrayCall


우선순위 : 3

Calls to a collection’s toArray() method should specify target arrays sized to match the size of the collection. Initial arrays that are too small are discarded in favour of new ones that have to be created that are the proper size.

컬렉션 객체의 toArray() 메소드를 호출할 때에는 대상 배열 크기를 명확하게 컬렉션
객체의 크기와 동일하게 맞춰야 한다.

너무 작은 크기의 초기 배열은 알맞은 크기의 배열이 생성되도록 하기 위해
그냥 버려지기 때문에 비효율적이다.

 

샘플 코드



ConfusingTernary


우선순위 : 3

Avoid negation within an “if” expression with an “else” clause. For example, rephrase:

if (x != y) diff(); else same(); as: if (x == y) same(); else diff();

Most “if (x != y)” cases without an “else” are often return cases, so consistent use of this rule makes the code easier to read. Also, this resolves trivial ordering problems, such as “does the error case go first?” or “does the common case go first?”.

 

비교구문에 부정연산자(!)를 사용하는 것은 피하는 것이 좋다.
어차피 if (x != y) diff(); else same();와 if (x == y) same(); else diff();는
동일하며 대체로 “if (x != y)”로 표현하는 경우에는 else문 없이 if문 안에서 바로
리턴하는 경우가 많기 때문에 이러한 룰을 일관성 있게 지켜서 코딩하는 것이
가독성을 높이는 방법 중 하나다.

 

그리고 이러한 것들이 사소하게는 문제 처리의 순서
즉,에러를 먼저 처리할 것인지, 정상 코드를 먼저 처리할 것인지에 대한 부분도
해결해 줄 수 있다.

 

샘플 코드



SimpleDateFormatNeedsLocale


우선순위 : 3

Be sure to specify a Locale when creating SimpleDateFormat instances to ensure that locale-appropriate formatting is used.

 

적절한 지역화 포맷이 필요한 경우에는 SimpleDateFormat의 인스턴스를 생성할 때
확실하게 locale을 지정하라.

 

샘플 코드



SimplifyConditional


우선순위 : 3

No need to check for null before an instanceof; the instanceof keyword returns false when given a null argument.

 

조건절에 instanceof 연산자를 사용할 경우에는 그 앞에 null 체크를 할 필요가 없다.
instanceof 연산자는 비교 대상이 null인 경우 false를 리턴한다.

 

샘플 코드

 

부연 설명

오라클의 자바 공식 문서에 보면 instanceof 연산자는 연산 대상으로 참조 타입이나
null 타입을 사용할 수 있다고 합니다. 그렇지 않은 경우 컴파일시에 오류가 발생을
합니다.

 

따라서 연산자로 null이 사용되는 경우에는 문제가 없으나 int, String, boolean 같은
기본형을 대상으로 하는 경우에는 컴파일 에러가 발생합니다.


반응형



ConstructorCallsOverridableMethod

우선순위 : 1

영어 실력이 그지같아서  번역이 힘드네요…원문 적고 설명은 샘플 밑에 부연 설명으로 대신하겠습니다.

마지막 줄만 설명하자면 일반적인 제어의 흐름을 따르는 메소드 호출을 하라는 것인데 예를들어

생성자인 Foo()에서 public 메소드인 buz()를 호출하는 private 메소드 bar()를 호출하는 것과 같이 비비 꼬아놓으면

문제의 소지가 있다는 말입니다.

 

원문

Calling overridable methods during construction poses a risk of invoking methods on an incompletely constructed object and can be difficult to debug. It may leave the sub-class unable to construct its superclass or forced to replicate the construction process completely within itself, losing the ability to call super(). If the default constructor contains a call to an overridable method, the subclass may be completely uninstantiable. Note that this includes method calls throughout the control flow graph – i.e., if a constructor Foo() calls a private method bar() that calls a public method buz(), this denotes a problem.

 

샘플 코드

SeniorClass

JuniorClass

 

 

부연 설명

위 샘플 코드를 직접 돌려보면 결과가 다음과 같이 나옵니다.

SeniorClass.constructor
Exception in thread “main” JuniorClass.toString
java.lang.NullPointerException
at JuniorClass.toString(JuniorClass.java:12)
at SeniorClass.<init>(SeniorClass.java:5)
at JuniorClass.<init>(JuniorClass.java:4)
at PMDTestMain.main(PMDTestMain.java:10)

스택트레이스를 따라가보면 JuniorClass의 생성자 > SeniorClass의 생성자 > JuniorClass의 toString 순으로

호출 되고 있습니다. extends SeniorClass로 인해 이미 SeniorClass의 toString은 JuniorClass의 toString으로

바뀐 상태입니다. 결국 super()를 호출한 시점에서 벌써 JuniorClass의 toString이 호출되고 있으나

이 시점에는 아직 name에 값이 할당되기 전이기 때문에 위와같이 NullPointerException이 발생하게 됩니다.


LocalVariableCouldBeFinal

우선순위 : 3

지역변수가 최초 값 할당 이후 값이 변경하지 않는다는 것을 보장하기 위해서

final 변수로 지정할 수 있다.

 

샘플 코드

 

 

부연 설명

만일 메소드 내의 특정 지역변수가 메소드의 처리 과정 내에서 그 값이 변하면 안되는 경우에는

실수에 의한 값 변경을 막기 위해서 final로 사용하는 것도 좋은 방법이라는 의미입니다.

아시다시피 final 변수에 값을 할당하게 되면 컴파일 시에 에러가 발생하기 때문에 쉽게 문제를 발견할 수 있게 됩니다.


AvoidReassigningParameters

우선순위 : 2

메소드에 전달된 파라미터를 직접 재할당하는 것은 피해야 한다.

임시 지역변수를 이용하여 값을 변경하는 것이 좋다.

 

샘플 코드

 

부연 설명

JAVA에서는 매개변수를 전달할 경우 primitive 타입은 변수 값의 복사본을 전달하지만 Object 타입을 전달할 경우

복사본이 아닌 참조값(변수가 저장된 메모리의 주소)를 넘기게 됩니다.

따라서 매개변수에 직접 새로운 값을 할당 할 경우 사실상 primitive 타입을 받은 경우에는 심각한 문제가 발생하지는

않습니다. 다만 코드가 길어질 경우 ‘분명히 X값을 넘겼는데 왜 Y’값으로 바뀌었지?’ 하는 혼란이 생길 수 있습니다.

하지만 Object 타입의 경우는 문제가 심각합니다. 전달받은 쪽에서 값을 변경하면 전달해준 쪽의 값도 같이 바뀌기 때문에…

따라서 특별한 목적이 있지 않는 한 전달받은 매개변수를 직접 수정하는 일은 없어야 합니다.

 

연습

결과

paramTest(할당 전) : strList.size = 4

paramTest(할당 전) : strItem = TEST1

paramTest(할당 전) : strItem = TEST2

paramTest(할당 전) : strItem = TEST3

paramTest(할당 전) : strItem = LOCALTEST4

paramTest(할당 후) : strList.size = 3

paramTest(할당 후) : strItem = LOCALTEST1

paramTest(할당 후) : strItem = LOCALTEST2

paramTest(할당 후) : strItem = LOCALTEST3

paramTest(할당 전) : str = STR_TEST1

paramTest(할당 후) : str = LOCAL_STR_TEST1

main : strList.size = 4

main : strItem = TEST1

main : strItem = TEST2

main : strItem = TEST3

main : strItem = LOCALTEST4

main : str = STR_TEST1



SimplifyBooleanReturns

우선순위 : 3

boolean 값을 리턴하게 될 때는 불필요한 분기문(if-else)을 사용하지 말아라.

대신에 조건을 테스트하는 문장 바로 리턴해도 된다.

샘플 코드

 

부연설명

추후 따로 설명을 하겠지만 if문이 늘어나면 Cyclomatic Complexity라고 하여 복잡성 지표가 올라가게 됩니다.

이는 코드의 가독성을 떨어뜨리고 이해를 어렵게 하는 원인이 되어 품질 관리에서는 특정 점수 이하로 유지하도록

하고 있습니다. (간단하게 복잡도 = 분기문 갯수 + 1이라고 생각하시면 됩니다.)

이러한 이유로 if문은 가능한한 사용하지 않는쪽으로 코딩을 하시는 것이 좋습니다.

반응형



NullAssignment

우선순위 : 3

코드 중간에서 변수에 null을 할당하는 것은 좋지 않은 형태이다.

이런 형태의 할당은 종종 개발자들이 프로그램의 흐름을 이해하지 못하고 코딩을 하고 있다는 표시가 될 수 있다.

주의 : 이런 형태의 경우가 드물게는 garbage collectoin에 도움이 되는 경우도 있다. (물론 잘 이해하고 사용했을

경우) 이렇게 의도하고 사용한 경우에는 이 룰을 무시해도 좋다.

 

샘플코드

 

부연 설명

이 경우 중요한 부부은 주석으로 표시된 ‘매우 크고 복잡한 코드’에 있다.

중간에 특정 변수를 null로 처리하는 경우 이를 제대로 인지하지 못하게 되면 뒤의 코드에서 null로 할당된

변수를 새로운 인스턴스로 할당하지 않고 바로 사용하게 될 소지가 있으며 이럴 경우 NullPointerException이

발생하게 될 것이다.

믈론 한 메소드 안에서 그렇게 길고 복잡한 코드를 사용하는 것 자체가 문제라면 문제지만…


OnlyOneReturn

우선순위 : 3

메소드에는 종료 포인트가 반드시 하나만 있어야 하며 이 종료 포인트는 반드시 메소드의 가장 마지막에

있어야 한다.

 

샘플코드 (나쁜 예)

 

부연 설명

이는 원칙적이고 일반적인 룰을 설명한 것이다.

흔히들 메소드 내에 if, if/else, while, for, try/catch등 각종 코드 블록이 만들어지고 각각의 블록에서

상황에 맞게 return문을 사용하는 경우가 많다. 상황에 따라 이렇게 할 수밖에 없는 경우도 있지만

이는 곧 코드의 복잡도(Cyclomatic Complexity)가 올라갔다는 반증이기도 하다.

가독성을 높이고 코드의 흐름을 원활하게 하기 위해서는 가능한한 return문은 하나만, 메소드의 가장

마지막에 위치할 수 있도록 하자.


AvoidLiteralsInIfCondition

우선순위 : 3

조건문의 조건식에 하드코딩 된 상수를 사용하지 말아라.

의미를 설명할 수 있는 이름을 가진 static 변수나 private 멤버 변수로 사용하는 것이 더 좋은 방법이다.

 

샘플코드

 

부연설명

위의 샘플코드에 ‘magic number’라는 표현이 나오는데 이 것은 Refactoring에서도 자주 언급되는 내용으로

마치 마술처럼 어떻게도 해석이 될 수 있다는 것을 빗댄 표현이다.

즉, 상수를 하드코딩해 놓았을 때 그 숫자 자체만으로는 어떤 의미인지 이해가 불가능하다.

따라서 이러한 숫자들은 static이나 private 멤버변수로 만들어 의미가 있는 이름으로 사용하도록

권장하고 있다.


UseObjectForClearerAPI

우선순위 : 3

public 메소드를 만들 때는 API 사용 조건에 대해 생각해야만 한다.

메소드를 public으로 만들었다는 것은 다른 클래스들에서 이 메소드를 사용하게 하는 것이고 따라서

보다 포괄적이고 개선된 API를 제공하고자 하는 것이다.

 

이런 경우 너무 많은 정보를 단순 나열 형태의 String으로 전달하게 된다면 이 파라미터들을 하나의 Object로

묶어 이 Object를 인자로 전달하는 것이 좋다. 일단 코딩이 길어지고 복잡해지는 것이나 파라미터의 갯수가

많음으로 해서 어떤 인자가 어떤 위치에 있어야 하는지에 대한 혼동을 줄일 수 있을 뿐더러 만일 전달해야 하는

파라미터의 수나 종류가 변경될 경우 이 Object만 수정하면 되기 때문이다.

 

샘플코드

 

반응형



AvoidBranchingStatementAsLastInLoop

우선순위 : 2

루프의 가장 마지막 위치에서 분기문을 사용하는 경우 버그가 발생할 가능성이 높다.

사용한 방법이 버그가 아닌지 확인하거나 다른 접근 방법을 사용해야 한다.

 

샘플 코드

 

부연설명

Loop문의 내의 continue문이나 break문은 문장의 흐름을 제어하는 역할로 가독성의 측면에서 본다면 문장의 정상적인

흐름을 방해하는 부분도 있습니다. 이러한 요소가 자주 사용된다는 것은 그만큼 문장의 흐름을 이해하기 힘들게 만들고

또 그러인해 버그가 발생할 가능성이 높아질 것입니다.

가급정 문장 흐름을 제어하는 문장은 적게 사용하는 것이 좋겠습니다.




SimplifiedTernary

우선순위 : 3

조건문?boolean 상수:foo 또는 조건문?foo:boolean 상수 형태의 3항 연산자를 사용이 필요하다면

이러한 문장은 각각 다음과 같이 간단하게 표현할 수 있다.

boolean 상수가 처음 결과이면서 true인 경우     : condition || foo

boolean 상수가 두 번째 결과이면서 true인 경우  : !condition || foo

boolean 상수가 처음 결과이면서 false인 경우    : !condition && foo

boolean 상수가 두 번째 결과이면서 false인 경우 : condition && foo

 

샘플 코드

 

3항 연산자는 대체로 가독성을 떨어뜨리므로 가능한한 단순한 표현으로 대체하라는 의미입니다.




~MustUseBraces

우선순위 : 3

 

이 룰셋에 해당하는 룰은 총 4가지가 있습니다.

전체적인 내용은 코드의 가독성을 위해 if, if/else, while, for문에 반드시 braces({})를 사용하라는 것입니다.

전체 내용 그대로 옮깁니다~

 

IfStmtsMustUseBraces

샘플 코드

 

WhileLoopsMustUseBraces

샘플코드