[ 一日30分 인생승리의 학습법]  Pynecone.io, 매력적이지만 아직은 아쉬운 

 

힙한 Pynecone을 실 서비스에 사용해 보려다 좌초되어 버린 이야기
 

프론트엔드, 백엔드, 서비스 호스팅까지 모두 Python을 뽀개버리겠다는
누가 봐도 원대한 꿈을 가진듯한 Pynecone이라는 프레임워크
 
그리고 그들의 꿈 대로면 누구라도 끌릴만한 특징으로 나 또한 한 발을 담가보았다.
하지만 "pynecone 브랜치"는 일주일도 되지 않아 나만의 작은 보물상자로 고이 들어가게 되었고...
 
이 글은 Pynecone을 실 서비스에 사용해 보겠다는 일념으로 이리저리 박치기해 보면서 느낀 점을 일기 삼아 정리하는 글이다.


Pynecone 소개

물론 이 아이를 알고 온 사람들도 있겠지만 분명 별로 유명하진 않은 녀석이라 모르는 게 당연하다고 생각하고 소개부터 해보련다.
 
Pynecone 은 2022년 11월, Nikhil Rao 와 Alek Petuskey 가 본업을 때려치우고(ㄷㄷ) 만들기 시작한 생겨난 지 얼마 되지 않은 프레임워크이다.
 

  • pynecone.io 창립자들의 귀여운 자기소개 (유튜브)
  • 노마드꼬더 니꼴라스의 소개 (유튜브)

 

Pynecone를 쓰면 뭐가 어떻게 좋을까?

프론트엔드. 백엔드. 호스팅.
순수 파이썬으로
몇 분 안에 웹 앱을 만드세요. 단일 명령으로 배포하세요.

 
Pynecone 은 지금까지 인프라&DB 엔지니어 / 백엔드 개발자 / 프론트엔드 개발자 / 디자이너까지 여러 사람들의 역할을 하나로 모아주는 역할을 해준다.
 

더 빠르게 모든 것을 구축하세요.
앱 전체를 단일 언어로 만드세요. 프론트엔드와 백엔드를 연결하기 위해 API를 작성하는 것을 걱정하지 마세요.

1. 모든 용도에 사용 가능합니다.
Pynecone을 사용하여 내부 도구 및 데이터 앱부터 복잡한 멀티페이지 앱까지 무엇이든 구축할 수 있습니다.

2. 모두 파이썬입니다.
앱 상태는 클래스입니다. 상태 업데이트는 클래스의 메서드입니다. 그리고 UI는 상태의 반영입니다.

 
또한 가장 큰 장점으로 꼽고 가장 큰 매력포인트라고 할 수 있는 것은 이 프레임워크가 정말 말 그대로 Python으로만 웹 서비스에서의 모든 것을 만들어낼 수 있다는 것이다.
 
 
우선 프론트엔드에서 다음과 같은 장점들을 가진다.

1. 60개 이상의 내장 UI 컴포넌트로 빠르게 시작하세요.
Pynecone에는 간단한 버튼부터 복잡한 그래프와 테이블까지 다양한 UI 컴포넌트 라이브러리가 포함되어 있습니다.

2. 완전히 커스텀할 수 있습니다.
Pynecone의 모든 컴포넌트는 완전히 커스텀할 수 있습니다. 프로젝트에 맞게 색상, 글꼴 및 스타일을 변경할 수 있습니다.

3. 사용자 정의 컴포넌트
몇 줄의 코드로 커스텀 컴포넌트를 만드세요. 원하는 React 컴포넌트를 간단히 래핑 하면 됩니다.

4. 이제 누구나 풀스택으로 일할 수 있습니다.
Pynecone을 사용하면 모든 엔지니어가 풀스택으로 작업할 수 있으므로 더욱 효율적이고 생산적인 워크플로우가 가능합니다.

 
생각보다 UI 셋이 풍부하여 어쩌면 UI 프레임워크로만 사용해도 무방하겠다는 생각마저 들게 한다.
심지어 커스텀이 프론트엔드 스펙과 완전히 일치하고 자유로워서 기존 프론트엔드 개발자가 접근하는 데에 큰 어려움이 없다!
 
그리고 만약 이 프레임워크를 모든 엔지니어가 동일하게 사용한다면 스택 간의 인사이트 차이로 발생하는 혼란 또한 당연히 줄어들 것이라고 볼 수 있겠다.
 
 
그리고 백엔드 측면에서도 이런 장점들이 있다.

