개발자 준의 기술블로그


  • 홈

  • 아카이브

  • 태그

  • 검색

[Frontend] Using Hammerjs in React

작성일 2021-02-01 | In Frontend
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

export const useGesture = (func) => {
  const ref = useRef(null)

  useEffect(() => {
    if (ref.current) {
      const hammer = new Hammer(ReactDOM.findDOMNode(ref.current))
      hammer.add(new Hammer.Press({ event: 'longpress', time: 500 }))
      //hammer.get('swipe').set({ direction: Hammer.DIRECTION_ALL })
      hammer.on('swipe', () => func?.())
    }
  }, [ref, func])

  return ref
}

Bind returned ref to desired element using createRef(), useRef(), forwardRef().

더 읽어보기 »

[Frontend] Frontend 기초 다지기

작성일 2021-01-20 | In Frontend

(주로 Reference 글들을 번역/요약하는 방식으로 작성되었습니다)

Hoisting 관점에서 var/function/function* vs let/const/class

All declarations are hoisted in JS.

However, var/function/function* are initialized as undefined whereas let/const/class are uninitialized. This means that a ReferenceError exception is thrown when you try to access it before initialization. It will only get initialized when let/const/class statement is evaluated, everything before that is called the temporal dead zone

Reference:
https://stackoverflow.com/questions/31219420/are-variables-declared-with-let-or-const-hoisted

Polyfill이란?

A polyfill is a piece of code (usually Javascript on the Web) used to provide modern functionality on older browsers that do not natively support it.

For example, a polyfill could be used to mimic the functionality of an HTML Canvas element on Microsoft Internet Explorer 7 using a Silverlight plugin, or mimic support for CSS rem units, or text-shadow, or whatever you want.

ex) core-js, babel/polyfill

Reference:
https://blog.bitsrc.io/how-to-write-cross-browser-web-apps-f26f0c5b49cb

Backend for Frontend (BFF)란?

  1. Multiple clients with a single general-purpose server

img1

Very simple but hard to maintain monolithic structure as service grows. Adding new features may cause side effects. Builds and tests take forever to run and it takes days to debug test failures.

  1. Multiple clients with a microservices architecture

img2

Microservices are greate for scalability and help solve a bunch of problems. Backend teams are typically responsible for a single service and no longer tripping over each other. Individual microservices are lightweight, customizable, decoupled, and readily extensible

However, there are still boundary issues amongst frontend teams. The responsibility of handling multiple clients is still encoded in one or many services. Frontend engineers are struggling to jam multiple use-cases into a single API layer and the customer experience is beginning to suffer.

  1. Multiple clients with dedicated backends and a microservices architecture

img3

Because our clients have very different needs. The BFF applications are lightweight translation layers that decouple individual clients from downstream services and server only a single frontend.

Benefits:

  • Frontend teams gets ownership of both their client app and its underlying resource consumption layer.
  • Mobile team is able to make changes such as payload size and requeset freqency reduction without having to extend and fork APIs developed for web-based use-cases.
  • Encapsulation

Reference:
https://medium.com/frontend-at-scale/frontend-architectural-patterns-backend-for-frontend-29679aba886c

SASS / SCSS

CSS의 문법은 배우기 쉽지만 프로젝트가 커짐에 따라 점점 유지보수가 어려워진다. 이러한 CSS의 한계를 보완하기 위해 사용하는 CSS Preprocessor가 SASS이다. SCSS는 SASS의 3번째 버전에서 추가되었는데 SASS의 모든 기능을 지원하면서 CSS구문과 완전히 호환되도록 만들어졌다.

SASS를 사용하면 다음과 같은 기능들을 사용할 수 있다.

  • 변수
  • 조건문과 반복문
  • Import
  • Nesting
  • Mixin
  • Extend/Inhertiance

SASS는 CSS pre-processor로서, SASS자체로 브라우저에 적용하는 것이 아니라 CSS를 확장해서 쉽고 편리하게 쓰기위해 쓰는 스크립팅 언어이기 때문에 컴파일을 하여 CSS로 변환한뒤 적용된다.

img4

Reference:
https://velog.io/@jch9537/CSS-SCSS-SASS
https://jinminkim-50502.medium.com/css-preprocessor-sass-scss-25dc8329f867

더 읽어보기 »

[Backend] Backend 기초다지기

작성일 2021-01-20 | In Backend

