목록으로

AI · 바이브 코딩

바이브 코딩 실전 18화: 정상 작동만 확인하면 반드시 사고 납니다

BeanCon
바이브 코딩으로 만든 Flutter 앱에서 로딩, 빈 화면, 오류 상태를 명확하게 처리하는 방법을 설명하는 대표 이미지

바이브 코딩 실전 18화입니다. Flutter와 Supabase 기반 MyLog를 예제로 정상 상황뿐 아니라 Loading, Success, Empty, Error 상태를 구분해 처리하는 방법을 정리했습니다. 네트워크 오류, 중복 요청, 재시도, Session 만료, 사용자 메시지와 로그 분리, Empty State와 Error State 디자인, 실패 케이스 테스트와 UI State Rules까지 다룹니다.

목차

지난 편에서는 로그인과 권한의 차이를 살펴봤습니다.

이제 MyLog는 로그인한 사용자를 확인하고,

Supabase RLS를 통해 자기 기록에만 접근할 수 있습니다.

구조만 보면 꽤 그럴듯합니다.

앱을 실행합니다.

로그인합니다.

기록 목록이 나옵니다.

새 기록을 작성합니다.

저장도 잘 됩니다.

상세 화면도 열립니다.

좋습니다.

그런데 여기서 질문 하나를 바꿔보겠습니다.

잘될 때 말고, 안 될 때는 어떻게 되나요?

이 질문을 시작하면 갑자기 확인해야 할 것이 많아집니다.

인터넷이 느리면?

서버 응답이 없으면?

기록이 하나도 없으면?

저장 중 오류가 나면?

로그인 세션이 만료되면?

사용자가 버튼을 연속으로 누르면?

제품은 정상적인 순간보다 정상적이지 않은 순간에 정체가 드러나는 경우가 많습니다.

오늘은 MyLog를 조금 더 질긴 앱으로 만들어보겠습니다.

1. 화면에는 정상 상태만 있는 것이 아니다

기록 목록 화면을 생각해보겠습니다.

처음 만들 때는 보통 이런 상태만 생각합니다.

Records 있음

↓

목록 표시

하지만 클라우드 데이터를 사용하기 시작하면 실제 화면은 최소 네 가지 상태를 가집니다.

Loading

Success

Empty

Error

한국말로 풀면

불러오는 중

↓

성공
├── 데이터 있음
└── 데이터 없음

또는

↓

오류

입니다.

Flutter는 선언형 UI 방식으로 현재 상태에 따라 화면을 다시 구성합니다. 공식 문서에서도 이를 흔히 UI는 State의 함수라는 개념으로 설명하고 있습니다. 즉 상태가 달라지면 화면도 그 상태에 맞게 달라져야 합니다.

오늘은 이 네 가지를 MyLog의 기본 화면 설계 원칙으로 만들겠습니다.

2. Loading, 사용자가 기다리고 있다는 것을 알려준다

MyLog를 실행합니다.

Supabase에 Record 목록을 요청합니다.

응답이 오기까지 0.1초일 수도 있고,

1초일 수도 있고,

네트워크 상황에 따라 훨씬 오래 걸릴 수도 있습니다.

그동안 아무것도 표시하지 않으면 사용자는 이렇게 생각합니다.

앱이 멈췄나?

그래서 Loading 상태가 필요합니다.

예를 들면

기록을 불러오는 중...

또는 간단한 로딩 인디케이터를 보여줄 수 있습니다.

⏳

기록을 불러오고 있습니다.

엄청난 기능은 아닙니다.

하지만 사용자에게

요청을 받았고 지금 처리 중입니다.

라고 알려주는 신호입니다.

3. 로딩 화면은 오래 보여주는 것이 목적이 아니다

여기서 착각하면 안 됩니다.

예쁜 로딩 애니메이션을 만드는 것이 오늘 목표는 아닙니다.

Loading의 목적은 아주 단순합니다.

작업 시작

↓

사용자에게 진행 중임을 알림

↓

성공 또는 실패

↓

Loading 종료

입니다.