1. 보일러플레이트를 건너뛰고 더 빠르게 시작하세요.
Pynecone은 강력한 백엔드 FastAPI와 SQLAlchemy로 구축되어 있습니다.

2. 한 줄의 명령으로 앱을 배포하세요.
앱이 성능이 우수하고 안전한지 확인하기 위해 자동으로 구성된 CDN, HTTPS, SSL 등이 제공됩니다.

 
Python 개발자들이라면 웬만하면 한번쯤 다뤄봤을 만한 FastAPI 와 SQLAlchemy 를 사용하였고, 이를 프레임워크에 흡수시켜 프레임워크의 랭귀지를 통해 접근할 수 있도록 하였다.
 
하여 FastAPI를 온전히 알지 못하더라도 프레임워크의 가이드만 따라가면 API와 엔드포인트를 수려하게 연결할 수 있고 SQLAlchemy를 제대로 알지 못하더라도 도메인 레벨에서의 데이터 I/O 를 부드럽게 컨트롤할 수 있었다.
 
 
 

그래서 직접 써봤다.

 
 
 

Pynecone 사용후기

토이프로젝트로 진행한 레포지토리는 다음과 같다: https://github.com/hsol/myokr/tree/pynecone

 

 

Bolierplate. 개발환경설정

내가 생각하는 Pynecone의 장점 중 가장 와닿은 건 Bolierplate 였다.
정말 간단하고 탄탄한 쉘 명령어로 세팅, 개발서버, 빌드, 배포까지 끊임없이 진행할 수 있으며
내가 서버를 어디에 띄웠는지 어떻게 연결했는지에 대해 크게 고민하지 않고 온전히 서비스에 집중할 수 있게 해 주었다.
 

  1. pynecone 라이브러리를 설치하고
  2. 원하는 앱 디렉터리에서 pc init, pc run 개발시작 딱 (Wow!)

 
개발환경을 세팅하고 인프라 스트럭쳐를 고민하고 배포 쿠킹파일을 작성하는 일련의 작업들을 싫어하다 못해 혐오하는 나로서는 마냥 행복한 포인트라고 할 수 있겠다.
 
그렇게 싱글생글대며 개발을 시작한다.
 

Frontend. 프론트엔드 개발

우선 처음 느낀 건 프레임워크의 골조가 굉장히 function based 하다는 것
각 페이지와 그 페이지의 컴포넌트들을 모두 function으로 선언하고 decorator를 통해 routing 하는 등 전형적으로 FastAPI의 정신을 이어받은 모양이다.
 
하지만 내 취향 하고는 잘 맞지 않았다. 일단 나는 프론트 개발을 할 때에도 class based component 신봉자였고(지금은 functional component와 hook의 소중함에 좀 더 설득되었지만) 지금 나 혼자 프로젝트를 하게 된 와중에 굳이 functional 하게 작성할 필요도 없었다.
 
그래서 일단 class based page component를 생성하였다.
route path와 component 정보, page event와 state, 라이프사이클까지 포함하는 낭만을 누리고 잠시 취해있다가, 이것을 토대로 본격적으로 프론트엔드 컴포넌트를 작성하기 시작한다.
 
페이지의 예시

import pynecone

from app import Global
from app.components.container import Container
from app.components.logo import Logo
from app.pages.base import BasePage


class IndexState(Global.State):
    def redirect(self):
        from app.pages.welcome import Welcome

        return pynecone.redirect(Welcome.route)


