์™ธ๋ถ€ ์•ก์„ธ์Šค ํ†ตํ•ฉ ์ƒ์„ฑ ๋ฐ ์‚ฌ์šฉํ•˜๊ธฐยถ

ํŠน์ • ์™ธ๋ถ€ ๋„คํŠธ์›Œํฌ ์œ„์น˜์— ๋Œ€ํ•œ ์•ก์„ธ์Šค๋ฅผ ํ™œ์„ฑํ™”ํ•˜๊ธฐ ์œ„ํ•ด ์™ธ๋ถ€ ์œ„์น˜๋ฅผ ์ง€์ •ํ•˜๋Š” ๋„คํŠธ์›Œํฌ ๊ทœ์น™ ๋ชฉ๋ก๊ณผ ์‚ฌ์šฉํ•˜๋„๋ก ํ—ˆ์šฉ๋œ ์‹œํฌ๋ฆฟ ๋ชฉ๋ก์„ ์ง€์ •ํ•˜๋Š” ์™ธ๋ถ€ ์•ก์„ธ์Šค ํ†ตํ•ฉ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. CREATE FUNCTION ๋˜๋Š” CREATE PROCEDURE๋กœ UDF ๋˜๋Š” ํ”„๋กœ์‹œ์ €๋ฅผ ์ƒ์„ฑํ•  ๋•Œ EXTERNAL_ACCESS_INTEGRATIONS ์ ˆ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ด ํ†ตํ•ฉ์„ ์ฐธ์กฐํ•จ์œผ๋กœ์จ ์ฒ˜๋ฆฌ๊ธฐ ์ฝ”๋“œ๊ฐ€ ์‹œํฌ๋ฆฟ์„ ์‚ฌ์šฉํ•˜์—ฌ ์™ธ๋ถ€ ์œ„์น˜์— ์ธ์ฆํ•˜๋„๋ก ํ—ˆ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ด€๋ฆฌ์ž๋Š” EXTERNAL_ACCESS_HISTORY ๋ทฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์™ธ๋ถ€ ๋„คํŠธ์›Œํฌ ์œ„์น˜์— ๋Œ€ํ•œ ์š”์ฒญ์„ ๋ชจ๋‹ˆํ„ฐ๋งํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์™ธ๋ถ€ ์•ก์„ธ์Šค๋ฅผ ์„ค์ •ํ•˜๊ณ  ์‚ฌ์šฉํ•˜๋Š” ๋ฐ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ ์˜ˆ์˜ ์ „์ฒด ์‹œํ€€์Šค๋Š” ์™ธ๋ถ€ ๋„คํŠธ์›Œํฌ ์•ก์„ธ์Šค ์˜ˆ์‹œ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

UDF ๋˜๋Š” ํ”„๋กœ์‹œ์ €์—์„œ ์™ธ๋ถ€ ๋„คํŠธ์›Œํฌ ์œ„์น˜์— ๋Œ€ํ•œ ์•ก์„ธ์Šค๋ฅผ ์„ค์ •ํ•˜๋ ค๋ฉด ๋‹ค์Œ ๋‹จ๊ณ„๋ฅผ ๋”ฐ๋ฅด์‹ญ์‹œ์˜ค.

  1. ๊ณต์šฉ ์ธํ„ฐ๋„ท ๋˜๋Š” ๋น„๊ณต๊ฐœ ์—ฐ๊ฒฐ ์„ ์‚ฌ์šฉํ•˜์—ฌ ์™ธ๋ถ€ ๋„คํŠธ์›Œํฌ ์œ„์น˜์— ์—ฐ๊ฒฐํ• ์ง€ ์—ฌ๋ถ€๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.

  2. ์™ธ๋ถ€ ๋„คํŠธ์›Œํฌ ์œ„์น˜๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋„คํŠธ์›Œํฌ ๊ทœ์น™์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

  3. ์ž๊ฒฉ ์ฆ๋ช… ์œ ์ง€๋ฅผ ์œ„ํ•œ ์‹œํฌ๋ฆฟ์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

  4. ์™ธ๋ถ€ ์œ„์น˜์— ์•ก์„ธ์Šคํ•  ๋•Œ ์ฒ˜๋ฆฌ๊ธฐ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์‹œํฌ๋ฆฟ๊ณผ ๋„คํŠธ์›Œํฌ ๊ทœ์น™์„ ์ง‘๊ณ„ํ•˜์—ฌ ์™ธ๋ถ€ ์•ก์„ธ์Šค ํ†ตํ•ฉ์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

  5. ๊ฐ’์œผ๋กœ ๋‚˜ํƒ€๋‚ธ ํ†ตํ•ฉ ์ด๋ฆ„์œผ๋กœ ์„ค์ •๋œ EXTERNAL_ACCESS_INTEGRATIONS ๋งค๊ฐœ ๋ณ€์ˆ˜๋กœ UDF ๋˜๋Š” ํ”„๋กœ์‹œ์ €๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์™ธ๋ถ€ ๋„คํŠธ์›Œํฌ ์œ„์น˜์— ์•ก์„ธ์Šคํ•˜๊ณ  ํ†ตํ•ฉ์—์„œ ๋„คํŠธ์›Œํฌ ๊ทœ์น™๊ณผ ์‹œํฌ๋ฆฟ์œผ๋กœ ์ง€์ •๋œ ์ž๊ฒฉ ์ฆ๋ช…์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜ ๋˜๋Š” ํ”„๋กœ์‹œ์ € ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•ฉ๋‹ˆ๋‹ค.

    ์ฒ˜๋ฆฌ๊ธฐ ์ฝ”๋“œ์—์„œ ์‹œํฌ๋ฆฟ์˜ ๋‚ด์šฉ์— ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ๋„๋ก SECRET ๋งค๊ฐœ ๋ณ€์ˆ˜๋ฅผ ํ†ตํ•ฉ์— ํฌํ•จ๋œ ์‹œํฌ๋ฆฟ ์ด๋ฆ„์œผ๋กœ ๋”ฐ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

    ํ•จ์ˆ˜ ๋˜๋Š” ํ”„๋กœ์‹œ์ € ์ฒ˜๋ฆฌ๊ธฐ ์ฝ”๋“œ์—์„œ ํ†ตํ•ฉ์— ํฌํ•จ๋œ ๋„คํŠธ์›Œํฌ ๊ทœ์น™์— ์ง€์ •๋œ ์™ธ๋ถ€ ๋„คํŠธ์›Œํฌ ์œ„์น˜์— ์•ก์„ธ์Šคํ•ฉ๋‹ˆ๋‹ค. ํ—ˆ์šฉ๋œ ๋„คํŠธ์›Œํฌ ๊ทœ์น™์— ์ง€์ •๋˜์ง€ ์•Š์€ ๋„คํŠธ์›Œํฌ ์œ„์น˜์— ๋Œ€ํ•œ ์•ก์„ธ์Šค ์‹œ๋„๋Š” ๊ฑฐ๋ถ€๋ฉ๋‹ˆ๋‹ค.

