[Flutter] flutter secure storage-①storage.read

겨레·2024년 7월 12일
0
post-thumbnail

지금까지 작성한 코드 상태에서 앱을 재시작하면 다시 로그인을 해야 하는 번거로운 문제가 발생함!

✔ 리프레시 토큰, 엑세스 토큰을 발급 받은 이유
👉 프론트에서 저장해 놓고 지속적으로 사용하기 위함

이 문제를 해결하려면?
데이터베이스 같은 곳에 저장해야 하는데, 토큰처럼 민감한 데이터는 어디에 저장해야 할까?

flutter_secure_storage 를 사용하면 된다!


flutter_secure_storage 패키지 설치해주기

사용 방법은 아래와 같다

import 'package:flutter_secure_storage/flutter_secure_storage.dart';

// Create storage 시큐어 스토리지 생성
final storage = new FlutterSecureStorage();

// Read value
// key값을 넣어 원하는 값 가져올 수 있음
String value = await storage.read(key: key); 

// Read all values
Map<String, String> allValues = await storage.readAll();

// Delete value
await storage.delete(key: key);

// Delete all
await storage.deleteAll();

// Write value
// 실제 값 집어 넣기 => key값과 value값을 named 파라미터로 넣어주면 됨!
await storage.write(key: key, value: value);

② 안드로이드 프로젝트 build.gradle 파일에서 minSdkVersion 설정해주기!
설정해주면 안드로이드에서 빌드 가능해짐!



③ secure storage에 저장하기
Dio로 데이터(리프레시 토큰&엑세스 토큰)를 받아오면, secure storage에다가 저장해주자!

③-1.const에 data.dart 파일 생성해주고, key값으로 쓸 값들 정의해주자!
(왜나면 여기저기서 쓸 수도 있으니까)

import 'dart:io';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';

// ACCESS_TOKEN, REFRESH_TOKEN이라는 키로 값들을 저장하자!
const ACCESS_TOKEN_KEY = 'ACCESS_TOKEN';
const REFRESH_TOKEN_KEY = 'REFRESH_TOKEN';

③-2.login_screen 쪽에 secure storage 불러오기 작성해주기!


④ view/splash_screen 파일 생성해주기

  • splash_screen 화면의 역할
    로그인하면 여러 가지 정보들과 데이터를 긁어오면서 어떤 페이지로 보내줘야 하는지 판단하는 기본 페이지라고 할 수 있음!
import 'package:flutter/material.dart';
import 'package:flutter_actual/common/const/colors.dart';
import 'package:flutter_actual/common/layout/default_layout.dart';

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

  
  Widget build(BuildContext context) {
    return DefaultLayout(
      backgroundColor: PRIMARY_COLOR,
      child: SizedBox(
        width: MediaQuery.of(context).size.width, // 너비를 최단으로 하면 자동으로 좌우로 가운데 정렬
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // 이미지 넣기
            Image.asset(
              'asset/img/logo/logo.png',
              width: MediaQuery.of(context).size.width / 2,
            ),
            const Text(
              'Food Order App',
              style: TextStyle(
                  fontFamily: 'NotoSans',
                  color: Colors.white,
                  fontSize: 30,
                  fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 16),
            const CircularProgressIndicator(
              color: Colors.white,
            )
          ],
        ),
      ),
    );
  }
}

⑤ mian에 LoginScreen()을 SplashScreen()으로 수정해주기

import 'package:flutter/material.dart';
import 'package:flutter_actual/common/view/splash_screen.dart';
import 'package:flutter_actual/user/login_screen.dart';

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

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

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Actual App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      // LoginScreen() -> SplashScreen()으로 수정
      home: const SplashScreen(), // 시작 화면 설정
    );
  }
}

그럼 이제 로그인 화면이 아니라 아래와 같은 화면이 보일거임!
(재시작 해도 SplashScreen이 보임)


⑥ SplashScreen StatelessWidget에서 StatefulWidget으로 변경해주기!


왜??? 👉 앱이 처음 실행됐을 때만, SplashScreen 실행될텐데 이 때 토큰의 유무를 확인할 거임!

그리고 secure storage는 전반적인 공유를 할 예정이기 때문에
로그인 스크린에서 선언해줬던 const storage = FlutterSecureStorage(); 코드를 data.dart 파일로 옮겨줄거임!

(근데 이렇게 옮긴 것도 나중에 다른 곳에 옮길 예정!)

📍 initState()

class _SplashScreenState extends State<SplashScreen> {
  
  void initState() {
    super.initState();
    checkToken();
  }
// (생략)

initState()에서는 await가 불가능하다! 그래서 함수를 만들어서 사용해야 한다.


아래는 수정된 splasch_screen.dart 전체 코드!

// ignore_for_file: unused_element

import 'package:flutter/material.dart';
import 'package:flutter_actual/common/const/colors.dart';
import 'package:flutter_actual/common/const/data.dart';
import 'package:flutter_actual/common/layout/default_layout.dart';
import 'package:flutter_actual/common/view/root_tab.dart';
import 'package:flutter_actual/user/login_screen.dart';

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

  
  State<SplashScreen> createState() => _SplashScreenState();
}

class _SplashScreenState extends State<SplashScreen> {
  
  void initState() {
    super.initState();

    checkToken();
  }

  // initState()에서는 await 불가능!
  // await storage.read(key: REFRESH_TOKEN_KEY);
  // await storage.read(key: ACCESS_TOKEN_KEY);

  // 함수를 만들어서 쓰자! -> 이제 스토리지로부터 토큰 가져올 수 있음
  void checkToken() async {
    final refeshToken = await storage.read(key: REFRESH_TOKEN_KEY);
    final accessToken = await storage.read(key: ACCESS_TOKEN_KEY);

    // 토큰이 없으면 LoginScreen으로
    if (refeshToken == null || accessToken == null) {
      Navigator.of(context).pushAndRemoveUntil(
          MaterialPageRoute(
            builder: (_) => const LoginScreen(),
          ),
          (route) => false);
    } else {
      // 둘 다 있다면, RootTab으로 (로그인을 했다는 뜻)
      Navigator.of(context).pushAndRemoveUntil(
        MaterialPageRoute(
          builder: (_) => const RootTab(),
        ),
        (route) => false,
      );
    }
  }

  
  Widget build(BuildContext context) {
    return DefaultLayout(
      backgroundColor: PRIMARY_COLOR,
      child: SizedBox(
        width: MediaQuery.of(context).size.width,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Image.asset(
              'asset/img/logo/logo.png',
              width: MediaQuery.of(context).size.width / 2,
            ),
            const Text(
              'Food Order App',
              style: TextStyle(
                  fontFamily: 'NotoSans',
                  color: Colors.white,
                  fontSize: 30,
                  fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 16),
            const CircularProgressIndicator(
              color: Colors.white,
            )
          ],
        ),
      ),
    );
  }
}
profile
호떡 신문지에서 개발자로 환생

0개의 댓글