ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Laravel, 리퀘스트에서 불필요한 필드가 있으면 자동으로 반려하기
    문제해결 2023. 9. 27. 18:13

    서버의 요청 처리 과정

    서버가 받은 요청을 처리하는 과정은 다음과 같다.

     

    검증 -> 추출 -> 정제 -> 로직 -> 응답

     

    집중해서 보고 싶은 부분은 검증과 추출의 일부분이다. 검증은 서버에 들어오는 요청에 API 스펙으로 정한 필드의 유무와 필드가 있다면 타입 등의 제약 사항을 제대로 지키는지 확인하는 데 사용된다. 추출은 검증 후에 로직에 맞게 필요한 데이터를 가져오는 과정이다. 라라벨은 검증에 필요한 도구를 제공한다. 밸리데이터, 폼리퀘스트 등이 있을 것이다. 그러나, 일반적인 상황에서 벗어난 것들은 직접 만들어줘야 한다. 이 글에서 다룰 내용은 요청 안에 스펙에 없는 필드가 왔을 때 자동으로 반려하는 것이다.

    검증할 요소들

    요청에서 검증할 요소들은 다음과 같다.

     

    1. 반드시 존재해야 하는 필드. 라라벨에선 required로 정해둘 수 있다.
    2. 포함할 수도 있고 안 할 수도 있는 필드. required는 없지만 정의는 되어있는 필드이다.
    3. 스펙 외의 필드. 정의되지 않은 필드이다.

    예를 들어, 아래 필드에서 email은 첫 번째에 해당하고, name은 두 번째에 해당한다.

    $rules = [
     'email' => ['required', 'email'],
     'name' => ['string', 'min:2']
    ]

    세 번째는 어떨까? 요청에서 스펙에 정하지 않은 필드가 포함되면 어떻게 처리할까? 처리하지 않으면 문제가 발생할까? 대부분은 문제없다. 추출 과정에서 필요한 것만 가져와서 처리하기 때문이다. 문제가 될만한 부분은 별다른 추출 없이 모든 필드를 통째로 가져와 사용하는 것이다. 하지만, 이 부분도 큰 문제는 아니다. 이 경우엔 라라벨의 엘로퀀트 ORM과 모델을 이용하여 레코드를 생성, 수정하는 것이 많을 텐데, 불필요한 필드는 무시되기 때문이다.

     

    그러면 왜 스펙 외의 필드. 정의되지 않은 필드를 반려하는 로직이 필요할까? 대부분의 경우엔 별 문제가 없겠지만, 예상하지 못한 문제가 생길 가능성을 원천적으로 차단하기 위한 것이다. 컨트롤러에 들어오는 요청 필드의 유효성을 컨트롤러 안에서 더 이상 신경 쓰고 싶지 않기 때문이기도 하다.

    리퀘스트에서 불필요한 필드가 있으면 자동으로 반려하기

    라라벨의 검증은 밸리데이터로도 할 수 있고 폼리퀘스트로도 할 수 있다. 밸리데이터는 주로 미들웨어나 컨트롤러 안에서 직접 호출해서 쓰는 편이고, 폼리퀘스트는 컨트롤러의 인자로 설정하여 서비스 컨테이너의 의존성 자동 주입 기능을 사용한다. 인자에 폼리퀘스트를 쓰기만 하면 컨트롤러가 호출되기 전에 요청 검증을 자동으로 진행한다.

     

    폼리퀘스트를 상속받아 authorize와 rules 메소드를 오버라이딩 하여 사용한다. authorize는 지금 목적엔 필요 없으니 제외하면 다음과 같은 코드를 볼 수 있다.

    class ExcludeRequest extends FormRequest
    {
        public function rules(): array
        {
            return [
                'email' => ['email', 'required'],
                'name' => ['string], 'min:2']
            ];
        }
    }

    요청이 컨트롤러에 들어오기 전에 rules 메소드가 호출되어 검증이 이루어지고, 검증에서 에러가 발생하면 컨트롤러가 호출되지 않고 요청이 반려된다.

     

    위 코드의 rules메소드를 보면 알 수 있겠지만, 어떤 것이 포함되어야 하는 지를 검증할 수는 있지만, "그 외에 모든 것들은 거부한다"는 식의 검증은 할 수 없다. 따라서 rules를 통한 검증 이후에 다시 스펙에 정해지지 않은 필드를 제거하는 과정이 필요하다.

     

    이를 위해 필요한 것은 다음과 같다.

     

    1. 검증 후에 자동으로 어떤 로직을 호출하는 과정이 있고, 우리가 그 과정에 개입할 수 있어야 한다.
    2. 요청의 필드와 검증에 규칙을 얻을 수 있어야 한다.
    3. 이 둘을 비교하여 문제가 발생하면, 에러를 생성하고 요청이 반려되어야 한다.
    4. 클래스로 만들어서 재사용

    1. 검증 후에 자동으로 호출되는 로직

    폼리퀘스트에는 검증 후에 자동으로 호출되는 after 메소드를 제공한다. 우리는 이 메소드를 오버라이딩하여 사용한다.

    class ExcludeRequest extends FormRequest
    {
        public function rules(): array
        {
            return [
                'email' => ['email', 'min:2']
            ];
        }
    	
        // 검증 후에 자동 호출
        public function after(): array
        {
            return [
                
            ];
        }
    }

    2. 요청의 필드와 검증 규칙 가져오기

    $this로 접근하여 rules 메소드를 호출하면 검증 규칙으로 쓰인 배열을 가져올 수 있다. 그리고 after 메소드안에서 callable을 담은 배열을 반환할 수 있기에 아래처럼 밸리데이터를 이용하여 요청의 필드를 가져올 수 있다.

        public function after(): array
        {
            return [
                function (Validator $validator) {
                    $fields = $validator->attributes(), 
                    $rules = $this->rules());
                }
            ];
        }

    3. 규칙에 포함되지 않은 필드가 있으면 요청 반려하기

    규칙에 포함되지 않은 필드가 요청에 있는지 확인하기 위해서 규칙과 요청 필드를 비교하면 된다. 이때에, array_diff_key 메소드를 사용할 수 있다. 이 메소드의 첫 번째 인자로 요청의 필드가 담긴 배열을 주고, 두 번째 인자로 규칙이 담긴 배열을 넘기면 규칙에 포함되지 않은 필드만 구별할 수 있다. 그리고 밸리데이터를 이용하여 에러를 발생시킨다.

    public function after(): array
        {
            return [
                function (Validator $validator) {
                    $result = array_diff_key($validator->attributes(), $this->rules());
                    if (!empty($result)) {
                        $validator->errors()->add(
                            'extra_key',
                            'extra key without specification'
                        );
                    }
                }
            ];
        }

    4. 클래스로 만들어서 재사용

    1 ~ 3 과정을 진행하는 ExcludeRequest를 다음과 같이 만들었다. 폼리퀘스트를 상속받아 after 메소드를 재정의한 것이다.

    <?php
    
    namespace App\Http\Requests;
    
    use Illuminate\Foundation\Http\FormRequest;
    use Illuminate\Validation\Validator;
    
    class ExcludeRequest extends FormRequest
    {
        public function after(): array
        {
            return [
                function (Validator $validator) {
                    $result = array_diff_key($validator->attributes(), $this->rules());
                    if (!empty($result)) {
                        $validator->errors()->add(
                            'extra_key',
                            'extra key without specification'
                        );
                    }
                }
            ];
        }
    }

    이 ExcludeRequest를 상속받아 다음과 같이 사용하면 끝이다.

    <?php
    
    namespace App\Http\Requests\Test;
    
    use App\Http\Requests\ExcludeRequest;
    
    class TestRequest extends ExcludeRequest
    {
        public function rules(): array
        {
            return [
                'email' => ['email', 'min:2']
            ];
        }
    }

     

    '문제해결' 카테고리의 다른 글

    Amazon Lightsail에 배포  (1) 2023.12.04
    라라벨 프로젝트 진행중에 문제해결  (0) 2023.07.13
    TOC 만들기  (0) 2023.02.09
    mipsel32 개발 환경 구축  (0) 2021.12.09
    VMWare기반 개발 환경을 호스트 OS로 옮겼다.  (0) 2021.10.21

    댓글

Designed by Tistory.