[토이 프로젝트] 첫 개인 플젝 '고함항아리' 개발 기록 (Qt C++)

허션·2025년 7월 27일
0

프로젝트

목록 보기
1/1
post-thumbnail

메인 화면 UI

깃허브 링크 에서 실행 파일을 다운 받아 사용할 수 있습니다!

프로젝트 소개

  • 프로젝트 이름 : Scream Jar - 고함 항아리

  • 기획/개발 동기 :

    공부와 과제 스트레스, 인간관계, 내 말을 듣지 않는 일 동료들과 팀플 동료들. 매번 친구들과의 카톡방에 하소연하기도 그렇고, 나와의 톡방에 계속 정체모를 비명만 쌓여간다. 프로젝트의 제목인 ‘고함 항아리’처럼, 사용자가 고함을 지르고 싶지만 여러 가지 사회적 · 환경적 · 상황적 여건으로 물리적으로 고함을 지르는 것이 불가할 때 사용자에게 위안을 주고자 계획되었다. 과다 경쟁 사회에서 현대인이 맞닥뜨리는 비합리적 규모의 스트레스를 합리적인 방안으로 풀어주는 앱을 프로젝트로 만들고자 했다.

    2024-2 전공과목으로 수강한 ‘컴퓨터프로그래밍’ 수업에서 진행한 자유 주제 프로젝트로, 재미있는 주제이고 프로젝트에 미련이 남아 이후 서버 연동을 통한 친구 추가 기능과 피드 확인 기능까지 확장하여 깃허브에 업로드했다.

    설명

  • 사용 언어/프레임워크 :

    ComponentTech
    FrontendC++ with Qt (Widgets, UI)
    BackendPython (Flask)
    DatabaseSupabase (PostgreSQL)
    UI DesignQt Designer (.ui files)

  • 기능 :

    • 😤 고함 지르기 : 텍스트박스에 고함을 입력하고 Enter 키 혹은 Scream! 버튼을 누름으로써 고함항아리에 저장할 수 있다. 이 때 스트레스 요인에 따라 고함의 카테고리를 설정할 수 있다. UI 중앙의 항아리를 클릭할 시, 저장된 고함들의 목록을 확인할 수 있다. scream page
    • 🗓️ 달력 기반 고함 분석 : 고함의 빈도를 캘린더 상 컬러코딩으로 시각화했으며, 카테고리별 고함 분포 비율을 확인할 수 있다. analysis
    • 🎨 커스터마이징 : 앱의 배경색을 사용자의 선호에 맞게 변경할 수 있으며, 변경된 색상은 유저 데이터에 저장된다. customization
    • 🧑‍🤝‍🧑 친구 기능 & 소셜 피드 : 유저 ID를 통해 친구 추가를 하고 추가된 친구의 가장 최근의 고함을 피드에서 확인할 수 있다. friend
      feed

