본문 바로가기
AI 오케스트레이션 캠프/미니프로젝트

3번째 미니프로젝트 1일차 회고|AutoHealing·SQLite·장애 복구

by 랩보다 AI 더 잘해지기 2026. 10. 12.
728x90

1. 새로운 미니프로젝트, 어르신 돌봄 서비스

3번째 미니프로젝트가 시작됐다. 이번 프로젝트는 어르신의 바이탈 정보와 실내 환경, IoT 기기 상태, CCTV 분석 결과 등을 종합해 현재 상태를 확인하고 보호자에게 필요한 대응을 안내하는 멀티에이전트 서비스다.

처음에는 폭염·한파 상황에서 안전 정보를 제공하는 서비스로 기획했지만, 이후 여러 데이터를 활용하는 어르신 돌봄 서비스로 방향이 바뀌었다. 이에 따라 내가 담당한 AutoHealing의 역할도 함께 확대됐다.

초기에는 날씨와 쉼터 조회 과정의 장애를 처리하는 데 초점을 맞췄다면, 변경된 서비스에서는 바이탈 정보 조회와 기기 실행 등 다양한 호출의 실패를 처리하고 복구 결과를 기록해야 했다.

이번 첫 개발 단계에서는 모든 팀원의 구현이 완성될 때까지 기다리기보다, 먼저 독립적으로 개발할 수 있는 저장·복구 기반을 구축했다.

2. AutoHealing은 왜 필요한가?

AutoHealing은 말 그대로 시스템에 문제가 발생했을 때 정해진 정책에 따라 복구를 시도하는 기능이다.

예를 들어 어르신의 바이탈 정보를 조회하는 과정에서 일시적인 오류가 발생했다고 가정해 보자. 단순히 오류 메시지를 반환하고 종료하면 서비스의 다음 단계가 정상적으로 진행되지 않을 수 있다.

이때 다음과 같은 대응을 고려할 수 있다.

  1.  
  2. 실패한 요청을 제한된 횟수만큼 재시도한다.

  3. 재시도해도 실패하면 이전에 정상적으로 확보한 캐시 데이터를 확인한다.

  4. 호출자가 허용한 경우에만 Mock 데이터를 대체 수단으로 사용한다.

  5. 복구가 성공했는지, 어떤 방법을 사용했는지 기록한다.

  6. 복구할 수 없다면 실패 상태를 명확하게 반환한다.

여기서 중요한 점은 무조건 재시도하거나 임의의 데이터로 결과를 채우는 것이 아니라, 실패 원인과 데이터의 유효성을 구분해 적절한 복구 방법을 선택하는 것이다.

특히 어르신 돌봄 서비스에서는 오래된 바이탈 정보를 최신 측정값처럼 취급하면 잘못된 판단으로 이어질 수 있다. 따라서 복구 기능을 구현할 때도 단순히 서비스가 중단되지 않는 것뿐 아니라 데이터의 정확성과 신뢰성을 함께 고려해야 했다.

이번 프로젝트에서 내가 맡은 AutoHealing은 이러한 복구 과정을 공통화하고, 장애 발생부터 최종 결과까지 추적할 수 있도록 기반을 만드는 역할이다.

3. 먼저 구축한 SQLite 저장 기반

가장 먼저 진행한 작업은 SQLite를 이용한 데이터 저장 구조와 접근 코드를 만드는 것이었다.

SQLite는 별도의 데이터베이스 서버를 실행하지 않고 파일 기반으로 데이터를 저장할 수 있는 데이터베이스다. 이번 프로젝트에서는 캐시와 실행 로그, 가상 어르신 프로필, 바이탈 측정 이력을 저장하는 데 활용했다.

저장 구조를 마련한 이유는 복구 과정에서 필요한 데이터를 보관하고, 어떤 요청이 어떤 결과로 끝났는지 추적할 수 있도록 하기 위해서다.

측정 시각과 서버 수신 시각 구분하기

바이탈 데이터를 저장하면서 중요하게 고려한 부분은 측정 시각과 서버 수신 시각을 구분하는 것이었다.

  • measured_at: 실제로 바이탈을 측정한 시각

  • 서버 수신 시각: 측정 데이터가 서버에 도착한 시각

두 시각은 항상 일치하지 않는다. 네트워크 지연이나 데이터 전송 문제로 인해 과거에 측정한 값이 나중에 도착할 수도 있기 때문이다.

예를 들어 오전 10시에 측정한 데이터가 네트워크 문제로 오전 10시 10분에 도착하고, 오전 10시 5분에 측정한 데이터가 오전 10시 6분에 도착했다고 가정해 보자.

