백앙금으로 앙금쿠키를 만드려고 아몬드가루를 1Kg 샀는데 아몬드가루가 너무 많이 남아서 파운드 케이크를 만들어 먹기로 했다.

재료

아몬드가루 200g

바닐라 익스트랙 6방울

올리브오일(버터 없어서 그냥 올리브오일 넣음) 20g

바나나 2개

베이킹 파우더 5g

소금 2g

계란 2개

에리스리톨 + 스테비아 가루 30g

 

 

나름 설탕을 안먹겠다고 대체감미료인 스테비아를 사봤는데 손으로 찍어먹었을 때 생각보다 화한맛이 많이 나서 걱정을 했는데 파운드 케이크로 먹으니까 화한맛은 나지 않았다.

주의할점은 에리스리톨을 너무 많이 먹으면 설사를 한다고 하니 적당히 먹어야 한다. 적정량은 인터넷 정보마다 다 다른데 보통 성인 기준 20g 정도인 것 같다. 어린아이들은 더 적게 먹어야 한다고 한다.

어린애들이 있으면 그냥 설탕을 넣거나 아니면 어짜피 바나나가 달기 때문에 안넣어도 좋을것 같다.

 

 

만든 방법

 

1.

올리브오일 + 바닐라 익스트랙 + 소금 + 스테비아 + 계란를 넣고 섞는다.

여러 블로그를 찾아보니 크림화 얘기를 많이 하길래 전동 휘핑기로 섞어주었다.

 

2.

바나나를 으깨서 위에서 만든거에다가 섞어주었다.

사실 으깨기 귀찮고 바나나가 너무 익어서 그냥 껍질만 까서 전동휘핑기로 휘저어줬는데 바나나가 물렁해서 잘 섞였다.

 

3.

아몬드가루와 베이킹파우더를 섞어서 체에 쳐서 위의 재료에 넣어준다.

굳이 체에 내리는 이유는 가루가 뭉치는것을 막기 위해서이다. 케이크를 만들때 밀가루같은 가루가 덩어리져있으면 하나하나 부수기도 힘들고 먹을때 흰가루 뭉친거 나오면 입맛이 떨어지기 때문에 체에 쳐서 내려주는게 좋다.

 

4. 

재료를 모두 섞어서 반죽을 만들어준다.

주의할 점은 반죽을 막 휘저어서 섞으면 안되고 # 모양으로 말 그대로 칼로 썰듯이 약하게 섞어주어야 한다.

그렇지 않으면 다 구워졌을 때 가운데가 포슬포슬하게 만들어지지 않고 떡처럼 만들어진다.

처음 몇번은 이래가지고 섞이긴 하나 싶었는데 그래도 반복하면 재료들이 완전히 섞이게 된다.

 

5.

오븐을 예열하고 구워준다.

나는 온도를 160도로 맞춰놓고 주워주었는데 25분 구웠더니 좀 부족한것 같아서 10분 굽고 5분 더 구워서

총 40분을 구워주었다.

 

 

이것도 안쪽이 살짝 떡처럼 된 느낌이 없지않아 있는데

이전에 만들었을 때는 안에 공기도 하나도 없고 그냥 완전 떡이 되었었기 때문에 이정도면 전보다 훨씬 낫네 하는 느낌으로 맛있게 먹었다.

 

소금을 3g 넣었더니 좀 짠맛이 나는것 같아서 소금은 2g만 넣어도 될것 같다.

'생활 > 요리' 카테고리의 다른 글

뒷다리살 수비드로 해먹기  (0) 2022.04.11
햄버거빵 굽기  (0) 2020.12.24
전에 만든 팥양갱  (0) 2020.12.24
앙금쿠키 만들기  (0) 2020.12.24

요즘 뒷다리살이 인기가 많아졌다고 한다. 우리집 근처의 식자재마트에서는 뒷다리살을 싸게 파는데 요즘은 물가때문인지 인기때문인지 가격이 많이 올랐다.

 

그래도 아직 5000원이면 1Kg을 살 수 있기 때문에 나도 뒷다리살을 자주 요리해서 먹는 편이다.

 

뒷다리살을 구워서도 먹고 수육으로도 먹고 스테이크로도 해먹었는데 이번에는 수비드로 해봤더니 과장을 좀 보태서 햄같은 느낌으로 먹을 수 있었다.

 

아무래도 지방이 적다보니 퍽퍽한 느낌은 어쩔 수 없지만 그래도 질기지는 않고 딱 먹기 좋게 만들어졌다.

 

만드는데 필요한 준비물도 전기밥솥과 지퍼백만 있으면 되기때문에 쉽게 만들 수 있었다.

 

 

우선 뒷다리살을 준비한다.

 

저대로 먹으면 너무 클것같아서 상하로 잘라주었다.

 

그리고 고기 냄새를 없애기 위해 카레가루를 발라주었다.

 

보통은 잡내를 잡기 위해 허브솔트를 뿌린다고 하는데 나같은 경우는 허브솔트 향이 너무 진해서 먹기가 힘들었기때문에 카레가루를 발라주었다.

 