기능 구현 설명

  • 로컬 DB - 서버 연동 아키텍처

    • 로컬 DB 구현 :

      • 따로 클래스를 두지 않고 database.hdatabase.cpp로 클래스 인스턴스 없이 곧바로 데이터베이스 함수를 이용할 수 있게 했다.
      • SQlite를 기반으로 하는 QSql을 이용하여 로컬 데이터베이스를 관리했다.
      • UserScream 두 가지 객체를 어떻게 관리할지의 문제
        → 사용자 정보를 저장하는 User의 table, 그리고 사용자가 입력하는 고함인 Scream의 table을 따로 두고, ‘userID’ 열을 새로 만들어 Scream 객체와 User 객체를 연결할 수 있도록 했다.
         users(
                 id TEXT PRIMARY KEY, 
                 username TEXT NOT NULL, 
                 wallColor TEXT, 
                 friendList TEXT DEFAULT
                )
                
        screams(
            id INTEGER PRIMARY KEY,
            userID TEXT, 
            categoryIndex INTEGER,
            content TEXT,
            screamDate TEXT,
            FOREIGN KEY (userID) REFERENCES users(id) ON DELETE CASCADE)
        )
    • 서버 구현 :

      • HTTP request를 보내는 서버 파일 server.pypyflask 를 이용해 작성했다.
      • Flask를 이용한 RESTful API 기반 서버 파일 + Render 서버 + PostgreSQL 기반 Supabase의 무료 데이터베이스 이용
        • 서버 연결을 통해 확장하고자 하는 기능이 사용자 인증, 친구 관리, 피드 확인뿐이었기에, 소규모(micro-framework)인 Flask가 적합할 것이라고 여겼으며 빠르게 습득하여 간단한 HTTP 요청을 작성하기 편리했다.
          • Client-side 코드 : QNetworkAccessManager 을 이용하여 ServerAPI 클래스를 따로 두어 작성. 서버 접근이 필요한 클래스들의 property로 ServerAPI* server을 두고, 가장 상위의 클래스에서 한 번의 초기화 이후 하위의 클래스들에게 넘겨주는 방식으로 이용.
        • 유료인 Render Disk를 사용하지 않는 이상, Render 서버 연결만으로는 데이터베이스가 앱의 실행 사이에서 지속적으로 유지되지 않음을 발견. → SQlite에서 PostgreSQL 기반으로 flask 파일 수정, supabase 연동
      • 친구 기능 구현 고민 : 각 User마다 친구의 목록을 테이블로 유지하기에는 비효율적이고 오버헤드가 클 것이라 생각되어, User에 친구 목록을 쉼표로 구분된 아이디 리스트의 문자열로 두는 방식으로 구현했다. 친구의 정보를 불러올 때는 ServerAPI 클래스의 함수를 이용해서 서버에 HTTP 요청을 보낸다.


    • 전체적 구조

      diagram

      • client ↔ 서버 상호작용
        서버의 request들은 모두 asynchronous function이었기에, request 완료 이후 signal emit → 메인 코드에서 signal으로 전달되는 데이터 혹은 완료 여부 이용
        UI (MainPage) ←→ ServerAPI ←→ Flask Server
        UI (MainPage) ←→ Database ←→ Local SQLite
      • 로컬 DB 초기 로그인에서 유저 정보를 서버에서 불러온 이후 로컬 DB에 캐싱, 이후 앱 종료까지 사용자의 정보는 로컬 DB에서만 read, 업데이트는 로컬 DB에 write 함과 동시에 server로 asynchronous write하는 write-through 이용
        [ App Starts ]
            ↓
        StartPage widget - pass credential input
            ↓
        login via ServerAPI
            ↓
        receive success signal in MainWindow 
        → load user from server + save in local DB + switch to MainPage UI
        
        [ New Scream ]
        		↓
        ServerAPI::saveScreamToServer() → POST to server
        		 +
        Save to Object(running-time data) & local cache
        [ User Adds Friend ]
        		↓
        ServerAPI::addFriend() → POST to server
        		+ 
        update local DB (friendList)

  • 문제 해결 과정

    • 친구 목록에서의 친구 유저 정보, 혹은 피드에서 친구의 고함 정보를 서버에서 불러올 때 약간의 지연이 발생한다. 유저가 이 지연이 버그가 아니라 로딩중임을 알 수 있도록, Qt의 view↔Model 구조에서 Model initialization 완료 시 view에 signal을 보내도록 하여 view가 signal을 받은 후에 로딩 표시를 숨기고 내용물을 표시할 수 있도록 했다.
    • circular inclusion 문제 : 두 헤더 파일에서 서로의 헤더 파일을 포함하는 #include 문이 상호적으로 있어서 circular inclusion으로 인한 에러가 발생했다. → header file에서 다른 class type를 이용해야 할 때, pointer로만 이용할거면 forward declaration으로만 구현 후 추후 cpp 파일에서 #include를 할 수 있다.

  • 기능 기획 고민

    • 비밀번호 사용 :
      • 서버 연결이 없었던 고함항아리의 초안은 username / password로, 회원가입과 로그인을 동시에 할 수 있는 구조. 개인의 컴퓨터에서만 사용했기에 민감한 정보인 비밀번호는 아예 사용 X
      • 서버 연결 이후에는 StartPage의 로그인과 회원가입 위젯을 따로 두고, 비밀번호를 서버 측에만 암호화한 상태로 저장하도록 했다. 로컬 데이터베이스에는 비밀번호를 일체 저장하지 않았기에, 자동 로그인 기능은 구현하지 않았다. (trade-off)
    • ‘피드’ 기능 :
      • 사용자의 개인적인 감정과 생각들을 자유롭게 ‘고함’으로 내지를 수 있다는 서비스의 본질을 유지하며, 타 사용자와 함께 연결됨으로써 느낄 수 있는 재미 요소를 ‘고함 피드(Scream Feed)’ 기능을 통해 개발하고 싶었다. 그러나 ‘친구’의 모든 고함을 볼 수 있다면 사용자의 프라이버시 침해로 인해 기존 기능에서의 제약이 발생할 것이 우려되었다.
      • 따라서 고민 끝에 친구 추가 후, 피드로는 친구가 가장 최근에 지른 고함만 보이도록 했다. Scream object에 private 여부를 property로 두어 공개로 표시된 고함만 보이도록 하는 방법도 있으나, UI/UX가 지나치게 복잡해질 것을 우려하여 앱의 편의를 유지할 수 있으면서 개별 사용자의 비밀 유지와 재미 사이의 균형을 잡을 수 있는 방안을 채택했다.
  • UX 고민, 개선

    • 메인 UI의 고함항아리를 클릭했을 때, 팝업 창이 여러 번 등장하지 않도록, 팝업에 대한 포인터를 만들어 관리했다.
    • 앱 사용이 복잡해지지 않도록(열려 있는 창의 수가 너무 많아지지 않도록) 윈도우 형태로 등장하는 QDialog와 단순 위젯 형태의 QWidget, 두 가지 종류의 팝업을 구분해서 사용했다. 사용 시간이 짧게 소요될 것으로 추정되는 친구 확인 및 친구 추가, 배경 색 변경의 경우 일시적 팝업 위젯인 QWidget을 사용했다.
    • 시작 화면에서의 로그인 입력, 그리고 메인 화면에서의 고함 입력 요소들은 사용자가 빈번하게 (앱을 시작할 때마다, 고함을 입력할 때마다) 사용하는 요소들이므로, login / Scream 버튼을 마우스로 클릭하지 않더라도 엔터 키를 누르면 자동적으로 버튼으로 연결되도록 했다. QTextEdit class를 CustomTextEdit class로 계승하여 구현했다.
    • 자신의 고함 확인 팝업에서 Date-time column header을 클릭할 경우 날짜 오름차순 / 내림차순 정렬을 변경할 수 있다. QSortFilterProxyModel class를 override하여 구현했다.
    • 고함이 저장될 때, 사용성 및 재미를 위해 QAnimation으로 고함항아리가 흔들리는 효과를 추가했다.
profile
다시해보자.

0개의 댓글