'Technical Note'에 해당되는 글 134건

Technical Note/apache & tomcat

Apache Multi-Processing Modules(MPM, 다중처리모듈)의 Prefork 와 Worker 방식의 비교
 ==> apache 서버가 클라이언트에게서 받아들인 요청을 처리하기 위해 "자식 process" 에게 분배하는 방식

Prefork 방식

- 하나의 자식 프로세스가 하나의 쓰레드를 갖는 구조로, 자식 프로세스는 1024까지 늘일 수 있다.
- 한개의 자식 프로세스는 한 개의 연결을 담당한다. 
- 프로세스가 생성되는 구조이므로 당연히 worker보다는 많은 메모리를 사용함.
- 프로세스간 메모리를 직접 공유하지 않으므로, 메모리 공간이 독립적이어서 안정적임

httpd.conf에서 httpd-mpm.conf 파일을 include 시켜야 한다.

httpd-mpm.conf 파일에서

  <IfModule mpm_prefork_module> 
      StartServers 5 
      MinSpareServers 5 
      MaxSpareServers 10 
      MaxClients 150 
      MaxRequestsPerChild 0 
  </IfModule>


옵션 설명 
StartServer: 아파치서버의 자식 프로세스 개수 지정

MinSpareServers, MaxSpareServers :
부하가 적어서 MinSpareServers 개수 보다 적었을 경우 최소한 이 개수 만큼 유지하려고 아파치가 노력하고 
부하가 증가하여 프로세스 개수가 많아질 경우에 MaxSpareServers 개수 이하로 줄이려고 아파치는 노력한다. 
즉, 절대적인 수치가 아니다.

MaxClient : 초기 시작시 실행가능한 최대 아파치 자식 프로세스의 개수를 지정 
            worker방식의 MaxClient 와는 전혀 다른 의미가 다르지만 결국 클라이언트의 요청을 처리하는 용량을 말하므로 사실상 유사.

MaxReqeustPerChild :클라이언트들의 요청 개수를 제한. 
           만약 자식 프로세스가 이 값만큼의 클라이언트 요청을 받았다면 이 자식 프로세스는 자동으로 죽게 된다. 
           0 인 경우 무한대임.

Maxclient 를 늘리기 위한 설정 ( Prefork 방식일 경우 ) 
  1) apache 1.x 버전 
    Apache 소스 디렉토리/src/include/httpd.h 에서
    define HARD_SERVER_LIMIT 256  ----> 512 혹은 1024 등으로 변경 저장후 컴파일

  2) apache 2.x~ 2.2.x 버전 
    apache소스디렉토리/server/mpm/prefork/prefork.c 에서 
    define DEFAULT_SERVER_LIMIT 256 ----> 512 혹은 1024 등으로 변경후 재부팅

 

Worker 방식
- 자식 프로세스들이 여러개의 쓰레드를 갖을 수 있으며, 각 쓰레드는 한번에 한 연결을 담당함. 
- Prefork보다 메모리 사용량이 적음. 통신량이 많은 서버에 적절함. 
- 쓰레드 간에 메모리 공간을 공유함. 리소스 경합이 발생하지 않도록 주의 필요. 특히 PHP를 쓰는 경우 유의하여야 함.


httpd.conf에서 httpd-mpm.conf 파일을 include 시켜야 한다.

httpd-mpm.conf 파일에서 
    
    <IfModule mpm_worker_module> 
        StartServers 2 
        MaxClients 150 
        MinSpareThreads 25 
        MaxSpareThreads 75 
        ThreadsPerChild 25 
        MaxRequestsPerChild 0 
    </IfModule>

 

옵션 설명  

StartServers(Default 3) : 시작시에 생성되는 서버 프로세스의 개수, 자식 프로세스의 수는 부하에 따라 동적으로 변경되기 때문에 이 값은 큰 의미가 없다.

ServerLimit (default : 16)                                                                                       
    - 구성 가능한 child 프로세스의 제한 수. 
    - 이 ServerLimit 값이 필요 이상 높게 설정 된다면, 불필요한 공유 메모리가 할당 되므로 적절한 설정 필요. 
    - MaxClient 와 ThreadPerChild 에서 요구한 프로세스 수보다 높게 설정하지 말 것.


