프레임워크

“프레임워크? 그게 라이브러리랑 뭐가 다른데요?”

아주 좋은 질문입니다! 많은 초보 개발자들이 헷갈려하는 부분이에요. 이 페이지에서는 프레임워크가 무엇인지, 왜 필요한지, 그리고 Laravel 프레임워크는 어떻게 구성되어 있는지 알아보겠습니다.

프레임워크란?

실생활 비유: 집 짓기

직접 짓기 (No Framework):

땅 고르기 → 기초 공사 → 골조 세우기 → 벽 쌓기
→ 지붕 올리기 → 배관 → 전기 → 인테리어

모든 것을 처음부터 스스로 결정하고 만들어야 합니다.
자유롭지만 어렵고 시간이 오래 걸립니다.

조립식 주택 (Framework):

이미 설계된 구조
 ↓
미리 만들어진 부품들
 ↓
정해진 방식대로 조립
 ↓
필요한 부분만 커스터마이징

빠르고 안전하지만, 정해진 규칙을 따라야 합니다.

프로그래밍에서의 프레임워크

프레임워크 = 애플리케이션의 뼈대(골격) + 규칙 + 도구

프레임워크는:
✅ 기본 구조를 제공합니다
✅ 정해진 규칙을 따르게 합니다
✅ 반복적인 작업을 자동화합니다
✅ 모범 사례를 강제합니다

프레임워크 vs 라이브러리

많이들 혼동하는 부분인데, 명확히 구분해볼게요.

라이브러리 (Library)

“당신이 라이브러리를 호출합니다”

// 라이브러리 예시: Guzzle (HTTP 클라이언트)
use GuzzleHttp\Client;

// 내가 원할 때 호출
$client = new Client();
$response = $client->get('https://api.example.com');

// 내가 제어권을 가짐
if ($response->getStatusCode() == 200) {
    // 내가 원하는 대로 처리
}

특징:

  • 필요할 때만 사용
  • 호출 주체: 개발자
  • 자유로운 구조
  • 부분적 사용 가능

프레임워크 (Framework)

“프레임워크가 당신의 코드를 호출합니다”

// 프레임워크 예시: Laravel
// 프레임워크가 정한 위치에 코드 작성
class UserController extends Controller
{
    // 프레임워크가 알아서 이 메서드를 호출함
    public function index()
    {
        return User::all();
    }
}

// routes/web.php에 등록
Route::get('/users', [UserController::class, 'index']);
// 프레임워크가 요청을 받아서 적절한 컨트롤러를 실행

특징:

  • 프레임워크의 규칙을 따름
  • 호출 주체: 프레임워크
  • 정해진 구조
  • 전체적 사용

제어의 역전 (Inversion of Control)

이게 핵심 차이입니다!

라이브러리:

개발자 → 라이브러리 호출
(내가 제어)

프레임워크:

프레임워크 → 개발자 코드 호출
(프레임워크가 제어)

실생활 비유:

라이브러리 = 도구 상자

필요한 도구(함수)를 꺼내서 사용
망치가 필요하면 망치를 꺼냄
톱이 필요하면 톱을 꺼냄

프레임워크 = 공장 라인

정해진 순서대로 작업
각 단계에서 할 일만 정의
전체 흐름은 공장(프레임워크)이 제어

왜 프레임워크를 사용하나요?

1. 바퀴를 재발명하지 마세요

프레임워크 없이:

// 사용자 인증을 직접 구현
function login($username, $password) {
    // 1. 비밀번호 해싱 (어떤 알고리즘?)
    // 2. SQL Injection 방어
    // 3. CSRF 토큰 검증
    // 4. 세션 관리
    // 5. Remember Me 기능
    // 6. 로그인 시도 제한 (Brute Force 방어)
    // 7. 2단계 인증
    // ... 수백 줄의 코드
}

Laravel 프레임워크:

// 단 한 줄!
Auth::attempt(['email' => $email, 'password' => $password]);

// 모든 보안 기능이 이미 구현되어 있음!

2. 검증된 구조

스파게티 코드 (프레임워크 없이):

