클라이언트와 서버 간의 통신에서 요청이 성공했는지, 오류가 발생했는지를 나타내기 위해 상태 코드(Status Code)를 사용한다.

 

gRPC 상태 코드는 기존 HTTP 상태 코드와 다르게 더 구체적이고 세밀한 오류 처리를 지원하며, 적절한 오류 처리를 위해 반드시 이해하고 있어야 한다.

 

gRPC 상태 코드 vs HTTP 상태 코드 비교

gRPC 상태 코드는 기존 HTTP 상태 코드와는 다르다.

구분 gRPC 상태 코드 HTTP 상태 코드
프로토콜 차이 HTTP/2를 기반으로 동작하지만,
gRPC는 HTTP 자체가 아님
HTTP 프로토콜의 일부
상태 코드 범위 16개 상태 코드 (7개 카테고리) 1xx ~ 5xx 상태 코드
의미의 구체성 특정한 상황을 더 세밀하게 설명하기 위해 설계됨 범용적인 상태 코드 사용
확장성 Protobuf 기반 -> 진화 및 확장에 유리 텍스트 기반 JSON/XML

▶ gRPC는 HTTP의 상태 코드보다 더 구체적인 상태를 제공하여, RPC 호출에서 발생할 수 있는 다양한 문제를 명확하게 구분할 수 있다.

 

gRPC 상태 코드 목록


gRPC 상태 코드는 크게 성공(Success)오류(Error)로 구분된다.

 

✅ 성공 코드

상태 코드 설명
OK (0) 요청이 성공적으로 완료됨
  • OK (0) 코드가 반환되면 클라이언트 요청이 성공적으로 처리된 것이다.
예제: 요청 성공 (OK 상태 코드)
public class UserServiceImpl extends UserServiceGrpc.UserServiceImplBase {
    @Override
    public void getUser(UserRequest request, StreamObserver<UserResponse> responseObserver) {
        UserResponse response = UserResponse.newBuilder()
            .setUserId(request.getUserId())
            .setName("Alice")
            .setEmail("alice@example.com")
            .build();

        responseObserver.onNext(response);
        responseObserver.onCompleted(); // 요청 성공 (OK 상태 반환)
    }
}
  • 정상적으로 응답을 반환하면 OK (0) 상태 코드가 클라이언트로 전달된다.

 

❌ 클라이언트 측 오류

상태 코드 설명
CANCELLED (1) 클라이언트가 요청을 취소함
INVALID_ARGUEMENT (3) 잘못된 인자가 전달됨
DEADLINE_EXCEEDED (4) 요청 시간이 초과됨 (Timeout 발생)
NOT_FOUND (5) 요청한 리소스를 찾을 수 없음
ALREADY_EXISTS (6) 동일한 데이터가 이미 존재함
  • 클라이언트 오류는 보통 요청 데이터 문제로 발생하며, 적절한 예외 처리를 통해 사용자에게 오류 메시지를 전달하는 것이 중요하다.
예제: 요청 시간 초과 (DEADLINE_EXCEEDED 상태 코드)
@Override
public void getUser(UserRequest request, StreamObserver<UserResponse> responseObserver) {
    try {
        // 5초간 처리 지연 (네트워크 지연 시뮬레이션)
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        responseObserver.onError(Status.DEADLINE_EXCEEDED
            .withDescription("Request timed out")
            .asRuntimeException());
        return;
    }

    UserResponse response = UserResponse.newBuilder()
        .setUserId(request.getUserId())
        .setName("Alice")
        .setEmail("alice@example.com")
        .build();

    responseObserver.onNext(response);
    responseObserver.onCompleted();
}
  • 클라이언트가 설정한 요청 시간 초과 시 DEADLINE_EXCEEDED (4) 코드가 반환된다.

 

❌ 서버 측 오류

상태 코드 설명
PERMISSION_DENIED (7) 권한이 없어 요청이 거부됨
UNAUTHENTICATED (16) 인증되지 않은 사용자 요청
RESOURCE_EXHAUSTED (8) 서버 자원이 부족하여 요청을 처리할 수 없음
FAILED_PRECONDITION (9) 요청이 현재 상태에서 처리될 수 없음
ABORTED (10) 동시성 문제로 요청이 중단됨
OUT_OF_RANGE (11) 허용된 범위를 벗어난 요청
UNIMPLEMENTED (12) 서버에서 해당 기능을 지원하지 않음
INTERNAL (13) 서버 내부 오류 발생
UNAVAILABLE (14) 서버가 다운되었거나 응답할 수 없는 상태
DATA_LOSS (15) 데이터 손실이 발생함
  • 서버 오류는 보통 시스템 장애, 인증 문제, 데이터 충돌로 인해 발생한다.
  • 서버 상태를 모니터링하고, 장애 발생 시 적절한 대응이 필요하다.
