Spring reactor 2.1.2 (netty 0.8.4) Mono.zip readTimeoutException 문제

web/Spring|2020. 7. 12. 20:34

 

Mono zip


각 Mono 처리 스레드를 병렬로 실행하고 이를 묶어서 사용할 수 있는게 Mono.zip이다. 

근데 Mono zip에서 병렬로 실행되는 작업 중 하나가 empty 또는 error가 발생 되면 바로 error 또는 complete를 내뱉게 되어있다. 하지만 각 Mono 구독 작업에 error와 empty 발생 시 문제에 대해 fallback 처리를 해주면 에러가 발생하더라도 그 로직을 타게 되어있다. 

 

하지만 2.1.2(netty 0.8.4) 버전을 사용하고 있을 때 호출 체인에서 첫 번째 요청의 실패 이후에 두 번째 요청이 정상적으로 이루어 지지 않아서 readTimeout이 발생되는 문제를 경험하였습니다.

 

이 문제를 해결하기 위해서 알아보던 중 2.1.2버전에 문제가 있는 것을 알게 되어 테스트를 해봤다.

 

 

테스트


아래 Mono.zip을 보면 두 개의 Mono 구독 작업을 병렬로 진행하도록 지정해놨고 각 작업 종료 후 response에 대한 부분을 출력하도록 해놨다.

public Mono<WedulResponse> circuitTest(WedulRequest request) {
    return Mono.zip(
        wedulClient.isWedulExist(request)
            .doOnError(e -> log.error("service error", e))
            .defaultIfEmpty(WedulResponse.builder().type("Error Return").build())
            .onErrorReturn(WedulResponse.builder().type("Error Return").build()),
        wedulTestClient.isWedulTestExist(request)
    ).map(
        d -> {
            System.out.println(d.getT2().getPage());
            System.out.println(d.getT1().getType());
            return WedulResponse.builder().isExist(d.getT1().isExist()).build();
        }
    ).doOnError(e -> log.error("error {}", e));
}

이 때 첫 번째 요청은 error가 발생하거나 empty 응답이 발생했을 때 기본값을 주도록 하고 socket timeout의 값은 1ms로 극단적으로 무조건 타임아웃이 나도록 지정해 놨다.

 

그리고 두 번째 요청http://wedul.space에서 사용중인 정상적인 api를 호출하도록 하였고 socket timeout 시간도 3000ms로 아주 넉넉하게 주었고 실제로 타임아웃이 날 이유가 없다.

 

그럼 정상적인 테스트 결과라고 한다면 아래와 같이 정상적인 응답이 와야한다. (page는 무조건 5로 나오게 지정해놨다.)

5
Error Return

 

 

실제로 응답은 예상된 대로 잘 왔다. 하지만 간혈적으로 아래와 같은 readTimeout exception이 별도로 계속 떨어졌다.

2020-07-12 20:17:30.998 ERROR 12214 --- [ctor-http-nio-3] r.netty.http.client.HttpClientConnect    : [id: 0x029e73cc, L:/127.0.0.1:63695 - R:localhost/127.0.0.1:8081] The connection observed an error

io.netty.handler.timeout.ReadTimeoutException: null

 

그래서 왜 그럴까 하고 검색을 해보니 2.1.2버전에 문제가 있어서 버전업을 하면 해결된다고 들었다. 

https://stackoverflow.com/questions/56048216/spring-webflux-timeout-with-multiple-clients

 

Spring webflux timeout with multiple clients

I have a service that interacts with a couple of other services. So I created separate webclients for them ( because of different basepaths). I had set timeouts for them individually based on https://

stackoverflow.com

 

그래서 버전을 2.2.4버전으로 업데이트하고 다시 테스트 해봤다. 실제로 아까 발생했던 readTimeoutException 문제는 더 발생하지 않았다.

 

 

실제 코드가 어떤 부분이 문제였는지는 찾지 못했지만 그래도 문제는 해결되어서 다행이다.

 

테스트 코드

https://github.com/weduls/circuit_breaker_test

댓글()

생성한 Custom validation으로 에러메시지 출력하기

web/Spring|2018. 12. 25. 00:45

바로 직전 https://wedul.tistory.com/562?category=595982 에서 Custom validation을 만들어서 입력된 값에 validation을 체크하는 방법을 알아봤다.

