Item75. 예외의 상세 메시지에 실패 관련 정보를 담으라.

  • 예외를 잡지 못해 프로그램이 실패하면 예외의 stack trace 정보를 자동으로 출력한다.
  • stack trace 정보는 예외 객체의 toString method를 호출해 얻는 문자열로 보통 예외 클래스 이름 뒤에 상세 메시지가 붙는 형태이다. 따라서 최대한 예외의 toString method에 실패 원인에 관한 정보를 가능한 많이 담아 반환하는게 좋다.

  • 즉 실패 순간의 상황에 관련된 객체의 상태, 매개변수 등을 담아서 출력해주는게 실패 원인을 분석할떄 좋다.

  • 당연한 말이지만 최종 사용자(고객)에게 보여줄 오류 메시지는 친절한 안내 메시지로 가독성이 중요한 반면 , 예외에는 가독성이 다소 떨어지더라도 실패 원인을 분석할때 유용한 정보들을 포함시켜 보여주는게 중요하다.

  • 실패를 적절히 포착하려면 필요한 정보를 예외 생성자에서 모두 받아서 상세 메시지까지 미리 생성해두는 방법도 괜찮다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

public IndexOutOfBoundsException(int lowerBound,int upperBound,int index){

// 실패를 포착하는 상세 메시지를 생성

super(String.format(
"최솟값 : %d , 최댓값: %d , 인덱스 : %d" ,
lowerBound , upperBound , index ));

// 프로그램에서 이용할 수 있도록 실패 정보를 저장해둔다.
this.lowerBound = lowerBound;
this.upperBound = upperBound;
this.index = index;
}

이렇게 실패 상황의 정보를 예외 클래스에 담아두면 예외 클래스 사용자가 메시지를 만드는 작업을 중복하지 않아도 된다는 장점을 가지고 있다.

또한 예외는 실패와 관련된 정보를 얻을 수 있는 접근자 method를 적절히 제공하는게 좋다.

1
2
3
getLowerBound();
getUpperBound();
// 실패 상황시 필드정보 접근자 method
Read more

Item74. method가 던지는 모든 예외를 문서화하라.

  • method가 던지는 검사 예외는 그 method를 올바로 사용하는데 중요한 정보이므로, 각 예외가 발생하는 상황을 자바독의 @throws 태그를 사용하여 정확히 문서화하자.

  • 공통 상위 클래스(ex)Exception,Throwable) 하나로 문서화하는 경우, method 사용자에게 예외에 대처할 수 있는 힌트를 주지 못하며 다른 예외들까지 삼켜버릴 수 있어 API 사용성을 크게 떨어트린다.

  • 비검사 예외도 검사예외처럼 문서화하면 개발자가 해당 오류가 나지 않도록 코딩하게 유도하므로 좋은 습관이다. 다만 비검사 예외는 method 선언의 @throws 목록에 넣지 말아야 한다.

  • 인터페이스 method의 경우 특히 발생 가능한 비검사 예외를 문서화해두는 것이 하위 구현체가 일관되게 동작하도록 하는데 중요하다

  • 한 class에 정의된 많은 method가 같은 이유로 같은 예외를 던진다면 class 레벨에 문서화해두는 방법도 있다.

Item73. 추상화 수준에 맞는 예외를 던지라

예외 번역 (Exception Translation)

  • method가 저수준 예외를 처리하지 않고, 바깥으로 예외를 전파시킬 때 내부 구현 방식을 드러내어, 윗 레벨 API를 오염시킨다.
    다음 릴리스에서 구현 방식을 바꾸면 기존 client 프로그램을 깨지게 만들수도 있다.

  • 이 문제를 피하려면 상위계층에서는 저수준 예외를 잡아, 자신의 추상화 수준에 맞는 예외로 바꿔 던져야 하는데 이를 예외 번역이라고 한다.

1
2
3
4
5
try{
}catch(LowerLevelException e){
//추상화 수준에 맞게 예외를 잡아 번역한다.
throw new HigherLevelException(...);
}

예외 번역의 예시로서, AbstractSequentialList 의 get method는 List 인터페이스의 get method 명세에 명시된 예외로 번역한다

