1 개요[ | ]
- 03. Creating Chirps
- 03. Chirps 생성
https://bootcamp.laravel.com/livewire/creating-chirps
이제 새로운 애플리케이션을 만들 준비가 되었습니다! 사용자들이 Chirps라는 짧은 메시지를 게시할 수 있도록 해봅시다.
2 모델, 마이그레이션, 컨트롤러[ | ]
사용자들이 Chirp를 게시할 수 있도록 하려면 모델, 마이그레이션, 컨트롤러를 만들어야 합니다. 각 개념을 좀 더 깊이 살펴보겠습니다:
- 모델은 데이터베이스의 테이블과 상호작용할 수 있는 강력하고 즐거운 인터페이스를 제공합니다.
- 마이그레이션은 데이터베이스의 테이블을 쉽게 생성하고 수정할 수 있게 해줍니다. 이를 통해 애플리케이션이 실행되는 모든 곳에서 동일한 데이터베이스 구조가 유지되도록 보장합니다.
- 컨트롤러는 애플리케이션에 대한 요청을 처리하고 응답을 반환하는 역할을 합니다.
거의 모든 기능은 이 세 가지 요소가 조화롭게 작동하는 것을 포함하며, artisan make:model
명령어를 사용하면 이 모든 것을 한 번에 생성할 수 있습니다.
Chirp를 위한 모델, 마이그레이션, 컨트롤러를 생성하려면 다음 명령어를 사용하세요:
php artisan make:model -mc Chirp
Note
php artisan make:model --help
명령어를 실행하여 사용가능한 모든 옵션을 볼 수 있습니다.
이 명령어는 다음 세 가지 파일을 생성합니다:
app/Models/Chirp.php
- Eloquent 모델database/migrations/<timestamp>_create_chirps_table.php
- 데이터베이스 테이블을 생성할 데이터베이스 마이그레이션app/Http/Controllers/ChirpController.php
- 들어오는 요청을 처리하고 응답을 반환할 HTTP 컨트롤러
3 라우팅[ | ]
컨트롤러에 대한 URL을 생성하기 위해 routes
디렉토리에 라우트를 추가해야 합니다.
Livewire를 사용하고 있기 때문에, Chirp 생성 폼과 기존 Chirp 목록을 표시하기 위해 단일 <codeRoute::get 라우트만 정의하면 됩니다. 또한, 이 라우트를 두 가지 미들웨어 뒤에 배치할 것입니다:
auth
미들웨어는 로그인한 사용자만 라우트에 접근할 수 있도록 합니다.verified
미들웨어는 이메일 인증을 활성화할 경우 사용됩니다.
<?php
use App\Http\Controllers\ChirpController;
use Illuminate\Support\Facades\Route;
Route::view('/', 'welcome');
Route::get('chirps', [ChirpController::class, 'index'])
->middleware(['auth', 'verified'])
->name('chirps');
Route::view('dashboard', 'dashboard')
->middleware(['auth', 'verified'])
->name('dashboard');
Route::view('profile', 'profile')
->middleware(['auth'])
->name('profile');
require __DIR__.'/auth.php';
Note
애플리케이션의 모든 라우트를 보려면
php artisan route:list
명령어를 실행하면 됩니다.
새 ChirpController
클래스의 index
메소드에서 테스트 메시지를 반환하여 라우트와 컨트롤러를 테스트해봅시다:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Response;
class ChirpController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): Response
{
return response('Hello, World!');
}
}
이전에 로그인된 상태라면 http://localhost:8000/chirps , 또는 Sail을 사용 중이라면 http://localhost/chirps 로 이동하여 메시지를 확인할 수 있습니다.
4 Livewire[ | ]
아직까진 별 게 없죠? 이제 ChirpController
클래스의 index
메소드를 업데이트하여 Blade 뷰를 렌더링해 봅시다.
<?php
namespace App\Http\Controllers;
use Illuminate\View\View;
class ChirpController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): View
{
return view('chirps', [
//
]);
}
}
이제 Blade 템플릿을 생성하고 새로운 Chirp를 생성할 폼을 렌더링하는 Livewire 컴포넌트를 포함해 봅시다:
<x-app-layout>
<div class="max-w-2xl mx-auto p-4 sm:p-6 lg:p-8">
<livewire:chirps.create />
</div>
</x-app-layout>
다음으로, 폼을 렌더링할 Livewire 컴포넌트를 생성합시다. 이를 위해, Artisan 명령어인 make:volt
를 사용할 수 있습니다.
아래의 스니펫은 컴포넌트를 생성하는 두 가지 방법을 제시합니다: 하나는 Class
(클래스) API를 사용하는 방법이고, 다른 하나는 Functional
(함수형) API를 사용하는 방법입니다. 이 튜토리얼 전반에서 두 API를 모두 보실 수 있으며, 선호하는 스타일의 Livewire 개발 방식을 선택하실 수 있습니다.
php artisan make:volt chirps/create --class
이 명령어는 새로운 Livewire 컴포넌트를 resources/views/livewire/chirps/create.blade.php
에 생성합니다.
Livewire 컴포넌트를 업데이트하여 폼을 표시해 봅시다:
<?php
use Livewire\Volt\Component;
new class extends Component
{
public string $message = '';
}; ?>
<div>
<form wire:submit="store">
<textarea
wire:model="message"
placeholder="{{ __('What\'s on your mind?') }}"
class="block w-full border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 rounded-md shadow-sm"
></textarea>
<x-input-error :messages="$errors->get('message')" class="mt-2" />
<x-primary-button class="mt-4">{{ __('Chirp') }}</x-primary-button>
</form>
</div>
이제 브라우저에서 페이지를 새로고침하여 기본 레이아웃에 렌더링된 새로운 폼을 확인하세요!
스크린샷이 위와 같지 않다면, Vite 개발 서버를 중지하고 다시 시작하여 Tailwind가 우리가 방금 만든 새 파일의 CSS 클래스를 감지할 수 있도록 해야 할 수 있습니다.
이 시점부터는 Vite 개발 서버가 npm run dev
로 실행 중일 때 Blade 템플릿에 변경사항을 자동으로 브라우저에서 새로고침합니다.
5 내비게이션 메뉴[ | ]
Breeze에서 제공하는 내비게이션 메뉴에 링크를 추가해봅시다.
데스크톱 화면에 대한 메뉴 항목을 추가하려면 Breeze에서 제공하는 navigation.blade.php
컴포넌트를 업데이트하세요:
<div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
<x-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')" wire:navigate>
{{ __('Dashboard') }}
</x-nav-link>
<x-nav-link :href="route('chirps')" :active="request()->routeIs('chirps')" wire:navigate>
{{ __('Chirps') }}
</x-nav-link>
</div>
모바일 화면에도 추가하려면:
<div class="pt-2 pb-3 space-y-1">
<x-responsive-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')" wire:navigate>
{{ __('Dashboard') }}
</x-responsive-nav-link>
<x-responsive-nav-link :href="route('chirps')" :active="request()->routeIs('chirps')" wire:navigate>
{{ __('Chirps') }}
</x-responsive-nav-link>
</div>
6 Chrip 저장[ | ]
우리의 폼은 Chirp
버튼을 클릭할 때 store
액션을 호출하도록 설정되어 있습니다. 이제 데이터를 검증하고 새 Chirp를 생성하기 위해 chirp.create
컴포넌트에 store
액션을 추가하겠습니다.
<?php
use Livewire\Attributes\Validate;
use Livewire\Volt\Component;
new class extends Component
{
#[Validate('required|string|max:255')]
public string $message = '';
public function store(): void
{
$validated = $this->validate();
auth()->user()->chirps()->create($validated);
$this->message = '';
}
}; ?>
<div>
<form wire:submit="store">
<textarea
wire:model="message"
placeholder="{{ __('What\'s on your mind?') }}"
class="block w-full border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 rounded-md shadow-sm"
></textarea>
<x-input-error :messages="$errors->get('message')" class="mt-2" />
<x-primary-button class="mt-4">{{ __('Chirp') }}</x-primary-button>
</form>
</div>
Livewire의 Validate
속성을 사용하여 사용자가 데이터베이스 컬럼의 255자 제한을 초과하지 않는 메시지를 제공하도록 하기 위해 Laravel의 강력한 검증 기능을 활용하고 있습니다.
그 후, chirps
관계를 활용하여 로그인된 사용자에 속하는 레코드를 생성하고 있습니다. 이 관계는 곧 정의할 예정입니다.
마지막으로, message
폼 필드 값을 비우고 있습니다.
7 관계 생성[ | ]
이전 단계에서 auth()->user()
객체에 chirps
메소드를 호출한 것을 눈치챘을 것입니다. 이 메소드를 User
모델에 생성하여 "has many" 관계를 정의해야 합니다:
<?php
...
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
...
public function chirps(): HasMany
{
return $this->hasMany(Chirp::class);
}
}
Note
Laravel은 다양한 종류의 모델 관계를 제공하며, 이에 대해서는 Eloquent 관계 문서에서 더 자세히 읽을 수 있습니다.
8 대량 할당 보호[ | ]
요청의 모든 데이터를 모델에 전달하는 것은 위험할 수 있습니다. 예를 들어, 사용자가 자신의 프로필을 편집할 수 있는 페이지가 있다고 생각해 보세요. 만약 요청 전체를 모델에 전달한다면, 사용자는 is_admin
컬럼과 같은 어떤 컬럼이든 편집할 수 있게 됩니다. 이것을 대량 할당 취약성이라고 합니다.
Laravel은 기본적으로 대량 할당을 차단하여 실수로 이러한 일이 발생하는 것을 방지합니다. 그러나 대량 할당은 매우 편리합니다. 왜냐하면 각 속성을 하나씩 할당하는 수고를 덜어주기 때문입니다. 우리는 "fillable" 속성으로 안전한 속성을 표시하여 대량 할당을 활성화할 수 있습니다.
Chirp
모델에 $fillable
속성을 추가하여 message
속성에 대한 대량 할당을 활성화해 봅시다:
<?php
...
class Chirp extends Model
{
...
protected $fillable = [
'message',
];
}
Laravel의 대량 할당 보호에 대해 더 알고 싶다면 문서를 참조하십시오.
9 마이그레이션 업데이트[ | ]
애플리케이션을 생성할 때, Laravel은 이미 database/migrations
디렉토리에 포함된 기본 마이그레이션을 적용했습니다. 현재 데이터베이스 구조를 점검하려면 php artisan db:show
와 php artisan db:table
명령어를 사용할 수 있습니다:
php artisan db:show
php artisan db:table users
따라서 지금 필요한 것은 Chirp
와 그 User
간의 관계와 메시지를 저장할 추가 열입니다. 앞서 만든 데이터베이스 마이그레이션 파일을 열어 추가 열을 작성해보겠습니다:
<?php
...
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('chirps', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('message');
$table->timestamps();
});
}
...
};
이 마이그레이션을 추가한 이후로 데이터베이스를 마이그레이트하지 않았으므로, 지금 마이그레이션을 진행해보겠습니다:
php artisan migrate
Warning
각 데이터베이스 마이그레이션은 한 번만 실행됩니다. 테이블에 추가적인 변경사항을 적용하려면 또 다른 마이그레이션을 생성해야 합니다. 개발 중에는
php artisan migrate:fresh
명령어를 사용하여 배포되지 않은 마이그레이션을 업데이트하고 데이터베이스를 처음부터 다시 빌드할 수 있습니다.
10 테스트[ | ]
생성한 폼을 사용하여 Chirp를 보낼 준비가 되었습니다! 아직 페이지에 기존 Chirp를 표시하지 않았기 때문에 결과를 볼 수는 없습니다.
메시지 필드를 비워두거나 255자 이상을 입력하면 유효성 검증이 작동하는 것을 볼 수 있습니다.
10.1 Artisan Tinker[ | ]
지금이 Artisan Tinker에 대해 배울 좋은 시점입니다. Artisan Tinker는 Laravel 애플리케이션에서 임의의 PHP 코드를 실행할 수 있는 REPL(읽기-평가-출력 루프)입니다.
콘솔에서 새 tinker 세션을 시작합니다:
php artisan tinker
다음으로, 데이터베이스에 있는 Chirp를 표시하기 위해 다음 코드를 실행합니다:
App\Models\Chirp::all();
=> Illuminate\Database\Eloquent\Collection {#4512
all: [
App\Models\Chirp {#4514
id: 1,
user_id: 1,
message: "I'm building Chirper with Laravel!",
created_at: "2022-08-24 13:37:00",
updated_at: "2022-08-24 13:37:00",
},
],
}
Tinker를 종료하려면 exit
명령어를 사용하거나 Ctrl
+ c
를 누릅니다.