[캡스톤_front] 지금까지의 구현 사항 흐름 정리하기_MainAccount(& api 요청으로 값 get 하기)

피용희·2024년 4월 26일
0

2024 캡스톤

목록 보기
19/19

MainAccount는 워낙 Navigate되는 부분이 많아서 우선 단일 분석으로 구성했다. 흐름과 핵심 기능을 위주로 설명할 예정이다.

여기는 내용이 상당시 길어서, 기능별로 잘라서 보도록 한다.

class _MainAccountState extends State<MainAccount>{
  late User user;
  late UserAccountInfo accountInfo;

  @override
  void initState() {
    super.initState();
    user = User();
    accountInfo = UserAccountInfo();
    _fetchUserData(); // initState에서 데이터 가져오도록 호출
    _fetchUserAccountData();
  }

우선 초기화와 관련된 부분이다. 이 화면에서는 로그인 세션을 통해 유져 정보를 가져와서, 유저 정보를 저장해야 하고, 유저의 AccountId를 바탕으로 AccountInfo를 가져와야 한다. 즉, 전체 화면에서 사용 가능한 싱글톤 클래스 두개가 필요하다.

이 싱글톤 클래스 내부에는 초기화 메서드가 구현되어 있기 때문에, initState를 통해 화면 빌드 초기에 한번 초기화를 진행해주면 초기화가 가능하다.

이 부분을 이해하기 위해서는 User 싱글톤 클래스가 어떻게 되어 있는지를 이해해야 하므로 잠시 살펴보도록 하자.

class User {

  late String AccountId; //이걸 list로 잡아야 하나...고민중
  late String Name; //이름값
  late String Profile; //프로필 사진
  late int amount;

  // 싱글톤 인스턴스 생성
  static final User _instance = User._internal();

  factory User() => _instance;

  // 내부 생성자
  User._internal() {
    AccountId = '';
    Name = '';
    Profile = '';
  }

  // API 데이터 초기화 메서드
  void initializeData(Map<String, dynamic> data) {
    AccountId = _getStringValue(data, 'AccountId');
    Name = _getStringValue(data, 'Name');
    Profile = _getStringValue(data, 'Profile');
  }

  // 새로운 JSON 데이터 추가 메서드
  void addNewData(Map<String, dynamic> newData) {
    amount = newData['amount'] ?? 0; // amount 값이 없으면 기본값 0으로 설정
  }

  String _getStringValue(Map<String, dynamic> data, String key) {
    return data[key].toString();
  }
}

싱글톤 클래스는 앞에서도 한 번 설명한 적이 있는데, 이를 한 번 정의해 두면 다른 여러 화면에서 전부 사용이 가능하다. 즉, api 요청을 너무 많이 해서 정보가 꼬이거나 서버 과부하가 일어나는걸 방지해준다.

https://velog.io/@sunj_0120/%EC%BA%A1%EC%8A%A4%ED%86%A4front-flutter-custom-keyboard-%EC%9E%85%EB%A0%A5%EA%B0%92%EC%9D%84-%EB%8D%94%ED%95%9C-json-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EB%8B%A4%EC%9D%8C-%ED%99%94%EB%A9%B4%EC%9C%BC%EB%A1%9C-%EB%84%98%EA%B8%B0%EA%B8%B0

싱글톤에 대한 자세한 개념은 여기를 살펴보면 된다.(이건 예시 데이터를 이용한거라 변수 구조가 약간 다르다. method랑 실행 방식만 이해하고 원하는대로 적용하면 된다.)

그렇다면 이 싱글톤 클래스를 어떻게 활용하는지 알아보자.

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

  @override
  State<MainAccount> createState() => _MainAccountState();
}

Map<String, dynamic>? apiResult; //http 주소 받아올

class _MainAccountState extends State<MainAccount>{
  late User user;
  late UserAccountInfo accountInfo;

  @override
  void initState() {
    ......
  }

