using (
created_at > (current_timestamp - interval '1 day')
);
using (
created_at > (current_timestamp - interval '1 day')
);
postgres 에서는 RLS (Row Level Security)를 지원합니다.
서버사이드에서 auth정보를 이용해서 접근 권한을 설정할 수 있는 기능입니다.
DB의 policies 를 살펴보면, 사용자에 따라 접근권한을 설정할때, auth.uid() 같은 함수를 이용해서 현 사용자만 접근 가능하도록 할 수 있죠.
그런데 만약 같은 팀원만 볼수 있게 하고 싶다면, 음.. 일단 RLS에서 team에 대한 table join 해서 팀에 소속되어있는지 확인해야 합니다.
(예제에도 그렇게 되어있죠)
그런데 table join이 들어간 코드와 아닌코드의 성능 차이기 약 99% 차이가 난다고 예제에 나와있어요.
table join이 빠지면 99% 성능 향상 ..
그런데 만약 auth token에 추가정보를 담고, RLS에서 auth token에 있는 정보를 활용해서 필터링 할 수 있다고 한다면, (가정입니다. 지금 시도해보고 있음), ㅅa
table join없이 처리가 가능합니다. 즉 향상된 성능으로 처리할 수 있다는 것이죠.
0. custom_jwt_token_hook 추가하기
1. custom jwt를 통해서 token에 추가정보가 들어간것 확인했습니다.
=> 정상적으로 설정이 완료 되면 token에 정보가 들어간 것을 볼 수 있다.
2. 서버에서 RLS 로직에서 추가정보를 꺼내올 수 있는지 확인 필요.
=> 확인 중인 사항
(select auth.jwt() -> 'app_metadata' -> 'teams')
아래처럼 org_id 값을 비교하고 싶을때, org_id가 int8 (숫자)인경우라면, 숫자를 문자로 변경해서 비교 해야함.
ALTER POLICY "Enable read access for same org"
ON "public"."devices"
TO authenticated
USING (
(auth.jwt() ->> 'org_id')::text = org_id::text
);
3.테스트를 위해서 local pc에서 custom jwt 를 테스트 해 볼 수 있는 환경 구축..
가이드에 이렇게 적혀있음
# This hook runs before a token is issued and allows you to add additional claims based on the authentication method used.
# [auth.hook.custom_access_token]
# enabled = true
# uri = "pg-functions://<database>/<schema>/<hook_name>"
config.toml 에 다음 과 같이 내용 추가.
[auth.hook.custom_access_token]
enabled = true
uri = "pg-functions://postgres/private/custom_jwt_token_hook"
=> 정상 동작 하는것 확인
이것을 시도 해보겠습니다. !!
!해피 코딩
[supabase] postgres plv8 활성화 (0) | 2025.01.24 |
---|---|
[Supabase] Edge function 만들기 (0) | 2024.12.11 |
[Flutter] supabase database 의 json 내부 query하기 (0) | 2024.09.19 |
Supabase Authentication을 hooking 해서 사용해야 하는 경우가 있습니다.
이때 sql query 함수를 개발하고 테스트를 보통은 cli를 통해서 진행할텐데, cli에 extension이 활성화 안되어있는 경우 검토가 힘들어지겠죠.
저도 역시 plv8이 활성화 안되어있어서 , 문제였습니다.
해결책은 다음 과 같이 찾아서 정리해봤습니다.
1. supabase에 기본적으로 extension으로 plv8이 있는 거 같음.
2. 그래서 이를 활성화 시키는 방법을 찾아서 실행.
3. 활성화 된것 확인
extension 활성화
CREATE EXTENSION plv8;
설치 확인
SELECT * FROM pg_available_extensions WHERE name = 'plv8';
코드 작성...
create or replace function custom_jwt_token_hook(event jsonb)
returns jsonb
language plv8
as $$
var org_id, role, org_depart_id;
-- Fetch the current user's level from the profiles table
var result = plv8.execute("select org_id, role, org_depart_id from public.user_profiles where user_id = $1", [event.user_id]);
if (result.length > 0) {
org_id = result[0].org_id;
org_depart_id = result[0].org_depart_id;
role = result[0].role;
} else {
org_id = 0;
org_depart_id = 0;
role = '';
}
-- Check if 'claims' exists in the event object; if not, initialize it
if (!event.claims) {
event.claims = {};
}
-- Update the level in the claims
event.claims.org_id = org_id;
event.claims.org_depart_id = org_depart_id;
event.claims.role = role;
return event;
$$;
grant all
on table public.user_profiles
to supabase_auth_admin;
revoke all
on table public.user_profiles
from authenticated, anon, public;
[supabase] auth hooking (0) | 2025.01.24 |
---|---|
[Supabase] Edge function 만들기 (0) | 2024.12.11 |
[Flutter] supabase database 의 json 내부 query하기 (0) | 2024.09.19 |
NginX 를 현재 서버에 설치해서 사용하는 경우, 서버의 이곳 저곳에 nginx 관련 정보들이 흩어져 있게 됩니다.
Docker로 설정할때 어려운 부분들이죠.
이를 한번 정리해보고자합니다.
0. 우선 nginx container를 하나 생성
docker run -d --name tmp-nginx nginx
docker cp 명령어를 사용하여 container의 /etc/nginx/nginx.conf 파일을 현재 프로젝트 폴더의 ./nginx/nginx.conf 로 옮긴다.
1. nginx.conf
docker cp tmp-nginx:/etc/nginx/nginx.conf ./nginx/nginx.conf
2. config.d
docker cp tmp-nginx:/etc/nginx/conf.d ./nginx
docker-compose 파일 작성하기
services:
api_gateway:
image: nginx
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/conf.d/:/etc/nginx/conf.d
ports:
- 8080:80
mywebserver:
build: ./mywebserver
myapiserver:
build: ./myapiserver
docker desktop을 설치하고 나서 docker-compose 를 찾으려고 하면 안찾아져요.
이유는 path가 안걸려 있어서 그래요.
mac에서 보면 docker와 compose 의 위치가 상이합니다. (왜 이렇게 해놨는지는... 저도잘..)
docker: /Applications/Docker.app/Contents/Resources/bin/docker
docker-compose: /Applications/Docker.app/Contents/Resources/cli-plugins/docker-compose
아무튼 그래서 docker-compose를 terminal에서 실행하려면, PATH에 추가해주는 것이 좋겠죠.
export PATH="~~~~:/Applications/Docker.app/Contents/Resources/bin:/Applications/Docker.app/Contents/Resources/cli-plugins:$PATH"
windows나 linux는 또다른 위치 입니다.
[Ubuntu]
docker-compose: usr/lib/docker/cli-plugins/docker-compose
docker: /usr/bin/docker /usr/lib/docker /etc/docker /usr/local/bin/docker /usr/libexec/docker
RUN npm run build || cat npm-debug.log
[React] Components (0) | 2024.09.07 |
---|---|
[AWS] 여러가지 알아야 할것들 (3) | 2024.07.12 |
Docker 간단히 이해하기 (0) | 2024.07.11 |
[Docker] proxy 문제 해결 (0) | 2021.11.11 |
REST API (0) | 2017.06.20 |
Edge Function 은 Supabase 기반의 serverless api 솔루션입니다.
AWS Lambda 나 Google Cloud Function 과 같은 서비스라고 보시면 됩니다.
EdgeFunction 을 어떻게 개발하지? 코드를 작성해서 클라우드에 올리나? 그럼 코드는 어떻게 확인하지.. 와 같은 의문이 들건데요.
간단히 결론부터 말하면 supabase 에서 cli(command line interface) 기반으로 Edge Function 을 개발할 수 있는 환경을 제공합니다.
이를 이용해서 로컬에서 개발하고 테스트 해볼 수 있습니다.
EdgeFunction을 작성하기 위해서는 PC에 개발 환경을 먼저 구축 해야 겠죠?
아래 가이드를 살펴보면서 작성하시면 좋겠습니다.
https://supabase.com/docs/guides/local-development?queryGroups=package-manager&package-manager=npm
CLI install 하고 실행하기
1. install
npm install supabase --save-dev
2. init
npx supabase init
3. start
npx supabase start
//brew 에서는
1. install
brew install supabase/tap/supabase
2. init
supabase init
3. start
supabase start
[결과 화면]
API URL: http://127.0.0.1:54321
GraphQL URL: http://127.0.0.1:54321/graphql/v1
S3 Storage URL: http://127.0.0.1:54321/storage/v1/s3
DB URL: postgresql://postgres:postgres@127.0.0.1:54322/postgres
Studio URL: http://127.0.0.1:54323
Inbucket URL: http://127.0.0.1:54324
JWT secret: super-secret-jwt-token-with-at-least-32-characters-long
anon key: ----dfasdfasdf----------
service_role key: -----asdfasdxx---------
S3 Access Key: ----asdf90as8d0f98asdf--------
S3 Secret Key: -----234hjk35h2k34j5h--------
S3 Region: local
npx supabase link --project-ref [project name]
첫 로그인을 시도하려고 하면 아래와 같은 에러가 발생합니다.
~/supabase$ npx supabse login
npm ERR! code E404
npm ERR! 404 Not Found - GET https://registry.npmjs.org/supabse - Not found
npm ERR! 404
npm ERR! 404 'supabse@*' is not in this registry.
npm ERR! 404
npm ERR! 404 Note that you can also install from a
npm ERR! 404 tarball, folder, http url, or git url.
npm ERR! A complete log of this run can be found in:
npm ERR! /home/netplant/.npm/_logs/2024-12-16T07_31_12_169Z-debug-0.log
netplant@netplant01:~/supabase$ npx supabase login
Hello from Supabase! Press Enter to open browser and login automatically.
엔터를 치면,
브라우저가 뜨고 supabse 에 로그인 화면이 나타납니다.
로그인을 합니다.
supabase verify 코드가 보이게 됩니다.
- Enter your verification code: f16d4f38
함수를 하나 만들어보자.
Edge function을 개발하기 위해서는 Cli를 이용해서 Edge Function을 만들어 줍니다.
supabase functions new my-function --no-verify-jwt
또는
npx supabase functions new my-function --no-verify-jwt
테스트를 위한 것이니 --no-verify-jwt 를 옵션으로 입력했습니다.
그러면 간단한 샘플코드와 함께 함수 폴더가 만들어 집니다.
supabase 밑에 functions 라는 폴더가 생기고, 그 아래 my-function이라는 폴더가 만들어 지죠.
폴더 위치 : supabase/functions/my-function
생성된 예제 코드
import "jsr:@supabase/functions-js/edge-runtime.d.ts"
console.log("Hello from Functions!")
Deno.serve(async (req) => {
const { name } = await req.json()
const data = {
message: `Hello ${name}!`,
}
return new Response(
JSON.stringify(data),
{ headers: { "Content-Type": "application/json" } },
)
})
/* To invoke locally:
1. Run `supabase start` (see: https://supabase.com/docs/reference/cli/supabase-start)
2. Make an HTTP request:
curl -i --location --request POST 'http://127.0.0.1:54321/functions/v1/my-function' \
--header 'Authorization: Bearer -sdjfklsdfjskldf-sdlfkasm23324l234dsdfk' \
--header 'Content-Type: application/json' \
--data '{"name":"Functions"}'
*/
함수에 대한 설정은 config.toml 파일에서 설정 할 수도 있습니다.
이렇게 코드가 만들어지고 나면 그다음은 함수를 테스트 해봐야 하는데요.
테스트를 위해서 함수를 제공하는 서비스를 실행합니다.
supabase functions serve my-function --no-verify-jwt
또는
npx supabase functions serve my-function --no-verify-jwt
그리고 나서 함수를 테스트 하기 위해서 샘플 코드에 주석으로 적혀있는 curl 명령을 실행해봅니다.
curl -i --location --request POST 'http://127.0.0.1:54321/functions/v1/my-function' \
--header 'Authorization: Bearer -sdjfklsdfjskldf-sdlfkasm23324l234dsdfk' \
--header 'Content-Type: application/json' \
--data '{"name":"Functions"}'
이렇게 해서 결과가 나오면 api 를 개발할 수 있는 환경이 완성 된것입니다.
함수 개발을 끝내고 나면, 서버에 올리는 일이 남았습니다.
//전체 함수를 서버에 올리기
supabase functions deploy
// 한 함수만 올리기
supabase functions deploy my-function
//JWT 토큰 없이 동작할수 있도록 올리기
supabase functions deploy hello-world --no-verify-jwt
이렇게 하면 새로 만든 함수가 supabase 에 등록이 됩니다.
이제 부터는 실제로 개발할때 유용한 몇가지 팁들을 같이 적어볼까 합니다.
[환경 변수 파일 .env]
일명 .env 또는 dotenv 라고 부르는 환경 변수 설정을 하는 방법입니다.
api key 또는 token 등 서비스를 구동할때 필요한 인증 키나 값들은 보통 소스코드에서 분리해서 사용하죠.
이럴때 일반적으로 .env를 활용하게 됩니다.
//env 사용하기
Deno.env.get('MY_SECRET_NAME')
그럼 어떻게 확인해 볼 수 있느냐?
supabase functions 서비스를 실행할때 env 파일을 설정해서 테스트 해볼 수 있습니다.
supabase functions serve --env-file ./supabase/.env
[시크릿 키 Secret]
Supabase 서버에서는 .env 파일을 그대로 사용할수 없고 ,supabase 에는 secret key를 관리하는 page에서 관리되는 키를 사용해야 합니다.
supabase site -> Project Settings -> Edge Functions -> Edge Function Secrets Management
가 바로 키를 관리하는 곳입니다.
CLI에 .env에 등록된 키들을 올리는 기능이 있으니 이를 활용하면 쉽게 올리고 테스트 가능하겠죠.
#.env에 있는 모든 키를 올릴때
supabase secrets set --env-file ./supabase/.env
#특정 키만 올일때
supabase secrets set MY_NAME=Chewbacca
되도록이면 로컬의 환경이 서버의 환경과 동일(또는 유사)한것이 개발 및 테스트시 예외상황을 줄일 수 있습니다.
그래서 서버의 내용을 로컬에 구축하는 작업을 진행해보겠습니다.
1. 서버의 데이터를 모두 로컬에 복사해오면 좋겠지만 오버해드(서버의 데이터 내용이 많으면 많을 수록 문제가 되겠죠)가 큽니다.
그래서 서버의 구조, 즉 db schema 를 로컬에 동일하게 구축하는 것입니다.
2. 로컬에는 기본적인 테스트 가능한 데이터만 입력합니다.
서버의 일부 데이터만 입력하는 방법입니다.
#해피코딩
[supabase] auth hooking (0) | 2025.01.24 |
---|---|
[supabase] postgres plv8 활성화 (0) | 2025.01.24 |
[Flutter] supabase database 의 json 내부 query하기 (0) | 2024.09.19 |
#Firebase 를 대처할만한 편리하고 저렴하게 사용할 수 있는 backend solution인 supabase 소개는 따로.!
Supabase에서 Database를 생성하고 나서, database 를 사용하는 방법은 다음과 같습니다.
supabase guide 를 보면 쉽게 따라하실수 있습니다.
https://supabase.com/docs/reference/dart/upgrade-guide?queryGroups=version&version=2.0x
지금 하고 싶은 이야기는 이미 supabase를 사용하고 계시는 분들에게 유용한 기능인데요.
database를 만들어 사용하다 보면, 데이터중에 json 으로 데이터를 넣어야 하는 경우가 있습니다.
그런데, json 으로 데이터 컬럼을 추가 할경우, 쿼리 할때 데이터를 가져온다음에 name 이나 type을 비교해야 하는 상당히 불편한 부분이 생깁니다.
다행히도 supabase의 eq() 함수는 json data 내부의 필드값을 접근 할 수 있는 기능을 제공합니다.
https://supabase.com/docs/reference/dart/using-filters
final data = await supabase
.from('test')
.select()
.eq('data->type', 'hello');
그런데 안타깝게도 아래와 같은 에러가 발생 할 수 있습니다.
PostgrestException(message: invalid input syntax for type json, code: 22P02, details: The input string ended unexpectedly., hint: null)
아 !, -> 기능으로 json 내부 접근해서 쿼리 할수 있을 줄 알았는데 동작을 안하네??? 이러고 좌절 하실수 있는데요.
걱정하지 마세요 !
제가 찾아본 자료에서는 json 내부에 접근하는 접근자 몇가지 있는데요.
가이드에 나와있는 접근자로는 오류가 발생하고 다른 접근자를 사용하면 됩니다.
가이드에 적힌 '->' 느ㄴ son형태의 값을 넘기는 것이고, '->>'를 사용하면 text 값을 정상적으로 처리할 수 있습니다.
final data = await supabase
.from('test')
.select()
.eq('data->>type', 'hello');
이렇게 사용하면 정상적으로 eq 가 동작합니다.
OperatorRight Operand TypeDescriptionExampleExample Result
-> | int | Get JSON array element (indexed from zero, negative integers count from the end) | '[{"a":"foo"},{"b":"bar"},{"c":"baz"}]'::json->2 | {"c":"baz"} |
-> | text | Get JSON object field by key | '{"a": {"b":"foo"}}'::json->'a' | {"b":"foo"} |
->> | int | Get JSON array element as text | '[1,2,3]'::json->>2 | 3 |
->> | text | Get JSON object field as text | '{"a":1,"b":2}'::json->>'b' | 2 |
#> | text[] | Get JSON object at specified path | '{"a": {"b":{"c": "foo"}}}'::json#>'{a,b}' | {"c": "foo"} |
#>> | text[] | Get JSON object at specified path as text | '{"a":[1,2,3],"b":[4,5,6]}'::json#>>'{a,2}' | 3 |
https://www.postgresql.org/docs/9.5/functions-json.html
[참고]
#해피코딩
[supabase] auth hooking (0) | 2025.01.24 |
---|---|
[supabase] postgres plv8 활성화 (0) | 2025.01.24 |
[Supabase] Edge function 만들기 (0) | 2024.12.11 |
JSON은 상당히 편리하면서 직관적인 데이터 형식이죠.
그래서 사람들이 많이 사용합니다.
코딩을 하다보면, json을 그대로 사용하기 보다는 보통 필요한 데이터 형을 class로 만들고 이를 json으로 encoding/decoding 하는 방식으로 많이 사용합니다.
그런데 매번 class 만들고 fromJson(), toJson()을 만들어서 사용하는것이 상당히 번거롭고, 실수할 때가 많습니다.
그래서 fromJson, toJson을 구현해주는 모듈들이 있죠.
대표적으로 json_serializable 이 있죠.
이를 활용하면 쉽게 json 변환이 가능한 data class를 정의 할 수 있습니다.
저는 요즘 freezed 라는 모듈을 주로 사용합니다.
freezed 2.5.7
https://pub.dev/packages/freezed
이 freezed 는 json_serialiazble 을 이용하여 data class를 구현해주는 모듈인데요. 제가 이를 사용하게 된 계기는 deep copy 때문이었습니다.
freezed는 fromJson, toJson 뿐만 아니라 copyWith() 함수를 자동으로 구현 해줍니다. 그리고 copyWith() 를 수행할때 멤버들의 copyWith()를 수행해서 전체가 복사가 됩니다.
이것이 하나의 큰 장점이고,
freezed 로 선언된 data class는 내부 데이터를 변경할 수 없습니다.
변경하려면, copyWith()로 특정 데이터만 변경해서 복사해서 사용해야 합니다.
그렇지 않고 변경가능한 class를 만들고 싶다면, @unfreezed를 이용합니다.
간혹 freezed 로 정의한 class의 멤버중에 freezed로 되어있지 않은 class들을 데이터로 포함할 경우가 있습니다.
그런데 간혹, toJson() 이 정상적으로 동작하지 않을때도 있는데요.
이럴때 toJson()이 불리도록 explicitToJson 설정이 필요할때가 있습니다.
@unfreezed
class MyDataModel with _$MyDataModelModel {
@JsonSerializable(explicitToJson: true)
factory MyDataModel({
required int id,
required String name,
DateTime? created_at,
DateTime? updated_at,
required Map<String, Health> data,
bool? is_demo,
})= _MyDataModel;
예를 들면 위와 같이 클래스를 정의한 경우, Health라는 class에 fromJson, toJson 을 구현 해놨다 하더라도, MyDataModel의 toJson()을 호출할때 Health의 toJson()이 호출되지 않습니다.
이유는 자동으로 생성된 코드에 Health의 toJson()을 호출하는 코드가 생성되지 않기 때문인데요.
이런 경우, toJson()이 불리도록, @JsonSerializable(explicitToJson: true) 을 class 밑에 선언해 줍니다.
#해피코딩
[Flutter] 유용한 위젯들 (0) | 2024.07.30 |
---|---|
[Flutter] 문서화 (0) | 2024.06.24 |
[Flutter] 2024년 새해 계획을 위해서 Todo 앱을 만들어 봤습니다. (1) | 2024.01.11 |
[Flutter] ListView 를 SingleChildScrollView 에서 사용하기 (0) | 2023.12.28 |
[Flutter] 빌드시 플렛폼 추가하기 (1) | 2023.12.28 |