본문으로 건너뛰기

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

모든 태그 보기

jQuery Opener Document 제어

· 약 1분

부모창을 제어하는 구문은 아래처럼 사용한다.

document.parent.getElementById('id')...
window.opener.document.getElementById('id')...

jQuery

jQuery를 사용하고 있다면 생각보다 쉽게 요소 선택을 할 수 있다.

var $id = $("#id", opener.document); // parent.document도 가능
$id.val("value");

2열처럼 바로 값 변경도 가능하다.

form.reset()의 input hidden 초기화 문제

· 약 1분

Javascript 의 form 의 reset() 메소드는 hidden field 와 check, radio button 에 대해 초기화를 시켜주지 않는다.

따라서 form 의 모든 field 를 초기화 시키려면 아래의 메소드가 필요하다.

$.fn.clearForm = function () {
return this.each(function () {
var type = this.type,
tag = this.tagName.toLowerCase();
if (tag === "form") {
return $(":input", this).clearForm();
}
if (
type === "text" ||
type === "password" ||
type === "hidden" ||
tag === "textarea"
) {
this.value = "";
} else if (type === "checkbox" || type === "radio") {
this.checked = false;
} else if (tag === "select") {
this.selectedIndex = -1;
}
});
};

예제

$("#form").clearForm();

설명

여기의 clearForm 메소드를 hidden 도 초기화할 수 있게 커스터마이징 했다.

jQuery3의 큰 변경점

· 약 3분

jQuery3이 기존 버전에서 어떻게 달라졌는지 알아보자.

이 함수들은 이제 ajax 기능으로만 사용할 수 있다. 다음처럼 url 을 로드하는 데에만 사용할 수 있다.

// 사용 가능
$(div).load(url);

기존에 사용하던 엘레먼트 로드 시 callback 함수를 받는 구문은 더이상 사용할 수 없지만 on('load') 로 충돌을 피할 수 있다.

// 사용 불가능
$(img).load(function () {
console.log("이미지 로드 완료");
});

// 사용 가능
$(img).on("load", function () {
console.log("이미지 로드 완료");
});

document on Ready 삭제

document 로드시에 호출되는 함수들을 정의하기 위한 위의 형태는 더 이상 사용할 수 없다. $(document).ready(function(){ }); 으로 변경해도 되나

=> $(function(){ }); 로 사용하자, 권장하는 방법이다.

deferred의 Promise 스펙

Promise/A+ 스펙을 지키지 않은 2버전까지는 오류가 있었다고 하는데, then, when 등의 메소드를 사용 중엔 별다른 문제가 없어서 체감상 크게 느껴지지 않았다.

=> then().then().then().then().catch() 와 같은 구문이 가능하다.

bind(), unbind(), delegate() undelegate() 삭제

위 구문 사용시 console.warning이 떴기에 바꿔왔더라면 큰 문제는 없다.

=> on(), off() 로 대체하면 된다.

andSelf() 삭제

이 메소드가 삭제되어 sementic UI 사용시 오류가 발생한다.

=> addBack() 으로 대체하면 된다.

param() 이 %20 을 + 기호로 바꾸지 않음

$.ajax로 return 받을 때 가끔씩 빈칸이 더하기로 반환되는 문제가 드디어 해결되었다.

event.props, event.fixHooks 삭제

jquery.onoff 라이브러리 사용시 오류가 난다.

=> fixHooks는 fix로 대체 가능하고, props는 빈 배열로 초기화시키면 된다.

data attribute 정의시 kebab-case를 사용가능

이 부분은 jQuery 문서의 예제를 참조했다.

var $div = $("<div />");
$div.data("clickCount", 2);
$div.data("clickCount"); // 2
$div.data("click-count", 3);
$div.data("clickCount"); // 3
$div.data("click-count"); // 3

var allData = $div.data();
allData.clickCount; // 3
allData["click-count"]; // undefined
allData["click-count"] = 14;
$div.data("click-count"); // 3, NOT 14 as it would be in jQuery 2.x
allData.clickCount; // 3
allData["click-count"]; // 14

하지만 헷갈리니 camelCase 로.

기타 변경점은 링크 참조

