도입 배경
회사 프로젝트에 언어 선택에 따라서 해당 다국어가 적용되는 방식으로 기본적인 다국어 처리는 적용되어있는 상태이다.
하지만 이번에 새로 오픈한 기능중에 유저들끼리 대화를 주고받고 관광지를 추천하게 되는 경우가 있는데 유저들이 한국어로 주고받아도 영어, 앞으로 추가될 일본어, 중국어 등의 사용자가 해당 대화를 번역해서 보여주기를 원하는 상황이였다.
크게 Google Translate API, Papago API 이렇게 두개의 선택지가 있었는데, 현재 회사에서는 Naver Cloud Platform 을 사용하고 있기 때문에 관리적인 입장에서 봤을때 Papago 가 더 효율적이라고 판단되어 해당 서비스를 이용해보려고 한다.
해당 블로그에서 작성되는 내용은 별도 예제를 위해 만들어서 진행할 예정이다.
Papago API 사용 설정
https://www.ncloud.com/product/aiService/papagoTranslation
NAVER CLOUD PLATFORM
cloud computing services for corporations, IaaS, PaaS, SaaS, with Global region and Security Technology Certification
www.ncloud.com
해당 사이트에 들어가 사용설정을 해주어야 한다.
큰 어려움은 없고 그냥 흐름을 따라가면 간단하게 API 사용신청을하고 키를 발급 받을수있다.
Application 등록
필요 서비스 설정
이렇게 하면 Client ID 와 Client Secret 을 얻을 수 있다.
공식문서에 따르면 각 API 는 다음과 같이 생겼다고 한다.
Text Translation: https://naveropenapi.apigw.ntruss.com/nmt/v1
Doc Translation: https://naveropenapi.apigw.ntruss.com/doc-trans/v1
Website Translation: https://naveropenapi.apigw.ntruss.com/web-trans/v1
Language Detection: https://naveropenapi.apigw.ntruss.com/langs/v1
Glossary: https://papago.apigw.ntruss.com/glossary
Postman 을 통한 사전 확인
API 를 사용하기 전에 키값이 정상적으로 작동하는지 확인하기 위해서 Postman 을 통해서 사전에 확인하는 편이다.
이렇게 확인하고 나면 나중에 앱에 적용할때도 API 가 어떻게 Input 을 받고 Output을 받는지 확인하기 편해진다.
물론 필수는 아니고 내 스타일이다.
Papago 공식문서에 따르면 요청 박식은 POST 방식으로 해야되고 위에서 말한
https://naveropenapi.apigw.ntruss.com/nmt/v1 뒤에 /translation 을 붙여줘야한다.
헤더는 공용으로 텍스트 번역, 문서 번역, 웹사이트 번역, 이미지 탐색 등 모두 동일하게 들어간다.
번역 지원언어는 다음과 같다.
사실 서비스에 필요한 웬만한 언어는 다 번역을 지원해준다.
해당 내용들을 종합해서 Postman에서 실행해보면
헤더는 다음과 같이 넣어주고
body 에도 다음과 같이 번역할 언어와 내용을 입력해주면된다.
특이한게 source 원어 에는 auto 도 지원해준다
그러면 다음과 같은 결과를 얻을수있는데
Postman이 쓰기 귀찮을때는 그냥 간단하게 curl 로 터미널에서 넣으면 되긴한다.
사실 이게 더 편한거같기도
curl --location --request POST 'https://naveropenapi.apigw.ntruss.com/nmt/v1/translation' \
--header 'X-NCP-APIGW-API-KEY-ID: {앱 등록 시 발급받은 Client ID}' \
--header 'X-NCP-APIGW-API-KEY: {앱 등록 시 발급 받은 Client Secret}' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'source=auto' \
--data-urlencode 'target=en' \
--data-urlencode 'text=안녕하세요, 저는 자전거를 타면서 사과를 먹는 것을 좋아합니다.'
그러면 다음과 같이 나오는걸 볼 수 있다.
이렇게 정삭적으로 API 가 작동하는걸 확인했으니까 플러터에 적용해보자
Flutter에 적용하기
플러터에 적용하기 위해서 나는 API 를 호출하기 위해서 http를 사용했다.
각자 환경에 맞게 API 를 호출하면 되는데 이번 예제에서 사용하는 text-translation 같은 경우에는 POST 방식으로 보내주어야 한다는 것만 잊지말자
http 설치
https://pub.dev/packages/http/install
http install | Dart package
A composable, multi-platform, Future-based API for HTTP requests.
pub.dev
flutter pub add http
http 라이브러리만 설치했으면 기본 준비는 끝났다.
Papago API 호출
import 'dart:convert';
import 'package:http/http.dart' as http;
Future<String> papago(String text, String targetLanguage) async {
String _clientId = '발급받은 Client ID';
String _clientSecret = '발급받은 Client Secret';
String _contentType = 'application/json';
String _url = 'https://naveropenapi.apigw.ntruss.com/nmt/v1/translation';
Map<String, String> headers = {
'X-NCP-APIGW-API-KEY-ID': _clientId,
'X-NCP-APIGW-API-KEY': _clientSecret,
'Content-Type': _contentType,
};
Map<String, String> data = {
'source': 'ko',
'target': targetLanguage,
'text': text,
};
var response = await http.post(Uri.parse(_url), headers: headers, body: jsonEncode(data));
if (response.statusCode == 200) {
var jsonData = jsonDecode(response.body);
return jsonData['message']['result']['translatedText'];
} else {
return 'Error: ${response.statusCode}';
}
}
다음과 url 은 기존 postman 에 넣은 그대로 넣어주면되고
헤더에는 필수 값들 APK-KEY-ID, API-KEY, Content-Type 을 넣어준다.
그런 다음 source 를 통해 원문 언어, target 에는 변경할 언어, text 는 번역할 문자를 넣어주면 된다.
데이터들을 다 넣으면 http.post 를 통해서 값들을 넣어서 호출해주면 된다.
이렇게 되면 papago API 를 사용해서 한국어를 원하는 언어로 변환할 준비가 다 됐다.
현재 프로젝트에 적용해야될 기능같은 경우에 사용자가 입력한 값을 변환해야 되는 상황이고, 장문에 대한 번역도 이상없이 잘 시행되는지 궁금해서 두개의 페이지로 나눠서 만들어봤다.
여기서 주의해야 할 점은 공식문서 상에 따르면 json 으로의 결과값은 다음과 같다.
{
"message": {
"result": {
"srcLangType": "ko",
"tarLangType": "en",
"translatedText": "Hello, I like to eat apple while riding a bicycle."
}
}
}
이에 따라 결과값은 반환할때 jsonData['message']['result'][translatedText'] 로 접근해야 우리가 원하는 번역된 값에 대해서 얻을 수 있다.
사용자 입력을 변역 테스트
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:papago_example/papago.dart';
class InputTextPage extends StatefulWidget {
const InputTextPage({super.key});
@override
State<InputTextPage> createState() => _InputTextPageState();
}
class _InputTextPageState extends State<InputTextPage> {
TextEditingController _textController = TextEditingController();
String _translatedText = '';
@override
void dispose() {
_textController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('사용자 입력 변환'),
leading: IconButton(
onPressed: () => context.go('/'),
icon: const Icon(Icons.arrow_back),
),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(10),
child: TextField(
controller: _textController,
),
),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () async {
var result = await papago(_textController.text, 'en');
setState(() {
_translatedText = result;
});
},
child: const Text('영어 변환'),
),
const SizedBox(width: 10),
ElevatedButton(
onPressed: () async {
var result = await papago(_textController.text, 'ja');
setState(() {
_translatedText = result;
});
},
child: const Text('일본어 변환'),
),
const SizedBox(width: 10),
ElevatedButton(
onPressed: () async {
var result = await papago(_textController.text, 'zh-CN');
setState(() {
_translatedText = result;
});
},
child: const Text('중국어 변환'),
),
],
),
const SizedBox(height: 20),
Container(
width: 300,
height: 300,
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: BorderRadius.circular(10),
),
child: Text(_translatedText),
),
],
),
),
);
}
}
코드는 크게 어렵지 않다 다음과 같이 텍스트 필드를 하나 만들고 거기에 사용자의 입력값을 버튼을 통해서 영어, 일본어, 중국어로 변환하게 만들었다.
결과는 다음과 같이 잘 번역되는걸 볼 수 있다.
장문 번역 테스트
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:papago_example/papago.dart';
class LongText extends StatefulWidget {
const LongText({super.key});
@override
State<LongText> createState() => _LongTextState();
}
class _LongTextState extends State<LongText> {
String _translatedText = '변환된 텍스트';
String _text = '''
제주도에서의 2월 겨울시즌 2박 3일 여행을 소개합니다. 이번 여행의 컨셉은 <자연 속 힐링과 문화 탐방>입니다. 제주도의 아름다운 자연과 함께 조용한 힐링을 즐기며, 제주의 문화와 역사를 탐방하는 여행을 계획해 보았습니다.
첫째 날, 여행은 도두봉에서 시작합니다. 도두봉은 제주의 숨은 비경을 감상할 수 있는 오름으로, 완만한 경사로 산책하기 좋습니다. 정상에서는 한라산과 사라봉의 멋진 전망을 즐길 수 있습니다. 산책 후에는 책방오늘을 방문하여 편안한 분위기에서 책과 함께 여유로운 시간을 보내세요. 점심은 제이학센에서 흑돼지학센을 즐기며 든든하게 채우시길 추천합니다. 오후에는 민오름을 방문하여 자연 속에서의 운동을 즐기고, 노형수퍼마켙에서 미디어아트를 경험해 보세요. 저녁에는 동문시장에서 다양한 제주 전통 음식을 맛보며 하루를 마무리합니다. 숙소는 아모렉스리조트에서 편안한 시간을 보내세요.
둘째 날, 아침은 아라리오뮤지엄 탑동시네마에서 현대미술작품을 감상하며 시작합니다. 이어서 신산공원에서 피크닉을 즐기며 여유로운 시간을 보내세요. 점심은 로치아에서 핸드드립 커피와 함께 즐기시길 권장합니다. 오후에는 삼성혈을 방문하여 제주 신화역사를 배우고, 보성시장에서 시장통닭과 순대국을 맛보세요. 저녁에는 김만덕기념관과 제주원도심을 둘러보며 제주의 역사를 탐방합니다. 숙소로 돌아가기 전, 산진천 옆 정원에서 봄의 향기를 만끽하세요.
셋째 날, 아침에는 탐라국입춘굿 축제를 방문하여 전통적인 굿을 체험해 보세요. 이후 제주 자연과 함께하는 효니요가 프로그램에 참여하여 몸과 마음의 건강을 챙기세요. 점심은 동문시장에서 다양한 음식을 맛보며 마무리합니다. 마지막 날에는 제주공항 근처에서 스쿠버 다이빙을 즐기며 여행을 마무리하세요. 이 여행은 자연 속에서의 힐링과 제주의 문화 탐방을 통해 특별한 추억을 만들어 줄 것입니다.
''';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('장문 변환'),
leading: IconButton(
onPressed: () => context.go('/'),
icon: const Icon(Icons.arrow_back),
),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 300,
height: 300,
color: Colors.grey[200],
padding: const EdgeInsets.all(10),
child: SingleChildScrollView(
child: Text(_text)),
),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () async {
var result = await papago(_text, 'en');
setState(() {
_translatedText = result;
});
},
child: const Text('영어 변환'),
),
const SizedBox(width: 10),
ElevatedButton(
onPressed: () async {
var result = await papago(_text, 'ja');
setState(() {
_translatedText = result;
});
},
child: const Text('일본어 변환'),
),
const SizedBox(width: 10),
ElevatedButton(
onPressed: () async {
var result = await papago(_text, 'zh-CN');
setState(() {
_translatedText = result;
});
},
child: const Text('중국어 변환'),
),
],
),
const SizedBox(height: 20),
Container(
width: 300,
height: 300,
padding: const EdgeInsets.all(10),
color: Colors.grey[200],
child: SingleChildScrollView(
child: SelectableText(_translatedText),
),
),
],
),
),
);
}
}
번역될 장문의 텍스트를 미리 입력해 두었다.
API 문서에 따르면 한번에 최대 5000자 까지 된다고 하는데 프로젝트 내에서 사용하기엔 무리없는 양이다.
실제로 여행관련 안내 메시지의 번역이 들어가야되기때문에 프로젝트에서 사용하는 일부 텍스트를 번역해보았다.
다음과 같이 사용자 입력과 비슷하게 영어, 일본어, 중국어를 사용할 수 있게 만들었다.
번역의 결과를 복사해서 구글 번역과의 차이를 두기 위해서 SelectableText를 사용했는데
여기서 살짝 아쉬운게 일반적인 대화의 흐름은 잘 캐치하고 번역을 잘하는데 여행지명을 번역할때는 살짝 아쉬운 부분이있다.
이제 API 기초 사용법은 쉽다는걸 알았으니 언어 설정에 따라 해당 페이지에 접근시 해당 채팅방의 모든 내용이 번역되는걸 앱 내부에 적용해보면 된다.
결론
사용할줄만 알면 상단히 편한 API가 많다. API의 호출은 현재 앱 개발에서 필수라고 생각되는데 다양한 API의 사용법을 익히고 어느 순간에는 어떤 API 가 들어갈지 안다면 개발에 있어서 상당히 편한 부분이 많아지는 것 같다.
소스코드
해당 포스팅에서 사용한 전체 예제이다.
현재 기준으로 키만 넣고 사용하면된다.
API 관련 세부 내용이 바뀌면 해당 요구사항에 맞춰서 바꿔주면 된다.
https://github.com/Hsungjin/Flutter/tree/main/personal_study/papago_example
Flutter/personal_study/papago_example at main · Hsungjin/Flutter
Contribute to Hsungjin/Flutter development by creating an account on GitHub.
github.com
'Flutter' 카테고리의 다른 글
Flutter Debounce 와 Throttle (0) | 2025.03.21 |
---|---|
Flutter 신용카드 예제 Like 카카오페이 2편 (0) | 2025.03.05 |
Flutter 신용카드 예제 Like 카카오페이 1편 (1) | 2025.02.19 |
Flutter 의 annotation 에 대해 (0) | 2025.02.12 |
Flutter Firebase Crashlytics 적용해보기 (0) | 2025.02.06 |