  // API 요청을 보내어 사용자 데이터를 가져오는 메서드
  Future<void> _fetchUserData() async {
    // userId를 사용하여 API 요청을 보냄
    Map<String, dynamic> userdata =
    await httpGet(path: '/api/users/${user.id}'); //name..? 암튼 구별 가능한 데이터
    // API 응답을 통해 사용자 데이터 업데이트

    if (userdata.containsKey('statusCode') && userdata['statusCode'] == 200) {
      // 사용자 데이터를 업데이트
      user.initializeData(userdata["data"]);
    } else {
      // API 요청 실패 처리
      debugPrint('Failed to fetch user data');
    }
  }

다음과 같이 Future를 이용한 비동기 메서드를 선언하여 진행한다.비동기로 처리하는 이유는, api 요청을 받아야 해당 method의 return값을 정의할 수 있기 때문이다.

  • httpGet(custom method이다. 구조는 이 아래에 바로 올리도록 하겠다.)을 통해서 api request를 받아온다.
  • ['statusCode'] == 200즉, request가 200으로 성공적으로 들어왔다면 user.initializeData(userdata["data"])를 통해 받아온 data의 data에 해당하는 부분을 update 한다.

여기서 ["data"] 부분을 업데이트 하는 이유는, 이 예시 데이터의 구조 때문이다. 다음을 살펴보자.


json 데이터의 구조를 잘 살펴보자. 우리가 사용해야할 id, avatar등이 data 안에 묶여 있다. 즉 묶여 있기 때문에 data를 인덱스로 가져와야 내부의 데이터들을 가져와서 클래스의 변수로 추가할 수 있다.(즉, 받아오는 json data에 따라 이 부분은 조정이 필요하다.)

⭐ api 요청에 실패할 경우, debugprinting 뿐만 아니라 경고창을 띄워서 다시 loading 할 수 있도록 만들어야 한다. 이 부분은 내일 마저 구현하려고 한다.

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

import 'dart:convert';

Future<Map<String, dynamic>> httpGet({required String path}) async {
 String baseUrl = 'https://reqres.in$path'; //base
 try {
   http.Response response = await http.get(Uri.parse(baseUrl), headers: {
     "accept": "application/json",
     "Content-Type": "application/json",
   });
   try {
     Map<String, dynamic> resBody =
     jsonDecode(utf8.decode(response.bodyBytes));
     resBody['statusCode'] = response.statusCode;
     return resBody;

   } catch (e) {
     return {'statusCode': 490};
   }
 } catch (e) {
   debugPrint("httpGet error: $e");
   return {'statusCode': 503};
 }
}

httpGet의 경우 api 요청을 통해 request를 받아와야 하므로 마찬가지로 Future 비동기로 구현되어 있다. 연결할 API 주소에 따라 base url을 바꿔서 사용하면 된다.

Text(
   // '${accountInfo.AccountName} 창고',
)                                         

그렇다면 사용은 어떻게 할까? 이런식으로 api결과가 저장된 클래스.변수로 값을 불러와서 간편하게 사용하면 된다.

ElevatedButton(
                        child: const Text(
                          '매듭 보내기',
                          style: TextStyle(
                            color: Color(0xFF4B4A48),
                            fontSize: 25,
                            fontFamily: 'Noto Sans KR',
                            fontWeight: FontWeight.w500,
                            height: 0,
                          ),
                        ),
                        onPressed: () {
                          setState(() {
                            Navigator.of(context).push(
                                MaterialPageRoute(builder: (context) => qrScanner()));
                          });
                        },
                        style: ElevatedButton.styleFrom(
                          fixedSize: Size(screenWidth* 0.85, screenHeight * 0.09),
                          padding: EdgeInsets.symmetric(horizontal: 20, vertical: 5),
                          shape: RoundedRectangleBorder(
                            borderRadius: BorderRadius.circular(20),
                          ),
                          backgroundColor: Color(0xFFFFD852),
                        ),
                      ),

각각의 버튼은 이런식으로 기능에 해당하는 page로 navigate하게 생성하였다.

profile
코린이

0개의 댓글

관련 채용 정보

Powered by GraphCDN, the GraphQL CDN