그 후에는 고기를 지퍼백에 넣고 물을 이용해 최대한 공기를 빼내 진공 비슷하게 만들어주었다.

 

진공포장기가 있으면 진공포장기로 하면 좋은데 우리집 진공포장기가 갑자기 작동을 안해서 이번에는 그냥 지퍼백으로 해주었다.

 

그 후 전기밥솥을 보온상태로 놓고 고기를 넣어주었다.

 

제대로 하는 사람들은 수비드 머신같은걸 사서 온도조절을 꼼꼼히 해주는데 나같은경우는 전문가도 아니고 해서 그냥 보온 밥솥에 넣어주었다.

 

보온상태에서 물을 만져보니 아무래도 80도까지는 아니겠지만 70도정도는 되지 않을까 싶은 온도였다.

 

한 12시간 정도 후에 꺼낸것 같은데 이전에 한거랑 비교를 해보니까 고기의 식감이 다 달랐다. 넣은 시간에 따라서 단단해진적도 있고 감자탕 고기처럼 연해진적도 있는데 정확히 어떻게하면 어떻게되는지는 잘 모르겠다.

 

꺼낸 고기에서는 물이 많이 생겨있는데 이전에 한번은 그 물로 카레를 하고 카레에 고기도 넣었더니 아주 맛있었다.

 

바로 꺼낸 고기는 너무 물컹물컹해서 자르기가 힘들기때문에 냉장고에 넣어두었다가 차갑게 식었을 때 칼로 자르면 아래 사진처럼 자를 수 있다.

 

 

 

마치 햄이나 차슈 같은 모양이 되는데 맛은 물론 그정도는 아니지만 그래도 요리에 넣어 먹으면 맛있다. 아무래도 뒷다리살이라 지방이 부족한 편이기때문에 기름을 많이 두르고 햄처럼 구워먹어도 맛이 좋을것 같다.

카레가루 외에는 양념이 되어있지 않기때문에 소스를 사용해서 요리를 하면 파는 요리처럼 되는게 아닐까? 하는 생각도 들었다.

아니면 수비드를 할 때 양념을 더 해주면 그대로 먹어도 간이 되어있다거나 하는것도 있을까?

 

고기가 아직 핑크빛이 도는데 이건 덜익은게 아니기때문에 먹어도 상관 없다고 한다. 불안하다면 한번 굽거나 조리해서 먹으면 좋다. 이렇게 조리한 뒷다리살은 다시 익혀도 질겨지지 않기때문에 다른 요리에 넣어서 먹기도 아주 좋았다.

나같은 경우는 라면에도 넣어먹고 까르보나라에도 넣어먹고 구워서 덮밥처럼 먹으니 맛있었다.

 

 

이렇게 맛있고 가격도 싼 뒷다리살은 나만 먹고싶었는데 인기와 가격이 올라버려서 대견하기도 하고 슬프기도 하다.

 

'생활 > 요리' 카테고리의 다른 글

아몬드가루로 바나나 파운드케이크 만들기  (0) 2022.05.06
햄버거빵 굽기  (0) 2020.12.24
전에 만든 팥양갱  (0) 2020.12.24
앙금쿠키 만들기  (0) 2020.12.24

이미지 분류를 하고싶은데 공부는 하기 싫어서 알아보던 도중 아주 좋은 프로그램을 발견했다.

바로 Lobe라는 프로그램인데

얼마나 좋은지 마이크로소프트에서 인수를 했다고 한다.

 

https://www.lobe.ai/

 

Lobe | Machine Learning Made Easy

Download the free, easy to use app that helps you train custom machine learning models and ship them in your app.

www.lobe.ai

 

프로그램을 간단히 설명하면 

이미지 셋을 넣고 라벨을 적어주기만 하면

이미지 셋으로 알아서 트레이닝을 한 후에

이미지 분류 모델을 만들어주는 프로그램이다.

 

UI도 단순해서 사용하기 쉽고 분류된 이미지도 생각보다 정확도가 높았다.

 

다만

 

이미지를 분류 한 후 Lobe 프로그램 내에서 직접 이미지를 분류해 주는 기능을 제공해 주지는 않았다.

이미지 분류 모델을 출력해주거나 혹은 API를 제공해서 어떤 이미지인지 알려주는 기능을 제공하였다.

 

 

모델을 출력해서 사용하는 방법도 있긴 하겠지만 가장 간단하게 분류를 할 수 있는 방법은

API를 이용하는 방식이라는 생각이 들어서

 

Lobe와 직접 API 통신을 해서 이미지를 분류해주는 간단한 프로그램을 만들었다.

 

 

 

Lobe에서 그림 이미지, 현실 이미지, 인터넷 문서 이미지를 넣어 트레이닝 하고

테스트를 해 보았다.

위의 이미지 18개를 이용하여 분류가 잘 되는지 테스트를 해보도록 하겠다.

 

 

프로그램을 실행시켜서 폴더 경로와 Lobe API URL를 입력해 준 후

