ToyProjects๐Ÿงธ/Dalmuriโญ

[Dalmuri] Supabase๋กœ ๋žœ๋ค ์ฃผ์ œ open API ๋งŒ๋“ค๊ธฐ

JanuDev 2025. 11. 25. 11:20

์ง€๊ธˆ ์ž‘์„ฑํ•˜๊ณ  ์žˆ๋Š” dalmuri ํ”„๋กœ์ ํŠธ ๋‚ด์— ๋žœ๋ค ์ฃผ์ œ๋ฅผ ๋˜์ง€๋ฉด ๊ทธ๊ฒƒ์— ๋Œ€ํ•ด ์ž์œ ๋กญ๊ฒŒ ์ด์•ผ๊ธฐํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค๊ณ  ์žˆ๋‹ค.

์ € ๋ฌธ๊ตฌ์— ๋งค์ผ ๋‹ค๋ฅธ ๋‚ด์šฉ์œผ๋กœ ๋ฐ”๋€Œ๊ณ  ์ž์œ ๋กญ๊ฒŒ ์ด์•ผ๊ธฐํ•˜๋Š”๊ฒƒ์„ ๊ตฌ์„ฑํ•œ๋‹ค.

๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด๋ฅผ ์™ธ๋ถ€์—์„œ ์—ฐ๊ฒฐ ํ›„ DB๋ฅผ ๊ตฌ์ถ•ํ•˜๊ณ  ์‹ถ์—ˆ๋Š”๋ฐ, ์ข‹์€ ์‹œ์Šคํ…œ์„ ์ฐพ๋Š” ์™€์ค‘ Supabase๋ผ๋Š”๊ฑธ ์•Œ๊ฒŒ ๋˜์—ˆ๋‹ค..

Supabase์— ๋Œ€ํ•œ ์„ค๋ช…์€ ์ถ”ํ›„์— ์ž‘์„ฑํ•œ๋‹ค...

 

์–ด์ฐŒ๋ฌ๊ฑด Supabase๋ฅผ ํ†ตํ•ด open API๋ฅผ ๋งŒ๋“ค๊ณ , ๊ทธ open API๋ฅผ ๋‚ด ํ”„๋กœ์ ํŠธ์— ๋ถˆ๋Ÿฌ ์œ„ ์œ„์ ฏ์— ๋ฌธ๊ตฌ๋ฅผ ๋„์šฐ๋Š” ๋ฐฉ์‹์œผ๋กœ ๋งŒ๋“ค๊ธฐ๋กœ ์ƒ๊ฐํ–ˆ๋‹ค. 

 

๋“ค์–ด๊ฐ€๊ธฐ ์ „์—!
๋จผ์ € Supabase์— ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค๋Š” ๊ฐ€์ • ํ•˜์— ์ž‘์„ฑํ•œ๋‹ค...

1. Supabase์—์„œ TABLE ์ƒ์„ฑ

ํ…Œ์ด๋ธ”์—๋Š” ID, ๋‚ด์šฉ, ๊ทธ๋ฆฌ๊ณ  insert time์ด ๋“ค์–ด๊ฐ€ ์žˆ๋‹ค. ๋žœ๋คํ•œ ์ฃผ์ œ๊ฐ€ ํ•ต์‹ฌ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋งŽ์€ ์ปฌ๋Ÿผ์„ ๋„ฃ์ง„ ์•Š์•˜๋‹ค.

์ €๋Ÿฐ์‹์œผ๋กœ ๋งŒ๋“ค์—ˆ๋‹ค. ์†Œ์žฌ๋Š” ์ฐจ์ฐจ ๋” ์ƒ๊ฐํ•ด๋ณผ ์˜ˆ์ •.

์ฐธ๊ณ ๋กœ ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑํ•  ๋•Œ ๋ฐ˜๋“œ์‹œ RLS์ •์ฑ…์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ‘œ์‹œ๋ฅผ ํ•ด์•ผ ํ•œ๋‹ค!

 

2. ๋ณด์•ˆ ์ •์ฑ… ์„ค์ •

Supabase์˜ ๊ฐ€์žฅ ๋งค๋ ฅ์ ์ธ ์žฅ์ ์œผ๋กœ, ๊ฐ ํ…Œ์ด๋ธ” ๋ณ„๋กœ ์ •์ฑ…์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

open API์˜ ํ•ต์‹ฌ์€ ์‚ฌ์šฉ์ž๊ฐ€ SELECT๋ฌธ๋งŒ ๊ฐ€๋Šฅํ•˜๋„๋ก ํ•ด์•ผ ํ•œ๋‹ค.

API๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์‚ฌ์šฉ์ž๊ฐ€ INSERT, UPDATE, DELETE๋ฅผ ํ•  ์ˆ˜ ์žˆ์œผ๋ฉด ์ ˆ๋Œ€ ์•ˆ๋˜๊ธฐ ๋•Œ๋ฌธ....

๋”ฐ๋ผ์„œ SELECT๋งŒ ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ณ„๋„์˜ ์„ค์ •์„ ์ถ”๊ฐ€ํ•œ๋‹ค.

 

1๏ธโƒฃ Policies ํด๋ฆญ > create Policy ํด๋ฆญ

 

2๏ธโƒฃ SELECT๋งŒ ์ฒดํฌ

 