(주로 Reference 글들을 번역/요약하는 방식으로 작성되었습니다)

ExpressJS 대신 NestJS를 사용하는 이유

ExpressJS는 un-opinionated, minimalistic framework로써, 개발자가 올바른 선택을 할 것이라 믿고 맡긴다. 개발자는 자신이 원하는 tools, technologies, middleware 등을 선택하여 적용할 수 있다. 또 Framework에서 강제하는 구조또한 없다. 이는 개인으로 작업할 때는 높은 자유도를 보장해주지만, 팀으로 작업할 때는 의견충돌을 야기할 수 있으며, 큰 프로젝트에 참여하는 경우 각각의 micro-service team들의 project structure와 tool이 달라 다른 팀으로의 이동을 힘들게 한다.

NestJS는 highly opinionated framework이다. 따라서 NestJS는 개발자들이 특정 tool을 사용하고 특정한 방식으로 코드를 작성하도록 가이드한다. 그리고 이미 controller를 try-catch block으로 감싸놓고, request body를 parse하고, error handler, middleware 등을 이미 다 추가해놓았기때문에 매번 새 프로젝트를 시작할때마다 같은 작업을 반복하지 않아도 된다.

추가적으로 아래와 같은 장점이 있다.

  • Typescript by default
  • CLI tooling
  • TypeORM support
  • Microservices (built-in support for GraphQL, WebSockets, gRPC, MQTT, etc)
  • Ease of testing

p.s. NestJS는 Express를 base로 사용한다고 한다.

Reference:
https://medium.com/monstar-lab-bangladesh-engineering/why-i-choose-nestjs-over-other-node-js-frameworks-6cdbd083ae67
https://codeburst.io/why-you-should-use-nestjs-for-your-next-project-6a0f6c993be

더 읽어보기 »

[React] 왜 Hook을 사용하는가

작성일 2021-01-12 | In Frontend

React Hook을 사용하는 이유

  1. Class-based component로 작성할 경우 state를 관리할때 코드가 여기저기 분산되지만 hook을 사용한다면 한군데로 코드가 모아지므로 가독성이 좋아진다. + Refactoring이 쉽다.
    • constructor에서는 state를 선언하고
    • componentDidMount()에서는 초기화를 하고
    • render()혹은 메소드에서 manipulation을 진행
  2. 간단한 component (자신의 state가 없고 props만을 사용한다거나)는 주로 functional component로 작성되는데, state를 추가하기 위해 class-based component로 바꾸지 않아도 된다.
  3. 헷갈리는 this를 사용할 필요가 없어진다.
  4. method binding이 필요가 없다.
  5. JS에서는 function이 더 가볍다. Class는 React에서는 불필요한 class의 메소드들을 추가적으로 포함한다.
  6. function은 closure를 사용한다. closure는 매우 memory-efficient하다.

Reference:
https://blog.bitsrc.io/why-we-switched-to-react-hooks-48798c42c7f
https://overreacted.io/how-are-function-components-different-from-classes/

더 읽어보기 »

[React] Virtual DOM 정리

작성일 2021-01-12 | In Frontend

브라우저 Rendering 과정

rendering process


Virtual DOM이 필요한 이유

위의 Rendering과정에서 볼 수 있듯이, 만약 DOM에 변화를 주게된다면, Rendering Tree를 만드는 과정부터 다 다시 계산을 해야된다.
Complex SPA에서는 많은 숫자의 DOM manipulation이 일어나게 되는데 이를 매번 실제 DOM에 변화를 주게된다면 매우 비효율적이다.
따라서 DOM manipulation이 필요할 때, 바로바로 실제 DOM에 변화를 주는 것이 아니라 중간에 Virtual DOM을 두고
이를 변경함으로써 일종의 Buffering을 만드는 것이다. 기존에는 30번의 DOM manipulation이 일어났다면,
이 30번을 Virtual DOM에 우선 반영하고, 30번 변경이 된 최종 결과물을 실제 DOM에 반영함으로써 단 1번만 계산을 수행하면 된다.
이 작업은 render function과 화면에 element를 출력하는 작업 사이에 이뤄지며 이를 reconciliation이라고 한다.

Virtual DOM은 offline DOM(in-memory representation)으로 실제로 rendering되는 것이 아니므로 변경하는 비용이 크지 않다.\