// index.php - 모든 게 섞여있음
<?php
session_start();
$conn = mysqli_connect('localhost', 'root', 'password', 'mydb');

if ($_POST['action'] == 'login') {
    $user = $_POST['username'];
    $pass = $_POST['password'];
    // HTML과 PHP가 뒤섞임
    ?>
    <html>
        <body>
            <?php
            if ($result = mysqli_query($conn, "SELECT * FROM users WHERE username='$user'")) {
                // SQL Injection 취약점!
                echo "Welcome!";
            }
            ?>
        </body>
    </html>
    <?php
}
?>

Laravel의 구조:

app/
├── Http/
│   └── Controllers/
│       └── AuthController.php    (로직)
├── Models/
│   └── User.php                   (데이터)
└── routes/
    └── web.php                    (라우팅)

resources/
└── views/
    └── login.blade.php            (화면)

명확한 분리! 각자의 역할!

3. 보안

프레임워크가 자동으로 처리하는 보안:

// CSRF 보호 - Laravel이 자동 처리
<form method="POST">
    @csrf  <!-- 토큰 자동 생성 -->
    <input name="email">
</form>

// SQL Injection 방지 - 자동 이스케이프
User::where('email', $email)->first();

// XSS 방지 - Blade 템플릿이 자동 이스케이프
  // 자동으로 안전하게 처리

// Mass Assignment 보호
class User extends Model {
    protected $fillable = ['name', 'email'];  // 허용 필드만
}

4. 생산성

개발 시간 비교:

로그인/회원가입 기능:
- 직접 구현: 2-3주
- Laravel: 2-3시간 (php artisan make:auth)

RESTful API:
- 직접 구현: 1-2주
- Laravel: 몇 시간

관리자 페이지:
- 직접 구현: 1-2개월
- Laravel Nova: 몇 일

5. 유지보수

6개월 후 코드를 다시 볼 때:

프레임워크 없이:

// 이게 뭐하는 코드지...?
// 어디를 수정해야 하지...?
// 이거 고치면 다른 게 깨지나...?

Laravel:

// 구조가 명확해서 쉽게 찾을 수 있음
// app/Http/Controllers/UserController.php
// 수정해도 다른 부분에 영향 없음
// 테스트 코드로 검증 가능

6. 협업

팀원 A: 프론트엔드 작업 (resources/views/)
팀원 B: API 개발 (app/Http/Controllers/API/)
팀원 C: 데이터베이스 (database/migrations/)

서로 간섭 없이 동시에 작업 가능!
구조가 정해져 있어서 코드 리뷰 쉬움!

프레임워크의 핵심 개념

1. MVC 패턴

MVC = Model-View-Controller

전통적인 웹 개발의 핵심 패턴입니다.

각 부분의 역할:

[사용자]
   ↓ 요청
[Controller] ← 라우터가 연결
   ↓ 데이터 요청
[Model] ← 데이터베이스와 통신
   ↓ 데이터 반환
[Controller]
   ↓ 데이터 전달
[View] ← 화면 렌더링
   ↓ HTML
[사용자]

Laravel에서의 MVC:

Model (데이터):

// app/Models/Post.php
class Post extends Model
{
    // 데이터베이스의 posts 테이블 표현
    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function comments()
    {
        return $this->hasMany(Comment::class);
    }
}

View (화면): ```blade@foreach($posts as $post) <article> <h2></h2> <p></p> 작성자: </article> @endforeach


**Controller (로직):**
```php
// app/Http/Controllers/PostController.php
class PostController extends Controller
{
    public function index()
    {
        // Model에서 데이터 가져오기
        $posts = Post::with('user')->latest()->get();

        // View에 데이터 전달
        return view('posts.index', compact('posts'));
    }
}

Route (연결):

// routes/web.php
Route::get('/posts', [PostController::class, 'index']);

2. 관습보다 설정 (Convention over Configuration)

“합리적인 기본값을 제공하자”

예시 1: 데이터베이스 테이블명

