이전 포스트의 캐싱문제를 해결 후 바로 작업을 이어나가려..... 고했지만
table 생성 후 기존 users 데이터를 Insert하는 과정에서 오류가 발생했다.
NeonDbError: syntax error at or near "$1"
오잉.. 보내는 sql을 콘솔에 찍어봐도 눈씻고 찾아봐도 $1이라는 문자열은 존재하지 않는다.
혹시나 하는 마음에 vercel dashboard UI에서 쿼리를 붙여넣기 해보았더니..
이번엔 데이터가 제대로 들어간다.
그러면 또.. 뭔가.. 문제를 해결해야 하는데 흠..
다시 늘 하던대로 확실한 부분 부터 정리를 하자면,
1. sql 쿼리 자체에는 문제가 없다.
= @vercel/postgres에서 가져온 sql이 쿼리문자열을 뭔가 처리하는 과정에서 생긴 오류로 판단.
관련 검색어로 검색을 해보았는데 명확한 답변은 없었다.
https://github.com/orgs/vercel/discussions/2336
해당 질문을 올린 사항이 문제상황을 제대로 설명해주었는데,
현재 상황을 정리하자면,
"쿼리를 Vercel Postgres UI 대시보드에 넣으면 제대로 작동하지만 sql템플릿 리터럴 태그에서는 작동하지 않음"
하지만 해당 글에서도 명확한 해결책은 나오지 않았다.
챗 GPT한테 물어보았다.
제공해주신 쿼리에서는 사용자가 입력한 데이터를 문자열 보간(interpolation)하여 SQL 쿼리에 넣고 있습니다. 이러한 방식은 SQL Injection 공격에 취약할 수 있습니다. 대신에 사용자 입력을 안전하게 처리하기 위해 파라미터화된 쿼리(prepared statement)를 사용하는 것이 좋습니다. 파라미터화된 쿼리는 사용자 입력을 쿼리에 직접 보간하지 않고, 쿼리의 미리 정의된 위치에 파라미터를 전달하여 SQL Injection 공격을 방지합니다. JavaScript의 PostgreSQL 클라이언트에서는 대부분 파라미터화된 쿼리를 지원합니다.
라면서 쿼리를 파라미터화 시킬것을 추천 해줬고,
시키는대로 values에 들어가는 값을 $1, $2, $3, ... 으로 넣어준 뒤,
values 라는 배열을 만들고 그 안에 순서를 맞춰서 변수들을 집어 넣어 주었다.
그리고 클라이언트가 필요하다기에 @vercel/postgres에서 createClient를 임포트 해온 뒤,
클라이언트를 생성하고, 연결,
try-catch 문 마지막에 finally를 추가하여 연결을 끊어주었다.
그다음 생성된 클라이언트에
client.query(sql, values)로 요청을 보냈더니 제대로 등록되었다.
궁금한건, 왜 때문에 오류가 발생했는지,
그리고 기존 sql`~` 방식으로는 해결 할 수 없는지 ( 굳이 클라이언트를 생성해야 하는지? )인데,
우선 나눠서 생각해보면
$1근처에서 오류가 발생했다고 하는건.. 결국 @vercel/postgres에서도 기본적으로
$1, $2, $3... 이런식으로 뭔가 중간에 처리를 하고 있다는것 같다.
문서를 찾아보니 맞았다.
https://vercel.com/docs/storage/vercel-postgres/sdk#preventing-sql-injections
The SDK sends the query string and the array of parameters to your PostgreSQL server.
This is called a parameterized query, and it's a common pattern in modern JavaScript SQL libraries
SDK는 쿼리 문자열과 매개변수 배열을 PostgreSQL 서버로 보냅니다.
이를 매개변수화된 쿼리 라고 하며 최신 JavaScript SQL 라이브러리의 일반적인 패턴입니다.
에.. 근데 대충 보면.. sql 자체가 일회성 쿼리에서, 스스로 파라미터화를 시켜서 동작하게끔 한다는것 같은데..
왜 동작을 안했던건진 잘 모르겠다. 여튼 뭐 $1 이 잘못되었다고 했던거고,
테스트로 만들었던 name varchar(255)하나만 있는 테이블에서는 실제로 sql도 제대로 동작..을 했었나?..
기억이 잘 안나서 테스트를 직접 해보니,
name하나만 있는 쿼리는 기존 방식대로 해도 정상동작을 했다.
내친김에 하나씩 추가해보면서 언제 오류가 발생하는지를 확인해 보았다.
어... 아...
테스트를 하면서 두가지 알게된 사실이 있는데 엄청난 삽질이였다.
우선 결론만 말하면 기존 sql로도 사용가능하다.
어.. 조금이나마 편하게 하기? 위해서
query를 변수화 시켜서 ``형태로 만들고
sql`${query}`로 추가했었는데,
이걸 인식 못하는것 같다 ㅎ..
query를 그냥 바로 sql에 작성하니까 제대로 작동했다. ㅎ..ㅎㅎ
어쩐지 이런 오류가 난사람이 없다더라니..
왜 저렇게 되는건지는 잘모르겠다.
좀 더 알아봐야겠지만.. 위에 같은 문제가 있는 사람도 보니까 뭔가 어렴풋하게 예측해보자면..
sql템플릿 리터럴 태그에는 문자열 + 변수의 형태만 들어가야 하는듯 하다.
뭔가 매서드를 이용하여 출력한다거나.. 변수로 만들어서 추가한다거나 하면 안되는듯?..
하려면 client.query(query, values)의 형태로 해야 하는듯 하다
결론은 NeonDbError: syntax error at or near "$1" 은
sql템플릿 리터럴 태그의 문법오류?.. 정도로 인식하면 될것 같다.
끝.
2024.04.08 최종 정리
https://frontend-bear.tistory.com/138
포스트에서 태그드 템플릿이라는 개념을 알게 되었다.
$1 오류라는건 즉, sql이라는 함수에서 변수들을 처리하면서 생기는 오륜데,
`hello my name is ${name}, ${age} years old`
라는 문자열이 들어간다면,
string은 ['hello my name is', ', ', ' years old']
values는 [name, age] 두개가 들어가게 되는데,
${sql}의 형태로 넣어버리니
string에는 아무것도 안들어가고,
values에만 sql에서 전달한 템플릿 리터럴이 전달되어 발생하는 문제였다.
위 https://github.com/orgs/vercel/discussions/2336 링크에서의 문제도 결국,
템플릿 리터럴 안에 또 템플릿 리터럴이 들어가 있어 발생하는 이슈인걸로 보인다.