๋‹ค๋ฅธ๊ฑธ ์„ ํƒํ•˜๋ฉด ์™ธ๋ถ€ ์‚ฌ์šฉ์ž๊ฐ€ INSERT, UPDATE, DELETE๋„ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜๋ฏ€๋กœ ๋ฌด์กฐ๊ฑด SELECT๋งŒ ํ—ˆ์šฉํ•œ๋‹ค.

 

3๏ธโƒฃ VIEW ์ƒ์„ฑ

์–ด์ฐจํ”ผ ์ปจํ…์ธ ๋ฅผ ๋žœ๋ค์œผ๋กœ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒƒ์ด๋ฏ€๋กœ ์‚ฌ์šฉ์ž๋Š” id๋‚˜ inserttime๊ฐ™์€ ๋ถ€๊ฐ€์ ์ธ ์ปฌ๋Ÿผ์€ ํ•„์š”์—†๋‹ค.

๋”ฐ๋ผ์„œ api ํ˜ธ์ถœ ์‹œ ์ปจํ…์ธ ๋งŒ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋„๋ก, ์ฆ‰ RANTO_CONTENT๋งŒ ๊ณต๊ฐœํ•  ์ˆ˜ ์žˆ๋„๋ก VIEW๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. 

 

(1) SQL ์—๋””ํ„ฐ ์˜คํ”ˆ

ํ™”๋ฉด์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ƒ๊ฒจ๋จน์—ˆ๋‹ค

(2) VIEW๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์ฟผ๋ฆฌ๋ฌธ ์ž‘์„ฑ

-- ๋ทฐ๋ฅผ ํ†ตํ•ด ์ปจํ…์ธ  ์ปฌ๋Ÿผ(RANTO_CONTENT)๋งŒ ๊ณต๊ฐœ
CREATE VIEW public."RANDOM_TOPIC_VIEW" 
  WITH (security_invoker = true)
AS
SELECT "RANTO_CONTENT" FROM public."RANDOM_TOPIC";

์ฃผ์˜์ !

PostegreSQL์€ ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ์—„๊ฒฉํžˆ ๊ตฌ๋ถ„ํ•œ๋‹ค. ๋˜ํ•œ ๋ณ„๋„์˜ ํ‘œ์‹œ๊ฐ€ ์—†๋‹ค๋ฉด ๋ฌด์กฐ๊ฑด ์†Œ๋ฌธ์ž๋กœ ์ธ์‹ํ•œ๋‹ค.

public.RANDOM_TOPIC์ด๋ผ๊ณ  ์ž‘์„ฑํ•˜๋ฉด public.random_topic์œผ๋กœ ์ธ์‹ํ•˜๋Š”๋ฐ, RANDOM_TOPIC = random_topic์œผ๋กœ ์ธ์‹์„ ํ•˜์ง€ ๋ชปํ•ด Error: Failed to run sql query: ์™€ ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๋‚˜ํƒ€๋‚ฌ๋‹ค. 

๋”ฐ๋ผ์„œ ๋Œ€๋ฌธ์ž๋กœ ํ…Œ์ด๋ธ”/์ปฌ๋Ÿผ์„ ์ž‘์„ฑํ•œ ๊ฒฝ์šฐ ๋ฐ˜๋“œ์‹œ ํฐ๋”ฐ์˜ดํ‘œ "" ๋ฅผ ์œ„์™€ ๊ฐ™์ด ๋ถ™์ผ ๊ฒƒ!

์ด๊ฑธ ๋ชฐ๋ผ์„œ ์ค‘๊ฐ„์— ์ข€ ํ—ค๋งธ๋‹คใ…‹ใ…‹ใ…Ž

๊ทธ๋ฆฌ๊ณ  Supabase์˜ SQL์€ ์ฃผ์„์„ ํ•˜์ง€ ์•Š์œผ๋ฉด ์Šคํฌ๋ฆฝํŠธ ์ „์ฒด๊ฐ€ ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์—(; ๋‹จ์œ„๋กœ ๋Š์–ด์ง€์ง€ ์•Š๋”๋ผ...?) ๋ฐ˜๋“œ์‹œ ์‹คํ–‰ํ•  ๊ตฌ๋ฌธ๋งŒ ๋‚จ๊ฒจ๋‘๊ณ  ๋‚˜๋จธ์ง€๋Š” ์ฃผ์„ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋„๋ก ํ•œ๋‹ค.

 

์ฐธ๊ณ ๋กœ security_invoker์„ ์„ค์ •ํ•ด์•ผ ํ–ˆ๋Š”๋ฐ, ๊ทธ ์›์ธ์€ ๋ณด์•ˆ ๋•Œ๋ฌธ์ด๋‹ค..

 

โž• SECURITY INVOKER vs SECURITY DEFINER