1
2
3
4
5
6
7
8
public E get(int idx){
ListIterator<E> i = listIterator(idx);
try{
return i.next();
}catch(NoSuchElementException e){
throw new IndexOutOfBoundsException("인덱스: " + index);
}
}
Read more

Item 72. 표준 예외를 사용하라

표준 예외를 재사용함으로서 얻는 장점

  1. 많은 개발자가 이미 익숙해진 예외를 그대로 사용함으로서, 다른 개발자가 API를 사용하기 편해진다.
  2. 예외 클래스 수가 적을수록 메모리 사용량도 줄어들고, 클래스를 적재하는 시간도 적게 걸린다.

가장 많이 사용되는 예외 예시

  1. IllegalArgumentException : 호출자가 인수로 부적절한 값을 넘길 때, 던지는 예외
  2. IllegalStateException : 대상 객체의 상태가 호출된 메소드를 수행하기에 적합하지 않을 때 주로 던진다. ex) 초기화되지 않은 객체
  3. ConcurrentModificationException : 단일스레드에서 사용하려고 설계한 객체를 여러 스레드가 동시에 수정하려 할 때 던진다.
  4. UnsupportedOperationException : client가 요청한 동작을 대상 객체가 지원하지 않을때 던진다. ex) 삼입만 가능한 List구현체에 remove method호출
  • Exception, RuntimeException , Throwable, Error class의 경우 여러 예외들을 포괄하는 클래스임으로 직접 재사용하는 것을 지양하자.

  • IllegalStateException 과 IllegalArgumentException 중에 어떤 표준 예외를 쓸지 선택하기 어렵다면, 인수값이 무엇이든 실패한 경우는 IllegalStateException , 그렇지 않은 경우는 IllegalArgumentException을 던지자.

Read more

이중화 기술

이중화 기술을 사용하는 목적

  • 이중화를 하지 않았을때 SPoF (Single Point Of Failure , 단일 장애점) 문제가 발생할 수 있다, 인프라를 구성하는 요소가 복수 개 이상으로 인프라를 구성해 특정 인프라에 문제가 발생하더라도 이중화된 다른 인프라를 통해 서비스가 지속되도록 해준다.(Fault Tolerance 보장)

이중화의 목적

  • SPoF를 제거하여, 시스템의 고가용성 보장 ( Fault Tolerance )
  • 이중화 구성 방식은 Active-Active 또는 Active-Standby 형태로 구성된다.
  • Active-Active로 구성시 이중화된 인프라에서 서비스 요청을 동시 처리할 수 있으므로, 처리 가능한 전체 용량이 증가하고, 장비간 네트워크 연결이나 회선의 대역폭이 증가한다. 증가된 인프라 용량을 기준으로 서비스시 장애가 발생하였을때 정상적인 서비스가 불가능해진다.
  • 단일 스위치의 물리적인 포트 여러개를 하나의 논리적인 포트로 구성하여 대역폭을 확장시킴

LACP 대역폭 확장을 통해 아래와 같은 기능이 제공된다. (IEEE 802.1AX-2008)

  1. 링크 사용률 향상
  2. 향상된 장애 회복
  • 모든 물리 포트를 Active 상태로 사용한다 ( Active-Active 방식 )
  • 물리 네트워크 인터페이스 일부에서 장애가 생기더라도, 나머지 물리 인터페이스로 서비스를 유지해준다.

주의점

  1. 논리 인터페이스의 대역폭을 서비스에 필요한 전체 트래픽 기준으로 서비스 트래픽을 산정하면 안된다.
  2. 물리 인터페이스간의 속도가 동일해야 한다.

LCAP 동작 방식

  • LACPDU (LACP Data Unit)이라는 프레임을 사용하여, Muticast 통신방식으로 한 장비와 LACPDU 프레임을 주고받는다 (1:1 구성방식)
  • 만약 여러개의 장비 (N:M) 간 LACP를 구성하고자할떄는 별개의 프로토콜 필요(MC-LAG protocol )

