클라이언트와 서버 간의 통신에서 요청이 성공했는지, 오류가 발생했는지를 나타내기 위해 상태 코드(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) 상태 코드가 발생하는 상황
- 서버 측에서 예외(Exception) 발생 시
- try-catch로 예외 처리를 하지 않고 서버 코드에서 예상치 못한 예외가 발생한 경우
- gRPC 서버와의 네트워크 문제 발생 시
- 네트워크 연결이 끊어졌거나, 응답이 제대로 반환되지 못한 경우
- 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 트레일러를 활용하여 응답 상태를 보다 정밀하게 전달할 수 있다.
'기술(Tech) > Network & System' 카테고리의 다른 글
[gRPC] gRPC의 보안 계층 🔒 (0) | 2025.03.15 |
---|---|
[gRPC] gRPC 예외 처리 및 에러 핸들링 (0) | 2025.03.13 |
[gRPC] RESTful API와 gRPC 간 변환 방법 (with. gRPC-Web) (1) | 2025.03.02 |
[gRPC] gRPC 서비스 요청 방식과 내부 동작 원리 (0) | 2025.02.23 |
[gRPC] RPC란? (0) | 2025.02.23 |
클라이언트와 서버 간의 통신에서 요청이 성공했는지, 오류가 발생했는지를 나타내기 위해 상태 코드(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) 상태 코드가 발생하는 상황
- 서버 측에서 예외(Exception) 발생 시
- try-catch로 예외 처리를 하지 않고 서버 코드에서 예상치 못한 예외가 발생한 경우
- gRPC 서버와의 네트워크 문제 발생 시
- 네트워크 연결이 끊어졌거나, 응답이 제대로 반환되지 못한 경우
- 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 트레일러를 활용하여 응답 상태를 보다 정밀하게 전달할 수 있다.
'기술(Tech) > Network & System' 카테고리의 다른 글
[gRPC] gRPC의 보안 계층 🔒 (0) | 2025.03.15 |
---|---|
[gRPC] gRPC 예외 처리 및 에러 핸들링 (0) | 2025.03.13 |
[gRPC] RESTful API와 gRPC 간 변환 방법 (with. gRPC-Web) (1) | 2025.03.02 |
[gRPC] gRPC 서비스 요청 방식과 내부 동작 원리 (0) | 2025.02.23 |
[gRPC] RPC란? (0) | 2025.02.23 |