(강의내용과 큰 상관없습니다. 강의 듣고 결과물 만드는 작업 중입니다.)

 

작업물에 wifi 애니메이션이 필요해서 제작해보기로했습니다.

뭐 대충 이런거..

wifi 이미지

 

딱히 기능은 없고 css 애니메이션으로만..

codepen.io에서 간단한 코드를 발견해서 크기만 수정해서 사용하기로 했습니다.

 

감사합니다 ㅎㅎ

코드 출처입니다.

https://codepen.io/roncallyt/pen/GymuE

 

Wifi signal, pure CSS

Português: Fazendo um sinal de wifi usando somente CSS e animações CSS3 através da regra @keyframes. English: Making a wifi signal using only CSS and ...

codepen.io

 

일단 styled-components를 통해 작업하는데, keyframe을 어떻게 처리할까 하고 찾아보다가 발견했고 참고글을 읽고 작업했습니다^^. 

공식문서에도 있겠지요. 쉽게 설명해주셨습니다.

https://medium.com/@shlee1353/%EB%A6%AC%EC%95%A1%ED%8A%B8-styled-components-%EC%95%A0%EB%8B%88%EB%A9%94%EC%9D%B4%EC%85%98-%EA%B5%AC%ED%98%84-fbbb8aa9e722

 

리액트 styled components 애니메이션 구현

웹에서 애니메이션을 구현할 때 transition과 animation을 사용합니다. transition은 엘리먼트의 상태변화에 쉽게 사용할 수 있으며, 정해진 시작점과 종료점을 가지고 있습니다. 예로, hover 상태시 엘리

medium.com

공식문서도 확인해봅시다.

styled-components.com/docs/basics#animations

 

styled-components: Basics

Get Started with styled-components basics.

styled-components.com

 

별도로 keyframes를 제공해줍니다.

import styled, { keyframes } from 'styled-components';

 

이건 완성 코드!

import React from 'react';
import PropTypes from 'prop-types';
import styled, { keyframes } from 'styled-components';

const lineDefaultStyle = `
    border: 2px solid transparent;
    border-radius: 100%;
`;

const appearIn = (themeColor) => keyframes`
    0%{ border-top-color: transparent; }
    25%{ border-top-color: ${themeColor}; }
    75%{ border-top-color: ${themeColor}; }
    100%{ border-top-color: ${themeColor}; }
`;

const appearMiddle = (themeColor) => keyframes`
    0%{ border-top-color: transparent; }
    25%{ border-top-color: transparent; }
    75%{ border-top-color: ${themeColor}; }
    100%{ border-top-color: ${themeColor}; }
`;

const appearOut = (themeColor) => keyframes`
    0%{ border-top-color: transparent; }
    25%{ border-top-color: transparent; }
    75%{ border-top-color: transparent; }
    100%{ border-top-color: ${themeColor}; }
`;

const OutLine = styled.div`
    ${lineDefaultStyle}
    margin: 1px auto;
    width: 25px;
    height: 30px;
    border-top-color: ${props => props.themeColor};
    animation: ${props => appearOut(props.themeColor)} 1.5s infinite linear
`;

const MiddleLine = styled.div`
    ${lineDefaultStyle}
    margin: 1px auto;
    width: 18px;
    height: 25px;
    border-top-color: ${props => props.themeColor};
    animation: ${props => appearMiddle(props.themeColor)} 1.5s infinite linear
`;

const InLine = styled.div`
    ${lineDefaultStyle}
    margin: 1px auto;
    width: 14px;
    height: 23px;
    border-top-color: ${props => props.themeColor};
    animation: ${props => appearIn(props.themeColor)} 1.5s infinite linear
`;

const Dot = styled.div`
    ${lineDefaultStyle}
    margin: 2px auto;
    width: 3px;
    height: 3px;
    border: 0;
    background: ${props => props.themeColor};
`;

const Wifi = (({ themeColor }) => {
    return(
        <OutLine themeColor={themeColor}>
            <MiddleLine themeColor={themeColor}>
                <InLine themeColor={themeColor}>
                    <Dot themeColor={themeColor} />
                </InLine>
            </MiddleLine>
        </OutLine>
    );
});

Wifi.propTypes = {
    themeColor: PropTypes.string,
};

Wifi.defaultProps = {
    themeColor: '#333',
};

export default Wifi;

테마색으로 라인을 그려주려고 외부에서 props를 전달받습니다.