Lodash 활용법

· 약 9분

Lodash는 underscore에서 성능을 개선한 라이브러리며 IE의 하위 브라우저에서 es5, es6의 메소드를 사용할 수 있게 해줄 뿐만 아니라 통계 사용시에는 필수로 사용할 수 밖에 없다. 공식 문서를 봐도 되지만 구체적인 예시가 있어야 이해하기가 편하다 시작해보자.

배열과 객체 모두 사용가능한 메소드

filter

_.filter( 콜렉션, 검색할 데이터 또는 콜백함수 ) 콜렉션에서 해당 데이터 또는 콜백 조건에 맞는 데이터를 포함한 콜렉션을 반환한다. 리스트 검색 조건에 유용하다.

var users = [
{ user: "barney", age: 36, active: true },
{ user: "fred", age: 40, active: false },
];

_.filter(users, (o) => !o.active);
// => [ { 'user': 'fred', 'age': 40, 'active': false } ]

// _.matches 메소드가 생략된 형태
_.filter(users, { age: 36, active: true });
// === _.filter(users, _.matches({ 'age': 36, 'active': true }))
// => [ { 'user': 'barney', 'age': 36, 'active': true } ]

// _.matchesProperty 메소드가 생략된 형태
_.filter(users, ["active", false]);
// => [ { 'user': 'fred', 'age': 40, 'active': false } ]

// _.property 메소드가 생략된 형태
_.filter(users, "active");
// => [ { 'user': 'barney', 'age': 36, 'active': true } ]

reject

_.reject( 콜렉션, 제외시킬 데이터 또는 콜백함수 ) 콜렉션에서 해당 데이터 또는 콜백 조건에 맞는 데이터를 제외한 콜렉션을 반환한다. Delete API를 호출 한 뒤 삭제된 결과값을 제외해서 리스트를 갱신하고 싶을 때 유용하다.

AngularJS와의 예시를 보자

$scope.remove = function (data) {
ApiService.delete(
{
id: data.id,
},
function (data) {
// 삭제 성공시 해당 데이터를 제외한 리스트로 변경
$scope.list = _.reject($scope.list, data);
}
);
};

size

_.size( 콜렉션 ) 콜렉션의 사이즈를 반환한다. length와 같다고 생각하면 된다.

_.size([1, 2, 3])
// => 3

_.size({ 'a': 1, 'b': 2 })
// => 2