open API๋Š” ๋ˆ„๊ตฌ๋‚˜ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค. 

  • SECURITY INVOKER(ํ˜ธ์ถœ์ž) : ์‹ค์ œ API ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ์‚ฌ์šฉ์ž์˜ ๊ถŒํ•œ์„ ๊ธฐ์ค€์œผ๋กœ RLS๊ฐ€ ์ ์šฉ๋˜๋ฉฐ. ๊ณต๊ฐœ์šฉ ๋ทฐ๋ผ๋ฉด public ์—ญํ• ์— ๋งž์ถ”์–ด ์•ˆ์ „ํ•˜๊ฒŒ ๊ณต๊ฐœํ•  ์ˆ˜ ์žˆ๋‹ค.
  • SECURITY DEFINER(์ •์˜์ž) : ๋ทฐ ์†Œ์œ ์ž์˜ ๊ถŒํ•œ์„ ๊ธฐ์ค€์œผ๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฐ˜ํ™˜๋˜๋ฉฐ, RLS ์ •์ฑ…์ด ๋ฌด์‹œ๋  ์ˆ˜ ์žˆ์–ด ์™ธ๋ถ€์—์„œ ์ ‘๊ทผํ•˜๋ฉด ์•ˆ๋˜๋Š” ๋ฐ์ดํ„ฐ๊นŒ์ง€ ๋…ธ์ถœ๋  ์ˆ˜ ์žˆ๋‹ค.

๋ˆ„๊ตฌ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ •์ฑ… ๊ถŒํ•œ์„ ์ ์šฉํ• ์ง€ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ธ๋ฐ, ์ฆ‰ ๋ทฐ ์†Œ์œ ์ž์˜ ๊ถŒํ•œ์ด ์ฝ๊ธฐ/์“ฐ๊ธฐ ๋“ฑ์˜ ์ค‘์š”ํ•œ ํŽธ์ง‘์ด ๊ฐ€๋Šฅํ•˜๋‹ค๋ฉด ์™ธ๋ถ€ ์‚ฌ์šฉ์ž๋„ RLS ์ •์ฑ…์ด ๋ฌด์‹œ๋˜์–ด ์ด์ƒํ•˜๊ฒŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ์œ„ํ—˜์ด ์ƒ๊ธฐ๋Š”๊ฑฐ๋‹ค.

๋”ฐ๋ผ์„œ open API๋กœ ์‚ฌ์šฉํ•  ์˜ˆ์ •์—์„œ VIEW๋ฅผ ๋งŒ๋“ค ๋• ๋ฐ˜๋“œ์‹œ 'INVOKER'๋กœ ํ•  ๊ฒƒ!

 

4๏ธโƒฃ ํ”„๋กœ์‹œ์ ธ ์ƒ์„ฑ

์œ„์—์„œ ์ƒ์„ฑํ–ˆ๋˜ RANDOM_TOPIC_VIEW๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋žœ๋ค์œผ๋กœ ํ† ํ”ฝ์„ ๊ฐ€์ ธ์˜ค๋Š” ํ”„๋กœ์‹œ์ ธ๋ฅผ ์ž‘์„ฑํ–ˆ๋‹ค.

CREATE FUNCTION get_random_topic() 
RETURNS "RANDOM_TOPIC_VIEW" 
as 
$$
  SELECT * FROM "RANDOM_TOPIC_VIEW" ORDER BY RANDOM() LIMIT 1;
$$
LANGUAGE sql STABLE;
  • ํ•จ์ˆ˜ ์ด๋ฆ„: get_random_topic
  • ํ•จ์ˆ˜ ์˜† ๊ด„ํ˜ธ()๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋ฐ›๋Š” ์˜์—ญ์ธ๋ฐ, ์ง€๊ธˆ์€ ์—†์œผ๋ฏ€๋กœ ๋น„์›Œ๋‘ 
  • RETURNS "RANDOM_TOPIC_VIEW" : RANDOM_TOPIC_VIEW๋ผ๋Š” VIEW ์ „์ฒด ๊ตฌ์กฐ์™€ ๊ฐ™์€ ํ˜•์‹์œผ๋กœ ๋ฐ˜ํ™˜. RANDOM_TOPIC_VIEW๋Š” RANTO_CONTENT ์ปฌ๋Ÿผ๋งŒ ์žˆ์œผ๋ฏ€๋กœ ๊ทธ๊ฒƒ๋งŒ ๋ฐ˜ํ™˜ํ•  ๊ฒƒ. PostegreSQL์—์„  TABLE์ด๋‚˜ VIEW ์ด๋ฆ„์„ ๋ฐ˜ํ™˜ ํƒ€์ž…์œผ๋กœ ์ง€์ •ํ•˜๋ฉด ๊ทธ ๊ตฌ์กฐ๋Œ€๋กœ ๋ฐ˜ํ™˜๋จ
  • AS $$...$$ : ํ•จ์ˆ˜์˜ ์‹ค์ œ ์ฟผ๋ฆฌ๋ฌธ์„ ๋‹ด๋Š” SQL ๋ธ”๋ก
    • SELECT * FROM "RANDOM_TOPIC_VIEW" : ํ•ด๋‹น ๋ทฐ์—์„œ ์ „์ฒด ์ปฌ๋Ÿผ์„ ์กฐํšŒ(์–ด์ฐจํ”ผ CONTENT๋งŒ ์žˆ์–ด์„œ ์ƒ๊ด€ ์—†์Œ)
    • ORDER BY RANDOM() : ๋žœ๋ค์œผ๋กœ ๋ฐฐ์ •ํ•จ
    • LIMIT 1: 1๊ฐœ์˜ ํ–‰๋งŒ ๊ฐ€์ ธ์˜ด
  • LANGUAGE sql : ์ด ํ•จ์ˆ˜๊ฐ€ SQL ๋ฌธ๋ฒ•์œผ๋กœ ์ž‘์„ฑ๋จ
  • STABLE : ๊ฐ™์€ ํŠธ๋žœ์žญ์…˜์—์„  ๊ฐ™์€ ์ž…๋ ฅ์— ๋Œ€ํ•ด ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค๊ณ  DB๊ฐ€ ํŒ๋‹จ