๊ณต์šฉ ์ธํ„ฐ๋„ท ๋˜๋Š” ๋น„๊ณต๊ฐœ ์—ฐ๊ฒฐ ์„ ํƒํ•˜๊ธฐยถ

์™ธ๋ถ€ ๋„คํŠธ์›Œํฌ ์œ„์น˜์— ์—ฐ๊ฒฐํ•  ๋•Œ Snowflake์—์„œ ์™ธ๋ถ€ ๋„คํŠธ์›Œํฌ ์œ„์น˜๋กœ์˜ ์—ฐ๊ฒฐ์€ ๊ณต์šฉ ์ธํ„ฐ๋„ท์„ ํ†ตํ•˜๊ฑฐ๋‚˜ Azure Private Link (Microsoft ์„ค๋ช…์„œ) ๋˜๋Š” AWS PrivateLink (AWS ์„ค๋ช…์„œ)๋ฅผ ํ†ตํ•ด ๋น„๊ณต๊ฐœ ์—ฐ๊ฒฐ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์™ธ๋ถ€ ๋„คํŠธ์›Œํฌ ์œ„์น˜์— ๋Œ€ํ•œ ์—ฐ๊ฒฐ์˜ ๋ณด์•ˆ ์š”๊ตฌ ์‚ฌํ•ญ์— ๋”ฐ๋ผ ๋น„๊ณต๊ฐœ ์—ฐ๊ฒฐ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋น„๊ณต๊ฐœ ์—ฐ๊ฒฐ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ณด์•ˆ ์š”๊ตฌ ์‚ฌํ•ญ์„ ์ถฉ์กฑํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ณต์šฉ ์ธํ„ฐ๋„ท์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ์ด ํ•ญ๋ชฉ์˜ ๋‹ค์Œ ์„น์…˜์— ์žˆ๋Š” ์ง€์นจ์„ ๋”ฐ๋ฅด์‹ญ์‹œ์˜ค.

๋น„๊ณต๊ฐœ ์—ฐ๊ฒฐ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ์ƒํ˜ธ ์ž‘์šฉ์„ ๊ตฌ์„ฑํ•˜๋Š” ์‚ฌ๋žŒ์—๊ฒŒ ACCOUNTADMIN ์—ญํ• ์ด ํ• ๋‹น๋˜์–ด ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ Snowflake ๊ณ„์ •์€ Business Critical Edition ์ด์ƒ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Azure Private Link ๋˜๋Š” AWS PrivateLink ์‚ฌ์šฉ ์‹œ ์ถ”๊ฐ€ ์ฒญ๊ตฌ ์š”๊ธˆ์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋‹ค์Œ ํ•ญ๋ชฉ๋ฅผ ๊ฒ€ํ† ํ•˜์‹ญ์‹œ์˜ค.