MaxClient (default : ServerLimit * ThreadsPerChild) 
        - 동시에 처리될 최대 커넥션(request)의 수 
        - MaxClients 수치를 초과한 후 온 요청들은 ListenBackLog에 의해 큐잉됨 
        - ThreadsPerChild 옵션과 매우 긴밀하게 작용함 
        - 동접자가 많을 경우, 이 MaxClient값을 증가시켜야 함. 
        - OS의 FD(File Descriptor)값을 증가 시켜 MaxClient 의 상한값을 증가시키도록 할 것.

 

MinSpareThreads(default 75) : 최소 thread 개수
  - 만약 서버에 idle 쓰레드가 충분하지 않다면 child 프로세스는 idle 쓰레드가 MinSpareThreads 보다 커질때까지 생성된다.

 

MaxSpareThreads(default 250) : 최대 thread개수 
   - 만약 서버에 너무 많은 idle 쓰레드가 존재하면 child 프로세스는 idle 쓰레드가 MaxSpareThreads 수보다 작아질 때까지 죽는다
   
ThreadPerChild : 개별 자식 프로세스가 지속적으로 가질 수 있는 Thread의 개수

MaxRequestPerChild : 자식 프로세스가 서비스할 수 있는 최대 요청 개수

ThreadLimit (default : 64)

- child 프로세스의 라이프주기 동안 ThreadsPerChild 의 최대 설정값을 설정한다. 
- ThreadLimit 가 ThreadsPerChild 보다 훨씬 높게 설정된다면, 여분의 미사용 공유 메모리가 할당될 것이다. 
- ThreadLimit 과 ThreadsPerChild 모두 시스템이 감당할 수 있는 값 보다 높게 설정하면, 
     아파치가 기동되지 않거나 시스템이 불안정하게 될 수 있다.  
- 이 값은 최대 예상 ThreadsPerChild 의 설정보다 높게 설정하면 안된다.

 

Maxclient 를 늘리기 위한 설정 ( Worker 방식일 경우 - 2.x버전에 한정됨) 
  1) Maxclient 는  StartServers * ThreadsPerChild 로 정해짐 
      ( MaxClient = StartServer * ThreadsPerChild ) 
    => worker 방식은 각각의 자식프로세스별로 여러개의 thread를 생성해서 요청을 처리하기 때문 
  2) Maxclient 를 늘리기 위한 설정 
    apache소스디렉토리/server/mpm/worker/worker.c 파일 을 수정 
    ( define DEFAULT_SERVER_LIMIT 16 값을 늘려준다 ) 

 


* 참고1.

 대부분 prefork 방식이 기본적으로 사용되며, 사용자가 많은 경우에는 worker방식을 사용한다.
 요즘 몇 몇 사이트에서 사용자 폭주로 인하여 worker 방식을 사용하는 경우가 간혹 있다.
 

*참고2.

apache 설치 시에 아래와 같이, 반드시  --with-mpm=worker 옵션을 설정 하고 설치한다. 
이 옵션을 주지 않을 경우, Default인 Prefork방식으로 설치된다(Linux에 한함)

================================================================ 
./configure --prefix=/home/paint/apache-2.2.15 --enable-mods-shared=all --enable-module=so --enable-so  --with-mpm=worker 
================================================================

 

* 참고3.

현재 worker 모듈 설치 되었는지 확인하는 방법

- "httpd -l" 명령으로 현재 설치된 Apache가 worker방식으로 설치되었는지 확인할 수 있다.

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

# httpd -l

Compiled in modules: 
  core.c 
  worker.c <----------------------요거임
  http_core.c 
  mod_so.c

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

 

또는, httpd -V 명령으로 확인 가능하다. (V는 대문자)

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

# httpd -V

Server version: Apache/2.2.15 (Unix)

Server built:   Jun 30 2010 16:59:45 
Server's Module Magic Number: 20051115:24

Server loaded:  APR 1.4.2, APR-Util 1.3.9

Compiled using: APR 1.4.2, APR-Util 1.3.9

Architecture:   32-bit

Server MPM:     Worker <------------------------요거

  threaded:     yes (fixed thread count)

    forked:     yes (variable process count)

