23.06.15
DEVOTEE를 활성화 시키면
지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.
버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.
| 컨텐츠 유형 | 제목 | 저장일 | 삭제 |
|---|
본인인증 로그인에 실패하였습니다.
회원이 아니시거나 본인인증 등록이
완료되지 않은 사용자입니다.
메뉴리스트 만들기 (app bar 및 drawer 만들기 )
gptApiScreen 만들기
GptController 만들기
총평
샘플 코드에서 Flutter 는 간편하게 구성해 보았습니다.
입력과 출력을 한 화면에 구성하고 controller를 통해 Fast-API 를 호출합니다.
Fast-API가 처리하는 동안 화면에서는 Indicator가 동작합니다.
처리가 완료되면 결과를 화면에 출력합니다.
샘플 코드라서 간편하게 구성을 하였지만, 실제 개발시에는 가장 많은 고민과 시간을 투입이 필요한 곳 입니다.
git에서 받은 버전은 예전에 EmailScreen 화면만 구현을 해 두었기에 이번에 새로 만들 화면 GptApiScreen 간의 이동이 되어야 합니다.
그래서 AppBar를 만들고 Drawer를 활용하여 메뉴 리스트를 만들어 보도록 하겠습니다.
메뉴 선택시 화면 전환을 위해 routeName 을 static 으로 생성해 줍니다. 그리고 앱바를 Scaffold 에서 추가를 합니다.
// lib > screens > email_screen.dart 파일 수정
import 'package:devcoean_flutter/controller/email_controller.dart';
...
class EmailScreen extends StatefulWidget {
// 메뉴를 이동하기 위해서 routeName 변수 설정 정의
static const routeName = '/email-screen';
...
}
class _EmailScreenState extends State<EmailScreen> {
Widget build(BuildContext context) {
return Scaffold(
// 앱바를 추가한다
appBar: AppBar(
title: Text("Devocean"),
),
body: SingleChildScrollView(
...
),
);
}
}Drawer를 만듭니다.
// lib > screens > email_screen.dart 파일 수정
import 'package:devcoean_flutter/controller/email_controller.dart';
...
class _EmailScreenState extends State<EmailScreen> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Devocean"),
),
// 아래 Drawer 메뉴 추가
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
DrawerHeader(
decoration: BoxDecoration(
color: Colors.blue,
),
child: Text(
'화면리스트',
style: TextStyle(
color: Colors.white,
fontSize: 24,
),
),
),
ListTile(
title: Text('Email전송메뉴'),
onTap: () {
Navigator.of(context).pushReplacementNamed("/email-screen");
},
),
ListTile(
title: Text('Gpt테스트메뉴'),
onTap: () {
Navigator.of(context).pushReplacementNamed("/gpt-screen");
},
),
],
),
),
body: SingleChildScrollView(
...
),
);
}
}Appbar 와 Drawer는 대부분의 화면에 공통으로 사용되기 때문에 Appbar Widget으로 만드는게 좋습니다. 지금은 우선 pass 하겠습니다.
화면의 레이아웃 입니다. 입력을 받고 버튼을 누르면 Indicator가 동작합니다.
api에서 response를 받으면 Indicator를 종료하고 화면에 결과를 출력하는 코드 입니다.
import 'package:devcoean_flutter/constants/constants.dart';
import 'package:devcoean_flutter/controller/gpt_controller.dart';
import 'package:flutter/material.dart';
import 'package:devcoean_flutter/widgets/dev_textfield_widget.dart';
// 전송을 누르면 로딩이 되었다가, api응답이 오면 다시 화면을 렌더링 해야하기 때문에
// Stateful 위젯으로 생성합니다.
// email_screen.dart 파일과 매우 유사 합니다.
class GptApiScreen extends StatefulWidget {
// 메뉴를 이동하기 위해서 routeName 변수 설정 정의
static const routeName = '/gpt-screen';
const GptApiScreen({Key? key}) : super(key: key);
State<GptApiScreen> createState() => _GptApiScreenState();
}
class _GptApiScreenState extends State<GptApiScreen> {
final TextEditingController _titleTextCtrl = TextEditingController();
String? bardResult; // bard API 결과를 저장할 변수
String? openAIResult; // openAI API 결과를 저장할 변수
String? kakaoResult; // kakao API 결과를 저장할 변수
var myFormState; // 상태를 관리할 enum 변수 Ready, Running, Done
// 초기화
void initState() {
bardResult = "";
openAIResult = "";
kakaoResult = "";
myFormState = processState.Ready;
super.initState();
}
Widget build(BuildContext context) {
return Scaffold(
// 앱바와 Drawer는 코드는 동일
appBar: AppBar(
title: Text("Devocean"),
),
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
DrawerHeader(
decoration: BoxDecoration(
color: Colors.blue,
),
child: Text(
'화면리스트',
style: TextStyle(
color: Colors.white,
fontSize: 24,
),
),
),
ListTile(
title: Text('Email전송메뉴'),
onTap: () {
Navigator.of(context).pushReplacementNamed("/email-screen");
},
),
ListTile(
title: Text('Gpt테스트메뉴'),
onTap: () {
Navigator.of(context).pushReplacementNamed("/gpt-screen");
},
),
],
),
),
// 자식 위젯들이 화면에서 넘어가는 경우 Scroll 이 될 수 있도록 SingleChildScrollView 적용
body: SingleChildScrollView(
// 전체 출력은 화면의 90%만 사용
child: Container(
height: MediaQuery.of(context).size.height * 0.9,
padding: const EdgeInsets.all(8),
// 컬럼 위젯은 중앙에서 시작하고 가로는 순서대로 꽉차게 (stretch)
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// 텍스트 필드 위젯
DevTextFieldWidget(
TextCtrl: _titleTextCtrl,
hint: "title",
),
// 간격
const SizedBox(height: 20),
// API를 호출할 버튼 , 버튼을 누르면 Running 상태로 상태를 변화 시키고
// GptController의 gptTranslate 함수 호출
// 결과가 완료되면 Done 상태로 만들고, 각각의 변수에 결과를 할당
ElevatedButton(
onPressed: () async {
if (mounted) {
setState(() {
myFormState = processState.IsRunning;
});
await GptController()
.gptTranslate(_titleTextCtrl.text)
.then((result) {
setState(() {
myFormState = processState.Done;
bardResult = result["bard_msg_ctt"];
openAIResult = result["openai_msg_ctt"];
kakaoResult = result["kakao_msg_ctt"];
});
});
}
},
child: const Text("GPT 테스트"),
),
const SizedBox(height: 50),
// Running 상태인경우, 버튼을 누른경우 에는
// 버튼의 하단 중간 영역에서 처리 중임을 표시
if (myFormState == processState.IsRunning)
const Center(
child: CircularProgressIndicator(),
)
// API에서 결과를 받았다면 결과를 화면에 출력
else if (myFormState == processState.Done)
Container(
height: MediaQuery.of(context).size.height * 0.5,
child: ListView.builder(
shrinkWrap: true,
itemCount: 3,
itemBuilder: (BuildContext content, int index) {
if (index == 0) {
return ListTile(
leading: Text("bard result : "),
title: Text(bardResult!),
);
}
if (index == 1) {
return ListTile(
leading: Text("openai result :"),
title: Text(openAIResult!),
);
}
if (index == 2) {
return ListTile(
leading: Text("kakao result :"),
title: Text(kakaoResult!),
);
}
}),
)
// 준비 상태 에서는 아무것도 보여주지 음
else
SizedBox(),
],
),
),
),
);
}
}Fast-API 서버에 request 하고 reponse를 받아오는 역할의 Controller
싱글톤 형식으로 만든다
import 'dart:convert';
import 'package:devcoean_flutter/models/email_model.dart';
import 'package:http/http.dart' as http;
import '../constants/constants.dart';
class GptController {
// 싱글턴 형식으로 사용한다
static final GptController _instance = GptController._internal();
factory GptController() {
return _instance;
}
GptController._internal() {}
// titleNm 을 body에 넣어 request 하고 그 결과를 받아서 리턴한다
Future<dynamic> gptTranslate(String titleNm) async {
try {
var fastApiUrl;
fastApiUrl = Uri.parse(baseUrl + '/gpt/translate/by/gpt');
http.Response response = await http.post(
fastApiUrl,
body: jsonEncode(
{"title_nm": titleNm},
),
headers: {
'accept': 'application/json',
'Content-Type': 'application/json'
},
);
final responseData = json.decode(utf8.decode(response.bodyBytes));
print(responseData);
if (response.statusCode != 200) {
final errorMessage = responseData['detail'];
print("error : $errorMessage");
// throw Exception(errorMessage);
}
return responseData;
} catch (error) {
throw error;
}
}
}route를 위해서 main.dart 파일 수정
import 'screens/email_screen.dart';
import 'screens/gpt_api_screen.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
// GptApiScreen 을 홈으로 설저
home: GptApiScreen(),
// 여기에 routes 를 정의를 해줘야 routeName 으로 화면 전환이 가능합니다.
routes: {
GptApiScreen.routeName: (context) => GptApiScreen(),
EmailScreen.routeName: (context) => EmailScreen(),
},
);
}
}이제 애뮬레이터에 Flutter 를 실행을 시켜 봅니다.
kakaotrans는 무료이면서도 매우 사용하기 쉬웠고 번역 결과 역시 만족스러웠습니다.
하지만 LLM 이라기 보다는 NLP 라이브러리로 일반 번역을 해도 해외 상품을 이해하기 어려운 경우가 종종 있었습니다.
openai 는 제품의 설명을 잘 이해하게 끔 동작했습니다.
내용의 완성도도 만족스러웠습니다.
다만 유료라는 점이 가장 불편했습니다.
제공하는 모델들이 많고, 튜닝의 요소들이 많이 있습니다. 이에 따라서 어떻게 튜닝하는가에 따라서 결과가 조금 다르게 보여지기도 했습니다.
그래서 사용자체는 매우 쉬우나, 커스텀마이징을 할려면 공부와 테스트는 필요합니다.
모델 역시 과거 모델을 사용하기 때문에 최신의 트렌드는 따라가지 못할 우려가 있었습니다. 최신은 더 비쌈 ㅜ
bardapi는 openai 의 API와 견주어 성능이나 내용의 완성도가 비슷합니다. 경우에 따라 더 나을 때도 있어 보입니다.
최신의 내용도 학습되어 있기도 합니다. 더욱이 무료입니다! (언제까지 제공될지는 모르겠지만)
그래서 bardapi 를 사용하고자 했는데.. 결국에는 사용하지 못했습니다.
이유는 초당 여러건의 api를 호출하게 되면 거부가 됩니다. rate limiting이 걸려있는 것 같았습니다.
초당으로 걸려 있다면, 분당, 일일 호출수도 얼마인지는 모르지만 limiting이 있을 것으로 추측을 합니다.
결론을 내리면 단어 처리 문구 처리, 단순 번역 등을 위해서는 kakaoTrans 활용
api를 초당 여러건 안전하게 처리해야 하는 서비스인 경우는 Open AI API
간간히 1~2건 사용하는 경우는 Bard를 사용하는게 아주 좋을 것 같습니다.
그래서 우선 제 같은 경우에는 openai api 와 kakaotrans를 섞어서 사용하고 있습니다. (플레이스토어 , 앱스토어 )
하지만 BardAPI 에러 원인을 조금 더 정확히 파악하고 가능하면 Bard API도 같이 사용을 하고자 생각하고 있습니다.
DEVOTEE를 활성화 시키면
지금 작성한 댓글에 AI가 댓글을 달아줍니다.