๋‹ค์Œ์œผ๋กœ, ์•„๋ž˜ ํ•ญ๋ชฉ ์ค‘ ํ•˜๋‚˜์— ํ‘œ์‹œ๋œ ๋Œ€๋กœ ๋น„๊ณต๊ฐœ ์—ฐ๊ฒฐ์„ ์‚ฌ์šฉํ•˜๋„๋ก ์™ธ๋ถ€ ๋„คํŠธ์›Œํฌ ์œ„์น˜์— ๋Œ€ํ•œ ์•ก์„ธ์Šค๋ฅผ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.

์™ธ๋ถ€ ๋„คํŠธ์›Œํฌ ์œ„์น˜๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋„คํŠธ์›Œํฌ ๊ทœ์น™ ๋งŒ๋“ค๊ธฐยถ

CREATE NETWORK RULE ๋ช…๋ น์„ ์‚ฌ์šฉํ•˜์—ฌ ์™ธ๋ถ€ ๋„คํŠธ์›Œํฌ์˜ ์œ„์น˜์™€ ์•ก์„ธ์Šค ์ œํ•œ์„ ๋‚˜ํƒ€๋‚ด๋Š” ๋„คํŠธ์›Œํฌ ๊ทœ์น™์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋„คํŠธ์›Œํฌ ๊ทœ์น™์—์„œ๋Š” ํ˜ธ์ŠคํŠธ ์ด๋ฆ„, ๋„คํŠธ์›Œํฌ์™€์˜ ํ†ต์‹  ๋ฐฉํ–ฅ(์ˆ˜์‹  ๋˜๋Š” ์†ก์‹ )๊ณผ ๊ฐ™์€ ๋„คํŠธ์›Œํฌ ์‹๋ณ„์ž๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

์™ธ๋ถ€ ๋„คํŠธ์›Œํฌ์— ๋Œ€ํ•œ ์•ก์„ธ์Šค๋ฅผ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•ด ๊ด€๋ฆฌ์ž๋Š” ์™ธ๋ถ€ ์•ก์„ธ์Šค ํ†ตํ•ฉ ์„ ์ƒ์„ฑํ•  ๋•Œ ๊ทœ์น™์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. ํ†ตํ•ฉ์— ํฌํ•จ๋œ ๊ฐ ๊ทœ์น™์€ ํ•จ์ˆ˜ ๋˜๋Š” ํ”„๋กœ์‹œ์ €๊ฐ€ ์•ก์„ธ์Šคํ•˜๋„๋ก ํ—ˆ์šฉ๋˜๋Š” ์™ธ๋ถ€ ๋„คํŠธ์›Œํฌ ์œ„์น˜๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

์™ธ๋ถ€ ์•ก์„ธ์Šค ํ†ตํ•ฉ์— ์‚ฌ์šฉํ•  ๋„คํŠธ์›Œํฌ ๊ทœ์น™์„ ์ƒ์„ฑํ•  ๋•Œ ๋‹ค์Œ์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

  • EGRESS๋ฅผ MODE ๋งค๊ฐœ ๋ณ€์ˆ˜ ๊ฐ’์œผ๋กœ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

  • HOST_PORT ๋˜๋Š” PRIVATE_HOST_PORT ์™€ ๊ฐ™์€ ๋„คํŠธ์›Œํฌ ์œ ํ˜•์„ ๋‚˜ํƒ€๋‚ด๋Š” TYPE ๋งค๊ฐœ ๋ณ€์ˆ˜ ๊ฐ’์ž…๋‹ˆ๋‹ค.

  • VALUE_LIST ๋งค๊ฐœ ๋ณ€์ˆ˜์— ์™ธ๋ถ€ ์œ„์น˜์˜ ์—”๋“œํฌ์ธํŠธ๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

  • (์„ ํƒ ์‚ฌํ•ญ) ์™ธ๋ถ€ ์œ„์น˜์˜ ์—”๋“œํฌ์ธํŠธ ์ด๋ฆ„์„ ํฌํ•จํ•œ ํฌํŠธ ๋ฒˆํ˜ธ๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ํฌํŠธ ๋ฒˆํ˜ธ๋ฅผ ์ƒ๋žตํ•˜๋ฉด Snowflake๋Š” ์™ธ๋ถ€ ์•ก์„ธ์Šค์— ๊ธฐ๋ณธ ํฌํŠธ ๋ฒˆํ˜ธ์ธ 443์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

    ์˜ˆ๋ฅผ ๋“ค์–ด ์—”๋“œํฌ์ธํŠธ์— ํฌํŠธ 80์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ VALUE_LIST ๋งค๊ฐœ ๋ณ€์ˆ˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    VALUE_LIST = ('example.com:80')
    
    Copy

์•ก์„ธ์Šค ์ œ์–ดยถ

