# 패키지 의존성 관리

#### 패키지 의존성의 개념

Dart에서 패키지 의존성 관리는 프로젝트가 다른 라이브러리나 패키지에 의존하는 경우에 필요한 작업이다. Dart 프로젝트는 여러 개의 외부 패키지를 필요로 할 수 있으며, 이 패키지들은 프로젝트의 동작에 중요한 역할을 한다. Dart 생태계에서는 `pubspec.yaml` 파일을 통해 이러한 패키지들을 관리한다.

`pubspec.yaml` 파일은 프로젝트의 메타데이터와 의존성을 정의하는 핵심 파일로, 여기에는 다음과 같은 정보가 포함된다:

* 프로젝트 이름, 설명
* 프로젝트가 의존하는 패키지 목록과 그 버전
* 추가적으로 사용할 수 있는 dev\_dependencies

패키지 의존성 관리를 효율적으로 하기 위해서는 버전 관리를 잘 설정하고, 각 패키지가 서로 충돌하지 않도록 해야 한다.

#### 버전 규칙과 호환성

Dart는 패키지 의존성 관리에서 **세맨틱 버전 관리**(Semantic Versioning, SemVer)를 따른다. SemVer은 패키지의 버전을 세 부분으로 나누어 관리한다:

$$
\text{Version} = \text{Major}.\text{Minor}.\text{Patch}
$$

* **Major**: 주 버전은 API가 불안정해질 때 증가한다.
* **Minor**: 부 버전은 새로운 기능이 추가될 때 증가하지만, 이전 버전과의 호환성은 유지된다.
* **Patch**: 패치 버전은 버그 수정 등 작은 변경 사항일 때 증가한다.

Dart에서 버전 번호 앞에 다음과 같은 제약을 사용할 수 있다:

* **^**: 호환 가능한 버전 범위를 나타낸다. 예를 들어, `^1.2.3`은 `>=1.2.3 <2.0.0` 범위 내에서의 패키지 버전을 허용한다.
* **>=**: 해당 버전 이상을 허용한다.
* **<**: 해당 버전 미만을 허용한다.

#### pubspec.yaml 파일 예시

다음은 `pubspec.yaml` 파일의 예시이다. 여기서 프로젝트가 특정 패키지들에 의존하고 있음을 확인할 수 있다:

```yaml
name: my_project
description: A sample Dart project
version: 1.0.0
environment:
  sdk: ">=2.12.0 <3.0.0"

dependencies:
  http: ^0.13.0
  json_annotation: ^4.0.0

dev_dependencies:
  build_runner: ^2.0.0
  json_serializable: ^4.1.0
```

위 파일에서 `dependencies` 항목은 프로젝트에서 사용되는 패키지들의 목록을 나타내며, `dev_dependencies`는 개발 환경에서만 필요한 패키지를 정의한다.

이와 같은 구조는 패키지 간 의존성을 명확하게 정의하며, 프로젝트를 빌드하거나 배포할 때 일관성 있는 환경을 유지하는 데 도움을 준다. 특히, `build_runner`와 같은 패키지는 코드 생성이나 빌드 프로세스를 자동화하는 데 사용되며, 일반적으로 개발 단계에서만 필요하다.

#### 의존성 충돌 해결

프로젝트가 여러 패키지를 의존하게 될 때, 동일한 패키지의 서로 다른 버전이 요구될 수 있다. 이를 **의존성 충돌**이라고 하며, Dart에서는 이러한 충돌을 해결하기 위해 **pub resolve** 메커니즘을 사용한다. `pub get` 명령을 실행하면 Dart는 `pubspec.lock` 파일을 생성하여 실제로 사용되는 패키지 버전들을 고정시킨다.

Dart에서 의존성 충돌을 해결하는 방법에는 두 가지가 있다:

1. **버전 제약을 완화**: 의존하는 패키지의 버전 범위를 넓게 설정하여 충돌을 피할 수 있다. 예를 들어, 특정 패키지의 버전을 `^1.0.0`에서 `^1.0.0 <=1.2.3`으로 확장할 수 있다.
2. **패키지 업데이트**: 호환되지 않는 버전의 패키지를 최신 버전으로 업데이트하여 충돌을 해결할 수 있다. 이를 통해 최신 API를 사용하면서 호환성을 유지할 수 있다.

