diff --git a/app/Http/Controllers/LoginController.php b/app/Http/Controllers/LoginController.php new file mode 100644 index 0000000..ca0f6fe --- /dev/null +++ b/app/Http/Controllers/LoginController.php @@ -0,0 +1,57 @@ +route('dashboard'); + } + + return view('login'); + } + + public function login(Request $request) + { + try { + $credentials = $request->validate([ + 'email' => ['required', 'email'], + 'password' => ['required'], + ]); + } catch (ValidationException $e) { + return redirect() + ->back() + ->withErrors($e->errors()) + ->withInput() + ->with('error', '입력 값 검증 실패'); + } + + if (Auth::attempt($credentials, false)) { + $request->session()->regenerate(); + + return redirect() + ->intended('/') + ->with('success', '로그인에 성공하였습니다.'); + } + + return redirect() + ->back() + ->withInput() + ->with('error', '입력한 자격증명이 올바르지 않습니다.'); + } + public function logout(Request $request) + { + Auth::logout(); + + $request->session()->invalidate(); + $request->session()->regenerateToken(); + + return redirect()->route('login'); + } +} diff --git a/app/Http/Controllers/ProductController.php b/app/Http/Controllers/ProductController.php new file mode 100644 index 0000000..4f693f6 --- /dev/null +++ b/app/Http/Controllers/ProductController.php @@ -0,0 +1,139 @@ +validate([ + 'name' => ['required', 'string'], + 'sku' => [ + 'required', + 'string', + Rule::unique('products', 'sku')->ignore($product->id), // 현재 상품은 예외 + ], + 'price' => ['required', 'numeric', 'min:0'], + 'image' => ['nullable', 'image', 'mimes:jpg,jpeg,png,webp', 'max:2048'], + ]); + + // 2) 텍스트 데이터 업데이트 + $product->name = $data['name']; + $product->sku = $data['sku']; + $product->price = $data['price']; + + // 3) 이미지 처리 + if ($request->hasFile('image')) { + + // 기존 이미지가 있으면 삭제 + if ($product->image && Storage::disk('public')->exists($product->image)) { + Storage::disk('public')->delete($product->image); + // Storage 파사드를 이용하면, 저장소 드라이버가 바뀌어도 코드 수정 최소화 + } + + // 새 이미지 저장 + $path = $request->file('image')->store('uploads', 'public'); + $product->image = $path; + } + + // 4) 저장 + $product->save(); + + // 5) 결과 응답 + return redirect() + ->route('product') + ->with('success', '상품정보가 수정되었습니다.'); + } + + public function destroy($id) + { + // 1. 모델을 먼저 가져온다 + $product = Product::findOrFail($id); + + // 2. 이미지가 있는 경우만 삭제 + if (!empty($product->image)) { + Storage::disk('public')->delete($product->image); + } + + // 3. DB 레코드 삭제 + $product->delete(); + + // 4. 목록으로 이동 + return redirect() + ->route('product') + ->with('success', '상품이 삭제되었습니다.'); + } + + public function store(Request $request) + { + // 1) 검증 + 중복 검사 + 사용자 정의 메시지 + $data = $request->validate([ + 'name' => ['required', 'string', 'max:100'], + 'sku' => ['required', 'string', 'max:255', 'unique:products,sku'], + 'quantity' => ['required', 'numeric', 'min:0'], + 'price' => ['required', 'numeric', 'min:0'], + 'image' => ['nullable', 'image', 'max:2048'], + ], [ + // name + 'name.required' => '상품명은 필수 입력 항목입니다.', + 'name.unique' => '이미 등록된 상품명입니다.', + + // sku + 'sku.required' => 'SKU는 필수 입력 항목입니다.', + 'sku.string' => 'SKU는 문자열이어야 합니다.', + 'sku.unique' => '이미 등록된 SKU입니다.', + + // quantity + 'quantity.required' => '수량은 필수 입력 항목입니다.', + 'quantity.numeric' => '수량은 숫자여야 합니다.', + 'quantity.min' => '수량은 0 이상이어야 합니다.', + + // price + 'price.required' => '가격은 필수 입력 항목입니다.', + 'price.numeric' => '가격은 숫자여야 합니다.', + 'price.min' => '가격은 0 이상이어야 합니다.', + + // image + 'image.image' => '업로드된 파일은 이미지여야 합니다.', + 'image.max' => '이미지 파일 크기는 2MB 이하만 가능합니다.', + ]); + + // 2) 이미지 업로드 처리 + if ($request->hasFile('image')) { + $data['image'] = $request->file('image')->store('products', 'public'); + // 저장 경로 예: storage/app/public/products/파일명.jpg + } + + // 3) DB 저장 + Product::create($data); + + // 4) 등록 후 리다이렉트 + return redirect() + ->route('product.input') + ->with('success', '상품이 등록되었습니다.'); + } +} diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php new file mode 100644 index 0000000..1935070 --- /dev/null +++ b/app/Http/Middleware/Authenticate.php @@ -0,0 +1,21 @@ +expectsJson()) { + return route('login'); // routes/web.php 에서 name('login') 사용 중 + } + + return null; + } +} + diff --git a/app/Models/Product.php b/app/Models/Product.php new file mode 100644 index 0000000..14042a9 --- /dev/null +++ b/app/Models/Product.php @@ -0,0 +1,16 @@ +paginate(1); + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 452e6b6..9aa3fe9 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -3,6 +3,7 @@ namespace App\Providers; use Illuminate\Support\ServiceProvider; +use Illuminate\Pagination\Paginator; class AppServiceProvider extends ServiceProvider { @@ -19,6 +20,6 @@ class AppServiceProvider extends ServiceProvider */ public function boot(): void { - // + Paginator::useBootstrap(); } } diff --git a/bootstrap/app.php b/bootstrap/app.php index c183276..c5c7a1e 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -11,7 +11,7 @@ return Application::configure(basePath: dirname(__DIR__)) health: '/up', ) ->withMiddleware(function (Middleware $middleware): void { - // + // 커스텀 alias 전부 제거 – 비워둠 }) ->withExceptions(function (Exceptions $exceptions): void { // diff --git a/config/app.php b/config/app.php index 423eed5..3adeaa5 100644 --- a/config/app.php +++ b/config/app.php @@ -65,7 +65,7 @@ return [ | */ - 'timezone' => 'UTC', + 'timezone' => 'Asia/Seoul', /* |-------------------------------------------------------------------------- diff --git a/database/migrations/2025_12_06_032222_create_products_table.php b/database/migrations/2025_12_06_032222_create_products_table.php new file mode 100644 index 0000000..9083db6 --- /dev/null +++ b/database/migrations/2025_12_06_032222_create_products_table.php @@ -0,0 +1,39 @@ +id(); // 기본 PK, auto_increment + + $table->string('name', 100)->comment('상품명'); + + $table->string('sku') + ->unique() + ->comment('SKU'); + + $table->integer('quantity') + ->default(0) + ->comment('수량'); + + $table->integer('price') + ->comment('가격'); + + $table->string('image') + ->nullable() + ->comment('상품이미지'); + + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('products'); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 6b901f8..7cf3156 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -5,6 +5,7 @@ namespace Database\Seeders; use App\Models\User; use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; +use Illuminate\Support\Facades\Hash; class DatabaseSeeder extends Seeder { @@ -18,8 +19,9 @@ class DatabaseSeeder extends Seeder // User::factory(10)->create(); User::factory()->create([ - 'name' => 'Test User', - 'email' => 'test@example.com', + 'name' => 'sokuree', + 'email' => 'sokuree@sokuree.com', + 'password' => Hash::make('password') ]); } } diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php new file mode 100644 index 0000000..6f08ce2 --- /dev/null +++ b/resources/views/dashboard.blade.php @@ -0,0 +1,66 @@ + @extends('layout') + + @section('main') + {{-- 페이지 제목 --}} +
00
+000
+| 상품명 | +유형 | +수량 | +날짜 | +
|---|---|---|---|
| 상품명1 | +입고 | +00 | +YYYY-mm-dd HH:ii:ss | +
| 상품명2 | +출고 | +00 | +YYYY-mm-dd HH:ii:ss | +