목록읽기 -> 분류 -> 파일 이동 순으로 눌러주면 된다.

 

분류가 완료되면 Lobe에서 분류해준 대로 파일을 이동시킨다.

 

그림 이미지로 분류된 이미지들

 

현실 이미지로 분류된 이미지들

 

 

문서 이미지로 분류된 이미지들

 

 

 

보면 현실 이미지에 아즈모단도 들어있고 하는데

아무래도 완변하게 이미지가 분류되지는 않았다.

이건 내가 학습시킨 이미지 세트 문제일 수도 있고 이미지 자체가 분류하기 힘든 형태라서 그럴 수도 있을 것 같다.

 

내가 저 세팅으로 거의 만개 정도의 이미지를 분류해 보았는데

내가 봤을때 잘못 분류됐다 싶은 이미지는 체감상 거의 3% 이하정도?

거의 97퍼센트 이상 정도는 정확하게 분류가 되었다고 생각된다.

 

요즘시대는 저렇게 머리 좋은사람들이 다 만들어주면

나같은 사람들은 이용만 하면되니 참 세상이 좋아졌다는 생각이 든다

 

 

코드는 여기 올려놓았습니다.

https://github.com/AhatLi/LobeClient

 

GitHub - AhatLi/LobeClient

Contribute to AhatLi/LobeClient development by creating an account on GitHub.

github.com

 

'IT > 개발' 카테고리의 다른 글

프로미스 동작 분석하기(2)  (0) 2021.08.27
프로미스 동작 분석하기(1)  (0) 2021.08.21
자바스크립트의 배열  (0) 2021.08.01
SonicClassifier 웹페이지 추가 및 완성  (0) 2021.07.16
Airsonic 플레이리스트 정렬  (0) 2021.07.11

회사에서 Vitrualbox를 사용해서 Centos를 설치한다음

네트워크 설정을 다 해주었는데 인터넷이 되지 않는 상태가 되었다.

 

구체적으로는
버추얼박스 Centos 가상머신을 생성 후 가상어댑터1 에는 호스트 전용 어댑터 가상어댑터2 에는 NAT를 설정 해 주고
Centos를 설치하였더니 enp0s3, enp0s8 이렇게 두개의 어댑터가 생성되었다.
enp0s3가 호스트 전용 어댑터이기 때문에

ONBOOT="yes"
BOOTPROTO="static"  
IPADDR=192.168.56.120  
GATEWAY="192.168.56.1"  
NETMASK="255.255.255.0"  
DNS1="8.8.8.8"  
DNS2="1.1.1.1"

이렇게 설정을 추가해 주고

enp0s8 에는 ONBOOT="yes" 만을 해 준 다음 service network start 를 해 주었다.

이 상태에서 ping 8.8.8.8 을 하였더니 연결이 되지 않는 상태가 되었다.

 

IP 충돌이라도 있나 싶어서 BOOTPROTO 항목을 dhcp로 설정하고 IPADDR 항목을 없앴더니

ping 8.8.8.8 이 정상적으로 동작했다.

 

DHCP에서 설정해준 IP는 192.168.56.119 였기 때문에 BOOTPROTO를 static으로 다시 변경 후

IPADDR을 192.168.56.119 로 설정하고 service network start를 해 주었는데

다시 인터넷이 되지 않았다...

 

 

그래서 인터넷을 뒤져보면서 이것저것 해보다가 ifup, ifdown 을 사용하여

네트워크 디바이스를 내리고 올리는 동작이 있어서 실행해 보았다.

 

ifdown enp0s3
ifup enp0s3

 

그런데 이 이후부터 인터넷이 정상동작하였다.

이해가 잘 안가서


route -n

 

명령어를 실행해서 라우팅 테이블을 보았는데

 

인터넷이 안될때의 라우팅 테이블

 

인터넷이 잘 될때의 라우팅 테이블

라우팅 테이블의 우선순위가 바뀐것을 확인하였다.

아무래도 enp0s3 디바이스를 내렸다가 다시 올렸기때문에 우선수위가 enp0s8 보다 올라간것 같다.

 

이 상태에서 재부팅을 하거나 네트워크 서비스를 재시작하면 우선순위가 위쪽 그림 상태로 돌아오기 때문인지

다시 인터넷이 안되게 되었다.

 

브릿지 어댑터를 사용하면 저런 문제 없이 VM간 통신도 되고 인터넷도 잘 되겠지만 회사 네트워크에서는 브릿지 어댑터를 사용할 수가 없다.

네트워크 디바이스의 우선순위가 저렇게 된 이유는 아마 내가 버추얼박스의 네트워크 어댑터를 잘못 설정해서 그런거겠지? 아마 내가 뭔가 잘못했을거다...

아마 NAT + 호스트 어댑터 상태로 해야하는데 호스트 어댑터 + NAT 로 설정해서 그런게 아닐까 싶기는 하다.

 

 

네트워크 디바이스를 올렸다가 내리는 것으로 일시적으로라도 우선순위를 바꿔서 인터넷이 되도록 설정을 할 수 있었다.