// 모델 이름: User
// 테이블 이름: users (자동으로 복수형)
class User extends Model
{
    // 아무것도 설정 안 해도 작동!
}

// 커스터마이징 필요할 때만
class Person extends Model
{
    protected $table = 'people';  // 수동 지정
}

예시 2: Primary Key

// 자동으로 'id' 컬럼을 PK로 인식
class User extends Model
{
    // 설정 불필요!
}

// 다른 컬럼을 PK로 쓸 때만
class User extends Model
{
    protected $primaryKey = 'user_id';
}

예시 3: 타임스탬프

// 자동으로 created_at, updated_at 관리
class Post extends Model
{
    // 자동으로 시간 기록!
}

// 필요 없으면 끄면 됨
class Log extends Model
{
    public $timestamps = false;
}

3. DRY (Don’t Repeat Yourself)

같은 코드를 반복하지 마세요!

Bad (반복):

class UserController {
    public function show($id) {
        if (!auth()->check() || auth()->user()->role != 'admin') {
            abort(403);
        }
        // ...
    }

    public function edit($id) {
        if (!auth()->check() || auth()->user()->role != 'admin') {
            abort(403);  // 중복!
        }
        // ...
    }

    public function delete($id) {
        if (!auth()->check() || auth()->user()->role != 'admin') {
            abort(403);  // 또 중복!
        }
        // ...
    }
}

Good (DRY):

// Middleware로 한 번만 정의
class AdminMiddleware {
    public function handle($request, $next) {
        if (!auth()->check() || auth()->user()->role != 'admin') {
            abort(403);
        }
        return $next($request);
    }
}

// 적용
Route::middleware('admin')->group(function () {
    Route::get('/users/{id}', [UserController::class, 'show']);
    Route::get('/users/{id}/edit', [UserController::class, 'edit']);
    Route::delete('/users/{id}', [UserController::class, 'delete']);
});

4. 의존성 주입 (Dependency Injection)

“필요한 것을 외부에서 주입받자”

Bad (하드코딩):

class OrderService
{
    public function createOrder($data)
    {
        // 클래스 내부에서 직접 생성 (강한 결합)
        $mailer = new Mailer();
        $paymentGateway = new StripeGateway();

        // ...
    }
}

Good (의존성 주입):

class OrderService
{
    // 생성자로 주입받음
    public function __construct(
        private MailerInterface $mailer,
        private PaymentGatewayInterface $gateway
    ) {}

    public function createOrder($data)
    {
        // 주입받은 객체 사용
        $this->mailer->send(...);
        $this->gateway->charge(...);
    }
}

// Laravel이 자동으로 주입해줌!

장점:

  • 테스트하기 쉬움 (Mock 객체 주입 가능)
  • 코드 재사용성 높음
  • 느슨한 결합 (Loose Coupling)

Laravel 프레임워크의 구조

디렉토리 구조

my-laravel-app/
│
├── app/                        # 애플리케이션 코어 로직
│   ├── Console/               # Artisan 커맨드
│   ├── Exceptions/            # 예외 처리
│   ├── Http/
│   │   ├── Controllers/       # 컨트롤러
│   │   ├── Middleware/        # 미들웨어
│   │   └── Requests/          # Form Request 검증
│   ├── Models/                # Eloquent 모델
│   └── Providers/             # 서비스 프로바이더
│
├── bootstrap/                  # 프레임워크 부트스트랩
│   └── app.php                # 애플리케이션 초기화
│
├── config/                     # 설정 파일들
│   ├── app.php                # 앱 기본 설정
│   ├── database.php           # DB 설정
│   └── ...
│
├── database/                   # 데이터베이스 관련
│   ├── migrations/            # 마이그레이션
│   ├── seeders/               # 시더 (초기 데이터)
│   └── factories/             # 팩토리 (테스트 데이터)
│
├── public/                     # 공개 디렉토리 (웹 루트)
│   ├── index.php              # 진입점
│   ├── css/
│   ├── js/
│   └── images/
│
├── resources/                  # 원본 리소스
│   ├── views/                 # Blade 템플릿
│   ├── css/
│   └── js/
│
├── routes/                     # 라우트 정의
│   ├── web.php                # 웹 라우트
│   ├── api.php                # API 라우트
│   └── console.php            # CLI 라우트
│
├── storage/                    # 저장소
│   ├── app/                   # 애플리케이션 파일
│   ├── framework/             # 프레임워크 캐시
│   └── logs/                  # 로그 파일
│
├── tests/                      # 테스트
│   ├── Feature/               # 기능 테스트
│   └── Unit/                  # 단위 테스트
│
├── vendor/                     # Composer 의존성
│
├── .env                        # 환경 변수
├── artisan                     # Artisan CLI
├── composer.json               # Composer 설정
└── package.json                # NPM 설정

