의존성 문제

“의존성(Dependency)? 왜 갑자기 어려운 용어가 나오나요?”

걱정하지 마세요. 이 개념은 생각보다 훨씬 간단하고, 현대 웹 개발에서는 절대 피할 수 없는 중요한 주제랍니다.

이 페이지에서는 의존성이 무엇인지, 왜 문제가 되는지, 그리고 어떻게 해결하는지 쉽게 알아보겠습니다.

의존성이란?

실생활 비유로 시작해볼까요?

케이크를 만든다고 상상해보세요.

케이크를 만들려면:
- 밀가루
- 설탕
- 계란
- 버터
- 베이킹파우더

이 모든 재료가 필요합니다.

케이크는 이 재료들에 “의존”합니다.

만약 밀가루가 없으면? 케이크를 만들 수 없죠. 계란이 상했다면? 역시 문제입니다.

프로그래밍에서도 마찬가지예요!

프로그래밍에서의 의존성

// 당신의 코드
class BlogPost {
    public function save() {
        // 데이터베이스에 저장하고 싶어요
        // 하지만... 데이터베이스 연결 코드가 필요해요!
    }
}

BlogPost 클래스는 데이터베이스 라이브러리에 의존합니다.

의존성 = 내 코드가 작동하기 위해 필요한 다른 코드나 라이브러리

왜 의존성이 생기나요?

모든 걸 혼자 만들 순 없어요

만약 의존성 없이 모든 걸 직접 만든다면:

// 이메일 보내는 기능을 직접 구현한다면...

class EmailSender {
    // SMTP 프로토콜 구현 (수천 줄)
    private function connectToSmtpServer() {
        // TCP 소켓 연결
        // SSL/TLS 핸드셰이크
        // SMTP 명령어 처리
        // ...
    }

    // MIME 인코딩 구현
    private function encodeMimeMessage() {
        // multipart/alternative 처리
        // Base64 인코딩
        // Quoted-printable 인코딩
        // ...
    }

    // 스팸 필터 우회 로직
    private function handleSpamScore() {
        // SPF 레코드 확인
        // DKIM 서명
        // ...
    }

    // ... 수천 줄 더...
}

vs 라이브러리 사용:

// 이미 잘 만들어진 라이브러리 사용
use PHPMailer\PHPMailer\PHPMailer;

$mail = new PHPMailer();
$mail->setFrom('from@example.com');
$mail->addAddress('to@example.com');
$mail->Subject = '제목';
$mail->Body = '내용';
$mail->send();

// 단 6줄!

거인의 어깨 위에 서기

Sir Isaac Newton이 말했죠:

“내가 더 멀리 볼 수 있었다면, 그것은 거인들의 어깨 위에 서 있었기 때문이다.”

프로그래밍도 마찬가지예요. 다른 훌륭한 개발자들이 만든 코드(라이브러리)를 사용함으로써, 우리는 더 빠르게, 더 좋은 프로그램을 만들 수 있습니다.

의존성의 문제점

문제 1: 의존성의 의존성 (Dependency Hell)

내 프로젝트
    ↓ 필요
Package A (이메일 라이브러리)
    ↓ 필요
Package B (문자열 처리, v2.0)

내 프로젝트
    ↓ 또 필요
Package C (이미지 처리)
    ↓ 필요
Package B (문자열 처리, v1.5) ← 어?! 버전이 다르네?

충돌 발생!

Package A는 Package B의 2.0 버전이 필요한데, Package C는 1.5 버전이 필요해요. 어떻게 해야 할까요?

문제 2: 버전 관리

// 6개월 전
// 내 컴퓨터에서는 잘 작동함
use CoolLibrary\Version1\Formatter;
$formatter = new Formatter();
$formatter->format($data); // 작동!

// 6개월 후
// 새 컴퓨터에 설치했더니...
use CoolLibrary\Version2\Formatter;
$formatter = new Formatter();
$formatter->format($data); // 에러! format() 메서드가 formatData()로 바뀜!

문제 3: 누락된 의존성

동료가 만든 프로젝트를 받았어요:

// colleague.php
use SomePackage\Util;

$util = new Util();
$util->doSomething();

실행해봤더니:

Fatal error: Class 'SomePackage\Util' not found

“어? 왜 안 되지? 동료 컴퓨터에서는 됐는데?”