3. ํ”„๋ก ํŠธ ํ”„๋กœ์ ํŠธ - supabase ์„ค์ •

1๏ธโƒฃ ๋‚ด ํ”„๋ก ํŠธ ํ”„๋กœ์ ํŠธ์— .env ํŒŒ์ผ ์„ค์ •

SUPABASE_URL์— ๋‚ด ํ”„๋กœ์ ํŠธ ์•„์ด๋””(https://๋‚ด ํ”„๋กœ์ ํŠธ ์•„์ด๋””.supabse.co)๋ฅผ ์ž‘์„ฑํ•˜๊ณ 

SUPABASE_ANON_KEY์— Publishable key๋ฅผ ๋ณต๋ถ™์„ ํ•˜๋„๋ก ํ•œ๋‹ค. 

์ฐธ๊ณ ๋กœ ANON = anonymous๋ž€ ๋œป์ด๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ ์‚ฌ์ดํŠธ๋ณ„๋กœ api key๊ฐ’์„ ๋ถ€๋ฅธ ํ˜ธ์นญ์ด ๋‹ค๋ฅด๋‹ค. ์„ค๋ช…์—๋Š” supabase anon key๋ผ๊ณ  ์“ฐ์—ฌ ์žˆ์–ด์„œ ๋„๋Œ€์ฒด ์ด๊ฒŒ ๋ญ” ํ‚ค์ธ๊ณ ... ์ฐพ์•„๋ณด๋‹ˆ ๊ทธ๋ƒฅ publishable key๋ฅผ ๊ฐ–๋‹ค์“ฐ๋ฉด ๋˜๋Š”๊ฑฐ์˜€๋‹คใ…‹

General > Project ID์™€ 

API Keys > Publishable key ๋ฅผ ๊ฐ€์ ธ์™€์„œ ๋ณต๋ถ™์„ ํ•˜๋ฉด๋œ๋‹ค.

 

2๏ธโƒฃ npm install supabase๋ฅผ ์„ค์น˜

๋‚ด ํ”„๋กœ์ ํŠธ๋Š” node.js๋ฅผ ์‚ฌ์šฉํ•˜๋‹ˆ๊นŒ npm์œผ๋กœ supabase๋ฅผ ์„ค์น˜ํ•œ๋‹ค.

 

3๏ธโƒฃ ์ฝ”๋“œ ์ž‘์„ฑ

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ณธ๋‹ค.

async function getRandomTopic () {
  const { data, error } = await supabase.rpc('get_random_topic')
  if(error) console.error(error)
  if(data) {
    console.log(data)
  }
  setTopic(data.RANTO_CONTENT)
}

์ €๋ ‡๊ฒŒ console.log๋ฅผ ํ†ตํ•ด data๋ฅผ ์ถœ๋ ฅํ•˜๋ฉด ์ž˜ ๋‚˜์˜จ๋‹ค!

 

4. ๋ฐฑ ํ”„๋กœ์ ํŠธ - supabase ์„ค์ •, ์—ฐ๊ฒฐ

๋‚ด ๋ฐฑ์€ java+spring์„ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— spring ํ˜•์‹์— ๋”ฐ๋ฅธ๋‹ค.

 

1๏ธโƒฃ ์ธํ…”๋ฆฌ์ œ์ด์— ๋‚ด DB ์—ฐ๊ฒฐ

์ด๊ฑฐ๋•Œ๋ฌธ์— ์ข€ ๋งŽ์ด ์• ๋จน์—ˆ๋Š”๋ฐ ๋ง‰์ƒ ํ•˜๋ฉด ์„ฑ๊ณตํ•  ์ˆ˜ ์žˆ๋‹ค.

์ผ๋‹จ ์ด ํ™”๋ฉด์ด ๋ณด์—ฌ์•ผ ํ•œ๋‹ค. ์ฐธ๊ณ ๋กœ supabase๋Š” PostgreSQL์„ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ํ•ด๋‹น SQL์„ ์„ ํƒํ•œ๋‹ค.

 

2๏ธโƒฃ ๋‚ด supabase DB ์ •๋ณด ์ž…๋ ฅ

์ •๋ณด ์ฐพ๋Š”๊ฒŒ ์ œ์ผ ์• ๋จน์—ˆ๋‹ค. ์ง„์งœ๋กœใ…‹ใ…‹ใ…‹ใ…‹

ํ•œ๊ตญ์–ด ์„ค๋ช…์€ ์™œ์ด๋ฆฌ ์—†์„๊นŒ

 

(1) connect ํด๋ฆญ

(2) Method: Sesson pooler ํด๋ฆญ

Session pooler๋กœ ํด๋ฆญํ•˜๊ณ  view parameters๋ฅผ ํด๋ฆญํ•˜๋ฉด ์ธํ…”๋ฆฌ์ œ์ด properties์— ๋ฌด์Šจ ์ •๋ณด๋ฅผ ์ž…๋ ฅํ•ด์•ผ ํ•˜๋Š”์ง€ ๋œฌ๋‹ค.

์ฐธ๊ณ ๋กœ database๋Š” ๋‚ด๊ฐ€ ์ž„์˜๋กœ ์ ์ง€ ๋ง๊ณ  postgres๋ฅผ ๋”ฐ๋ผ ์ ๋Š”๋‹ค. ๋‚œ ๊ดœํžˆ ํ”„๋กœ์ ํŠธ๋ช…์œผ๋กœ ํ–ˆ๋‹ค๊ฐ€ ์• ๋จน์—ˆ๋‹ค..

๊ทธ๋ฆฌ๊ณ  ๊ธฐ๋ณธ ํฌํŠธ๋ฒˆํ˜ธ๋Š” 5432์ด๋‹ค.

 

(3) ์ธํ…”๋ฆฌ์ œ์ด์— ๊ทธ๋Œ€๋กœ ์ž‘์„ฑ, ์—ฐ๊ฒฐ

์—ฐ๊ฒฐ ์„ฑ๊ณต
๊ธฐ์กด์— create, installํ–ˆ๋˜ ํ…Œ์ด๋ธ”๊ณผ row๋“ค

๊ธฐ์กด์— ๋งŒ๋“ค์—ˆ๋˜ ํ…Œ์ด๋ธ”์ด ์ž˜ ๋ณด์ธ๋‹ค. ์˜ค์˜ˆ!

 

๊ทธ ๋’ค๋กœ pom.xml์— ์˜์กด์„ฑ ์ถ”๊ฐ€

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency
<dependency>
	<groupId>org.postgresql</groupId>
	<artifactId>postgresql</artifactId>
</dependency>

์ฐธ๊ณ ๋กœ dependencyManagement์— ์ž‘์„ฑํ•˜๋ฉด ์•ˆ๋จ! ๊ฑฐ๊ธฐ์„œ ์ž‘์„ฑํ–ˆ๋‹ค๊ฐ€ ๊ดœํ•œ ์‹œ๊ฐ„ ๋ณด๋ƒˆ๋‹ค..

pom.xml์— ์ž‘์„ฑํ–ˆ์œผ๋ฉด applicaiton.properties์— ๊ฐœ์ธ jdbc ์—ฐ๊ฒฐ ์ •๋ณด ์ž‘์„ฑ.

๊ทธ๋ฆฌ๊ณ  ์ฝ”๋“œ ์ž‘์„ฑ, ํ•ด๊ฒฐ!

 

3๏ธโƒฃ ์ฝ”๋“œ ์ž‘์„ฑ

 MVCํŒจํ„ด์— ์˜๊ฑฐํ•˜์—ฌ ์ž‘์„ฑํ•˜์˜€๋‹ค. ์—ฌ๊ธฐ์„  controller, service๋งŒ ๊ณต๊ฐœ

 

(1) controller

public class RandomTopicController {

    public final RandomTopicService service;

    @GetMapping("get-random-topic")
    public RandomTopicResponseDTO getRandomTopic() {
        return service.getRandomTopic();
    } // getRandomTopic

}

 

(2) service

public class RandomTopicService {

    private final JdbcTemplate jdbcTemplate;

    public RandomTopicResponseDTO getRandomTopic () {
        String sql = "SELECT * FROM get_random_topic()";
        Map<String, Object> result = jdbcTemplate.queryForMap(sql);
        String content = (String) result.get("RANTO_CONTENT");

        RandomTopicResponseDTO res = new RandomTopicResponseDTO();
        res.setRantoContent(content);

        return res;
    } // getRandomTopic

}

 

์œ„์™€ ๊ฐ™์ด ์ž‘์„ฑํ•˜๋ฉด ํ˜ธ์ถœ ์‹œ ์ •์ƒ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์˜จ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค. 

 5. Supabase Edge Function์œผ๋กœ openAPI๋ฅผ ๋งŒ๋“ค๊ธฐ

์ด์ œ localhost๊ฐ€ ์•„๋‹Œ ๋ชจ๋“  ์ปดํ“จํ„ฐ์—์„œ api๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋„๋ก ์ž‘์—…์„ ํ•œ๋‹ค.

๊ทธ๋Ÿฌ๊ธฐ ์œ„ํ•ด์„  ๋˜ scoop์„ ๊น”์•„์•ผ ํ•˜๋Š”๋ฐ... ์™œ๋ƒ๋ฉด ๋”์ด์ƒ supabase๊ฐ€ npm install -g supabase๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค ใ…Ž....

์„ค์น˜ํ•˜๊ธฐ ๊ทธ ์ „์—

C:\Dalmuri
โ”‚
โ”œโ”€ src/                   ← React ์†Œ์Šค
โ”œโ”€ public/
โ”œโ”€ package.json
โ””โ”€ supabase/              ← Supabase Edge Function ์ „์šฉ
    โ”œโ”€ config.toml
    โ””โ”€ functions/
        โ””โ”€ get-random-topic/
            โ”œโ”€ index.ts
            โ””โ”€ supabase.function.toml

์ด๋Ÿฐ์‹์œผ๋กœ ๋‚˜๋ˆ ์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— supabase๋ฅผ ์œ„ํ•œ ํด๋”๋ฅผ ๋”ฐ๋กœ ํŒ๋‹ค.

 

(1) scoop ์„ค์น˜

Invoke-RestMethod https://get.scoop.sh | Invoke-Expression

 

(2) Supabase Edge Function ์„ค์น˜

scoop bucket add supabase https://github.com/supabase/scoop-bucket.git
scoop install supabase

 

(3) Supabase CLI์— ๋กœ๊ทธ์ธ

supabase login

๋ผ๊ณ  ์น˜๋ฉด ๋‹ค์Œ ํ™”๋ฉด์ด ๋‚˜์˜ค๋Š”๋ฐ,

์ €๊ธฐ์„œ ๋‚˜์˜ค๋Š” ์ฝ”๋“œ๋ฅผ powershell์ฐฝ์— ๋ณต๋ถ™ํ•œ๋‹ค.

๊ทธ๋Ÿฌ๋ฉด ๋กœ๊ทธ์ธ์ด ๋˜๋ฉด์„œ

์ด๋”ด๊ฒŒ ๋œฌ๋‹ค. ์ง€๊ธˆ ๐Ÿถ๊ณ ์ƒํ•˜๋ฉด์„œ ์ฝ”๋”ฉํ•˜๊ณ  ์žˆ๋Š”๋ฐ ์žฅ๋‚œํ•˜๋ƒ?

 

(4) ํ‚ค๊ฐ’ ๋“ฑ๋ก

ํ”„๋ก ํŠธ๋•Œ .env์— ํ‚ค๊ฐ’์„ ์ถ”๊ฐ€ํ–ˆ๋˜ ๊ฒƒ๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋“ฑ๋กํ•œ๋‹ค.

supabase secrets set SUPABASE_URL=https://<URprojectID>.supabase.co
supabase secrets set SUPABASE_ANON_KEY=<your-anon-key>

๋ฅผ ๋“ฑ๋กํ•˜๋Š”๋ฐ, ๋งŒ์•ฝ

์ด๋Ÿฐ ๋ฅ˜์˜ ์—๋Ÿฌ ๋ฌธ๊ตฌ๊ฐ€ ๋‚˜๋ฉด ์ด๋ฏธ SUPABASE_URL, SUPABASE_ANON_KEY ๊ฐ™์€ ์ด๋ฆ„์ด Edge Functions์—์„œ ์ด๋ฏธ ์‹œ์Šคํ…œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ ๋”ฐ๋กœ secrets๋กœ ๋“ฑ๋กํ•  ํ•„์š” ์—†๋‹ค๋Š” ๋œป์ด๋‹ค.

 

(5) ์ฝ”๋“œ ์ž‘์„ฑ

import { serve } from "https://deno.land/std@0.168.0/http/server.ts"
import { createClient } from "https://esm.sh/@supabase/supabase-js@2.36.0";

serve(async () => {
  // ํ™˜๊ฒฝ๋ณ€์ˆ˜์—์„œ supabase ์ •๋ณด ๊บผ๋‚ด๊ธฐ
  const SUPABASE_URL = Deno.env.get("SUPABASE_URL")
  const SUPABASE_ANON_KEY = Deno.env.get("SUPABASE_ANON_KEY")

  const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY)

  try {
   // ์—ฌ๊ธด ๋ณธ์ธ ์ƒํ™ฉ์— ๋งž๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด ๋œ๋‹ค.
    const { data, error } = await supabase.select .... 
    if (error) console.error(error)
    if(data){
      console.log(data)
    }

    if(!data || data.length === 0) {
      return new Response(JSON.stringify({rantoContent:null}), {
        status: 200,
        headers: {
          'content-type': 'application/json',
          'Access-Control-Allow-Origin': '*', // ๊ณต๊ฐœ ํ—ˆ์šฉ
        },
      })
    }

  } catch (err) {
    console.error(err)
    return new Response(JSON.stringify({rantoContent:null}), {
      status: 500,
      headers: {
        'content-type': 'application/json',
        "Access-Control-Allow-Origin": "*",
      },
    })
  }
})