class Index(BasePage):
    route = "/"

    def get_component(self) -> pynecone.Component:
        return Container.wrapper(
            pynecone.flex(
                pynecone.vstack(
                    pynecone.flex(Logo.big, height="20vh", align="center"),
                    pynecone.spacer(),
                    pynecone.button(
                        "시작하기",
                        bg=Global.Palette.MANTIS,
                        color="black",
                        size="lg",
                        on_click=IndexState.redirect,
                    ),
                ),
                direction="column",
                align="center",
                justify="center",
                height="100%",
            ),
            height="100vh",

 
프론트엔드 개발자분들은 get_component 부분이 뭔가 익숙할 것이다.
정말 python 으로 프론트엔드 개발을 한다는 느낌을 그대로 주는게 신기하고 snake_case 라는점, style 이 굳이 attributes 로 들어간다는 점만 뺀다면 그동안 써온 모양대로라 별 거부감 없이 작성할 수 있었다.
 
무엇보다 그들이 강조한 UI 프레임워크는 생각보다 더 편했는데, 마치 bootstrap UI 를 백엔드에서 작성하는 듯한 느낌을 주었다.
 
그럼 이쯤에서 프론트엔드 개발자분들이 가장 궁금해할 부분이 있다.
그래서 state 는 대체 어떻게 관리하는데?
맞다. 나도 제일 궁금했고 쓰면서 가장 부딪한 영역이기도 했다.
 
상태 관리의 예시

import typing

import pynecone
from pynecone.event import EventChain
from pynecone.utils import call_event_fn

from app import Global
from app.components.container import Container
from app.okr_gpt import KeyResultProvideFailedError
from app.pages.base import BasePage
from app.states.okr import OKRState


class WelcomeKeyResultsState(OKRState):
    def on_load(self):
        self.error_message = ""
        if self.objective:
            try:
                key_result_strings = Global.GPT.get_key_results(self.objective)
            except KeyResultProvideFailedError as e:
                self.error_message = str(e)
                for idx in range(5):
                    setattr(self, f"kr{(idx + 1)}", None)
                return

            for idx, key_result in enumerate(key_result_strings[:5]):
                setattr(self, f"kr{(idx + 1)}", key_result)
        else:
            self.error_message = "목표가 설정되지 않았어요. 이전으로 돌아가서 다시 목표를 입력해주세요!"

    def set_key_results(self, key_results: list[str]):
        super().set_key_results(key_results)

    def prev(self):
        from app.pages.welcome_objective import WelcomeObjective

        return pynecone.redirect(WelcomeObjective.route)

    def next(self):
        from app.pages.welcome_save_complete import WelcomeSaveComplete
        from app.pages.welcome_need_login import WelcomeNeedLogin

        if self.session.user:
            return pynecone.redirect(WelcomeSaveComplete.route)

        return pynecone.redirect(WelcomeNeedLogin.route)


class WelcomeKeyResults(BasePage):
    route = "/welcome-key-results"

    def get_on_load_event_handler(self) -> typing.Callable[[], None] | None:
        return WelcomeKeyResultsState.on_load

    def get_component(self) -> pynecone.Component:
        def key_result_input(key_result: str, order: int):
            return pynecone.form_control(
                pynecone.form_label(f"Key Result {order}"),
                pynecone.text_area(
                    default_value=key_result,
                    on_blur=getattr(WelcomeKeyResultsState, f"set_key_result_{order}"),
                ),
                width="100%",
                is_required=True,
            )

        return Container.with_cta(
            pynecone.vstack(
                pynecone.cond(
                    WelcomeKeyResultsState.has_key_results,
                    pynecone.vstack(
                        pynecone.text("멋진 목표네요!"),
                        pynecone.text_area(
                            default_value=WelcomeKeyResultsState.objective,
                            on_blur=WelcomeKeyResultsState.set_objective,
                            wrap="off",
                        ),
                        pynecone.text("이 목표에 대해 Key-Result 들을 생각해봤어요."),
                        *([pynecone.spacer()] * 3),
                        key_result_input(WelcomeKeyResultsState.kr1, 1),
                        key_result_input(WelcomeKeyResultsState.kr2, 2),
                        key_result_input(WelcomeKeyResultsState.kr3, 3),
                        pynecone.cond(
                            WelcomeKeyResultsState.kr4,
                            key_result_input(WelcomeKeyResultsState.kr4, 4),
                            pynecone.box(),
                        ),
                        pynecone.cond(
                            WelcomeKeyResultsState.kr5,
                            key_result_input(WelcomeKeyResultsState.kr5, 5),
                            pynecone.box(),
                        ),
                        *([pynecone.spacer()] * 3),
                        pynecone.text("어때요? 제시된 Key-Result 들이 마음에 드시나요?"),
                        pynecone.text("마음에 들지 않는다면, 입맛에 맞도록 직접 수정 해보세요."),
                        align_items="flex-start",
                        width="100%",
                    ),
                    pynecone.cond(
                        WelcomeKeyResultsState.error_message,
                        pynecone.vstack(
                            pynecone.alert(
                                pynecone.alert_icon(),
                                pynecone.alert_title(
                                    WelcomeKeyResultsState.error_message
                                ),
                                status="error",
                            ),
                            width="100%",
                            align_items="center",
                        ),
                        pynecone.vstack(
                            pynecone.circular_progress(is_indeterminate=True),
                            pynecone.text("Key Result 를 고민하는 중이에요."),
                            width="100%",
                            align_items="center",
                        ),
                    ),
                ),
                align_items="flex-start",
            ),
            display="flex",
            flex_flow="column",
            justify_content="center",
            cta_left=pynecone.button(
                "목표 다시 설정", width="100%", on_click=WelcomeKeyResultsState.prev
            ),
            cta_right=pynecone.cond(
                WelcomeKeyResultsState.has_key_results,
                pynecone.button(
                    "저장",
                    width="100%",
                    on_click=WelcomeKeyResultsState.next,
                    disabled=WelcomeKeyResultsState.error_message,
                ),
                pynecone.box(),
            ),
        )

 
아유 길다. 쓸데없는 UI 와 로직들이 좀 섞여있으니 이해 해주길
우리는 여기서 크게 두가지를 볼 수 있다. State 와 Component
 
기본적으로 Pynecone 에서 상태는 Global 로 하나만 존재한다.
어쩌면 redux, recoil 들의 정신을 따라건가? 싶은 지점도 보이기도 했다.
 
Global 상태를 App 에 정의하고, 이 상태를 상속받아 SubState 를 작성할 수 있다.
혹은 Global 에서만 상태를 운용할 수도 있다.
 
자식은 부모를 참조할 수 있으며 부모는 자식을 참조할 수 없다.
Python 개발자라면 이 SubState 개념이 inheritance 관계로 이루어져있으며 pydantic 을 베이스로 하고 있기 때문에 아마도 낯설지 않게 이해할 수 있을 것이다.
 
이 상태는 React 의 상태와 비슷하게 사용할 수 있으며 각 필드는 mutable 객체로 pynecone.Var 라는 클래스를 상속받아 매 변화에 event 를 trigger 할 수 있고 변화한 값을 내려준다.

 

경축! 아무것도 안하여 에스천사게임즈가 새로운 모습으로 재오픈 하였습니다.
어린이용이며, 설치가 필요없는 브라우저 게임입니다.
https://s1004games.com


 
다만 ...
이 상태라는 것이 굉장히 제한적이었던 것이, component 를 동적으로 렌더링 하려 할 때(ex: 조건부 렌더, 반복 렌더), 상태 필드를 주입해줄 경우 이 필드들이 렌더과정에 속하지 못하여 값의 변화와 값을 제대로 받아오지 못하는 문제가 있었다. 물론 React 에서는 당연히 단방향으로 주입만 되어있다면 알아서 잘 딱 깔끔하게 센스 있게 내려주는 부분이다.
 
때문에 이 변화를 추적하기 위해 이리저리 몽키패칭을 넣어야 했고 여기서 슬슬 열이 받기 시작한다.
 

Backend. 백엔드 개발

백엔드 개발에서 흥미로운 경험이라고 할만했던 것은 역시나 상태관리였다.
백엔드와 프론트엔드가 소켓통신을 하면서 상태를 연동하고, 백엔드에서 동기적으로 변경된 상태가 프론트엔드에 바로 반영되어 UI 가 변하는 모습은 정말 눈물이 날 정도로 감동스러웠다.
 
또한 Python 로직이 그대로 프론트엔드와 맞물려 있다 보니 로직이 잘못되었을 때에 어느 포인트에서 문제가 생겼는지 디버깅하기도 꽤 편했다(Pycharm 디버깅에 물리는걸 실패해서 이건 조금 아쉽다)
 
하지만 나에겐 장점보다 단점이 부각되었던 개발경험이었으니...
 
프론트엔드야 UX 를 조금 포기하거나 UI를 조금 비틀어서 해결하면 되는 문제지만
백엔드에서의 문제는 이 프레임워크 사용 자체를 포기하기에 충분했다.
 
우선 Pynecone 은 작년 11월부터 만들어져 객관적으로도 성숙하지 못하다는 것을 전제로 자비를 장착하고 써본다.
 
1. 안정성이 아쉽다.
next.js 와 fastapi 서버는 별도로 띄워지고, 이 서버들의 생애주기를 Pynecone 에서 관리하는것은 아니기 때문에 당연히 둘 중 하나가 꺼지거나 잘못될 경우 골치 아파진다. 이 문제는 창립자들도 언급하고 인지하고 있는 문제라 시간이 지나면 조금 나아지지 않을까 싶다.
(그치만 이렇게 자주 삑나면 화가 난다구)
 
2. 모듈성이 아쉽다.
"Pynecone 이 다 해줄게요!" 를 모토로 삼은 만큼 Pynecone 은 그냥 모든 걸 해준다.
이 말을 거꾸로 해본다면 어디도 붙일 수 없다는 심히 안타까운 뜻이 된다.
Pynecone 프레임워크 내에 각종 라이브러리들을 import 하여 사용하는 건 문제가 없지만 정작 Pynecone 의 꽤 좋은 기능들을 다른 프레임워크 내에서 별도로 사용할 수는 없는 게 단점이라고 볼 수 있겠다.
작은 규모의 개발에는 분명히 유리하지만 기존 서비스 환경을 가졌거나 MSA 가 아닌 서비스의 경우에는 아예 엄두도 못 낼 프레임워크일 것 같다.
 
3. 프론트와 나도 통신하고 싶다고!
캐시, 로컬&세션스토리지, 쿠키, 세션 ... 이것들을 직접 다루지 못한다.
정말 청천벽력 같은 소식인 게 session specific 한 데이터는 영속성이 없다는 소리다. 어... 물론 아예 없진 않다. 얘들도 소켓통신에 쓰기 위해 다루는 "토큰"이라는 게 존재하고 이건 sessionStorage 에 저장하는데, 각 통신에 router date 에 넣어서 보내주고 어떻게든 이걸 사용하면 토큰이 살아있는 동안은 데이터를 유지시킬 수 있다.
 
하지만 얘가 어디에 있냐, sessionStorage 이다. 그렇다. 페이지가 내려가면 없어진다ㅋㅋㅋ
그래서 직접 oauth 라도 구현해서 써보겠다 한다면 그 인증토큰은 어디에 저장할 건지 ... 우리는 클라이언트와 직접 소통할 방법이 없고 Pynecone 은 클라이언트의 자원을 쓸 방법을 마련해놓지 못했다.
 
그렇게 나는 Pynecone 내에서 세션 구현을 포기하게 되었다.
물론 구현할 방법은 있을 것 같다. 세션을 컨트롤할 수 있는 모 서버를 두어 로그인시키고 Pynecone 측으로 토큰을 요청하여 토큰을 세션에 락인 시키며 세션 스토리지가 초기화될 때마다 Pynecone 페이지를 새로 띄우도록 유도하여 토큰을 새로 락인 시키는 ... 변태같은
 

나에게는 맞지 않는, 하지만 ...

사실 직접 구현하면 뚝딱 30분 ~ 1시간이면 하는 일들을 안 해준다는 프레임워크 붙잡고 이리 꼬고 저리 꼬면서 일주일 정도 골치 썩인 결과 그래도 나 이거 웬만큼 써봤다고 말할 수 있겠다는 생각이 생겼다.
 
내가 만들려던 서비스는 그냥 이 글로 완전히 미련을 버리고 새로 만들 거다.
그래도 나에게 맞지 않는 거지 분명 쓰면서 느낀 장점들과 wow 포인트는 칭찬하고 추천할만했다.
 
그래서 내가 생각할 때 Pynecone 을 쓰면 좋은 사람들은
 

  • 한 스택밖에 모르지만 토이프로젝트라도 내 손으로 온전히 만들어보고 싶은 서비스 개발자들
  • Python 으로 모델링하는 게 익숙한 ML 엔지니어들
  • Python 으로 데이터를 다루고 분석하는 게 익숙한 데이터 엔지니어들
  • 심지어 디자이너들에게도 굉장히 유용한 프레임워크라고 할 수 있다.

 
 
그리고 나는 앞으로 Pynecone 이 더 성숙해지기 전까지는 이렇게 사용할 것 같다.
 

  • 제품의 랜딩 페이지
    렌딩 페이지랑 렌딩 페이지 API 서버 따로 띄우는 거 귀찮다. 얘가 알아서 다 해줄 거니깐 얘 쓰는 게 좋을 것 같다.
  • 유틸리티
    딱히 회원정보 필요 없고 기능만 제공하면 되는 앱은 이것만 써도 충분히 커버될 것 같다. 어쩌면 electron 과 같은 웹앱 방향의 가능성도 찾아볼 수 있겠다.
  • 토이프로젝트
    Python 은 하고 싶은데 히로쿠 띄우기도 귀찮고 하면 얘네 호스팅 곧 서비스한다니깐 DB 도 sqlite 로 간단히 뚝딱 하면 딱이겠다.

 

출처: https://hsol.tistory.com/979 [한솔닷컴:티스토리]

 

 

 

 

 

 

 

 

 

 

 

본 웹사이트는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.
번호 제목 글쓴이 날짜 조회 수
1198 [ 一日30分 인생승리의 학습법] GPT 언어 모델에서 생성 데이터를 JSON 형태로 주고 받게 해주는 Function Calling 살펴보기 file 졸리운_곰 2024.05.04 137
» [ 一日30分 인생승리의 학습법] Pynecone.io, 매력적이지만 아직은 아쉬운 file 졸리운_곰 2024.05.04 148
1196 [ 一日30分 인생승리의 학습법] supabase 폼 미쳤다 file 졸리운_곰 2024.04.27 151
1195 [ 一日30分 인생승리의 학습법] VBA Web Scraping: How Can VBA Be Used To Scrape Website Data? file 졸리운_곰 2024.04.13 119
1194 [ 一日30分 인생승리의 학습법] 윈도우 실행파일 구조(PE파일) file 졸리운_곰 2024.03.31 132
1193 [ 一日30分 인생승리의 학습법] [Analysis] PE(Portable Executable) 파일 포맷 공부 file 졸리운_곰 2024.03.31 141
1192 [ 一日30分 인생승리의 학습법] 성공하는 메타버스의 3가지 조건 file 졸리운_곰 2024.03.30 118
1191 [ 一日30分 인생승리의 학습법] REST, REST API, RESTful 과 HATEOAS file 졸리운_곰 2024.03.10 126
1190 [ 一日30分 인생승리의 학습법] 렌더링 삼형제 CSR, SSR, SSG 이해하기 file 졸리운_곰 2024.03.10 97
1189 [ 一日30分 인생승리의 학습법] 엑셀 VBA에서 셀레니움 사용을 위한 Selenium Basic 설치 file 졸리운_곰 2024.02.23 119
1188 [ 一日30分 인생승리의 학습법]500 Lines or Less Blockcode: A Visual Programming Toolkit : 500줄 이하의 블록코드: 시각적 프로그래밍 툴킷 졸리운_곰 2024.02.12 86
1187 [ 一日30分 인생승리의 학습법] 구글 클라이언트(앱) 아이디를 발급받으려면 어떻게 해야 하나요? 졸리운_곰 2024.01.28 94
1186 [ 一日30分 인생승리의 학습법] 빅뱅 프로젝트를 성공적으로 오픈하기 위한 팁 졸리운_곰 2023.12.27 120
1185 [ 一日30分 인생승리의 학습법]“빅뱅 전환보다 단계적 전환 방식이 이상적 애자일팀과 협업 쉽게 체질 개선을” file 졸리운_곰 2023.12.27 103
1184 [ 一日30分 인생승리의 학습법] Big-bang / phased 접근 file 졸리운_곰 2023.12.27 232
1183 [ 一日30分 인생승리의 학습법] CodeDragon 메뉴 데이터 전환의 개념 이해 - 데이터 전환의 개념, 데이터 전환방식, 데이터 전환방식 및 장단점 비교, 데이터전환 이후 검토해야 할 사항 졸리운_곰 2023.12.27 82
1182 [ 一日30分 인생승리의 학습법] 블록체인과 IPFS를 이용한 안전한 데이터 공유 플랫폼 - 분쟁 해결 시스템 file 졸리운_곰 2023.12.27 116
1181 [ 一日30分 인생승리의 학습법] 블록체인과 IPFS를 이용한 안전한 데이터 공유 플랫폼 - 개념과 리뷰 시스템 file 졸리운_곰 2023.12.27 92
1180 [ 一日30分 인생승리의 학습법] 소켓 CLOSE_WAIT 발생 현상 및 처리 방안 file 졸리운_곰 2023.12.03 85
1179 [ 一日30分 인생승리의 학습법] robots 설정하기 졸리운_곰 2023.12.03 127
대표 김성준 주소 : 경기 용인 분당수지 U타워 등록번호 : 142-07-27414
통신판매업 신고 : 제2012-용인수지-0185호 출판업 신고 : 수지구청 제 123호 개인정보보호최고책임자 : 김성준 sjkim70@stechstar.com
대표전화 : 010-4589-2193 [fax] 02-6280-1294 COPYRIGHT(C) stechstar.com ALL RIGHTS RESERVED