이제 cwebp 을 이용하여 파일을 webp로 변환해야 한다. 어떻게 하면 될까? 찾아보니 앱 루트에 assets 폴더를 넣고 그 안에 외부실행 파일을 넣어서 실행하면 된다고 한다.
앱의 루트 경로에 assets/bin 폴더를 만들고 그 안에 cwebp 실행 파일을 넣고 process.run 으로 파일을 실행해봤다.
ProcessException: Operation not permitted
이런 오류가 뜬다. 파일 실행 권한이 없다고 하는 듯하다. 구글링을 하며 여러가지 방법을 찾아봤는데, 어떤 것도 도움이 되지 않았다. macos/Runner/ 밑에 있는 모든 *.entitlements 파일에
<key>com.apple.security.app-sandbox</key> <true/>
를 넣어보라던가,
chmod 755 cwebp
chmod +x cwebp
등을 이용해서 파일 실행 권한을 변경해보라던가, 두 가지 방법을 모두 시도해봤지만 안 되었다. 구글링으로 안 되어서, 최후의 수단을 썼다.
가라 gemini.
나: assets/bin에 있는 cwebp를 그대로 실행하는 것은 불가능한 것인지?
제미니: ✦ 네, 정확히 보셨습니다. 계속된 실패를 통해 App Sandbox 환경에서는 assets에 포함된 실행 파일을 런타임에 복사하여 실행하는 것이 불가능하다는 결론에 도달했습니다. macOS의 보안 모델이 이를 허용하지 않습니다.
(...)
따라서, 접근 방식을 근본적으로 변경해야 합니다.
cwebp를 데이터 'asset'이 아닌, 앱의 일부로 공식 포함되는 'Helper Executable'로 만들어야 합니다. 이렇게 하면 앱이 서명될 때 cwebp도 함께 신뢰할 수 있는 코드로 등록됩니다.
호오...
그래서 일단 gemini가 제안한 방법에 대해서 조금 더 찾아봤다. 애플에서 제공하는 문서에 따르면 파일 유형에 따라 적절한 폴더에 리소스들을 모아서 번들 안에 프로그램이 포함되게 할 수 있는 것 같다. 나는 cwebp 가 helper tool 이라고 판단했기에, macos/Helpers/ 경로에 cwebp를 위치시켜 번들에 포함되도록 했다.
// cwebp 파일의 위치를 찾는 역할
static File _getCWebpExecutable() {
File mainExecutable = File(Platform.resolvedExecutable);
final executableDir = mainExecutable.parent;
return File(p.join(executableDir.path, 'cwebp'));
}
final File cwebp = _getCWebpExecutable();
//...
// process.run을 이용해 cwebp 프로그램 실행
final ProcessResult result = await Process.run(cwebp.path, [
inputPath,
'-o',
tempOutputPath,
]);
cwebp를 패키지에 포함하고, cwebp 파일이 포함된 위치를 찾아서 Process.run 으로 실행하고, 변환이 성공하면 파일이 변환된 경로를 반환하도록 작성했다. 그 후, main.dart 에서 FilePicker를 이용해 저장할 경로를 선택하고 저장하면 완성.
결과는 성공적이었다. 452 KB 용량의 JPG 파일이 256 KB 용량으로 축소되어 저장되었다. (그리고 저 캡쳐본은 1.7 MB의 PNG 파일인데, 변환하니 84 KB 의 용량으로 줄어들었다.)
현재는 webp로 변환 시 기본적인 옵션만 적용되도록 했지만, 추후에 옵션을 변경하는 기능과 Animated WebP로 변환하는 기능도 추가해볼 예정이다.