요즘 Flutter로 교환일기 쓰는 어플을 개발 중이다.
함께 Flutter 개발을 하는 친구와 아토믹 디자인 시스템 기반으로 개발하기로 결정했다.
사실 클라이언트 부분에서 협업하는 건 이번이 두 번째이기도 하고,
같이 개발하게 된 친구는 WEB 개발까지 잘하는 친구라서 나도 잘해야겠다는 책임감이 엄청 컸다! 😅
이번 프로젝트는 12월까지 진행되는 장기 프로젝트라서,
클라이언트 스프린트 리뷰도 2시간씩 진행하고 있다.
또한, 코드를 깔끔하게 짜기 위한 고민도 함께 하고 있다..
같은 문제에 대해 깊이 있게 논의하니,
혼자 개발할 때보다 훨씬 재미도 커진 것 같다! 🎉
현재는 UI 개발을 진행 중이다.
특히 컴포넌트 제작 중인데, 여기서 고민이 꽤 깊어졌다... 🤔
아토믹 디자인 시스템을 기반으로 개발하다 보니,
각 컴포넌트의 재사용성과 구조적 효율성에 대해 계속해서 생각하게 됐다.
아토믹 구조란?
같은 위젯이 반복되는
우리 서비스 특성을 고려하여 개발 전략 결정
UI 컴포넌트를 만들 때,
너무 세밀한 부분까지 고민하다 보니..
개발 속도가 느려지는 것 같다.
특히 나는 공통 컴포넌트와 재사용성에 대해 너무 집착하는 거 같다.
일단 SPRINT2 에서 내가 맡은 부분의 디자인이다.
여기에서 내가 고민인거는..
이렇게 맨 위 텍스트, 중간에 박스, 아래 List 해서
각각의 atom과 molecule을 만들고
그 후 이렇게 하늘색 부분을 organism으로 한 후
보라색 부분을 template으로 만들면 된다고 생각했다.
먼저 이렇게 네 화면이 구조가 비슷하니
공통 컴포넌트를 뽑을 수 있을 거 같았다.
근데 저 4번째 사진에 혼자 있는 타이머때문에 고민을 많이 했다.
timer 컴포넌트를 따로 만들어야하나..
하다가
일단 이렇게 3개의 파라미터로 텍스트를 받는다.
(안들어오면 ""처리)
그리고 파라미터로 minutes가 들어온다면
타이머까지 보여준다.
이렇게 생각해서 코드는
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:project_today/core/constant/colors.dart';
import 'package:project_today/core/constant/fonts.dart';
class CustomNameText extends StatefulWidget {
/// 이름
final String name;
/// 이름 옆 텍스트
final String topText;
/// 아래 텍스트
final String bottomText;
/// 분 (null이면 타이머 표시 안 함)
final int? minutes;
const CustomNameText({
super.key,
this.name = "",
this.topText = "",
this.bottomText = "",
this.minutes,
});
_CustomNameTextState createState() => _CustomNameTextState();
}
class _CustomNameTextState extends State<CustomNameText> {
int? _minutes;
int _seconds = 0; // 초
Timer? _timer;
void initState() {
super.initState();
if (widget.minutes != null) {
_minutes = widget.minutes;
_startTimer(); // 타이머 시작
}
}
// 타이머 시작 함수
void _startTimer() {
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
if (_minutes == 0 && _seconds == 0) {
timer.cancel(); // 시간이 끝나면 타이머 종료
} else {
setState(() {
if (_seconds > 0) {
_seconds--; // 초가 0보다 크면 초만 줄임
} else {
_seconds = 59; // 초를 59로 다시 설정
if (_minutes != null) {
_minutes = (_minutes ?? 1) - 1; // 분을 하나 줄임
}
}
});
}
});
}
void dispose() {
_timer?.cancel();
super.dispose();
}
String _formatTime() {
String minutesStr = (_minutes ?? 0).toString().padLeft(2, '0');
String secondsStr = _seconds.toString().padLeft(2, '0');
return "$minutesStr:$secondsStr";
}
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
widget.name,
style: FontSystem.buttonBold
.copyWith(color: GreyColorSystem.Grey90, fontSize: 18),
),
Text(
widget.topText,
textAlign: TextAlign.center,
style: FontSystem.buttonLight
.copyWith(color: GreyColorSystem.Grey90, fontSize: 18),
),
],
),
Text(
widget.bottomText,
style: FontSystem.buttonLight
.copyWith(color: GreyColorSystem.Grey90, fontSize: 18),
),
if (widget.minutes != null)
Text(
"자동 시작까지 ${_formatTime()} 남았어요",
style: FontSystem.buttonLight
.copyWith(color: GreyColorSystem.Grey90, fontSize: 18),
),
],
);
}
}
이렇게 짰다.
text 3개를 왜 String? 으로 안 받냐고 생각할 수 있겠지만.
내가 짠 코드에
topText: "친구들이 모두 들어오면\n교환일기가 시작돼요!",
minutes: 5,
를 넣으면 위처럼 된다.
bottomText에 ""가 들어갔으니
topText와 timer 사이에 공간이 생기게 된다
심지어 사이즈도 딱 맞음
만약 bottomText가 null 일 때 안 보이게 설정한다면
이렇게 topText와 timer 사이의 공간이 없어져
추가적인 조치를 취해줘야 하는데 ,,
아 지금 생각해보니까 이게 더 효율적이려나 ?
음 ... 흠 ....
organism을 만드는 과정에서도 고민이 많았다.
왼쪽 두 화면에 해당하는 organism 1개,
오른쪽 두 화면에 해당하는 organism 1개를 따로 만들지,
아니면 두 개를 합쳐 하나로 만들고 파라미터로 제어할 지 고민이 많았다.
결국 옵셔널 파라미터를 추가해서 하나의 organism으로 통합했다.
결론적으로는 적당한 타협점을 찾는 게 중요하다는 생각이 들었다.
내가 오가니즘을 2개 만들 지, 하나로 합칠 지 정답은 없는 거 같다.
완벽함을 추구하기보다는 유연하게 코드를 짜는 것이 더 중요하다는 걸 배우고 있다!..ㅜㅜ (그냥 누가 정해주면 좋겠음)