뭔가 중복되는 코드들을 줄일 수 있을 것 같은데, 최적화는 추후에..^^;;

 

완성된 와이파이!

wifi

근데 왜 노트북에서는 잘 되던게, 데탑오니까 애니메이션 반복이 안되는지는 봐야겠습니다~

infinite가 빠졌군요. ㅎ 코드는 수정해두었습니다.~

 

여튼 약간 엉성하긴 한데, 좋습니다. 굿굿

 

 

 

React 강의 듣고 나만의 사이트 만들기 시작!! 

먼저 초반부 Front 작업을 진행한다.

쪼랩이라 라이브러리 다운받을때 빼먹는건 진행하면서 차차 고치도록 해야겠다.

친절하게 코드가 다 들어있지않은 이유는 스스로 공부하는 목적이기때문에 사소한 부분이 많이 빠져있다.

 

1.

내 사이트명의 폴더를 생성한다.

OKAYOON

 

2. 

Front 와 Back 따로 진행해야하기에 폴더를 두개 생성한다.

OKAYOON

-front

-back

// front

npm init
npm i next@9 react react-dom 
npm i prop-types
npm i eslint eslint-plugin-import eslint-plugin-react eslint-plugin-react-hooks -d

npm i -D babel-eslint eslint-config-airbnb eslint-plugin-import eslint-plugin-react-hooks eslint-plugin-jsx-a11y

npm i antd styled-components @ant-design/icons

npm init 해준 뒤 install해주자

내가 본 제로초님 강의에서는 next9버전을 사용했다.

next@9의 @는 특정 버전을 지정하여 install 할 수 있다.

eslint를 사용하지 않는다면 install 할 필요없다.

-D는 개발모드에서만 사용할 모듈들이다.

antd를 통해 스타일 작업을 진행하였다. (bootstrap과 같이 웹, 앱 디자인 개발을 위한 프레임워크이다.)

 

index.js

pages 폴더 예시

 

pages 폴더 하위에 파일을 생성하면 next가 자동으로 인식해준다.

라우터 필요없이 주소/login 식으로 접근 가능하다.

next를 통해 localhost로 확인하기위해서는 package.json의 script 부분 수정이 필요하다.

(물론 명령어를 쳐도된다.)

 

package.json

package.json 예시

아래와 같이 하면 포트를 바꿀 수 있다.

// package.json

"scripts": {
	"dev": "next -p 3060"
},

그 후 cmd에서 run 시켜준다.

npm run dev

 

작업을 하다보면 콘솔에 에러가 뜨는것을 확인할 수 있다.

className 콘솔창 예시

babel를 통해 해결해주자.

관련 플러그인을 install 해준다.

npm i babel-plugin-styled-components

설정도 빼먹지말자

 

/front 하위로 .babelrc 파일을 생성하여 아래 내용을 작성한다.

{
    "presets": ["next/babel"],
    "plugins": [
        ["styled-components", {
            "ssr": true,
            "displayName": true
        }]
    ]
}

ssr: 서버사이드 렌더링

displayName: 렌더링이 된 후 보호된 네이밍들을 컴포넌트 이름으로 변경한다.

 

 

redux, saga를 통해 state관리 및 통신작업을 할 것이다.

npm i redux next-redux-wrapper react-redux redux-devtools-extension
npm i redux-saga next-redux-saga axios immer

npm i -d redux-devtools-extension

 

front 하위에 store 폴더를 생성한 뒤 파일을 하나 만들자.

네이밍은 configureStore.js인데, 알아서하자.

내용은 아래와 같다.

import { createWrapper } from 'next-redux-wrapper';
import { applyMiddleware, createStore, compose } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import createSagaMiddleware from 'redux-saga';

import reducer from '../reducers';
import rootSaga from '../sagas';


const loggerMiddleware = ({ dispatch, getState }) => (next) => (action) => {
  console.log(action);
  return next(action);
};

const configureStore = () => {
  const sagaMiddleware = createSagaMiddleware();
  const middlewares = [sagaMiddleware, loggerMiddleware];
  const enhancer = process.env.NODE_ENV === 'production'
    ? compose(applyMiddleware(...middlewares))
    : composeWithDevTools(applyMiddleware(...middlewares));
  const store = createStore(reducer, enhancer);
  store.sagaTask = sagaMiddleware.run(rootSaga);
  return store;
};


const wrapper = createWrapper(configureStore, {
  debug: process.env.NODE_ENV === 'development',
});


export default wrapper;