그럼 이 validation체크를 통해서 front에 상황에 맞는 에러를 보내줄 수 있도록 조치를 취해보자.

우선 @valid 처리를 했었던 컨트롤러에서 에러 메시지를 수집해야한다. 


1. Controller

Spring에서 Validation 작업을 진행할 시 validation에 문제가 발생하면 에러 내용을 묶어서 BindingResult로 처리할 수 있도록 제공해준다. 이를 사용하기 위해서 parameter로 BindingResult값을 추가해준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
 * 회원가입
 *
 * @param reqDto
 * @return
 * @throws Exception
 */
@RequestMapping("/join")
public ResponseEntity<?> join(@Valid UserDto reqDto, BindingResult bindingResult) throws Exception {
    // check constraint rules
    this.checkConstraintRule(bindingResult);
 
    return ResponseEntity.ok(userService.insertUser(reqDto));
}
cs


2. 에러 처리

BindingResult에 validation을 체크하고 발생한 에러들에 대한 내용을 하나씩 뽑아서 국제화 메시지로 변경해주고 \n으로 데이터를 묶어서 view에 전달할 수 있도록 데이터를 바꿔 준다. 공통적으로 사용하것이기 때문에 공통 Controller 클래스를 하나 만들어서 사용한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.wedul.common.controller;
 
import com.wedul.common.error.BadRequestException;
import com.wedul.common.util.MessageBundleUtil;
import lombok.AllArgsConstructor;
import org.apache.commons.lang.StringUtils;
import org.springframework.validation.BindingResult;
 
import java.util.stream.Collectors;
 
/**
 * wedulpos
 *
 * @author wedul
 * @since 2018-12-24
 **/
@AllArgsConstructor
public class BaseController {
 
  private final MessageBundleUtil messageBundleUtil;
 
  protected void checkConstraintRule(BindingResult bindingResult) throws BadRequestException {
    String msg = null;
    if (bindingResult.hasErrors()) {
       msg = bindingResult.getFieldErrors()
              .stream()
              .map(error -> messageBundleUtil.getMessage(error.getDefaultMessage()))
              .collect(Collectors.joining("\n"));
    }
 
    if(StringUtils.isNotBlank(msg)) {
      throw new BadRequestException(msg);
    }
 
    return;
  }
 
}
 
cs


3. 에러 핸들링

나는 에러를 에러 코드와 메시지로 전달해주는 방식을 좋아한다. 사실 다른 정보를 다 전달해줘봐야 프론트에서 처리하기도 어렵고 나머지는 로그로써 확인하는게 더 편하다. 그래서 전달하는 값을 정제하기 위해서 @ControllerAdvice를 통해 출력되는 에러를 재정의 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package com.wedul.common.config;
 
import com.wedul.common.enums.EnumErrorType;
import com.wedul.common.error.BadRequestException;
import com.wedul.common.error.ForbiddenException;
import com.wedul.common.error.NotFoundException;
import com.wedul.common.error.InternalServerException;
import lombok.Builder;
import lombok.Data;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
 
/**
 * 에러 유형을 나타내는 Config
 *
 * @author wedul
 * @Date 2017. 07. 09
 */
@ControllerAdvice
public class ExceptionConfig {
 
  @Data
  @Builder
  private static class ErrorResponse {
    private int errCode;
    private String msg;
  }
 
  @ExceptionHandler({Exception.class})
  @ResponseBody
  public ErrorResponse errorHandler(Exception ex) {
    if(ex instanceof BadRequestException) {
      return this.getError(400, ex);
    } else if(ex instanceof ForbiddenException) {
      return this.getError(403, ex);
    } else if(ex instanceof NotFoundException) {
      return this.getError(404, ex);
    } else if(ex instanceof InternalServerException) {
      return this.getError(500, ex);
    } else {
      return ErrorResponse.builder().errCode(500).msg(ex.getMessage()).build();
    }
  }
 
  /**
   * 기본 에러 내용 출력
   *
   * @param errorCode
   * @param ex
   * @return
   */
  private ErrorResponse getError(int errorCode, Exception ex) {
    String message = ex.getMessage();
    if(StringUtils.isBlank(message)) {
      message = EnumErrorType.getErrorMsg(errorCode);
    }
 
    return ErrorResponse.builder().errCode(errorCode).msg(message).build();
  }
 