예제: 리소스 부족 (RESOURCE_EXHAUSTED 상태 코드)
@Override
public void getUser(UserRequest request, StreamObserver<UserResponse> responseObserver) {
    if (serverMemoryIsFull()) {
        responseObserver.onError(Status.RESOURCE_EXHAUSTED
            .withDescription("Server memory is full, try again later")
            .asRuntimeException());
        return;
    }

    UserResponse response = UserResponse.newBuilder()
        .setUserId(request.getUserId())
        .setName("Alice")
        .setEmail("alice@example.com")
        .build();

    responseObserver.onNext(response);
    responseObserver.onCompleted();
}
  • 서버 자원이 부족할 경우 RESOURCE_EXHAUSTED (8) 상태 코드가 반환된다.

 

❌ 알 수 없는 오류

상태 코드 설명
UNKNOWN (2) 정확한 원인을 알 수 없는 오류가 발생했을 때 반환되는 코드

 

UNKNOWN (2) 상태 코드가 발생하는 상황

  1. 서버 측에서 예외(Exception) 발생 시
    • try-catch로 예외 처리를 하지 않고 서버 코드에서 예상치 못한 예외가 발생한 경우
  2. gRPC 서버와의 네트워크 문제 발생 시
    • 네트워크 연결이 끊어졌거나, 응답이 제대로 반환되지 못한 경우
  3. gRPC 내부에서 원인을 특정할 수 없는 오류가 발생했을 때
    • gRPC 라이브러리 내부에서 예기치 못한 문제가 발생한 경우

 

예제: 서버에서 예외 발생 (UNKNOWN 상태 코드)
public class UserServiceImpl extends UserServiceGrpc.UserServiceImplBase {
    @Override
    public void getUser(UserRequest request, StreamObserver<UserResponse> responseObserver) {
        // 강제 예외 발생
        int result = 10 / 0;  // 0으로 나누기 예외 발생 (ArithmeticException)

        UserResponse response = UserResponse.newBuilder()
            .setUserId(request.getUserId())
            .setName("Alice")
            .setEmail("alice@example.com")
            .build();

        responseObserver.onNext(response);
        responseObserver.onCompleted();
    }
}
  • 예외가 발생하면 gRPC는 자동으로 UNKNOWN (2) 상태 코드를 반환한다.
  • 하지만 이유를 명확하게 알 수 없으므로, 예외를 직접 처리하는 것이 중요하다.

 


gRPC Metadata란?


gRPC 통신에서는 클라이언트-서버 간의 통신에 필요한 추가적인 정보를 함께 전달하는 메타데이터(Metadata)를 사용할 수 있다.

메타데이터는 헤더(Header)트레일러(Trailer)로 구분된다.

 

Header Metadata (헤더 메타데이터)

  • HTTP/2 헤더 프레임을 통해 전송된다.
  • 클라이언트가 서버에 요청을 보낼 때 포함된다.
  • 인증 토큰, 요청 ID, 클라이언트 정보 등을 포함할 수 있다.
예제: 클라이언트에서 헤더 메타데이터 전송
Metadata metadata = new Metadata();
metadata.put(Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER), "Bearer token123");

UserRequest request = UserRequest.newBuilder().setUserId(1).build();
stub.withCallCredentials(new CallCredentials() {
    @Override
    public void applyRequestMetadata(RequestInfo requestInfo, Executor appExecutor, MetadataApplier applier) {
        applier.apply(metadata);
    }
}).getUser(request);

 

 

 

Trailer Metadata (트레일러 메타데이터)

  • 응답의 마지막 부분에서 전송된다.
  • 서버가 응답을 완료한 후, 상태 코드와 함께 오류 메시지를 반환할 수 있다.
예제: 서버에서 트레일러 메타데이터 전송
Metadata trailers = new Metadata();
trailers.put(Metadata.Key.of("grpc-status", Metadata.ASCII_STRING_MARSHALLER), "14");
trailers.put(Metadata.Key.of("grpc-message", Metadata.ASCII_STRING_MARSHALLER), "Server unavailable");

responseObserver.onError(Status.UNAVAILABLE
    .withDescription("Server is down")
    .asRuntimeException());
  • gRPC는 HTTP/2 트레일러를 활용하여 응답 상태를 보다 정밀하게 전달할 수 있다.