이렇게 미들웨어를 만든 다음 

_app.js 파일에서 사용해주자.

_app.js 예시

페이지에 _app.js나 _document.js등과 같이 Next.js에 내장되어 있는 _document.js, _app.js, _error.js를 커스터마이징하여 레이아웃을 새롭게 구성하는 방법이 있다.

그냥 pages 폴더 하위에 _app.js라는 네이밍으로 파일을 생성하면된다.

(next에서 제공하는 것으로 특정 컴포넌트 렌더링 시 Layout을 적용 제외하기 위한 방법이라고 한다.)

 

여튼 저기에다가 미들웨어 만들어 둔 것을 wrapper.withRedux로 감싸줘야 프로젝트의 모든 컴포넌트와 페이지에 적용된다고 한다. 

Next.js에서 Redux 사용하기에 대해 읽어보자 --> velog.io/@jehjong/Next.js%EC%97%90%EC%84%9C-Redux%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-Redux-wrapper

 

Next.js에서 Redux사용하기 (Redux wrapper) (1/2)

컴포넌트에서 공통적으로 쓰이는 데이타가 흩어져있기 때문에 부모 컴포넌트에서 데이타를 받아서 자식 컴포넌트에게 각각 보내줘야한다컴포넌트끼리 데이타를 전달하는 과정도 매우 복잡하

velog.io

 

 

스코프에 대한 지식이 없다면 먼저 간단히 읽고 오세요.

실행컨텍스트에 대해서 알고 진행해야하기에 실행 컨텍스트 글을 꼭 참고해주세요.

 

실행 컨텍스트(Execution context)

실행 컨텍스트는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체입니다. 자바스크립트는 어떤 실행 컨텍스트가 활성화되는 시점에 선언된 변수를 위로 끌어올리고(호이스팅, hoisting) 외부

okayoon.tistory.com

 

 

 

클로저(Closure)

여러 함수형 프로그래밍 언어에서 등장하는 보편적인 특성입니다.

클로저는 객체지향과 함수형 프로그래밍에서 매우 중요한 개념입니다.

 

특성<- 이라는 것 보이시죠?

자바스크립트 고유의 개념이 아니라서 많은 사람들이 헷갈려하고(나 또한..) 정의를 요약하기가 힘듭니다.

 

 

"자신을 내포하는 함수의 컨텍스트에 접근할 수 있는 함수"

- 더글라스 크록포드, 자바스크립트 핵심가이드

 

"함수가 특정 스코프에 접근할 수 있도록 의도적으로 그 스코프에서 정의하는것"

- 에단 브라운, 러닝 자바스크립트

 

" 컴퓨터 언어에서 클로저는 일급 객체 함수의 개념을 이용하여 스코프에 묶인 변수를 바인딩하기 위한 일종의 기술이다."  

- 위키백과

 

 

위의 글들만 봤을 때 혼란스러웠습니다...

추가적으로 자바스크립트 코어책을 보고 난 후 관련 지식들을 수정해서 작성해보려고 합니다.

 

 

개념

클로저는 어떤 함수 A가 선언된 당시 이 A 함수가 만들어질때 저장된 환경들과 또 다른 함수 B의 환경 사이에서의 상호관계에 따른 현상입니다.

=> 함수가 선언될 당시의 lexical environment(함수 선언당시 실행 컨텍스트 내의 식별자 정보, 외부환경 정보)의 상호관계에 따른 현상입니다.

 

이해가 어려우니 예시를 보고 다시 이야기해보겠습니다.

 

예시1

외부 함수의 변수를 참조하는 내부함수

(여기서 클로저는 식별자 name을 참조하기 위한 내부함수 inner에서 발생하는 현상입니다.)

var outer = function(){
	var name = 'hans';
    
    var inner = function(){
    	console.log(name);
    };
    
    inner();
};

outer();

1) inner 함수 내부에는 식별자 name이 없습니다.

=> 이것은 즉 inner 함수의 lexical environment의 environmentRecord에 name 식별자가 없다는 것을 의미합니다.

=> environmentRecord는 현재 컨텍스트와 관련된 코드의 식별자 정보(매개변수의 이름, 함수 선언, 변수명등)들이 저장해둡니다.

 

2) inner 함수는 lexical environment의 outer-environmentReference에서 상위 함수의 컨텍스트를 찾는데, 이때 상위 컨텍스트는 outer입니다.

