모듈 구조

한 앱을 두 팀이 나눠 만들 때 어떻게 붙이고 어떻게 떼는지를 정리했습니다.

패키지 구조

app (발주사)
홈 · 블랙박스 연동 · 영상
라우터 · FCM · 로그인
등록만 한다
contract (연결 규약)
ui_kit (공통 부품)

기능 박스를 누르면 외주·내부가 바뀝니다

  • · 기능 패키지는 서로를 참조하지 않습니다
  • · 기능 패키지는 app을 참조하지 않습니다. contract와 ui_kit만 참조합니다
  • · app은 기능 패키지를 등록만 합니다. 기능 내부를 알지 않습니다

떼어내기

기능 옆 버튼을 누르면 그 기능을 발주사가 직접 맡을 때 바뀌는 파일이 나옵니다.

주행·주차 리포트
운전 점수
주정차 단속 알림
가치봄
내가 가본 곳
파인케어
파인케어 발주사 개발팀이 직접 맡는 경우
바뀌는 파일
app/pubspec.yaml
-   feature_finecare:
-     path: ../packages/feature_finecare

app/lib/router.dart
-   ...finecareRoutes,

app/lib/push/handlers.dart
-   FinecarePushHandler(),
다른 기능 패키지 변경0개
contract 변경0개

패키지 폴더는 그대로 두고 소유만 발주사로 넘기면 됩니다. 폴더 안에 화면·데이터·테스트·검증용 앱이 함께 있어 발주사 개발팀이 그대로 이어서 개발할 수 있습니다.

연결 규약

이 표가 1주차에 두 팀이 합의할 문서의 초안입니다.

항목누가 제공기능 패키지가 하는 일
화면 이동app이 라우터 소유자기 화면 목록과 진입 경로를 내보낸다
로그인 정보app이 세션 제공contract의 세션 값을 읽기만 한다
서버 호출app이 API 클라이언트 제공 (토큰 갱신 포함)받은 클라이언트로 자기 API만 부른다
푸시app이 FCM 초기화와 수신알림 유형별 처리기를 등록한다
권한공통 권한 서비스필요한 권한을 요청만 한다
차량 상태app (블랙박스 연동 담당)주행 중 여부를 읽는다 (주정차 단속 알림 이용 제한에 필요)
테마발주사 디자인 토큰토큰만 쓴다. 색·글자 크기를 직접 정하지 않는다
로그·분석app이벤트 이름과 값만 넘긴다
세션을 contract에 선언하고 app이 채운다
// packages/contract/lib/session.dart
final sessionProvider = Provider<Session>(
  (ref) => throw UnimplementedError('app에서 채웁니다'),
);

// app/lib/main.dart
ProviderScope(
  overrides: [
    sessionProvider.overrideWithValue(currentSession),
  ],
  child: const App(),
);
푸시는 app이 받고 유형별로 넘긴다
// packages/contract/lib/push.dart
abstract class PushHandler {
  bool canHandle(String type);
  void open(BuildContext context, Map<String, dynamic> data);
}

// packages/feature_gachibom/lib/gachibom_push_handler.dart
class GachibomPushHandler implements PushHandler {
  @override
  bool canHandle(String type) => type.startsWith('gachibom.');
  ...
}

FCM을 두 곳에서 초기화하지 않도록 수신은 app 한 곳에서 하고 기능은 처리기만 등록합니다.

저장소와 통합 순서

(발주사 저장소)
├─ app/                      발주사
├─ packages/
│  ├─ contract/              1주차 합의 후 공동 관리
│  ├─ ui_kit/                발주사 소유, 저희는 PR로 기여
│  ├─ feature_report/
│  │  ├─ lib/src/
│  │  │  ├─ data/            API 호출, 모델(freezed)
│  │  │  ├─ domain/          계산·규칙
│  │  │  └─ presentation/    화면, ViewModel(Riverpod)
│  │  ├─ example/            기능 검증용 Demo App
│  │  └─ test/
│  ├─ feature_score/
│  └─ ...
└─ melos.yaml                패키지 전체 분석·테스트 한 번에

example/ — 결과물로 요구하신 기능 검증용 Demo App입니다. contract를 가짜 값으로 채워 app 없이 기능만 따로 돌려볼 수 있습니다.

통합 순서 (주차 단위)
  1. 1주차
    contract 초안 합의 · 6개 기능의 빈 화면을 app에 먼저 붙인다
    합치는 길을 첫 주에 뚫어 둡니다
  2. 2주차~
    매주 금요일 병합 · 발주사 개발팀이 PR 리뷰
    서버 API가 준비되지 않은 기능은 합의한 명세로 가짜 응답을 두고 개발
  3. 마지막
    기능별 인수 · 빌드·통합 가이드 전달

마지막에 한꺼번에 합치면 어긋남을 늦게 발견합니다. 첫 주에 빈 화면이라도 합쳐 두는 것이 재작업을 줄이는 가장 확실한 방법입니다.