Menu
CoddyTech

finally로 정리하기

Coddy Flutter 여정의 데이터와 네트워크 섹션에 포함된 레슨. 27개 중 12번째.

호출이 성공하든 실패하든 반드시 해야 하는 작업이 있습니다. 스피너를 숨기거나 버튼을 다시 활성화하는 일입니다. 이를 try와 catch에 각각 작성하다 보면 둘 중 한 곳을 빠뜨리기 쉽습니다. Dart에는 이를 위한 세 번째 블록인 finally가 있습니다.

try {
  await saveName(name);
} catch (e) {
  // 오류를 표시합니다
} finally {
  // 항상 실행됩니다: try 이후 또는 catch 이후
}

finally는 매번 마지막에 실행됩니다. 성공하면 try 다음에, 실패하면 catch 다음에 실행됩니다. 따라서 호출 전에 설정한 것을 원래대로 되돌리는 작업을 한곳에서 처리할 수 있습니다.

여기서는 앱이 저장하는 동안 저장 버튼을 회색으로 비활성화합니다. 이때 onPressed: saving ? null : save를 사용합니다. onPressed가 null인 버튼은 누를 수 없습니다. saving이 다시 false가 되지 않으면 버튼은 계속 회색으로 비활성화된 상태로 남습니다.

challenge icon

챌린지

중급

필드를 비운 채 저장하세요. 오류가 표시되고 저장 버튼이 회색으로 유지되어 다시 시도할 수 없습니다. saving = false를 try 블록에서 꺼내 별도의 setState 안에 있는 finally 블록으로 옮기세요.

직접 해보기

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: NameScreen(),
    );
  }
}

class NameScreen extends StatefulWidget {
  const NameScreen({super.key});

  @override
  State<NameScreen> createState() => _NameScreenState();
}

class _NameScreenState extends State<NameScreen> {
  final controller = TextEditingController();
  String status = '';
  bool saving = false;

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  Future<void> saveName(String name) async {
    await Future.delayed(const Duration(seconds: 1));
    if (name.isEmpty) {
      throw Exception('The name cannot be empty');
    }
  }

  Future<void> save() async {
    setState(() {
      saving = true;
    });
    try {
      await saveName(controller.text);
      setState(() {
        status = 'Saved: ${controller.text}';
        saving = false;
      });
    } catch (e) {
      setState(() {
        status = '$e';
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Padding(
        padding: const EdgeInsets.all(24),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            TextField(
              controller: controller,
              decoration: const InputDecoration(labelText: 'Name'),
            ),
            const SizedBox(height: 16),
            ElevatedButton(onPressed: saving ? null : save, child: const Text('Save')),
            const SizedBox(height: 16),
            Text(status, style: const TextStyle(fontSize: 18)),
          ],
        ),
      ),
    );
  }
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

데이터와 네트워크의 모든 레슨

직접 연습해 보세요: 온라인 Flutter 컴파일러