Thursday, August 24, 2023

Flutter CLI: 터미널에서 완성하는 앱 개발 워크플로우

현대적인 애플리케이션 개발 환경에서 커맨드 라인 인터페이스(Command Line Interface, CLI)는 단순한 보조 도구를 넘어 개발 생산성과 워크플로우 자동화의 핵심적인 역할을 수행합니다. 그래픽 사용자 인터페이스(GUI)가 제공하는 직관적인 편리함도 중요하지만, 반복적인 작업을 스크립트로 자동화하고, 빌드 및 배포 파이프라인을 구축하며, 프로젝트의 세밀한 설정을 제어하는 데 있어 CLI의 강력함은 대체 불가능합니다. Flutter 개발 생태계에서 이러한 중추적인 역할을 담당하는 것이 바로 Flutter CLI입니다.

Flutter CLI는 Flutter SDK에 내장된 강력한 명령 줄 도구로, 개발자가 터미널이나 커맨드 프롬프트에서 Flutter 프로젝트의 전체 생명주기를 관리할 수 있도록 지원합니다. 프로젝트 생성부터 의존성 관리, 코드 분석, 테스트, 빌드, 그리고 배포에 이르기까지, 앱 개발에 필요한 거의 모든 작업을 단 몇 줄의 명령어로 수행할 수 있습니다. 이는 개발자가 IDE(통합 개발 환경)에만 의존하지 않고, 보다 유연하고 효율적으로 개발 프로세스를 제어할 수 있음을 의미합니다. 이 글에서는 Flutter CLI의 기초부터 실무 활용까지, 그 잠재력을 최대한 이끌어내는 방법을 체계적으로 살펴보겠습니다.

1. Flutter CLI의 핵심 가치와 장점

Flutter CLI는 단순히 명령어를 실행하는 도구가 아닙니다. Flutter 프레임워크가 추구하는 'Write once, run anywhere' 철학을 기술적으로 구현하고, 개발자 경험을 극대화하는 핵심 엔진입니다. CLI가 제공하는 다양한 기능들은 다음과 같은 핵심적인 장점으로 귀결됩니다.

> 진정한 크로스플랫폼 개발의 실현

Flutter의 가장 큰 매력은 단일 코드베이스로 Android, iOS, 웹, 데스크톱(Windows, macOS, Linux) 애플리케이션을 모두 구축할 수 있다는 점입니다. Flutter CLI는 이 과정을 매끄럽게 지원합니다. flutter create 명령어 하나만으로 각 플랫폼에 필요한 네이티브 프로젝트 구조(예: `android`, `ios` 폴더)를 자동으로 생성합니다. 또한, flutter run -d <device-id>와 같은 명령어를 통해 특정 플랫폼의 기기나 에뮬레이터에서 앱을 손쉽게 실행하고 테스트할 수 있습니다. flutter build <platform> 명령어를 사용하면 각 플랫폼 스토어에 제출할 수 있는 최종 배포 패키지(APK, AppBundle, IPA 등)를 생성하는 복잡한 과정을 표준화된 방식으로 처리합니다. 이 모든 것이 CLI를 통해 일관된 경험으로 제공되기에 개발자는 플랫폼별 빌드 시스템의 복잡한 내부 구조를 깊이 알지 못해도 효율적으로 멀티플랫폼 앱을 개발하고 배포할 수 있습니다.

> 일관성 있는 프로젝트 관리와 협업 환경

팀 단위 프로젝트에서 일관된 개발 환경과 프로젝트 구조는 매우 중요합니다. Flutter CLI는 flutter create를 통해 모든 Flutter 프로젝트가 표준화된 디렉터리 구조를 갖도록 보장합니다. 이를 통해 새로운 팀원이 프로젝트에 합류하더라도 코드의 위치를 빠르게 파악하고 적응할 수 있습니다. 또한, 프로젝트의 모든 의존성 정보는 `pubspec.yaml` 파일에 명시적으로 선언되며, flutter pub get 명령어를 통해 모든 팀원이 동일한 버전의 라이브러리를 설치하고 개발 환경을 동기화할 수 있습니다. 이는 "제 컴퓨터에서는 됐는데..."와 같은 일반적인 협업 문제를 원천적으로 방지하는 데 큰 도움이 됩니다.

