Bringing python into Front-End with Pyodide
브라우저에서 파이썬 코드를 실행해본다
Running ...
bGV0IHB5b2RpZGUgPSBhd2FpdCBsb2FkUHlvZGlkZSh7IGluZGV4VVJMOiAnaHR0cHM6Ly9jZG4uanNkZWxpdnIubmV0L3B5b2RpZGUvdjAuMjAuMC9mdWxsLycgfSk7CmF3YWl0IHB5b2RpZGUubG9hZFBhY2thZ2UoIm1pY3JvcGlwIik7CmF3YWl0IHB5b2RpZGUucnVuUHl0aG9uQXN5bmMoYAogICBpbXBvcnQgbWljcm9waXAsIGpzCiAgIGF3YWl0IG1pY3JvcGlwLmluc3RhbGwoJ2Vtb2ppJykKICAgaW1wb3J0IGVtb2ppCiAgIHJlc3VsdCA9IGVtb2ppLmVtb2ppemUoJ1B5dGhvbiBpcyA6dGh1bWJzX3VwOicpCiAgIGpzLmRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJyNjb250YWluZXInKS5pbm5lclRleHQ9cmVzdWx0CmApOw==
let pyodide = await loadPyodide({ indexURL: 'https://cdn.jsdelivr.net/pyodide/v0.20.0/full/' });
await pyodide.loadPackage("micropip");
await pyodide.runPythonAsync(`
import micropip, js
await micropip.install('emoji')
import emoji
result = emoji.emojize('Python is :thumbs_up:')
js.document.querySelector('#container').innerText=result
`);
간략히 정리한 사용 예시 코드
bGV0IHB5b2RpZGU7CnsKICAgIC8vIFB5b2RpZGXripQg67Cx7JeU65OcIOyXhuydtCDsm7nruIzrnbzsmrDsoIDsl5DshJwg7YyM7J207I2sIOy9lOuTnOulvCDqtazrj5nqsIDriqXtlZjqsowg7ZW07KSA64ukCiAgICAvLyBDUHl0aG9uIOydhCDsm7nslrTshYjruJTrpqzroZwg7Y+s7YyF7ZWc6rKD7J2064ukCiAgICAvLyDsm7ntjpjsnbTsp4Ag7IOB7JeQIGh0dHBzOi8vY2RuLmpzZGVsaXZyLm5ldC9weW9kaWRlL3YwLjIwLjAvZnVsbC9weW9kaWRlLmpzIOy2lOqwgO2VmOqzoCDslYTrnpjsmYAg6rCZ7J20IOyLpO2Wie2VmOuptCDrkJzri6QKICAgIC8vIOyCrOyaqeydtCDrhIjrrLQg6rCE64uo7ZWY64ukCiAgICAvLyDtjIzsnbTsjazsnZggcHJpbnQg7ZWo7IiY64qUIGNvbnNvbGUubG9nIOulvCDtlZzqsoPsspjrn7wg7Lac66Cl65Cc64ukCiAgICAvLyBsb2FkUHlvZGlkZSDripQg65GQ67KI7J207IOBIOuhnOuUqeydtCDslYjrkJjripTqsoPsnLzroZwg7ZmV7J2465Cc64ukCiAgICBweW9kaWRlID0gYXdhaXQgbG9hZFB5b2RpZGUoeyBpbmRleFVSTDogJ2h0dHBzOi8vY2RuLmpzZGVsaXZyLm5ldC9weW9kaWRlL3YwLjIwLjAvZnVsbC8nIH0pOwogICAgYXdhaXQgcHlvZGlkZS5ydW5QeXRob25Bc3luYyhgCiAgICAgICAgIGZvciBpIGluIHJhbmdlKDEwKToKICAgICAgICAgICAgIHByaW50KGkpCiAgICAgYCk7Cn0KewogICAgLy8gcnVuUHl0aG9uQXN5bmMg66W8IO2Gte2VtCDsi6TtlontlZwg7YyM7J207I2sIOy9lOuTnOydmCDqsrDqs7zripQg7JWE656Y7JmAIOqwmeydtCDrsJvsnYQg7IiYIOyeiOuLpAogICAgLy8g7YyM7J207I2sIOy9lOuTnCDsiJjtlonqsrDqs7zroZwg7Ja77J2AIOuNsOydtO2EsOulvCDtlITroaDtirjsl5Trk5zsuKHsl5DshJwg7IKs7Jqp6rCA64ql7ZWcIOuqqOyKteydtOuLpAogICAgbGV0IHJlc3VsdCA9IGF3YWl0IHB5b2RpZGUucnVuUHl0aG9uQXN5bmMoYAogICAgICAgICBpbXBvcnQganNvbgogICAgICAgICBqc29uLmR1bXBzKGxpc3QoJ3cnICogMTApKQogICAgIGApOwogICAgY29uc29sZS5sb2cocmVzdWx0KQp9CnsKICAgIC8vIO2UhOuhoO2KuOyXlOuTnOy4oeydmCDrjbDsnbTthLDrpbwg7YyM7J207I2s7Jy866GcIOuEmOq4sOuKlCDrsKnrspXsnYAg7JWE656Y7JmAIOqwmeydtCDtlbTso7zrqbQg65Cc64ukCiAgICBweW9kaWRlLmdsb2JhbHMuc2V0KCJkYXRhZnJvbWpzIiwgMTApOwogICAgbGV0IHJlc3VsdCA9IGF3YWl0IHB5b2RpZGUucnVuUHl0aG9uQXN5bmMoYAogICAgICAgICBpbXBvcnQganNvbgogICAgICAgICBqc29uLmR1bXBzKGxpc3QoJ2QnICogZGF0YWZyb21qcykpCiAgICAgYCk7CiAgICBjb25zb2xlLmxvZyhyZXN1bHQpCn0KewogICAgLy8g7JWE656Y7JmAIOqwmeydtCBKU+ydmCDtlajsiJjrj4Qg64SY6ri4IOyImCDsnojri6QKICAgIHB5b2RpZGUuZ2xvYmFscy5zZXQoImpzRnVuY3Rpb24iLCAoKSA9PiBNYXRoLnJhbmRvbSgpKTsKICAgIGxldCByZXN1bHQgPSBhd2FpdCBweW9kaWRlLnJ1blB5dGhvbkFzeW5jKGAKICAgICAgICAganNGdW5jdGlvbigpCiAgICAgYCk7CiAgICBjb25zb2xlLmxvZyhyZXN1bHQpCn0KewogICAgLy8g7JWE656Y7JmAIOqwmeydtCBKU+ydmCDtgbTrnpjsiqTrj4Qg64SY6ri4IOyImCDsnojri6QKICAgIC8vIOq3uOufrOuCmCDtgbTrnpjsiqTripQgbmV3IOyXhuydtCDtlajsiJjsspjrn7wg7Iuk7ZaJ7ZWgIOyImCDsl4bri6QKICAgIC8vIOq3uOufsOuNsCDtjIzsnbTsjazsl5DshJzripQgbmV3IOqwgCDsl4bri6TripTqsoPsnbQg65Sc66CI66eI7J2064ukCiAgICAvLyBuZXfrpbwg67aZ7J2066m0IO2MjOydtOyNrOyXkCDsl4bripTtgqTsm4zrk5zrnbwg7JeQ65+sCiAgICAvLyBuZXfrpbwg7JWI67aZ7J2066m0IEpTQ2xhc3PripQg7YG0656Y7Iqk6528IO2VqOyImOyymOufvCDsi6TtlontlaDsiJgg7JeG7Ja07IScIOyXkOufrAogICAgY2xhc3MgSlNDbGFzcyB7IH0KICAgIHB5b2RpZGUuZ2xvYmFscy5zZXQoIkpTQ2xhc3MiLCBKU0NsYXNzKTsKICAgIHRyeSB7CiAgICAgICAgYXdhaXQgcHlvZGlkZS5ydW5QeXRob25Bc3luYyhgCiAgICAgICAgICAgICBKU0NsYXNzKCkKICAgICAgICAgYCk7CiAgICB9IGNhdGNoIChlKSB7CiAgICAgICAgY29uc29sZS5sb2coZSkKICAgIH0KfQp7CiAgICAvLyDtjIzsnbTsjawg7L2U65OcIOyViOyXkOyEnCBqcyDrnbzripQg66qo65OI7J2EIOyehO2PrO2KuCDtlaAg7IiYIOyeiOuKlOuNsAogICAgLy8ganMg64qUIOu4jOudvOyasOyggOyDgeydmCB3aW5kb3fsnbTri6QKICAgIC8vIOymiSB3aW5kb3cg6rCd7LK066W8IO2Gte2VtCDrqqjrk6AgV2ViQVBJ7JeQIOygkeq3vO2VoCDsiJgg7J6I64ukCiAgICAvLyDsl4Tssq0g66mL7KeE6rGw64ukLi4KICAgIGxldCByZXN1bHQgPSBhd2FpdCBweW9kaWRlLnJ1blB5dGhvbkFzeW5jKGAKICAgICAgICAgZnJvbSBqcyBpbXBvcnQgY29uc29sZSwgZmV0Y2gsIGRvY3VtZW50CiAgICAgICAgIGltcG9ydCBqcwogICAgICAgICBhc3luYyBkZWYgcG9zdF9kYXRhKCk6CiAgICAgICAgICAgICByZXNwb25zZSA9IGF3YWl0IGZldGNoKCfsnKDslYzsl5jso7zshownKQogICAgICAgICAgICAganNvbiA9IGF3YWl0IHJlc3BvbnNlLmpzb24oKQogICAgICAgICAgICAgcmV0dXJuIGpzb24KICAgICAgICAgCiAgICAgICAgIGF3YWl0IHBvc3RfZGF0YSgpCiAgICAgIGApOwogICAgY29uc29sZS5sb2cocmVzdWx0KQp9CnsKICAgIC8vIG1pY3JvcGlw652864qU6rKMIOyeiOuLpCDsnbTqsbgg7J207Jqp7ZW07IScIO2MjOydtOyNrCDsvZTrk5zslYjsl5DshJwg66qo65OI7J2EIOyEpOy5mO2VoCDsiJgg7J6I64ukCiAgICAvLyBqbWVzcGF0aOuekSBlbW9qaeuqqOuTiOydhCDshKTsuZjtlbTshJwg7IKs7Jqp7ZW067O4IOuqqOyKteydtOuLpAogICAgYXdhaXQgcHlvZGlkZS5sb2FkUGFja2FnZSgibWljcm9waXAiKTsKICAgIGxldCByZXN1bHQgPSBhd2FpdCBweW9kaWRlLnJ1blB5dGhvbkFzeW5jKGAKICAgICAgICAgaW1wb3J0IG1pY3JvcGlwCiAgICAgICAgIGF3YWl0IG1pY3JvcGlwLmluc3RhbGwoJ2ptZXNwYXRoJykKICAgICAgICAgYXdhaXQgbWljcm9waXAuaW5zdGFsbCgnZW1vamknKQogICAgICAgICBpbXBvcnQgam1lc3BhdGgsIGVtb2ppCiAgICAgICAgIHByaW50KGptZXNwYXRoLnNlYXJjaCgnZm9vLmJhcicsIHsiZm9vIjogeyJiYXIiOiAiYmF6In19KSkKICAgICAgICAgcHJpbnQoam1lc3BhdGguc2VhcmNoKCdmb28uYmFyWypdLm5hbWUnLCB7ImZvbyI6IHsiYmFyIjogW3sibmFtZSI6ICJvbmUifSwgeyJuYW1lIjogInR3byJ9XX19KSkKICAgICAgICAgcHJpbnQoZW1vamkuZW1vaml6ZSgnUHl0aG9uIGlzIDp0aHVtYnNfdXA6JykpCiAgICAgYCk7CiAgICBjb25zb2xlLmxvZyhyZXN1bHQpCn0KewogICAgLy8g7YyM7J207I2s7JeQ7IScIOyWu+ydgCDrjbDsnbTthLDrpbwg7ZSE66Gg7Yq47JeQ7IScIOyCrOyaqe2VmOq4sAogICAgLy8g7Ja77J2AIOuNsOydtO2EsOqwgCDsm5Dsi5ztmJXsnbgg6rK97JqwCiAgICBsZXQgcmVzdWx0MCA9IGF3YWl0IHB5b2RpZGUucnVuUHl0aG9uQXN5bmMoYAogICAgICAgMTIzCiAgICBgKTsKICAgIGNvbnNvbGUubG9nKHJlc3VsdDApCiAgICAvLyDslrvsnYAg642w7J207YSw6rCAIOybkOyLnO2YleydtCDslYTri4zqsr3smrDripQgdG9KcygpIOyCrOyaqQogICAgbGV0IHJlc3VsdDEgPSBhd2FpdCBweW9kaWRlLnJ1blB5dGhvbkFzeW5jKGAKICAgICAgIGRlZiBhYmMoKToKICAgICAgICAgIHJldHVybiAxMjMKICAgICAgIGFiYwogICAgYCk7CiAgICBjb25zb2xlLmxvZyhyZXN1bHQxLnRvSnMoKSgpKQogICAgLy8g7JWE656Y7JmAIOqwmeydgOqyjCDrkJjripTqsIAg7ZW067Sk64qU642wIOyViOuQnOuLpAogICAgbGV0IHJlc3VsdDIgPSBhd2FpdCBweW9kaWRlLnJ1blB5dGhvbkFzeW5jKGAKICAgICAgIGltcG9ydCBqc29uCiAgICAgICBqc29uCiAgICBgKTsKICAgIGNvbnNvbGUubG9nKHJlc3VsdDIudG9KcygpLmR1bXBzKHsgdGVzdDogMTIzIH0pKQogICAgLy8g7JWE656Y7JmAIOqwmeydgOqyjCDrkJjripTqsIAg7ZW067Sk64qU642wIOyViOuQnOuLpAogICAgbGV0IHJlc3VsdDMgPSBhd2FpdCBweW9kaWRlLnJ1blB5dGhvbkFzeW5jKGAKICAgICAgIHsidGVzdCI6MTIzfQogICAgYCk7CiAgICBjb25zb2xlLmxvZyhyZXN1bHQyLmR1bXBzKHJlc3VsdDMpKQp9
let pyodide;
{
// Pyodide는 백엔드 없이 웹브라우저에서 파이썬 코드를 구동가능하게 해준다
// CPython 을 웹어셈블리로 포팅한것이다
// 웹페이지 상에 https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pyodide.js 추가하고 아래와 같이 실행하면 된다
// 사용이 너무 간단하다
// 파이썬의 print 함수는 console.log 를 한것처럼 출력된다
// loadPyodide 는 두번이상 로딩이 안되는것으로 확인된다
pyodide = await loadPyodide({ indexURL: 'https://cdn.jsdelivr.net/pyodide/v0.20.0/full/' });
await pyodide.runPythonAsync(`
for i in range(10):
print(i)
`);
}
{
// runPythonAsync 를 통해 실행한 파이썬 코드의 결과는 아래와 같이 받을 수 있다
// 파이썬 코드 수행결과로 얻은 데이터를 프론트엔드측에서 사용가능한 모습이다
let result = await pyodide.runPythonAsync(`
import json
json.dumps(list('w' * 10))
`);
console.log(result)
}
{
// 프론트엔드측의 데이터를 파이썬으로 넘기는 방법은 아래와 같이 해주면 된다
pyodide.globals.set("datafromjs", 10);
let result = await pyodide.runPythonAsync(`
import json
json.dumps(list('d' * datafromjs))
`);
console.log(result)
}
{
// 아래와 같이 JS의 함수도 넘길 수 있다
pyodide.globals.set("jsFunction", () => Math.random());
let result = await pyodide.runPythonAsync(`
jsFunction()
`);
console.log(result)
}
{
// 아래와 같이 JS의 클래스도 넘길 수 있다
// 그러나 클래스는 new 없이 함수처럼 실행할 수 없다
// 그런데 파이썬에서는 new 가 없다는것이 딜레마이다
// new를 붙이면 파이썬에 없는키워드라 에러
// new를 안붙이면 JSClass는 클래스라 함수처럼 실행할수 없어서 에러
class JSClass { }
pyodide.globals.set("JSClass", JSClass);
try {
await pyodide.runPythonAsync(`
JSClass()
`);
} catch (e) {
console.log(e)
}
}
{
// 파이썬 코드 안에서 js 라는 모듈을 임포트 할 수 있는데
// js 는 브라우저상의 window이다
// 즉 window 객체를 통해 모든 WebAPI에 접근할 수 있다
// 엄청 멋진거다..
let result = await pyodide.runPythonAsync(`
from js import console, fetch, document
import js
async def post_data():
response = await fetch('유알엘주소')
json = await response.json()
return json
await post_data()
`);
console.log(result)
}
{
// micropip라는게 있다 이걸 이용해서 파이썬 코드안에서 모듈을 설치할 수 있다
// jmespath랑 emoji모듈을 설치해서 사용해본 모습이다
await pyodide.loadPackage("micropip");
let result = await pyodide.runPythonAsync(`
import micropip
await micropip.install('jmespath')
await micropip.install('emoji')
import jmespath, emoji
print(jmespath.search('foo.bar', {"foo": {"bar": "baz"}}))
print(jmespath.search('foo.bar[*].name', {"foo": {"bar": [{"name": "one"}, {"name": "two"}]}}))
print(emoji.emojize('Python is :thumbs_up:'))
`);
console.log(result)
}
{
// 파이썬에서 얻은 데이터를 프론트에서 사용하기
// 얻은 데이터가 원시형인 경우
let result0 = await pyodide.runPythonAsync(`
123
`);
console.log(result0)
// 얻은 데이터가 원시형이 아닌경우는 toJs() 사용
let result1 = await pyodide.runPythonAsync(`
def abc():
return 123
abc
`);
console.log(result1.toJs()())
// 아래와 같은게 되는가 해봤는데 안된다
let result2 = await pyodide.runPythonAsync(`
import json
json
`);
console.log(result2.toJs().dumps({ test: 123 }))
// 아래와 같은게 되는가 해봤는데 안된다
let result3 = await pyodide.runPythonAsync(`
{"test":123}
`);
console.log(result2.dumps(result3))
}