=> outer-envirnmentReference는 현재 호출된 함수가 선언될 당시(할성화될 당시)의 LexicalEnvironment를 참조한 것입니다. 

=> 즉, 선언 당시 outer의 lexical environment를 참조합니다.

 

3) inner의 outer-environment로 인해 outer 함수의 lexical environment에서 name 식별자를 찾을 수 있습니다.

=> 스코프 체이닝!

 

4) outer 함수가 끝나게되면 outer함수의 실행 컨텍스트가 종료되며 lexical environment에 저장된 식별자(name, inner)에 대한 참조를 지웁니다.

=> 이때 만약 참조하는 변수가 있다면 메모리에 남아있고, 참조되지 않는다면 가비지 컬렉터의 수집대상이 됩니다.

 

 

예시2

외부 함수의 변수를 참조하는 내부 함수

var outer = function(){
	var a = 1;
    
    var inner = function(){
    	return ++a;
    };
    
    return inner;
};

var outerFunc = outer();
console.log(outerFunc()); // 2
console.log(outerFunc()); // 3

1) inner 함수를 반환하고 있습니다.

2) outerFunc가 선언될 때 outer의 실행 컨텍스트가 종료됩니다.

=> 이때 우리는 식별자 a와 inner가 가비지컬렉팅될 것이라 생각합니다.만 아닙니다.

 

3) outerFunc는 outer의 실행 결과인 inner 함수를 참조합니다.

4) 이후 outerFunc가 호출될 때 앞에서 반환한 inner 함수를 실행합니다.

5) inner가 실행될 때 inner의 lexical environment의 environmentRecord에서 식별자 a를 찾습니다.

=> 없기때문에 lexical environment의 outer-environment에서 상위 함수의 컨텍스트를 찾습니다.

=> inner 함수가 선언된 위치의 lexical environment가 참조복사되기 때문에 outer 함수의 lexical environment를 찾을 수 있습니다. 

 

6) outer의 lexical environment 의 environmentRecord에서 식별자 a를 찾습니다.

 

 

여기서 

"반환된 inner 함수가 실행될때 outer함수 실행 컨텍스트는 종료되었는데, 어떻게 outer함수의 lexical environment에 접근할 수 있는가?"

라는 의문이 들었지만, 코어 자바스크립트 책에서 친절히 설명해줍니다..

 

!!!

이것은 가비지 컬렉터 동작 방식때문이라고 합니다.

가비지 컬렉터는 값을 참조하는 변수가 하나라도 있다면 수집 대상에 포함하지 않습니다.

즉, outer의 컨텍스트가 종료된 뒤에 반환된 inner 함수에서 식별자 a를 참조하고 있고...

outerFunc로 선언되어있기 때문에 언제라도outerFunc가 실행될 수 있음을 인지합니다..

그렇기 때문에 반환된 inner 함수가 활성화 되었을때, 위의 예시대로(lexical environment^^) 동작해야하기 때문에 가비지 컬렉터의 대상이 되지 않으며.... 이것으로 인해 동작이 가능한 것입니다. 

 

그리고 위의 클로저의 예시로 return을 설명했지만

실제로는 외부로 전달이 return만을 의미하지 않으며... return 없이도 클로저가 발생하는 다양한 경우들이 있습니다. 

뭐... 콜백함수를 고차함수로 바꿔 클로저를 활용하는 방식이나 클로저를 통한 변수 보호, 커링함수 등....

=> 커링함수는 여러개의 인자를 받는 함수를 하나의 인자만 받는 함수로 나누어 순차적으로 호출될 수 있게 체인형태로 구성된 함수를 말합니다. 각 호출될 때마다 인자들을 메모리에 저장하고 마지막 호출 시에 가비지컬렉팅 대상이 되는 방식으로 사용하는 방식입니다.

 

 

 

그래서..?

간단히 말하면 클로저는 어떠한 현상에 의해 메모리에 남겨진 변수들의 집합(?)입니다.

=> 어떤 함수에서 선언한 변수를 참조하는 내부함수에서만 발생하는 현상, 외부 함수의 LexicalEnvironment가 가비지 컬렉팅되지 않는 현상을 말합니다.

 

책에서는 더 다양한 예시와 표현들이 있지만,

가장 근접한 표현의 글이라고 작성되어있던것 중 하나의 글을 가져와보았습니다.

 

 

"함수를 선언할 때 만들어지는 유효범위가 사라진 후에도 호출할 수 있는 함수"

- 존레식, 자바스크립트 닌자 비급

 

 