하여간 별게 다 문제임

'IT > 기타' 카테고리의 다른 글

Jellyfin 미디어 서버 소개  (2) 2020.12.24
SMPlayer + SVP 4 설정하기  (0) 2020.12.24
드미트리 렌더와 스플래시  (0) 2020.12.24

이전에 쓴 글에 이어서 프로미스를 구현한 뒤 예제를 통해 동작을 확인해보겠습니다.

 

 

이전 포스트

https://ahat-li.tistory.com/24

 

프로미스 동작 분석하기(1)

자바스크립트 공부를 하다가 프로미스에 대해서 공부한 내용을 정리해볼까 한다. 프로미스가 내부에서 어떻게 동작하는지 프로미스를 간단하게 구현하고 예제를 통해 분석해보았다. 아래 코드

ahat-li.tistory.com

 

 

예제2)

new _Promise((res, rej) => {
     res(1);
})
.then((e) => {
     console.log("aa ", e);
     return e + 1
})
.then((e) => {
     console.log("bb ", e);
})

 

예제2는 예제1과 비슷하지만 프로미스 내부의 res 함수를 실행시키는 방법이 다릅니다.

예제 1은 setTimeout(() => { res(1)}, 3000); 이렇게 res 함수가 setTimeout 안에 들어있지만

예제 2에서는 res가 바로 실행되게 됩니다.

 

예제를 분석하여 어떤 방식으로 프로미스 동작이 이루어지는지 확인해보겠습니다.

우선 new 를 통해 첫번째 프로미스 객체가 생성됩니다.

그리하여 프로미스 생성자가 실행되는데 생성자 내부에서 인자로 받은 collback 함수 객체를 바로 실행합니다.

저 함수 객체는

(res, rej) => {

     res(1);

}

이런 객체인데 보시면 res 함수를 바로 실행시키고 있습니다.

res와 rej가 resolve, reject 함수과 연결되어 있기 때문에

res를 통해 첫번째 프로미스 객체의 resolve 함수가 바로 실행되게 됩니다.

 

resolve 메서드를 보면 프로미스 객체의 상태로 fulfilled로 변경하고있습니다.

프로미스1의 promiseResult에 res를 통해 전달된 1이라는 값이 들어갑니다

또한 프로미스1의 fulfilledFun 함수객체를 실행하게 되어있지만

프로미스1의 fulfilledFun가 undefined 이기 때문에 아무런 동작도 하지 않습니다.

프로미스 생성자의 모든 동작이 완료되었기 때문에 콜스택이 비워지게 됩니다.

 

 

이후 프로미스1의 메서드인 then이 실행됩니다.

예제1 에서는 then이 resolve보다 먼저 실행되었지만

예제2 에서는 then 보다 resolve가 먼저 실행되었다는 차이점이 있습니다.

 

그래서 then 메서드의 동작도 달라지는데요 프로미스1의 상태가 fulfilled이기 때문에 

return new _Promise(resolve =>   resolve(onFulfilled(this.promiseResult)));

이런 동작을 하게 됩니다.

 

 then의 메서드로 전달된 onFulfilled 함수 객체를 실행합니다.

then에 전달된 함수 객체는

(e) => {

     console.log("aa ", e);

     return e + 1

}

이런 모양을 하고 있습니다.

이 함수객체를 실행하기 때문에 aa 1 이라는 콘솔을 출력하고 2를 리턴하게 됩니다.