동료는 SomePackage를 설치했지만, 여러분은 설치하지 않았거든요!

문제 4: 수동 관리의 고통

10개의 라이브러리를 사용한다고 가정:

1. Library A - 직접 다운로드
2. Library B - 직접 다운로드
   - 어? Library B가 Library X가 필요하네?
3. Library X - 직접 다운로드
   - Library X가 Library Y와 Z가 필요...
4. Library Y - 직접 다운로드
5. Library Z - 직접 다운로드
6. Library C - 직접 다운로드
   ...

😱 도저히 관리할 수 없어!

해결책: 패키지 매니저

패키지 매니저란?

패키지 매니저 = 의존성을 자동으로 관리해주는 도구

다른 언어의 패키지 매니저:

  • JavaScript: npm, yarn
  • Python: pip
  • Ruby: gem
  • PHP: Composer ← 우리가 사용할 것!

Composer: PHP의 구원자

Composer는 2012년에 등장해서 PHP 개발을 완전히 바꿔놓았습니다.

Composer가 하는 일:

1. 필요한 패키지 다운로드
2. 의존성의 의존성도 자동으로 다운로드
3. 버전 충돌 자동 해결
4. 모든 걸 자동으로 로드
5. 업데이트 관리

Composer 실전

Composer 설치

Mac / Linux:

curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer

Windows:

https://getcomposer.org/Composer-Setup.exe 다운로드

확인:

composer --version
# Composer version 2.7.0

composer.json - 의존성 설명서

모든 Composer 프로젝트의 핵심은 composer.json 파일입니다.

{
    "name": "my-company/my-project",
    "description": "내 멋진 프로젝트",
    "type": "project",
    "require": {
        "php": "^8.4",
        "monolog/monolog": "^3.0"
    },
    "require-dev": {
        "phpunit/phpunit": "^10.0"
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    }
}

각 부분 설명:

"require": {
    "php": "^8.4",              // PHP 8.4 이상 필요
    "monolog/monolog": "^3.0"   // Monolog 3.x 버전 필요
}

^3.0의 의미:

  • 3.0 이상, 4.0 미만
  • 3.1, 3.2, 3.9.99 모두 OK
  • 4.0은 NO (큰 변경이 있을 수 있음)

패키지 설치

명령어:

composer require monolog/monolog

Composer가 하는 일:

1. Packagist.org에서 monolog/monolog 검색
   ↓
2. 최신 안정 버전 확인 (예: 3.5.0)
   ↓
3. monolog의 composer.json 확인
   ↓
4. monolog가 필요로 하는 패키지들 확인
   ↓
5. 모든 패키지와 의존성 다운로드
   ↓
6. vendor/ 폴더에 설치
   ↓
7. composer.lock 파일 생성/업데이트
   ↓
8. 완료!

결과:

my-project/
├── composer.json       (설정 파일)
├── composer.lock       (정확한 버전 정보)
└── vendor/             (모든 패키지)
    ├── monolog/
    │   └── monolog/
    ├── psr/
    │   └── log/        (monolog의 의존성)
    └── autoload.php    (자동 로딩)

composer.lock - 버전 고정

composer.json:

{
    "require": {
        "monolog/monolog": "^3.0"  // "3.x 아무거나"
    }
}

composer.lock:

{
    "packages": [
        {
            "name": "monolog/monolog",
            "version": "3.5.0",     // 정확한 버전!
            "source": {
                "url": "https://github.com/Seldaek/monolog.git",
                "reference": "c4e8a0d9a..."
            }
        }
    ]
}

왜 중요한가요?

시나리오 1: composer.lock 없이

당신 (1월): Monolog 3.5.0 설치 → 테스트 통과 ✅
동료 (3월): Monolog 3.6.0 설치 → 버그 발생 ❌

"내 컴퓨터에서는 되는데?!" 문제 발생

시나리오 2: composer.lock 있으면

당신 (1월): Monolog 3.5.0 설치 → composer.lock 생성
동료 (3월): composer install → composer.lock 읽음 → 3.5.0 설치

모두 같은 버전! ✅

실전 명령어

새 프로젝트 시작:

mkdir my-project
cd my-project
composer init  # 대화형으로 composer.json 생성

패키지 설치:

# 프로덕션 의존성
composer require guzzlehttp/guzzle

