신규 기능 구현 절차
이 페이지는 현재 구현의 확장 지점과 개발 제안을 연결합니다. 예제 요구사항은 가상이며 해당 기능을 이미 구현했다는 뜻이 아닙니다. 기준은 quber/mr7 @ 7695c9a8. DA/는 apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/, LA/는 apps/SKMLauncher2/app/src/main/java/com/sk/airbot/skmlauncher/입니다.
제품 차단 정책은 메서드 이름을 열거하며, callback 전달도 메서드별 수신자 목록을 확인합니다. 새 이름을 추가했다면 두 목록을 반드시 검토해야 합니다. 아래 01과 04에서 실제 파일과 실패 증상을 확인하세요.
04 / 새 완료 이벤트와 수신자 연결 →먼저 결정할 계약
| 결정할 것 | 개발 전에 적을 내용 | 구현 위치에 미치는 영향 |
|---|---|---|
| 노출 입구 | 화면 / 클라우드 / SDK / 예약 중 실제 지원 채널 | producer와 callback consumer의 작업 범위 |
| 입력 | method, 필수·선택 key, 타입, 단위, 기본값, 허용 범위 | MainApi parser와 내부 파라미터, 구버전 호환 |
| 정책 | product mode, lock/privacy/night, battery/station, 현재 실행 기능 | StateManager + 해당 도메인, 예약/WSS는 별도 판정 |
| 실행 수명 | 즉시 완료 또는 장시간 작업; 중복 요청·선점·취소 | 기존 manager 확장 또는 독립 실행 객체 필요성 |
| 상태 | 메모리 상태 / 영속 설정 / 보고 이력 중 무엇인지 | transaction / preferences / DB의 소유자 |
| 결과 | 수락·진행·완료·실패·취소의 정의, 누가 받는지 | callback 라우팅, UI/IoT mapping, 실제 장치 확인 |
| 복원 | 재부팅 후 설정만 복원할지, 작업도 재개할지 | 초기화·알람·장치 재연결 경로와 검증 |
기존 시스템에 통일된 requestId·완료 상태 모델이 이미 있다고 가정하지 않습니다. 상관 ID, 중복 제거, timeout이 필요하다면 요구사항으로 명시하고 producer부터 consumer까지 함께 설계해야 합니다.
01 · 새 명령 또는 기존 명령에 옵션 추가
예제 요구사항: 앱과 클라우드에서 특정 청정 옵션을 요청할 수 있게 한다. 이름과 값은 제품 요구사항으로 확정해야 하며, 아래는 구현 순서입니다.
| 순서 | 직접 수정 또는 분석할 곳 | 구현할 것 / 결정할 것 | 놓치면 보이는 증상 |
|---|---|---|---|
| 1. 기존 옵션인지 판정 | DA/data/Constants.java:16, DA/data/AirCleaningParameter.java:23, DA/main/api/MainApi.java:523 |
기존 setAirCleanerOperation의 mode/speed/position 확장인지 새 method인지 결정. 기존 코드값은 재사용 의미를 바꾸지 않음 |
기존 앱의 같은 값이 다른 동작을 수행 |
| 2. producer 연결 | 앱은 해당 repository, 클라우드는 apps/IotAgent/app/src/main/java/com/sk/airbot/iotagent/mqtt/topic/CmdTopic.java:359 |
외부 입력을 Bundle 계약으로 변환. 모든 채널 지원이 요구되지 않으면 해당 producer만 변경 | UI에만 추가되고 cloud/예약에서 옵션 유실 |
| 3. 일반 진입 | DA/DeviceAgent.java:263 → DA/main/api/MainApi.java:150 |
기존 Stub은 method 읽고 executeMethod 호출. 새 method는 MainApi 분기, 옵션은 해당 기존 분기의 파싱·검증 확장 | 호출 수락 뒤 기능 선택이 되지 않음 |
| 4. 제품 차단 | DA/event/StateManager.java:149 · checkBlockedStatus |
UPGRADE/VITALSIGN 및 Live View의 이름 목록에 새 명령을 포함할지 판정. 기존 이름의 옵션 확장도 정책 의미 점검 | 새 이름만 업데이트 중 실행 가능해짐 |
| 5. 도메인 허용 | DA/policy/CmdPolicyManager.java:471 · DA/cleaning/AirCleaningPolicy.java:60 |
시작/변경/정지에 적용할 정책과 현재 동작의 선점 방식 결정 | 허용 판단은 맞지만 기존 이동/보안과 충돌 |
| 6. 실행·상태 | DA/cleaning/BaseCleaningManager.java:213 · DA/cleaning/CleaningTransaction.java:68 |
Basic/Moving 선택, 파라미터 복사·저장, 정지/재개, 장치 출력까지 연결 | 파라미터는 전달됐지만 실행에 미반영·재개 시 유실 |
| 7. 표시·보고 | DA/cleaning/BaseCleaningManager.java:596 · DA/report/CleaningReportManager.java |
startAirClear callback, 상태 조회, 완료/중단 보고의 추가 의미를 consumer와 맞춤 | 실제 동작과 UI/보고서가 다름 |
필수 작업 산출물: 입력 계약 표, 허용/차단 표, 상태 전이와 취소 정의, 변경 파일 목록, 결과 확인 로그 지점. 새 Manager와 AIDL 변경은 자동 필수가 아닙니다.
최소 회귀: 기존 필드만 보내는 앱 → 기존 동작 유지 / 옵션 누락 → 정한 기본값 / 잘못된 값 → 명시한 거절 / lock·UPGRADE·Live View → 기대한 차단 / START 후 STOP → 실제 정지와 결과 일치. 청정의 현재 조기 IoT 전달과 정책의 반환값 쟁점은 정책 근거를 확인합니다.
02 · 새 영속 설정 추가
예제 요구사항: “청정 옵션의 기본값”을 설정 화면에서 바꾸고 재부팅 뒤에도 유지한다. 이는 가상 요구사항이며 현재 해당 key가 있다는 뜻은 아닙니다.
| 순서 | 위치 | 작업 | 영향과 검사 |
|---|---|---|---|
| 1. 저장 계약 | DA/data/pref/SettingPreferences.java:26 · SettingsPreferencesKey |
key, 타입, 기본값, 기존 기기에서 key가 없을 때의 값 정의 | string/int/boolean 혼용 금지; 기존 저장 파일 호환 |
| 2. 저장·런타임 적용 | DA/settings/Settings.java · 기존 setVoiceRecognitionEnable:462 / setLanguage:806 참고 |
setter/getter와 적용 대상 분리. 저장 전후 적용 순서는 기능 특성에 맞게 결정 | 값만 저장하고 실행 객체는 이전 값인 상태 방지 |
| 3. 초기화·reset | DA/settings/Settings.java:86 · initSettings / :231 · resetSettings |
초기화에서 key를 읽어 필요한 동작 적용; reset 대상이면 기본값 복원 추가 | 재부팅·공장 초기화·부분 초기화 결과 |
| 4. API·UI | DA/main/api/MainApi.java와 해당 Launcher repository/ViewModel |
GET/SET, 값 검증, 화면 저장/조회, 외부 변경 갱신 | UI 진입 직후와 외부 변경 직후 같은 값 |
| 5. 소비자 | 해당 executor와 정책의 getter 사용처 | 시작할 때 읽을지 즉시 실행 중 동작에 적용할지 정의 | 수동/예약/클라우드 요청마다 일관성 |
| 6. 알림 | DA/settings/Settings.java:1063 · settingChanged / refreshPui |
기존 settingChanged 경로 사용 여부와 수신 후 재조회 확인 | 저장 성공·적용 완료·화면 갱신을 각각 확인 |
실제 저장소 함정: DA/provider/DeviceAgentProvider.java:38의 query는 Setting/SKM 저장소를 구분하지만 :64의 insert와 :96의 update는 SKMPreferences에 씁니다. ContentResolver로 썼다고 Settings가 사용하는 SettingPreferences key가 바뀐다고 가정하면 안 됩니다. provider 계약을 확장할 때는 기존 호출자와 권한·호환성을 별도 분석합니다.
권장 검증: key 없는 기존 설치 → default / 정상 값 저장 후 재부팅 / 잘못된 타입 / 반복 SET / 기능 실행 중 SET / UI와 cloud의 GET / reset 후 예상값. 설정값 자체와 적용된 장치 상태를 별도로 관찰합니다.
03 · 새 예약 유형 추가
예제 요구사항: 새 사용자 기능을 지정한 시간에 실행한다. 기존 청정 예약의 옵션만 늘리는 경우에는 새 type이 필요한지 먼저 판단합니다.
DB 저장 성공이나 알람 수신만으로 개발 완료를 판단하지 않습니다. 새 type의 실행 객체가 등록되어야 최종 기능 요청까지 이어집니다.
| 단계 | DeviceAgent 쪽 | Launcher / 외부 쪽 | 완료 확인 |
|---|---|---|---|
| 타입·필드 | DA/schedule/Schedule.java:38 코드, :85 Bundle parser, :211 toBundle |
LA/data/repository/ScheduleRepository.java:357 목록 모델, :782 parseScheduleIntoBundle |
저장→조회→편집→재저장 시 같은 값 |
| CRUD | DA/main/api/MainApi.java:2235 addSchedule / :2296 scheduleiot, DA/schedule/ScheduleManager.java |
LA/data/repository/ScheduleRepository.java:666 editSchedule |
add/update/remove 및 비활성 처리 |
| DB | DA/schedule/db/ScheduleDao.java · ScheduleDbHelper.java |
cloud schema가 바뀔 때 backend 계약 | 기존 데이터 유지; 컬럼 변경 때만 migration |
| 알람·재판정 | DA/schedule/ScheduleInitRunner.java, DA/schedule/ScheduleAlarmReceiver.java:71 / :134 |
시간·타임존·요일 입력 규칙 | 재부팅/시간 변경/삭제 뒤 알람 동작 |
| 유형별 정책 | Receiver 일반 판정·Security 재판정·UPGRADE 우선 | 화면에 보이는 예약 가용성과 차단 안내 | 새 type이 다른 type의 예외를 우연히 상속하지 않는지 |
| 실행 전달 | DA/schedule/ScheduleAlarmReceiver.java:334 setSchedule Bundle |
LA/ui/SKMLauncherApp.java:1138 → ReceivedScheduleInfo → LA/data/model/executableSchedule/ExecutableScheduleManager.java:17 등록 / :48 조회 |
Activity 없음·등록 누락·dialog 조건과 실제 API 호출 |
| 실행 확인 | DA/main/api/MainApi.java:378 setScheduleExecuted 전달 |
LA/data/repository/ScheduleRepository.java:622 sendScheduleExecuted / LA/data/model/executableSchedule/ExecutableSchedule.java:105 |
이 응답의 의미와 실제 장치 완료는 별도로 확인 |
현재 실행 확인의 정확한 의미: LA/data/model/executableSchedule/ExecutableSchedule.java:105의 executeSchedule은 sendExecutedSchedule(..., true)를 먼저 호출한 뒤 execute(...)를 호출합니다. 따라서 isExecuted=true는 실제 기능 호출에 앞서 송신될 수 있으며, 장치 완료 판정에 사용할 수 없습니다.
작업 순서: 계약 합의 → parser/저장 왕복 → 실제 기능 API 구현 → Launcher 실행 객체 등록 → Receiver 정책 → UI/클라우드 노출 → 재부팅·예외 회귀. 모든 TYPE_* 비교와 문자열 type 비교를 함께 검색합니다.
필수 조합: 구버전 저장 데이터, 알 수 없는 type, 삭제/비활성, night/lock/privacy, station 밖·저전력, 일반/보안 예외, Launcher Activity 없음, API 정책 거절, 실제 기능 실패. 새 예약에도 일반 기능의 정책이 다시 적용되는지 추적합니다.
04 · 새 완료 이벤트 추가
예제 요구사항: 장시간 기능이 실제로 끝났을 때 화면과 클라우드가 결과를 받는다. 아래는 일반 Bundle callback 경로이며 전용 WSS/Streaming 인터페이스는 별도 추적합니다.
| 순서 | 근거 파일·메서드 | 필요한 수정/검토 |
|---|---|---|
| 완료 지점 정의 | 실제 도메인 executor / 장치 callback | 요청 수신·작업 enqueue·장치 응답 중 어느 시점을 완료로 볼지 명시. 실패·취소·timeout도 결정 |
| 이벤트 작성 | DA/event/StateManager.java:538 · notifyStateChanged 또는 DA/cleaning/BaseCleaningManager.java:596 예시 |
기존 Bundle의 method와 결과 field 패턴 참고. 새 이벤트 이름과 consumer 계약 작성 |
| 주 Handler 전달 | DA/app/AppCmd.java:18 → DA/main/MainHandler.java:79 |
sendModuleCallback_main과 MainHandler의 특수 분기/필터 확인 |
| 수신자 정책 | DA/app/ModuleCmdCallbackPolicy.java → DA/main/api/MainApi.java:3624 |
getAvailableCaller(method)가 빈 목록이면 전달되지 않음. 원하는 실제 수신자만 매핑 |
| client 등록 | DA/DeviceAgent.java:130 / :180 · registerCallback / registerCallbackByName |
실제 caller 이름과 등록된 callback을 확인. 문자열 포함 비교와 legacy not-defined 경로 존재 |
| 표시·전송 | Launcher 해당 callback 분기 / apps/IotAgent/app/src/main/java/com/sk/airbot/iotagent/IotAgent.java:741 |
method 소비, UI 상태 갱신, cloud 응답·이벤트 선택 |
| 수명 종료 | DA/event/StateManager.java:570 · registerDeathMonitor |
client 종료/재등록, 늦게 온 결과와 새 작업 혼동, 중복 완료 처리 |
주의: ModuleCmdCallbackPolicy는 callback 라우팅 정책입니다. caller 문자열 비교를 서명 인증으로 해석하거나 보안 권한 검증의 대체물로 사용하지 않습니다. 동기 Binder 반환값도 새 비동기 완료 이벤트의 보장이 아닙니다.
회귀 검증: 정상 등록된 consumer만 수신 / 미등록·종료된 consumer / 시작 뒤 취소 / 다음 요청 뒤 이전 결과 도착 / 연결 재등록 / 동일 완료 두 번 / 장치 실패 / 정책에서 거절된 요청. 결과 로그는 요청 입력·정책 결과·실행 시작·장치 결과·callback 전달·consumer 처리의 여섯 지점을 연결합니다. 이것은 권장 관측 지점이며 현재 모든 로그가 존재한다는 뜻은 아닙니다.
AIDL·저장·빌드 변경 판단표
| 바뀌는 것 | 함께 변경할 범위 | 변경이 자동으로 필요하지 않은 부분 |
|---|---|---|
| 기존 Bundle에 key 또는 method 문자열 추가 | producer/parser/executor/consumer 및 정책 목록 | 기존 generic AIDL signature |
| typed Binder 메서드·callback·Parcelable 계약 | 서비스와 실제 client의 AIDL/SDK 및 호환성 | 관련 없는 앱 |
| 영속 설정 key 추가 | 기본값·저장·초기화·reset·GET/SET 소비자 | 예약 DB migration |
| 예약 DB 컬럼/형식 변경 | DAO/helper·migration·serializer·기존 데이터 | 스키마가 그대로인 단순 정책 변경 |
| 새 패키지/서비스·권한 | Manifest·제품 빌드 선택·연결/해제·권한 검토 | 단순 도메인 옵션 변경 |
| 외부 device protocol | 실제 wrapper·전송·수신·단위·timeout와 firmware 계약 | 호출하지 않는 다른 MCU/HAL 경로 |
코드에서 영향 범위를 찾는 명령
아래는 저장소 루트에서 실행하는 읽기 전용 검색 예시입니다. 첫 결과만 고치지 말고 producer, consumer, 초기화, reset, 테스트를 분류합니다.
# 명령의 producer / parser / consumer
rg -n 'setAirCleanerOperation|startAirClear' apps
# 새 명령이 지나야 할 공통 제한과 callback 라우팅
rg -n 'checkBlockedStatus|getAvailableCaller|onModuleCallbackLoop' apps/DeviceAgent
# 예약 type와 실행 등록
rg -n 'TYPE_|scheduleTypeCode|setScheduleExecuted|getExecutableSchedule' apps/DeviceAgent apps/SKMLauncher2
# 영속 설정 적용·초기화·reset
rg -n 'SettingsPreferencesKey|initSettings|resetSettings|settingChanged' apps/DeviceAgent
기준 커밋은 git rev-parse quber/mr7로 확인하고, 분석할 파일은 git show quber/mr7:파일경로로 읽습니다. 작업 트리의 미추적 기능을 기준 브랜치의 구현으로 섞지 않습니다.
개발 티켓에 붙일 작업 정의
요구사항 / 지원 입구:
기준 커밋 / 현재 동작 / 변경 후 동작:
입력 method·keys·기본값·호환성:
정책(제품 모드 / 도메인 / 예약·WSS 예외):
직접 수정 파일·메서드 / 연관 검토 파일·판정:
상태 소유자 / thread / 취소·timeout / 영속 여부:
결과 method·수신자·완료 정의 / 실패·중복·지연 결과:
빌드·배포 대상 / 기존 데이터 호환:
단위 검증 결과 / IPC·DB 검증 결과 / 실기기 검증 결과:
문서의 정적 근거는 APK나 실제 동작의 증거가 아닙니다. 구현 시 변경한 모듈의 최소 단위 검증부터 수행하고, IPC/저장/장치 경계를 바꾸면 해당 경계의 통합 검증으로 범위를 넓힙니다.
Connections
세부 개발 지도 45개 작업 · 수정 위치 · 영향 범위 · 기존 구조 Atlas · 개발 지도