return new _Promise(resolve =>   resolve(2);

 

이제 then 메서드의 리턴값이 될 프로미스 객체를 생성하기 위해 

2번째 프로미스의 생성자를 실행하게 됩니다.

프로미스2의 상태값이 전달되고 프로미스2 생성자에 전달된 callback 함수객체를 바로 실행하게 됩니다.

프로미스2의 생성자에 전달된 callback 함수객체는

resolve =>   resolve(2) 이런 모양입니다.

callback 메서드를 바로 실행하기 때문에 프로미스2의 resolve 메서드가 실행되게 되는데 

여기서 프로미스2의 상태가 fulfilled로 바뀌고 promiseResult에는 2가 들어갑니다.

프로미스2의 fulfilledFun은 undefined 이기 때문에 아무런 동작도 하지 않습니다.

 

이후 프로미스2의 생성이 완료되고 콜스택은 다시 비워지게 됩니다.

 

 

 

다음은 프로미스2의 메서드인 then이 실행되게 됩니다.

전체적인 동작은 프로미스1의 then과 똑같습니다.

프로미스2의 상태가 fulfilled 인 상태에서 then 메서드가 실행되었기 때문에

then에 인자로 전달된 함수객체가 바로 실행되어 

bb 2 라는 콘솔 텍스트가 출력됩니다.

 

이후 프로미스3 객체가 생성되어 프로미스3의 생성자가 실행됩니다.

 

 

프로미스3의 생성자에 전달된 callback 함수에 의해서 프로미스3의 resolve 함수가 실행됩니다.

다만 프로미스3의 then 메서드가 실행되지 않기때문에 프로미스3의 생성자를 끝으로 예제2의 동작은 완료됩니다.

 

위에서 살펴본 내용을 요약하면 다음과 같이 볼 수 있을것 같습니다.

 

resolve 메서드가 then 메서드보다 먼저 실행될 경우

resolve 메서드가 실행된 시점에 프로미스 객체의 상태가 미리 fulfilled 상태로 변경되고

이후 then 메서드가 실행될 때 프로미스 객체의 상태가 fulfilled 일 때 인자로 들어온 함수 객체를 바로 실행시킴

이후 새로운 프로미스 객체를 생성하고 그 객체의 상태를 fulfilled 로 변경 후 리턴함

 

 

다만 자바스크립트 공식 프로미스에서도 저렇게 동작하는지는 나중에 제대로 확인을 해 보아야 할 것 같습니다...

 

이렇게 프로미스의 동작이 어떻게 이루어지는지 간단한 예제를 통해 알아보았는데

예제는 간단하지만 동작 자체는 상당히 복잡했습니다.

 

 

 

사실 위의 예제에서 사용하기 위해 구현한 프로미스 클래스에는 문제점이 있습니다.

프로미스 내부에서 저렇게  프로미스 객체를 생성하여 리턴하는 방식으로 

비동기 동작을 여러개 연결할 수 있습니다.

하지만 이번 예제를 위해 구현한 프로미스 클래스에서는 저런 방식의 동작이 불가능합니다...

 

이를 일부 함수동작을 바꾸는 것으로 동작하게 만들 수 있습니다.

다음 포스트에서는 일부 함수 내용을 수정하여 위와 같은 예제가 동작할 수 있도록 하겠습니다.

 

감사합니다.

자바스크립트 공부를 하다가 프로미스에 대해서 공부한 내용을 정리해볼까 한다.

프로미스가 내부에서 어떻게 동작하는지 프로미스를 간단하게 구현하고 예제를 통해 분석해보았다.

 

아래 코드는 프로미스를 간단히 구현한 코드이다.

const _Promise = class {
constructor(callBack) {
this.status = "pending";
this.promiseResult = undefined;
this.fulfilledFun = undefined;
this.rejectedFun = undefined;
callBack(
(v) => this.resolve(v),
(v) => this.reject(v));
}
_doTask(t) {
	 if (t !== undefined) {
	      setTimeout(t, 0);
	 }
}
resolve(v) {
if (this.status !== "pending") {
	return this;
}
this.status = "fulfilled";
this.promiseResult = v;
this._doTask(this.fulfilledFun)
}
then(onFulfilled) {
switch (this.status) {
case "pending": {
return new _Promise(resolve => {
       this.fulfilledFun = () => {resolve(onFulfilled(this.promiseResult))};
})
}
case "fulfilled": { return new _Promise(resolve => resolve(onFulfilled(this.promiseResult))); }
case "rejected": { break; }
}
return this;
} 

reject(err) {}
catch (onRejected) {}
}

 

아래 블로그를 참고하였습니다.

https://velog.io/@teihong93/Promise-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-1

 

Promise 구현하기 (1)

자바스크립트의 비동기 처리는 프로미스가 있기 전과 후로 나뉠 정도로, 프로미스라는 개념은 자바스크립트 진형에 많은 영향을 주었습니다.지금이야 async await 이라는 더 직관적이고 깔끔한 문

velog.io

 

인터넷을 찾아보니 프로미스를 구현한 예제는 많이 있는데 그 중에서 가장 간단한 예제를 골라 더 간단하게 만든 것이다.

다만 then과 catch 부분은 사용하는 상태만 다를 뿐이지 내부 구현 자체는 비슷하다.

그래서 코드를 더 간단하게 하기 위하여 catch 관련 부분은 코드를 제거한 뒤 분석해 보았다.

 

간단한 예제 2가지를 사용하여 내부에서 어떻게 동작하는지 살펴보았다.

 

예제 1)

new _Promise((res, rej) => {
     setTimeout(() => { res(1)}, 3000);
})
.then((e) => {
     console.log("aa ", e);
     return e + 1
})
.then((e) => {
     console.log("bb ", e);
})

간단한 프로미스 예제이다.

딱봐도 3초 후에 aa 1이 출력되고

그 후 bb 2가 출력될 것이라고 예상할 수 있다.

 

 

예제 분석

예제1이 실행되면 우선 첫번째 프로미스 객체를 생성한다.

그로 인해서 프로미스1의 생성자가 실행되게 된다.

생성자 함수가 실행되면 해당 프로미스 객체의 상태는 pending 상태가 된다.

생성자 함수를 보면 인자로 받은 콜백이라는 이름의 함수객체를 실행시키는데

이로 인해서 res와 rej가 프로미스1의 resolve, reject와 연결되게 된다.

이후 함수객체로 전달된 setTimeout(() => { res(1)}, 3000); 명령 또한 실행되게 된다.

