안녕하세요. 이번 시간에는 sqlalchemy에서 like 연산자를 쓰는 방법을 알아보겠습니다. 하나 조심해야 할 것은, 당연하게도 유저의 입력값을 믿으면 안 된다인데요. 이게 무슨 소리일까 싶습니다. like의 경우 %와 _ 등으로 인해 의도치 않은 결과가 나올 수 있습니다. 입력값 필터링을 하지 않거나 escaping을 하지 않은 경우 문제가 발생할 수도 있다는 의미입니다.

 


  먼저 실습에 사용할 데이터입니다. 테이블 A에는 title이 aa, ba, _a, %a인 것이 있습니다.

 

 그리고 models.py에 정의된 모델 A에 대한 클래스입니다.

 

 저는 like 연산자를 썼습니다. Query parameter로 들어오는 keyword에 대해, 접두어가 keyword인 title을 찾습니다. 무엇이 문제일까요? 정확히 말하면 f"{keyword}%"가 문제입니다. 입력값 필터링을 하지 않고 사용자의 입력을 믿어버리면 절대로 안 됩니다. keyword가 특정한 문자가 들어오지 않는다면 의도대로 동작이 할 겁니다. 예를 들어, keyword에 a를 넣어보겠습니다.

 

  a로 시작하는 "aa"만 나옵니다.

 

 

 문제는 _a인데요. 저 경우 where title like '_a%'로 rendering이 되어 버립니다. like에서 _는 어떠한 character와도 매칭됩니다. 따라서, "aa", "ba", "_a", "%a" 이렇게 4개가 매치되게 됩니다. 당연하게도 %a를 넣어도 의도치 못한 결과가 나오게 됩니다. 왜 그럴까요? 2가지 이유가 있습니다. 유저 입력을 그대로 믿어버린 것. 그리고 특정한 문자가 escaping이 되지 않은 것입니다.

 

 


 어떻게 해야 할까요? 접두 검색의 경우, startswith를 쓰면 됩니다. 그리고 autoescape를 True로 설정하면 됩니다.

 

 이 프로그램을 보겠습니다. A.title.startswith(keyword, autoescape=True)라고 되어 있네요.

 

 

 그러면 _a로 시작하는 결과만 나옵니다. 이는 _가 어떠한 하나의 캐릭터와 매치되는 특수 문자가 아니라, 다른 것으로 랜더링 되어 버렸기 때문입니다. 그리고 %와 _가 나오는 곳에 앞에 /가 붙습니다. 즉, %와 _가 escaping 됩니다. 이는 문서에도 언급되어 있는 부분입니다.

 

 

 쿼리를 출력해 보겠습니다. 그러면 Like :title_1 || '%' escape '/'가 나오네요. 사실 title에 그대로 %가 남아 있었으면 _a만 출력되지않았을 겁니다. 이 말은, 문서에서 언급한 대로 특정한 문자들이 LIKE 쿼리 안에서 escape가 된 상태로 render가 된다는 의미입니다. 접미 검사는 어떻게 하면 될까요?

 

 비슷한 원리로, endswith를 쓰면 됩니다. autoescape를 True로 startswith와 마찬가지로, LIKE 안에 들어가는 param 부분에 포함되어 있는 %와 _를 escaping 하게 됩니다.

 

 어떠한 string이 포함되었는지 검사하기 위해서는 contains를 쓰면 됩니다.