LACP 모드

  1. Active Mode : LACPDU를 먼저 송신하고 상대방이 LACP로 구성된 경우 , LACP를 구성
  2. Passive Mode: LACPDU를 송신하진 않지만, LACPDU를 수신받으면 응답해 LACP를 구성
  • LACP를 구성한 모든 장비에서 LACPDU 프레임을 보내는게 아니라, 한쪽은 Active 한쪽은 Passive Mode로 설정해도 LACP가 구성된다. 단 두 장비 모두 Passive Mode 인 경우에는 당연히 LACP 연결이 되지 않는다.
  • 일반적으로 1~8개의 물리인터페이스를 LACP로 하나의 논리 인터페이스로 구성하지만 벤더에 따라 다름.

LACP와 PXE

  • 서버와 Active-Active 형태로 네트워크 인터페이스 이중화시에도 LACP 프로토콜이 사용된다.

  • 서버 네트워크 인터페이스를 하나의 논리 포트로 묶는 과정을 Bonding 또는 Teaming이라고 부르며 OS에서 설정하게 됨.

  • LACP 설정은 본딩과 티밍에서 액티브-액티브로 사용하기 위한 옵션 설정임으로, 운영체제 설치 전에는 LACP를 사용할 수 없다.

  • 서버가 OS를 설치하기 전은 LACP를 사용할 수 없음. 따라서 LACP 로 구성하려는 서버를 PXE로 OS 를 설치하고자 할때는 일반 인터페이스로 구성해 OS를 설치하고 LACP 설정을 다시 한 후 스위치 포트 설정을 다시 해야 함.

  • LACP protocol에 옵션을 주어서, OS 설치전에는 한개의 네트워크 인터페이스만 활성화, LACPDU 패킷을 수신하기 시작하면 두개의 네트워크 인터페이스를 활성화하여 LACP 로 구성할 수 있도록 옵션을 줄 수 있다.

벤더 기술명
Cisco lacp suspend-individual
Arista lacp fallback
Extreme lacp fallback
HP lacp edge-port
( PXE (Pre-Boot Environment) : 네트워크 인터페이스를 통해 컴퓨터를 부팅할 수 있게 해주는 환경 )
Read more

Item71. 필요 없는 검사 예외 사용은 피하라

  • 검사 예외를 제대로 활용하면 API를 사용하는 client가 예외에서 발생한 문제를 처리하여 프로그램의 안정성을 높일 수 있으나, 불필요한 검사 예외는 프로그램 복잡성만 늘린다.

예를 들면 다음과 같이 검사 예외를 던졌는데 client가 아래와 같이 처리해야 된다면 오히려 코드만 불필요하게 길게 만든다.

1
2
3
catch(TheCheckedException e){
throw new AssertionError();
}
1
2
3
4
catch(TheCheckedException e){
e.printStackTrace();
System.exit(1);
}
  • 그외에 단점으로 검사 예외를 던지는 method는 Stream 안에서 직접 사용할 수 없다.
Read more

Item70. 복구할 수 있는 상황에는 검사 예외를, 프로그래밍 오류에는 런타임 예외를 사용하라

-Java는 문제 상황을 알리는 타입 (Throwable)으로 3가지 ( 검사 예외, 런타임 예외 , 에러 ) 를 제공한다.

(ref- https://butter-shower.tistory.com/87 )

  • RuntimeException은 예외처리를 하지 않아도 컴파일 가능한 비검사형 예외이며, 검사형 예외(ex) IllegalAccessException)는 예외 처리를 하지 않으면 컴파일 오류가 터지므로 꼭 처리해주어야 하는 예외이다.

  • Error class도 비검사형 예외에 속하며, Exception class 와 차이점은 시스템레벨에서 비정상적인 상황이 발생한 심각한 수준의 오류라고 한다. 반면 Exception은 개발자가 구현한 로직에서 발생한 오류이다.

Read more

Item69. 예외는 진짜 예외 상황에만 사용하라

다음과 같이 예외를 예외 상황이 아닌 경우에 사용하는 것을 지양하라고 언급하고 있다.

1
2
3
4
5
6
7
8
9
10
// 굳이 예외를 써서 루프를 종료 
try{
int i = 0;
while(true){
range[i++].climb();
}
}
catch(ArrayIndexOutOfBoundException e){
//
}