> 풍부한 생태계와의 완벽한 통합

Flutter는 pub.dev라는 중앙 패키지 저장소를 통해 수만 개의 라이브러리와 플러그인을 제공하는 강력한 생태계를 갖추고 있습니다. Flutter CLI는 이 생태계를 활용하는 핵심적인 관문입니다. 예를 들어, 상태 관리 라이브러리인 'provider'를 프로젝트에 추가하고 싶다면, 터미널에서 flutter pub add provider 명령어만 실행하면 됩니다. 이 명령어는 자동으로 최신 안정 버전의 패키지를 `pubspec.yaml` 파일에 추가하고, 필요한 파일을 다운로드하여 프로젝트에 통합합니다. 패키지를 제거하거나 업데이트하는 작업 또한 flutter pub remove, flutter pub upgrade와 같은 간단한 명령어로 처리할 수 있어, 외부 라이브러리 관리가 매우 용이합니다.

> 개발 생산성을 극대화하는 개발 사이클

Flutter의 가장 혁신적인 기능 중 하나는 '핫 리로드(Hot Reload)'입니다. 이는 코드를 수정한 후 몇 초 만에 실행 중인 앱의 UI에 변경 사항을 즉시 반영하는 기능입니다. 이 강력한 기능은 flutter run 명령어를 통해 활성화됩니다. 개발자가 코드를 저장하면 CLI가 변경 사항을 감지하여 실행 중인 가상 머신(VM)으로 델타(변경분)를 전송하고, 앱을 재시작하지 않고도 위젯 트리를 다시 빌드합니다. 이 덕분에 UI를 미세 조정하거나 로직을 수정할 때마다 매번 앱을 다시 컴파일하고 시작해야 하는 지루한 대기 시간을 획기적으로 줄일 수 있어, 개발 흐름이 끊기지 않고 생산성이 극대화됩니다.

2. 개발 환경 구축: Flutter SDK 설치부터 점검까지

Flutter CLI를 사용하기 위한 첫걸음은 개발 환경을 올바르게 설정하는 것입니다. 이 과정은 운영체제별로 약간의 차이가 있지만, Flutter SDK를 다운로드하고, 실행 경로를 시스템에 등록하며, 개발에 필요한 부가적인 도구들을 설치하는 핵심적인 단계로 구성됩니다.

> 사전 준비물

Flutter를 설치하기 전에 몇 가지 기본적인 도구가 시스템에 설치되어 있어야 합니다.

  • Git: Flutter SDK는 Git을 사용하여 버전 관리가 되므로, Git이 반드시 설치되어 있어야 합니다.
  • IDE 또는 텍스트 에디터: Visual Studio Code, Android Studio, IntelliJ 등 Dart와 Flutter 플러그인을 지원하는 IDE를 권장합니다.

> 운영체제별 Flutter SDK 설치 및 환경 변수 설정

Windows

  1. SDK 다운로드: Flutter 공식 웹사이트에서 Windows용 최신 안정 버전(stable)의 SDK 압축 파일을 다운로드합니다.
  2. 압축 해제: 다운로드한 zip 파일의 압축을 풉니다. 이때, `C:\Program Files`와 같이 관리자 권한이 필요한 디렉터리는 피하는 것이 좋습니다. 일반적으로 `C:\src\flutter`와 같은 경로를 권장합니다.
  3. 환경 변수 설정:
    • Windows 검색창에서 '환경 변수'를 검색하여 '시스템 환경 변수 편집'을 실행합니다.
    • '환경 변수' 버튼을 클릭한 후, '사용자 변수' 또는 '시스템 변수' 목록에서 'Path'를 선택하고 '편집'을 누릅니다.
    • '새로 만들기'를 클릭하고, Flutter SDK 압축을 해제한 경로에 있는 `bin` 폴더의 전체 경로(예: `C:\src\flutter\bin`)를 추가합니다.
    • 모든 창에서 '확인'을 눌러 변경 사항을 저장합니다.
  4. 확인: 새로 연 PowerShell이나 명령 프롬프트 창에서 flutter --version 명령어를 실행하여 Flutter가 성공적으로 인식되는지 확인합니다.