_.size('apple'
// => 5

sampleSize

_.sampleSize( 콜렉션, [추출 갯수=1]) 콜렉션에서 추출 갯수만큼 랜덤 값을 배열로 반환한다.

_.sampleSize(_.range(1, 45), 7);
// => 로또 번호 추출

Array

배열에 사용 가능한 메소드

compact

_.compact( 배열 ) 배열에서 false, null, 0, ""(빈값), undefined, NaN의 값을 제외시킨 배열을 반환한다.

_.compact([0, 1, false, 2, "", 3]);
// => [1, 2, 3]

take

_.take( 배열, [가져올 요소 수=1]) 배열에서 앞에서부터 n개의 요소를 반환한다.

_.take([3, 5, 4, 7, 9], 3);
// => [3, 5, 4]

uniq

_.uniq( 배열 ) 배열의 중복 값을 제거한다.

_.uniq([1, 1, 3]);
// => [1, 3]

zip

_.zip( 배열, 배열... ) 배열의 인덱스에 따라 배열을 다시 만든다. 예제를 보는게 이해가 빠르다.

_.zip(["a", "b"], [1, 2], [true, false]);
// => [['a', 1, true], ['b', 2, false]]

Object

객체에 사용 가능한 메소드

get

_.get( 객체, 가져올 키, 기본값 ) 객체에서 해당 키 값만을 가져온다.

var object = { a: 1, b: 2, c: 3, e: { f: 5 } };

_.get(object, "a");
// => 1

_.get(object, "d");
// undefined

_.get(object, "d", 4);
// 4

_.get(object, "e.f");
// 5

omit

_.omit( 객체, 제외할 키 배열 ) 객체에서 해당 키를 제외한 객체를 반환한다.

var object = { a: 1, b: "2", c: 3 };

_.omit(object, ["a", "c"]);
// => { 'b': '2' }

_.omit(object, "a");
// => { 'b': '2', 'c': 3 }

values

_.values( 객체 ) 객체에서 값만을 추출한 배열을 반환한다.

_.values({ a: 1, b: 2, c: [3, 4] });
// => [ 1, 2, [3, 4] ]

_.values("hi");
// => ['h', 'i']

Util

유틸성이 있는 메소드

constant

_.constant( 반환 값 ) 반환 값을 반환해주는 함수이다. 단독으로는 거의 사용하지 않고, 다른 Lodash 함수들과 같이 사용한다.

_.constant(0);
// => 0

_.constant({ a: 1 });
// => { a: 1 }

times

_.times( 반복횟수, 콜백함수 ) 콜백함수 조건에 맞게 반복횟수만큼의 데이터를 배열로 반환한다. 초기화를 시킬 때 유용하다.

_.times(3, _.constant(0));
// => [0, 0, 0]

range

_.range( [시작인덱스], 종료인덱스, [증가 폭=1]) 배열을 초기화 시킬 때 유용하다. 단지 배열을 숫자로 초기화해야된다면 _.range가 속도면에서 _.times(n, constant)보다 월등히 빠르다.

_.range(0, 6, 0);
// => [0, 0, 0, 0, 0, 0]

_.range(4);
// => [0, 1, 2, 3]

Why

이런 편리함이 있는데도 왜 안 쓰는지 모르겠지만, 몇 가지 이유가 더 있다

Lazy Evaluation

링크를 참조하자

코드로 요약하자면

// 이런 구문을
var result = [],
temp1 = [],
temp2 = [],
temp3 = [];

for (var i = 0; i < source.length; i++) {
temp1[i] = func1(source[i]);
}

for (i = 0; i < source.length; i++) {
temp2[i] = func2(temp1[i]);
}

for (i = 0; i < source.length; i++) {
temp3[i] = func3(temp2[i]);
}

result = temp3;

// 아래처럼 바꿔 엄청난 속도 향상을 가져올 수 있다
var result = [];
for (var i = 0; i < source.length; i++) {
result[i] = func3(func2(func1(source[i])));
}

chaining

또 다른 큰 이유는 chain을 활용해 functional programming이 가능하단 것이다. 예를 들어 다음과 같은 데이터가 있다고 보자

data
[
{
tag: {
name: "tag1",
media: {
nodes: [
{
id: "uid1",
user: "gracefullight",
caption: "caption1",
likes: 10,
},
{
id: "uid2",
user: "gracefullight",
caption: "caption2",
likes: 20,
},
],
},
},
},
{
tag: {
name: "tag2",
media: {
nodes: [
{
id: "uid3",
user: "gracefullight",
caption: "caption3",
likes: 30,
},
{
id: "uid4",
user: "gracefullight",
caption: "caption4",
likes: 40,
},
],
},
},
},
];

js

이 데이터의 nodes 데이터만 가져와서 새로운 data 배열을 만들고 싶다면 어떻게 할까? lodash를 모르는 상태라면 대충 이런식의 로직이 나온다.

// for문 애호가
const data = ["위에 데이터 배열"];

let result = [];
for (let i = 0, len = data.length; i < len; i++) {
let nodes = data[i].tag.media.nodes;

for (let j = 0, len2 = nodes.length; j < len2; j++) {
// 머리가 슬슬 아파진다.
}
}

// 더 나은 방법
result = data
.map((item) => {
// 여기까지의 결과는 [[{}, {}], [{}, {}]] 모양이 될 것이다
return item.tag.media.nodes;
})
.reduce((prev, item) => {
// 아 여기서 난관이다. 배열을 벗기면서 내부의 오브젝트만 prev array에 push 또는 concat해줘야한다.
return prev;
}, []);

With lodash

lodash를 사용하면 다음과 같이 직관적으로 구현이 가능하다.

result = _.chain(data).map("tag.media.nodes").flatten().values()[
// 결과는 다음과 같다
({ id: "uid1" }, { id: "uid2" }, { id: "uid3" }, { id: "uid4" })
];

여담

Lodash는 jQuery와 중복되는 이름을 가진 메소드가 몇 가지 있는데, 차이점은 콜백함수에서 value를 첫번째 인자로 받는다는 것이고 브라우저에서 native method를 사용할 수 있으면 그걸 사용한다는 것이다. (each를 예를들면 array.forEach)

// lodash
_.each(["a", "b", "c"], (value, index, list) => {});

// jQuery
$.each(["a", "b", "c"], (index, value) => {});

따라서 lodash를 사용하는 것을 권한다. (속도도 더 빠르다)

MomentJS 활용법

· 약 2분

java에 joda-time이 있듯 javascript에는 moment가 있다. 기초적인 moment 사용법은 알고 있다고 가정한다.

현재시각과 비교해 날짜를 어제 오후 12:31처럼 표시하고 싶은 경우 사용한다.

소스

moment(date).calendar(today, { sameElse: "YYYY-MM-DD HH:mm:ss" });

date는 비교할 날짜, today는 현재시각 값이다. 일주일 이하일 경우 어제.. 그저께.. 등으로 반환되고, 이상일 경우 sameElse에 등록된 포맷으로 반환된다.

global locale 설정을 지정하지 않았을 경우 sameElse가 들어가는 option object 안에 locale을 커스터마이징 할 수 있다.

기타 옵션은 API 참조

diff

A에서 B의 차이를 구할 때 쓰는 diff method를 쓰다가, 월 또는 년도의 차이를 계산하는 경우가 생긴다.

소스

worst case

moment("2016-06").diff("2015-01", "month");

이렇게 처리하면 되지 않을까? 동작은 하지만 오류가 날 수 있다고 console.warning 이 찍힌다.

good case

moment([2016, 06]).diff([2015, 01], "month");

실행이 잘되지만, YYYY-MM을 split해서 넣어줘야하는 번거로움이 있다.

best case

moment("2016-06", "YYYY-MM").diff("2015-01", "month");

moment 형식으로 변환시 두번째 파라미터에 포맷을 지정하면 console.warning도 없고 번거로움도 사라진다!

Materialize pickadate 설정

· 약 2분

기본예제는 type="date" 로만 설정이 되있어서 type="text"에도 적용 가능하게 onSet callback 을 설정했고, 한글로 보이게 수정했다.

<input type="text" name="date" class="date" />

<script>
$(function () {
// pickadate 옵션 전역설정
$.extend($.fn.pickadate.defaults, {
monthsFull: [
"1월",
"2월",
"3월",
"4월",
"5월",
"6월",
"7월",
"8월",
"9월",
"10월",
"11월",
"12월",
],
monthsShort: [
"1월",
"2월",
"3월",
"4월",
"5월",
"6월",
"7월",
"8월",
"9월",
"10월",
"11월",
"12월",
],
weekdaysFull: ["일", "월", "화", "수", "목", "금", "토"],
weekdaysShort: ["일", "월", "화", "수", "목", "금", "토"],
selectMonths: true,
selectYears: 140,
showMonthsShort: false,
showWeekdaysFull: false,
close: "닫기",
clear: false,
today: "오늘",
format: "yyyy-mm-dd",
formatSubmit: "yyyy-mm-dd",
max: true, // 이 옵션이 ture면 오늘까지밖에 날짜 선택을 못한다
closeOnSelect: true,
onSet: function (e) {
if (e.select) {
this.close();
}
},
});

// 활성화
$(".date").pickadate();
});
</script>

결과

image from hexo

Express 환경에서 node_modules 안의 script 사용

· 약 1분

앵귤러 또는 다른 브라우저에 필요한 스크립트를 npm 으로 설치하고 node_modules 안에 있는 스크립트로 참조하고 싶을 때 다음과 같이 라우팅을 추가한다.

// scripts 경로로 접근시 node_modules을 사용할 수 있게 설정
app.use("/scripts", express.static(path.join(__dirname, "node_modules")));
<!-- 결과 -->
<script src="/scripts/angular/angular.min.js"></script>

Windows10에서 NodeJS MariaSQL 모듈 설치

· 약 3분

mariasql package 설치 명령어 실행시 오류를 내뿜으며 node-debug.log 를 확인하라고 명령어가 나올 경우 아래와 같이 하면 된다.

debug log 에는 node-gyp rebuild 를 하라고 나오는데, 이 오류메세지와는 아무 관련이 없다.

해결

Python 2.7 설치

윈도우에 Python 2.7 버전이 설치되어있지 않으면 설치해야한다. 파이썬 2 이상 3 미만 버전을 쓰면 되는데, 2.7 을 강조하니 쓰자.

여기서 다운로드 한다. 설치시 Window PATH 등록 옵션을 꼭 선택해야한다.

Python 경로를 npm 에 등록

$ npm config set python "/the/python/path" --global

the/python/path 에 자신의 python 설치 경로를 넣어주자.

Microsoft Visual Studio Community 2015 설치

왜 VS 2015 를 설치해야하지? 라고 생각이 들텐데, **Visual C++**을 사용하기 때문이다.

여기서 다운로드한다. 설치시 프로그래밍언어 탭에서 Visual C++을 꼭 선택해서 설치해야한다.

기존에 VS2015 가 설치되어있는 경우, 프로그램 추가/삭제에서 선택 후 수정 메뉴를 눌러 Visual C++ 옵션을 추가한 뒤 업데이트해준다.

2017 년 기준 위 링크가 만료되어 cpp-build-tools를 설치해야한다

Visual Studio 버전을 npm 에 등록

$ npm config set msvs_version "2015" --global

MariaSQL Package 설치

$ npm install mariasql --save

여담

Windows10 에서 mariasql package 설치시 C++ 컴파일이 필요하니, VS2015 로 C++ 컴파일러를 설치해야된다라는 걸 msdn 이나 npm 에 친절히 남겨줬으면 이렇게까지 시간을 날리지 않았을텐데...

최근 해결방법

빠르고 위의 오류가 절대 발생하지 않는 mysql2 패키지를 사용하면 된다.

image from hexo

Cannot find module '../build/Release/bson'

· 약 2분

mongoose-post-find 모듈 사용시 bson 라이브러리를 찾지 못해 설치가 안되는 경우가 있다.

{ Error: Cannot find module '../build/Release/bson'
at Function.Module._resolveFilename (module.js:440:15)
at Function.Module._load (module.js:388:25)
at Module.require (module.js:468:17)
at require (internal/module.js:20:19)
// 여기서 오류가 발생한다
at Object.<anonymous> (your project\mongoose-post-find\node_modules\bson\ext\index.js:15:10)
at Module._compile (module.js:541:32)
at Object.Module._extensions..js (module.js:550:10)
at Module.load (module.js:458:32)
at tryModuleLoad (module.js:417:12)
at Function.Module._load (module.js:409:3)
at Module.require (module.js:468:17)
at require (internal/module.js:20:19)
at Object.<anonymous> (your project\mongoose-post-find\node_modules\bson\lib\bson\index.js:3:24)
at Module._compile (module.js:541:32)
at Object.Module._extensions..js (module.js:550:10)
at Module.load (module.js:458:32) code: 'MODULE_NOT_FOUND' }
js-bson: Failed to load c++ bson extension, using pure JS version

해결

해당 패키지 경로의 15번째 줄을 따라가보면 bson 패키지의 경로를 지정해주는 부분이 있는데, 이 부분을 같은 패키지 내의 bson 경로로 일치시켜주면 된다.

bson/index.js
var bson = null;

try {
// Load the precompiled win32 binary
if (process.platform == "win32" && process.arch == "x64") {
bson = require("./win32/x64/bson");
} else if (process.platform == "win32" && process.arch == "ia32") {
bson = require("./win32/ia32/bson");
} else {
bson = require("../build/Release/bson");
}
} catch (err) {
// Attempt to load the release bson version
try {
// 여기의 상대경로를 같은 패키지의 bson의 경로로 일치시켜주면 된다.
bson = require("../browser_build/bson");
} catch (err) {
console.dir(err);
console.error(
"js-bson: Failed to load c++ bson extension, using pure JS version"
);
bson = require("../lib/bson/bson");
}
}

React 시작하기

· 약 4분

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 의 비교를 보길 원한다.