๋ณด์•ˆ์„ ์œ„ํ•ด Snowflake์—์„œ๋Š” ๋„คํŠธ์›Œํฌ ๊ทœ์น™์„ ์ƒ์„ฑํ•  ๋•Œ ๋‹ค์Œ์„ ๋ณด์œ ํ•œ ์—ญํ• ์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • ๊ทœ์น™์„ ๋ณด์œ ํ•  ์Šคํ‚ค๋งˆ์— ๋Œ€ํ•œ CREATE NETWORK RULE ๊ถŒํ•œ์ž…๋‹ˆ๋‹ค.

์˜ˆยถ

๋‹ค์Œ ์˜ˆ์‹œ์˜ ์ฝ”๋“œ๋Š” Google Translation API์— ๋Œ€ํ•œ ์•„์›ƒ๋ฐ”์šด๋“œ ์š”์ฒญ์— ๋Œ€ํ•ด google_apis_network_rule ์ด๋ผ๋Š” ๋„คํŠธ์›Œํฌ ๊ทœ์น™์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

๋” ๋งŽ์€ ์˜ˆ๋Š” ์™ธ๋ถ€ ๋„คํŠธ์›Œํฌ ์•ก์„ธ์Šค ์˜ˆ์‹œ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

CREATE OR REPLACE NETWORK RULE google_apis_network_rule
  MODE = EGRESS
  TYPE = HOST_PORT
  VALUE_LIST = ('translation.googleapis.com');
Copy

์ž๊ฒฉ ์ฆ๋ช…์„ ๋‚˜ํƒ€๋‚ด๋Š” ์‹œํฌ๋ฆฟ ๋งŒ๋“ค๊ธฐยถ

CREATE SECRET ์„ ์‚ฌ์šฉํ•˜์—ฌ ์™ธ๋ถ€ ๋„คํŠธ์›Œํฌ ์œ„์น˜๋กœ ์ธ์ฆํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ์ž๊ฒฉ ์ฆ๋ช…์„ ๋‚˜ํƒ€๋‚ด๋Š” ์‹œํฌ๋ฆฟ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์‹œํฌ๋ฆฟ์—๋Š” ์‚ฌ์šฉ์ž ์ด๋ฆ„ ๋ฐ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋˜๋Š” ๋ณด์•ˆ ํ†ตํ•ฉ ๋“ฑ์˜ ์ž๊ฒฉ ์ฆ๋ช…์ด ํฌํ•จ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

OAuth๋ฅผ ์ง€์›ํ•˜๋Š” ์™ธ๋ถ€ ๋„คํŠธ์›Œํฌ ์œ„์น˜์— ์•ก์„ธ์Šคํ•˜๋ ค๋ฉด ํด๋ผ์ด์–ธํŠธ ID, ํด๋ผ์ด์–ธํŠธ ์‹œํฌ๋ฆฟ, ํ† ํฐ ์—”๋“œํฌ์ธํŠธ ๋“ฑ OAuth ํ๋ฆ„์— ํ•„์š”ํ•œ ๊ฐ’์ด ํฌํ•จ๋œ ๋ณด์•ˆ ํ†ตํ•ฉ ์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ ์‹œํฌ๋ฆฟ์— ํฌํ•จํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ์ข‹์Šต๋‹ˆ๋‹ค.

์‹œํฌ๋ฆฟ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

  • ๊ด€๋ฆฌ์ž๊ฐ€ ์™ธ๋ถ€ ์•ก์„ธ์Šค ํ†ตํ•ฉ ์„ ์ƒ์„ฑํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

    ํ†ตํ•ฉ ์ƒ์„ฑ ์‹œ, ๊ด€๋ฆฌ์ž๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ํ†ตํ•ฉ์„ ์‚ฌ์šฉํ•˜๋Š” ํ•จ์ˆ˜๋‚˜ ํ”„๋กœ์‹œ์ €๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ์ฒ˜๋ฆฌ๊ธฐ ์ฝ”๋“œ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์‹œํฌ๋ฆฟ์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

  • ๊ฐœ๋ฐœ์ž๊ฐ€ UDF ๋˜๋Š” ํ”„๋กœ์‹œ์ € ์ฒ˜๋ฆฌ๊ธฐ๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

    ๊ฐœ๋ฐœ์ž๋Š” ์ฒ˜๋ฆฌ๊ธฐ ์ฝ”๋“œ๊ฐ€ ์™ธ๋ถ€ ์œ„์น˜์— ๋Œ€ํ•œ ์š”์ฒญ์„ ํ•  ๋•Œ ์ธ์ฆํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ž๊ฒฉ ์ฆ๋ช…์ด ํฌํ•จ๋œ ํ—ˆ์šฉ๋œ ์‹œํฌ๋ฆฟ์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ์ฒ˜๋ฆฌ๊ธฐ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ ๊ฐœ๋ฐœ์ž๋Š” ์ฒ˜๋ฆฌ๊ธฐ ์ฝ”๋“œ์— ์ž๊ฒฉ ์ฆ๋ช…์„ ๋ฆฌํ„ฐ๋Ÿด ๊ฐ’์œผ๋กœ ํฌํ•จํ•˜๋Š” ๋Œ€์‹  Snowflake API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‹œํฌ๋ฆฟ์— ํฌํ•จ๋œ ์ž๊ฒฉ ์ฆ๋ช…์„ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ 

