Android/오르다 다이어리

[Android/오르다 다이어리] 레거시 리팩토링 10 - Release 2 (v2.0.0) 완료 및 후기

yujinius 2025. 3. 2. 21:57
📝 [Android/오르다 다이어리] 3년 전 레거시 리팩토링, 기능 완성 & UI/UX 개선 - Release 2 (v2.0.0) 후기

📌 지난 글:https://yujinius45.tistory.com/164

 

[Android/오르다 다이어리] 레거시 리팩토링 09 - HomeFragment 애니메이션 적용 & UI 개선

[Android/오르다 다이어리] 레거시 리팩토링 09 - HomeFragment 애니메이션 적용 & UI 개선 지난 글:https://yujinius45.tistory.com/163 이제 기능 미완성이었던 것도 완성되고 UI/UX 개선도 완료되어서 릴리즈를

yujinius45.tistory.com

 


🚀 3년 전 레거시 코드 리팩토링, 그리고 마침내 기능 완성

오르다 다이어리를 처음 만들었을 때는 기능이 많았지만, 구조적으로 정리가 덜 되어 있었고, UI/UX에 대한 고민이 부족했던 상태였다.

처음 앱을 만들었을 때는 "기능만 구현되면 된다!"라는 마인드로 개발했었다.

하지만 3년이 지난 지금 다시 프로젝트를 리팩토링하면서,

과거에 미처 고려하지 못했던 부분들이 많았다는 걸 깨달았고, 이를 하나하나 개선해 나갔다.

이번 Release 2 (v2.0.0)에서는 미완성 기능을 완성하고, UX를 개선하며, 코드 구조를 리팩토링했다.

이번 프로젝트를 통해 많이 배웠던 점들을 정리해 보려고 한다.


🔍 3년 전 코드에서 개선이 필요했던 점

v1.0.0에서는 기본적인 일정 관리, 일기 작성, 알람, 지도 메모 기능이 포함되어 있었다.

하지만, 실제 사용하면서 여러 문제점이 드러났다.

기존 코드에서 개선이 필요했던 부분

  1. UI/UX 문제
    • 모든 화면이 개별적인 Activity로 구성되어 있어, 사용자가 홈으로 돌아가서 버튼을 눌러야만 다른 기능으로 이동 가능
    • 화면 이동이 불편하고 비효율적이었으며, 처음 사용자가 홈 화면에서 어떻게 앱을 시작해야 하는지 명확하지 않았음
    • 홈 화면이 정적 → 처음 앱을 실행한 사용자는 어떤 기능을 먼저 사용해야 할지 알기 어려움
  2. 기능 미완성
    • MapMemo 기능이 완성되지 않음 → 앱을 종료하면 저장한 데이터가 사라지는 문제 발생
    • DiaryWritingFragment상태 유지 불가 → 화면 회전 시 입력한 내용이 사라짐
  3. 코드 구조 및 보안 문제
    • DBHelper가 MainActivity에 종속되어 있음 → 독립적인 테스트 및 유지보수가 어려움
    • SQL Injection 취약점 존재 → 사용자의 입력값이 SQL 문과 함께 실행되어 보안 위험이 있었음
    • 직접 SQLite를 사용 → 관리가 어려워 유지보수성이 떨어짐

📌 리팩토링을 하며 배운 점

이번 프로젝트를 진행하면서 단순히 기능을 추가하는 것이 아니라, 어떻게 하면 유지보수하기 좋은 코드가 될까?를 고민하게 되었다.

내가 내 코드를 다시 보면서 개발을 할 때, 당장 기능이 동작하는 것보다, 이후 유지보수가 편하고 확장 가능한 코드를 작성하는 것이 중요하다는 것을 깨달았다.

 


1️⃣ 싱글톤 패턴을 적용하며 유지보수성이 높아진 사례

📌 기존 코드 문제점:

기존에는 DBHelperMainActivity에서 static 변수로 선언되어 있었다.

즉, 어떤 Fragment에서든 DBHelper를 사용하려면 MainActivity를 먼저 실행해야 했다.

 

📌 문제 발생 예시

public class MainActivity extends AppCompatActivity {
    public static DBHelper mDBHelper;  // MainActivity에 종속
}

이렇게 되면 DBHelper가 MainActivity에 강하게 결합되면서,

  • 다른 클래스에서 MainActivity 없이 DBHelper를 사용할 수 없음
  • MainActivity가 실행되지 않으면 DBHelper도 초기화되지 않음
  • 테스트 코드에서 독립적으로 DBHelper를 사용하기 어려움

📌 해결 방법: 싱글톤 패턴 적용

