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

 

작업물에 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

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

 

자바스크립트에서의 this는 어디서든 사용할 수 있으며 다른 언어와는 조금 다르게 동작합니다.

실행 컨텍스트가 생성될때 this의 바인딩이 일어나며 우선순위가있습니다.

실행 컨텍스트는 함수를 호출할때 생성되므로 또 다른 의미로는 함수를 호출할때 this가 결정된다고 할 수도 있습니다.

또한 함수의 호출마다 this가 달라질 수도 있다는 의미이기도합니다.

 

 

This

  • 전역공간에서의 this는 전역객체 (브라우저 - window, 노드 - global)를 참조

  • 메서드일 경우 호출한 객체를 this로 참조

  • 함수일 경우 this는 전역객체

  • 콜백함수는 제어권을 넘겨받은 함수가 정의한 this 참조, 정의가 없으면 전역객체

  • 생성자 함수는 생성될 인스턴스를 참조

  • call, apply, bind 같은 명시적 바인딩일 경우 인자로 전달된 객체

  • 그외 엄격모드는 undefined로 초기화 됨 (엄격, 비엄격에 차이가 있다)

 

 

전역

전역공간에서의 this는 전역객체를 가리킵니다.

전역 객체는 런타임환경에 따라 다른 이름과 정보를 가지고 있는데, 브라우저는 window, 노드환경에서는 global입니다.

console.log(this === window); // true

 

 

함수

호출한 방법에 의해 this가 결정됩니다. 

함수로서 호출하는 방법과 메서드로서 호출하는 경우에따라 this가 다른 값이 될 수 있습니다.

 

예제1)

var obj = {
	funcA : function(){
    	console.log(this);
    },
    option : {
    	funcB : function(){
    		console.log(this);
    	}
    }
};

console.log(obj.funcA()); // obj가 this임
console.log(obj.option.funcB()); // obj.option가 this임

점(.) 표기법으로 연결된 경우 마지막 점 바로 앞에 연결된 객체가 this가 됩니다.

 

 

예제2) 같은 코드라도 엄격모드와 비엄격모드에서 달라질 수 있습니다.

// 엄격모드 
function isThis(){ 
	"use strict"; 
    return this; 
};

console.log(isThis()); // undefined

// 비엄격모드 
function isThis() { 
	return this; 
};

console.log(isThis()); // Window { 0: Window, 1: ..... }

 

 

명시적방법(call, apply, bind)

상황별 this가 바인딩되는 규칙을 깨고 this에 별도의 대상을 바인딩하는 방법이 있습니다.

다만 명시적으로 별도의 this를 바인딩 할 경우 this를 예측하기 어렵다는 단점이 있습니다.

 

call

메서드의 호출 주체인 함수를 즉시 실행하도록 하는 명령입니다.

첫번째 인자로 this를 바인딩, 이후 인자들은 호출할 함수의 매개변수로 전달합니다.

var getName = function(name){
	console.log(this, name);
};

getName('hong'); // Window { ... } 'hong'

getName.call({ a : 1 }, 'hong'); // {a: 1} 'hong'

 

apply

call 메서드와 기능적으로 완전히 동일합니다.

apply 메서드는 두번째 인자를 배열로 받아서 매개변수로 지정한다는 점만 call과 다릅니다.

var getName = function(name){
	console.log(this, name);
};

getName('hong'); // Window { ... } 'hong'

getName.apply({ a : 1 }, ['hong']); // {a: 1} 'hong'

* 문자열 'hong'을 배열로 호출하지 않으면 타입에러가 발생합니다.

 

 

bind

ES5에서 추가된 기능이며 call 메서드와 비슷하지만 즉시 호출하지 않습니다.

넘겨받은 this 및 인수들을 바탕으로 새로운 함수를 반환합니다.

var getName = function(name){
	console.log(this, name);
};

getName('hong'); // Window { ... } 'hong'

var bindGetName = getName.bind({a: 1});
bindGetName('hong'); // {a: 1} 'hong'

* bind 메서드를 적용해서 새로 만든 함수는 name 프로퍼티에 동사 bind의 수동태인 bound라는 접두어가 붙으며 call과 apply보다 추적이 수월합니다.

 

 

ES6의 arrow function

함수 내부에서 this가 전역객체를 바라보는 문제를 보완하고자 this를 바인딩하지 않는 화살표 함수를 도입했습니다.

화살표 함수는 실행 컨텍스트를 생성할 때 this 바인딩 과정 자체가 빠져있어 this에 접근하려고하면 스코프체인상 가장 가까운 this에 접근합니다.

즉, 상위 스코프의 this를 그대로 활용할 수 있습니다.

const car = { 
	name : avante, 
	getPrice: function() {
		return this.name;
	},
}; 

console.log(car.name()); // avante

 

 

위와같은 내용들로 인해 this가 이것이다라고 정의할 수는 없습니다.

 


 

출처

+ Recent posts