์ƒˆ๋กœ ๊ณ ์นจ ํ† ํฐ์ด ํ•„์š”ํ•œ OAuth ์‹œํฌ๋ฆฟ์˜ ๊ฒฝ์šฐ Snowflake์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์‹œ์Šคํ…œ ํ•จ์ˆ˜๋ฅผ ํ†ตํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ํฌํ•จํ•˜์—ฌ ์—ฌ๋Ÿฌ ๋ฐฉ๋ฒ•์œผ๋กœ ํ† ํฐ์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋ณด๋ ค๋ฉด OAuth๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Google Translate API์— ์•ก์„ธ์Šคํ•˜๊ธฐ ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

์•ก์„ธ์Šค ์ œ์–ดยถ

๋ณด์•ˆ์„ ์œ„ํ•ด Snowflake์—์„œ๋Š” ์‹œํฌ๋ฆฟ์„ ์ƒ์„ฑํ•  ๋•Œ ๋‹ค์Œ์„ ๋ณด์œ ํ•œ ์—ญํ• ์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • ์‹œํฌ๋ฆฟ์„ ๋ณด์œ ํ•  ์Šคํ‚ค๋งˆ์— ๋Œ€ํ•œ CREATE SECRET ๊ถŒํ•œ.

์˜ˆยถ

๋‹ค์Œ ์˜ˆ์‹œ์˜ ์ฝ”๋“œ๋Š” OAuth๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ธ์ฆํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ๊ฐ’์ด ํฌํ•จ๋œ ๋ณด์•ˆ ํ†ตํ•ฉ(google_translate_oauth ๋กœ ํ‘œ์‹œ๋จ)์„ ์ง€์ •ํ•˜๋Š” oauth_token ์ด๋ผ๋Š” ์‹œํฌ๋ฆฟ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

๋ณด์•ˆ ํ†ตํ•ฉ ์ƒ์„ฑ์„ ์œ„ํ•œ ์ฝ”๋“œ๋ฅผ ํฌํ•จํ•œ ๋” ์™„์ „ํ•œ ์˜ˆ๋Š” ์™ธ๋ถ€ ๋„คํŠธ์›Œํฌ ์•ก์„ธ์Šค ์˜ˆ์‹œ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

CREATE OR REPLACE SECRET oauth_token
  TYPE = OAUTH2
  API_AUTHENTICATION = google_translate_oauth
  OAUTH_REFRESH_TOKEN = 'my-refresh-token';
Copy

ํŒ

์ด ๋ฏธ๋ฆฌ ๋ณด๊ธฐ์—์„œ๋Š” API ํ‚ค๋ฅผ ์ž๊ฒฉ ์ฆ๋ช…์œผ๋กœ๋งŒ ์‚ฌ์šฉํ•˜๋ ค๋Š” ๊ฒฝ์šฐ TYPE ์„ GENERIC_STRING ์œผ๋กœ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

CREATE OR REPLACE SECRET bp_maps_api
  TYPE = GENERIC_STRING
  SECRET_STRING = 'replace-with-your-api-key';
Copy

์™ธ๋ถ€ ์•ก์„ธ์Šค ํ†ตํ•ฉ ๋งŒ๋“ค๊ธฐยถ

CREATE EXTERNAL ACCESS INTEGRATION ๋ช…๋ น์„ ์‚ฌ์šฉํ•˜์—ฌ UDF ๋ฐ ํ”„๋กœ์‹œ์ €์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋„๋ก ํ—ˆ์šฉ๋œ ๋„คํŠธ์›Œํฌ ๊ทœ์น™(์™ธ๋ถ€ ๋„คํŠธ์›Œํฌ ์œ„์น˜๋ฅผ ๋‚˜ํƒ€๋ƒ„)๊ณผ ํ—ˆ์šฉ๋œ ์‹œํฌ๋ฆฟ(์ธ์ฆ์„ ์œ„ํ•œ ์ž๊ฒฉ ์ฆ๋ช…์„ ๋‚˜ํƒ€๋ƒ„)์„ ์ง‘๊ณ„ํ•˜๋Š” ์™ธ๋ถ€ ์•ก์„ธ์Šค ํ†ตํ•ฉ์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํŠนํžˆ, ์™ธ๋ถ€ ์•ก์„ธ์Šค ํ†ตํ•ฉ์€ ํ†ตํ•ฉ์„ ์ฐธ์กฐํ•˜๋Š” UDF ๋ฐ ํ”„๋กœ์‹œ์ €๊ฐ€ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋„คํŠธ์›Œํฌ ๊ทœ์น™๊ณผ ์‹œํฌ๋ฆฟ์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

