[Flutter] FlutterSecureStorage를 활용한 로그인 상태 유지와 회원 정보 불러오기 (feat.http API)
FlutterSecureStorage란?
보통 로그인 시스템을 만들면
아이디&비밀번호를 서버로 요청하여 일치하는지 확인 후 검증이 완료되면 접속을 하도록 짜여진다.
하지만 여기서 중요한 것은 로그인/로그아웃을 구현하려면
검증만 되는 것이 아니라 '상태 유지'를 해야 한다.
검증이 되었다면 로그인 중인 상태를 유지한 채로 회원이 서비스를 이용할 수 있어야 한다.
여기서 상태 유지를 할 때 flutter 디바이스 내부에 정보를 저장해야 하는데
shared_preferences 패키지 같은 것을 이용해 이런 정보들을 그대로 저장하게 되면 보안에 취약해지게 된다.
해서 이런 보안 문제를 해결하기 위하여 나온 것이 FlutterSecureStorage라고 한다.
즉, 로그인/로그아웃 등으로 '상태 유지' 관리가 되어야 하는데
유지가 되려면 회원의 정보가 저장되어야 하는데 보안을 보완시킨 것이라고 보면 될 것 같다.
FlutterSecureStorage 사용법
pubspec.yaml에 패키지 추가
flutter_secure_storage: ^6.0.0
dependencies: 하위에다가 flutter_secure_storage: ^6.0.0를 추가해주고 (버전은 그때그때 확인하는 게 좋음)
상단에 Pub get을 눌러준다.
FlutterSecureStorage 적용해보기
이번 개인 프로젝트에서 구현했던 로그인 쪽 플러터 코드를 가져왔다.
로그인 페이지 UI에서 이메일과 패스워드 텍스트 필드, 그리고 로그인 버튼을 구현했다고 가정했을 때
http를 사용하여 spring으로 로그인 요청을 보내는 버튼을 만들어보자.
static const storage = FlutterSecureStorage(); //클래스 하단, 위젯 위에 작성
// 로그인 버튼 누르면 실행
signInAction() async {
//메일과 패스워드 텍스드 필드에 적힌 텍스트들을
//spring API에 만들어놓은 요청 타입으로 변환시켜줌.
MemberSignInRequest memberSignInRequest =
MemberSignInRequest(emailController.text, passwordController.text);
// async 다음엔 무조건 await를 써주어야함(쓰레드 이해가 있어야함)
// spring API를 만들어놓은것에 작성한 이메일과 패스워드를 파라메터로 전달해준다.
await SpringHttpApi().signInApi(memberSignInRequest);
// 만약 통신에 성공되었다면!
if (SpringHttpApi.signInResponse.statusCode == 200) {
debugPrint('통신 성공!');
// spring쪽에서는 로그인 검증이 되면 프론트로 회원 정보를 넘겨주도록 하였다.
// 받은 정보를 변수에 담아주고
var val = SpringHttpApi.signInResponse.body;
// 받은 데이터를 flutter에서 사용할 수 있도록 변환해준다.
var account = Member.fromJson(jsonDecode(val));
// 그 다음 FlutterSecureStorage에 받은 정보들을 저장해준다.
await storage.write(key: 'userToken', value: account.userToken);
storage.write(key: 'email', value: account.email);
storage.write(key: 'nickname', value: account.nickname);
storage.write(key: 'memberPoint', value: account.memberPoint.toString());
_signInSuccessShowDialog(); // 접속 성공 안내 창
debugPrint('접속 성공!');
} else {
_signInFailShowDialog(); // 접속 실패 안내 창
debugPrint('error');
}
}
이렇게 storage에 write를 써서 서버로부터 받은 회원의 정보를 저장한다.
그러면 후에 아래와 같이
read를 통해 로그인 상태를 확인할 수도 있고,
static const storage = FlutterSecureStorage();
dynamic memberInfo = '';
@override
void initState() {
super.initState();
// 비동기로 flutter secure storage 정보를 불러오는 작업
WidgetsBinding.instance?.addPostFrameCallback((_) {
_asyncMethod();
});
}
_asyncMethod() async {
memberInfo = await storage.read(key: 'userToken');
//member 정보가 있을 시 이동할 페이지
if (memberInfo != null) {
Navigator.push(context,
MaterialPageRoute(builder: (context) => const HomePage()));
} else {
debugPrint('로그인이 필요합니다');
}
}
이렇게 회원 정보를 띄워야 할 때는 dynamic 변수를 생성해놓고
setState를 사용해줘야 바로 반영이 되어서 이렇게 구현했다.
static final _storage = FlutterSecureStorage();
dynamic nickname = '';
dynamic email = '';
@override
void initState() {
super.initState();
WidgetsBinding.instance?.addPostFrameCallback((_) {
_memberInfoRead();
});
}
_memberInfoRead() async {
nickname = await _storage.read(key: 'nickname');
email = await _storage.read(key: 'email');
setState(() {
nickname = nickname;
email = email;
});
}
계정 띄우는 UI 쪽에다가 회원 정보를 띄울 수 있다.
UserAccountsDrawerHeader(
currentAccountPicture: const CircleAvatar(
backgroundImage: AssetImage("assets/images/logo.png"),
backgroundColor: Colors.black,
),
accountName: Text(nickname),
accountEmail: Text(email),
),
회원 프로필 띄우는 것도 이런 방식으로 저장이 되면 불러와서 띄우면 된다.
FlutterSecureStorage를 통해서 서버에 닉네임 변경이나 패스워드 변경 같은 것도 요청해서 수정하면 될 것 같다.
전체적인 흐름
로그인 구현의 전체적인 흐름은
fluuter 로그인 페이지 UI를 구현 →
spring으로 로그인 요청 api를 만들기 →
로그인 검증이 되면 회원 정보를 리턴 받아 데이터를 플러터에서 받는다(받을 객체 생성) →
storage에 write해준다 →
그 후에 read로 회원 정보 읽기가 필요한 곳에서 구현!
FlutterSecureStorage를 왜 써야하는지와, 쓰면 어떤 점들을 활용할 수 있는지를
처음부터 알지 못한채로 일단 필요한거같아보이니 냅다 가져다 썼었는데
구현을 하다보면 어라..? 이것도 필요한데? 저것도 필요한데?하는 부분이 많이 생긴다.
단순하게 회원 가입 - 로그인을 구현하는데도 많은 검증 로직을 짜야하고 데이터를 주고 받고 하는 부분들이 꽤 복잡하다.
플러터는 이렇게 공부할 시간도 없이 바로 프로젝트를 시작하면서 적용을 했다보니 많은 시행 착오가 있었다.
개인 프로젝트가 현재는 끝났는데 프로젝트 하면서 내가 활용했던 것들을 시간날때마다 기록을 하려고한다!
(프로젝트 하는동안 블로그를 쓸 시간이. . 정말 없었다.)
아마 팀프로젝트 시작하면 또 그럴거같아서! 틈틈히~ 써보려고한다.