특히 마지막이 중요합니다.

오류가 발생했는데도 Loading이 계속 남아 있다면?

빙글빙글...

빙글빙글...

빙글빙글...

사용자는 영원히 기다립니다.

지난 실기기 테스트에서 우리가 일부러 인터넷을 끊어봤던 이유도 바로 이런 문제를 찾기 위해서였습니다.

4. Flutter에서도 비동기 결과에 따라 화면을 나눈다

Flutter 공식 네트워크 예제에서는 FutureBuilder를 이용해 비동기 작업의 상태에 따라

Loading

Data

Error

를 각각 다르게 표시하는 방식을 보여줍니다. 요청이 실패했는데 Error 상태를 처리하지 않으면 로딩 화면이 계속 표시되는 것처럼 잘못 보일 수 있다는 점도 함께 확인할 수 있습니다.

MyLog에서 반드시 FutureBuilder를 써야 한다는 뜻은 아닙니다.

우리가 기억할 것은 구조입니다.

Future 시작

↓

기다리는 중
→ Loading

↓

결과 있음
→ Success

↓

예외 발생
→ Error

이 흐름을 명확하게 관리해야 합니다.

5. Success도 사실 두 종류다

데이터 요청이 성공했습니다.

그러면 성공 화면만 만들면 될까요?

조금 더 나눠야 합니다.

데이터가 있는 성공

오늘의 기록
2026.08.23

바이브 코딩 메모
2026.08.22

데이터가 없는 성공

아직 작성한 기록이 없습니다.

첫 기록을 남겨보세요.

[기록 작성]

두 상황 모두 API 요청은 성공했습니다.

차이는 데이터가 있느냐 없느냐입니다.

바로 Empty State입니다.

6. Empty는 오류가 아니다

기록이 하나도 없다고

오류가 발생했습니다.

라고 보여주면 이상합니다.

아직 아무 기록도 작성하지 않은 신규 사용자라면 데이터가 없는 것이 정상입니다.

따라서

Empty

≠

Error

입니다.

이 구분은 생각보다 중요합니다.

예를 들어 검색 기능을 나중에 추가했다고 해보겠습니다.

검색 결과가 0개입니다.

이것도 오류가 아닙니다.

검색 결과가 없습니다.

라고 안내하면 됩니다.

데이터가 없다는 것도 하나의 정상적인 화면 상태입니다.

7. 좋은 Empty State는 다음 행동도 알려준다

단순하게

데이터 없음

이라고 끝내는 것보다 조금 친절하게 만들 수 있습니다.

MyLog에서는

아직 작성한 기록이 없습니다.

첫 기록을 남겨보세요.

[새 기록 작성]

정도가 좋습니다.

사용자는 지금 상황을 이해하고 바로 다음 행동을 할 수 있습니다.

Empty State에서 중요한 질문은 두 가지입니다.

왜 아무것도 없는가?

↓

이제 무엇을 하면 되는가?

앱이 이 두 가지를 알려주면 빈 화면이 덜 당황스럽습니다.

8. 진짜 중요한 것은 Error State다

이제 서버 요청이 실패했다고 해보겠습니다.

가능한 이유는 많습니다.

인터넷 연결 없음

서버 응답 지연

Supabase 오류

권한 오류

Session 문제

잘못된 데이터

예상하지 못한 예외

그런데 사용자에게 이런 메시지를 보여주면 곤란합니다.

PostgrestException
AuthApiException
SocketException

사용자는 개발자가 아닙니다.

오류 메시지는 사용자가 무엇을 해야 하는지 알 수 있게 만들어야 합니다.

9. 개발자 오류와 사용자 메시지는 분리한다

예를 들어 네트워크 오류가 발생했습니다.

개발 로그에는 이런 정보가 필요할 수 있습니다.

NetworkException
Request failed
RecordService.getRecords()

하지만 사용자 화면에서는

기록을 불러오지 못했습니다.

네트워크 연결을 확인한 뒤 다시 시도해주세요.

[다시 시도]

정도면 충분합니다.

개발자