Server compiled with....

 -D APACHE_MPM_DIR="server/mpm/worker" 
 -D APR_HAS_SENDFILE 
 -D APR_HAS_MMAP 
 -D APR_HAVE_IPV6 (IPv4-mapped addresses enabled) 
 -D APR_USE_SYSVSEM_SERIALIZE 
 -D APR_USE_PTHREAD_SERIALIZE 
 -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT 
 -D APR_HAS_OTHER_CHILD 
 -D AP_HAVE_RELIABLE_PIPED_LOGS 
 -D DYNAMIC_MODULE_LIMIT=128 
 -D HTTPD_ROOT="/home/paint/apache-2.2.15" 
 -D SUEXEC_BIN="/home/paint/apache-2.2.15/bin/suexec" 
 -D DEFAULT_SCOREBOARD="logs/apache_runtime_status" 
 -D DEFAULT_ERRORLOG="logs/error_log" 
 -D AP_TYPES_CONFIG_FILE="conf/mime.types" 
 -D SERVER_CONFIG_FILE="conf/httpd.conf"

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

Technical Note/apache & tomcat

아파치의 Timeout의 시간이란? 

아파치 제공문서(설정파일 포함)에 의하면, 

Timeout 300 

-The number of seconds before receives and sends time out. 
-The TimeOut directive currently defines the amount of time Apache will wait for three things: 
  1.The total amount of time it takes to receive a GET request. 
  2.The amount of time between receipt of TCP packets on a POST or PUT request. 
  3.The amount of time between ACKs on transmissions of TCP packets in responses.
 

인데 번역(?)하자면, 

- 받고/보내는 time out의 초단위 시간 
- 아파치가 다음의 3가지 사항을 기다리는 시간으로 정의됨 
  1. (아파치가 클라이언트로 부터) GET 요청(GET 방식의 URL요청)을 받는데 걸리는 시간. - (요청) 
  2. (아파치가 클라이언트로 부터) POST나 PUT 방식의 요청에 대한 TCP 패킷을 받는데 걸리는 시간. - (요청) 
  3. (아파치가 클라이언트에게) TCP 패킷을 전송할때 ACKs 세그먼트를 보내는데 걸리는 시간 - (응답) 

위의 내용을 쉽게 이해할것 같지만, 
TCP/IP 네트워크에서 "TCP 3 way handshaking"라는 TCP의 제어기능을 이해해야만 
Timeout의 개념을 알 수 있습니다. 

질문의 내용은 3번의 응답에 관한 내용인데, 파일을 다운로드할때는 3번의 응답 과정이 모두 끝나고 실제로 Data 패킷을 전송하는 단계이므로 Timeout과 관계가 없습니다. 
그렇게 때문에 아주 덩치 큰 파일(100M 이상)을 HTTP 프로토콜을 이용해서 다운로드할때 5분 이상이 걸려도 Time out이 되지 않는 이유가 여기에 있습니다. 

질문에 대한 결론은 
Timeout 지시자는 실제로 파일을 다운로드하는 Data 패킷 전송과 관계가 없으며, 앞에서 설명한 1,2번의 요청에 걸리는 시간과 3번의 응답에 걸리는 시간과 관계가 있습니다. 

1, 2번의 요청에 관한 내용은 따로 설명하지 않아도 이해할 수 있는 부분이므로 생략하고, 좀더 개념적으로 확실히(?) 알기 위해서 "TCP 3 way handshaking"이라는 녀석에 대해서 
조금 알아보죠.. 

예를들어, 
TCP/IP 네트워크에서는 A호스트에서 B호스트로 접속하여 Data 패킷을 전송할때 단 한번의 접속으로 Data 패킷을 보내는 것이 아니라 모두 3번에 걸쳐서 이루어집니다. 