서버 수신 시각만 기준으로 최신 데이터를 선택하면 오전 10시 측정값이 더 최신인 것처럼 잘못 판단할 수 있다.

따라서 바이탈 조회는 실제 측정 시각인 measured_at을 기준으로 최신 데이터를 선택하도록 구성했다.

이 작업을 통해 데이터를 저장하는 것만큼이나 어떤 시간 정보를 기준으로 데이터를 해석할 것인지 결정하는 일이 중요하다는 점을 확인했다.

4. 바이탈 저장·조회 어댑터 구현

다음으로 바이탈 데이터를 저장하고 조회할 수 있는 어댑터를 준비했다.

어댑터(Adapter)는 서로 다른 코드나 시스템이 정해진 인터페이스를 통해 데이터를 주고받을 수 있도록 연결하는 역할을 한다. 이번 작업에서는 API 담당자가 바이탈 데이터를 저장하거나 조회할 때 내부 DB 구조를 직접 다루지 않아도 되도록 연결 지점을 마련했다.

주요 구현 내용은 다음과 같다.

  • 바이탈 데이터 저장

  • 저장 결과로 DB 행 ID 반환

  • 내부 reading_id를 이용한 중복 저장 방지

  • 바이탈 측정 이력 조회

여기서 중복 저장 방지는 적용 범위를 구분해야 한다.

동일한 내부 reading_id로 같은 데이터를 다시 저장하면 기존 행 ID를 반환하도록 했다. 하지만 요청을 다시 보낼 때마다 새로운 ID가 생성된다면 이를 자동으로 동일 데이터라고 판단하는 것은 아니다.

즉, 특정 ID를 기준으로 한 중복 처리는 구현했지만, 모든 재요청을 자동으로 식별하는 범용적인 멱등성 처리를 완성한 것은 아니다.

멱등성(Idempotency)은 같은 요청을 여러 번 실행해도 결과가 중복해서 발생하지 않도록 하는 성질이다. 장애가 발생한 요청을 재시도하는 시스템에서는 중요한 개념이다.

이번 구현을 통해 중복 저장을 방지하려면 단순히 저장 함수만 만드는 것이 아니라, 데이터의 고유 식별자를 어떻게 관리할 것인지도 함께 정해야 한다는 점을 확인했다.

5. 조회 실패에 대비한 재시도와 캐시 복구

AutoHealing의 핵심 작업 중 하나는 조회 실패를 처리하는 공통 복구 함수를 만드는 것이었다.

정상적으로 조회된 결과는 캐시에 저장하고, 일시적인 조회 실패가 발생하면 제한된 횟수만큼 재시도하도록 구성했다.

재시도 이후에도 실패하면 캐시를 확인하고, 필요한 경우 호출자가 명시적으로 제공한 Mock 대체 데이터를 사용할 수 있도록 했다.

다만 복구 과정에서 몇 가지 예외 상황을 구분해야 했다.

캐시가 있다고 항상 사용할 수 있는 것은 아니다

캐시는 이전에 조회한 결과를 임시로 보관해 두었다가 필요할 때 활용하는 저장 공간이다. 외부 조회가 실패했을 때 캐시를 사용하면 서비스의 연속성을 높일 수 있다.

하지만 캐시 데이터가 항상 유효한 것은 아니다.

  • 최근 데이터가 없을 수 있다.

  • 데이터가 존재하더라도 유효기간이 지났을 수 있다.

  • DB 자체에 장애가 발생했을 수 있다.

이런 상황을 모두 동일하게 처리하면 불필요한 조회가 반복되거나 잘못된 데이터가 복구 결과로 사용될 수 있다.

따라서 이번 구현에서는 최근 데이터가 없거나 만료된 경우 같은 DB를 반복해서 조회하지 않도록 했으며, DB 자체에 장애가 발생하면 동일한 SQLite에 저장된 캐시를 복구 수단으로 사용하지 않도록 구분했다.

이는 캐시가 DB와 독립된 저장소가 아니라 같은 DB에 의존한다면, DB 장애 시 캐시 역시 이용할 수 없기 때문이다.

또한 Mock 데이터는 호출자가 명시적으로 제공한 경우에만 사용하도록 했다. 복구에 실패했다고 해서 임의의 가상 데이터를 정상 데이터처럼 반환하지 않도록 하기 위해서다.

이번 작업에서 중요하게 생각한 부분은 복구 성공률을 높이는 것만이 아니었다. 복구할 수 없는 상황을 정확히 구분하고, 사용할 수 없는 데이터를 사용하지 않는 것 역시 AutoHealing의 역할이었다.