위와 같이 예외가 아닌 상황에 사용하게 되면 가독성이 떨어지는 것은 물론 다음과 같은 단점들도 있다고 한다.

  1. 예외는 예외 상황에 쓰도록 설계되었으므로, JVM 구현체에서 명확한 검사만큼 최적화되지 않았을 가능성이 크다.
  2. 코드를 try-catch 블록 안에 넣으면 JVM이 적용할 수 있는 최적화가 제한된다.
  3. 향상된 for문으로 사용하면 중복검사를 알아서 JVM이 최적화함으로 굳이 위처럼 예외처리 할필요가 없다.
1
2
3
4
// 표준 배열 순회 관용구 
for(Mountain m : range ){
m.climb();
}
  1. 디버깅을 어렵게 만든다. 다른 반복문이 ArrayIndexOutOfBoundException 을 터트린다고 가정하였을때, 첫번쨰 잘못된 예시는 이를 반복문 종료 상황으로 판단한다.
Read more

Item68. 일반적으로 통용되는 명명 규칙을 따르라.

Java 명명규칙은 철자와 문법 범주로 나뉜다.

철자 규칙

  • 패키지,클래스,인터페이스,메소드,필드,타입 변수의 이름을 다룬다.
  • 프로젝트 생성시 패키지명은 조직의 인터넷 도메인 이름을 역순으로 사용하는게 관례이다.

ex) Google : com.google , Naver : com.naver

  • 나머지 패키지 이름은 해당 패키지를 설명하는 하나이상의 요소로 이루어진다.이떄 각 요소는 8자 이하의 짧은 단어로 구성한다.

ex) com.naver.cafe

  • 많은 기능을 가진 경우 붙여할 요소가 많다면 계층적으로 이름을 짜는것도 좋다

ex) java.util.concurrent.atomic

  • class와 인터페이스의 이름의 경우 단어의 첫글자는 대문자로 시작한다. ex) List

  • method와 필드 변수명은 첫글자만 소문자로 하고, 나머지 단어의 첫글자는 대문자로 쓴다. ex) phoneNumber

  • 값이 불변인 static final

  • 상수필드는 예외적으로 모두 대문자를 사용하고, 단어사이는 밑줄로 구분한다. ex) NEGATIVE_INFINITY

  • 타입 매개변수는 보통 한 문자로 표현한다.

  • 대표적으로 관례상 많이 사용하는 타입매개변수들은 다음과 같다.

    T : 임의의 타입
    E : Collection 원소의 타입
    K , V : Map의 키와 값
    x : 예외
    R : 메소드 반환 타입

문법 규칙

  • 객체를 생성할 수 있는 클래스는 보통 단수 명사 혹은 명사구를 사용 ex) Thread
  • 객체를 생성할 수 없는 클래스는 보통 복수형 명사 사용 ex) Collectors, Collections
  • 인터페이스의 이름은 클래스와 똑같이 짓거나, able로 끝나는 형용사를 사용 ex) Runnable
  • Annotation의 경우 명확한 규칙은 없다.
  • 동작을 수행하는 method의 이름은 동사로, boolean값을 반환하는 method의 이름은 is 또는 has로 시작한다. ex) isPrime,hasSiblings
  • 해당 인스턴스의 속성을 반환하는 method는 get으로 시작하는 동사구로 짓거나, 명사로 짓는다. ex) getSize , size

그 외에 타입 변환과 관련된 특별한 method 이름 규칙은 다음과 같다.

  • toType 형태 (toArray,toList) : 객체의 타입을 다른 타입의 객체로 반환하는 method 명
  • asType 형태 (asList) : 객체의 내용을 다른 뷰로 보여주는 method명
  • typeValue 형태 (intValue) : 객체의 값을 기본 타입값으로 반환하는 method명

Item67. 최적화는 신중히 하라

  • 성능떄문에 아키텍쳐 구조를 희생하면 안된다.좋은 아키텍쳐를 가지고 있는 경우 각 모듈이 정보 은닉 원칙을 따르므로, 개별 모듈을 독립적으로 재설계함으로, 성능을 향상시킬 수 있다.

  • 성능 결함은 모듈의 구현 로직을 바꿔서 나중에 최적화할 수 있지만, 전체 아키텍쳐 구조가 성능을 제한하는 상황은 시스템 전체를 다시 작성해야 하는 단점이 있다.

Read more