문제 원인 필요


사용자

다음 행동 필요

입니다.

둘을 같은 메시지로 만들 필요가 없습니다.

10. 오류 화면에는 가능하면 탈출구를 만든다

오류 메시지만 보여주고 끝내면 사용자는 할 일이 없습니다.

기록을 불러올 수 없습니다.

끝.

앱을 껐다 켜야 할까요?

기다려야 할까요?

로그아웃해야 할까요?

모릅니다.

그래서 복구 가능한 오류라면

[다시 시도]

를 제공하는 것이 좋습니다.

흐름은 단순합니다.

Record 조회

↓

실패

↓

Error State

↓

다시 시도

↓

재요청

↓

Success 또는 Error

제품은 오류가 없는 프로그램이 아니라 오류에서 돌아올 길이 있는 프로그램에 가깝습니다.

11. 다시 시도 버튼도 막 눌리게 두지 않는다

사용자가 Record를 저장합니다.

응답이 조금 늦습니다.

버튼을 다시 누릅니다.

또 누릅니다.

또 누릅니다.

갑자기 Record가 네 개 생겼습니다.

바이브 코딩 기록
바이브 코딩 기록
바이브 코딩 기록
바이브 코딩 기록

기록 증식 이벤트입니다. 🫠

그래서 저장 중에는 상태를 하나 둡니다.

isSaving = true

개념적으로는

저장 버튼 클릭

↓

Saving

↓

버튼 비활성화

↓

서버 응답

↓

Success / Error

↓

버튼 다시 활성화

입니다.

12. 화면마다 상태가 조금씩 다르다

모든 화면이 똑같은 상태를 가지는 것은 아닙니다.

예를 들어 Record List는

Loading
Success
Empty
Error

가 필요합니다.

Record Create는 조금 다릅니다.

Idle

Saving

Validation Error

Save Error

Success

로그인 화면 역시

Idle

Signing In

Auth Error

Network Error

Success

처럼 볼 수 있습니다.

이렇게 생각하면 상태를 훨씬 구체적으로 설계할 수 있습니다.

13. bool을 계속 늘리는 것도 조심하자

처음에는 상태가 간단합니다.

bool isLoading = false;

하나면 충분합니다.

그런데 앱이 커지기 시작하면

isLoading
isSaving
hasError
isEmpty
isRefreshing
isLoggedIn

같은 값이 계속 늘어날 수 있습니다.

그러다 이런 이상한 상태도 가능해집니다.

isLoading = true
hasError = true

지금 로딩 중인가요?

오류가 난 건가요?

둘 다인가요?

상태가 복잡해지면 하나의 명확한 상태 모델로 표현하는 방법도 고려할 수 있습니다.

예를 들어

RecordListState

loading

loaded

empty

error

처럼 말입니다.

현재 MyLog에서 거대한 상태관리 라이브러리를 추가할 필요는 없지만,

화면이 어떤 상태에 있을 수 있는지는 명확하게 정의하는 습관을 들이겠습니다.

14. 이번에도 AI에게 먼저 상태를 찾게 한다

바로 코드를 바꾸지 않습니다.

AI에게 이렇게 요청해볼 수 있습니다.

현재 MyLog 화면을 UI State 관점에서 분석해줘.

아직 코드를 수정하지 마.

다음 화면을 확인해줘.

- Login
- Record List
- Record Create
- Record Detail

각 화면에서 발생 가능한 상태를

1. Loading
2. Success
3. Empty
4. Error
5. 사용자 입력 처리 상태

기준으로 정리해줘.

추가로

- 무한 Loading 가능성
- 오류 후 복구 경로가 없는 부분
- 버튼 중복 클릭 가능성
- 서버 오류를 그대로 사용자에게 표시하는 부분

도 찾아줘.

현재 구조를 최대한 유지해서 분석해줘.

이제 AI에게

오류 처리 좀 해줘.

라고 두루뭉술하게 요청하지 않아도 됩니다.

15. MyLog의 Record List부터 정리해보자

현재 Record 목록의 상태를 명확하게 정의해보겠습니다.