요청 생명주기 (Request Lifecycle)

사용자가 URL 접속하면 어떤 일이 일어날까요?

1. public/index.php
   ↓
2. bootstrap/app.php (애플리케이션 생성)
   ↓
3. HTTP Kernel 실행
   ↓
4. Service Providers 등록 (register)
   ↓
5. Service Providers 부팅 (boot)
   ↓
6. Middleware 실행 (요청 전)
   ↓
7. Router (라우트 매칭)
   ↓
8. Controller (비즈니스 로직)
   ↓
9. Model (데이터베이스)
   ↓
10. View (HTML 생성)
   ↓
11. Middleware 실행 (응답 후)
   ↓
12. Response (사용자에게 전송)

코드로 보는 흐름:

// 1. public/index.php
require __DIR__.'/../vendor/autoload.php';
$app = require_once __DIR__.'/../bootstrap/app.php';
$kernel = $app->make(Kernel::class);
$response = $kernel->handle($request = Request::capture());
$response->send();

// 2. routes/web.php
Route::get('/posts/{id}', [PostController::class, 'show']);

// 3. app/Http/Controllers/PostController.php
class PostController extends Controller
{
    public function show($id)
    {
        $post = Post::findOrFail($id);
        return view('posts.show', compact('post'));
    }
}

// 4. app/Models/Post.php
class Post extends Model
{
    // Eloquent가 자동으로 데이터베이스 쿼리
}

// 5. resources/views/posts/show.blade.php
<h1></h1>
<p></p>

Service Container (서비스 컨테이너)

Laravel의 심장!

// 클래스를 Container에 바인딩
app()->bind(MailerInterface::class, SmtpMailer::class);

// 필요할 때 자동으로 해결 (Resolve)
class OrderController extends Controller
{
    // Laravel이 자동으로 SmtpMailer 주입!
    public function __construct(MailerInterface $mailer)
    {
        $this->mailer = $mailer;
    }
}

// 수동으로도 가능
$mailer = app(MailerInterface::class);

싱글톤 등록:

// 앱 전체에서 하나의 인스턴스만 사용
app()->singleton(Cache::class, function () {
    return new RedisCache();
});

Service Provider

애플리케이션의 부팅 및 설정

// app/Providers/AppServiceProvider.php
class AppServiceProvider extends ServiceProvider
{
    // 서비스 등록
    public function register()
    {
        $this->app->bind(
            RepositoryInterface::class,
            EloquentRepository::class
        );
    }

    // 부팅 (모든 서비스가 등록된 후)
    public function boot()
    {
        // 뷰 Composer
        View::composer('*', function ($view) {
            $view->with('appName', config('app.name'));
        });

        // 모델 이벤트
        User::creating(function ($user) {
            $user->uuid = Str::uuid();
        });
    }
}

Facade

“정적 메서드처럼 보이지만 사실은 아니에요”

// Facade 사용
use Illuminate\Support\Facades\Cache;

Cache::put('key', 'value', 3600);
$value = Cache::get('key');

// 실제로는 이렇게 동작:
app('cache')->put('key', 'value', 3600);
app('cache')->get('key');

// Facade는 편의 문법!

자주 사용하는 Facade:

// 데이터베이스
DB::table('users')->get();

// 인증
Auth::user();
Auth::check();

// 라우팅
Route::get('/path', $callback);

// 뷰
View::make('welcome');

