Skip to main content
Summary

Laravel은 많은 일을 대신 해 준다. URL을 분석하고, 컨트롤러를 실행하고, 데이터베이스와 대화하고, 로그인 상태도 관리한다. 처음 보면 이 과정이 마법처럼 보일 수 있다. 하지만 Laravel은 웹 요청의 흐름, PHP 코드 실행 방식, Composer의 오토로딩 위에 세워진 도구다.

이 장에서는 앞으로 반복해서 만날 기본기를 한 번에 정리한다. 모든 문법을 외우는 것이 목표는 아니다. Laravel 코드가 어떤 흐름으로 실행되는지, 낯선 파일과 폴더가 왜 필요한지 이해하는 것이 목표다.

Laravel을 배우기 전에 알아야 할 웹과 PHPAI 생성 이미지

브라우저 요청은 어디로 가는가

사용자가 브라우저에서 도메인 뒤에 붙는 경로인 /posts로 접속한다고 하자. 브라우저는 서버에 HTTP 요청을 보낸다. HTTP는 브라우저와 서버가 요청과 응답을 주고받기 위한 약속이다. 요청에는 주소, 메서드, 쿠키, 폼 데이터 같은 정보가 들어갈 수 있다. 서버는 이 요청을 처리한 뒤 HTML, JSON, 이미지 같은 응답을 돌려준다.

개발 환경에서는 Laravel의 내장 개발 서버를 사용할 수 있고, 실제 운영 환경에서는 보통 Nginx나 Apache 같은 웹 서버가 앞에 선다. 중요한 점은 웹 서버가 Laravel 프로젝트 전체를 직접 공개하지 않는다는 것이다. Laravel에서는 보통 public 디렉터리만 웹에서 접근 가능하게 둔다. 그 안의 index.php가 애플리케이션의 입구 역할을 한다.

Laravel의 요청 흐름은 크게 다음과 같다.

  1. 브라우저가 요청을 보낸다.

  2. 웹 서버가 요청을 받아 public/index.php로 전달한다.

  3. index.php가 Composer 오토로더를 불러온다.

  4. Laravel 애플리케이션이 부팅된다.

  5. 미들웨어가 요청을 검사한다.

  6. 라우터가 URL에 맞는 기능을 찾는다.

  7. 컨트롤러나 라우트에 직접 적은 코드가 실행된다.

  8. 응답이 다시 브라우저로 돌아간다.

Laravel 공식 문서도 큰 틀에서 이 흐름을 public/index.php, Composer 오토로더, 애플리케이션 부팅, 서비스 프로바이더, 미들웨어, 라우터, 응답 반환의 순서로 설명한다.1 여기서 서비스 프로바이더는 Laravel과 패키지의 기능을 애플리케이션에 등록하고 준비하는 단계라고 이해하면 된다. 즉 Laravel은 요청을 숨겨서 처리하는 것이 아니라, 정해진 파이프라인에 따라 처리한다.

앞으로 만들 애플리케이션에서는 글 목록을 보여 주고, 글을 작성하고, 수정하고, 삭제하는 기능을 단계적으로 만들 것이다. 사용자가 /posts에 접속하면 라우터가 이 주소를 찾고, 컨트롤러가 글 목록을 준비하고, Blade 템플릿이 HTML 화면을 만든다. 이 모든 과정은 지금 본 요청 흐름 위에서 움직인다.

Laravel 코드에서 자주 만나는 PHP 문법

Laravel은 PHP 프레임워크다. PHP 문법을 완벽히 알아야 Laravel을 시작할 수 있는 것은 아니지만, 자주 나오는 표현은 익숙해야 한다.

가장 기본은 변수다. PHP 변수는 $로 시작한다.

<?php

$title = '첫 번째 글';
$count = 3;
$isPublished = true;

문자열, 숫자, 참거짓 값을 변수에 담을 수 있다. Laravel에서도 요청 값, 데이터베이스 결과, 설정 값이 변수에 담긴다.