์ฝ”๋“œ๋ฅผ ์–ด๋–ป๊ฒŒ ์ž‘์„ฑํ• ์ง€๋Š” ๊ฐœ๋ฐœ์ž๋งˆ๋‹ค ๊ฐ๊ฐ ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ์˜ˆ์‹œ๋กœ์„œ ์ €๋ ‡๊ฒŒ ๋‚จ๊ฒจ๋‘”๋‹ค.

 

(6) ๋ฐฐํฌํ•˜๊ธฐ

๋ฐฐํฌ ๋ช…๋ น์–ด๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

supabase functions deploy ๋ฐฐํฌํ•  function ์ด๋ฆ„ --no-verify-jwt

์™œ --no-verify-jwt๋ฅผ ๋ง๋ถ™์˜€๋ƒ๋ฉด...supabase๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ JWT ์ธ์ฆ์„ ์š”๊ตฌํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๊ทผ๋ฐ ์•„๋ฌด๋‚˜ ํ˜ธ์ถœํ•˜๋Š” ๊ณต๊ฐœ api๋กœ ๋งŒ๋“ค๋ ค๋ฉด jwt์ธ์ฆ์„ ๋„๊ฑฐ๋‚˜, anon key๋ฅผ ์‹ฌ์–ด์„œ ํ˜ธ์ถœํ•ด์•ผ ํ•œ๋‹ค.

 

  • ์ธ์ฆ ์ผœ์ง„ ์ƒํƒœ
    • Authorization ํ—ค๋” ํ•„์ˆ˜
    • Supabase ANON_KEY ๋˜๋Š” ๋กœ๊ทธ์ธ ํ† ํฐ ํ•„์š”
    • ์•ˆ ๋ถ™์ด๋ฉด 401 ์—๋Ÿฌ
  • --no-verify-jwt ์‚ฌ์šฉ
    • ์ธ์ฆ ์ฒดํฌ ๋”
    • ๋ˆ„๊ตฌ๋‚˜ URL๋งŒ ์•Œ๋ฉด ํ˜ธ์ถœ ๊ฐ€๋Šฅ
    • ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‰ฝ๊ฒŒ fetch ๊ฐ€๋Šฅ

 

