주뇽's 저장소

[Flutter] FlutterSecureStorage를 사용하여 로그인 상태 관리하기 본문

웹개발/Flutter

[Flutter] FlutterSecureStorage를 사용하여 로그인 상태 관리하기

뎁쭌 2024. 5. 25. 09:05
728x90
반응형

FlutterSecureStorage를 사용하여 로그인 상태 관리하기

FlutterSecureStorage는 보안성과 신뢰성을 제공하는 패키지로, Flutter 애플리케이션에서 민감한 데이터를 안전하게 저장할 수 있다. 이 글에서는 Flutter 애플리케이션에서 FlutterSecureStorage를 사용하여 로그인 상태를 관리하는 방법에 대해 설명한다.

프로젝트 설정

Flutter 패키지 추가

먼저, Flutter 프로젝트를 생성하고 필요한 패키지를 추가한다.

 

pubspec.yaml 파일을 열고 flutter_secure_storageget 패키지를 추가한다.

dependencies:
  flutter:
    sdk: flutter
  get: ^4.3.8
  flutter_secure_storage: ^9.2.2

이후, 패키지를 설치한다.

flutter pub get

FlutterSecureStorage 설정

Android 설정

android/app/src/main/AndroidManifest.xml 파일에 필요한 퍼미션을 추가한다.

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application
        android:label="login_secure_storage"
        android:icon="@mipmap/ic_launcher">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <meta-data
              android:name="io.flutter.embedding.android.NormalTheme"
              android:resource="@style/NormalTheme"/>
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />
    </application>
    <uses-permission android:name="android.permission.INTERNET"/>
</manifest>

iOS 설정

ios/Runner/Info.plist 파일에 다음 항목을 추가한다.

<key>io.flutter.embedded_views_preview</key>
<true/>

ios/Podfile 파일을 열고 플랫폼 버전을 설정한다.

platform :ios, '10.0'

이후, 터미널에서 iOS 프로젝트의 의존성을 설치한다.

cd ios
pod install
cd ..

 

로그인 페이지 구현

lib/main.dart 파일을 수정하여 로그인 상태를 확인하고, 로그인 상태에 따라 다른 페이지를 보여주도록 한다.

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'login_page.dart';
import 'home_page.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  final FlutterSecureStorage secureStorage = const FlutterSecureStorage();

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      debugShowCheckedModeBanner: false,
      home: FutureBuilder(
        future: _checkLoginStatus(),
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return Center(child: CircularProgressIndicator());
          } else {
            if (snapshot.hasData && snapshot.data == true) {
              return HomePage();
            } else {
              return LoginPage();
            }
          }
        },
      ),
    );
  }

  Future<bool> _checkLoginStatus() async {
    String? isLoggedIn = await secureStorage.read(key: 'isLoggedIn');
    return isLoggedIn == 'true';
  }
}

로그인 페이지와 로직 구현

lib/login_page.dart 파일을 생성하여 로그인 페이지를 구현한다.

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'home_page.dart';

class LoginPage extends StatefulWidget {
  @override
  _LoginPageState createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  final _formKey = GlobalKey<FormState>();
  final FlutterSecureStorage secureStorage = const FlutterSecureStorage();
  final _usernameController = TextEditingController();
  final _passwordController = TextEditingController();

  Future<void> _login() async {
    if (_formKey.currentState!.validate()) {
      String username = _usernameController.text.trim();
      String password = _passwordController.text.trim();

      // TODO: Replace with your actual login logic
      if (username == 'admin' && password == 'password') {
        await secureStorage.write(key: 'isLoggedIn', value: 'true');
        Get.offAll(() => HomePage());
      } else {
        _showErrorDialog();
      }
    }
  }

  void _showErrorDialog() {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: Text('로그인 실패'),
        content: Text('아이디 또는 비밀번호를 확인해주세요.'),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: Text('확인'),
          ),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListView(
        children: [
          SizedBox(height: 70),
          Text('로그인', style: TextStyle(fontSize: 32), textAlign: TextAlign.center),
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: Form(
              key: _formKey,
              child: Column(
                children: [
                  TextFormField(
                    controller: _usernameController,
                    decoration: InputDecoration(labelText: '아이디'),
                    validator: (value) {
                      if (value == null || value.isEmpty) {
                        return '아이디를 입력해주세요.';
                      }
                      return null;
                    },
                  ),
                  TextFormField(
                    controller: _passwordController,
                    decoration: InputDecoration(labelText: '비밀번호'),
                    obscureText: true,
                    validator: (value) {
                      if (value == null || value.isEmpty) {
                        return '비밀번호를 입력해주세요.';
                      }
                      return null;
                    },
                  ),
                  SizedBox(height: 20),
                  ElevatedButton(
                    onPressed: _login,
                    child: Text('로그인'),
                  ),
                  TextButton(
                    onPressed: () {
                      // TODO: Navigate to signup page
                    },
                    child: Text('아직 회원가입이 되어 있지 않나요?'),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

홈 페이지 구현

lib/home_page.dart 파일을 생성하여 홈 페이지를 구현한다.

import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'login_page.dart';
import 'package:get/get.dart';

class HomePage extends StatelessWidget {
  final FlutterSecureStorage secureStorage = const FlutterSecureStorage();

  Future<void> _logout() async {
    await secureStorage.delete(key: 'isLoggedIn');
    Get.offAll(() => LoginPage());
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('홈 페이지'),
        actions: [
          IconButton(
            icon: Icon(Icons.logout),
            onPressed: _logout,
          ),
        ],
      ),
      body: Center(
        child: Text('환영합니다!'),
      ),
    );
  }
}

마무리

이상으로 Flutter와 FlutterSecureStorage를 사용하여 로그인 상태를 관리하는 방법에 대해 알아보았다. FlutterSecureStorage를 통해 민감한 데이터를 안전하게 저장하고 관리할 수 있으며, 이를 통해 앱의 보안성을 높일 수 있다.