  /**
   * Error code 만들기
   *
   * @return String
   * @date 2017. 7. 9.
   * @author wedul
   */
  private String makeErrorCode(Exception ex) {
    StackTraceElement[] ste = ex.getStackTrace();
    StringBuffer sb = new StringBuffer();
    StackTraceElement[] arrayOfStackTraceElement1;
    int j = (arrayOfStackTraceElement1 = ste).length;
    for (int i = 0; i < j; i++) {
      StackTraceElement el = arrayOfStackTraceElement1[i];
      String className = el.getClassName();
      if (className.startsWith("com.wedul.wedulpos")) {
        sb.append(className.substring(className.lastIndexOf("."+ 1).toUpperCase()).append("[");
        sb.append(el.getLineNumber()).append("]");
        break;
      }
    }
    if (StringUtils.isBlank(sb.toString())) {
      return ex.getStackTrace()[0].getClassName();
    }
    return sb.toString();
  }
}
 
cs


4. 테스트 진행

1) @NotBlank, @Email 확인


2) Custom Validation인 password check



소스코드 : https://github.com/weduls/wedulpos_boot

참고 : https://meetup.toast.com/posts/147

댓글()
  1. 이정욱 2019.08.12 18:53 댓글주소  수정/삭제  댓글쓰기

    정말 깔끔하고 좋은 코드 감사합니다.

Spring Validation을 이용해서 요청 검증처리

web/Spring|2018. 8. 9. 23:30

대게 개발을 진행할 때 front에서 validate를 체크하고 민감한 정보에 대해서는 한번더 체크를 진행하고 작업을 했었다. 

하지만 클라이언트에서만 validation을 체크하게 되는 경우 브라우저에서 악의적인 행동에 대해서 대응하기 어려워질수 있기 때문에 백엔드에서도 Validation을 처리해야한다. 

여기서 사용되는 @valid 어노테이션들을 알아보자.

1. DTO validation 선언
우선적으로 DTO에 각 속성에 필요한 @valid 옵션들을 추가한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.wedul.springboottest.rest.dto;
 
import lombok.*;
 
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
 
/**
 * 사용자 클래스.
 * User: wedul
 * Date: 2018-08-05
 * Time: 오후 9:47
 */
@Data
@AllArgsConstructor
@EqualsAndHashCode
@NoArgsConstructor
public class UserDto {
    @NotBlank(message = "이름을 입력해주세요.")
    private String name;
 
    private int age;
 
    @NotBlank(message = "이메일을 입력해주세요.")
    @Email(message = "이메일을 양식을 지켜주세요.")
    private String email;
 
    public void updateInfo(UserDto user) {
        this.age = user.getAge();
        this.email = user.getEmail();
    }
}
cs


@Valid 데이터 종류

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@AssertFalse : false 값만 통과 가능
@AssertTrue : true 값만 통과 가능
@DecimalMax(value=) : 지정된 값 이하의 실수만 통과 가능
@DecimalMin(value=) : 지정된 값 이상의 실수만 통과 가능
@Digits(integer=,fraction=) : 대상 수가 지정된 정수와 소수 자리수보다 적을 경우 통과 가능
@Future : 대상 날짜가 현재보다 미래일 경우만 통과 가능
@Past : 대상 날짜가 현재보다 과거일 경우만 통과 가능
@Max(value) : 지정된 값보다 아래일 경우만 통과 가능
@Min(value) : 지정된 값보다 이상일 경우만 통과 가능
@NotNull : null 값이 아닐 경우만 통과 가능
@Null : null일 겨우만 통과 가능
@Pattern(regex=, flag=) : 해당 정규식을 만족할 경우만 통과 가능
@Size(min=, max=) : 문자열 또는 배열이 지정된 값 사이일 경우 통과 가능
@Valid : 대상 객체의 확인 조건을 만족할 경우 통과 가능
 
출처: http://goldenraccoon.tistory.com/entry/Valid-annotation-종류 [황금너구리 블로그]
cs


2. @Request에서 전달받는 파라미터 설정
간단하게 @Valid 설정을 통해 전달받을 객체에 대한 유효성 체크를 설정할 수 있다.

1
2
3
4
5
6
7
8
9
10
 /**
   * Edit user info response entity.
   *
   * @param user the user
   * @return the response entity
 */
 @PutMapping(value = "/user/info")
 public ResponseEntity<?> editUserInfo(@RequestBody @Valid UserDto user) {
     return ResponseEntity.ok(userService.updateUser(user));
 }