# 개발 의존성 (테스트, 디버깅 도구 등)
composer require --dev phpunit/phpunit

기존 프로젝트 클론:

git clone https://github.com/someone/project.git
cd project
composer install  # composer.lock에 따라 설치

업데이트:

# 모든 패키지 업데이트 (composer.json 범위 내)
composer update

# 특정 패키지만 업데이트
composer update monolog/monolog

# 업데이트 가능한 패키지 확인만
composer outdated

패키지 제거:

composer remove monolog/monolog

정보 확인:

# 설치된 패키지 목록
composer show

# 특정 패키지 정보
composer show monolog/monolog

# 의존성 트리
composer show -t

Autoloading - 자동 로딩

예전 방식 (Composer 없이)

// 지옥같은 include 지옥
require_once 'src/Models/User.php';
require_once 'src/Models/Post.php';
require_once 'src/Controllers/UserController.php';
require_once 'src/Controllers/PostController.php';
require_once 'src/Utils/Validator.php';
require_once 'src/Utils/Helper.php';
// ... 수십 개 더...

// 하나라도 빼먹으면? 에러!
// 순서가 틀리면? 에러!

Composer 방식 (PSR-4 Autoloading)

composer.json:

{
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    }
}

Autoload 생성:

composer dump-autoload

사용:

// 단 한 줄만 있으면 됨!
require __DIR__ . '/vendor/autoload.php';

// 이제 모든 클래스가 자동으로 로드됨
use App\Models\User;
use App\Controllers\UserController;

$user = new User();  // 자동으로 src/Models/User.php 로드!
$controller = new UserController();  // 자동으로 로드!

// include/require 필요 없음!

동작 원리:

new App\Models\User 호출
    ↓
Autoloader 작동
    ↓
"App\" → "src/" 매핑 확인
    ↓
App\Models\User → src/Models/User.php 변환
    ↓
파일 자동 include
    ↓
클래스 사용 가능!

실전 예제: 패키지 활용

예제 1: HTTP 요청 (Guzzle)

설치:

composer require guzzlehttp/guzzle

사용:

<?php
require 'vendor/autoload.php';

use GuzzleHttp\Client;

$client = new Client();

// API 호출이 이렇게 쉬워져요!
$response = $client->request('GET', 'https://api.github.com/users/laravel');
$data = json_decode($response->getBody(), true);

echo "Laravel has {$data['public_repos']} public repos!";

Guzzle 없이 직접 구현한다면:

// 수백 줄의 curl 코드...
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.github.com/users/laravel');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERAGENT, 'My App');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
// ... 더 많은 옵션들...
$response = curl_exec($ch);
// ... 에러 처리...
curl_close($ch);

예제 2: 로깅 (Monolog)

설치:

composer require monolog/monolog

사용:

<?php
require 'vendor/autoload.php';

use Monolog\Logger;
use Monolog\Handler\StreamHandler;

// 로거 생성
$log = new Logger('my-app');
$log->pushHandler(new StreamHandler('app.log', Logger::WARNING));

// 로그 기록
$log->warning('사용자 로그인 실패', ['user_id' => 123]);
$log->error('데이터베이스 연결 실패', ['error' => $e->getMessage()]);

// app.log 파일에 자동으로 기록됨!

예제 3: 날짜/시간 처리 (Carbon)

설치:

composer require nesbot/carbon

사용:

<?php
require 'vendor/autoload.php';

use Carbon\Carbon;

// 현재 시간
echo Carbon::now(); // 2025-11-18 14:30:00

// 상대적 시간 표현
echo Carbon::now()->subDays(5)->diffForHumans();
// "5일 전"

// 날짜 계산
$christmas = Carbon::createFromDate(2025, 12, 25);
echo "크리스마스까지 {$christmas->diffInDays()}일 남았어요!";

// 타임존 변환
echo Carbon::now('America/New_York')->toDateTimeString();

예제 4: 환경 변수 (vlucas/phpdotenv)

설치:

composer require vlucas/phpdotenv

.env 파일:

DB_HOST=localhost
DB_USER=root
DB_PASS=secret
API_KEY=abc123xyz

사용:

<?php
require 'vendor/autoload.php';

$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();