Virtual DOM은 DOM을 추상화 한 자바스크립트 객체를 구성하여 사용한다.
업데이트하는 과정은:

  1. 업데이트한 전체 UI를 Virtual DOM에 re-rendering
  2. 실제 DOM과 생성된 Virtual DOM을 비교
  3. 바뀐 부분만 실제 DOM에 적용

Reference:
https://hashnode.com/post/the-one-thing-that-no-one-properly-explains-about-react-why-virtual-dom-cisczhfj41bmssp53mvfwmgrq
https://brunch.co.kr/@eight-two-five/14

더 읽어보기 »

[React] React 기초 다지기

작성일 2021-01-12 | In Frontend

Class-based component의 constructor에서 super()를 호출하는 이유

Javascript는 언어적 제약사항으로서 생성자에서 super를 호출하기 전에는 this를 사용할 수 없다.\

props를 접근하기 위해 this.props를 사용하게 되는데, 이 this.props는 사용자가 정의한 component가 상속받고 있는 React.Component에 정의되어있다. super()는 자신의 부모의 생성자를 호출하는 것인데, React.Component의 생성자가 super(props)로 넘겨준 props를 자신의 this.prop에 초기화하는 것이고, 이 이후부터 사용자가 정의한 component에서도 this.props를 통해 부모인 React.Component의 this.props를 접근할 수 있게 되는 것이다.\

하지만 React에서는 super(props)를 통해 props를 전달하지 않고, super()만 호출하더라도 this.props를 통해 접근할 수 있는데, 이는 React가 컴포넌트의 생성자 호출 이후 해당 객체에 props속성을 세팅해주기 때문이다.

1
2
const instance = new YourComponent(props);
instance.props = props;

내부적으로 위와같이 처리가 되기 때문에 사용자가 실수로 props를 누락하더라도 정상적으로 작동한다.

Reference:
https://min9nim.github.io/2018/12/super-props/

리스트의 Key에 index를 사용하면 안되는 이유

key는 React가 DOM element들을 식별하기 위해 사용한다. 따라서 만약 리스트의 중간에 element를 추가하거나 삭제했지만 key가 이전과 같이 유지되는 경우 React는 DOM이 변화하지 않은 것으로 인식하고 그대로 보여준다.

예시:

1
2
3
key: 0 item: a
key: 1 item: b
key: 2 item: c

index 1의 값이 b에서 z로 바뀌어도 index를 key로 사용하기 때문에 key는 그대로이다.

1
2
3
key: 0 item: a
key: 1 item: z
key: 2 item: c

React는 DOM element가 바뀌지 않은것으로 판단한다.

1
2
3
key: 0 item: c
key: 1 item: a
key: 2 item: b

또 순서가 바뀌더라도 key는 똑같기때문에 반영되지 않는다.

아래의 3가지 조건이 모두 만족될 경우는 index를 key로 사용해도 안전하다고 한다:

  1. the list and items are static - they are not computed and do not change
  2. the items in the list have no ids
  3. the list is never reordered or filtered

Reference:
https://robinpokorny.medium.com/index-as-a-key-is-an-anti-pattern-e0349aece318

언제 useCallback()과 useMemo()를 사용하여야 하는가?

Reference:
https://kentcdodds.com/blog/usememo-and-usecallback

더 읽어보기 »

[CSS] Grid 정리

작성일 2021-01-11 | In Frontend
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
.container {
    /* 3개의 행을 각각 100px로 만들겠다 */
    grid-template-rows: repeat(3, 100px); 
    /* 3개의 열을 각각 3등분해서 만들겠다(flex-grow:1과 같음) */
    grid-template-columns: repeat(3, 1fr); 
    /* 명시적으로 정의한 행과 열을 넘어갔을 때(암시적) 어떻게 배치할지*/
    grid-auto-flow: row;

    /* 그리드 컨텐츠의 수직 정렬을 설정*/
    align-content: center; /* row를 어떻게 정렬할지 */
    align-items: center;   /* 하나의 row안에서 어떻게 정렬할지 */
    /* 그리드 컨텐츠의 수평 정렬을 설정*/
    justify-content: center; /* column을 어떻게 정렬할지 */
    justify-items: center;   /* 하나의 column안에서 어떻게 정렬할지 */
    /* align-content와 justify-content에 동시적용됨 */
    place-content: center;


}