์™ธ๋ถ€ ์•ก์„ธ์Šค ํ†ตํ•ฉ์€ ๊ด€๋ฆฌ์ž๊ฐ€ UDF์™€ ํ”„๋กœ์‹œ์ €์—์„œ ์™ธ๋ถ€ ๋„คํŠธ์›Œํฌ ์œ„์น˜์— ๋Œ€ํ•œ ์•ก์„ธ์Šค๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ํ†ตํ•ฉ์€ ํ†ตํ•ฉ์„ ์ฐธ์กฐํ•˜๋Š” UDF์™€ ํ”„๋กœ์‹œ์ €์—์„œ ์‚ฌ์šฉํ•˜๋„๋ก ํ—ˆ์šฉ๋œ ์œ„์น˜์™€ ์ž๊ฒฉ ์ฆ๋ช…๋งŒ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ด€๋ฆฌ์ž๋Š” ์™ธ๋ถ€ ์œ„์น˜์— ๋Œ€ํ•œ ์•ก์„ธ์Šค๋ฅผ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ํ†ตํ•ฉ์„ ํ™œ์„ฑํ™”ํ•˜๊ฑฐ๋‚˜ ๋น„ํ™œ์„ฑํ™”ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์•ก์„ธ์Šค ์ œ์–ดยถ

๋ณด์•ˆ์„ ์œ„ํ•ด Snowflake์—์„œ๋Š” ์™ธ๋ถ€ ์•ก์„ธ์Šค ํ†ตํ•ฉ์„ ์ƒ์„ฑํ•  ๋•Œ ๋‹ค์Œ์„ ๋ณด์œ ํ•œ ์—ญํ• ์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • ๊ณ„์ •์— ๋Œ€ํ•œ CREATE INTEGRATION ๊ถŒํ•œ.

  • ํ†ตํ•ฉ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋“  ์‹œํฌ๋ฆฟ์— ๋Œ€ํ•œ USAGE ๊ถŒํ•œ๊ณผ ์‹œํฌ๋ฆฟ์˜ ์Šคํ‚ค๋งˆ์— ๋Œ€ํ•œ USAGE ๊ถŒํ•œ.

์˜ˆยถ

๋‹ค์Œ ์˜ˆ์‹œ์˜ ์ฝ”๋“œ๋Š” google_apis_access_integration ์ด๋ผ๋Š” ์™ธ๋ถ€ ์•ก์„ธ์Šค ํ†ตํ•ฉ์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ํ†ตํ•ฉ์€ google_apis_network_rule ๋„คํŠธ์›Œํฌ ๊ทœ์น™(๋„คํŠธ์›Œํฌ ์œ„์น˜๋ฅผ ๋‚˜ํƒ€๋ƒ„)๊ณผ oauth_token ์‹œํฌ๋ฆฟ(์ž๊ฒฉ ์ฆ๋ช…์„ ๋‚˜ํƒ€๋ƒ„)์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

์ด ๊ทœ์น™๊ณผ ์‹œํฌ๋ฆฟ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์™ธ๋ถ€ ๋„คํŠธ์›Œํฌ ์œ„์น˜๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋„คํŠธ์›Œํฌ ๊ทœ์น™ ๋งŒ๋“ค๊ธฐ ๋ฐ ์ž๊ฒฉ ์ฆ๋ช…์„ ๋‚˜ํƒ€๋‚ด๋Š” ์‹œํฌ๋ฆฟ ๋งŒ๋“ค๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

CREATE OR REPLACE EXTERNAL ACCESS INTEGRATION google_apis_access_integration
  ALLOWED_NETWORK_RULES = (google_apis_network_rule)
  ALLOWED_AUTHENTICATION_SECRETS = (oauth_token)
  ENABLED = true;
Copy

ํ•จ์ˆ˜ ๋˜๋Š” ํ”„๋กœ์‹œ์ €์—์„œ ์™ธ๋ถ€ ์•ก์„ธ์Šค ํ†ตํ•ฉ ์‚ฌ์šฉํ•˜๊ธฐยถ

