flutter(dart)

[flutter] flask 서버와 연동 + DB 내용 보여주기

칠구의 스터디 2024. 2. 22. 17:08

이번엔 플라스크 서버와 플러터를 연동해서 서버 내용을 플러터 화면에 보여주는 걸 적어보도록 하겠다.

 

0,제품명,카테고리,전면사진,후면사진,알레르기성분
1,신라면(봉지),라면,신라면봉지-1.jpg,신라면봉지-2.jpg,밀 대두 돼지고기 계란 쇠고기
8801043015714,신라면 컵,라면,이미지2-1.jpg,이미지2-2.jpg,밀 대두 우유 계란 쇠고기
8801043015981,신라면 큰 사발면,라면,이미지3-1.jpg,이미지3-2.jpg,밀 대두 돼지고기 우유 계란 쇠고기

 

서버에 이런DB가 저장되 있을 때

  

이렇게 화면에 보여준 모습이다.

 


진행과정

1. 서버 및 DB 만들기 (플라스크)
2. DB에 데이터 넣기
3. 플러터 화면 구현 및 서버 연동

먼저 플라스크는 SQLAlchemy을 사용하고 CSV를 통해 DB를 관리하기로 했다.

 

1. 서버 및 DB 만들기 (플라스크)

먼저 서버 파일인 py 

from flask import Flask, jsonify, request
from flask_sqlalchemy import SQLAlchemy
from flask_cors import CORS
import csv
# Flask 앱 초기화
app = Flask(__name__)
CORS(app)
# SQLite 데이터베이스 설정
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///products.db'
db = SQLAlchemy(app)
# 데이터 모델 정의
class Product(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    barcode = db.Column(db.String(20), nullable=False)
    name = db.Column(db.String(80), nullable=False)
    kategorie = db.Column(db.String(255), nullable=True)
    frontproduct = db.Column(db.String(255), nullable=True)
    backproduct = db.Column(db.String(255), nullable=False)
    allergens = db.Column(db.String(255), nullable=True)
    def __repr__(self):
        return f'<Product {self.name}>'
# 데이터베이스 생성 및 CSV 데이터 추가
if __name__ == '__main__':
    with app.app_context():
        db.create_all()
        # CSV 파일에서 데이터를 읽어와 데이터베이스에 추가
        with open('C:/Users/82109/Desktop/src/flutter_banergy/lib/DB/product.csv', 'r', encoding='utf-8') as csvfile:
            csvreader = csv.DictReader(csvfile, fieldnames=['barcode', 'name', 'kategorie', 'frontproduct', 'backproduct', 'allergens'])
            for idx, row in enumerate(csvreader):
                if idx == 0:  # 첫 번째 행은 헤더이므로 건너뜁니다.
                    continue
                # 중복 체크
                existing_product = Product.query.filter_by(barcode=row['barcode']).first()
                if existing_product:
                    print(f"제품 '{row['name']}'(바코드: {row['barcode']})은 이미 데이터베이스에 존재합니다.")
                    continue
                new_product = Product(
                    barcode=row['barcode'],
                    name=row['name'],
                    kategorie=row['kategorie'],
                    frontproduct=row['frontproduct'],
                    backproduct=row['backproduct'],
                    allergens=row['allergens']
                )
                db.session.add(new_product)
        db.session.commit()
        
# 라우팅: 제품 추가
@app.route('/add', methods=['POST'])
def add_product():
    if request.method == 'POST':
        data = request.get_json()
        # 중복 체크
        existing_product = Product.query.filter_by(barcode=data['barcode']).first()
        if existing_product:
            return jsonify({'message': '이미 존재하는 제품입니다.'}), 400
        new_product = Product(
            barcode=data['barcode'],
            name=data['name'],
            kategorie=data['kategorie'],
            frontproduct=data['frontproduct'],
            backproduct=data['backproduct'],
            allergens=data['allergens']
        )
        db.session.add(new_product)
        db.session.commit()
        return jsonify({'message': '제품이 성공적으로 추가되었습니다.'}), 201

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8000, debug=True)

