본문 바로가기
코드이그나이터와 php7와 mysql

크레이의 라라벨 도전기 #4 - 모델(Model)

※ 이 게시글은 크레이의 IT개발 관련 성장기를 다루고 있습니다. 관련지식이 약간 있어야 이해되실 수 있습니다. 가벼운 마음으로 읽어보시면서 흥미가 생기고 의욕이 생긴다면? 개발자의 자질이 있으신 겁니다 :)

모델(Model)하면 무엇이 떠오르시나요?
아름다운 외모의 여성분이 떠오를수도 있고, 멋진 상품이 떠오를수도 있는데요.

MVC 패턴에서의 모델은 하나의 주제를 대상으로 한 PHP 클래스입니다.
잘 만들어진 모델 클래스는 매우 유용하게 사용할 수 있는데요.
특히 라라벨에는 이런 모델 클래스가 많이 들어 있는 것으로 보입니다.

라라벨에서는 데이터베이스를 다루는 엘로퀀트(Eloquent)라는 모델 클래스가 제공되는데요.
라라벨이 '우아~'한 엔진인 줄 알았는데 이제보니 '우와~'한 엔진이더군요 :)

엘로퀀트에 대해 설명드리기는 아직 사용법이 익숙하지 않아 추후에 다뤄보기로 하고,
오늘은 크레이가 코드이그나이터4에서 다루는 모델 방식으로 진행하도록 하겠습니다.
오늘 단계까지 진행되면 비로서 MVC ( Model - View - Controller ) 패턴이 완성되는 것이지요.

이 게시글은 지난 게시글에서 이어서 내용이 진행됩니다.

https://itadventure.tistory.com/604

 

크레이의 라라벨 도전기#3 - 컨트롤러(Controller)

※ 이 게시글은 크레이의 IT개발 관련 성장기를 다루고 있습니다. 관련지식이 약간 있어야 이해되실 수 있습니다. 가벼운 마음으로 읽어보시면서 흥미가 생기고 의욕이 생긴다면? 개발자의 자질

itadventure.tistory.com


오늘의 결과물은 아래와 같습니다.
목록 페이지에 접속하면 책 목록이 화면에 보이는 것은 동일한데요.

http:IP주소/list

검색어를 입력 후 검색 버튼을 클릭하면

검색단어로 시작되는 책 목록이 검색되어 나열됩니다.

그 외에 책목록을 클릭하면 상세 내역이 보이는 것은 지난 게시글과 동일한데요,
역시나 책 제목과 이미지가 매칭되지 않아 생뚱맞는건 여전합니다 :)


라우터 추가 POST 방식

우선 라우터 소스는 아래와 같이 변경되었습니다.

Route::get('/book/list', 'BookController@list');
Route::post('/book/list', 'BookController@list');
Route::get('/book/{id}', 'BookController@detail');

별반 바뀐건 없지만 아래 코드가 추가되었습니다.

Route::post('/book/list', 'BookController@list');

그러고 보니 2개의 URL이 동일한데 왜 2개를 선언하였을까요?

Route::get('/book/list', 'BookController@list');
Route::post('/book/list', 'BookController@list');

'흠.. 둘 다 /book/list 인데..'

이 것은 URL 주소가 동일해도 GET 요청POST 요청라우터가 다르다고 구분하기 때문인데요.
책 목록 페이지에 처음 접근할 때는 GET 으로 접근하지만.
제목을 검색할 때는 POST 로 접근하기 때문입니다.

/book/list [  GET 요청 ] /book/list [  POST 요청 ]

여기서는 2가지 모두 동일한 BookController@list, 즉 list() 메소드를 사용하게 되어 있으나,
각각 다른 메소드명을 주면, 각각 다른 메소드를 사용합니다.
이 방식은 Node.js와 동일한 개념이라 크레이는 비교적 쉽게 이해되었는데요,
다른 메소드 접근 방식에 대해서는 책 정보 추가, 수정 기능 제작시 다뤄보겠습니다.

컨트롤러 수정

컨트롤러 소스는 아래와 같이 수정되었습니다.
app/Http/Controllers/BookController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use DB;
use App\Book;

class BookController extends Controller
{
	public function list(Request $request){
		return view('book.list', [
			'books'=>Book::list($request),
			'findStr'=>$request->findStr
		]);
	}
	
	public function detail($id){
		return view('book.detail', [ 
			'book'=>Book::get($id)
		]);
	}
}

원래는 이 소스코드에서 직접 SQL문을 실행하는 방식이었으나
해당 부분이 모조리 Book 모델로 옮겨갔습니다.
그 외에 책을 검색하면 검색어를 모델 클래스에 전달해주기 위해 $request 변수가 추가하였고
검색어를 뷰 화면에 보여주기 위해 $findStr 항목을 뷰에 전달해 주도록 변경하였습니다.

'findStr'=>$request->findStr

Book 모델 클래스 추가

