본문으로 건너뛰기

"javascript" 태그로 연결된 113개 게시물개의 게시물이 있습니다.

Any application that can be written in JavaScript, will eventually be written in JavaScript

모든 태그 보기

React 시작하기

· 약 3분

ES6 과 this binding 에 대한 이해

ES5 스타일로 React 를 사용할 수 있지만 JSX 를 사용하기에 한 번은 컴파일이 필요하다. 따라서 ES6 의 문법을 사용하는게 낫다. (직관적이기도 하고) 그래서 ES6 의 문법이 익숙해져야하고, javascript 에서 this 를 왜 바인딩 하는지에 대한 이해가 필요하다.

JSX 이란

React 를 제대로 사용하기 위해서는 JSX 라는 새로운 구문(확장자)로 javascript 를 짜야하는데, JSX 는 XML 스타일의 자바스크립트 표현식이라고 생각하면 된다. 브라우저에서 돌아가게 하려면 순수한 자바스크립트 형태가 되어야 하기 때문에 컴파일이 필요하다.

React 구버전은 JSXTranspiler 를 통해 변환을 했는데, 지금은 지원하지 않고 Babel을 사용해 컴파일 해야한다.

Babel 이란

Babel 은 ES6 의 구문을 구버전의 브라우저에서 사용할 수 있게 컴파일해주는 자바스크립트 컴파일러라고 생각하면 된다. 추가적으로 JSX 도 컴파일해준다.

NodeJS 환경에서는 Gulp 를 이용해 자동으로 컴파일이 되게 설정할 수 있지만, 브라우저에서 단독으로 사용할 수 있게 해보는 방법을 알아보자.

설치

## bower
$ bower install babel-standalone --save
## npm
$ npm install babel-standalone --save

또는 여기서 직접 받는다.

React

설치

## bower
$ bower install react --save
## npm
$ npm install react --save

또는 여기서 직접 받는다.

실행

<script src="/bower_components/babel-standalone/babel.min.js"></script>
<script src="/bower_components/react/react.min.js"></script>
<script src="/bower_components/react/react-dom.min.js"></script>

<script type="text/babel" src="/react_login_form.jsx"></script>

babel 과 react, 데이터 바인딩을 위한 react-dom 을 가져온다.

그리고 react_login_form.jsx 를 text/babel 타입으로 가져오면 된다.

예제

// 리액트에 넣을 모듈을 선언한다.
class LoginForm extends React.Component {
constructor(props) {
super(props);
this.state = { id: "", pw: "" };
}

// input의 데이터가 변할 때 state의 값이 변경할 수 있게한다
handleChange(key, event) {
this.state[key] = event.target.value;
this.setState(this.state);
}

handleSubmit(event) {
console.log(this.state);
// 여기에 ajax 호출을 구현하면 된다
// $.ajax().then(function(){
// this.setState와 같은 상태변경 처리
// }.bind(this));
event.preventDefault();
}

render() {
return (
<form onSubmit={this.handleSubmit.bind(this)}>
아이디 :
<input
type="text"
value={this.state.id}
onChange={this.handleChange.bind(this, "id")}
/>
비밀번호 :
<input
type="password"
value={this.state.pw}
onChange={this.handleChange.bind(this, "pw")}
/>
<input type="submit" value="Submit" />
</form>
);
}
}

// LoginForm class를 id="login"에 render
ReactDOM.render(<LoginForm />, document.getElementById("login"));

여담

주관적으론 데이터 바인딩을 위해서만 사용하려면 Vue를 사용하는게, 완벽한 SPA 를 만들고 싶다면 역시 Angular2가 답인듯 싶다.

추가로 React 와 Angular2 의 비교를 보길 원한다.

HTML 태그 모두 제거

· 약 1분

스마트 에디터 같은 에디터를 쓸 대 내용을 텍스트로만 받아야할 경우가 있다.

예제

var data = data.replace(/[<][^>]*[>]/g, "");

설명

< > 안의 태그들을 모두 빈값으로 치환한다.

JSON.parse unexpected type Error

· 약 1분