Loading

기록을 불러오는 중...

Success

오늘의 기록

Flutter 실기기 테스트
바이브 코딩 정리

Empty

아직 작성한 기록이 없습니다.

[첫 기록 작성]

Error

기록을 불러오지 못했습니다.

네트워크 연결을 확인해주세요.

[다시 시도]

이 네 가지가 서로 섞이지 않게 만듭니다.

16. 데이터 요청을 build 안에서 무작정 반복하지 않는다

Flutter에서 네트워크 요청을 붙이다 보면 이런 실수가 생길 수 있습니다.

화면이 다시 그려질 때마다 API를 다시 호출하는 것입니다.

Flutter 공식 네트워크 예제에서도 API 호출을 build() 안에서 직접 반복 실행하는 것을 권장하지 않습니다. build()는 생각보다 자주 호출될 수 있기 때문에 요청 결과를 상태로 보관하거나 적절한 생명주기에서 시작하도록 설명합니다.

즉 이런 흐름이 좋습니다.

화면 초기화

↓

Record 요청 시작

↓

결과 저장

↓

UI 갱신

반대로

UI 다시 그림

↓

API 요청

↓

UI 다시 그림

↓

또 API 요청

이 반복되면 예상하지 못한 요청 폭탄이 생길 수도 있습니다.

AI가 만든 코드에서 API 호출 위치도 한번 확인해볼 가치가 있습니다.

17. Retry를 누르면 Loading으로 돌아가야 한다

오류 화면에서

[다시 시도]

를 누릅니다.

이때 그냥 API만 다시 호출하면 화면 상태가 어색할 수 있습니다.

조금 더 자연스러운 흐름은

Error

↓

Retry

↓

Loading

↓

Success
또는
Error

입니다.

상태의 이동을 이렇게 생각하면 UI 구현도 단순해집니다.

18. State를 화면보다 먼저 적어보자

이제부터 화면을 만들거나 수정할 때 작은 습관 하나를 추가하겠습니다.

먼저 코드가 아니라 상태를 적습니다.

예를 들어

Record List

Initial
→ Loading

Loading
→ Success
→ Empty
→ Error

Error
→ Retry
→ Loading

간단한 상태 흐름입니다.

이렇게 적어두면 AI도

오류가 난 뒤 어디로 가야 하지?

를 추측할 필요가 줄어듭니다.

19. 기록 저장에도 상태 흐름을 만든다

Record Create에서는 이런 흐름이 됩니다.

Idle

↓

저장 버튼

↓

Validation

├── 실패
│    ↓
│ Validation Error
│
└── 성공
     ↓
   Saving
     ↓
   API 요청
     ↓
   Success / Error

저장 성공이면 목록으로 돌아갑니다.

저장 실패면 작성 화면에 그대로 남습니다.

이 부분도 중요합니다.

서버 저장이 실패했는데 목록으로 돌아가버리면 사용자는 저장된 줄 알 수 있습니다.

20. 오류가 나도 입력 내용은 가능하면 유지한다

긴 기록을 작성했습니다.

오늘 회의에서 논의한 프로젝트 내용...

1000자를 썼습니다.

저장 버튼을 눌렀습니다.

인터넷 오류.

그리고 입력창까지 초기화됐습니다.

사용자는 다시 1000자를 써야 합니다.

높은 확률로 앱을 좋아하지 않게 됩니다.

따라서 저장 실패 시에는 가능하면

입력 내용 유지

+

오류 메시지

+

다시 저장 가능

하도록 합니다.

이런 작은 차이가 제품 경험에 꽤 큰 영향을 줍니다.

21. 모든 Error를 하나로 합치지는 않는다

처음에는 이런 식으로 구현하고 싶어집니다.

오류가 발생했습니다.

모든 상황에 동일합니다.

간단합니다.

하지만 사용자가 해결할 수 있는 오류는 어느 정도 구분하는 것이 좋습니다.

예를 들면

네트워크 오류

→ 인터넷 연결을 확인해주세요.


인증 오류