cs

3. 테스트
위의 설정한 validation 설정을 위반하여 request를 보내보자

요청

응답

validation 위반 시 400에러와 함께 오류 field와 기존에 설정한 defaultMessage를 확인할 수 있다.


4. 추가적인 action에 대한 validation 설정
단순하게 regex와 null, email 등등에 대한 validation 체크 이외에 중복 체크등을 진행하기 위해서는 별도의 작업이 필요하다.

4-1)  기존의 Validation과 동일한 방식으로 표현하기 위해서 별도의 RuntimeException 클래스를 만든다.

이 예외 클래스는 Response Stauts는 400으로 전달될 수 있게 @ResponseStatus(HttpStatus.BAD_REQUEST)로 설정하고 예외가 발생한 field와 defaultMessage를 출력해주기 위해 별도의 속성을 만들어준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package com.wedul.springboottest.rest.exception;
 
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
 
import java.util.List;
 
/**
 * Created by Leo.
 * User: wedul
 * Date: 2018-08-09
 * Time: 오후 9:06
 */
// 기존에 validation의 에러와 동일하게 400 에러가 발생하도록 추가
@ResponseStatus(HttpStatus.BAD_REQUEST)
public class ValidationException extends RuntimeException {
 
    private Error[] errors;
 
    public ValidationException(String defaultMessage, String field){
        this.errors = new Error[]{new Error(defaultMessage, field)};
    }
 
    public ValidationException(Error[] errors) {
        this.errors = errors;
    }
 
    public Error[] getErrors() {
        return errors;
    }
 
    public static class Error {
 
        private String defaultMessage;
        private String field;
 
        public Error(String defaultMessage, String field) {
            this.defaultMessage = defaultMessage;
            this.field = field;
        }
 
        public String getDefaultMessage() {
            return defaultMessage;
        }
 
        public String getField() {
            return field;
        }
    }
 
 
}
cs


이 생성된 ValidationException 클래스를 ErrorAttributes 인터페이스를 재 정의하여 Validation 오류가 발생하였을 때 별도의 작업을 할 수 있도록 처리해준다. (이 Bean 객체는 Application에 정의해준다.)


※ ErrorAttributes는 기본적으로 스프링 부트에서 에러 처리하는 BasicErrorController인데 이 인터페이스는 DefaultErrorAttributes 클래스를 참조하여 커스터마이징이 가능하다.
참고
https://brunch.co.kr/@sbcoba/9


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.WebRequest;
 
 
 
@SpringBootApplication
public class SpringboottestApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(SpringboottestApplication.class, args);
    }
 
    @Bean
    public ErrorAttributes errorAttributes() {
        return new DefaultErrorAttributes() {
 
        @Override
        public Map<String, Object> getErrorAttributes(WebRequest webRequest,
                                                          boolean includeStackTrace) {
            Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest, includeStackTrace);
            Throwable error = getError(webRequest);
 
            // validatijon Exception에 경우 별도의 처리를 진행한 에러 데이터 추가
            if (error instanceof ValidationException) {
                errorAttributes.put("errors", ((ValidationException)error).getErrors());
            }
            return errorAttributes;
        }
    };
}
cs


그리고 서비스 코드 영역에서 중복, 금지 등등을 확인하고  ValidationException을 throw하면 front에 동일한 형태에 에러코드를 전송해 줄 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 서비스 코드
 
@Override
public ResultDto insertUser(UserDto user) {
    if (null == users) {
       return ResultDto.fail("UserList Empty");
    }
 
    if (null == user) {
        return ResultDto.fail("Request User Is Null");
    } else {
        if (users.contains(user)) {
            // 중복된 부분에 대해 ValidationException
            throw new ValidationException("name""이름이 중복됩니다.");
        }
        users.add(user);
        return ResultDto.success();
    }
}
cs

테스트 해보면 다음과 같이 중복코드에 대해서 체크가 정상적으로 되는 것을 알 수 있다.

물론 이렇게 RuntimeException을 만들지 않고 팩토리 객체를 만들어서 동일하게 실패 메시지를 만들어서 줘도 상관없다.

하지만 예측이 어려운 Runtime Exception을 잘 정의해놓으면 편하게 처리할 수 있다. 




댓글()