1단계 : A ----- 접속시도(SYN, SYNchronize sequene number 보냄) -----> B 
2단계 : A <---- 확인단계(SYN, ACK(ACKnowledgment 보냄) ----------- B 
3단계 : A ----- 전송시작(ACK, Data 전송시작) --------------------> B 

이와 같이 TCP 네트워크는 패킷을 순서대로 맞게 전달하기 위해서 이런 제어기능을 하게됩니다. 

3번에 걸쳐서 마치 악수하듯이 이루어진다해서 "3 way handshaking"이라는 말이 나온것 같군요. 

헷갈리지 않아야할 점은 앞에서 Data 전송이라고해서 요청에 대한 응답에 만 해당되는것이 요청도 이와 같이 3단계를 걸쳐서 이루어 집니다. 

좀더 정확하고 많은 정보를 원한다면 TCP/IP 네트워크에 대한 전문서적을 읽어보시길 바랍니다(필자는 이정도 수준이라서...^.9). 

질문의 내용과 연결해서, 
1, 2번의 요청, 즉 "xxx 받는데 걸리는 시간"은 위의 3단계 모두에 해당되는 시간이고, 3번의 응답, 즉 "xxx ACKs 세그먼트를 보내는데 걸리는 시간"은 2단계에 해당되는 시간을 의미합니다. 

따라서 
이미 질문에 대한 답이 나와 있듯이 "파일을 다운로드하는 경우"는 이미 2단계가 모두 끝나고 3단계를 의미하므로 아파치의 Timeout 과 관련이 없습니다. 

Timeout 300 

의 의미는 

- URL GET 요청이나 POST, PUT 요청을 할때 네트워크 환경이 지나치게 너무 느린 환경(아주 멀리 떨어져 있는 아주 느린 환경)에서 접속을 할때 걸리는 시간이 300초가 넘어가면 Timeout이 됩니다. 
- 또한 다운로드가 아닌 ACKs 세그먼트 메시지를 보낼때도 마찬가지로 너무 느린 환경이나 네트워크 장애로 인해서 시간이 300초를 초과할 경우에 Timeout이 됩니다. 

간혹 멀리 떨어진 외국의 싸이트를 접속하다보면 갑작스런 네트워크 장애로 Timeout 이라는 Error 메시지를 본 경험이 있는데 이와 같은 이유로 Timeout이 되기도 합니다. 

이와 관련된 
HTTP 1.1 status codes(RFC 2068) 

"408"   ; Request Time-out
"413"   ; Request Entity Too Large
"414"   ; Request-URI Too Large
"502"   ; Bad Gateway
"504"   ; Gateway Time-out

408 Request Timeout

   The client did not produce a request within the time that the server
   was prepared to wait. The client MAY repeat the request without
   modifications at any later time.

413 Request Entity Too Large

   The server is refusing to process a request because the request
   entity is larger than the server is willing or able to process. The
   server may close the connection to prevent the client from continuing
   the request.

   If the condition is temporary, the server SHOULD include a Retry-
   After header field to indicate that it is temporary and after what
   time the client may try again.

414 Request-URI Too Long

   The server is refusing to service the request because the Request-URI
   is longer than the server is willing to interpret. This rare
   condition is only likely to occur when a client has improperly
   converted a POST request to a GET request with long query
   information, when the client has descended into a URL "black hole" of
   redirection (e.g., a redirected URL prefix that points to a suffix of
   itself), or when the server is under attack by a client attempting to
   exploit security holes present in some servers using fixed-length
   buffers for reading or manipulating the Request-URI.

502 Bad Gateway

   The server, while acting as a gateway or proxy, received an invalid
   response from the upstream server it accessed in attempting to
   fulfill the request.

504 Gateway Timeout

   The server, while acting as a gateway or proxy, did not receive a
   timely response from the upstream server it accessed in attempting to
   complete the request.

원문 : http://yesyo.com/mintbbs/bbs/board.php?bo_table=linux&wr_id=208&sfl=&stx=&sst=wr_hit&sod=asc&sop=and&page=9

Technical Note/apache & tomcat
도메인들 간 쿠키 공유하기 (Sharing cookies across multiple domains hosted on different servers)

왜 필요한가?

웹사이트나 웹어플리케이션을 개발할때 필요에 따라 단일 도메인이나 별개의 도메인 또는 서브도메인을 사용할수 있다. 그리고 일반적으로 보안이나 성능, 서버부하 등에 대한 이유로 서브 도메인 형식으로 구성한다

구글은 이 개념을 이해하는데에 좋은 예이다. 구글은 메일, 검색, 구글+ 등의 서비스에 대해 서브도메인 형식으로 서비스를 제공한다. 구글은 자신들의 서비스에 단일 로그인 정보를 사용하기 때문에 서비스 간에 로그인 정보를 공유할수 있는 방법이 필요하다. 보통 로그인 데이터는 브라우저 세션에 저장된다. 이때 각 서브도메인은 별개의 세션을 유지하기 때문에 세션 기능을 이용하는한 로그인 정보를 공유하는것은 불가능하다. 바로 이점이 쿠키가 필요한 이유다. 쿠키값은 여러 사이트에서 접근할수 있으며 각각의 어플리케이션에 필요한 정보를 유지할수 있다. PHP는 도메인들간 쿠키를 공유하는 기능을 지원한다

도메인 간 쿠키 공유의 제한점

비록 도메인 간에 쿠키값을 저장하고 접근하는것이 가능하긴 하지만 몇가지 제한사항이 있다. 다른 도메인의 쿠키값은 저장할수 없다는 것이다. 단지 동일한 사이트의 서브도메인 쿠키를 이용할수 있을 뿐이다.

동일한 서버에서 서비스되는 www.example1.com 와 www.example2.com 이라는 사이트가 있다고 가정하자. 이 두개는 같은 서버에 있기는 하지만 도메인 이름이 다르다. 따라서 이 두 사이트간 쿠키를 공유할 수는 없다. 하지만 www.example.com 과 이 사이트의 서브도메인을 활용한 사이트, 예를들어 app.example.com, demo.example.com 를 사용한다면 이 세 도메인간의 쿠키는 공유될수 있다. 이때 이 서비스가 반드시 동일한 서버에 있을 필요는 없으며, 모든 도메인이 하나의 top domain에 속할 필요는 없다.


Technical Note/AGILE

프로젝트에서의 마일스톤이란?

milestone : 중요한 단계, 일정표, 이정표, 주요 관리점.
프로젝트 일정 관리를 위한 마일스톤
흘러드는 모든 단위 작업이 끝나기 전까지는 종료된 것으로 생각하지 않는 단위 작업 또는 사건.

  • 특정한 프로젝트와 관련하여 어떤 중요한 시점
  • '시점'이라 하여 하나만 존재하는 것이 아닌 특정 부서에서 중요한 이슈가 있는 시점들은 모두 마일스톤으로 해석하여 추가
  • 멈춰서서 자신 및 프로젝트 전체의 위치를 체크하고 판단할 수 있도록 함이 리더의 역량
  • 부정확한 마일스톤은 프로젝트를 지연시키고 예산을 초과하게 하며, 그룹 사이에 혼란과 불심을 심어주므로 명확하고 간결해야 한다.

마일스톤의 일정

  • 하나의 마일스톤을 위한 적당한 기간은 4주에서 8주이다.
  • 허나, 새로운 팀(업무 역량에 대한 판단이 안 설 경우)이거나, 익숙하지 않은 프로젝트의 경우 기간이 작은 마일스톤이 필요
    • 맡고 있는 업무에 집중할 수 있도록 팀 유지
    • 결정적인 시기에 프로젝트 가시성 증가
    • 작은 마일스톤의 양을 팀의 숙련도와 다루고 있는 주제의 친숙도에 맞추어야 한다.

마일스톤 정확하게 만들기

  • 일정의 정확성을 증가시키려면 최대한으로 불필요한 요소를 제거
  • 불확실한 요소 제거를 위해 마일스톤의 애매함을 피하는데 전력을 기울여야 한다.
    • 마일스톤 애매함 - 너무 많은 기능을 하나의 마일스톤 안에 쑤셔넣는 것

마일스톤 차트

* 출처 : http://blog.naver.com/hnc21?Redirect=Log&logNo=100062798967

마일스톤 차트는 팀이 프로젝트의 최종산출물을 생산하기 위한 목표를 설정하고,
그 목표를 중요한 하위 목표들로 세분하여 각각의 하위 목표들에 마감시한을 할당한 것으로,
프로젝트에 대한 전체적인 그림을 보고자 할 때 사용됨.

[양식설명]

  • 구분 : WBS 1수준(level)의 업무를 기입
  • 세무업무 : 마일스톤으로 지정한 업무를 기입
  • 기간 : 일/주/보름/월/분기 등으로 구분하여 표현
  • 완료된 마일스톤, 예정된 마일스톤, 일정을 넘겨 완료된 마일스톤을 위와 같이 분리하여 표현

마일스톤(Milestone)
http://heekyung156.blog.me/150074378873
이 글은 위 자료의 글을 가져왔음을 밝힘니다.

Technical Note/JAVA

NullPointerException을 예방하는 방법

댓글을 달아주세요

NullPointerException은 null 때문에 발생하는 Runtime Exception입니다.

null인 객체에 접근하여 의도치 않게 NPE가 발생할 수 있지만 더 중요한 문제는 null 자체의 의미가 모호해 다양한 버그를 만들어냅니다. 예를 들어 메소드의 호출 결과로 null이 반환 되었을 경우 데이터가 없음을 의미할 수도 있지만 실패를 의미할 수도 있습니다. 심지어 성공을 의미하는 경우도 있을 수 있죠.

1
2
3
4
Map<String, String> map = new HashMap<String, String>();
map.put("hello", null);
map.get("hello");   // "hello" key의 value인 null을 return
map.get("nice");    // "nice" key가 없으므로 null을 return

위의 경우도 key에 대한 value가 null인지, key가 없어서 null을 return 한건지 return 받은 null 값을 가지고는 그 의미가 모호합니다. 누가 null을 만들었는지 참 얄밉지만 어쨋든 null 체크를 제대로 안했던지, 설계상의 결함이던지 null로 인한 버그는 결국 개발자 책임입니다.

똥이 무서워서 피하기보단 더러워서 피한다고… NPE를 예방하는 방법에 대해 정리해봤습니다.
들어가기에 앞서 null에 대한 프로그래머 대가들의 한마디…

“Null Sucks!”
- Doug Lea : Concurrent Programming In Java 저자 & JDK의 concurrency utilities 개발자

“I call it my billion-dollar mistake.”
- C.A.R. Hoare 2009년 어느 컨퍼런스에서.
1965년 Algol W 언어에서 처음으로 null reference를 만든 장본인 (Quick Sort도 발명)

코딩 습관 들이기

객체가 언제든 null일 수 있다는 판단하에 접근하면 도움이 될 것 같습니다.

1. equals 메소드 사용시
문자열 비교시 non-null String 기준으로 비교합니다. equals 메소드는 symmetric하므로 a.equals(b)와 b.equals(a)가 동일합니다. 그렇다면 null 일 수 있는 객체에서 equals 메소드 호출은 피하는게 낫겠죠?

1
2
3
4
5
6
public void doSomething() {
   // name이 null일 경우, NPE 발생!
   if (name.equals("BAD")) {
      // do something
   }
}
1
2
3
4
5
6
public void doSomething() {
   // name이 null이어도 NPE 발생 안함
   if ("BAD".equals(name)) {
      // do something
   }
}

다만 equals를 통과한 null 객체가 if문 안에서 메소드 호출 등의 이유로 다시 사용된다면 동일한 NPE가 발생할 수 있습니다. 이럴 경우엔 미리 null 체크를 하는게 좋겠죠?

2. toString()보다는 valueOf()를 사용할 것
1번 처럼 null 일 수 있는 객체에서 메소드 호출은 NPE 발생 위험이 있겠죠? static으로 제공되는 valueOf()를 사용하면 null을 Parameter로 넘겨도 null을 return할 뿐 NPE는 발생하지 않습니다. (valueOf()는 String, Boxed Primitives 클래스(Integer, Double 등)에서 static 메소드로 제공 됨)

1
2
3
BigDecimal bd = getPrice();
System.out.println(String.valueOf(bd)); // NPE 발생안함
System.out.println(bd.toString()); // NPE 발생

3. 메소드에서 null return하지 않기
가장 간단한 방법은 메소드 구현시 의미없는 null을 return하지 않도록 처리합니다. null이 아닌 빈 문자열 또는 빈 콜렉션을 return 하도록 합니다.

1
2
3
4
5
6
7
8
9
10
11
public List<User> getUsers() {
   // ...
 
   Result result = executeNamedQuery(GET_ALL_USERS);
 
   if (!result.isEmpty()) {
      return result;
   }
 
   return Collections.EMPTY_LIST; // or EMPTY_SET or EMPTY_MAP, etc. Depending on your return type
}

4. null을 Parameter로 넘기지 말 것
null을 Parameter로 넘길 경우 받는 쪽에서도 어찌보면 불필요한 null 체크가 필요하겠죠? null 자체에 어떤 특별한 의미가 없다면 주지도 받지도 맙시다.

5. 불필요한 autoboxing, unautoboxing 피하기 & Object 보다는 기본형 사용하기
Boxed Primitives 클래스와 기본형(primitive) 타입간에 자동으로 autoboxing, unboxing을 해주는데요. 자동으로 기본형 변환을 해주기 때문에 헷갈리기 싶습니다. (그냥 기본값이 들어가겠거니…) 가능하면 null reference를 가질 수 있는 객체가 아닌 자바 기본형(Primitive)을 이용하는 것도 방법입니다.

1
2
3
Person ram = new Person("ram");
// getPhone()에서 return된 결과가 null일 경우 NPE 발생
int phone = ram.getPhone();
1
2
3
4
5
6
private static Integer count;
 
// NPE 발생
if( count <= 0) {
  System.out.println("Count is not started yet");
}

6. Chaining 메소드 호출 자제하기

1
String city = getPerson(id).getAddress().getCity();

중간에 return 받은 값이 null일 경우 NPE가 발생하며 Stack Trace에서도 해당 line 위치만 출력되기 때문에 어디서 에러가 발생했는지 디버깅하기도 어렵습니다.

Library 이용하기

Apache Commons lang, Google Guava(예전 Goolge Collections) 등 null safe한 method를 이용하는 방법입니다. 자세한 사용은 Library의 JavaDoc을 읽어보길 권합니다.

1. Apache Commons의 StringUtils
String의 null 체크를 간단히 할 때 많이 사용하는 클래스입니다. StringUtils.isNotEmpty(), isBlank(), isNumeric(), isWhiteSpace() 등이 있죠.

1
2
3
4
5
6
7
8
9
10
11
// StringUtils methods are null safe, they don't throw NullPointerException
System.out.println(StringUtils.isEmpty(null));
System.out.println(StringUtils.isBlank(null));
System.out.println(StringUtils.isNumeric(null));
System.out.println(StringUtils.isAllUpperCase(null));
 
Output:
true
true
false
false

2. Guava의 Optional 클래스 이용하기
Optional는 nullable한 T를 non-null 값으로 대체시키기 위한 방법인데요. (말이 어렵죠?)
Optional 객체는 non-null인 T reference를 포함하거나 아무것도 포함하고 있지 않습니다.

한마디로 Optional 객체는 명시적으로 null 값을 갖지 않는다는거죠.
NullObject Pattern을 일반화시켰다고나 할까요? 바로 이 점을 이용해서 NPE를 예방합니다.

– absent : 아무 것도 포함하고 있지 않은 상태
– present : non-null 값을 갖은 상태

1) Optional 객체의 생성 (static 메소드)