.item:nth-child(1) {
    /* CSS의 index는 0이 아닌 1부터 시작한다 */
    grid-row-start: 1;
    grid-row-end: 3;

    grid-column-start: 2;
    grid-column-end: 4;

    /* 끝나는 위치가 4이고, 길이를 2로 지정. 즉, 2~4까지를 너비로 가짐 */
    grid-column-start: span 2;
    grid-column-end: 4;
}

Reference: https://heropy.blog/2019/08/17/css-grid/

더 읽어보기 »

[WebRTC] WebRTC Signaling 정리

작성일 2020-06-11 | In Frontend

4줄요약:

  1. WebRTC는 peer-to-peer communication을 하기 위해 signaling이라는 process를 거쳐야한다.
  2. Peer-to-Peer 연결을 하기 위해 ICE Framework를 사용하며 UDP > TCP > STUN > TURN(relay)순으로 시도한다.
  3. 연결이 되었으면 offer-and-answer mechanism으로 서로의 media capability를 교환한다. 자신의 RTCPeerConnection 인스턴스에 상대방의 자신과 상대방의 정보를 저장한다.
  4. RTCPeerConnection의 등록된 정보로 서로 연결하고 communication이 이루어진다.

Signaling: process of coordination communication. In order for a WebRTC app to set up a call, its client need to exchange the following information:

  • Metadata (codec, bandwidth, etc)
  • Network data (IP addr, port, etc)
  • etc

To avoid redundancy and to maximize compatibility with established technologies, signaling methods and protocols are not specified by WebRTC standards. You can use socket to build signaling services.

RTCPeerConnection: API used by WebRTC apps to create connection between peers and communicate audio and video. RTCPeerConnection has two tasks:

  • Obtain local media conditions (resolutions, codec capabilities) using offer-and-answer mechanism.
  • Obtain potential network address for the app’s host, known as candidates.

Offer-and-answer mechanism (A is calling B):

  1. A creates RTCPeerConnection object
  2. A creates an offer with createOffer()
  3. A calls setLocalDescription() to send A’s setup to B
  4. A stringifies offer and sends it to B using signaling mechanism
  5. B calls setRemoteDescription() so that B’s RTCPeerConnections knows about A’s setup
  6. B calls createAnswer()
  7. B calls setLocalDescription() to send B’s setup to A
  8. B stringifies offer and sends it to A using signaling mechanism
  9. A calls setRemoteDescription() so that A’s RTCPeerConnection knows about B’s setup

Very similar to handshake procedure

ICE framework: framework used to find network interfaces and ports (candidates)

In reality most devices live behind one or more layers of NAT, some have antivirus SW that blocks certain ports and protocols. You can use ICE framework to overcome complexities. To enable this to happen, your app must pass ICE server URLs to RTCPeerConnection. ICE tries to find the best path to connect peers.

ICE tries to find the best path to connect peers. It tries all possibilities in parallel and chooses the most efficient option that works.

  1. ICE first tries to make a connection using the host address obtained from a device’s OS and network card.(UDP > TCP)
  2. If that fails(which it will for devices behind NATs), ICE obtains an external address using a STUN server.
  3. If that fails, traffic is routed through a TURN relay server.
    Every TURN server supports STUN. A TURN server is a STUN server with additional built-in relaying functionality.

STUN: NATs provides an IP address for use within a private local network to a device but this can’t be used externally. You need a public address. WebRTC uses STUN get around this problem. STUN server live on the public internet and check the IP:port address of an incoming request (from an app running behind a NAT) and send that address back as a response. In other words, the app uses a STUN server to discover its IP:port from a public perspective.

TURN: RTCPeerConnection tries to set up direct communication using UDP. if that fails, it tries TCP. if that fails, TURN servers are used to relay data. TURN is used to relay audio, video, and data streaming between peers, not signlaing data. TURN servers have public addresses, so they can be contacted by peers even if the peers are behind firewalls or proxies.

Reference:
www.html5rocks.com/en/tutorials/webrtc/basics/
www.html5rocks.com/en/tutorials/webrtc/infrastructure/
www.html5rocks.com/en/tutorials/webrtc/datachannels/

더 읽어보기 »

[WebRTC] Janus screen sharing 만들기

작성일 2020-06-11 | In Frontend

Janus의 official page에 있는 데모 페이지 소스코드를 읽어보다 official documentation에는 없는 내용이 있어서 기록함.

Send에 plugin으로부터 response가 왔을때 성공여부에 따른 callback을 호출할 수 있다.

데모 페이지 소스 코드의 일부:

1
2
3
4
5
6
7
8
9
10
11
12
    screentest.send({"message": create, success: function(result) {
        var event = result["videoroom"];
        Janus.debug("Event: " + event);
        if(event != undefined && event != null) {
            // Our own screen sharing session has been created, join it
            room = result["room"];
            Janus.log("Screen sharing session created: " + room);
            myusername = randomString(12);
            var register = { "request": "join", "room": room, "ptype": "publisher", "display": myusername };
            screentest.send({"message": register});
        }
    }});

screentest는 pluginHandler인데, send에서 저렇게 일반적으로 보내는 message말고도 success와 error 콜백함수를 보낼 수 있다.

아래는 janus.js안의 코드인데, send의 파라미터로 넘어온 객체 전체를 sendMessage라는 private method에 넘겨준다.

1
send : function(callbacks) { sendMessage(handleId, callbacks); },

그리고 sendMessage에서는:

1
2
3
callbacks.success = (typeof callbacks.success == "function") ? callbacks.success : Janus.noop;
callbacks.error = (typeof callbacks.error == "function") ? callbacks.error : Janus.noop;
var message = callbacks.message;

이런식으로 사용한다.

더 읽어보기 »

[WebRTC] Janus media server 설치하기

작성일 2020-06-11 | In Frontend

설치하는 과정을 편하게 Dockerfile로 만들었다.

기존에 다른사람이 올려놓은 Dockerfile은 최신 janus와 안맞아서 패키지들을 최신가이드에 맞게 바꿔주었다.
이미지 빌드와 실행은 repository의 readme에 나와있다.
https://github.com/cjsjyh/janus_server

=========================

Official Repository:https://github.com/meetecho/janus-gateway

ReadMe를 따라서 진행하다가 안되서 추가된 부분들이 있다.

aptitude install libmicrohttpd-dev libjansson-dev \ libssl-dev libsrtp-dev libsofia-sip-ua-dev libglib2.0-dev \ libopus-dev libogg-dev libcurl4-openssl-dev liblua5.3-dev \ libconfig-dev pkg-config gengetopt libtool automake

Readme에는 없지만 libnice를 설치하기 위해 추가로 패키지를 설치해야된다

1
2
brew install gtk-doc  // macOS
apt-get install gtk-doc-tools // Ubuntu

libnice 설치:

1
2
3
4
5
git clone https://gitlab.freedesktop.org/libnice/libnice
cd libnice
./autogen.sh
./configure --prefix=/usr
make && sudo make install

libsrtp 설치:
Readme에 써진대로 1.5.4를 설치하면 최신버전의 openssl을 지원하지 않아 ./configure --prefix=/usr --enable-openssl에서 에러가 난다. 아래처럼 버전을 작성일 기준 가장 최신버전인 2.3.0버전으로 바꿔주었다.

1
2
3
4
5
wget https://github.com/cisco/libsrtp/archive/v2.3.0.tar.gz
tar xfv v2.3.0.tar.gz
cd libsrtp-2.3.0
./configure --prefix=/usr --enable-openssl
make shared_library && sudo make install

저는 http를 사용하여 janus서버와 통신할 것이기 때문에 기타 연결방법을 위한 패키지 설치는 건너뛰도록 하겠습니다.

documentation 빌드를 위한 패키지 설치:

1
aptitude install graphviz

doxygen을 그냥 설치하면 가장 최신버전이 깔리는데 그럼 janus에서 에러를 낸다. 에러를 내지 않는 1.8.11버전으로 다운로드를 한다

1
2
wget https://svwh.dl.sourceforge.net/project/doxygen/rel-1.8.11/doxygen-1.8.11.src.tar.gz

실제 janus패키지 설치:

1
2
3
4
5
6
7
8
9
git clone https://github.com/meetecho/janus-gateway.git
cd janus-gateway
sh autogen.sh

./configure --prefix=/opt/janus
make
make install

make configs

websocket과 data channel은 사용할 수도 있을 것 같아서 제외한 나머지는 비활성화 시켰습니다.

1
./configure --disable-rabbitmq --disable-mqtt

--여기서부터 Dockerfile로 넘어가서 작업함–

더 읽어보기 »
1 2 3 … 5
Junsoo Choi

Junsoo Choi

41 포스트
8 카테고리
46 태그
RSS
Github LinkedIn
© 2021 Junsoo Choi
Contact: cjsjyh@gmail.com