CREATE FUNCTION ๋˜๋Š” CREATE PROCEDURE ๋ช…๋ น์„ ์‚ฌ์šฉํ•˜์—ฌ UDF ๋˜๋Š” ํ”„๋กœ์‹œ์ €๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์™ธ๋ถ€ ๋„คํŠธ์›Œํฌ ์œ„์น˜์— ๋Œ€ํ•œ ์•ก์„ธ์Šค๋ฅผ ํ™œ์„ฑํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • EXTERNAL_ACCESS_INTEGRATIONS ๋งค๊ฐœ ๋ณ€์ˆ˜์˜ ๊ฐ’์„ ํ•˜๋‚˜ ์ด์ƒ์˜ ํ†ตํ•ฉ์œผ๋กœ ์„ค์ •ํ•˜์—ฌ ์ด ๋งค๊ฐœ ๋ณ€์ˆ˜๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

    ์—ฌ๊ธฐ์„œ ์ง€์ •ํ•˜๋Š” ๊ฐ ํ†ตํ•ฉ์€ ํ†ตํ•ฉ์ด ์ง€์ •ํ•˜๋Š” ์™ธ๋ถ€ ๋„คํŠธ์›Œํฌ ์œ„์น˜์™€ ์‹œํฌ๋ฆฟ์— ๋Œ€ํ•œ ์•ก์„ธ์Šค๋ฅผ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค.

  • SECRETS ๋งค๊ฐœ ๋ณ€์ˆ˜์˜ ๊ฐ’์„ ํ•˜๋‚˜ ์ด์ƒ์˜ ์‹œํฌ๋ฆฟ๊ณผ ์ฒ˜๋ฆฌ๊ธฐ ์ฝ”๋“œ์—์„œ ์•ก์„ธ์Šคํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ด๋ฆ„์œผ๋กœ ์„ค์ •ํ•˜์—ฌ ์ด ๋งค๊ฐœ ๋ณ€์ˆ˜๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

    ๊ฐ’์œผ๋กœ ์ง€์ •ํ•˜๋Š” ์‹œํฌ๋ฆฟ์€ ์™ธ๋ถ€ ์•ก์„ธ์Šค ํ†ตํ•ฉ์—์„œ๋„ ์ง€์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • ์ฒ˜๋ฆฌ๊ธฐ ์ฝ”๋“œ์—์„œ ์‹œํฌ๋ฆฟ์— ์•ก์„ธ์Šคํ•˜์—ฌ ์™ธ๋ถ€ ๋„คํŠธ์›Œํฌ ์œ„์น˜๋กœ ์ธ์ฆํ•˜๊ธฐ ์œ„ํ•œ ์ž๊ฒฉ ์ฆ๋ช…์„ ๋ถˆ๋Ÿฌ์˜ต๋‹ˆ๋‹ค.

์ฐธ๊ณ 

์ฝ”๋“œ์— ์ž๊ฒฉ ์ฆ๋ช…์„ ๋ฆฌํ„ฐ๋Ÿด ๊ฐ’์œผ๋กœ ํฌํ•จํ•˜๋Š” ๋Œ€์‹  ํ•ญ์ƒ ์ž๊ฒฉ ์ฆ๋ช…์„ ๋‚˜ํƒ€๋‚ด๋Š” Snowflake ์‹œํฌ๋ฆฟ์„ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค. ์ž๊ฒฉ ์ฆ๋ช…์„ ๋ณดํ˜ธํ•˜๋Š” ๊ฒƒ ์™ธ์—๋„, ์‹œํฌ๋ฆฟ์„ ์‚ฌ์šฉํ•˜๋ฉด ์‹œํฌ๋ฆฟ์— ๋Œ€ํ•œ READ ๊ถŒํ•œ์ด ๋ถ€์—ฌ๋œ ์‚ฌ์šฉ์ž๋งŒ UDF ๋˜๋Š” ํ”„๋กœ์‹œ์ €์— ์ด ๊ถŒํ•œ์ด ํฌํ•จ๋œ ํ†ตํ•ฉ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ž๊ฒฉ ์ฆ๋ช… ์‚ฌ์šฉ์„ ๊ฐ์‚ฌํ•˜๊ณ  ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Snowflake๋Š” ํŠน์ • UDF์—์„œ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š” ์ด ์—ฐ๊ฒฐ ์ˆ˜๋ฅผ ์ œํ•œํ•ฉ๋‹ˆ๋‹ค. ๋ฆฌ์†Œ์Šค ๊ณ ๊ฐˆ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก ํ•˜๋ ค๋ฉด ์—ฐ๊ฒฐ์„ ์ตœ๋Œ€ํ•œ ๋งŽ์ด ์žฌ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค. UDF ์ดˆ๊ธฐํ™” ์ค‘์— TCP ํด๋ผ์ด์–ธํŠธ ๋˜๋Š” ์„ธ์…˜์„ ํ•œ ๋ฒˆ ๋งŒ๋“  ๋‹ค์Œ ๋‚˜๋จธ์ง€ ์ฟผ๋ฆฌ์— ๋Œ€ํ•ด UDF ์ฒ˜๋ฆฌ๊ธฐ์—์„œ ํด๋ผ์ด์–ธํŠธ๋‚˜ ์„ธ์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ์ตœ๋Œ€ํ•œ ๋งŽ์ด ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์•ก์„ธ์Šค ์ œ์–ดยถ