배열은 여러 값을 묶을 때 쓴다. PHP 배열은 순서가 있는 목록으로도 쓰이고, 이름표가 붙은 데이터 묶음으로도 쓰인다.

<?php

$post = [
    'title' => '첫 번째 글',
    'body' => 'Laravel을 배우기 시작했습니다.',
    'published' => true,
];

$posts = [
    ['title' => '첫 번째 글'],
    ['title' => '두 번째 글'],
];

'title' => '첫 번째 글'에서 왼쪽은 키, 오른쪽은 값이다. 나중에 컨트롤러에서 화면으로 데이터를 넘길 때도 이런 배열을 자주 사용한다.

함수는 반복되는 코드를 이름 붙여 묶는 방법이다. 현대 PHP에서는 매개변수와 반환값의 타입을 함께 적는 경우가 많다.

<?php

function excerpt(string $body, int $length = 50): string
{
    return mb_substr($body, 0, $length);
}

string $body는 $body가 문자열이어야 한다는 뜻이다. : string은 이 함수가 문자열을 반환한다는 뜻이다. 타입 선언은 실수를 빨리 발견하게 도와준다. 예를 들어 숫자가 필요한 곳에 배열이 들어오면 더 이른 시점에 오류를 확인할 수 있다.

클래스는 데이터와 동작을 함께 묶는 방법이다. Laravel의 컨트롤러, 모델, 서비스 클래스는 모두 이 구조를 사용한다.

<?php

namespace AppServices;

class SlugGenerator
{
    public function make(string $title): string
    {
        return strtolower(str_replace(' ', '-', $title));
    }
}

namespace는 클래스의 주소 같은 역할을 한다. 같은 이름의 클래스가 여러 곳에 있어도 충돌하지 않게 한다. AppServicesSlugGenerator라는 전체 이름을 가지면, Laravel과 Composer가 이 클래스 파일을 찾아 불러올 수 있다.

다른 네임스페이스의 클래스를 사용할 때는 use를 쓴다.

<?php

use AppServicesSlugGenerator;

$generator = new SlugGenerator();
$slug = $generator->make('Hello Laravel');

처음에는 $, =>, ::, ->, namespace, use 같은 기호가 낯설 수 있다. 하지만 Laravel 코드 대부분은 이 조합으로 읽힌다. ->는 객체의 메서드나 속성에 접근할 때 쓰고, ::는 클래스 자체에 연결된 기능을 호출할 때 자주 쓴다.

Composer는 PHP 프로젝트의 부품 관리자다

Laravel 프로젝트를 설치하면 vendor라는 디렉터리가 생긴다. 이 안에는 Laravel 자체와 Laravel이 의존하는 외부 패키지들이 들어 있다. 직접 작성한 애플리케이션 코드가 아니라, Composer가 내려받은 코드다.

Composer는 PHP의 의존성 관리 도구다. 의존성이란 내 프로젝트가 실행되기 위해 필요한 외부 라이브러리를 뜻한다. Laravel도 여러 패키지 위에 만들어져 있으므로 Composer 없이는 정상적으로 설치하고 관리하기 어렵다.

Composer에서 자주 보는 파일과 디렉터리는 다음 네 가지다.

  • composer.json: 프로젝트가 어떤 패키지를 필요로 하는지 적는 파일

  • composer.lock: 실제로 설치된 패키지의 정확한 버전을 기록하는 파일

  • vendor/: 설치된 외부 패키지가 들어 있는 디렉터리

  • vendor/autoload.php: Composer가 만든 오토로더 파일

composer install을 실행하면 Composer는 composer.lock에 기록된 버전에 맞춰 패키지를 설치한다. 잠금 파일이 있으면 팀원과 서버가 같은 버전의 패키지를 쓰게 되어 예기치 않은 차이를 줄일 수 있다. Composer는 패키지를 vendor 디렉터리에 설치하고, vendor/autoload.php 파일도 만든다. 이 파일을 불러오면 필요한 클래스를 일일이 require하지 않아도 사용할 수 있다.2