6. 장애 주입과 실행 로그

복구 기능이 실제로 동작하는지 확인하려면 정상적인 상황뿐 아니라 의도적으로 실패 상황을 만들어 검증할 필요가 있다.

이를 위해 시연 요청별로 다음 장애를 주입할 수 있도록 준비했다.

  • vital: 바이탈 조회 관련 장애

  • iot: IoT 관련 장애

  • vision: CCTV 영상 분석 관련 장애

  • llm: 대규모 언어 모델 호출 관련 장애

장애 주입은 특정 기능의 실패 상황을 의도적으로 발생시켜 복구 로직이 올바르게 동작하는지 확인하는 방법이다.

여기서 고려한 부분은 전역 상태 대신 요청별 설정을 사용했다는 점이다. 전역 상태를 이용해 장애 여부를 관리하면 한 요청에 설정한 장애가 다른 요청의 실행에도 영향을 줄 수 있다.

반면 요청별 설정을 사용하면 시연 요청마다 서로 다른 장애 상황을 지정할 수 있어 독립적인 검증에 유리하다.

실행 로그에는 시도 번호와 함께 성공, 재시도, 대체, 실패 상태 및 복구 방법을 기록하도록 했다.

또한 SQLite에 로그를 저장하는 작업마저 실패할 경우를 고려해 최소한의 오류 정보를 메모리에 남기고 호출자에게 전달하도록 했다.

이렇게 하면 로그 저장에 실패했다는 이유로 원래 발생한 오류까지 파악할 수 없게 되는 상황을 줄일 수 있다.

복구 기능은 결과만 정상적으로 반환한다고 완성되는 것이 아니다. 어떤 문제가 발생했고, 몇 번 재시도했으며, 캐시나 Mock 중 어떤 방법으로 복구했는지를 확인할 수 있어야 이후 문제를 분석할 수 있다.

7. 테스트 80개 통과와 GitHub 공유

초기 구현과 보완 작업을 진행한 뒤 백엔드 테스트를 실행했고, 총 80개가 통과했다. 이 수치에는 기존 placeholder 테스트 1개가 포함되어 있다.

테스트가 통과했다는 사실은 구현한 코드가 준비된 테스트 조건에서 기대한 동작을 수행했다는 의미다. 다만 모든 통합 상황이나 예외 조건이 검증됐다는 뜻은 아니므로, 다른 팀원의 코드와 연결한 이후에도 추가 검증이 필요하다.

작업 내용은 autohealing_jso 브랜치에 커밋하고 푸시한 뒤 한글 이슈와 PR을 작성해 main에 병합했다.

개발 계획서와 연결 가이드도 docs 디렉터리에 함께 올렸다.

팀 프로젝트에서는 내 코드가 정상적으로 동작하는 것뿐 아니라, 다른 팀원이 해당 기능을 어떻게 사용해야 하는지 이해할 수 있어야 한다. 특히 여러 모듈을 연결하는 과정에서는 함수의 입력값과 반환값, 실패 시 동작이 명확하지 않으면 통합 단계에서 문제가 발생하기 쉽다.

따라서 구현 코드와 함께 사용 방법을 공유하는 것도 이번 작업의 중요한 부분이었다.

8. 팀 코드 통합 후 발견한 문제

초기 구현을 마친 뒤 최신 main 브랜치의 코드를 확인하면서 실제 통합 과정에서 보완해야 할 사항들이 드러났다.

1) 부분 바이탈 데이터 지원

수집 API에서 일부 바이탈 값만 전달할 수 있다면 저장 함수와 DB도 이러한 형태를 지원해야 한다.

예를 들어 혈압과 체온 중 일부 값이 누락됐을 때, 이를 무조건 오류로 처리하거나 임의의 값으로 채우면 원본 데이터의 의미가 달라질 수 있다.

따라서 누락값을 어떻게 표현하고 저장할 것인지 수집·저장·조회 단계에서 일관되게 정해야 한다.

2) 시나리오 정보의 일관성

시뮬레이터에서 전달한 scenario 정보가 저장과 조회 과정에서도 일관되게 유지되는지 확인해야 했다.

시나리오별로 다른 상황을 재현하는 프로젝트에서는 요청 단계에서 설정한 조건이 중간에 누락되면 의도한 결과를 재현하기 어려워진다.

3) CCTV 재시도 책임 정리

CCTV 분석 과정에서 내부 모듈과 외부 호출 계층이 모두 재시도를 수행하면 불필요한 호출이 중첩될 수 있다.

