이번 챕터의 내용은 https://github.com/koeunyeon/ci4/tree/controller-validation 에서 코드를 확인하실 수 있습니다.
웹 프로그래밍의 오랜 격언 중 하나는 "사용자의 입력은 믿지 말 것"입니다. 사용자가 굳이 악의가 있어서라기보다는 사용법이 익숙치 않아서, 혹은 재미로, 어떤 이유로든간에 개발자가 의도한 바와 다르게 작동시킨다는거죠.
그래서 우리는 유효성 검사가 필요합니다. 데이터가 원하지 않은 형식이나 값이 들어왔을 때 처리해야 합니다.
Sample
컨트롤러에 아래의 메소드를 추가합니다.
public function valid(){ // (1)
$input = $this->validate([ // (2)
"name" => [ // (3)
'rules' => 'required|min_length[4]|max_length[10]', // (4)
'errors' => [ // (5)
'required' => '이름이 필요합니다',
'min_length' => '이름은 최소 4글자 이상입니다.',
'max_length' => '이름은 최대 10글자 이하입니다.'
]
],
"age" => [ // (6)
'rules' => 'required|is_natural|less_than[150]', // (7)
'errors' => [ // (8)
'required' => '필수값입니다', // (9)
'is_natural' => "나이는 자연수여야 합니다.",
'less_than' => "정말 150세 이상이신가요?"
]
]
]);
if ($input){ // (10)
return $this->response->setJSON("성공했습니다."); // (11)
}else{
$errors = $this->validator->getErrors();
return // (12)
$this
->response
->setStatusCode(400, "bad parameter") // (13)
->setJSON($errors);
}
}
코드를 확인해 보겠습니다.
(1) 유효성 검사를 할 메소드 valid
를 선언합니다.
(2) 코드이그나이터의 컨트롤러에서 유효성을 검사하기 위해서는 $this->validate
메소드를 사용합니다. 파라미터는 유효성 검사 규칙을 입력받습니다.
validate
메소드는 자동으로 요청 파라미터를 읽어서 유효성 검사 규칙에 맞는지 검사하는 역할을 합니다.
이 메소드는 부모 클래스인 BaseController
에 정의되어 있습니다.
반환값은 성공/실패 여부를 나타내는 true
혹은 false
입니다.
(3) 유효성 검사 규칙은 연관 배열로 이루어집니다. 연관배열의 키는 요청 파라미터의 키와 동일합니다.
(4) 개별 값을 검증하는 규칙은 "rules"
를 키로, 규칙들을 |
로 구분해서 나열합니다. 검증 규칙에 파라미터가 필요하다면 규칙 이름 뒤에 []
로 전달합니다. 규칙은 공백이 없어야 합니다.
null
, false
, ""
(빈 문자열), []
(빈 배열)이면 실패합니다.min_length[4]
는 문자열의 길이가 4보다 작다면 실패한다는 의미입니다.max_length[10]
은 문자열의 길이가 10보다 크다면 실패한다는 의미입니다.(5) errors
는 오류 메세지를 지정합니다. 키는 유효성 검사 규칙입니다.
'required' => '이름이 필요합니다'
는 required
유효성 검사가 실패할 경우 보여질 오류 메세지를 '이름이 필요합니다.'
로 지정한다는 뜻입니다.
파라미터가 있는 유효성 검사 규칙일 경우 파라미터를 제외하고 규칙 이름만으로 오류 메세지 키를 지정해야 합니다. 따라서 'min_length[4]' => '이름은 최소 4글자 이상입니다.'
가 아니라 'min_length' => '이름은 최소 4글자 이상입니다.'
로 오류 메세지를 설정합니다.
(6) 검사할 파라미터의 키를 (3) 처럼 지정하면 됩니다.
(7) age
에 대해 유효성 검사를 열거합니다. (4)가 name
에 대해 유효성 검사 규칙을 열거한 것과 동일한 방법입니다.
(8) age
에 대한 오류 메세지를 지정합니다. (5)가 name
에 대한 오류 메세지를 설정한 것과 같습니다.
(9) age
에 각 항목에 대해 오류 메세지를 설정하는 부분입니다. name
을 (5)에서 설정한 것과 같습니다.
(10) 컨트롤러에서 유효성 검사가 실패하면 $input
변수에 false
가 담깁니다. 성공했을 경우 true
가 됩니다.
(11) 성공한 경우 성공했다는 JSON을 클라이언트에 응답합니다.
(12) 실패했을 경우 HTTP STATUS CODE 400(Bad Request)와 함께 오류 메세지를 리턴합니다. 예제처럼 객체 하나의 메소드를 연속으로 호출하는 것을 메소드 체이닝이라고 부르는데, 객체를 만드는 패턴 중 하나입니다.
(13) 코드이그나이터4에서 응답 상태 코드를 설정하려면 response
객체에 setStatusCode
메소드를 사용하면 됩니다. 첫번째 파라미터는 상태 코드, 두번째 파라미터는 상태 메세지입니다. 두번째 파라미터는 옵셔널 파라미터여서 굳이 설정할 필요는 없지만, 메세지의 정확성을 나타내기 위해 추가적으로 선언했습니다.
이번에는 직접 HTML 폼을 만드는 대신, PHPStorm의 HTTP Request 기능을 이용해서 테스트해 보겠습니다. 간단하게 말하면 HTTP 요청을 브라우저에서 직접 하는 대신 미리 정의된 규칙을 통해 테스트하는 방법입니다.
VSCode를 쓰신다면 REST Client 플러그인을 쓰시면 되고, 그 외의 에디터는 POSTMAN 등을 이용해서 PHPStorm이 없이도 HTTP 클라이언트와 동일한 기능을 사용할 수 있습니다.
이 방법을 사용하면 URL과 요청 파라미터 등을 저장해놓고 사용할 수 있기 때문에 매 번 테스트할 때마다 손으로 직접 데이터를 입력하는 번거로움을 피할 수 있게 됩니다. 보통 데이터만 클라이언트와 교환하는 백엔드 개발에서 많이 사용하는 방법입니다.
PHPStorm탐색기에서 최상위 디렉토리인 sample
을 선택한 후 rest_test
디렉토리를 생성합니다.
이후 sample/rest_test
디렉토리를 우클릭하고 New
-> Http Request
를 선택한 후 valid
라고 입력해서 valid.http
파일을 생성합니다.
Add request 버튼을 클릭하고 POST Parameters body를 차례로 선택합니다. POST 요청을 보낼 수 있는 템플릿이 나옵니다.
POST http://localhost:80/api/item
Content-Type: application/x-www-form-urlencoded
id=99&content=new-element
###
이제 템플릿의 내용을 아래와 같이 변경합니다.
###
# name required
POST http://localhost:8080/sample/valid
Content-Type: application/x-www-form-urlencoded
age=30
변경된 부분은 테스트할 주소 http://localhost:80/api/item => http://localhost:8080/sample/valid 이고, HTTP 본문의 내용이 id=99&content=new-element
=> age=30
로 변경되었습니다.
#
으로 시작하면 주석입니다. 이름 파라미터를 전달하지 않았을 경우 유효성 검사가 실행되는지 확인하는 용도입니다.
###
은 각 요청을 구분합니다. 파일 하나에서 여러개의 요청을 동시에 할 수 있기 때문에 각 요청을 구분하는 구분자로 ###
을 사용합니다.
첫번째 줄은 요청 메소드와 주소를 나타냅니다.
POST http://localhost:8080/sample/valid
첫번째 공백 이후 다음 공백까지는 HTTP 요청 헤더입니다. 예제에서는 Content-Type
을 설정하는 용도로 사용했습니다.
Content-Type: application/x-www-form-urlencoded
공백이 나오고 나면 본문이 시작합니다. POST
파라미터도 GET
파라미터와 동일하게 &
로 각 파라미터를 구분하고, 파라미터의 키와 값은 키=값
형태로 서버에 전달합니다.
age=30
테스트해 봅시다. 줄번호 옆의 초록색 오른쪽 화살표를 클릭한 후 Run on localhost:8080
을 선택합니다.
아래 화면에 결과가 나오는 것을 확인할 수 있습니다.
응답 내용을 살펴보겠습니다.
POST http://localhost:8080/sample/valid
HTTP/1.1 400 bad parameter
Host: localhost:8080
Date: Tue, 19 Jan 2021 12:38:26 GMT
Connection: close
X-Powered-By: PHP/7.4.8
Cache-control: no-store, max-age=0, no-cache
Content-Type: application/json; charset=UTF-8
Debugbar-Time: 1611059906
Debugbar-Link: http://localhost:8080/index.php/?debugbar_time=1611059906
{
"name": "이름이 필요합니다"
}
Response code: 400 (bad parameter); Time: 2156ms; Content length: 27 bytes
첫번째 줄은 우리가 요청한 주소입니다.
POST http://localhost:8080/sample/valid
첫번째 공백 이후 다음 공백까지는 HTTP 응답 헤더입니다.
HTTP/1.1 400 bad parameter
Host: localhost:8080
Date: Tue, 19 Jan 2021 12:38:26 GMT
Connection: close
X-Powered-By: PHP/7.4.8
Cache-control: no-store, max-age=0, no-cache
Content-Type: application/json; charset=UTF-8
Debugbar-Time: 1611059906
Debugbar-Link: http://localhost:8080/index.php/?debugbar_time=1611059906
그리고 HTTP 본문이 시작합니다. HTTP 응답 메세지를 그대로 보여주는 것입니다.
응답 코드가 우리가 설정한 대로 400 bad parameter
임을 확인할 수 있습니다. 추가로 HTTP 응답 바디에 JSON 형식으로 오류 메세지가 출력되었습니다. 우리가 원하는 대로 유효성 검증이 성공했다는 뜻입니다.
{
"name": "이름이 필요합니다"
}
Response code: 400 (bad parameter); Time: 2156ms; Content length: 27 bytes
이번에는 모든 유효성을 검사해 보겠습니다. 기존의 valid.http 파일 하단에 아래 내용을 붙여 넣습니다.
###
# name, age required
POST http://localhost:8080/sample/valid
Content-Type: application/x-www-form-urlencoded
###
# name 최소 길이 검증
POST http://localhost:8080/sample/valid
Content-Type: application/x-www-form-urlencoded
name=hi&age=50
###
# name 최대 길이 검증
POST http://localhost:8080/sample/valid
Content-Type: application/x-www-form-urlencoded
name=abcdefghijklmn&age=50
###
# age 입력 안되었을 경우
POST http://localhost:8080/sample/valid
Content-Type: application/x-www-form-urlencoded
name=hihi
###
# age 자연수인지 검증
POST http://localhost:8080/sample/valid
Content-Type: application/x-www-form-urlencoded
name=abcde&age=-1
###
# age 자연수인지 검증
POST http://localhost:8080/sample/valid
Content-Type: application/x-www-form-urlencoded
name=abcde&age=-5.5
###
# age 150 이상인지 검증
POST http://localhost:8080/sample/valid
Content-Type: application/x-www-form-urlencoded
name=abcde&age=-160
###
# 모두 통과
POST http://localhost:8080/sample/valid
Content-Type: application/x-www-form-urlencoded
name=abcde&age=50
###
기호는 각 요청(request)을 구분합니다. 우리가 만든 valid.http 파일에는 총 9개의 요청이 있습니다. 하나씩 실행시키기는 번거로우므로 Run all requests in file
을 클릭하고 run with no enviorment를 클릭합니다.
잠시 기다리면 http 클라이언트가 개별 요청을 하는 것을 볼 수 있습니다.
결과를 목록으로 확인할 수 있습니다. 목록을 클릭하면 각 요청과 응답을 확인할 수 있습니다.