Optional.of(T)
- T로 받은 non-null 값을 포함하는 Optional 객체 반환, T가 null 일 경우 NPE 발생
Optional.absent()
- 아무것도 포함하고 있지 않는 absent Optional 객체 반환
Optional.fromNullable(T)
- T로 받은 값이 non-null일 경우 present로, null일 경우 absent로 처리한 Optional 객체 반환

2) Optional 객체를 다루기 위한 메소드

boolean isPresent()
- Optional 객체가 non-null 인스턴스를 포함할 경우 true 반환
T get()
- Optional 객체가 present 일 경우 포함하고 있는 인스턴스를 반환, absent일 경우 IllegalStateException 발생
T or(T)
- Optional 객체의 present 값을 반환. 만일 값이 없을 경우 명시한 T를 반환 (기본값)
T orNull()
- Optional 객체의 present 값을 반환, 만일 값이 없을 경우 null을 반환. fromNullable의 역임
Set asSet()
- Optional 객체에서 포함하고 있는 인스턴스의 변경 불가능한 싱글톤 Set을 반환. 만일 인스턴스가 없다면 변경불가능한 Empty set을 반환

3) 간단 예제

1
2
3
4
5
6
7
Optional<Integer> possible = Optional.of(5);
System.out.println(possible.isPresent());
System.out.println(possible.get());
 