#### pubspec.lock 파일

`pubspec.lock` 파일은 `pubspec.yaml` 파일의 의존성을 기반으로 Dart 패키지 관리자(pub)가 해결한 실제 패키지 버전을 기록하는 파일이다. 이 파일은 프로젝트의 의존성 트리를 고정된 상태로 유지하여, 동일한 패키지 버전을 사용하도록 보장한다. `pubspec.lock` 파일이 없다면, 다른 개발자가 동일한 코드를 실행할 때 패키지 버전이 달라질 수 있어, 예기치 않은 버그나 비호환 문제가 발생할 수 있다.

`pubspec.lock` 파일의 주요 목적은 다음과 같다:

* 프로젝트의 의존성을 특정 버전으로 고정하여 환경 간의 일관성을 유지
* 패키지 버전 충돌을 방지
* 동일한 의존성을 갖는 다른 개발자가 협력할 때의 재현 가능성 확보

#### `pub get`과 `pub upgrade` 명령어

Dart에서 패키지 의존성을 관리할 때 자주 사용하는 두 가지 명령어가 있다: `pub get`과 `pub upgrade`.

**pub get**

`pub get` 명령어는 `pubspec.yaml` 파일을 기반으로 필요한 패키지를 설치하고, 그 패키지들의 정확한 버전 정보를 `pubspec.lock` 파일에 기록한다. 이 명령어는 일반적으로 프로젝트를 처음 시작할 때 또는 새로운 패키지를 추가한 후 실행한다. `pub get`은 이미 `pubspec.lock`에 기록된 버전 정보를 우선적으로 사용하여, 의존성 버전이 변하지 않도록 보장한다.

```bash
$ dart pub get
```

**pub upgrade**

`pub upgrade` 명령어는 패키지들의 버전을 최신으로 업데이트한다. 만약 특정 패키지가 새로운 기능이나 버그 수정을 포함한 버전을 출시했다면, 이 명령어를 통해 의존성 목록을 최신 상태로 유지할 수 있다. `pub upgrade`는 기존의 `pubspec.lock` 파일을 무시하고, 가능한 최신 버전으로 패키지들을 다시 해결한다.

```bash
$ dart pub upgrade
```

#### 버전 충돌 해결 시나리오

다음과 같은 예시를 통해 버전 충돌을 해결하는 과정을 살펴보자. 만약 두 개의 패키지가 서로 다른 버전의 `http` 패키지에 의존하고 있다면, Dart 패키지 관리자는 이를 해결하려고 시도할 것이다.

* **패키지 A**는 `http: ^0.12.0` 버전을 요구
* **패키지 B**는 `http: ^0.13.0` 버전을 요구

이러한 상황에서는, 두 패키지가 모두 만족할 수 있는 호환 가능한 버전을 찾는 것이 중요하다. Dart의 의존성 해결 알고리즘은 가능한 경우 두 버전을 모두 충족할 수 있는 최적의 버전을 찾아낸다. 만약 호환 가능한 버전이 존재하지 않으면, 에러가 발생하고 개발자는 의존성을 수정해야 한다.

#### dev\_dependencies와 의존성 분리

프로젝트의 의존성은 크게 두 가지로 분류된다:

1. **dependencies**: 애플리케이션 코드에서 실제로 사용되는 패키지들
2. **dev\_dependencies**: 테스트나 빌드 도구와 같은 개발 단계에서만 사용되는 패키지들

예를 들어, `test` 패키지와 같은 도구는 애플리케이션이 배포될 때는 필요하지 않지만, 개발 중에는 필수적이다. 이러한 개발 의존성은 `dev_dependencies` 섹션에 따로 정의할 수 있다. 이렇게 분리함으로써, 애플리케이션 배포 시 불필요한 패키지들을 포함하지 않도록 최적화할 수 있다.

#### `pubspec.yaml`의 의존성 제약 설정