클로저를 이해하고나면 드는 생각은,

메모리를 너무 낭비하는 것 아닌것인가? 라고 생각합니다. 또한 실제로 메모리 누수의 위험을 이유로 클로저를 지양하라고 합니다.

하지만 메모리 소모는 클로저의 본질적 특성입니다.

그리고 이 특성을 잘 활용하는 것이 중요합니다.

 

메모리 관리 중 하나의 예를 들면 null, undefined를 사용하는 것입니다. 

가비지 컬렉터를 이해하여 사용하지 않는 메모리에 대해 회수를 해주는것이 중요합니다.

function init(){
	name = null;
	count = null;
    date = null;
}

(null과 undefined에 대해서도 글한번 읽어보세요.)

 

 

과거에 썼던 글에 오류를 발견하고 수정했는데, 새로쓰게되었습니다..

과거의 글아 안녕.....☆

그때 생각했던 개념과는 많이 달라졌군요...

성장한 것이겠죠.. ? 공부는 이래서 쭉 해야하는 것 같습니다.

 


 

참조

https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Closures

코어자바스크립트 : 핵심개념과 동작원리로 이해하는 자바스크립트 프로그래밍, 정재남

 

콜 스택(Call stack)과 힙(Heap)

자바스크립트 엔진이 자바스크립트를 실행할 때 원시 타입 및 참조 타입을 저장하는 메모리 구조

  • 콜 스택 : 원시타입 값함수 호출의 실행 컨텍스트(Execution Context) 저장
  • : 객체, 배열, 함수와 같이 크기가 동적으로 변할 수 있는 참조타입 값 저장

 

동작 원리

이미지 및 코드 출처 : https://github.com/baeharam/Must-Know-About-Frontend/blob/master/Notes/javascript/stack-heap.md

let a = 10;
let b = 35;
let arr = [];

function func() {
  const c = a + b;
  const obj = { d: c };

  return obj;
}

let o = func();

 

1. 전역 실행 컨텍스트(GEC, Global Execution Context) 생성 후 원시값은 콜 스택에 참조값은 힙에 저장

  • 주소 값과 배열, 함수 값을 제외하고는 모두 원시 값(그리고 함수의 호출 값)이므로 콜 스택에만 저장된다.

  • 이때 함수의 호출은 콜 스택에 주소 값은 저장되나 값이 없다.

  • 주소 값과 배열, 함수의 값은 참조 값이므로 콜스택과 힙에 저장된다.

 

2. 함수 func()가 실행되면 새로운 함수 실행 컨텍스트(FEC, Function Execution Context)가 생성되며 원시 값이나 참조 값에 따라 각각 콜 스택, 힙에 동일하게 저장된다.

 

함수 컨텍스트의 원칙 4가지

  • 전역 컨텍스트 생성 후 함수 호출 시 함수 컨텍스트가 생성된다.

  • 컨텍스트 생성 시 컨텍스트 안에 변수객체(arguments, variable), scope, chain, this가 생성된다.

  • 컨텍스트 생성 후 함수가 실행되는데, 사용되는 변수들은 변수 객체 안에서 찾고 없으면 스코프 체이닝으로 찾는다.

  • 험수 실행이 마무리되면 해당 컨텍스트는 사라진다.

    • 다만 클로저일 경우에는 제외되며 전역 컨텍스트는 페이지 종료 시 사라진다.

 

 

 

3. 함수가 종료되면 함수 실행 컨텍스트는 사라진다.

 

 

4. 전체 코드 종료 후 페이지가 종료되면 전역 실행 컨텍스트가 사라진다.

이때 전역 실행 컨텍스트 제거에 따라 스택의 값이 없기 때문에 힙에서 참조하고 있던 값이 없어지므로 가비지 컬렉터에 의해 제거된다.

자바스크립트는 객체 생성 시 자동으로 메모리 할당, 참조된 값이 없으면 자동으로 가비지 컬렉션에 의해 메모리 해제가 된다.

++ 오해하면 안되는 것은 가비지 컬렉션이 자동으로 해제해준다고 해도 메모리 관리는 해줘야한다.

 

 


출처 및 참조

콜 스택(Call stack)과 힙(Heap)

https://github.com/baeharam/Must-Know-About-Frontend/blob/master/Notes/javascript/stack-heap.md

실행 컨텍스트

https://www.zerocho.com/category/JavaScript/post/5741d96d094da4986bc950a0

 

+ Recent posts