Output:
true
5

4) 응용 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Integer a = 10;
Integer b = null;
 
Optional present = Optional.fromNullable(a);
Optional absent = Optional.fromNullable(b);
 
System.out.println("a is present : " + present.isPresent());
System.out.println("b is present : " + absent.isPresent());
System.out.println("a value : " + present.or(0));
System.out.println("b value : " + absent.or(0));
 
Output:
a is present : true
b is present : false
a value : 10
b value : 0

http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/Optional.html

https://code.google.com/p/guava-libraries/wiki/UsingAndAvoidingNullExplained#Optional

요건 Wrapper 메소드 형식으로 Optional 객체를 반환하게끔 사용하면 좋을 것 같네요.

3. Guava의 Preconditions 또는 NullPointerTester 클래스 이용하기
http://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/base/Preconditions.html#checkNotNull(T)
http://guava-libraries.googlecode.com/svn-history/r144/trunk/javadoc/com/google/common/testing/NullPointerTester.html

Nullness Annotation 활용하기

메소드의 return값 또는 파라미터의 null 허용여부를 Annotation을 이용하여 지정합니다. 코딩 습관 들이기 3, 4번 항목을 제약하기 위해 사용하는데요.

IDE에서 지원하는 @Nullable, @NotNull 등의 Annotation을 사용하면 코드에서 NPE 발생 가능여부를 미리 경고해줍니다. 또 해당 Annotation 제약사항을 위반했을 경우 컴파일시 NPE가 아닌 IllegalArgumentException 또는 IllegalStateException이 발생됩니다. Argument로 NotNull이어야 한다고 정했는데 Null이 들어왔다면 의미상 NPE보다는 잘못된 Argument가 맞겠죠?