→ 다시 로그인해주세요.


입력 오류

→ 제목을 입력해주세요.


일반 서버 오류

→ 잠시 후 다시 시도해주세요.

정도입니다.

그렇다고 DB 내부 오류 코드를 전부 사용자에게 번역할 필요는 없습니다.

사용자의 다음 행동이 달라지는 오류를 중심으로 구분하면 됩니다.

22. Session 만료도 생각해보자

사용자가 로그인한 상태로 오랫동안 앱을 사용하지 않았습니다.

다시 Record를 불러오는데 인증 관련 문제가 발생합니다.

이때 단순하게

기록을 불러오지 못했습니다.

라고만 하면 사용자는 해결 방법을 모릅니다.

인증 상태가 정말 유효하지 않다면

로그인 정보가 만료되었습니다.

다시 로그인해주세요.

[로그인]

처럼 인증 흐름으로 되돌려보낼 수 있습니다.

다만 모든 네트워크 오류를 Session 오류로 착각해서 강제 로그아웃시키면 안 됩니다.

인터넷 잠깐 끊겼다고 매번 로그인 다시 하라고 하면 상당히 피곤한 앱이 됩니다.

23. 사용자에게 보여주는 메시지는 짧게

AI에게 오류 메시지를 만들어달라고 하면 종종 굉장히 친절해집니다.

서버와 통신하는 과정에서 예상하지 못한 네트워크 예외가 발생하였습니다.
현재 네트워크 연결 상태를 점검하고 잠시 후 다시 시도하여 주십시오.

맞는 말입니다.

그런데 모바일 화면에서는 조금 무겁습니다.

MyLog처럼 작은 앱이라면

기록을 불러오지 못했습니다.

네트워크 연결을 확인해주세요.

정도면 충분합니다.

오류가 발생했다고 사용자를 논문 심사장으로 보내지 않겠습니다.

24. AI에게 구현 범위를 명확하게 준다

이번 작업을 실제로 요청해보겠습니다.

MyLog의 비동기 화면 상태 처리를 개선한다.

이번 작업 범위:

Record List
- Loading
- Success
- Empty
- Error
- Retry

Record Create
- Idle
- Validation Error
- Saving
- Save Error
- Success

Login
- Idle
- Signing In
- Auth Error
- Network Error
- Success

공통 요구사항:
- 요청 중 중복 버튼 클릭 방지
- 오류 발생 시 Loading 종료
- 사용자에게 내부 Exception 문자열을 그대로 노출하지 않기
- 복구 가능한 오류에는 다시 시도 경로 제공
- 저장 실패 시 입력 내용 유지
- 기존 데이터 구조와 RLS는 변경하지 않기
- 새로운 상태관리 패키지는 추가하지 않기

아직 코드를 수정하지 말고
현재 상태 처리 문제와 수정할 파일을 먼저 설명해줘.

이번에도 새 패키지는 추가하지 않습니다.

현재 구조로 해결 가능한 수준부터 정리합니다.

25. 오류 처리를 추가한다고 모든 catch를 비워서는 안 된다

AI가 이런 코드를 만들 수도 있습니다.

try {
  await loadRecords();
} catch (_) {
  // ignore
}

앱은 안 죽습니다.

굉장히 안정적으로 보입니다.

문제는 오류가 어디서 발생했는지 아무도 모릅니다.

오류 처리는

오류를 없애는 것

이 아닙니다.

예외 발생

↓

앱이 크래시하지 않게 처리

↓

사용자에게 적절한 상태 표시

↓

개발자가 원인을 추적할 정보 확보

까지 포함합니다.

모든 예외를 조용히 삼켜버리면 다음에 문제를 찾기가 더 어려워집니다.

26. 사용자 메시지와 로그를 분리한다

예를 들어 Record 조회가 실패했습니다.

화면에는

기록을 불러오지 못했습니다.

라고 표시합니다.

개발 환경에서는

RecordService.getRecords failed
Network timeout

같은 원인 정보를 확인할 수 있어야 합니다.