๊ทผ๋ฐ anon-key๋ฅผ ๋ถ™์ด๊ณ  ํ˜ธ์ถœํ•˜๋ฉด ๋‚˜์ค‘์— ๊ดœํžˆ ํ‚ค๊ฐ’ ๋…ธ์ถœ๋ ๊นŒ๋ด ๊ทธ๋ƒฅ jwt ์ธ์ฆ์„ ์—†์ด ๋ถ€๋ฅผ ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •ํ–ˆ๋‹ค.

๋‹จ ๋ฐฐํฌํ•  ๋•Œ ๋งˆ๋‹ค ๋ฐ˜๋“œ์‹œ no verify jwt๋ฅผ ๋ถ™์ผ ๊ฒƒ!

 

6. ๊ฒฐ๊ณผ๋ฌผ!

๋žœ๋ค์œผ๋กœ ์ฃผ์ œ ํ•˜๋‚˜๋ฅผ ๋˜์ง€๋Š” ๊ณต๊ฐœ API๋กœ, ์ธ์ฆ์ด๋‚˜ ํ‚ค ์—†์ด ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.

 

Endpoint

https://llfsxgdcamgbxpwcsnwh.functions.supabase.co/get-random-topic

 

Request 

Method URL Headers Body
GET /get-random-topic ์—†์Œ ์—†์Œ

 