// 요청
Request::input('name');

// 응답
Response::json(['status' => 'ok']);

// 캐시
Cache::remember('key', 3600, fn() => DB::table('users')->get());

// 이벤트
Event::dispatch(new OrderShipped($order));

Laravel의 강력한 기능들

1. Eloquent ORM

객체로 데이터베이스 다루기

// CRUD가 이렇게 간단!

// Create
$user = User::create([
    'name' => '홍길동',
    'email' => 'hong@example.com'
]);

// Read
$users = User::where('active', true)->get();
$user = User::find(1);

// Update
$user->name = '김철수';
$user->save();

// Delete
$user->delete();

// 관계
$user->posts;  // 사용자의 모든 게시글
$post->comments;  // 게시글의 모든 댓글
$post->user->name;  // 게시글 작성자 이름

2. Blade 템플릿

PHP보다 우아한 템플릿

@extends('layouts.app')

@section('title', '게시글 목록')

@section('content')
    <h1>게시글</h1>

    @if($posts->count() > 0)
        @foreach($posts as $post)
            <article>
                <h2></h2>
                <p></p>

                @can('update', $post)
                    <a href="">수정</a>
                @endcan
            </article>
        @endforeach
    @else
        <p>게시글이 없습니다.</p>
    @endif
@endsection

3. Migration (마이그레이션)

코드로 데이터베이스 관리

// database/migrations/2025_11_18_create_posts_table.php
class CreatePostsTable extends Migration
{
    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->foreignId('user_id')->constrained();
            $table->string('title');
            $table->text('content');
            $table->boolean('published')->default(false);
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('posts');
    }
}

실행:

php artisan migrate              # 마이그레이션 실행
php artisan migrate:rollback     # 롤백
php artisan migrate:fresh        # 모두 삭제 후 재실행

4. Validation (검증)

폼 입력 검증 자동화

// Controller에서
public function store(Request $request)
{
    $validated = $request->validate([
        'title' => 'required|max:255',
        'content' => 'required',
        'email' => 'required|email|unique:users',
        'age' => 'required|integer|min:18|max:100',
        'website' => 'nullable|url',
        'tags' => 'array|min:1|max:5',
        'tags.*' => 'string|max:50',
    ]);

    Post::create($validated);
}

Form Request로 분리:

// app/Http/Requests/StorePostRequest.php
class StorePostRequest extends FormRequest
{
    public function rules()
    {
        return [
            'title' => 'required|max:255',
            'content' => 'required',
        ];
    }

    public function messages()
    {
        return [
            'title.required' => '제목은 필수입니다.',
            'content.required' => '내용을 입력해주세요.',
        ];
    }
}

// Controller
public function store(StorePostRequest $request)
{
    // 이미 검증됨!
    Post::create($request->validated());
}

5. Queue (작업 큐)

시간이 오래 걸리는 작업 비동기 처리

// Job 생성
class ProcessPodcast implements ShouldQueue
{
    public function __construct(
        public Podcast $podcast
    ) {}

    public function handle()
    {
        // 시간이 오래 걸리는 작업
        $this->podcast->process();
    }
}

// Controller에서 큐에 추가
ProcessPodcast::dispatch($podcast);

// 사용자는 기다리지 않고 바로 응답받음!

6. Events & Listeners

이벤트 기반 프로그래밍

// Event
class OrderShipped
{
    public function __construct(
        public Order $order
    ) {}
}

// Listener
class SendShipmentNotification
{
    public function handle(OrderShipped $event)
    {
        Mail::to($event->order->user)->send(
            new OrderShippedMail($event->order)
        );
    }
}

// 이벤트 발생
event(new OrderShipped($order));

// 여러 Listener가 자동으로 실행됨!

7. Artisan CLI

강력한 명령줄 도구

# 코드 생성
php artisan make:controller UserController
php artisan make:model Post -mfsc  # migration, factory, seeder, controller 한번에!
php artisan make:middleware AdminMiddleware

# 데이터베이스
php artisan migrate
php artisan db:seed

# 캐시
php artisan cache:clear
php artisan config:cache