현재 ItelliJ, Eclipse IDE 및 Find Bugs에서는 서로 다른 Annotation 라이브러리를 사용하는데요. JCP에 Software Defect Detection하기 위한 Annotation이 JSR-305로 요청 중이나 현재 dormant(중단) 상태네요. 언제 JDK에 포함될런지는 모르겠네요 :’(

상세 사용법은 아래를 참고하세요.

1. Eclipse (Juno 이상)
http://help.eclipse.org/juno/index.jsp?topic=%2Forg.eclipse.jdt.doc.user%2Ftasks%2Ftask-using_null_annotations.htm

2. IntelliJ
http://www.jetbrains.com/idea/documentation/howto.html

3. FindBugs
http://findbugs.sourceforge.net/manual/annotations.html

4. IntelliJ 예제
– 프로젝트에 /REDIST/annotations.jar 추가
– Project Settings > Inspections > Probable bugs 체크 > Constant conditions & exception 체크 > Sueggest @Nullable annotation …. 체크

1
2
3
4
5
6
7
8
9
10
11
12
@Nullable
private String getDefinition() {
   // ...
}
 
public void doSomething() {
   if (getDefinition().equals("DEFINITION")) {
      // ...
   }
 
   // ...
}

@Nullable인 getDefinition()는 NPE가 발생할 수 있다.’고 IntelliJ에서 경고합니다.

1
2
3
4
5
6
7
8
9
10
private User findUser(@NotNull final String id) {
   // ...
}
 
public void doSomething() {
   // try invoking with null parameter
   User user = findUser(null);
 
   // ...
}

@NotNull 사용한 곳에서 null 값을 전달하고 있다고 IntelliJ에서 경고합니다.

기타

1. DB 테이블 컬럼 생성시 default값 설정하기
사용자로부터 입력 받은 값 중 null을 허용하더라도 default값을 설정했다면,
추후 해당 테이블에서 값을 불러와 사용할 경우 null을 예방할 수 있겠죠.

2. Null Object Pattern, Factory Pattern 사용하기

3. Spring MVC에서 지원되는 Bean Validation에서 @NotNull 사용하기 (JSR-303)

결론

1. 개발자가 코딩시 주의하여 NPE를 발생시키는 코딩 습관은 자제한다.
2. 간단한 Apache Commons나 Guava Library의 null safe 관련 클래스를 학습한 후 적용한다.
3. IDE에서 지원하는 Annotation 등을 이용해서 NPE 발생 부분은 미리 캐치하여 예방한다.

# 참고 사이트
http://javarevisited.blogspot.kr/2013/05/ava-tips-and-best-practices-to-avoid-nullpointerexception-program-application.html
http://howtodoinjava.com/2013/04/05/how-to-effectively-handle-nullpointerexception-in-java/
http://isagoksu.com/2009/development/java/how-to-avoid-nullpointerexceptions-npe/
https://code.google.com/p/guava-libraries/wiki/UsingAndAvoidingNullExplained
http://www.scottlogic.com/blog/2013/09/09/nullable-in-kepler.html


1 ··· 18 19 20 21 22 23 24 ··· 27
블로그 이미지

zzikjh