다만 지난 #16에서 배운 원칙은 그대로 유지합니다.

Access Token

Refresh Token

Secret

비밀번호

같은 민감정보를 로그에 남기지는 않습니다.

27. Error State도 화면 디자인의 일부다

UI 디자인을 하면 보통 정상 화면만 예쁘게 그립니다.

그런데 실제 제품에서는 오류 화면도 꽤 자주 보입니다.

그래서 Error State도

빨간 글씨 한 줄

로 대충 끝낼 필요는 없습니다.

MyLog에서는 단순하게

기록을 불러오지 못했습니다.

네트워크 연결을 확인해주세요.

[다시 시도]

정도로 일관되게 구성합니다.

너무 과한 경고 아이콘이나 공포스러운 빨간 화면은 필요 없습니다.

사용자가 해야 할 일만 명확하면 됩니다.

28. Empty State와 Error State는 모양도 달라야 한다

두 화면을 비교해보겠습니다.

Empty

아직 작성한 기록이 없습니다.

첫 기록을 남겨보세요.

[기록 작성]

Error

기록을 불러오지 못했습니다.

잠시 후 다시 시도해주세요.

[다시 시도]

버튼의 목적부터 다릅니다.

Empty

→ 새로운 데이터 만들기


Error

→ 원래 요청 다시 하기

따라서 한 화면을 재사용하면서 문구만 살짝 바꾸기보다 사용자의 다음 행동이 자연스럽게 드러나게 만드는 것이 좋습니다.

29. 상태 코드가 화면 곳곳에 퍼지기 시작하면 다시 구조를 본다

지난 편에서 리팩터링을 배웠습니다.

이번 작업으로

isLoading
isSaving
errorMessage
records

같은 상태 로직이 Screen 파일에 계속 늘어날 수 있습니다.

아직은 충분히 관리할 수 있습니다.

하지만 UI가

데이터 요청,

상태 전환,

오류 변환,

재시도 로직

까지 모두 가지기 시작하면 지난 #15에서 했던 역할 분리를 다시 검토할 수 있습니다.

Flutter의 현재 아키텍처 가이드도 UI는 데이터를 표시하고 사용자 입력을 처리하는 역할에 집중하고, 데이터 접근과 비즈니스 로직은 별도 계층으로 나누는 방향을 권장합니다. 다만 공식 문서 역시 이를 절대 규칙이 아니라 프로젝트 상황에 맞춰 적용할 지침으로 설명합니다.

우리는 여전히 MyLog 규모에 맞게 단순하게 갑니다.

30. 정상 케이스보다 실패 케이스를 많이 테스트해보자

이번 회차에서는 테스트 방식도 바꿔보겠습니다.

기존에는

로그인 성공

기록 저장 성공

기록 조회 성공

을 확인했다면,

이번에는 일부러 실패시킵니다.

테스트 1

인터넷 OFF

앱 실행

→ 무한 Loading이 아닌가?

테스트 2

인터넷 OFF

Record 저장

→ 입력 내용이 남아 있는가?

테스트 3

인터넷 다시 ON

Retry

→ 정상적으로 복구되는가?

테스트 4

제목 없이 저장

→ 네트워크 요청 전에 Validation 되는가?

테스트 5

저장 버튼 빠르게 연속 클릭

→ Record가 중복 생성되지 않는가?

테스트 6

새 계정 로그인

→ Empty State가 자연스럽게 나타나는가?

테스트 7

잘못된 로그인 정보

→ 개발자 Exception이 화면에 그대로 노출되지 않는가?

이제 테스트가 조금 못돼지고 있습니다.

좋은 방향입니다. 😎

31. 가장 위험한 오류는 사용자가 성공했다고 믿게 만드는 오류다

예를 들어 저장 버튼을 누릅니다.

앱이 바로 목록 화면으로 이동합니다.

사용자는 저장됐다고 생각합니다.

그런데 실제 API 요청은 실패했습니다.

서버에는 데이터가 없습니다.

이건 꽤 나쁜 오류입니다.

왜냐하면 사용자는 실패 사실조차 모르기 때문입니다.