public class DBHelper extends SQLiteOpenHelper {
    private static DBHelper instance;

    public static synchronized DBHelper getInstance(Context context) {
        if (instance == null) {
            instance = new DBHelper(context.getApplicationContext());
        }
        return instance;
    }
}

 

📌 유지보수성이 어떻게 향상되었는가?

DBHelper가 특정 Activity에 의존하지 않게 됨 → 어디서든 DBHelper.getInstance(context)로 호출 가능

테스트 코드에서 독립적으로 DBHelper를 사용할 수 있음

메모리를 절약하고 중복 인스턴스 생성을 방지함


2️⃣ MVVM 패턴 적용으로 개선

📌 기존 코드 문제점:

기존에는 Activity, Fragment 등 UI에서 UI 상태를 직접 관리하고 있었다.

다른 화면들은 DB에서 데이터를 로드해와서 상태 유지를 하고 있었으나 일기 작성 화면 같은 경우 화면이 회전되면 데이터가 초기화되는 문제가 있었다.

 

📌 문제 발생 예시

public class DiaryWritingFragment extends Fragment {
    private EditText diaryContent;

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        diaryContent = view.findViewById(R.id.diaryContent);
    }
}

이렇게 하면 Fragment가 다시 생성될 때 데이터가 사라지는 문제 발생

 

📌 해결 방법: ViewModel을 사용하여 데이터 상태 유지

public class DiaryViewModel extends ViewModel {
    private final MutableLiveData<String> diaryContent = new MutableLiveData<>();

    public LiveData<String> getDiaryContent() {
        return diaryContent;
    }

    public void setDiaryContent(String content) {
        diaryContent.setValue(content);
    }
}

 

📌 어떻게 개선되었는가?

화면 회전 시에도 데이터가 유지됨 → 사용성이 향상됨

UI 로직과 비즈니스 로직이 분리됨 → 이후 추가 기능을 넣는다면 ViewModel에 추가해주면 됨

테스트 코드에서 ViewModel만 따로 테스트 가능 → 추후 기능이 추가된다면 진행할 수 있음


3️⃣ SQL Injection 방지 및 코드 관리가 쉬워진 사례

 

📌 기존 코드 문제점:

기존에는 사용자 입력값을 그대로 SQL 쿼리에 삽입하여 SQL Injection 취약점이 존재했다.

 

📌 문제 발생 예시 (보안 취약)

db.execSQL("INSERT INTO TODOLIST_TB (title, content) VALUES ('" + title + "', '" + content + "');");

만약 사용자가 "'); DROP TABLE TODOLIST_TB; --"을 입력하면, DB 테이블이 삭제될 수 있음

 

📌 해결 방법: Prepared Statement 사용

SQLiteDatabase db = getWritableDatabase();
String query = "INSERT INTO TODOLIST_TB (title, content) VALUES (?, ?)";
SQLiteStatement stmt = db.compileStatement(query);
stmt.bindString(1, title);
stmt.bindString(2, content);
stmt.executeInsert();

 

📌 어떻게 개선되었는가?

SQL Injection이 방어됨 → 보안 강화

쿼리가 가독성 좋게 정리됨 → 이후 추가할 때는 좀 더 읽기 쉬워졌음

테스트 코드에서 SQL이 정상 동작하는지 검증 가능


🚀 결론 및 마무리

이번 리팩토링을 통해, 앱을 단순히 기능만 구현하는 것이 아니라, 유지보수성이 높은 구조로 만드는 것이 중요하다는 것을 배웠다.

특히, 싱글톤 패턴 적용, MVVM 구조 도입, UI/UX 개선, 보안 강화 등의 작업을 진행하면서 성장할 수 있었다. (이후 더 작업을 진행한다면 RoomDB 적용을 고려해볼 것 같다. )

예전에는 기능이 돌아가기만 하면 된다고 생각했지만, 이제는 코드의 확장성과 유지보수성까지 고민하게 되었다.

앞으로도 더 좋은 코드와 UI/UX를 고민하면서 발전해 나가야겠다!

 


 

 

🔗 https://github.com/yujin45/Team3_Orda_Diary/releases/tag/v2.0.0 (상세 변경 사항은 릴리즈 노트 참고)

 

Release 📌 Orda Diary Release 2 (v2.0.0) · yujin45/Team3_Orda_Diary

What's Changed [Refactor] targetSdk 34 적용 및 권한 변경 대응 by @yujin45 in #1 [Refactor] Activity → Fragment 전환 및 기능 개선 + DB 수정 버그 해결 by @yujin45 in #2 [Refactor] DiaryWritingFragment Configuration Chang...

github.com