๋ณด์•ˆ์„ ์œ„ํ•ด Snowflake์—์„œ๋Š” UDF ๋˜๋Š” ํ”„๋กœ์‹œ์ €๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ๋‹ค์Œ์„ ๋ณด์œ ํ•œ ์—ญํ• ์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • ์ฐธ์กฐํ•˜๋Š” ๋ชจ๋“  ์‹œํฌ๋ฆฟ์— ๋Œ€ํ•œ READ ๊ถŒํ•œ๊ณผ ์‹œํฌ๋ฆฟ์˜ ์Šคํ‚ค๋งˆ์— ๋Œ€ํ•œ USAGE ๊ถŒํ•œ.

  • ์ฐธ์กฐํ•˜๋Š” ๋ชจ๋“  ํ†ตํ•ฉ์— ๋Œ€ํ•œ USAGE ๊ถŒํ•œ.

์ด๋Ÿฌํ•œ ๊ถŒํ•œ์„ ์š”๊ตฌํ•จ์œผ๋กœ์จ ๊ด€๋ฆฌ์ž๊ฐ€ ์™ธ๋ถ€ ์•ก์„ธ์Šค๋ฅผ ํ™œ์„ฑํ™”ํ•  ์ˆ˜ ์žˆ๋Š” ์‚ฌ์šฉ์ž๋กœ ๊ตฌ์„ฑ๋œ ์„ธํŠธ๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ GRANT <privileges> โ€ฆ TO ROLE ๋ฐ ์•ก์„ธ์Šค ์ œ์–ด ๊ถŒํ•œ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

์˜ˆยถ

๋‹ค์Œ ์˜ˆ์‹œ์˜ ์ฝ”๋“œ๋Š” google_apis_access_integration ์ด๋ผ๋Š” ์™ธ๋ถ€ ์•ก์„ธ์Šค ํ†ตํ•ฉ์„ ์ง€์ •ํ•˜์—ฌ google_translate_python ์ด๋ผ๋Š” UDF๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค(์ž์„ธํ•œ ๋‚ด์šฉ์€ ์™ธ๋ถ€ ์•ก์„ธ์Šค ํ†ตํ•ฉ ๋งŒ๋“ค๊ธฐ ์ฐธ์กฐ). ํ†ตํ•ฉ์€ ํ†ตํ•ฉ์„ ์ฐธ์กฐํ•˜๋Š” UDF์—์„œ ์‚ฌ์šฉํ•˜๋„๋ก ํ—ˆ์šฉ๋œ ๋„คํŠธ์›Œํฌ ๊ทœ์น™(์™ธ๋ถ€ ๋„คํŠธ์›Œํฌ ์œ„์น˜๋ฅผ ๋‚˜ํƒ€๋ƒ„)๊ณผ ์‹œํฌ๋ฆฟ(์ž๊ฒฉ ์ฆ๋ช…์„ ๋‚˜ํƒ€๋ƒ„)์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ทœ์น™๊ณผ ์‹œํฌ๋ฆฟ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์™ธ๋ถ€ ๋„คํŠธ์›Œํฌ ์œ„์น˜๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋„คํŠธ์›Œํฌ ๊ทœ์น™ ๋งŒ๋“ค๊ธฐ ๋ฐ ์ž๊ฒฉ ์ฆ๋ช…์„ ๋‚˜ํƒ€๋‚ด๋Š” ์‹œํฌ๋ฆฟ ๋งŒ๋“ค๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

Python ์ฒ˜๋ฆฌ๊ธฐ ์ฝ”๋“œ๋Š” _snowflake.get_oauth_access_token ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‹œํฌ๋ฆฟ์—์„œ OAuth ํ† ํฐ์„ ๋ถˆ๋Ÿฌ์˜จ ๋‹ค์Œ ๊ทธ ํ† ํฐ์„ ์‚ฌ์šฉํ•ด ์™ธ๋ถ€ ์œ„์น˜๋กœ ์ธ์ฆํ•ฉ๋‹ˆ๋‹ค. ์ฒ˜๋ฆฌ๊ธฐ ์ฝ”๋“œ๋Š” ์ง€์ •๋œ URL์— ์š”์ฒญํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, URL์˜ ํ˜ธ์ŠคํŠธ๊ฐ€ ํ†ตํ•ฉ์—์„œ ์ง€์ •ํ•œ ๋„คํŠธ์›Œํฌ ๊ทœ์น™์— ๋‚˜์—ด๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

CREATE OR REPLACE FUNCTION google_translate_python(sentence STRING, language STRING)
RETURNS STRING
LANGUAGE PYTHON
RUNTIME_VERSION = 3.9
HANDLER = 'get_translation'
EXTERNAL_ACCESS_INTEGRATIONS = (google_apis_access_integration)
PACKAGES = ('snowflake-snowpark-python','requests')
SECRETS = ('cred' = oauth_token )
AS
$$
import _snowflake
import requests
import json
session = requests.Session()
def get_translation(sentence, language):
  token = _snowflake.get_oauth_access_token('cred')
  url = "https://translation.googleapis.com/language/translate/v2"
  data = {'q': sentence,'target': language}
  response = session.post(url, json = data, headers = {"Authorization": "Bearer " + token})
  return response.json()['data']['translations'][0]['translatedText']
$$;
Copy