Response

  • 200 ok
[
  {
    "RANTO_CONTENT": "์˜ค๋Š˜ ์ €๋…๋ฉ”๋‰ด ์ถ”์ฒœ!"
  }
]
  • 500 error : null / undefined ๋ฐ˜ํ™˜

CORS ์ •์ฑ…

  • ๋ชจ๋“  ๋„๋ฉ”์ธ์—์„œ ์ ‘๊ทผ ๊ฐ€๋Šฅ (Access-Control-Allow-Origin: *)
  • ํ”„๋ก ํŠธ์—์„œ ๋ฐ”๋กœ fetch ํ˜ธ์ถœ ๊ฐ€๋Šฅ

์˜ˆ์‹œ

async function fetchRandomTopic() {
  const response = await fetch('https://llfsxgdcamgbxpwcsnwh.functions.supabase.co/get-random-topic');
  const data = await response.json();
  console.log(data[0].RANTO_CONTENT);
}

fetchRandomTopic();

์ด๊ฑฐ ๋งŒ๋“œ๋Š”๋ฐ ์™œ์ด๋ฆฌ ํž˜๋“œ๋ƒ...

๊ทธ๋ฆฌ๊ณ  ๊ณ„์† ํ˜ธ์ถœํ•˜์ง€ ๋งˆ๋ผ supabase ๋ฌด๋กœ๋ฒ„์ „์ด๋ผ ์„œ๋ฒ„ ๋ถ€์…”์งˆ๊นŒ ๊ฑฑ์ •๋œ๋‹ค... ๋‚˜๋„ ํ•˜๋ฃจ์— ํ•œ๋ฒˆ ํ˜ธ์ถœํ•˜๋Š”๋ฐ

(๊ทธ๋Ÿด๊ฑฐ๋ฉด ์™œ ์˜คํ”ˆํ•จ?ใ…‹ใ…‹ใ…‹;)

7. ์ค‘๊ฐ„์ค‘๊ฐ„ ์˜ค๋ฅ˜ ์›์ธ

1. RANDOM_TOPIC_VIEW๊ฐ€ ์žˆ๋Š”๋ฐ DB์—์„œ ์ธ์‹์„ ๋ชปํ•œ ๊ฒฝ์šฐ

๐Ÿ‘‰ ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜ ํƒ€์ž…(Returns Type) ๋ถˆ์ผ์น˜. ํ•จ์ˆ˜ ๋ทฐ ๊ตฌ์กฐ์˜ ์—ฌ๋Ÿฌํ–‰(SETOF)์„ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ–ˆ์œผ๋‚˜ ์ดˆ๊ธฐ์— ๋‹จ์ผํ–‰์„ ๋ฐ˜ํ™˜ํ•˜๋Š”๊ฒƒ์ฒ˜๋Ÿผ ์ž‘์„ฑ๋จ. VIEW๋Š” ์—ฌ๋Ÿฌ ํ–‰์„ ๋ฐ˜ํ™˜ํ•˜๋‚˜ ๋‚ด๊ฐ€ ๋‹จ์ผ ํ–‰๋งŒ ๋ฐ˜ํ™˜ํ•œ๋‹ค๊ณ  ์ž˜๋ชป ์„ ์–ธํ–ˆ์Œ