사실 파이썬 코드는 내가 안짜고 같이 하시는 분이 짜주셨기 때문에 딱 필요한 부분만 아는 수준이다.

코드를 짜주신 분이 DB 생성 및 CSV 연동을 다 해주셔서 실행만 하면 알아서 instance 폴더에 DB가 만들어졌다.

데이터베이스 생성 부분만 내 위치의 절대경로로 수정만 하면 됨

with open('여기에 나의 CSV 절대 경로 넣으면 됨', 'r', encoding='utf-8') as csvfile: 

 

2. DB에 데이터 넣기

여기는 사실 정말 쉽다.  CSV에 데이터를 적어서 저장하면 된다.

나 같은 경우엔  id, barcode , name  ,kategorie  ,frontproduct ,backproduct ,allergens 로 테이블이 구성되어 있는데

id는 자동으로 생성되기 때문에 생략해주고 순서에 맞게barcode부터 적어서 저장하면 된다.

그후 py을 실행하면  해당 데이터들이 삽입된다.

 

잘 저장되었나 확인하는 방법은 여러가지가 있지만 코드에 디버깅을 해놨기 때문에

터미널에 뜨는 주소에 들어가보면 된다.

참고로 host에0.0.0.0을 넣으면 외부 접속이 허용되고 post는 내 마음대로 수정해도 상관없음

 

코드 상 연동을 했다고 해도 파이썬 파일을 실행하지 않으면 화면에 뜨지 않는다

파이썬 파일 , 플러터 파일 둘다 실행해야 함 

 

3. 플러터 화면 구현 및 서버 연동

백쪽은 이제 끝나고 프론트를 할 시간이다. 

 

화면 dart 부분

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

  @override
  _ProductGridState createState() => _ProductGridState();
}

class _ProductGridState extends State<ProductGrid> {
  late List<Product> products = [];

  @override
  void initState() {
    super.initState();
    fetchData(); // initState에서 데이터를 불러옵니다.
  }

  Future<void> fetchData() async {
    final response = await http.get(
      Uri.parse('http://127.0.0.1:8000/'),
    );
    if (response.statusCode == 200) {
      setState(() {
        final List<dynamic> productList = json.decode(response.body);
        products = productList.map((item) => Product.fromJson(item)).toList();
      });
    } else {
      throw Exception('Failed to load data');
    }
  }

  @override
  Widget build(BuildContext context) {
    return GridView.builder(
      gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 2,
      ),
      itemCount: products.length,
      itemBuilder: (context, index) {
        return Card(
          child: InkWell(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Expanded(
                  child: Image.network(
                    products[index].frontproduct,
                    fit: BoxFit.cover,
                  ),
                ),
                const SizedBox(height: 8.0),
                Text(
                  products[index].name,
                  style: const TextStyle(fontWeight: FontWeight.bold),
                ),
                const SizedBox(height: 4.0),
                Text(products[index].allergens),
              ],
            ),
          ),
        );
      },
    );
  }

여기서는 gridview를 사용해서 한줄에 2개씩 배치한 후

화면엔 내 기준 이미지 , 이름 , 알레르기가 보여지게 하였다.  

 

서버 연동 부분은 fetchData를 사용해서 서버에 데이터를 요청한 후 JSON 형식으 데이터를 받아오도록 하였다

여기서 서버에 요청을 위해 import 'package:http/http.dart' as http; 이걸 임포트 해줘야 한다.

Uri.parse('http://127.0.0.1:8000/') 여기 부분이 서버랑 다르면 연동이 안되기 때문에 주의!!

 

또 화면에 보여지는 products 리스트들을 클래스 선언해주면 된다.

// DB Product

class Product {
  final int id;
  final String barcode;
 .....

  Product({
    required this.id,
    required this.barcode,
    ....
  });

  factory Product.fromJson(Map<String, dynamic> json) {
    return Product(
      id: json['id'],
      barcode: json['barcode'],
      ....
    );
  }
}

나는 코드가 길어지는 걸 방지하기 위해 따로 파일을 만들어서 화면페이지랑 연동했다.