setTimeout 함수는 실행되면 Web API 영역으로 이동하여 3초동안 대기하게 된다.

프로미스1 객체가 생성되었고 그 다음은 프로미스 1의 메서드인 then 이 실행된다.

then 메서드에서는 프로미스 객체를 새로 생성하여 리턴하는 동작을 한다.

프로미스2 객체의 생성자가 실행되며 이때 전달된 함수 객체를 보면

화살표 함수로 이루어져 있고 this.fulfilledFun 라는 부분이 있다.

 

화살표 함수에서 this는 자신을 실행시킨 외부객체의 this를 가리키게 되므로 여기에서의 this는 프로미스1이 된다.

즉 프로미스1의 fulfilledFun 에 () => {resolve(onFulfilled(this.promiseResult))} 라는 값을 넣게 된다.

resolve에는 this가 붙지 않았으니 프로미스2의 resolve가 되고 onFulfilled 부분은 then 메서드에 전달된 함수객체가 된다.

그리고 나서 두번째 then이 실행되는데 두번째 첫번째 then 에서 프로미스2 객체가 리턴되었으니 프로미스2의 then 메서드가 된다.

이전 동작과 똑같이 프로미스 3 객체가 생성되고 프로미스2의 fulfilledFun에 값이 들어가게 된다.

 

 

이후에는 특별한 동작이 없다가 Web APIs에 들어간 setTimeout 함수가 완료되어 내부의 res 함수가 실행되게 된다.

 

 

이후 프로미스1의 resolve 메서드가 실행되게 되는데

진행되면서 프로미스 1의 상태가 fulfilled 상태로 변한다.

보면 프로미스1의 fulfilledFun 값을 setTimeout 에 감싸 실행한다.

fulfilledFun 함수를 setTimeout 으로 실행하는 이유는 프로미스는 자신이 실행하는 함수를

마이크로 태스크큐에 넣어 실행하지만 그 부분은 따라할 수가 없으니 비슷하게 태스크큐에 넣어서 실행하기 위함이다.

Web APIs 에 전달된 함수는 태스크큐로 전달된다.

 

그리하여 

() => {resolve(onFulfilled(this.promiseResult))} 이런 함수가 실행되게 되는데

우선 onFulfilled(this.promiseResult) 이 부분부터 콜스택에 들어가게 된다.

onFulfilled 라는건 아까전에 첫번째 then에 전달된 함수객체인데

구체적으로는 

(e) => {

     console.log("aa ", e);

     return e + 1

}

이거다.

여기서 e는 프로미스1 객체의 promiseResult이므로 1이 된다.

그렇게 aa 1 이라는 콘솔 텍스트를 출력하고

2를 리턴한다.

 

이후 2가 리턴되었기 때문에 

() => {resolve(2)} 

이런 함수가 콜스택에 들어가게 된다.

여기에서 resolve는 프로미스2의 resolve가 되는데 

resolve 함수를 보면 아까와 마찬가지로 자신의 fulfilledFun 을 setTimeout에 넣고 실행한다.

 

프로미스2의 fulfilledFun은 

(e) => {

     console.log(bb ", e);

}

가 되고 e는 2이기 때문에 

bb 2 라는 콘솔 텍스트를 출력하게 된다.

 

 

이후 프로미스3의 resolve 함수도 실행하지만 

프로미스3은 fulfilledFun 값이 undefined 이므로 아무것도 실행하지 않는다.

 

이렇게 

aa 1

bb 2

라는 텍스트를 출력하는것으로 프로미스 예제 1번이 완료되었다.

 

 

 

 

예제 1을 사용하여 프로미스를 분석해 보았는데

여기서는 프로미스의 then 메서드가 resolve 메서드보다 먼저 실행된 것을 알 수 있다.

프로미스 객체에 then 메서드의 인자로 들어온 함수객체를 등록시키고

resolve 메서드가 실행되면 등록시켜놓은 함수객체를 실행시키는 방식이라고 요약할 수 있을것 같다.

 

이와 다르게 예제2는 resolve 메서드가 then 메서드보다 먼저 실행되므로 

동작방식이 조금 달라진다.

최근 자바스크립트에대해서 공부를 하고있는데 배열에대해서 공부한 내용을 정리해보려고 한다.

자바스크립트 배열의 특징

  1. 자바스크립트 배열은 번호가 메겨진 인덱스를 갖는 특별한 유형의 객체이다.
    • 자바스크립트의 배열은 다른 언어의 배열과 유사한 “객체”이다.
      (index를 key로 가지며 length를 갖는 특수 객체)
  2. 배열에 여러 다른 유형의 변수, 오브젝트, 함수를 요소로 가질 수 있다.
    • (js는 함수를 객체로 취급하기 때문)
  3. 다른 언어의 배열(밀집배열)과는 다르게 메모리가 연속적으로 이어져 있지 않음(희소배열)

자바스크립트 배열의 장점

  • 배열의 요소를 위한 메모리 공간이 동일한 크기를 갖지 않아도 됨.
  • 그렇기 때문에 다양한 요소를 배열 내에 넣을 수 있음
  • 요소를 삽입 삭제하는 경우에는 일반적인 배열보다 빠른 성능

 

 

자바스크립트 배열의 단점

  • 연속되지 않은 메모리로 인하여 다른 언어의 배열에 비하여 배열 요소 참조에 대한 속도가 느림

 

 

V8엔진에서의 배열

V8엔진을 비롯한 최신 자바스크립트 엔진에서는 위의 단점을 극복하기 위하여 희소배열과 밀집배열을 모두 지원하며 상황에 맞는 배열이 자동으로 사용됨

0번째 인덱스부터 순서대로 시작하는 키 값 을 사용하는 배열을 생성하는 경우 엔진은 배열의 각 요소를 선형 저장 버퍼 형태(밀집배열)로 저장함.

예) array = [1,2,3] 