macOS

  1. SDK 다운로드: 터미널을 열고 Git을 사용하여 Flutter SDK를 복제(clone)하는 것이 가장 일반적인 방법입니다.
    git clone https://github.com/flutter/flutter.git -b stable
  2. 환경 변수 설정:
    • 터미널에서 사용하는 셸의 설정 파일(일반적으로 `~/.zshrc` 또는 `~/.bash_profile`)을 엽니다.
      open ~/.zshrc
    • 파일 맨 아래에 다음 줄을 추가합니다. `[PATH_TO_FLUTTER_GIT_DIRECTORY]` 부분은 위에서 `git clone`을 실행한 Flutter 폴더의 전체 경로로 변경해야 합니다.
      export PATH="$PATH:[PATH_TO_FLUTTER_GIT_DIRECTORY]/flutter/bin"
    • 파일을 저장하고 닫은 후, 터미널에 변경 사항을 적용합니다.
      source ~/.zshrc
  3. 확인: 새 터미널 창에서 flutter doctor를 실행하여 설치 상태를 확인합니다.

> `flutter doctor`: 개발 환경 자가 진단

환경 변수 설정이 완료되었다면, 가장 먼저 실행해야 할 명령어는 flutter doctor입니다. 이 명령어는 Flutter 개발에 필요한 모든 요소(Flutter SDK, 연결된 기기, 플랫폼별 개발 도구 등)를 점검하고, 문제가 있는 경우 해결 방법을 상세히 안내해주는 매우 유용한 진단 도구입니다.

flutter doctor -v

`-v` (verbose) 플래그를 추가하면 더 상세한 진단 결과를 볼 수 있습니다. `flutter doctor`의 출력 결과는 일반적으로 다음과 같은 항목들로 구성됩니다.

  • [✓] Flutter: Flutter SDK 자체의 설치 상태를 나타냅니다.
  • [!] Android toolchain: Android 앱 개발에 필요한 환경입니다. Android Studio, Android SDK, cmdline-tools 등이 제대로 설치 및 설정되었는지 확인합니다. 문제가 있다면, 보통 Android Studio를 열고 SDK Manager에서 필요한 컴포넌트를 설치하라는 메시지가 나타납니다.
  • [!] Xcode: macOS에서 iOS 앱을 개발하기 위한 환경입니다. Xcode와 CocoaPods의 설치 상태를 점검합니다. `sudo gem install cocoapods`와 같은 명령어로 문제를 해결할 수 있습니다.
  • [✓] Chrome: 웹 앱 개발을 위한 Chrome 브라우저 설치 여부를 확인합니다.
  • [!] Android Studio: Android Studio 설치 여부와 Flutter/Dart 플러그인 설치 상태를 확인합니다.
  • [!] Connected device: 현재 PC에 연결되어 개발용으로 인식된 기기(실제 기기 또는 에뮬레이터)의 수를 보여줍니다.

모든 항목 앞에 녹색 체크 표시(✓)가 나타날 때까지 `flutter doctor`가 제안하는 해결 방법을 따라 문제를 해결하는 것이 중요합니다.

3. 프로젝트의 첫걸음: `flutter create`와 디렉터리 구조

개발 환경 구축이 완료되었다면, 이제 Flutter CLI를 사용하여 첫 프로젝트를 생성할 차례입니다. flutter create 명령어는 단순히 빈 폴더를 만드는 것을 넘어, 잘 구조화된 프로젝트 템플릿을 생성하여 개발의 시작을 돕습니다.

> `flutter create` 명령어 심층 분석

가장 기본적인 프로젝트 생성 명령어는 다음과 같습니다.

flutter create my_awesome_app