# 큐
php artisan queue:work

# 서버
php artisan serve

# 커스텀 명령어도 만들 수 있음!
php artisan make:command SendEmails

프레임워크 선택 가이드

Laravel을 선택하세요:

✅ 빠른 개발이 필요할 때
✅ 모던한 PHP 기능을 활용하고 싶을 때
✅ 풍부한 생태계가 필요할 때
✅ 풀스택 애플리케이션을 만들 때
✅ 개발자 경험을 중시할 때
✅ 초보자부터 시작할 때

Symfony를 선택하세요:

✅ 엔터프라이즈급 프로젝트
✅ 극도로 유연한 설정 필요
✅ 재사용 가능한 컴포넌트 필요
✅ 레거시 시스템과 통합

CodeIgniter를 선택하세요:

✅ 매우 가벼운 프레임워크 필요
✅ 공유 호스팅 환경
✅ 낮은 러닝 커브
✅ 레거시 PHP 지원 필요

프레임워크 없이:

❌ 대부분의 경우 권장하지 않음
✅ 학습 목적
✅ 극도로 단순한 스크립트
✅ 성능이 극도로 중요 (마이크로초 단위)

실전 예제: 블로그 만들기

1. 프로젝트 생성

composer create-project laravel/laravel blog
cd blog
php artisan serve

2. 데이터베이스 설정

.env:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=blog
DB_USERNAME=root
DB_PASSWORD=secret

3. 모델과 마이그레이션

php artisan make:model Post -mfc

Migration:

// database/migrations/xxxx_create_posts_table.php
public function up()
{
    Schema::create('posts', function (Blueprint $table) {
        $table->id();
        $table->string('title');
        $table->string('slug')->unique();
        $table->text('content');
        $table->boolean('published')->default(false);
        $table->timestamps();
    });
}

실행:

php artisan migrate

4. 라우트

// routes/web.php
Route::get('/', [PostController::class, 'index']);
Route::get('/posts/{post:slug}', [PostController::class, 'show']);

5. 컨트롤러

// app/Http/Controllers/PostController.php
class PostController extends Controller
{
    public function index()
    {
        $posts = Post::where('published', true)
            ->latest()
            ->paginate(10);

        return view('posts.index', compact('posts'));
    }

    public function show(Post $post)
    {
        return view('posts.show', compact('post'));
    }
}

6. 뷰


@section('content')
    <h1>블로그 게시글</h1>

    @foreach($posts as $post)
        <article>
            <h2>
                <a href="">
                    
                </a>
            </h2>
            <p></p>
        </article>
    @endforeach

    
@endsection

완성! 몇 분 만에 블로그가 만들어졌습니다.

마무리

프레임워크는:

  • ✅ 애플리케이션의 골격
  • ✅ 검증된 구조와 패턴
  • ✅ 생산성 향상
  • ✅ 보안 기본 제공
  • ✅ 유지보수 용이

Laravel은:

  • ✅ 현대적인 PHP 프레임워크
  • ✅ 우아한 문법
  • ✅ 풍부한 기능
  • ✅ 활발한 커뮤니티
  • ✅ 초보자 친화적

다음 단계

축하합니다! “01.Start” 섹션을 모두 마쳤습니다.

이제 여러분은:

  • ✅ Laravel이 무엇인지 알고
  • ✅ PHP 언어를 이해하고
  • ✅ 의존성 관리를 배우고
  • ✅ 프레임워크의 개념을 이해했습니다

다음 섹션에서는 Laravel을 실제로 설치하고 개발 환경을 구축해보겠습니다!


네비게이션


핵심 요약

프레임워크는 애플리케이션의 뼈대와 규칙을 제공합니다.

  • 제어의 역전 (IoC): 프레임워크가 코드를 호출
  • MVC 패턴: Model-View-Controller 분리
  • Convention over Configuration: 합리적 기본값
  • Laravel은 현대적이고 강력한 PHP 프레임워크
  • Eloquent, Blade, Artisan 등 풍부한 기능

다음 섹션에서는 Laravel을 설치합니다!


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