의존성 문제
“의존성(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이 어떻게 설계되었는지 알아봅시다.
네비게이션
- 이전: PHP 언어
- 다음: 프레임워크
- 상위: 01.Start 목차
핵심 요약
의존성은 내 코드가 작동하기 위해 필요한 다른 코드입니다.
- Composer는 PHP의 패키지 매니저
- composer.json으로 의존성 선언
- composer.lock으로 정확한 버전 고정
- Autoloading으로 클래스 자동 로딩
- Laravel은 Composer 없이 불가능
다음 장에서는 프레임워크의 개념을 배웁니다!
| 마지막 업데이트: 2025-11-18 | Laravel 12 & PHP 8.4 기준 |