`pubspec.yaml` 파일에서 의존성을 설정할 때, Dart에서는 다양한 방식으로 의존성을 제어할 수 있다. Dart 프로젝트는 외부 패키지뿐만 아니라 로컬 패키지나 Git에서 가져온 패키지, 그리고 패키지 아카이브에서 직접 설치한 패키지를 사용할 수 있다. 각 의존성은 아래와 같은 방식으로 정의된다:

**패키지 의존성**

가장 일반적인 방법은 pub.dev에서 제공하는 패키지를 `dependencies`에 추가하는 것이다. 패키지는 버전 제약과 함께 추가되며, pub가 이를 자동으로 다운로드하고 관리한다.

```yaml
dependencies:
  http: ^0.13.0
  json_annotation: ^4.0.0
```

**Git 의존성**

특정 패키지를 Git 저장소에서 직접 가져오고 싶을 때는, Git URL을 사용하여 의존성을 추가할 수 있다. Git 의존성은 저장소 URL뿐만 아니라 특정 브랜치나 태그, 커밋 해시로도 지정할 수 있다.

```yaml
dependencies:
  my_package:
    git:
      url: https://github.com/username/my_package.git
      ref: master
```

**로컬 패키지 의존성**

로컬 디렉토리에서 패키지를 의존성으로 추가할 수도 있다. 프로젝트 내부의 다른 모듈을 테스트하거나 공유할 때 사용된다.

```yaml
dependencies:
  my_local_package:
    path: ../my_local_package
```

#### 의존성 관리 자동화 도구

의존성 관리는 Dart 생태계에서 자동화된 도구로 더욱 효율적으로 처리할 수 있다. 이러한 도구는 패키지 관리뿐만 아니라 코드 빌드 및 테스트에도 도움이 된다.

* **Pub**: Dart 패키지 관리 도구로, `pub get`과 `pub upgrade` 같은 명령을 통해 의존성을 쉽게 설치하고 업데이트할 수 있다.
* **Build Runner**: Dart와 Flutter 프로젝트에서 코드 생성과 빌드를 자동화하는 도구이다. 주로 코드 제너레이션과 같은 작업에 사용되며, `build_runner` 패키지를 통해 실행된다.

```bash
$ dart run build_runner build
```

이러한 도구를 사용하면 수작업으로 패키지를 설치하거나 관리할 필요 없이, 프로젝트에서 사용하는 패키지들을 자동으로 관리할 수 있다. 이를 통해 개발자는 더 빠르고 일관된 개발 환경을 유지할 수 있다.

#### Pub 캐시 관리

Pub는 패키지를 설치할 때 캐시 디렉토리를 사용하여 동일한 패키지를 여러 번 다운로드하지 않도록 한다. 기본적으로 `~/.pub-cache` 경로에 패키지가 저장되며, 이는 동일한 패키지를 사용하는 여러 프로젝트가 있을 때 다운로드 시간을 절약해 준다. 때로는 캐시된 패키지에 문제가 발생하거나, 버전 충돌이 있을 경우 캐시를 지워야 할 수도 있다.

캐시를 삭제하는 명령어는 다음과 같다:

```bash
$ dart pub cache clean
```

이 명령어는 모든 캐시된 패키지를 삭제하며, 이후 프로젝트에서 다시 필요한 패키지를 다운로드하게 된다.

#### 의존성 감사와 보안

의존성 관리에서 중요한 부분 중 하나는 외부 패키지의 보안이다. Dart 생태계에서는 패키지의 안전성을 보장하기 위해 몇 가지 툴을 제공한다. 예를 들어, 패키지가 신뢰할 수 없는 소스에서 다운로드된 경우, 경고가 발생하며 개발자가 이를 확인할 수 있도록 한다.

또한 `pub.dev`에서는 각 패키지에 대해 **Dart 패키지 스코어**를 제공하여, 패키지의 품질과 유지 관리 상태를 점검할 수 있다. 이러한 점수는 패키지의 최신 버전 여부, 문서화 수준, 그리고 코드의 품질을 기반으로 평가된다.

보안적인 관점에서, 패키지를 사용할 때는 반드시 최신 버전으로 유지하고, 가능하면 의존성을 주기적으로 감사하여 문제를 예방하는 것이 중요하다.
