hello, robot

ROS 2에서 두 정수를 더해 돌려주는 간단한 서비스 서버와 클라이언트를 만들어 봤어요. 요청을 보내면 응답이 오는 걸 확인하는 것까지는 교재 예제 그대로였는데, 그 예제에는 실제로 쓰면 서버를 멈추게 하는 구멍이 하나 있었어요.

서비스는 요청과 응답이 한 번씩 오가요

토픽서비스
흐름한 방향으로 계속 흐르는 스트림한 번 요청하고 한 번 응답받는 왕복
카메라 영상”이 값을 계산해 줘”

서버가 create_service로 콜백을 등록해 두면, 클라이언트가 요청할 때마다 그 콜백이 불려서 응답을 채워 돌려줘요. 콜백은 요청 객체를 받아 계산한 뒤, 미리 만들어져 넘어온 응답 객체에 값을 넣고 그대로 반환해야 해요. 반환을 빠뜨리면 응답에 None이 나가서 클라이언트가 조용히 실패해요.

콜백에서 예외가 나면 서버가 죽어요

예외 하나가 어디까지 번지나
rclpy는 콜백에서 발생한 예외를 실행기로 그대로 전파해요.
감싸지 않으면콜백에서 예외int64 범위 초과 등실행기로 전파rclpy가 그대로 올림spin() 중단서버 노드가 멈춤클라이언트 무한 대기응답이 안 오는 줄 모름try로 감싸면콜백에서 예외같은 입력except가 잡음로그에 원인 남김안전한 기본값 응답success·message 필드로서버는 계속 돎다음 요청 처리무한 대기에 걸린 프로세스는 죽은 프로세스보다 발견이 늦어서 더 곤란해요.넓은 범위의 except는 보통 피해야 할 패턴이지만, 서비스 콜백에서는 "죽지 않고 응답은 돌려준다"가 더 중요해서 예외적으로 허용해요.

이 예외가 어디서 나는지 보면 감이 잡혀요. 두 수의 합을 응답의 int64 필드에 넣는데, 파이썬의 int는 자릿수 제한이 없는 반면 ROS 2의 int64는 8바이트 고정이에요. 파이썬에서는 아무렇지 않게 계산되던 큰 수가 응답 필드에 대입되는 순간, 메시지 정의에서 생성된 검증 코드가 범위를 벗어났다며 예외를 던져요. 입력이 조금만 커져도 서버가 죽을 수 있다는 뜻이에요.

콜백을 감싸서 죽지 않게 만들어요

INT64_MIN, INT64_MAX = -(2**63), 2**63 - 1
try:
    result = request.a + request.b
    if not (INT64_MIN <= result <= INT64_MAX):
        raise OverflowError('결과가 int64 범위를 벗어남')
    response.sum = result
except OverflowError as exc:
    self.get_logger().error(str(exc))
    response.sum = 0
except Exception as exc:
    self.get_logger().error(f'처리 실패: {exc}')
    response.sum = 0
return response

넓은 범위의 except는 보통 피해야 할 패턴이지만, 서비스 콜백에서는 “죽지 않고 응답은 돌려준다”가 더 중요해서 예외적으로 허용해요. 대신 반드시 로그에 원인을 남겨 나중에 추적할 수 있게 해요.

기본값 0이 남기는 모호함
실패했을 때 sum에 0을 넣으면, 클라이언트는 그게 정말 합이 0인지 실패해서 0인지 구분할 수 없어요. 그래서 실무에서는 응답에 bool successstring message 필드를 함께 둬요. 같이 만든 모터 on/off 서비스의 응답은 이미 그 형태로, 성공 여부와 사유 문자열을 함께 돌려줘요.

클라이언트도 자신을 지켜야 해요

서버만 고쳐서는 부족해요.

지점방어안 하면
wait_for_service제한 시간서버가 안 뜨면 영원히 대기
spin_until_future_complete제한 시간응답이 안 오면 영원히 대기
future.result()None 확인 후 접근이번엔 클라이언트가 예외로 죽음

future.result()는 호출이 실패하면 None을 돌려주는데, 확인 없이 곧바로 response.sum에 접근하면 클라이언트 쪽이 죽어요. 결과가 None인지 먼저 보고 접근하는 습관이 필요해요.

정리하면 서비스는 두 규칙으로 지켜요. 서버는 콜백을 감싸 어떤 요청에도 죽지 않고 응답을 돌려주고, 클라이언트는 타임아웃으로 무한 대기를 끊어내요. 계산 로직 몇 줄보다 이 두 방어를 갖추는 게 실제로 돌아가는 서비스에 훨씬 가까운 일이었어요.