이제 진짜 끝이 보인다. 그동안 애정을 쏟아부은 앱이 예쁘게 잘 만들어져서 기쁘지만, 이 팀으로 활동하는 것도 얼마남지 않아서 아쉽기도 하다. 그래도 만남이 있다면 헤어짐도 있는 법이니..... 남은 기간에 충실하도록 하자. 오늘도 열심히 개발한 결과를 일부 정리해보도록 하자면...
현재 우리가 개발 중인 서비스에서의 핵심 기능을 하나만 꼽으라면, 당연히 'TOOK(툭)'일 것이다. 툭은 Nearby Connection 을 통해 구현했는데, 블루투스나 와이파이의 광대역을 기반으로 주변 기기를 찾아 연결할 수 있는 기술이다. 다만 하나 아쉬운 점이 있다면, 이 기술은 오직 안드로이드 기기 간에만 사용이 가능하다는 것이다.
우리 팀에서 사용한 패키지는 nearby connection 인데, 처음에는 iOS-안드로이드 간의 크로스플랫폼으로도 지원한다고 하였다. 하지만 iOS 빌드에서 상당히 많은 오류가 발생했는데, 애플은 확실히 기준을 더 엄격하게 잡는 것 같다. 나는 주로 기능개발을 하느라 빌드는 직접 해보진 못했는데, 빌드를 맡아서 진행한 팀원분의 말로는 안드로이드에서 허용하는 패키지를 애플에서는 옛날 버전의 코드로 작성된 것이라고 받아주지 않기도 하고 이슈가 많다고 한다.
내가 현재 주요하게 작업 중인 QR모드와 메세지를 마무리하면, 직접 빌드하며 이것저것 도전해볼 생각이다. 그 와중에 iOS에서도 nearby가 가능하게 만든다면야 너무나도 좋겠다. 하지만 현재로서는 우선 QR을 통해서도 교환할 수 있도록 개발하는 것을 우선순위로 삼았다. 안드로이드 핸드폰 중에서도 3년 이상 된 경우에는 nearby를 지원하지 않는 경우도 있었기 때문이다.
따라서 2가지 패키지를 뜯어보고, 우리 서비스에 어떻게 하면 적용할 수 있을까 고민해보았다.
우선 간단히 생각해본 로직은 위와 같다. 우리 서비스에서 nearby를 통해 '툭' 교환을 하려면, 한 명은 Advertiser == Sender 역할을 하고 다른 한 명은 Discoverer == Receiver 역할을 해야한다. 그래서 보내는 사람은 자기 명함을 위로 올리고, 받는 사람은 자기 명함을 아래로 내리는 방식이다. 이 방식을 QR에도 그대로 적용해보았다.
위로 올리는 사람은 QR을 생성해주고, 아래로 내리면 QR을 찍도록 카메라가 켜진다. 다만 하나 문제가 있는데, 카메라가 자동으로 켜지지 않는다... 화면을 터치해야 켜지는데, 내일 해결해야 한다.
어제에 이어 메세지 보완 작업을 했는데, 오늘은 채팅리스트에 최근 대화 시간과 배지를 구현해보았다. 배지는 카카오톡에서 보이는 이런 배지 말이다. 시간은 인스타그램처럼 몇분 전, 몇시간 전 등이 나오도록 만들었다. 몇 개의 메세지를 읽지 않았는지, 마지막 나눈 대화의 시각이 언제인지를 각 채팅방에 대해서 보여주기 위해서 채팅 리스트의 코드를 살짝 수정해보았다.
// 마지막 시각을 계산해서 띄워주기
Text(
widget.lastMsgTime,
style: TextStyle(
fontSize: 12,
fontWeight: widget.notReadCnt > 0
? FontWeight.bold
: FontWeight.normal),
),
SizedBox(
height: 5,
),
// 읽지 않은 메세지의 갯수를 띄워주기
widget.notReadCnt > 0
? Badge(
badgeContent: Text(widget.notReadCnt.toString(),
style: TextStyle(
color: Colors.white,
fontFamily: 'CherryBomb',
fontSize: 13)),
badgeColor: Color(0xff8338EC),
)
: SizedBox(width: 1)
우선 배지의 경우, DB와 API에서 이미 가져오도록 해놨었기에 레이아웃만 구성해주면 되었다. 반면 시간의 경우 약간의 수정이 필요했는데, 첫 번째로 서버에서 보내주는 시간이 UTC 기준이었기 때문이다. 이상하게도 DB에서는 한국 시간 기준으로 잘 저장되는데, BE 내에서 몇 가지를 거쳐오는 과정에서 UTC 기준으로 바뀌어서 들어온다. 아마 AWS 서버를 거치면서 그런 것이 아니었을까 싶은데, 간단히 Flutter에서 수정해주기로 했다.
DateTime lastmsgtime = DateTime.parse(e[3]).toLocal()
이렇게 하면 간단히 2022-08-02T10:59:00Z 와 같이 서버에서 보내주는 시간을 로컬타임, 즉 우리 기준으로는 한국 시간에 맞게 +9시간 된 DateTime으로 변환시켜준다. 그리고 이렇게 저장해놓은 lastmsgtime을 '2시간 전', '방금' 등과 같이 바꾸어 저장하기 위해서 직접 함수를 짜서 활용했다.
dateToText(DateTime msgtime) {
DateFormat dateFormat = DateFormat('yyyy-MM-dd HH:mm:ss');
Duration diff = dateFormat
.parse(dateFormat.format(DateTime.now()))
.difference(dateFormat.parse((dateFormat.format(msgtime))));
if (diff.inDays > 0) {
if (diff.inDays > 7) {
DateFormat dateFormat = DateFormat("yyyy-MM-dd");
return dateFormat.format(msgtime);
} else {
return '${diff.inDays}일 전';
}
} else if (diff.inHours > 0) {
return '${diff.inHours}시간 전';
} else if (diff.inMinutes > 0) {
return '${diff.inMinutes}분 전';
} else {
return '방금';
}
}
마지막으로 대화를 나눈지 일주일이 경과했다면 굳이 '24일 전' 등으로 표현할 필요는 없다고 생각해서 7일을 기준으로 잡았다. 그리고 초 단위까지 갱신하도록 할까 하다가, 인스타그램에서 DM을 보내고 해당 화면에서 계속 지켜보다보니 '방금' 다음에 '2분 전'으로 바뀌는 것을 확인했기에... 그냥 분 단위까지만 계산하고 1분 이내면 '방금'으로 표기하기로 했다. 인스타그램보다 더 정확한 시간을 자랑하는 네모의 DM 😋
변경 전에는 '지도'가 'Map'이란 이름으로 가운데에 있는 상태였다. 하지만 우리의 핵심 기능이 'TOOK' 교환 기능이다보니, 이 공유기능을 가장 중간으로 옮기기로 했다. 문제는 BottomNavigationBar가 각 페이지마다 각각 들어가있어서 수정이 쉽지 않다는 점...! 그래서 이전에 배운 Provider를 써먹어보기 좋은 기회라고 생각해서 내가 수정하기로 하고 작업해보았다.
import 'package:flutter/material.dart';
class BottomNavigationProvider extends ChangeNotifier {
bottomNavigationBarClick(nowIndex, context) {
return BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.contacts),
label: '연락처',
),
BottomNavigationBarItem(
icon: Icon(Icons.map),
label: '지도',
),
BottomNavigationBarItem(
icon: Icon(Icons.share),
label: '공유',
),
BottomNavigationBarItem(
icon: Icon(Icons.message),
label: '메시지',
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: '마이페이지',
),
],
currentIndex: nowIndex,
onTap: (int nextIndex) {
if (nextIndex == nowIndex) {
return;
}
switch (nextIndex) {
case 0:
Navigator.pushNamed(context, '/contacts');
break;
case 1:
Navigator.pushNamed(context, '/map');
break;
case 2:
Navigator.pushNamed(context, '/sharing');
break;
case 3:
Navigator.pushNamed(context, '/message');
break;
case 4:
Navigator.pushNamed(context, '/mypage');
break;
}
});
}
}
이런 식으로 provider로 쓸 수 있게 파일로 작성해둔 후에는 main.dart에 이 Provider를 사용하겠다는 것을 명시해주어야 한다. 따라서, 다음과 같이 MaterialApp을 MultiProvider로 감싸주었다.
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (BuildContext context) => BottomNavigationProvider()),
],
child: MaterialApp(
title: 'Nemo test',
theme: style.theme,
initialRoute: '/',
routes: {
...
},
),
);
}
}
이후에는 모든 페이지마다 일괄적으로 적용이 가능하다.
bottomNavigationBar: context
.read<BottomNavigationProvider>()
.bottomNavigationBarClick(currIndex, context),
이런 식으로 가져다 쓰면 된다. 당장은 옮길 일이 또 없겠지만, 혹시나 나중에 바텀바 수정이 필요하더라도 이 파일 하나만 수정하면 웬만큼 해결할 수 있게 되었다...! 잘 활용하면 현업에서 일할 때 유지보수에 아주 좋을거 같다.