4P by GN⁺ 2일전 | ★ favorite | 댓글 2개
  • 이 오픈 소스 프로젝트는 C와 Win32 API만으로 제작된 경량 네이티브 Windows Todo 애플리케이션임
  • 프레임워크에 의존하지 않고 최소 용량(최대 26.5KB)으로 동작하며, 고급 Windows GUI와 시스템 통합을 직접 구현함
  • Todo 항목 추가, 수정, 삭제, 완료 표시 등 기본 기능뿐만 아니라 시스템 트레이 통합 및 자동 시작 옵션 등 실제 생산성 기능을 제공함
  • 저장소는 이진 파일로 영구적이며, AppData 폴더에 최대 100개의 할 일 목록을 저장함
  • 대형 프레임워크 없이 OS에 매우 밀착된 고전적인 방식의 프로그래밍과 가벼운 실행 환경이 강점임

🌟 Simple Todo (C / WinAPI)

프로젝트 개요

  • 이 프로젝트는 C와 Win32 API만을 이용해 현대적인 네이티브 Windows Todo 앱을 만들음
  • 고급 Windows GUI 프로그래밍 및 시스템 통합능력을 선보임
  • 프로젝트 용량이 매우 작으며(최대 26.5KB), Windows 고유의 외관을 그대로 유지함

✨ 주요 기능

  • 할 일 항목 생성, 편집, 삭제 가능함
  • 작업을 완료 처리할 수 있음
  • AppData에 영구 저장되어 항상 데이터가 보존됨
  • 시스템 트레이와 통합되어 최소화 시 트레이로 이동함
  • 네이티브 Windows 스타일의 외관을 지님
  • Windows 시작 시 자동 실행 옵션이 제공됨

🛠️ 기술적 세부사항

  • 전부 순수 C로 코딩됨
  • GUI 구현에 Win32 API만 사용함
  • Tiny 실행 파일 크기(UPX 압축 시 26.5KB)
  • 시스템 트레이 통합 기능
  • 매니페스트를 통한 모던 비주얼 스타일 적용

💾 데이터 저장

  • 모든 할 일은 이진 파일 하나에 저장됨
  • 저장 경로: %APPDATA%\TodoApp\todos.dat
  • 이진 포맷이며 최대 100개 항목 저장 가능함

📋 필수 요구사항

  • Windows 운영체제 환경 필요함
  • MinGW-w64 (GCC 컴파일러) 및 Windows SDK 필요함

🎮 사용 방법

  • bin/todo.exe 실행 후 인터페이스를 이용해 다음 동작 가능함
  • "Add" 버튼으로 새 할 일 추가
  • 항목 선택 후 "Edit" 클릭하여 수정
  • "Delete"로 항목 삭제
  • "Complete"로 완료 처리
  • 각 항목 우선순위 지정 가능함

🏗️ 프로젝트 구조

  • src/ 폴더에 메인 진입점(main.c), 할 일 관리 로직(todo.c), 구조체 선언(todo.h), GUI 구현(gui.c) 존재함
  • bin/에 컴파일된 실행 파일 배치됨
  • 빌드 스크립트(build.bat)와 프로젝트 문서 포함함

🔧 개발 요소

  • Win32 API: 창 관리 및 GUI 전반 구현
  • Common Controls: 현대식 UI 요소 사용
  • UXTheme: Windows 비주얼 스타일 적용 지원
  • File I/O: 데이터 영구 저장 실현

📝 라이선스

  • MIT 라이선스로 자유롭게 사용 및 수정 가능함

🤝 기여 안내

  • Pull Request 환영
  • 누구나 프로젝트에 참여 가능함

📫 연락처 및 링크