이 명령어는 `my_awesome_app`이라는 이름의 폴더를 생성하고 그 안에 Flutter 프로젝트를 초기화합니다. 하지만 flutter create는 더 많은 옵션을 제공하여 프로젝트를 세밀하게 설정할 수 있습니다.

  • 조직(Organization) 지정: --org 플래그는 앱의 패키지 이름(Android)이나 번들 식별자(iOS)를 결정하는 데 사용되는 조직의 도메인을 지정합니다. 이는 앱 스토어에 앱을 게시할 때 고유 식별자로 사용되므로 매우 중요합니다.
    flutter create --org com.mycompany my_awesome_app
    위 명령어는 Android 패키지 이름을 `com.mycompany.my_awesome_app`으로 설정합니다.
  • 플랫폼 지정: --platforms 플래그를 사용하여 프로젝트가 지원할 플랫폼을 명시적으로 선택할 수 있습니다. 예를 들어, 모바일 앱만 개발한다면 다음과 같이 지정할 수 있습니다.
    flutter create --platforms=android,ios my_mobile_app
  • 템플릿 선택: --template 플래그를 사용하면 다양한 목적의 프로젝트 템플릿을 생성할 수 있습니다.
    • `app` (기본값): 일반적인 Flutter 애플리케이션 템플릿을 생성합니다.
    • `package`: 다른 프로젝트에서 재사용할 수 있는 순수 Dart 라이브러리를 생성합니다.
    • `plugin`: Dart 코드와 네이티브 플랫폼(Android/iOS) 코드를 함께 사용하여 플랫폼별 기능을 구현하는 플러그인을 생성합니다.
    • `skeleton`: 간단한 목록/상세 화면 구조를 가진, 보다 발전된 형태의 앱 템플릿을 생성하여 프로젝트 구조를 참고하는 데 도움이 됩니다.
    flutter create --template=skeleton my_structured_app

> Flutter 프로젝트 디렉터리 구조 상세 해부

flutter create로 생성된 프로젝트 폴더는 다음과 같은 표준화된 구조를 가집니다. 각 디렉터리와 파일의 역할을 이해하는 것은 효율적인 개발의 기초가 됩니다.

my_awesome_app/
├── android/            # Android 네이티브 프로젝트
├── ios/                # iOS 네이티브 프로젝트
├── lib/                # 모든 Dart 코드가 위치하는 핵심 디렉터리
│   └── main.dart       # 앱의 시작점(entry point)
├── test/               # 테스트 코드 디렉터리
├── web/                # 웹 네이티브 프로젝트 (생성 시 포함된 경우)
├── windows/            # Windows 네이티브 프로젝트 (생성 시 포함된 경우)
├── macos/              # macOS 네이티브 프로젝트 (생성 시 포함된 경우)
├── linux/              # Linux 네이티브 프로젝트 (생성 시 포함된 경우)
├── .gitignore          # Git이 추적하지 않을 파일/폴더 목록
├── analysis_options.yaml # 정적 코드 분석(Lint) 규칙 설정 파일
├── pubspec.yaml        # 프로젝트 메타데이터 및 의존성 관리 파일
├── pubspec.lock        # 의존성 패키지들의 정확한 버전을 고정하는 파일
└── README.md           # 프로젝트 설명 파일
  • `android` / `ios` / `web` ... : 각 플랫폼의 "호스트" 역할을 하는 네이티브 프로젝트입니다. Flutter 코드는最终적으로 이 네이티브 프로젝트를 통해 컴파일되고 실행됩니다. 앱 아이콘 변경, 스플래시 화면 설정, 푸시 알림이나 위치 정보 같은 플랫폼별 권한 설정 등은 이 디렉터리 내의 파일(예: Android의 `AndroidManifest.xml`, `build.gradle`, iOS의 `Info.plist`, `Podfile`)을 직접 수정하여 처리해야 합니다.
  • `lib`: 이 디렉터리가 Flutter 개발의 중심입니다. 앱의 모든 Dart 코드는 이곳에 위치해야 합니다. 프로젝트 규모가 커지면 `lib` 내부에 `screens`, `widgets`, `models`, `services` 와 같은 하위 디렉터리를 만들어 코드를 체계적으로 관리하는 것이 좋습니다. `lib/main.dart` 파일은 앱이 시작될 때 가장 먼저 실행되는 코드를 담고 있습니다.
  • `test`: 단위 테스트(Unit Test), 위젯 테스트(Widget Test), 통합 테스트(Integration Test) 코드를 작성하는 공간입니다. 잘 작성된 테스트는 앱의 안정성을 보장하는 데 필수적입니다.
  • `pubspec.yaml`: 프로젝트의 "설명서"와 같은 매우 중요한 파일입니다.
    • `name`, `description`, `version`: 앱의 이름, 설명, 버전 정보.
    • `environment`: 호환되는 Dart SDK 버전 범위를 지정합니다.
    • `dependencies`: 앱의 실행에 필요한 라이브러리(패키지) 목록입니다.
    • `dev_dependencies`: 테스트 코드나 빌드 스크립트 등 개발 과정에서만 필요한 라이브러리 목록입니다.
    • `flutter`: Flutter와 관련된 설정을 합니다. `assets` 섹션에 이미지나 폰트 파일 경로를 등록하여 앱에서 사용할 수 있도록 합니다.
  • `analysis_options.yaml`: Dart 코드 분석기의 규칙을 설정하는 파일입니다. 이 파일을 통해 팀의 코딩 스타일을 통일하고, 잠재적인 오류를 사전에 방지하는 린트(lint) 규칙을 적용할 수 있습니다.