// 이제 환경 변수 사용 가능
$host = $_ENV['DB_HOST'];
$user = $_ENV['DB_USER'];
$pass = $_ENV['DB_PASS'];

// 민감한 정보를 코드에 넣지 않아도 됨!

Packagist.org - 패키지 저장소

PHP의 npm registry

Packagist (https://packagist.org)는 PHP 패키지들의 중앙 저장소입니다.

인기 패키지 Top 10 (2024년):

1. symfony/symfony       - Symfony 프레임워크 컴포넌트
2. laravel/framework     - Laravel 프레임워크
3. guzzlehttp/guzzle     - HTTP 클라이언트
4. monolog/monolog       - 로깅 라이브러리
5. phpunit/phpunit       - 테스트 프레임워크
6. doctrine/orm          - ORM
7. nesbot/carbon         - 날짜/시간 라이브러리
8. symfony/console       - CLI 도구 제작
9. vlucas/phpdotenv      - 환경 변수 관리
10. intervention/image   - 이미지 처리

패키지 검색:

composer search image
composer search --type=library image

패키지 정보:

composer show monolog/monolog

Laravel과 Composer

Laravel은 Composer 없이는 불가능

Laravel 설치:

composer create-project laravel/laravel my-blog

Laravel의 composer.json:

{
    "name": "laravel/laravel",
    "require": {
        "php": "^8.2",
        "laravel/framework": "^11.0",
        "laravel/tinker": "^2.9"
    },
    "require-dev": {
        "fakerphp/faker": "^1.23",
        "laravel/pint": "^1.13",
        "laravel/sail": "^1.26",
        "mockery/mockery": "^1.6",
        "nunomaduro/collision": "^8.0",
        "phpunit/phpunit": "^11.0",
        "spatie/laravel-ignition": "^2.4"
    }
}

Laravel이 사용하는 패키지들:

symfony/* (약 30개)
    - routing, http, console 등
    - Laravel은 Symfony 위에 구축됨!

league/*
    - flysystem (파일 시스템 추상화)
    - mime-type-detection

nesbot/carbon
    - 모든 날짜/시간 처리

monolog/monolog
    - 로깅

doctrine/inflector
    - 단수/복수형 변환 (user → users)

그 외 수십 개...

Laravel 패키지 추가:

# Debugbar (개발 도구)
composer require --dev barryvdh/laravel-debugbar

# Socialite (소셜 로그인)
composer require laravel/socialite

# Sanctum (API 인증)
composer require laravel/sanctum

버전 명세 이해하기

Semantic Versioning (유의적 버전)

버전 번호: 3.5.2

3   .   5   .   2
↑       ↑       ↑
|       |       패치 (Patch)
|       |       - 버그 수정
|       |       - 하위 호환성 유지
|       |
|       마이너 (Minor)
|       - 새 기능 추가
|       - 하위 호환성 유지
|
메이저 (Major)
- 큰 변경
- 하위 호환성 깨질 수 있음

Composer 버전 제약 문법

{
    "require": {
        "vendor/package": "1.0.0",      // 정확히 1.0.0
        "vendor/package": ">=1.0",      // 1.0 이상
        "vendor/package": ">=1.0 <2.0", // 1.0 이상, 2.0 미만
        "vendor/package": "1.0.*",      // 1.0.x (1.0.0, 1.0.1...)
        "vendor/package": "~1.2",       // >=1.2 <2.0
        "vendor/package": "^1.2.3",     // >=1.2.3 <2.0.0 (추천!)
    }
}

캐럿(^) vs 틸드(~):

// 캐럿 (^) - 추천!
"^1.2.3"  = ">=1.2.3 <2.0.0"  // 1.x.x 모두
"^0.3"    = ">=0.3.0 <0.4.0"  // 0.3.x만

// 틸드 (~)
"~1.2.3"  = ">=1.2.3 <1.3.0"  // 1.2.x만
"~1.2"    = ">=1.2.0 <2.0.0"  // 1.x.x 모두

실전 예시:

{
    "require": {
        "php": "^8.4",                  // PHP 8.4.x
        "laravel/framework": "^12.0",   // Laravel 12.x
        "guzzlehttp/guzzle": "^7.0",   // Guzzle 7.x
        "monolog/monolog": "^3.0"      // Monolog 3.x
    }
}

고급 Composer 기능

Scripts - 자동화

composer.json:

{
    "scripts": {
        "post-install-cmd": [
            "@php artisan key:generate --ansi"
        ],
        "test": [
            "@php artisan test"
        ],
        "format": [
            "./vendor/bin/pint"
        ]
    }
}

실행:

composer test    # 테스트 실행
composer format  # 코드 포매팅

Platform Packages

{
    "require": {
        "php": "^8.4",
        "ext-mbstring": "*",   // mbstring 확장 필요
        "ext-pdo": "*",        // PDO 확장 필요
        "ext-redis": "*"       // Redis 확장 필요
    }
}

Repositories - 비공개 패키지

{
    "repositories": [
        {
            "type": "vcs",
            "url": "https://github.com/mycompany/private-package"
        }
    ],
    "require": {
        "mycompany/private-package": "^1.0"
    }
}

Prestissimo/Hirak - 병렬 다운로드

# 더 빠른 설치 (병렬 다운로드)
composer global require hirak/prestissimo

일반적인 문제와 해결

문제 1: Memory Limit

# 에러
Fatal error: Allowed memory size of 1610612736 bytes exhausted

# 해결
php -d memory_limit=-1 /usr/local/bin/composer install

문제 2: 버전 충돌

# 에러
Problem 1
  - package-a requires package-b ^2.0 -> satisfiable by package-b[2.0]
  - package-c requires package-b ^1.0 -> satisfiable by package-b[1.0]

# 해결: 중재 버전 찾기 또는 패키지 교체
composer why package-b  # 왜 필요한지 확인
composer why-not package-b 2.0  # 왜 안 되는지 확인

문제 3: 캐시 문제

# 캐시 삭제
composer clear-cache

# 완전히 재설치
rm -rf vendor composer.lock
composer install

문제 4: Autoload 안 됨

# Autoload 재생성
composer dump-autoload

실전 워크플로우

새 프로젝트 시작

# 1. 프로젝트 생성
mkdir my-project
cd my-project

# 2. Composer 초기화
composer init

# 3. 패키지 추가
composer require monolog/monolog
composer require --dev phpunit/phpunit

# 4. Git 초기화
git init
echo "vendor/" >> .gitignore  # vendor는 커밋하지 않음!
echo ".env" >> .gitignore
git add .
git commit -m "Initial commit"

기존 프로젝트 클론

# 1. 클론
git clone https://github.com/user/project.git
cd project

# 2. 의존성 설치
composer install  # composer.lock 사용

# 3. 환경 설정
cp .env.example .env
php artisan key:generate  # Laravel의 경우

협업 시

# 팀원 A: 새 패키지 추가
composer require new-package/cool-lib
git add composer.json composer.lock
git commit -m "Add cool-lib package"
git push

# 팀원 B: 변경사항 받기
git pull
composer install  # 업데이트된 composer.lock 적용

Composer vs npm 비교

유사점

Composer          |  npm
------------------+------------------
composer.json     |  package.json
composer.lock     |  package-lock.json
vendor/           |  node_modules/
packagist.org     |  npmjs.com
composer install  |  npm install
composer require  |  npm install <pkg>
composer update   |  npm update

차이점

Composer (PHP):

  • 프로젝트별 설치가 기본
  • Global 설치는 드물게 사용
  • Autoloading 포함

npm (JavaScript):

  • Global 설치 자주 사용 (-g)
  • require() 또는 import 사용
  • devDependencies 구분 명확

다음 단계

의존성 관리에 대해 이해했으니, 이제 프레임워크가 무엇인지 알아볼 차례입니다.

“Composer로 패키지를 관리하는 건 알겠는데, Laravel은 왜 프레임워크라고 부르나요?”

좋은 질문이에요! 다음 장에서 프레임워크의 개념과 Laravel이 어떻게 설계되었는지 알아봅시다.


네비게이션


핵심 요약

의존성은 내 코드가 작동하기 위해 필요한 다른 코드입니다.

  • Composer는 PHP의 패키지 매니저
  • composer.json으로 의존성 선언
  • composer.lock으로 정확한 버전 고정
  • Autoloading으로 클래스 자동 로딩
  • Laravel은 Composer 없이 불가능

다음 장에서는 프레임워크의 개념을 배웁니다!


마지막 업데이트: 2025-11-18 Laravel 12 & PHP 8.4 기준