따라서 중요한 데이터 변경에서는

요청

↓

서버 성공 확인

↓

성공 UI

순서를 명확하게 하는 것이 좋습니다.

성공하지 않았는데 성공한 척하면 안 됩니다.

32. 반대로 성공했는데 오류처럼 보이는 경우도 조심한다

조금 더 어려운 상황도 있습니다.

Record 저장 요청을 보냈습니다.

서버에는 정상 저장됐습니다.

그런데 응답이 돌아오는 순간 네트워크가 끊겼습니다.

앱에서는

저장 실패

로 판단할 수도 있습니다.

사용자가 다시 저장하면 중복 Record가 생길 가능성도 있습니다.

이런 문제는 나중에 재시도 전략, ID 생성 방식, 멱등성 같은 더 깊은 주제로 이어집니다.

현재 MyLog에서는 여기까지 복잡하게 구현하지 않겠습니다.

다만 한 가지는 기억해두겠습니다.

네트워크 오류라고 서버 작업까지 반드시 실패한 것은 아니다.

제품이 커지면 다시 만나게 될 문제입니다.

33. 오류를 없애는 것보다 상태를 명확하게 만든다

AI에게

오류 없게 만들어줘.

라고 말하면 목표가 애매합니다.

오류는 생길 수 있습니다.

네트워크도 끊깁니다.

서버도 실패합니다.

사용자도 잘못 입력합니다.

우리가 원하는 것은

오류가 없는 앱

보다는

오류가 발생해도

사용자가 상황을 이해하고

앱이 이상한 상태에 빠지지 않고

다시 시도할 수 있는 앱

입니다.

이 차이가 꽤 중요합니다.

34. PROJECT_RULES에도 상태 처리 원칙을 추가한다

이제 프로젝트 규칙에 몇 줄 추가해두겠습니다.

## UI State Rules

- 네트워크 또는 비동기 화면은 Loading, Success, Empty, Error 상태를 검토한다.
- 오류 발생 시 Loading 상태를 반드시 종료한다.
- 사용자에게 내부 Exception 문자열을 그대로 노출하지 않는다.
- 복구 가능한 오류에는 Retry 방법을 제공한다.
- 데이터 저장 중 중복 요청을 방지한다.
- 저장 실패 시 사용자 입력을 불필요하게 삭제하지 않는다.
- Empty 상태를 Error로 처리하지 않는다.
- API 호출을 UI rebuild마다 반복하지 않는다.
- 오류를 무시하기 위해 빈 catch를 사용하지 않는다.

이제 새로운 API 기능을 만들 때마다 기본적으로 이 기준을 확인할 수 있습니다.

35. 오늘의 삽질 방지 팁 🛠️

AI가 이렇게 보고했다고 해보겠습니다.

예외 처리를 추가해서 앱이 더 이상 크래시하지 않습니다.

좋습니다.

그다음 질문은 이것입니다.

예외가 발생했을 때

1. Loading은 종료되는가?
2. 사용자에게 어떤 화면이 나오는가?
3. 다시 시도할 수 있는가?
4. 기존 입력 데이터는 유지되는가?
5. 개발자는 원인을 추적할 수 있는가?

try-catch를 넣었다고 오류 처리가 끝난 것은 아닙니다.

크래시 대신 아무 일도 안 일어나는 앱도 꽤 답답합니다.

36. 오늘 MyLog는 기능보다 표정이 늘었다

이번 편에서는 새로운 핵심 기능을 거의 추가하지 않았습니다.

그런데 앱이 표현할 수 있는 상태가 많아졌습니다.

예전 MyLog는 사실상 표정이 하나였습니다.

🙂

정상

이제는

⏳
기다리는 중

🙂
정상

📭
데이터 없음

⚠️
오류

정도를 표현할 수 있습니다.

이게 생각보다 중요합니다.

사용자는 앱 내부에서 무슨 일이 벌어지고 있는지 볼 수 없기 때문입니다.

화면이 앱의 상태를 대신 설명해줘야 합니다.