4. 개발 워크플로우를 지배하는 필수 명령어

프로젝트 구조를 이해했다면, 이제 Flutter CLI의 다양한 명령어를 활용하여 실제 개발 워크플로우를 진행할 차례입니다. 이 명령어들은 개발, 테스트, 빌드 등 각 단계에서 개발자의 작업을 효율적으로 만들어줍니다.

> 개발 및 디버깅 (`flutter run`)

flutter run은 개발 과정에서 가장 빈번하게 사용되는 명령어입니다. 이 명령어는 앱을 컴파일하여 연결된 기기나 에뮬레이터에 설치하고 실행합니다.

flutter run

실행 후 터미널은 앱과 연결된 상태를 유지하며, 코드 변경을 감지하여 핫 리로드와 핫 리스타트를 수행할 수 있게 해줍니다.

  • 핫 리로드 (Hot Reload): 터미널에서 `r` 키를 누르면 실행됩니다. 앱의 상태를 유지한 채로 UI 코드 변경 사항만 빠르게 적용합니다. UI 레이아웃을 수정하거나 텍스트를 변경하는 등의 작업에 이상적입니다.
  • 핫 리스타트 (Hot Restart): 터미널에서 `Shift + R` 키를 누르면 실행됩니다. 앱의 상태는 초기화되지만, 앱 전체를 다시 컴파일하는 것보다는 훨씬 빠르게 앱을 재시작합니다. 상태 변수 초기화나 앱의 로직을 크게 변경했을 때 유용합니다.

flutter run 명령어는 다양한 플래그와 함께 사용하여 더 정교한 제어가 가능합니다.

  • 기기 선택 (`-d`): 여러 기기가 연결된 경우, 특정 기기에서 앱을 실행합니다. `flutter devices` 명령어로 사용 가능한 기기 ID 목록을 확인할 수 있습니다.
    flutter run -d chrome  # Chrome 브라우저에서 웹 앱으로 실행
    flutter run -d 'iPhone 14 Pro' # 특정 시뮬레이터에서 실행
        
  • 빌드 모드 선택:
    • `--debug` (기본값): 디버깅 정보와 핫 리로드를 포함한 개발용 모드입니다.
    • `--profile`: 앱의 성능을 측정하고 분석하기 위한 모드입니다. 핫 리로드는 비활성화되지만, DevTools와 같은 성능 분석 도구를 사용할 수 있습니다.
    • `--release`: 앱 스토어에 배포하기 위한 최종 최적화 모드입니다. 가장 빠른 성능을 보이지만, 디버깅 정보는 모두 제거됩니다.
    flutter run --profile
        
  • 빌드 플레이버 (`--flavor`): 개발, 스테이징, 프로덕션 등 환경별로 다른 설정(API 엔드포인트, 앱 이름 등)을 가진 앱을 빌드할 때 사용됩니다.
    flutter run --flavor development
        