위처럼 중간이 비어 있는 형태의의 배열을 생성하거나 배열의 요소를 삭제할 경우 배열의 형태는
해시 테이블 형태로(희소배열) 생성, 변경된다.

예)
const array = []; 
array[0] = 1; 
array[1] = 2; 
array[3] = 3;

성능면에서 밀집배열이 희소배열보다 성능이 뛰어나기 때문에

가능하다면 밀집배열의 형태로 배열을 사용하는것이 좋다.

 

 

자바스크립트 배열의 성능을 올리기 위해서는(밀집배열을 사용하기 위해서는)

  1. 인덱스가 0부터 시작되는 연속된 키 값을 사용하는 배열을 만든다.
  2. 배열의 크기를 미리 선언하지 않는다.
  3. 배열의 요소를 삭제하지 않는다.

 

 

자바스크립트 배열 선언방법

  1. 리터럴
    var arr = []; arr[0]; undefined
    var arr = [1, 2]; arr[0]; 1
    var arr = [5, "three", 1]; arr[0]; 5
  2. 객체
    var arr = new Array(); arr[0]; undefined
    var arr = new Array(3); arr[0]; undefined
    var arr = new Array(1, 2, 3, 4, 5); arr[0]; 1

객체 선언방법 사용 시 Array(3); 형태로 선언 할 경우

Length가 3인 배열이 생성되는 등 사용방법에 혼동이 발생할 수 있음
특별한 이유가 없다면 리터럴 형태로 배열을 사용하는것이 좋다.

 

 

선언방식별 속도 비교

console.time('using[]')
for(var i=0; i<2000000; i++){var arr = []};
console.timeEnd('using[]')

console.time('using new')
for(var i=0; i<2000000; i++){var arr = new Array()} 
console.timeEnd('using new')

일반적으로 new Array로 배열을 생성하는 것이 시간이 더 오래 걸리는 것을 확인

  • 이유 :
    배열을 리터럴 방식으로 생성할 경우 자바스크립트 컴파일러 시점에서 [] 문자를 읽자마자 이미 배열을 생성하겠다는 것이 명확함
    반대로 new Array 의 경우 더 많은 단어로 이루어져 있으며 new 문자열을 읽고 이후 Array 문자열을 읽은 후 배열을 생성하겠다는 것이 명확해짐

또한 각 선언방식을 해석하면 아래와 같이 분석할 수 있는데

[]: ARRAY_INIT
[1]: ARRAY_INIT (NUMBER)
[1, foo]: ARRAY_INIT (NUMBER, IDENTIFIER)
new Array: NEW, IDENTIFIER
new Array(): NEW, IDENTIFIER, CALL
new Array(5): NEW, IDENTIFIER, CALL (NUMBER)
new Array(5,4): NEW, IDENTIFIER, CALL (NUMBER, NUMBER)
new Array(5, foo): NEW, IDENTIFIER, CALL (NUMBER, IDENTIFIER)

Array 라는 문자열은 IDENTIFIER 즉 식별자로써 분석이 됨.
자바스크립트 컴파일러 입장에서는 Array 라는 문자열이 배열을 생성하는 식별자 라는 것을 알기 위해서 식별자 목록을 조회하게 됨

 

 

 

추가 테스트

위쪽에서는 var 형태로 배열을 생성하였고 리터럴 형태로 사용했을 때 배열의 생성속도가 더 빠른것을 확인하였음
추가로 let, const 형태로 배열을 선언하는 테스트를 해보았다.

console.time('using var []')
for(var i=0; i<2000000; i++){var arr = []};
console.timeEnd('using var []')
console.time('using var new')
for(var i=0; i<2000000; i++){var arr = new Array()} 
console.timeEnd('using var new')

console.time('using let []')
for(var i=0; i<2000000; i++){let arr1 = []};
console.timeEnd('using let []')
console.time('using let new')
for(var i=0; i<2000000; i++){let arr2 = new Array()} 
console.timeEnd('using let new')

console.time('using const []')
for(var i=0; i<2000000; i++){const arr1 = []};
console.timeEnd('using const []')
console.time('using const new')
for(var i=0; i<2000000; i++){const arr2 = new Array()} 
console.timeEnd('using const new')

