Release 0.4 Week 2: Nextcloud Desktop Client의 Windows 버그 수정
Source: Dev.to
새로운 도전으로 이동
OpenCTI에 대한 풀 리퀘스트를 제출한 뒤, Release 0.4의 후반부에서는 다른 무언가에 도전하고 싶었습니다. 저는 전 세계 수백만 명이 사용하는 인기 오픈‑소스 파일 동기화 도구인 Nextcloud Desktop Client (GitHub)를 살펴보기로 했습니다.
Nextcloud는 사용자가 데이터를 직접 관리하면서 여러 기기 간에 파일을 동기화할 수 있게 해줍니다. 데스크톱 클라이언트는 Windows, macOS, Linux와 깊게 통합된 C++ 애플리케이션입니다. 저는 주로 JavaScript를 다뤘기 때문에, C++ 영역은 제게 큰 도전이었습니다.
발견한 버그
Nextcloud 이슈를 살펴보다가 Issue #9197을 발견했습니다:
https://github.com/nextcloud/desktop/issues/9197
사용자 입장에서 문제는 간단했습니다: Windows에서 커스텀 폴더 아이콘이 Nextcloud가 파일을 동기화할 때마다 초기화되는 것이었습니다.
Windows에서는 폴더를 우클릭 → 속성 → 맞춤 → 아이콘 변경을 통해 커스텀 아이콘을 지정할 수 있습니다. 많은 사용자가 이를 통해 파일을 시각적으로 정리합니다. 하지만 이 버그가 있으면, 동기화가 진행될 때마다 커스텀 아이콘이 사라지고 기본 폴더 아이콘으로 바뀌어 버립니다.
근본 원인 파악
수정에 앞서 Windows가 커스텀 폴더 아이콘을 어떻게 처리하는지 이해해야 했습니다. 조사 결과 Windows는 두 가지를 사용한다는 것을 알게 되었습니다:
- 아이콘 경로가 들어 있는 숨김
desktop.ini파일 (폴더 내부) - 폴더에 설정된
FILE_ATTRIBUTE_SYSTEM플래그
두 요소가 모두 존재해야 커스텀 아이콘이 표시됩니다. 어느 하나라도 없으면 Windows는 기본 아이콘을 보여줍니다.
그 다음 Nextcloud 코드베이스를 탐색해 이 플래그가 사라지는 지점을 찾았습니다. 관련 코드는 다음 파일에 있었습니다:
src/libsync/vfs/cfapi/cfapiwrapper.cpp
이 파일은 Windows Cloud Files API (CFAPI) 를 다루며, Nextcloud가 가상 파일 지원을 위해 사용합니다. 동기화 과정에서 코드가 파일 속성을 기본값(FILE_ATTRIBUTE_NORMAL 또는 FILE_ATTRIBUTE_DIRECTORY)으로 설정하면서 기존 FILE_ATTRIBUTE_SYSTEM 같은 플래그를 보존하지 않는 것을 발견했습니다.
해결 방법
해결책은 속성을 업데이트하기 전에 현재 파일 속성을 읽어 기존 플래그를 보존하는 것이었습니다. cfapiwrapper.cpp의 세 함수를 수정했습니다.
1. updatePlaceholderState()
기존 속성을 읽고 보존하도록 코드 추가:
// Preserve existing file attributes (especially FILE_ATTRIBUTE_SYSTEM for custom folder icons)
const auto currentAttributes = GetFileAttributesW(reinterpret_cast(path.utf16()));
if (currentAttributes != INVALID_FILE_ATTRIBUTES) {
metadata.BasicInfo.FileAttributes = currentAttributes;
}
2. createPlaceholderInfo()
기본값을 설정하기 전에 기존 속성을 확인하도록 수정:
const auto currentAttributes = GetFileAttributesW(reinterpret_cast(path.utf16()));
if (currentAttributes != INVALID_FILE_ATTRIBUTES) {
cloudEntry.FsMetadata.BasicInfo.FileAttributes = currentAttributes;
} else {
cloudEntry.FsMetadata.BasicInfo.FileAttributes = fileInfo.isDir() ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_NORMAL;
}
3. 배치 플레이스홀더 생성 (또한 updatePlaceholderState())
배치 플레이스홀더 생성 함수에도 동일한 보존 로직을 적용했습니다.
직면한 어려움
- 프로젝트를 로컬에서 빌드하는 것이 어려웠습니다. Nextcloud Desktop은 Qt 6와 KDE 라이브러리를 포함한 복잡한 의존성을 가지고 있어 Windows 환경에서 빌드 설정이 까다로웠습니다.
- CFAPI 이해: Windows Cloud Files API는 저에게 완전히 새로운 영역이었으며, 동작 방식을 파악하기 위해 Microsoft 문서를 읽는 데 많은 시간을 투자했습니다.
- 거대한 C++ 코드베이스 탐색: 수백 개의 파일과 복잡한 추상화가 얽혀 있어, 변경이 필요한 정확한 위치를 찾는 데 인내와 꼼꼼한 추적이 필요했습니다.
현재 상황
수정을 완료했고, 코드베이스 분석을 통해 변경 사항이 올바른지 확인했습니다. 이제 풀 리퀘스트를 만들 준비가 되었습니다.
변경된 파일
src/libsync/vfs/cfapi/cfapiwrapper.cpp
이슈 참조
https://github.com/nextcloud/desktop/issues/9197
배운 점
- 저수준에서 Windows 파일 속성이 어떻게 작동하는지, 특히
FILE_ATTRIBUTE_SYSTEM에 대해 배웠습니다. - Cloud Files API (CFAPI)가 동기화 엔진과 어떻게 통합되는지 이해했습니다.
- 익숙하지 않은 대규모 C++ 코드베이스를 탐색하고 파악하는 방법을 익혔습니다.
- 파일을 수정하는 작업 중 시스템 상태를 보존하는 것이 얼마나 중요한지 깨달았습니다.
앞으로의 계획
- 적절한 문서를 포함한 깔끔한 풀 리퀘스트 제출
- 유지관리자들의 코드 리뷰 피드백에 대응
- 수정이 머지되도록 노력
결론
2주 차는 C++와 Windows 시스템 프로그래밍이라는 낯선 영역으로 저를 밀어넣었습니다. 어려웠지만, 데스크톱 동기화 애플리케이션이 내부적으로 어떻게 동작하는지에 대해 훨씬 깊은 이해를 얻었습니다. 1주 차에 진행한 OpenCTI 기여와 함께, Release 0.4 전체에 걸친 저의 오픈‑소스 여정을 최종 블로그 포스트에서 공유하고 배운 점을 되돌아볼 예정입니다.