> 의존성 관리 (`flutter pub`)

flutter pub 명령어 그룹은 `pubspec.yaml` 파일과 관련된 모든 의존성 관리 작업을 처리합니다.

  • 의존성 추가:
    flutter pub add http  # 'http' 패키지를 dependencies에 추가
    flutter pub add --dev test # 'test' 패키지를 dev_dependencies에 추가
        
  • 의존성 설치: `pubspec.yaml` 파일에 명시된 모든 패키지를 다운로드하고 프로젝트에 적용합니다. Git에서 프로젝트를 클론한 후 가장 먼저 실행해야 할 명령어입니다.
    flutter pub get
        
  • 의존성 업그레이드: `pubspec.yaml`에 정의된 버전 제약 조건 내에서 패키지들을 최신 버전으로 업데이트합니다.
    flutter pub upgrade
        

> 코드 품질 및 테스트

  • 코드 분석 (`flutter analyze`): Dart 코드 분석기를 실행하여 코드에서 잠재적인 오류, 버그, 스타일 문제를 찾아냅니다. CI/CD 파이프라인에 통합하여 코드 품질을 일관되게 유지하는 데 매우 유용합니다.
    flutter analyze
        
  • 코드 포맷팅 (`flutter format`): 프로젝트 내의 모든 Dart 파일을 공식 Dart 스타일 가이드에 맞게 자동으로 포맷팅합니다.
    flutter format .
        
  • 테스트 실행 (`flutter test`): `test/` 디렉터리에 있는 모든 테스트 코드를 실행합니다. 특정 파일만 테스트할 수도 있습니다.
    flutter test
    flutter test test/my_widget_test.dart
        

> 빌드 및 배포 (`flutter build`)

개발이 완료된 앱을 스토어에 배포하기 위한 최종 패키지를 생성합니다.

  • Android:
    flutter build apk --release       # APK 파일 생성
    flutter build appbundle --release # Google Play 스토어 권장 형식인 AAB 파일 생성
        
  • iOS (macOS에서만 가능):
    flutter build ipa --release
        
    이후 Xcode를 사용하여 App Store Connect에 업로드합니다.
  • Web:
    flutter build web
        
    `build/web` 디렉터리에 생성된 파일을 웹 서버에 배포합니다.

> 기타 유용한 명령어

  • 프로젝트 정리 (`flutter clean`): 이전 빌드에서 생성된 임시 파일과 빌드 아티팩트를 모두 삭제합니다. 의존성 문제나 빌드 오류가 발생했을 때 해결책이 되기도 합니다.
    flutter clean
        
  • SDK 관리:
    flutter channel             # 현재 사용 중인 채널(stable, beta, dev, master) 확인
    flutter channel stable      # 안정 버전 채널로 변경
    flutter upgrade             # 현재 채널의 최신 버전으로 Flutter SDK 업그레이드
        

5. 실전 예제: CLI로 만드는 간단한 할 일 목록 앱

지금까지 배운 CLI 명령어들을 활용하여 간단한 할 일 목록(To-Do) 앱을 만들어 보겠습니다. 이 예제는 프로젝트 생성부터 의존성 추가, 코드 작성, 실행까지 실제 개발 워크플로우를 경험하게 해줄 것입니다.

1단계: 프로젝트 생성

먼저, 적절한 조직 이름과 함께 새로운 Flutter 프로젝트를 생성합니다.

flutter create --org com.example todo_app
cd todo_app

2단계: `main.dart` 파일 수정

`lib/main.dart` 파일의 내용을 모두 지우고, 다음 코드로 대체합니다. 이 코드는 할 일을 추가하고, 완료 상태를 토글할 수 있는 기본적인 UI와 로직을 포함하고 있습니다.