database 에 이미 json 으로 저장된 데이터를 가져와 JSON.parse 메소드로 JSON 데이터를 파싱하는 중에 값이 string 인데도 오류가 나는 경우가 있다.

로그를 확인해보면

{"data":"[{"name":"gracefullight"},{"name","daniel"}]"}

[] 배열 기호 앞뒤로 따움표가 들어가 있다. 이중 object 가 있다면 선언문 앞뒤로 따움표가 들어가 파싱 오류가 난다.

원인

jQuery.ajax 의 dataType 을 json 으로 설정해 데이터를 받는데, 이미 json 형태의 string 이 넘어와 두번 치환이 된 것 같다.

다시 json 을 인식할 수 있게 치환해보자

소스

const jsonReplace = function (j) {
if (j) {
j = j
.replace(/"\[/g, "[")
.replace(/\]"/g, "]")
.replace(/"\{/g, "{")
.replace(/\}"/g, "}");
}
return j;
};

설명

해당 함수로 오류가 나는 json string 을 치환 후에 다시 JSON.parse 를 실행하면 된다.

여담

정규식을 활용해 replace 중첩을 피하게 짤 수 있을 것 같은데...

hasOwnProperty vs prototype.hasOwnProperty

· 약 1분

hasOwnProperty

for-in 구문을 쓸 때 상속된 프로퍼티나 this 구문을 방지하기 위하여 보통 hasOwnProperty 로 체크한다.

for (prop in obj) {
// eslint-disable-next-line no-prototype-builtins
if (obj.hasOwnProperty(prop)) {
// code...
}
}

문제점

위와 같은 방식은 아래와 같이 hasOwnProperty 가 재정의 된 객체에서 오류를 발생할 수 있다.

소스

const obj = {
hasOwnProperty: function () {
return false;
},
data1: 1,
};

for (prop in obj) {
// obj.hasOwnProperty(prop) 의 결과는 항상 false
// eslint-disable-next-line no-prototype-builtins
if (obj.hasOwnProperty(prop)) {
// 이 구문은 실행되지 않는다.
}
}

prototype.hasOwnProperty

따라서 prototype 을 사용해 코딩해야한다.

소스

for (prop in obj) {
// 첫번째 방법
if ({}.hasOwnProperty.call(obj, prop)) {
// 체크
}

// 두번째 방법
if (Object.prototype.hasOwnProperty.call(obj, prop)) {
// 체크
}
}

설명

첫번째 방법은 익명 Object 를 생성한 후 obj 와 prop 을 밀어넣는 방식이고 두번째 방법은 기본 Object 의 hasOwnProperty 를 가져와 obj 와 prop 을 밀어넣는 방식이다.

두번째 방법이 새(익명) Object 를 생성하지 않기에 이 방법을 사용하면 된다.

더 자세하게 알고 싶다면 mdn을 참조한다.

Throttle, Debounce Pattern

· 약 2분

javascript throttle 패턴과 debounce 패턴에 대해 알아보자.

Throttle

매 ms 마다 한 번만 호출된다. mousemove, scroll 같은 이벤트로 호출되는 함수는 이벤트 발생시 무한정 호출되어 성능 저하를 가지고 오는데, 이를 방지할 수 있다.

모바일에서 스크롤링 더보기에 사용할 수 있다.

소스

const throttleFunction = (function () {
"use strict";

const timeWindow = 500; // 여기에 시간(ms)을 지정한다
let lastExecution = new Date(new Date().getTime() - timeWindow);
// ES6 이하일 경우 ...args에 호출할 parameter 만큼 준다 function(arg1, arg2...)
const throttleFunction = function (...args) {
// 여기에 로직을 구현한다
};

return function () {
if (lastExecution.getTime() + timeWindow <= new Date().getTime()) {
lastExecution = new Date();
return throttleFunction.apply(this, arguments);
}
};
})();

// 사용법
throttleFunction(param1, param2);

Debounce

마지막 호출로부터 ms 후에 함수를 한번 호출한다. 지연된 호출을 할 수 있게 해주는데, 호출이 반복되는 동안은 실행을 방지하고, 호출이 멈춘 뒤 지정한 ms 후에 함수를 실행해 성능 저하를 막을 수 있다.

검색 자동완성 기능에 적합하다.

소스

const debounceFunction = (function () {
"use strict";

const timeWindow = 500; // 여기에 시간(ms)을 지정한다
let timeout;
// ES6 이하일 경우 ...args에 호출할 parameter 만큼 준다 function(arg1, arg2...)
const debounceFunction = function (...args) {
// 여기에 로직을 구현한다
};

return function () {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const context = this;
const args = arguments;

clearTimeout(timeout);
timeout = setTimeout(function () {
debounceFunction.apply(context, args);
}, timeWindow);
};
})();

// 사용법
debounceFunction(param1, param2);

여담

즐겨 사용하는 sublime text package 인 javascript patterns에서 참조했다.

lodash를 사용한다면 debounce, throttle로 더 쉽게 사용할 수 있다.

UMD Patterns

· 약 2분

UMD (Universal Module Definition) Pattern 이란 AMD (requireJS), CommonJS (node), 일반 browser 환경에서 통합해서 쓸 수 있는 javascript module pattern 이다.

jQuery, moment, D3 등의 라이브러리에서도 해당 패턴을 사용한다.

예제 - jQeury 종속형

새로 만들 예제의 모듈명은 module1 이고 jQuery 에 종속적이라고 가정한다.

(function (global, factory) {
if (typeof define === "function" && define.amd) {
// AMD 환경
define(["exports", "jquery"], factory);
} else if (
typeof exports === "object" &&
typeof exports.nodeName !== "string"
) {
// CommonJS 환경
factory(exports, require("jquery"));
} else {
// 일반 브라우저
factory((global.module1 = global.module1 || {}), global.$);
}
})(this, function (exports, $) {
// module1의 private 기능 구현

// exports에 public function을 붙혀주면 된다.
exports.action = function () {};
});

종속되는 모듈을 추가하려면, AMD 는 배열안에, commonJS 와 일반은 파라미터로 추가한 뒤 12 번째 줄의 콜백함수 안에 원하는 변수로 받으면 된다.

예제 - 기본형

(function (global, factory) {
if (typeof define === "function" && define.amd) {
define(["exports"], factory);
} else if (
typeof exports === "object" &&
typeof exports.nodeName !== "string"
) {
factory(exports);
} else {
factory((global.module1 = global.module1 || {}));
}
})(this, function (exports) {
exports.action = function () {};
});

즉시실행 함수에 window 객체와 함수를 파라미터로 보내서 전역 객체를 생성하는 방식이다.

예제 - 3 항연산자 사용

(function (global, factory) {
typeof exports === "object" && typeof module !== "undefined"
? factory(exports)
: typeof define === "function" && define.amd
? define(["exports"], factory)
: factory((global.module1 = global.module1 || {}));
})(this, function (exports) {
exports.action = function () {};
});

설명

window 객체 안에 모듈이 선언되므로, module1.action(); 으로 바로 호출해 사용하면 된다.

더 자세한 설명을 보고싶다면 umd를 참조하면 된다.

jsonp function

· 약 1분

jsonp (json with padding)은 다른 서버의 데이터를 내 브라우저에서 호출하기 위해 사용한다. client 에서 callback 함수명과 전송할 값을 같이 보내어 server 에서 callback 함수 안에 data 를 담아 반환하는 방식이다.

전송할 값을 보낼 때, script 태그를 사용한다.

소스

function jsonp(url, callback) {
const callbackName = "jsonp_" + Math.round(100000 * Math.random());
window[callbackName] = function (data) {
delete window[callbackName];
document.body.removeChild(script);
callback(data);
};

const script = document.createElement("script");
script.src =
url + (url.indexOf("?") >= 0 ? "&" : "?") + "callback=" + callbackName;
document.body.appendChild(script);
}

stackoverflow 에서 가져온 소스인데 현재 출처를 찾을 수 없다.

설명

임의의 함수명으로 callback 함수를 생성한 뒤 script 태그를 생성해 server 와 통신한다.

예제

jsonp("http://aaa.com?data=" + data, function (result) {
console.log(result);
});

여담

jQuery 가 있다면 jQuery ajax의 dataType 을 jsonp 로 설정해주면 되고, 요새는 CORS 를 적용해 그냥 호출하는 방식으로 가는 추세다.

Object freeze, seal, preventExtensions - 객체 수정 제어

· 약 2분

es5 부터 객체의 수정을 제어할 수 있다. ie9 부터 지원하고, ie8 은 es5-sham을 추가하면 된다.

freeze method

객체를 불변으로 만든다.

예제

const frozenObject = {
value1: 1,
value2: 2,
};

Object.freeze(frozenObject);
console.log(Object.isFrozen(frozenObject)); // true

// 삭제 불가
delete frozenObject.value1;
console.log(frozenObject.value1); // 1

// 변경 불가
frozenObject.value2 = 3;
console.log(frozenObject.value2); // 2

// 객체 추가 불가
frozenObject.value3 = 3;
console.log(frozenObject.value3); // undefined

seal method

확장, 축소는 불가능하지만 writable 쓰기 가능하다.

예제

const sealedObject = {
value1: 1,
value2: 2,
};

Object.seal(sealedObject);
console.log(Object.isSealed(sealedObject)); // true

// 삭제 불가
delete sealedObject.value1;
console.log(sealedObject.value1); // 1

// 변경 가능
sealedObject.value2 = 3;
console.log(sealedObject.value2); // 3

// 객체 추가 불가
sealedObject.value3 = 3;
console.log(sealedObject.value3); // undefined

preventExtensions method

확장만 불가능하다.

예제

const preventExtensionObject = {
value1: 1,
value2: 2,
};

Object.preventExtensions(preventExtensionObject);
console.log(Object.isExtensible(preventExtensionObject)); // false

// 삭제 가능
delete preventExtensionObject.value1;
console.log(preventExtensionObject.value1); // undefined

// 변경 가능
preventExtensionObject.value2 = 3;
console.log(preventExtensionObject.value2); // 3

// 객체 추가 불가
preventExtensionObject.value3 = 3;
console.log(preventExtensionObject.value3); // undefined

설명

불변하게 하려면 얼리고, 수정이 가능하게 하려면 봉인하고 추가만 불가능하게 하려면 확장방지하자

client 에서 객체 변조를 못하게 전역변수를 얼려버리자

여담

freeze 는 중첩된 object 에 적용이 안 되는데, 이 때 deepFreeze 함수를 사용해 재귀적으로 객체를 얼려버리면 된다.

deepFreeze method

mdn을 참조했다.

소스

function deepFreeze(obj) {
if (!Object.isFrozen(obj)) {
Object.freeze(obj);
}

for (const key in obj) {
if (
{}.hasOwnProperty.call(obj, key) ||
!(
typeof obj[key] === "function" ||
(typeof obj[key] === "object" && !!obj[key])
)
) {
continue;
}

deepFreeze(obj[key]);
}
}

Javascript 기본값 연산자 (축약된 삼항연산자)

· 약 1분

Javascript 기본값 연산자 (축약된 삼항연산자)

한번 쓰면 헤어나올 수 없는 기본값 연산자에 대해 알아보자.

소스

// JavaScript syntax to set a default value
const text = someString || "default text";
const text2 = someString || someString2 || "default text2";

설명

someString 이 있으면 someString 을 반환, 아니면 default text 를 반환한다. 계속 붙혀나가면서 쓸 수 있다.

php7 에서는 해당 기능이 추가되었다. || 대신에 ?? 를 사용하면 된다.

lodash 에선 defaultTo를 사용하면 된다.

링크 클릭시 return false가 페이지에 그대로 출력될 때

· 약 1분

링크를 클릭했는데, 페이지에 return false;가 찍혀나오는 경우가 있다.

소스

<script>
function test() {
if (true) {
return false;
}
}
</script>

<!-- 페이지에 return false;가 출력됨 -->
<a href="javascript:test();">테스트</a>

<!-- 대체 -->
<a href="javascript://" onClick="test();">테스트</a>

설명

a href 속성 안에 javascript 를 사용시 발생한다. javascript 호출문인 onClick 을 이용하자.