'Raw use of parameterized class 'List' 라는 경고를 보고 Raw use가 무엇인지 찾아봤는데 이펙티브 자바에 있는 내용이라 아직 초반부를 보고 있지만 해당 부분을 읽어보았다.

 

제네릭 타입은 일련의 매개변수화 타입(parameterized type)을 정의하는데 List<String>의 경우 원소의 타입이 String인 리스트를 뜻하는 매개변수화 타입인 것이다.

 

Raw Type이란 제네릭 타입에서 타입 매개변수를 전혀 사용하지 않을 때를 말하는데 아래와 같이 원소의 타입을 정의하지 않고 List를 그대로 사용한 것이다.

 

List<String> listA = new ArrayList<>();

//Raw use of parameterized class 'List'
List listB = new ArrayList<>();

 

Raw Type은 제네릭이 도입되기 이전에 수 많은 코드들과 호환되도록 하기 위해 있는 것인데 가장 큰 문제는 타입 오류를 런타임에서야 발견할 수 있는 것이다.

 

제네릭을 활용하면 엉뚱한 타입의 인스턴스를 넣으려 할 때, 컴파일 오류가 발생해서 런타임 되기 전에 오류를 알아차릴 수 있다. Raw Type을 쓰면 제네릭이 주는 안전성과 표현력을 잃게 되는 것이다.

 

제네릭 타입을 쓰고 싶지만 실제 타입 매개변수가 무엇인지 신경 쓰고 싶지 않으면 Raw Type보다 비한정적 와일드 카드를 사용하는 것이 좋다.

 

예외적으로 class 리터럴에는 매개변수화 타입을 사용할 수 없기 때문에 List.class 형태의 Raw Type으로 사용해야 하며, instanceOf 연산자도 런타임시에는 제네릭 정보가 지워지기 때문에 매개변수화 타입에는 적용할 수 없어 다음과 같이 검사 형변환(checked cast)으로 사용하는 것이 좋다.

 

if (o instanceof Set) {
    Set<?> s = (Set<?>) o;
    ...
}

 

아직 제네릭에 대한 공부가 더 필요하지만 Raw Type으로 선언 시 컴파일시에 문제점을 발견할 수 있는 제네릭의 장점을 살리지 못하고 런타임 오류가 발생할 수 있는 위험에 대해 알 수 있었다.

 

자바 예외는 최상위 예외 계층인 Throwable, 그 하위로 Exception과 Error가 있는데 Error는 메모리 부족 같이 애플리케이션에서 복구 불가능한 시스템 예외인데 catch로 예외를 잡으면 하위 예외(Error)까지 함께 잡기 때문에 Exception 예외를 실질적인 최상위 예외로 생각하면 된다. 

 

Exception과 그 하위 예외는 RuntimeException을 제외하고 전부 컴파일러가 체크하는 체크 예외(잡아서 처리하거나 밖으로 던지지 않을 경우 컴파일 오류 발생)인데 RuntimeException은 컴파일러가 체크 하지 않는 언체크 예외(하위 예외 포함)로 런타임 예외라고 부른다.

 

예외는 잡아서 처리하거나 호출한 곳으로 던지게 되는데 잡거나(catch) 던질(throws) 경우 지정한 예외의 하위 예외들도 함께 처리가 된다. 예외를 처리하지 못할 경우 자바 main() 쓰레드는 예외 로그를 출력하면서 시스템이 종료되는데 웹 애플리케이션은 시스템이 종료되지 않는 대신 WAS가 해당 예외를 받아서 오류 페이지를 보여주는 식으로 처리를 한다.

 

체크 예외는 예외를 잡아서 처리하지 않는 경우 예외를 던지는 throws를 무조건 선언해야 하는데 예외를 누락하지 않도록 컴파일 시점에서 확인을 해준다는 장점이 있지만 실제로는 모든 예외를 전부 처리해야 되고 의존관계에 문제가 생기는 등의 단점이 있다.

언체크 예외는 말 그대로 컴파일러가 예외를 체크하지 않는 예외로 체크 예외와 달리 throws를 생략해도 되는데 중요한 예외의 경우에는 throws를 선언해서 코드를 호출하는 개발자에게 정보를 줄 수 있다.

 

기본적으로는 런타임 예외를 사용하고 체크 예외는 비즈니스 로직상 의도적으로 던지는, 반드시 처리해야 하는 문제에만 사용을 하는 것이 좋다. 예를 들어 JDBC를 사용하다가 JPA 같은 기술로 변경하는 경우 throws SQLException 코드를 전부 throws JPAException으로 바꿔야 하는 등 OCP, DI에 큰 문제가 생기는데 대부분의 예외는 서비스, 컨트롤러에서 복구 불가능한 예외라서 불필요한 의존관계 문제가 생기는 것이다.

 

복구 불가능한 예외의 경우 서블릿 필터나 스프링 인터셉터, 스프링 ControllerAdvice를 통해 일관성 있게 공통으로 처리해서 오류를 빠르게 인지하는 것이 중요한데 체크 예외를 런타임 예외로 변환하는 경우 예외를 일일히 throws 하지 않아도 되고 공통으로 처리를 하게 되면 변경시에도 영향 범위가 최소화 된다.

 

 

[참고] 인프런 김영한님 강의를 공부한 내용입니다.

+ Recent posts