import 'package:flutter/material.dart';

// To-Do 항목을 위한 데이터 모델
class TodoItem {
  String title;
  bool isDone;

  TodoItem({required this.title, this.isDone = false});
}

void main() {
  runApp(const TodoApp());
}

class TodoApp extends StatelessWidget {
  const TodoApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Todo App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: const TodoListScreen(),
    );
  }
}

class TodoListScreen extends StatefulWidget {
  const TodoListScreen({super.key});

  @override
  State<TodoListScreen> createState() => _TodoListScreenState();
}

class _TodoListScreenState extends State<TodoListScreen> {
  final List<TodoItem> _todoItems = [];
  final TextEditingController _textFieldController = TextEditingController();

  void _addTodoItem(String title) {
    if (title.isNotEmpty) {
      setState(() {
        _todoItems.add(TodoItem(title: title));
      });
      _textFieldController.clear();
    }
  }

  void _toggleTodoItem(int index) {
    setState(() {
      _todoItems[index].isDone = !_todoItems[index].isDone;
    });
  }

  Widget _buildTodoList() {
    return ListView.builder(
      itemCount: _todoItems.length,
      itemBuilder: (context, index) {
        final todo = _todoItems[index];
        return ListTile(
          leading: Checkbox(
            value: todo.isDone,
            onChanged: (value) => _toggleTodoItem(index),
          ),
          title: Text(
            todo.title,
            style: TextStyle(
              decoration: todo.isDone ? TextDecoration.lineThrough : null,
            ),
          ),
        );
      },
    );
  }

  Future<void> _displayDialog() async {
    return showDialog<void>(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: const Text('Add a new todo item'),
          content: TextField(
            controller: _textFieldController,
            decoration: const InputDecoration(hintText: 'Type your new todo'),
            autofocus: true,
          ),
          actions: <Widget>[
            TextButton(
              child: const Text('Cancel'),
              onPressed: () {
                Navigator.of(context).pop();
              },
            ),
            TextButton(
              child: const Text('Add'),
              onPressed: () {
                _addTodoItem(_textFieldController.text);
                Navigator.of(context).pop();
              },
            ),
          ],
        );
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Todo List'),
      ),
      body: _buildTodoList(),
      floatingActionButton: FloatingActionButton(
        onPressed: _displayDialog,
        tooltip: 'Add Item',
        child: const Icon(Icons.add),
      ),
    );
  }
}

3단계: 앱 실행 및 테스트

이제 터미널에서 `flutter run` 명령어를 실행하여 앱을 에뮬레이터나 실제 기기에서 실행합니다.

flutter run

앱이 실행되면, 화면 오른쪽 하단의 '+' 버튼을 눌러 새로운 할 일을 추가하는 다이얼로그를 열 수 있습니다. 할 일을 입력하고 'Add'를 누르면 목록에 추가됩니다. 각 항목 앞의 체크박스를 눌러 완료 상태를 변경할 수 있습니다.

이 간단한 예제를 통해 Flutter CLI가 어떻게 개발 워크플로우의 각 단계를 지원하는지 확인할 수 있습니다. 복잡한 앱을 개발할 때도 이러한 기본적인 흐름은 동일하게 적용됩니다.

6. 문제 해결 가이드 및 고급 활용 팁

Flutter CLI를 사용하다 보면 다양한 문제 상황에 직면할 수 있습니다. 이 장에서는 자주 발생하는 문제들의 해결 방법과 Flutter CLI를 더욱 효과적으로 활용하기 위한 몇 가지 팁을 소개합니다.

> 자주 묻는 질문(FAQ) 및 문제 해결

Q1: 터미널에서 `flutter` 명령어를 찾을 수 없다고 나옵니다.

A1: 이 문제는 대부분 환경 변수 설정이 잘못된 경우에 발생합니다.

  1. Flutter SDK의 `bin` 폴더 경로가 시스템의 `Path` 환경 변수에 정확하게 추가되었는지 다시 확인하세요.
  2. 경로에 오타가 없는지, 폴더 위치가 올바른지 점검합니다.
  3. 환경 변수를 수정한 후에는 반드시 터미널(명령 프롬프트)을 새로 열어야 변경 사항이 적용됩니다.