Laravel의 public/index.php가 초기에 Composer 오토로더를 불러오는 이유가 여기에 있다. 컨트롤러, 모델, 프레임워크 내부 클래스가 요청 중에 자연스럽게 사용되려면 먼저 오토로딩이 준비되어야 한다.

중요한 규칙이 있다. vendor 디렉터리 안의 코드는 직접 수정하지 않는다. 패키지 업데이트나 재설치 때 사라질 수 있기 때문이다. 애플리케이션 코드는 app, routes, resources, database 같은 프로젝트 디렉터리에 작성한다.

순수 PHP 코드와 Laravel 코드의 차이

프레임워크 없이 PHP로 작은 페이지를 만든다면 요청 주소를 직접 확인하고, 필요한 파일을 직접 불러오고, 응답도 직접 출력해야 한다.

<?php

$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);

if ($path === '/posts') {
    $posts = [
        ['title' => '첫 번째 글'],
    ];

    require __DIR__ . '/views/posts.php';
    exit;
}

http_response_code(404);
echo 'Not Found';

이 방식은 작을 때는 이해하기 쉽다. 하지만 기능이 늘어나면 문제가 생긴다. URL 분기 코드가 길어지고, 데이터베이스 연결 코드가 여기저기 흩어지고, 로그인 확인 같은 공통 처리를 매번 반복하게 된다.

Laravel은 이런 반복을 정리해 준다. URL과 기능 연결은 라우트 파일에 적는다.

<?php

use IlluminateSupportFacadesRoute;
use AppHttpControllersPostController;

Route::get('/posts', [PostController::class, 'index']);

실제 처리 코드는 컨트롤러에 둔다.

<?php

namespace AppHttpControllers;

class PostController
{
    public function index()
    {
        $posts = [
            ['title' => '첫 번째 글'],
        ];

        return view('posts.index', ['posts' => $posts]);
    }
}

순수 PHP 예제와 Laravel 예제가 하는 일은 비슷하다. /posts 요청이 오면 글 목록을 준비하고 화면을 보여 준다. 차이는 구조다. Laravel은 라우팅, 컨트롤러, 뷰의 역할을 나눈다. 덕분에 기능이 늘어나도 어디에 무엇을 작성해야 하는지 예측하기 쉽다.

프레임워크는 코드를 짧게 만드는 도구만은 아니다. 팀원과 같은 방식으로 코드를 배치하게 해 주는 약속이기도 하다. 라우트는 routes에서 찾고, 컨트롤러는 app/Http/Controllers에서 찾고, 화면은 resources/views에서 찾는다. 이 약속 덕분에 프로젝트가 커져도 길을 잃을 가능성이 줄어든다.

자동화는 이해 가능한 흐름 위에 있다

Laravel을 배울 때 가장 경계해야 할 태도는 오류가 나면 아무 명령어나 복사해서 실행하는 것이다. 반대로 가장 좋은 태도는 흐름을 추적하는 것이다.

요청이 들어오면 먼저 웹 서버를 지난다. 그다음 public/index.php에서 시작한다. Composer 오토로더가 클래스를 불러올 준비를 한다. Laravel 애플리케이션이 부팅되고, 미들웨어와 라우터를 거쳐 컨트롤러가 실행된다. 컨트롤러는 필요한 데이터를 준비하고, 뷰나 JSON 응답을 반환한다.

앞으로 라우팅, MVC, Eloquent, 인증, 테스트, 배포를 배울 때도 이 흐름은 계속 반복된다. 새로운 기능이 나와도 먼저 물어보면 된다. 이 코드는 요청이 들어오기 전 준비 단계인가, 요청을 검사하는 단계인가, URL을 기능에 연결하는 단계인가, 데이터를 다루는 단계인가, 응답을 만드는 단계인가.

이 질문에 답할 수 있으면 Laravel의 자동화된 기능은 더 이상 마법이 아니다. 이해 가능한 도구가 된다.

참고

1Laravel Request Lifecycle

2Composer Basic usage

책 목록

라라벨 학습서: 웹 기초부터 배포까지 배우는 Laravel 실전 입문

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기