규칙 65 - 예외를 무시하지마라.

JAVA/Effective Java|2018. 6. 6. 00:19

사용하려고 하는 API의 설계자가 예외가 발생될 수 있다고 명시한 예외는 조심해야한다.


무시방법은 간단하게 호출하는 메서드를 빈 catch 블록이 붙은 try 문으로 감싸면, 예외를 무시할 수있다.  

1
2
3
4
5
try {
            
} catch (Exception ex) {
    
}
cs


빈 catch 블록은 예외를 선언한 목적으로 만들어진다.

만약 이렇게 빈 catch 블록을 만들려고 하는 경우에는 적어도 왜 넘겼는지 주석을 달아주는 것이 좋다.



출처 : 조슈아 블로크, 『 Effective Java 2/E』, 이병준 옮김, 인사이트(2014.9.1), 규칙65

댓글()

규칙 61 - 추상화 수준에 맞는 예외를 던져라

JAVA/Effective Java|2018. 6. 1. 23:05

메서드가 하는 일과 관련성이 없는 예외가 메서드에서 발생하면 디버깅하기 어렵거나 관리하기 어려울 수 있다.


이는 추상화 수준이 낮은 곳에서 발생한 예외를 그대로 밖으로 전달하면 이런일이 발생한다.



이런 문제를 해결하기 위해서 상위계층에서는 하위계층에서 발생하는 예외를 반드시 받아서 상위 계층 추상화 수준에 맞는 예외로 바꿔서 던져야한다.


이를 예외 변환(exception translation)이라고 한다.



예를 들어 몇가지 사례를 살펴보자.


우선 AbstractSequentialList 클래스를 살펴보자. 이 클래스의 get 메서드의 명세를 보면 예외가 발생되었을 때 예외 변환을 해서 보내달라는 것을 확인할 수 있다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 /**
     * Returns the element at the specified position in this list.
     *
     * <p>This implementation first gets a list iterator pointing to the
     * indexed element (with <tt>listIterator(index)</tt>).  Then, it gets
     * the element using <tt>ListIterator.next</tt> and returns it.
     *
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E get(int index) {
        try {
            return listIterator(index).next();
        } catch (NoSuchElementException exc) {
            throw new IndexOutOfBoundsException("Index: "+index);
        }
    }
cs





그리고 예외 변환의 특별한 사례인 예외 연결 (exception chaining)이있다. 


이는 하위 계층에서 발생한 예외정보가 상위계층 예외를 발생시킨 문제를 디버깅 할때 사용한다.


사용방법은 원인이 상위 계층 예외로 전달되는데, 상위 계층 예외에 있는 Throwable.getCause를 호출하면 해당 정보를 꺼낼 수 있다.




[예외 연결]


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 
/**
 * 상위계층 에러인 wedulError
 * 
 * @author jeongcheol
 *
 */
public class WedulError extends Exception {
    public WedulError(Throwable cause) {
        super(cause);
    }
}
 
 
 
 
import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;
 
public class Main {
 
    public static void main(String args[]) throws WedulError {
        
        List<String> data = Arrays.asList("cjung""wedul");
        try {
            data.get(3);
        } catch (NoSuchElementException e) {
            throw new WedulError(e);
        }
    }
    
}
 
 
cs



이런 예외 연결 기능을 사용하면 프로그램 안에서 예외의 원인에 접근할 수 있을뿐 아니라 최초에 발생한 예외의 스택 추적 정보를 상위계층 예외에 통합할 수 있다.




결론은 아무 생각없이 아래 계층에서 생긴 예외를 밖으로 전달하기만 하는 것보다야 예외 기법 낫지만 남용해서는 안된다.

제일 좋은 방법은 아예 에러없는 코딩을 하는 것이다.


그리고 하위계층에서 발생한 에러는 하위계층에서 처리하고 어쩔수 없이 전달될경우에는 적절한 예외로 예외 변환해서 보내야한다.




출처 : 조슈아 블로크, 『 Effective Java 2/E』, 이병준 옮김, 인사이트(2014.9.1), 규칙61 인용.

댓글()

규칙 60 - 표준 예외를 사용해라

JAVA/Effective Java|2018. 5. 29. 23:55

예외를 사용할 때 기존에 정의되어있는 표준예외를 사용하는 것이 좋다.