따라서 어느 계층에서 재시도를 담당할 것인지 정리하고, 각 계층의 책임을 명확하게 나눌 필요가 있다.

4) 분석 결과의 필수 관찰값과 confidence 보존

분석 성공 결과에 포함되는 필수 관찰값과 숫자형 confidence를 그대로 보존할 필요가 있었다.

confidence는 모델이 특정 분석 결과에 대해 얼마나 확신하는지를 나타내는 수치다. 이 값을 문자열이나 다른 형태로 변환하거나 필수 관찰값을 누락하면 후속 처리에서 문제가 발생할 수 있다.

따라서 분석 결과의 데이터 형식과 필수 필드를 명확히 하고, 각 모듈이 동일한 계약을 따르도록 맞춰야 한다.

5) 복구 지표와 화면 로그 연결

복구 기능의 동작을 확인할 수 있도록 복구율과 복구시간 지표를 마련하고, 실행 로그를 화면에서도 확인할 수 있도록 연결하는 작업이 필요했다.

이러한 지표가 있으면 단순히 복구가 성공했는지 확인하는 수준을 넘어, 어떤 장애가 자주 발생하고 복구에 얼마나 시간이 걸리는지도 분석할 수 있다.

이번 통합 과정에서 확인한 문제들은 각각 별개의 기능처럼 보이지만, 공통적으로 여러 모듈 사이의 데이터 형식과 책임을 맞추는 작업이라는 점에서 연결되어 있었다.

9. 첫날의 회고: 기능 구현만큼 중요한 데이터 계약

이번 1일차에는 AutoHealing의 기본 저장 구조와 복구 로직을 마련하고, 테스트와 GitHub 공유까지 진행했다.

처음에는 조회 실패 시 재시도하고 캐시를 활용하는 기능을 구현하는 것이 핵심이라고 생각할 수 있다. 하지만 팀 코드와 연결하는 단계에서는 개별 함수의 동작만큼이나 각 모듈이 데이터를 어떤 형식으로 주고받는지가 중요했다.

부분 바이탈을 허용할 것인지, 누락값을 어떻게 표현할 것인지, 실제 측정 시각을 어떤 기준으로 사용할 것인지, 재시도는 어느 계층에서 담당할 것인지와 같은 결정이 모두 연결되어 있었다.

특히 장애 복구 과정에서 데이터를 무조건 채우거나 오래된 값을 최신 데이터처럼 취급하면 시스템이 정상적으로 실행되더라도 결과는 잘못될 수 있다.

결국 안정적인 복구를 위해서는 세 가지가 함께 갖춰져야 한다.

  • 명확한 데이터 계약: 모듈 간 입력값과 반환값, 필수 필드 및 누락값 처리 방식을 통일한다.

  • 예외 상황을 고려한 복구 정책: 재시도와 캐시, Mock 대체의 조건을 구분한다.

  • 추적 가능한 검증 체계: 테스트와 로그를 통해 복구 과정과 결과를 확인한다.

다음 작업에서는 부분 바이탈 데이터 저장 지원을 우선적으로 진행할 예정이다. 수집 API에서 허용하는 데이터 형태를 DB와 조회 계층까지 일관되게 반영하는 것이 첫 번째 과제다.

이번 미니프로젝트의 첫날은 단순히 AutoHealing 코드를 작성하는 데서 끝나지 않았다. 실제 서비스 흐름에 연결하기 위해 무엇을 통일해야 하는지 확인하고, 개별 구현을 팀 전체의 동작으로 이어가기 위한 기반을 마련한 날이었다.

핵심 정리

  • SQLite를 이용해 캐시, 실행 로그, 가상 어르신 프로필, 바이탈 측정 이력 저장 기반을 구축했다.

  • measured_at과 서버 수신 시각을 구분해 실제 측정 시각 기준으로 데이터를 조회하도록 했다.

  • 제한된 재시도, 캐시 복구, 명시적 Mock 대체를 포함한 AutoHealing 경로를 구현했다.

  • 요청별 장애 주입과 복구 로그를 준비하고, 백엔드 테스트 80개 통과를 확인했다.

  • 팀 코드 통합 과정에서 부분 바이탈 지원, 시나리오 전달, 재시도 책임, 분석 결과 보존 등의 보완점을 확인했다.

  • 다음 작업은 누락값을 보존하면서 수집·저장·조회 간 데이터 계약을 일치시키는 것이다.

#AI오케스트레이션 #멀티에이전트 #미니프로젝트 #AutoHealing #Python #SQLite #장애복구 #데이터계약 #백엔드개발 #개발자회고

댓글