결과

크롬, Node.js, 파이어폭스, 엣지브라우저에서 테스트를 진행한 결과

엔진별로 차이가 있지만 현재 가장 많이 사용되는 자바스크립트 엔진인 V8 엔진에서는
let, const로 배열을 생성 시 리터럴 방식과 객체방식간의 큰 성능 차이는 없는 것으로 보임

그러나 파이어폭스와 엣지 브라우저에서는 확연한 성능차이가 확인되었으며
사용자의 브라우저 환경을 특정할 수 없고 더 간편하고 안전한 배열 생성방식인 리터럴 방식으로 배열을 생성하는 것이 좋음
또한 배열은 가능하다면 const로 생성하는것이 좋다.

 

 

 

 

참고

http://45.55.116.124/javascript.html
http://www.ktword.co.kr/test/view/view.php?m_temp1=257
https://evan-moon.github.io/2019/06/25/hashtable-with-js/
https://poiemaweb.com/js-array-is-not-arrray
https://dh2note.tistory.com/115
https://ljlm0402.netlify.app/javascript/performance/
https://junwoo45.github.io/2019-11-04-memory_model/

'IT > 개발' 카테고리의 다른 글

프로미스 동작 분석하기(2)  (0) 2021.08.27
프로미스 동작 분석하기(1)  (0) 2021.08.21
SonicClassifier 웹페이지 추가 및 완성  (0) 2021.07.16
Airsonic 플레이리스트 정렬  (0) 2021.07.11
Simple Gallary Server  (0) 2021.01.10

이전 포스트

https://ahat-li.tistory.com/21

 

Airsonic 플레이리스트 정렬

에어소닉을 사용해본 사람이라면 알겠지만 에어소닉에는 플레이리스트를 정렬할 수 있는 방법이 하나밖에 없다. 바로 손으로 일일히 하나하나 정리하는 것이다. 그냥 앨범별, 폴더별, 아티스트

ahat-li.tistory.com

 

간단하게 Airsonic의 플레이리스트 정렬하는 프로그램을 만들었었는데 GUI가 없으니까 사용이 불편했다.

그래서 Golang의 GUI 라이브러리를 알아보다가 어짜피 Airsonic도 외부에서 접속하는데

정렬기능도 외부에서 접속하는기 더 편하지 않을까 싶어서 웹페이지로 만들기로 했다.

 

이전에 이미지서버 프로그램을 간단히 만들었었는데

주변사람들한테 보여줬더니 UI가 이게 뭐냐고 너무 없어보인다는 소리를 많이들었다.

 

내 나름대로 접속할때 1바이트라도 덜사용하게 만들려고 심플하게 한거였는데....

 

그래서 이번에는 최근 공부하고있는 react를 golang과 간단히 연동시켜서 웹페이지를 만들었다.

사용한 템플릿은 berry의 무료버전인데 MIT라이센스인데다가 디자인이 깔끔해서 사용하기 아주 좋은 템플릿이다.

 

https://github.com/codedthemes/berry-free-react-admin-template

 

codedthemes/berry-free-react-admin-template

Berry free react material-ui admin template for easing and faster web development. - codedthemes/berry-free-react-admin-template

github.com

 

반응형기능도 당연히 있기때문에 PC브라우저와 모바일 브라우저 둘다 호환이 된다.

 

어쨌든 기능을 완성하고 템플릿을 수정해서

웹페이지에 접속하여 Airsonic 내의 플레이리스트와 스타리스트를 정렬할 수 있는 프로그램이 완성되었다.

 

만드는 도중 테스트 과정에서 내가 천 몇백개정도 모아놓았던 플레이리스트와 스타리스트가 날아가긴 했지만

어찌어찌 무사히 만들 수 있었다...

 

 

 

이미 깔끔하게 디자인되어있는 템플릿을 사용해서 만들었더니 UI도 꽤나 깔끔하게 완성이 된것 같다.

 

다만 내가 메인으로 정했던 "음악"에 대한 플레이리스트와 스타리스트만 정렬이 가능하다

Airsonic은 아티스트와 앨범 등으로도 리스트를 만들 수 있지만 두가지에 대해서는 정렬을 지원하지 않는다.

 

이제 음악 정렬도 쉽게 할 수 있으니 내가 날려먹은 플레이리스트와 스타리스트를 다시 모아야겠다...

 

https://github.com/AhatLi/SonicClassifier

 

AhatLi/SonicClassifier

Sort playlist for Airsonic, subsonic . Contribute to AhatLi/SonicClassifier development by creating an account on GitHub.

github.com

 

'IT > 개발' 카테고리의 다른 글

프로미스 동작 분석하기(1)  (0) 2021.08.21
자바스크립트의 배열  (0) 2021.08.01
Airsonic 플레이리스트 정렬  (0) 2021.07.11
Simple Gallary Server  (0) 2021.01.10
Airsonic 로컬가사를 출력하도록 소스코드 수정하기  (0) 2020.12.24

+ Recent posts