그 이유는 다음과 같다.
- 배우기 쉽고 사용하기 편리한 API를 만들수 있으며 다른 프로그래머들도 친숙한 널리 퍼진 관습을 따르기 때문이다.
- 잘모르는 예외가 없어 API 가독성을 높일수있다.
- 예외클래스를 별도로 만들어서 클래스를 늘리지 않으면 프로그램의 메모리 요구량이 줄어들고 클래스를 로딩하는 시간도 줄어든다.


대표적인 표준 예외



예외
용례
lllegalArgumentException
null이 아닌 인자값이 잘못되었을때
llegalStateException
객체 상태가 메서드 호출을 처리하기에 적절치 않을때
NullPointerException
null 값을 받으면 안 되는 인자에 null이 전달되었을 때
IndexOutOfBoundsException
인자로 주어진 첨자가 허용 범위를 벗어났을 때
ConcurrentModificationException
병렬적 사용이 금지된 객체에 대한 병렬 접근이 탐지 되었을 때
UnsupportedOperationException
객체가 해당 메서드를 지원하지 않을떄





출처 : 조슈아 블로크, 『 Effective Java 2/E』, 이병준 옮김, 인사이트(2014.9.1), 규칙60 인용.



댓글()

규칙 58 복구가능 상태에는 점검지정 예외를 사용하고, 프로그래밍 오류에는 실행시점 예외를 이용하라.

JAVA/Effective Java|2018. 5. 29. 23:53

자바에는 몇 가지에 throwable을 제공한다.

점검지정 예외 (checked error)
컴파일 시점에 예외가 발생하는 부분으로 컴파일 시에 에러를 처리하는 코드를 삽입하지 않으면 컴파일이 되지 않는다.


1
2
3
4
5
6
7
8
9
10
public void ioOperation(boolean isResourceAvailable) {
  try {
    if (!isResourceAvailable) {
      throw new IOException();
    }
  } catch(IOException e) {
    // Handle caught exceptions.
  }
}
 
cs




unchecked error
컴파일 시점에 체크되지 않는 에러
실생시점 예외(runtime exception)와 오류(error)은 모두 unchecked error에 속한다.



실행시점 예외
NullPointerException, ArrayIndexOutOfBoundException와 같이 실행시간에 발생되는 에러



오류
OutofMemoryError ThreadDeath 같은 에러
catch 블럭으로 잡아도 대응 방법이 없다.




이런 예외들을 어떻게 기준을 잡고 처리해야 하는지 알아보자.

unchecked error의 기본적인 규칙은 caller 측에서 복구할것으로 여겨지는 상황에 대해서는 점검지점 예외를 사용해야한다.

-> 점검지점 예외를 사용한다는 것은 API 사용자에게 복구할 권한을 준다는 것이다. 그러므로 점검지점 예에서 발생한 에러는 catch에서 무조건 처리를 해야한다.


프로그래밍 오류를 표현할 때는 실행시점 예외를 사용하라.

-> 실행시점에 발생하는 대다수의 에러는 규칙위반으로 발생한다. 예를 들어 배열의 인덱스를 넘어가는 접근을 할때 발생하는 ArrayIndexOutOfBoundException이 대표적인 예이다.


예외 정보 문자열을 파싱해서 원하는 정보를 얻어서 처리하는 것은 굉장히 위험한 행동이다.

-> 예외정보는 각 버전 별로 변경될 수 있으며, 문자열에 경우 내용이 변하거나 없어질 수있다. 또한 정확한 예외에 대한 명세를 한 클래스 자체가 거의 없기 때문에 더욱더 위험하다.



결론을 말하면 복구 가능한경우에는 try-catch, throws를 사용하여 점검가능한 예외를 사용하고, 그것이 아닌 경우에는 실행시점 예외를 사용하라.

출처 : 조슈아 블로크, 『 Effective Java 2/E』, 이병준 옮김, 인사이트(2014.9.1), 규칙58 인용.

댓글()

java.lang.Error: Unresolved compilation problem

web/Spring|2018. 5. 27. 11:32

프로젝트를


git을 통해서 내려받고 실행시키고자 할때, 가끔 다음과 같은 오류가 발생할 때가 있다.


java.lang.Error: Unresolved compilation problem



이 경우 해결방법은 2가지이다.

1. 프로젝트 clean

2. tomcat clean, tomcat 디렉토리 clean


댓글()