그리고 Book ( 책 ) 모델 클래스가 하나 추가되었습니다.
이 클래스 파일이 바로 오늘의 주인공! 모델(Model)이지요.


app/Book.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

use DB;

class Book extends Model
{
    public static function list($request){
		$findStr = $request->input('findStr');
		if($findStr=='')return DB::select("SELECT * FROM books;");
		return DB::select(
			"SELECT * FROM books WHERE bookName like ?;",
			[ $findStr . '%' ]
		);
	}
	
	public static function get($id){
		$result=DB::select(
			"SELECT * FROM books WHERE `id`=? LIMIT 1;", 
			[ $id ]
		);
		if(count($result)==0) die("Book id is error.");
		return $result[0];
	}
}

책 목록 페이지가 꽤 바뀌었는데요, 처음 접근하든 제목을 검색하든 간에 2가지 모두
첫째는 컨트롤러 list() 메소드를 호출했다가,
두번째로 모델의 list() 메소드를 호출합니다.
그리고 최종적으로는 뷰에 그 결과값을 전달하여 책의 목록을 보여주는 것이지요.

BookController::list($request) => ② Book::list($request) => ③ list.blade.php

여기서 컨트롤러는 최소한의 코드로 모델을 호출, 뷰에 넘겨주는 조정자(Controller)로서의 역활만 수행하고,
Book 모델 클래스은 책을 관리하는 부분만 수행합니다.

뷰 수정

책 목록 뷰는 아래와 같이 수정되었습니다.

<html>
<body>
	<style>
	h1{ text-decoration: underline; }
	table { border-collapse: collapse; }
	td { border:1px solid #373737; }
	.book { cursor:pointer; }
	</style>
	
	<script src="//code.jquery.com/jquery-3.2.1.min.js"></script>
	
	<h1>책 목록</h1>
	
	<div>
		<form id='frmFind' action='/book/list' method='POST'>
			@csrf
			제목 검색 : <input type=text name=findStr value="{{ $findStr ?? '' }}">
			<button id="btnFind">검색</button>
			<button id="btnFindReset">검색해제</button>
		</form>
	</div>
	
	<table>
	<tr>
		<td>책제목</td>
		<td>저자</td>
		<td>페이지</td>
		<td>출판사</td>
	</tr>
	@foreach ($books as $book)
		<tr class='book' bookId="{{ $book->id }}">
		<td>{{ $book->bookName }}</td>
		<td>{{ $book->Author }}</td>
		<td>{{ $book->Page }}</td>
		<td>{{ $book->Publisher }}</td>
		</tr>
	@endforeach
	</table>
	
	<script>
	$(".book")
	.mouseover(function(){ $(this).css('background-color', '#f0f0f0'); })
	.mouseout(function(){ $(this).css('background-color', ''); })
	.click(function(){
		location.href='/book/' + $(this).attr('bookId');
	});
	
	$("#btnFind")
	.click(function(){ frmFind.submit(); });
	
	$("#btnFindReset")
	.click(function(){ frmFind.findStr.value=''; frmFind.submit(); });
	
	</script>
</body>
</html>


여기서 책 목록을 검색하는 부분이 아래 소스인데요.

<form id='frmFind' action='/book/list' method='POST'>
    @csrf
    제목 검색 : <input type=text name=findStr value="{{ $findStr ?? '' }}">
    <button id="btnFind">검색</button>
    <button id="btnFindReset">검색해제</button>
</form>

특히 중간에 보이는 @csrf 는 Cross Site Request Forgery ( 교차 사이트 요청 위조 ) 라고 해서
웹 사이트에 전송되는 데이터를 해킹하여 전송하는 것을 말하는데요.
라라벨에서는 @csrf 코드를 넣어주는 것마으로 보안 토큰이라는 것을 삽입하여 웬만한 해킹은 막아 줍니다.

이 항목은 라라벨에서는 선택이 아닌 필수이기 때문에 <form> 태그 안쪽에 무조건 사용해 주어야 하는데요.
넣어주지 않으면 검색과 같은 기능을 작동할 때 아래와 같은 오류 페이지를 만나게 됩니다.

이 때문에 기본적으로는 POSTMAN 같은 툴로 테스트를 할 수도 없는 문제가 있지요.

개발중 테스트 목적이 있는 경우 이 설정을 잠시 끌 수 있습니다.
app/Http/Middleware/VerifyCsrfToken.php 소스 코드를 열어

아래 코드를

protected $except = [
    //
];

아래와 같이 수정하면  POSTMAN 에서도 테스트가 가능합니다.

protected $except = [
    '*'
];

 


모델 클래스에 대해 알아보고 사용한 부분을 공유드렸는데요. 필요하신 분에게 도움이 되셨을지 모르겠습니다.
오늘도 방문해주신 모든 분들께 감사드리며, 크레이의 IT기술 성장기는 계속됩니다~ :)