톺아보기
Pynecone 에는 다양한 레이어들이 있다.
우선 Pynecone 서버를 총괄하는 "앱" 부터 보자.
앱
앱은 다음과 같은 요소들을 멤버변수로 가진다.
- 서버 페이지들과 페이지, 컴포넌트 스타일
- FastAPI 기반 api 인스턴스
- Socket.IO 구성요소들의 인스턴스
- 프론트와 소켓통신으로 연동되는 페이지 상태
- 각 페이지 로딩 이벤트들
- 미들웨어
앱을 초기화하고, 각 메소드들을 호출해 앱을 세팅하고, 앱을 컴파일 시켜 서버를 새로빌드한다. (* production 환경에서는 컴파일 과정이 생략된다.)
그리고 앱이 컴파일될 때는 다음과 같은 작업들이 이루어진다.
- 데코레이터로 라우팅된 페이지들을 추가
- SQLModel 들의 테이블을 db 에 create
- 프론트엔드 .web 폴더를 새로 빌드
- 이벤트 핸들러들을 런타임에 호출할 수 있도록 function 으로 변환
프레임워크 디버깅을 해야 한다면 앱 부터 찾아 들어가자.
이제 앱에서 한층 더 내려가면 페이지와 상태가 존재한다.
페이지
프론트엔드에 존재해야할 페이지가 Python 으로 구현되어있다.
페이지는 기본적으로 function base 로 구성된다.
def index():
return pc.text("Root Page")
def custom():
return pc.text("Custom Route")
페이지는 컴포넌트를 반환한다. React 의 컴포넌트로 이해해도 무방하다.
컴포넌트는 pynecone.io 공식문서 에서 간편하게 가져다 쓸 수 있다.
이렇게 준비된 function 을 add_page 에 route path 와 함께 전달해주면 라우팅이 끝난다.
add_page
app = pc.App()
app.add_page(index)
app.add_page(custom, route="/custom-route")
add_page 에 function 을 넘길 경우 기본적으로 function 의 __name__ 으로 라우팅된다.
하지만 이런 요상한 방법은 쓰지 않을 것이다. route 파라미터에 path 를 넘기면 해당 페이지로 라우팅된다.
더 나아가서 Python 생태계에서 자주 사용되는 데코레이터를 통해 라우팅할 수도 있다.
@pc.route(route="/", title="My Beautiful App")
def index():
return pc.text("A Beautiful App")
상태
Pynecone 의 꽃이라고 말할 수 있겠다.
프론트엔드의 React state 와 Socket.io 로 값을 직접통신하는데, 100% 프론트 계산에 비해서는 좀 버벅이는 느낌도 있긴 하지만 일반적인 i/o 보다는 빠른 편이다.
상태는 세가지 종류의 컨셉을 포함하고 있다.
- 변수(Vars)
- 이벤트 핸들러(Event Handlers)
- 중첩상태(Substates)
먼저, 가장 심오한 중첩상태에 대해 알아보자
중첩상태
중첩상태에서 상태는 오직 단방향으로만 전파되며, 자식상태에서 부모상태를 참조할 수 있지만 역으로는 참조할 수 없도록 설계되어있다. (React 자체에서는 어떻게든 가능하지만 여긴 구현에서부터 막혀있다) 이는 중첩상태의 구현방법에서 왜 그런지 알 수 있다.
중첩상태는 Python 클래스로 구현되어있고, 이 클래스의 inheritance 룰을 따라간다.
그래서 당연히, 부모가 자식 상태를 참조할 수 없다.
물론 모든 상태가 초기화된 후 __subclasses__ 를 뒤져서 참조할 수 있는 마술과 같은 방법은 있었지만 일단은 없는게 맞다.(마개조금지)
중첩동작은 매직 메소드인 __init_subclass__ 를 통해 상속의 형태로 이루어지고 상태의 멤버변수인 substates 에 각 자식상태들이 초기화된다.
변수
익히들 알고 있는 React 의 컴포넌트 상태이다.
이벤트 핸들러 내부동작에서 변경될 수 있으며 변경될 경우 매칭되어있는 React state 에 Socket.io 로 통신하고, 관련된 컴포넌트에 전파된다(이건 당연히 React 니깐)
가장 중요한 점. 변수는 JSON 직렬화 가능해야 한다
언급했듯이 프론트엔드와 백엔드 간의 소켓통신에 사용되기 때문에 직렬화를 할 수 있어야 한다.
혹시 구조체가 필요하다면 pynecone 에 내장 되어있는 pydantic 의 BaseModel 을 채택할 수 있다.
계산된 변수
Computed Variable 이라고 하면 익숙하다.
매 호출때마다 계산되고, @property 와 거의 동일하게 사용하면 된다.
@pc.var
def 계산된_변수(self) -> str:
return self.변수 + "입니다"
백엔드 전용변수
Python 에서 주로 사용되는 _underscore 접두사 네이밍으로 정의한다.
프론트엔드에 렌더링되지 않으므로 성능이점과 보안이점을 챙길 수 있다. 그리고 이 경우 프론트엔드와 통신하지 않아도 되기 때문에 직렬화 가능할 필요도 없다는건 보너스
class 상태(pc.State):
_백엔드_전용_변수: str
그럼 다음으로 넘어가기전에 짚어볼 점.
Pynecone 에서는 컴포넌트에 Python function 을 넣을 수 없다.
아래는 불가능한 예:
class State(pc.State):
number: int
def index():
return pc.text(float(State.number))
javascript 에서 가능한 연산정도는 가능하다지만, 불규칙하게 구현되어있으니 시도할 생각을 하지 않는게 정신건강에 이롭다. 그래서 Computed Variable 과 이벤트 핸들러를 적절하게 활용할 필요가 있다.
이벤트 핸들러
javascript 의 Event handling 을 전달받아 동작한다.
각 핸들러들은 on_click 이나 on_mouse_over 와 같은 이벤트로 트리거된다.
이벤트 핸들러로 구현된 Collatz 추측:
class CollatzState(pc.State):
count: int = 0
def start_collatz(self, count):
"""Run the collatz conjecture on the given number."""
self.count = abs(int(count))
return self.run_step
async def run_step(self):
"""Run a single step of the collatz conjecture."""
await asyncio.sleep(0.2)
if self.count % 2 == 0:
# If the number is even, divide by 2.
self.count /= 2
else:
# If the number is odd, multiply by 3 and add 1.
self.count = self.count * 3 + 1
if self.count > 1:
# Keep running until we reach 1.
return self.run_step
def index():
return pc.vstack(
pc.badge(
CollatzState.count,
font_size="1.5em",
color_scheme="green",
),
pc.input(on_blur=CollatzState.start_collatz),
)
https://pynecone.io/docs/state/events 에서 확인해보자.
이벤트 핸들러는 state 도 쓸 수 있지만 lambda 로 직접 간단하게 작성할 수도 있다.
또한 list 를 넣어줄 경우 이벤트 핸들러를 체이닝 시킬 수도 있다.
pc.heading(
ChainExampleState.count,
on_click=[
ChainExampleState.toggle_progress,
ChainExampleState.increment,
ChainExampleState.toggle_progress,
],
_hover={"cursor": "pointer"},
)
이것만 있으면 웬만한건 다 구현할 수 있다.
DB 는 pydantic 과 SQLModel 을 쓰는데 별로 어렵지 않다. 도메인 분리가 조금 골치아픈 정도?
이제 톺아보았으니 본격적으로 페이지를 만들어볼 시간이 되었다.
'it > programming' 카테고리의 다른 글
3. 레이어 구상하기 / Pynecone 으로 내 홈페이지 만들기 (1) | 2023.05.19 |
---|---|
1. 프로젝트 세팅 / Pynecone 으로 내 홈페이지 만들기 (0) | 2023.04.07 |
0. 프롤로그 / Pynecone 으로 내 홈페이지 만들기 (0) | 2023.04.07 |