[Go Library] Lorca - 카운터

Study·2021년 6월 1일
0

고랭 라이브러리

목록 보기
2/4

고랭 라이브러리 Lorca 를 이용하여 카운터 페이지를 만들어보자.

예제 깃헙 페이지에서 Lorca 에서 함수를 이용하는 방법에 대하여 알아 볼 수있다.

counter.go

//go:embed index.html
var fs embed.FS

type counter struct {
	sync.Mutex
	count int
}

임베드와 카운터 구조체

고랭의 임베드를 활용하여 index.html 파일을 변수에 포함한다.

// go:embed [파일이름] 이란 주석을 다는 것만으로 그 내용을 변수에 저장할 수 있다.

개발자가 할 일은 embed 를 go build 하는 것 뿐이다.

파일을 읽을 때 표기법이 잘못되었거나 읽을 수 없을 때 컴파일 오류가 발생한다.

embed.FS 형태의 파일 시스템 뿐만 아니라 바이트나 문자열로도 사용할 수 있다.

UI 에 바인딩된 고 타입은 스레드에 안전해야 한다. 각각 바인딩된 것 자체로 고루틴에서 실행되기 때문인데, 그래서 counter 구조체에서 뮤텍스를 사용한다.

위 경우에서 원자성을 사용할 수 있게 된다.
하지만 보다 복잡한 경우엔 적절한 동기화를 사용하자.

카운터 리시버

func (c *counter) Add(n int) {
	c.Lock()
	defer c.Unlock()
	c.count = c.count + n
}

func (c *counter) Value() int {
	c.Lock()
	defer c.Unlock()
	return c.count
}

Add 리시버는 스레드에 안전하게 잠금 풀기를 하여 countn 을 더한다.

Value 리시버도 마찬가지로 안전하게 잠금 풀기를 하여 count 를 반환한다.

함수 카운터

이제 인덱스 UI 를 띄우며, 카운터 객체를 바인딩해보자.

func Couter() {
	...
}

Lorca

Lorca 를 띄워보자.

args := []string{}
if runtime.GOOS == "linux" {
	args = append(args, "--class=Lorca")
}
ui, err := lorca.New("", "", 480, 320, args...)
if err != nil {
	log.Fatal(err)
}
defer ui.Close()

lorca.New 를 통해 마지막 인수에 각종 정보를 전달한다.

두 번째 줄을 확인하면 사용자의 운영체제가 리눅스일 경우 --class=Lorca 정보를 전달한다.

추가적으로 GUI 의 너비와 높이를 지정하고 defer 을 통해 연기된 Close() 를 실행한다.

UI 바인드

ui.Bind("start", func() {
	log.Println("UI is ready")
})

c := &counter{}
ui.Bind("counterAdd", c.Add)
ui.Bind("counterValue", c.Value)

형식은 ui.Bind(name string, f interface{}) 와 같으며
name 은 js 에서의 함수이름으로 사용할 변수 이름이다.

그래서 위와 같은 경우는 "start"UI is ready 로그 출력

"counterAdd"c.Add, "couterValue"c.Value 를 바인드한다.

HTML 로드

ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
	log.Fatal(err)
}
defer ln.Close()
go http.Serve(ln, http.FileServer(http.FS(fs)))
ui.Load(fmt.Sprintf("http//%s/index.html", ln.Addr()))

앞서 Lorca의 작동원리로 설명한 바가 있듯이, 크롬을 통하여 원격 바인딩하여 소켓을 통해 주고받기 때문에 net.Listen 함수로 네트워크 연결을 한다.

그럼 Lorca 가 사용자의 설치된 크롬을 통하여 주고받는다.

그 후 http.Serve 함수를 통해 해당 네트워크로 HTTP 연결을 수락하고 새로운 고루틴을 만든다. 그리고 이전에 임베드로 저장해 두었던 index.html 파일로의 경로로 리다이렉션한다.

그리고 이전에 생성하였던 ui 에 해당 주소의 내용을 로드한다.

자바스크립트

ui.Eval(`
	console.log("Hello, world!");
	console.log('Multiple values:', [1, false, {"x":5}]);
`)

위와 같이 자바스크립트 문법을 Eval 함수를 통하여 JS 코드를 디버깅도 할 수 있다.

종료

sigc := make(chan os.Signal)
signal.Notify(sigc, os.Interrupt)
select {
case <-sigc:
case <-ui.Done():
}

채널을 생성하여 인터럽트되거나 UI 를 종료할때 까지 기다린다.

index.html

<!doctype html>
<html>
<head>
    ...
</head>
<body onload=start()>
  ...
</body>
</html>

위 body 에서의 onload=start() 를 살펴보면 앞서 바인드 하였던 start 를 호출하고 있는 것을 볼 수 있다.

앱을 실행하면 위와 같이 body 가 로드되면서 go 의 start 가 실행되었다는 것을 확인할 수 있다.

<script>
    const counter = document.querySelector('.counter');
    const btnIncr = document.querySelector('.btn-incr');
    const btnDecr = document.querySelector('.btn-decr');

    // GO 기능이 비동기적이기 때문에 async/await 를 사용한다.
    const render = async () => {
        counter.innerText = `Count: ${await counterValue()}`;
    };

    btnIncr.addEventListener('click', async () => {
        await counterAdd(1); // Call Go function
        render();
    });

    btnDecr.addEventListener('click', async () => {
        await counterAdd(-1); // Call Go function
        render();
    });

    render();
</script>

위의 코드도 동일하게 클릭 이벤트 리스너 내에서 go 에서 바인딩되었던 함수를 호출하고 있는 것을 확인할 수 있다.

go 의 기능은 비동기적이기 때문에 자바스크립트에서도 동일하게 비동기를 사용하기 위해 async/await 키워드를 사용해야 한다.

profile
Study

0개의 댓글

관련 채용 정보