Q2: `flutter run` 실행 시 Gradle 관련 오류가 발생합니다. (Android)

A2: Gradle은 Android 빌드 시스템으로, 종종 캐시나 버전 문제로 오류를 일으킬 수 있습니다. 다음 단계를 시도해 보세요.

  1. `flutter clean` 명령어를 실행하여 이전 빌드 캐시를 삭제합니다.
  2. 프로젝트의 `android` 폴더로 이동하여 `./gradlew clean` (macOS/Linux) 또는 `gradlew.bat clean` (Windows)를 직접 실행합니다.
  3. Android Studio에서 프로젝트의 `android` 폴더를 열고, Gradle 동기화를 시도하여 필요한 버전의 Gradle을 자동으로 다운로드하게 할 수 있습니다.
  4. `android/gradle/wrapper/gradle-wrapper.properties` 파일에서 `distributionUrl`에 명시된 Gradle 버전이 현재 프로젝트와 호환되는지 확인합니다.

Q3: `pod install` 관련 오류가 발생합니다. (iOS)

A3: CocoaPods는 iOS의 의존성 관리 도구입니다. 이와 관련된 문제는 보통 다음 방법으로 해결할 수 있습니다.

  1. 프로젝트의 `ios` 디렉터리에서 `rm -rf Pods Podfile.lock` 명령어로 기존 Pods 관련 파일들을 삭제합니다.
  2. `pod repo update`를 실행하여 로컬 CocoaPods 스펙 저장소를 최신 상태로 업데이트합니다.
  3. 다시 `pod install` 또는 `flutter run`을 실행하여 의존성을 새로 설치합니다.

Q4: `pubspec.yaml` 파일에 패키지를 추가했는데 코드에서 import가 안 됩니다.

A4: `pubspec.yaml` 파일을 수정한 후에는 반드시 의존성을 프로젝트에 설치하는 과정이 필요합니다.

  1. 터미널에서 `flutter pub get` 명령어를 실행했는지 확인하세요.
  2. IDE(VS Code, Android Studio)를 사용 중이라면, `pubspec.yaml` 파일을 저장할 때 자동으로 `pub get`이 실행되도록 설정되어 있는지 확인합니다.
  3. 그래도 문제가 해결되지 않으면, IDE를 완전히 재시작하여 캐시를 비우고 다시 시도해 보세요.

> 생산성 향상을 위한 고급 팁

  • IDE 터미널 활용: VS Code나 Android Studio에 내장된 터미널을 사용하면 코드 에디터와 커맨드 라인 사이를 빠르고 편리하게 오갈 수 있어 작업 효율이 높아집니다.
  • 명령어 별칭(Alias) 사용: 자주 사용하는 긴 명령어는 셸의 별칭 기능으로 단축하여 사용할 수 있습니다. 예를 들어, `.zshrc`나 `.bashrc` 파일에 다음을 추가하면 `f`만 입력해도 `flutter`가 실행됩니다.
    alias f='flutter'
    alias fr='flutter run'
    alias fpg='flutter pub get'
        
  • CI/CD 파이프라인 연동: GitHub Actions, Jenkins, Codemagic과 같은 CI/CD(지속적 통합/지속적 배포) 도구에 Flutter CLI 명령어들을 스크립트로 작성하여 테스트, 분석, 빌드, 배포 과정을 자동화할 수 있습니다. 이는 팀의 생산성을 극대화하고 휴먼 에러를 줄이는 데 필수적입니다.

결론적으로, Flutter CLI는 Flutter 개발 경험의 근간을 이루는 강력하고 필수적인 도구입니다. 이 글에서 다룬 내용들을 바탕으로 Flutter CLI의 다양한 기능들을 적극적으로 탐색하고 활용한다면, 여러분의 앱 개발 워크플로우는 더욱 빠르고, 안정적이며, 효율적으로 발전할 것입니다.


0 개의 댓글:

Post a Comment