개발 고민#
요즘 체계적인 개발을 하기 위한 고민을 해요. 어떤 기능을 만들고 싶은데 그 기능을 어떤 순서로 만드는 것이 좋을지, 지금하고 있는게 맞는지 의심하다 보니 정작 코드를 많이 안치고 있어요.
지금 타이핑하는 것보다 "분명 더 나은 방식이 있을 것 같은데.."라고 생각하면서 이 의문의 정답이 있는지 아니면 하기 싫어서 허황된 것을 좇고 있다는 생각이 들어요. ChatGPT에게도 비슷한 질문을 했는데 답변은 이미 다 알고 있는 것들이지만 실제로 해오고 있는지 스스로를 돌아보게 되더라구요.
GPT는 저에게 개발 프로세스, 아키텍처, 협업 방식, 도구 선택에 대해 접근해 보라고 알려줬어요. 이 질문에 AI는 뭐라고 했는지 공유해 드릴게요. 물론 GPT의 답변이 고민에 대한 해답이 되지는 못했어요. 저는 어떤 기능에 대해 만들어야 하는 순서가 궁금했었거든요.
질문#
개발 언어는 배웠지만 개발하는 방식에 대해서는 늘 고민이야. 항상 효율적이고 올바른 방식으로 개발하고 있다고 생각이 안 들거든. 네가 아는 지식을 총동원해서 앱/웹 서비스 개발을 위한 가장 효율적인 개발 방식을 알려줘.
GPT 답변#
1. 요구사항 정의 및 기획#
- 목표 설정: 앱의 목적과 주요 기능을 명확히 정의합니다. 사용자 문제를 해결하기 위한 구체적인 목표를 설정하세요.
- 사용자 스토리: 사용자가 서비스에서 어떤 행동을 할지를 생각하며 사용자 스토리를 작성합니다. 이는 요구사항을 기능적으로 구체화하는 데 도움이 됩니다.
- MVP (Minimum Viable Product) 정의: 모든 기능을 한 번에 구현하는 것은 비효율적입니다. 핵심 기능만 포함한 최소 기능 제품을 정의하고, 이를 바탕으로 빠르게 사용자 피드백을 받습니다.
2. 아키텍처 설계#
- 모듈화: 각 기능을 독립적인 모듈로 나누어 설계합니다. 이를 통해 코드의 재사용성과 유지보수성을 높일 수 있습니다.
- 클린 아키텍처: 클린 아키텍처(또는 계층적 아키텍처)를 채택하면, 비즈니스 로직, UI, 데이터 접근을 명확히 분리할 수 있어 코드 관리가 쉬워집니다.
- API 설계: 프론트엔드와 백엔드가 명확히 통신할 수 있도록 RESTful 또는 GraphQL API를 설계합니다. API 문서화는 필수입니다.
3. 기술 스택 선택#
- 프론트엔드: React, Vue.js, Angular 같은 프레임워크 중 하나를 선택합니다. 각 프레임워크는 학습 곡선과 성능, 커뮤니티 지원이 다르므로 프로젝트 성격에 맞게 선택하세요.
- 백엔드: Node.js, Django, Ruby on Rails, Spring 등 필요한 성능과 확장성에 맞는 백엔드 프레임워크를 선택합니다.
- 데이터베이스: 관계형 데이터베이스(MySQL, PostgreSQL)와 NoSQL 데이터베이스(MongoDB, Firebase) 중 요구사항에 맞게 선택합니다.
- 클라우드 서비스: AWS, Google Cloud, Azure 같은 클라우드 플랫폼을 사용해 인프라를 자동화하고 배포를 쉽게 관리합니다.
4. 버전 관리 및 협업 도구#
- Git: 버전 관리를 위한 Git을 사용합니다. GitHub, GitLab, Bitbucket 등을 통해 협업을 강화할 수 있습니다.
- CI/CD: Jenkins, GitLab CI, CircleCI 같은 도구로 CI/CD 파이프라인을 구축해 자동화된 테스트와 배포를 설정합니다.
- 프로젝트 관리: Jira, Trello, Asana 같은 도구를 사용해 작업을 관리하고 팀의 생산성을 극대화합니다.
5. 테스트#
- 단위 테스트: 각 기능의 단위 테스트를 통해 코드가 의도한 대로 작동하는지 확인합니다. Jest, Mocha, JUnit 등 테스트 프레임워크를 사용하세요.
- 통합 테스트: 모듈 간 상호작용을 테스트합니다. API 테스트는 Postman, Newman 등을 활용할 수 있습니다.
- 자동화 테스트: Selenium, Cypress 같은 도구를 활용해 반복적인 테스트를 자동화합니다.
6. 배포 및 운영#
- 배포 자동화: CI/CD 파이프라인을 통해 배포를 자동화하고, 무중단 배포(Zero-downtime deployment)를 목표로 합니다.
- 모니터링: New Relic, Datadog, Prometheus 같은 모니터링 도구로 애플리케이션의 상태를 실시간으로 모니터링하고 문제를 빠르게 파악합니다.
- 로깅: Logstash, Kibana, Grafana 같은 도구를 사용해 로그를 관리하고 분석합니다.
7. 지속적인 개선#
- 사용자 피드백 반영: MVP 또는 초기 버전을 배포한 후 사용자 피드백을 적극 반영해 기능을 개선합니다.
- 코드 리팩토링: 주기적으로 코드를 리팩토링해 유지보수성을 높이고, 기술 부채를 줄입니다.
- 성능 최적화: 애플리케이션의 성능을 지속적으로 모니터링하고, 필요에 따라 데이터베이스 최적화, 캐싱, 코드 최적화를 진행합니다.
파일을 생성하고 단순 반복되는 코드를 치기는 싫어서 freezed나 json_serializable 같은 코드 생성 패키지를 찾아보고 VS Code 스니펫도 시도해 봤어요. 그리고 Flutter에 개발 중인 Macros라는 것도 있는데 이걸 활용하면 더 편해지지 않을까 싶어요.
저도 저 스스로와도 많은 대화를 해야겠지만 이런 걸 원하는 것 같아요.
- 어떤 서비스를 만들고 싶은데 모든 걸 제가 만들어야 하는 상황
- 앱 출시와 지속적인 운영을 위해 먼저 해야 할 것: 기획, 디자인, 개발
- 항목마다 간단하게 결정해서 진행하고 싶음
- 할 게 많은 개발에서 생산성을 챙기고 싶음
- 개발 생산성을 높이려고 폴더 구조, Flutter 개발 아키텍처, 상태 관리 사용, 유용한 패키지 활용 조사 등을 찾고 있음
- 개발 아키텍처와 상태 관리를 결합해 코드 퀄리티를 높이고 싶음
저는 앱 서비스 개발로 플러터를 선택했어요. 그런데 플러터로 개발하다 보니 스프링 개발 때 쓰던 MVC 패턴 같은 건 없나 찾아보게 되었어요. 플러터에는 개발 아키텍처가 여러 개 있지만 그중에서도 Riverpod과 연결할 수 있는 Feature-First 아키텍처가 마음에 들었어요. 이 아키텍처를 잘 쓰는 건 아니지만 적용해 보면 어떨까 싶었어요.
그런데 뭐 하나 만들 때마다 내가 아키텍처를 잘 따르고 있는지 강박적으로 생각하게 되더라구요. 이러다 보니 이 아키텍처를 잘 따르면서도 기능을 만드는 순서가 존재할 것 같았어요. 처음부터 UI를 만들 것이냐, Model을 만들고 UI와 분리된 기능까지 만든 뒤, UI를 마지막에 만들어야 할까. 이런 생각이 들어서 GPT에게도 아래 방식에 관해 물어봤었어요.
질문#
개발 아키텍처는 만들어야 할 파일과 폴더가 많아. Feature-First Architecture를 만드는 순서를 알려줘.
GPT 답변#
Feature-First Architecture에서 파일과 폴더가 많아지는 것은 자연스러운 일이지만, 이를 효율적으로 관리하기 위해서는 구조를 설정하고 기능을 추가하는 순서가 중요합니다. 이 순서를 따름으로써 프로젝트의 복잡성을 줄이고, 점진적으로 확장할 수 있습니다. 아래는 Feature-First Architecture를 구축할 때 고려할 수 있는 단계별 순서입니다.
1. 기본 폴더 구조 설정#
먼저, 프로젝트의 전반적인 구조를 잡아야 합니다. 이를 통해 프로젝트 내에서 코드가 어디에 위치할지를 명확히 할 수 있습니다.
lib/
├── features/
│ ├── feature_name/
│ │ ├── data/
│ │ ├── domain/
│ │ └── presentation/
├── core/
│ ├── common_widgets/
│ ├── exceptions/
│ ├── network/
│ └── theme/
└── main.dart
이 단계에서는 각 기능에 해당하는 폴더와 코어 폴더를 기본적으로 설정해 두는 것이 중요합니다. 기능이 추가될 때마다 해당 기능에 맞는 하위 폴더를 생성합니다.
2. 기능 개발 순서#
개별 기능을 추가할 때, 각 단계에서 필요한 파일과 폴더를 생성하고 구현합니다. 기능을 추가하는 순서는 다음과 같습니다:
- 기능 요구사항 분석 및 기획
- 해당 기능의 역할과 요구사항을 정의합니다.
- 주요 유스케이스와 사용자 플로우를 파악하여, 어떤 데이터와 로직이 필요한지 결정합니다.
- 도메인 계층 구현 (Domain Layer)
- Entities: 해당 기능에서 사용될 주요 엔티티(비즈니스 모델)를 정의합니다.
- 예:
lib/features/login/domain/entities/user.dart
- 예:
- Use Cases: 비즈니스 로직을 처리하는 유스케이스를 작성합니다.
- 예:
lib/features/login/domain/use_cases/login_use_case.dart
도메인 계층을 먼저 구현하는 이유는 기능의 핵심 로직을 중심으로 데이터와 프레젠테이션 계층을 설계할 수 있기 때문입니다.
- 예:
- Entities: 해당 기능에서 사용될 주요 엔티티(비즈니스 모델)를 정의합니다.
- 데이터 계층 구현 (Data Layer)
- Repositories: 도메인 계층에서 정의한 인터페이스를 구현하여, 데이터 소스를 관리합니다.
- 예:
lib/features/login/data/repositories/auth_repository_impl.dart
- 예:
- Data Providers: API 호출이나 로컬 데이터베이스 접근 등을 담당하는 클래스들을 구현합니다.
- 예:
lib/features/login/data/providers/auth_api_provider.dart
- 예:
- Models: API나 로컬 데이터에서 사용되는 데이터를 모델로 정의합니다.
- 예:
lib/features/login/data/models/user_model.dart
이 단계에서는 데이터 소스를 도메인 계층과 연결해 기능이 실제로 동작할 수 있도록 합니다.
- 예:
- Repositories: 도메인 계층에서 정의한 인터페이스를 구현하여, 데이터 소스를 관리합니다.
- 프레젠테이션 계층 구현 (Presentation Layer)
- State Management: Riverpod을 사용하여 상태 관리를 구현합니다. 여기서 필요한 상태와 비즈니스 로직을 담은 상태 노티파이어를 정의합니다.
- 예:
lib/features/login/presentation/state/login_state.dart
- 예:
- Screens: 기능의 화면(UI)을 구현합니다. 상태에 따라 UI가 어떻게 변화하는지 정의합니다.
- 예:
lib/features/login/presentation/screens/login_screen.dart
- 예:
- Widgets: 해당 기능에서 재사용 가능한 위젯을 구현합니다.
- 예:
lib/features/login/presentation/widgets/login_form.dart
프레젠테이션 계층에서는 최종적으로 사용자에게 보이는 화면과 상호작용을 구현합니다.
- 예:
- State Management: Riverpod을 사용하여 상태 관리를 구현합니다. 여기서 필요한 상태와 비즈니스 로직을 담은 상태 노티파이어를 정의합니다.
- 단위 테스트 작성
- 각 계층에 대한 단위 테스트를 작성합니다. 이때, 도메인 계층과 데이터 계층에서의 테스트를 우선으로 진행합니다.
- 예:
lib/features/login/domain/use_cases/login_use_case_test.dart
- 예:
lib/features/login/data/repositories/auth_repository_impl_test.dart
이 과정은 각 계층의 기능이 독립적으로 올바르게 동작하는지를 보장합니다.
- 기능 통합 및 검토
- 도메인, 데이터, 프레젠테이션 계층이 잘 통합되었는지 확인하고, 유닛 테스트와 통합 테스트를 통해 검증합니다.
- 초기에는 작은 기능 단위로 시작해 전체적인 구조와 설계를 반복적으로 검토하고 개선합니다.
3. 공통 코드 및 리팩토링#
기능들이 추가되면서 공통적으로 사용되는 코드가 많아질 것입니다. 이를 위해 core 폴더에 공통적인 로직이나 유틸리티, 위젯 등을 리팩토링하여 모듈화합니다.
- 공통 위젯: 여러 기능에서 사용할 수 있는 공통 위젯은
core/common_widgets/
에 위치시킵니다. - 네트워크 관리: API 호출 로직은
core/network/
에 모듈화하여 관리합니다. - 예외 처리: 공통 예외 처리는
core/exceptions/
에서 관리하고, 각 기능에서 이를 활용합니다.
4. 프로젝트 확장 및 유지보수#
- 새 기능을 추가할 때, 위에서 설명한 순서를 반복하여 점진적으로 프로젝트를 확장합니다.
- 기존 기능에서 발견된 문제나 개선사항이 있으면, 리팩토링 단계를 통해 개선합니다.
5. 자동화 및 배포#
- CI/CD 설정: 코드 푸시 시 테스트와 빌드를 자동화하여 안정적으로 프로젝트를 관리합니다.
- 릴리즈 관리: 버전 관리를 통해 새 기능이 제대로 릴리즈될 수 있도록 관리합니다.
주요 기능부터 시작해 점진적으로 확장하며, 테스트와 리팩토링을 통해 프로젝트의 품질을 유지하는 것이 중요합니다.
마무리#
최근 스레드에서 개발과 관련된 글을 읽었는데 아래 문구가 저에게 굉장히 강하게 다가왔어요. 저도 혼자 개발하고 있는게 있는데 늦어지고 있는게 사실이거든요.
코드가 아름다운 스타트업은 망한다.
너무 오래, 많이 고민하는 건 좋은 성과를 내지 못하는 지름길인 것 같아요. 이런 좋다고 생각하지 못하는 습관을 고쳐봐야겠어요.
코드 수준이 높은 건 좋아도 일(비즈니스)부터 진행하는 것이 우선이 되도록 해야겠어요.
Treat people as if they were what they ought to be, and you help them to become what they are capable of being.
— Johann Wolfgang von Goethe