๋”ฐ๋ผ์„œ ๊ธฐ์กด์˜ RETURNS_๋ทฐ_์ด๋ฆ„์—์„œ RETURNS SETOF ๋ทฐ_์ด๋ฆ„์œผ๋กœ ๋ณ€๊ฒฝํ•จ

 

2. [XX000] FATAL: MaxClientsInSessionMode: max clients reached - in Session mode max clients are limited to pool_size

์ด ์—๋Ÿฌ๋Š” connection pool์„ 30์œผ๋กœ ๋Š˜๋ ธ๋”๋‹ˆ ํ•ด๊ฒฐ์€ ํ–ˆ์ง€๋งŒ.. connection pool์— ๋Œ€ํ•ด ๊ณต๋ถ€ํ•ด์•ผ๊ฒ ๋‹ค. ์ด๊ฑด ๋‹ค๋ฅธ ํฌ์ŠคํŠธ์—์„œ!

 

3. scoop์™€ supabase๊ฐ€ ์ƒˆ๋กœ ๋งŒ๋“  ํ”„๋กœ์ ํŠธ์—์„œ ์ธ์‹์ด ๋˜์ง€ ์•Š์•„ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ

์‹œ์Šคํ…œ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ํŽธ์ง‘ > ํ™˜๊ฒฝ ๋ณ€์ˆ˜ > ์ƒˆ๋กœ๋งŒ๋“ค๊ธฐ > path ํด๋ฆญ > scoops\shims๊ฐ€ ์žˆ๋Š” ํŒŒ์ผ์„ ์ถ”๊ฐ€

๊ทผ๋ฐ๋„ ์ธ์‹ ์•ˆ๋˜๋ฉด ๊ป๋‹ค ์ผœ๋ผ. ๋‚œ ๋ฌ๋‹คใ…‹

 

[์ฐธ๊ณ  ์ž๋ฃŒ]

https://rlawo32.tistory.com/entry/SUPABASE-SUPABASE-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0

 

[Supabase] Supabase ์‚ฌ์šฉํ•˜๊ธฐ

1. Supabase๋ž€?Supabase๋Š” BaaS(Backend as a Service)๋กœ ์„œ๋น„์Šคํ˜• ๋ฐฑ์—”๋“œ์ด๋‹ค.ํ”„๋ก ํŠธ์—”๋“œ๋‹จ์—์„œ ๋ฐฑ์—”๋“œ ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•ด ์ฃผ๋Š” ์„œ๋น„์Šค ๋ชจ๋ธ์ด๋‹ค. ์ฆ‰, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ API๋ฅผ ๊ตฌ์ถ•ํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ๋ณต์žกํ•œ ์„ค์ •๊ณผ

rlawo32.tistory.com

 

https://velog.io/@manner9945/supabase-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-6tyu3xcx

 

supabase ์‚ฌ์šฉํ•˜๊ธฐ

๋ฐฑ์—”๋“œ ๊ธฐ๋Šฅ๋“ค์„ ์ง€์›ํ•˜๋Š” ํด๋ผ์šฐ๋“œ ์„œ๋น„์Šค ํ”„๋ก ํŠธ์—”๋“œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋ฐฑ์—”๋“œ ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋ธ PostgreSQL์„ ๊ธฐ๋ฐ˜์œผ๋กœ SQL ์ฟผ๋ฆฌ ๋ฐ ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜

velog.io

https://velog.io/@itzel_02/IntelliJ-DB-%EC%97%B0%EB%8F%99

 

IntelliJ & DB ์—ฐ๋™

๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค์˜ ์„ค์ •์—์„œ ์œ„์˜ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์™€ ํŒŒ์ผ์— ์ž‘์„ฑ์œ„์˜ ์‚ฌ์ง„๊ณผ ๊ฐ™์ด ์—ฐ๊ฒฐ์ด Succeeded๋œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Œ

velog.io

https://like-grapejuice.tistory.com/214

 

PostgreSQL ํ™˜๊ฒฝ์„ค์ • Intellij๋กœ connectํ•˜๊ธฐ + java code๋กœ ์‹คํ–‰ํ•˜๊ธฐ

์ž ์˜ค๋Š˜์€ Intellij IDE๋ฅผ ์ด์šฉํ•ด์„œ postgresql์— connect ํ•˜๊ณ  java code๋กœ ์‚ฌ์šฉํ•˜๋Š” ๋ฒ•์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์ž. ์‚ฌ์šฉํ•˜๋Š” ํ™˜๊ฒฝ์€ Window8, Intellij IDEA Ultimate 2020.3.3 Connect ์ผ๋‹จ java๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋‹น์—ฐํžˆ JDK๊ฐ€ ํ•„์š”

like-grapejuice.tistory.com

https://ai-one.tistory.com/entry/Supabase-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%A0%91%EC%86%8D-%EC%A0%95%EB%B3%B4-%EC%95%88%EB%82%B4

 

IT ๊ฐœ๋ฐœ์ž ๋ธ”๋กœ๊ทธ - Java, Spring Boot, React ๋“ฑ

๋‹ค์–‘ํ•œ ๊ฐœ๋ฐœ ์ง€์‹๊ณผ ๋…ธํ•˜์šฐ๋ฅผ ๊ณต์œ ํ•˜๋Š” ๋ธ”๋กœ๊ทธ | Java, Spring Boot, React, Next.js, NAS, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, ๋ฆฌ๋ˆ…์Šค

ai-one.tistory.com