37. 오늘의 완료 조건

이번 편도 체크해보겠습니다.

[ ] 비동기 화면에서 Loading, Success, Empty, Error를 구분했다.

[ ] Empty와 Error가 다른 상태라는 것을 이해했다.

[ ] Record List에 Loading 상태가 있다.

[ ] Record List에 Empty State가 있다.

[ ] Record List에 Error State가 있다.

[ ] 오류 화면에서 다시 시도할 수 있다.

[ ] Retry 시 다시 Loading 상태로 전환된다.

[ ] Record 저장 중 버튼 중복 클릭을 방지한다.

[ ] 저장 실패 시 사용자가 작성한 내용이 유지된다.

[ ] 제목 Validation은 서버 요청 전에 처리된다.

[ ] 사용자에게 내부 Exception 문자열을 그대로 보여주지 않는다.

[ ] 네트워크 오류와 인증 오류를 필요한 수준에서 구분한다.

[ ] 오류 발생 시 무한 Loading에 빠지지 않는다.

[ ] API 호출이 build 과정에서 불필요하게 반복되지 않는지 확인했다.

[ ] 인터넷을 실제로 끊어 실패 상황을 테스트했다.

[ ] PROJECT_RULES.md에 UI State 원칙을 추가했다.

여기까지 됐다면 MyLog는

정상적으로 동작하는 앱

에서

정상적이지 않은 상황도 어느 정도 버틸 수 있는 앱

으로 한 단계 올라왔습니다.

38. 그런데 사람이 매번 이 모든 걸 직접 확인해야 할까?

지금까지 우리는 직접 테스트를 많이 했습니다.

앱을 실행하고,

로그인하고,

기록을 작성하고,

인터넷을 끄고,

다시 켜고,

버튼을 여러 번 눌렀습니다.

이 방식은 굉장히 중요합니다.

하지만 기능이 계속 늘어나면 문제가 생깁니다.

Record 기능 하나를 수정할 때마다

회원가입 확인

로그인 확인

목록 확인

기록 작성

상세 확인

오류 확인

로그아웃 확인

을 전부 손으로 반복해야 합니다.

처음에는 5분입니다.

나중에는 30분,

한 시간으로 늘어날 수 있습니다.

그리고 사람은 반복 작업에서 꼭 하나씩 빼먹습니다.

이제 AI에게 코드만 작성하게 하지 말고

테스트 코드도 같이 작성하게 할 차례입니다.

다음 편

[바이브 코딩 실전 #19] AI에게 테스트 코드까지 시켜보자 | 비개발자를 위한 앱 테스트 입문

다음 편에서는

Unit Test,

Widget Test,

Integration Test가 무엇인지,

비개발자가 어디까지 알아야 하는지,

AI가 작성한 테스트를 무조건 믿으면 왜 안 되는지,

그리고 MyLog의 핵심 사용자 흐름을 자동으로 확인하는 방법까지 살펴보겠습니다.

코드가 빨라졌다면 테스트도 같이 빨라져야 합니다.

안 그러면 우리는 AI 뒤를 따라다니며 매일 버튼만 누르는 인간 QA 매크로가 될 수도 있습니다. 🤖🧪

연재 기준: 2026년 8월현재 Flutter 공식 문서는 선언형 UI에서 State가 UI를 결정하는 구조를 설명하며, 비동기 데이터 처리 예제에서도 Loading, Data, Error 상태를 구분해 렌더링하는 방식을 안내하고 있습니다. 또한 네트워크 요청을 build() 안에서 반복 실행하지 않도록 주의하고 있습니다. 실제 적용 시점에는 Flutter 최신 공식 문서를 함께 확인하는 것을 권장합니다.

#바이브코딩 #VibeCoding #AI코딩 #Flutter #Flutter상태관리 #로딩처리 #에러처리 #EmptyState #ErrorState #LoadingState #네트워크오류 #Flutter앱개발 #앱UX #비개발자앱개발 #MVP개발 #MyLog #바이브코딩실전

댓글

0

댓글을 불러오는 중입니다.