Hacker News 의견
  • win32 gui 프로그래밍에는 내가 좋아하는 부분이 있음, 비록 조금 특이하긴 하지만 Raymond Chen 블로그를 보면 그 이유를 알 수 있음, win32 API는 8088 프로세서 시절에서 시작되었고, 특정 방식으로 하면 코드 40바이트를 아끼거나 레지스터를 하나 덜 쓰는 식임, 예전에 mingw와 Petzold 책을 보며 많은 간단한 gui 앱을 직접 쓴 경험 있음, 커스텀 컨트롤, 그래픽/텍스트 그리기, 스크롤 처리, 히트 테스트 등 모든 과정을 직접하는 게 정말 재미있었음, 네 앱에서 strcpy, sprintf 쓰는 게 보였는데, 진지하게 하는 프로그래밍이라면 길이 체크되는 변형을 반드시 사용해야 함, 컴파일러가 바로 경고를 보내지 않았던 게 신기함, win32 API에는 표준 C 라이브러리 함수들을 대체하는 함수가 많음, 실행 파일 크기를 더 줄이고 싶으면 <Windows.h>만 쓰고 cstdlib 없이 작성해보길 추천함, memset 대신 ZeroMemory, memcpy 대신 CopyMemory 사용 가능함, 물론 raw C코딩이 어느 순간 굉장히 고통스러워지지만, 처음 몇 번은 순수 C로 직접 해보는 게 배우는 데 제일 도움됨, 이런 사소한 것들 다루는 구성 감각이 쌓임, win32 gui 프로그래밍을 더 해보고 싶다면 WTL (Windows Template Library)도 추천하고 싶음, win32 API를 C++로 래핑해줌으로써 동작 원리를 훨씬 쉽게 파악하게 해줌
    • 요즘은 최소한 strcpy 대신 strncpy 정도는 써줘야 함, 안 그러면 모든 사람이 계속 지적할 것임, zig를 쓰는 큰 이유 중 하나가 이런 흔한 실수가 줄어든다는 점임, 물론 c도 괜찮음
    • memset 대신 ZeroMemory, memcpy 대신 CopyMemory라는 내용에 대해, MSVC 인트린식은 rep stos/movs 명령을 써서 함수 호출보다 코드도 작아지고, import 테이블 크기도 줄어듦
    • 나도 예전엔 이걸 많이 했는데, 솔직히 네이티브 코드로 네이티브 UI 개발하는 능력이 그리움
    • memset 및 memcpy 대신 ZeroMemory, CopyMemory 제공한 이유에 대한 질문임, 왜 굳이 기존 C 표준 라이브러리 대신 이런 걸 만들었는지 궁금함
  • 예전에는 CreateWindow를 매번 힘들게 호출하기보다는 .rc 파일로 다이얼로그 리소스를 작성해서(Visual Studio에는 다이얼로그 에디터도 제공) CreateDialog를 썼음, 그러면 모든 컨트롤이 한 번에 생성됨, 애플리케이션 manifest만 추가하면 모던 UI 스타일과 고해상도 DPI도 지원 가능함
    • 이 방식을 쓰면 컨트롤 간 탭 이동 같은 키보드 단축키 지원도 자동으로 가능함, 단, 크기 조정 같은 건 여전히 직접 해야 하긴 하는데, 코드도 금방 늘려 쓸 수 있고 어렵지 않음
    • Petzold 책에도 나오는 방법이니 한 번 찾아보길 추천함
  • 나도 예전에 비슷한 걸 Linux용으로 2KiB 이하 어셈블리로 구현해본 경험 있음, C로 작성하고 동적 링크하면 Linux 기준으로 20KiB 이내로 쉽게 만들 수 있음, Windows는 아예 내장 기능이 많아 더 쉬울 거라 생각함, 덕분에 이런 시도 응원하고 싶음, 글 마지막에 있는 링킹 옵션을 참고하면 용량 더 줄이기 쉬울 것임
  • 프레임웍 없이 만들면 DPI 스케일링 시 폰트 뭉개짐, Tab 지원 없음, 텍스트 필드에서 Ctrl-A 선택 같은 프리-모던 프레임웍 기본 기능 대부분 부재, 행 추가시 오류 등 있음, 그런데 어떤 면에서 이게 "모던"인지 궁금함
    • DPI 인식 설정 예시를 첨부함, 코드가 버전에 따라 다양한 Windows 함수(user32:SetProcessDpiAwarenessContext, shcore:SetProcessDpiAwareness, user32:SetProcessDPIAware)로 DPI 인식 설정을 시도함, 정말 옛날 버전이면 아무것도 호출하지 않음
    • 모던이란 말이 어울리지 않는 게, 너무 용량만 크고 기능은 부족함(컨트롤 간 탭 이동은 직접 구현 쉬움)
  • 6502로 프로그래밍 해본 입장에서는 이제 278KB도 경량으로 간주된다는 현실이 괴로움
    • 내가 바이너리 사이즈 분석을 하다보니, 첫 번째 장애물은 build.bat가 core.autocrlf=false 설정에서 제대로 동작하지 않는 것임, 이걸 core.autocrlf=true로 바꾸고 다시 클론하니 빌드 성공함, mingw의 특정 툴체인은 102KB짜리 .exe를 뱉어냄, 즉 278KB보다 훨씬 효율적임, 더 줄이고 싶다면 GCC에 추가 플래그를 줄 수 있음, gcc -s -Oz -flto로 47KB까지도 가능함, 바이너리 크기에만 관심 있다면 개선 여지는 많음
    • 이 정도 용량이 되는 건 플랫폼 및 실행파일 포맷 때문임, 스택 트레이스 정보, 동적 링크 인프라, 예외 처리 테이블 등 여러 가지가 공간을 차지함
    • 데모 신(scene) 대회에 '64KB TODO 앱' 카테고리를 신설 요청하고 싶음
    • 솔직히 이렇게 용량이 큰 게 놀라움, 예전엔 아이콘 용량 빼면 더 작았던 기억임, MinGw 때문인지 궁금함
    • 6502? 그건 호사임, 내 시절엔 아예 CPU가 없는 경우도 많았음
    • win32 어셈블리 프로그래밍이 갑자기 유행하던 시절을 떠올리게 함, 특히 쉐어웨어 다운로드가 커지던 시기에 특히 각광받았음, 초기 Palm Pilot 68k 프로그래밍도 생각남, 비-레트로 어셈블리의 마지막 불꽃 같았음
    • 어떤 사람은 15KB짜리 quickrun.exe도 만들었음, C와 pure Win32 API만 사용함, 바이너리 용량 줄이기 위한 핵은 없음, Mingw32 컴파일러 사용, alias로 빠르게 앱을 실행하는 GUI 앱임
    • 오늘 저녁엔 Z80, 64KB RAM 시스템을 에뮬레이트하는 내 에뮬레이터 디버깅을 하는 중임, 가끔 이렇게 세월과 환경이 많이 변했다는 걸 새삼 느낌, 그래도 크기 증가만큼 엄청난 발전도 이뤄내지 않았나 싶음
    • 8비트에서 64비트 아키텍처까지 주소 포인터 하나만 해도 8배 커짐, 불평하지 말고 이런 변화 자체가 예술임을 감상해보기 바람
    • 278KB면 5 1/4인치 플로피 디스크에 겨우 들어갈 용량임
  • 이 앱은 재미로 그냥 직접 만들어 본 것임, 댓글에서 지적하듯 더 합리적으로는 C++나 다른 언어로 할 수도 있었겠지만, 나로선 그냥 해보는 게 즐거웠음
    • 30년 전쯤 나도 거의 똑같이 Windows 첫 프로그램을 이렇게 만들어봤던 경험임, 차이점이라면 나는 C++ 컴파일러를 썼다는 점, 그 당시에는 C 스타일 코드를 C++ 컴파일러로 짜는 게 공식 문서에서 가이드되는 방법이었음, C++가 C의 상위호환이라서 Microsoft도 그렇게 하는 경향이 있었음
    • 진심으로 Windows 11 기본 to-do 앱보다 네 앱이 훨씬 더 쓰고 싶음
    • win32 API를 쓸 때는 어떤 언어를 쓰든 본질이 크게 달라지지 않음, 오히려 언어 바꾸면 더 혼란스러워질 수 있음, C++ 스타일에 집착하면 win32 API를 처음 배우는 사람에겐 오히려 더 혼란만 가중될 수 있음, 이러한 간단/귀여운 개인 프로젝트로 win32 API에 익숙해지는 건 개발자로서 기본기라고 생각함
    • 별도로 YoutubeGO라는 내 또다른 앱도 있으니 한 번 살펴봐주면 기쁨
    • 이런 식의 깔끔한 네이티브 UI 프로젝트가 나도 프로그래밍을 배우게 했던 동기라 공감하고 칭찬함
  • 웹이나 소프트웨어에서 278KB 텔레메트리 전송하려고 메가바이트급 JS나 C#을 로드하는 문화가 많은데, 이런 시도는 신선함
    • C# + WinForms로 만든 비슷한 앱은 디스크에 10KB 미만, 6MB RAM만 소모함, 이 앱은 1.5MB RAM 사용함, 둘 다 실행 즉시 뜸
  • 지금 보면 정적 라이브러리를 링크한 것처럼 보임, DLL로 링크하면 앱 크기를 획기적으로 줄일 수 있음
    • 그건 좀 반대임, 프로그램에 DLL을 반드시 같이 배포해야 한다면(DLL이 OS에 포함되어 있지 않다면) 각 DLL마다 자체 c런타임을 포함하고 있어서 오히려 덩치가 커짐, EXE 하나에 정적으로 다 때려박으면 C 런타임이 한 벌만 포함되고, 사용하지 않는 함수는 쉽게 제거 가능함, DLL은 여러 프로그램이 동일 DLL을 공유할 때만 용량이 줄어듦
    • CRT(런타임 라이브러리)를 정적으로 링크하는 게 오히려 불필요한 코드를 줄이는데 이득임, DLL을 동적으로 링크하면 MS에서 VCRUNTIME DLL을 따로 받아 설치해야 하기도 함, Visual Studio에서는 MSVCRT에 동적 링크는 어렵기도 하고, 다만 LGPL 준수를 요구하는 상황에서는 예외임
  • 최근 새로 출시된 빠른 파일 탐색기 File Pilot이 생각남, C로 만들어졌고 1.8MB밖에 안 됨
  • '모던', '네이티브' Windows Todo 앱이라고 하는데, 정말 무엇이 현대적인지 의문임, 그리고 C++로 짜면 여러 문제도 예방할 수 있고 글로벌 변수도 없앨 수 있음, std::string, std::array, std::list, 익명 네임스페이스 쓰고 malloc도 제거하면 코드 길이가 절반에 버그도 줄어드는 결과를 보게 될 것임
    • 글로벌 변수들이 500줄 남짓 앱에서 별 영향 없음, 용도도 명확함, std::string, std::list로 바꾼다고 해서 실제 어셈블리 결과물이 같아지는 건 아님, 내부 동작을 정말 모르는 티임
    • Petzold 책조차 최신판에선 C++모드로 Visual C++로 빌드하고 C/C++ 공통구문을 권장함, Windows 95 시절엔 거의 C로만 짜는 사람은 별로 없었고, VB, Delphi, C++로 이미 주류 언어가 이동한 상태였음
    • 표준 string이나 array/list 쓰자는 의견에 대해, winapi를 직접 쓸 정도면 std::string 대신 LPWSTR(wide string)을 쓰는 게 API와 더 잘 맞고 추천됨, char[]처럼 구식 방법보다는 LPWSTR 권장, std::array나 list로 코드를 더 좋게 만들진 않을 것 같음
    • 현대적이진 않지만, 기초에 가까운 프로그래밍은 근본 동작을 제대로 알 수 있게 도움됨, 문제도 단순해서 이해하기 쉬움, 좋은 학습용 프로젝트라 생각함, 어셈블리 버전의 이런 앱 구현 사례도 궁금함