bkjang / dev-tips Goto Github PK
View Code? Open in Web Editor NEW๐ This repository is a collection of development tips and troubleshooting I have experienced during development.
๐ This repository is a collection of development tips and troubleshooting I have experienced during development.
์ฒ์์๋ react-native-community/react-native-geolocation ์ฌ์ฉํ์๋ค๊ฐ ์ฌ์ฉ์์๊ฒ ์์น ์ ๋ณด ๊ถํ์ ๋ํด ๋ฌผ์ด๋ณด๋ ํ์ธ ์ฐฝ์ ๋์ฐ๊ณ ์ฑ์ด ๋น์ ์ ์ข ๋ฃ๋๋ ๋ฌธ์ ๊ฐ ๋ฐ์.
JS์ navigator.geolocation
๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ๋ฐ
์ฝ๋์ ์คํ์ผ์ ์๋ฆ๋ต๊ฒ ํต์ผ์ฑ ์๊ฒ ๋ง๋ค์ด์ค๋ค.
{
"trailingComma": "all",
"tabWidth": 2,
"semi": true,
"singleQuote": true
}
์๋ฐ์คํฌ๋ฆฝํธ์ ๋ฌธ๋ฒ์ ์ฒดํฌํ๊ณ ํต์ผ์ฑ ์๋ ์ฝ๋๋ฅผ ์์ฑํ ์ ์๋๋ก ๋์์ค๋ค.
// npm install eslint-config-airbnb eslint-config-prettier
"eslintConfig": {
"extends": [
"react-app",
"airbnb",
"prettier"
],
"rules": {
"no-unused-vars": 1,
"no-console": 0,
"react/jsx-filename-extension": 0
}
},
์ง๋์น๊ฒ ๋ฌธ๋ฒ ์ฒดํฌ๋ฅผ ํ๊ฒ ๋๋ฉด ์์ฐ์ฑ์ด ๋จ์ด์ง ์๋ ์๊ธฐ ๋๋ฌธ์ ๊ตณ์ด ๊ผญ eslint ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ ํ์๋ ์๋ค.
์์ฃผ ์ฌ์ฉ๋๋ ์ฝ๋์ ๋ํ์ฌ ๋จ์ถ์ด๋ฅผ ์ฌ์ฉํ์ฌ ๋น ๋ฅด๊ฒ ๋ง๋ค์ด๋ธ๋ค.
// Preferences => User Snippet
{
"Create Functional React Component": {
"prefix": "fc",
"body": [
"import React from 'react';",
"",
"function ${TM_FILENAME_BASE}() {",
" return (",
" <div>",
" ${1:Functional Component}",
" </div>",
" )",
"}",
"",
"export default ${TM_FILENAME_BASE};"
],
"description": "Create Functional React Component"
}
}
ํ ํ๋ฉด์ 3๊ฐ์ ์ปดํฌ๋ํธ๊ฐ ์๊ณ ๊ฐ์ฅ ์๋์ ์๋ ์ปดํฌ๋ํธ๊ฐ ์คํฌ๋กค ์ปดํฌ๋ํธ์ด๋ค.
์คํฌ๋กค ์ปดํฌ๋ํธ๋ฅผ ์๋๋ก ๋ด๋ ธ์ ๋ ์์ ์ปดํฌ๋ํธ๋ค์ ์ ํ ์ํ๋ก ์ต์๋จ์๋ To Top
๊ธฐ๋ฅ์ ํ๋ ํ์ดํ๊ฐ ํจ๊ป ํ์๋์ด์ผ ํ๋ค.
js๋ฅผ ์ฌ์ฉํ์ฌ ์๊ฐ์ ์ธก์ ํด์ผํ๋ ๊ฒฝ์ฐ๊ฐ ์๋ค.
์๋ฅผ ๋ค์ด ๋ด ์ฝ๋๊ฐ ์ผ๋ง๋งํผ์ ์๊ฐ์ ์ฌ์ฉํ๋์ง ์๊ณ ์ถ์ ๊ฒฝ์ฐ๋ค.
๋ ์ฐ๋กํ๋ง์ฒ๋ผ ๋ช ์ด ๋์ ์ด๋ฒคํธ๊ฐ ํ ๋ฒ๋ง ๋ฐ์ํ๊ฒ ๋ง๋ค์ด์ผ ํ๋ ๊ฒฝ์ฐ๋ ์๋ค.
๋ฌผ๋ก ํ๋ก ํธ์ js ์ฝ๋์์๋ ์์ฒญ ์ ๋ฐํ ์๊ฐ์ด ํ์ํ ๊ฒฝ์ฐ๊ฐ ๋ง์ง๋ ์๋ค.
ํ์ง๋ง ์ ํ๋ ๋์ ์๊ฐ์ ์ฌ์ฉํ ์ฝ๋๋ ์ธ์ ์ด๋ป๊ฒ ์คํ๋์ด๋ ๋์ผํ๊ฒ ๋์ํ๋ค๋ ๊ฒ์ ๋ณด์ฅํ๋ค. ํ์ง์ด ์ข์์ง๋ค.
๋ฐ๋ผ์ ์๊ฐ์ ์ธก์ ํ๋ ๋ฐฉ๋ฒ์ ์์๋ ํ์๊ฐ ์๋ค.
js์์ ์๊ฐ์ ์ธก์ ํ๋ ๋ฐฉ๋ฒ์ ํฌ๊ฒ 4๊ฐ์ง๊ฐ ์กด์ฌํ๋ค.
Date๋ ๊ฐ์ฅ ๊ณ ์ ์ ์ด๋ฉฐ ์ฝ๊ฒ ์ ๊ทผํ ์ ์๋ API๋ค.
Date.now๋ฅผ ์ฌ์ฉํ๋ฉด 1970๋ 1์ 1์ผ 0์ 0๋ถ 0์ด๋ถํฐ ํ์ฌ๊น์ง ๊ฒฝ๊ณผ๋ ๋ฐ๋ฆฌ ์ธ์ปจ๋(milliseconds)๋ฅผ Number ํ์ ์ผ๋ก ๋ฐํํ๋ค.
ํน์ ์ฝ๋ ์ด์ ๊ณผ ์ดํ์ Date.now๋ฅผ ์ฌ์ฉํ๋ฉด ๊ทธ ์ฝ๋์ ์คํ ์๊ฐ์ ์ ์ ์๋ค.
const start = Date.now();
let arr = ['1'];
for (let i = 0; i < 10000000; i++) {
arr.push('' + i);
}
const end = Date.now();
console.log((end - start) / 1000);
Date.now ๋ฉ์๋๋ ์์คํ ์๊ฐ์ ๊ธฐ์ค์ผ๋ก ํ์ฌ ํ์์คํฌํ๋ฅผ ๋ฐ๋๋ค. ๊ทธ๋์ user agent์ ๋ฐ๋ผ ๋ค๋ฅผ ์ ์์ด ์ ๋ขฐ์ฑ์ด ๋จ์ด์ง๋ค.
์ด๊ฑธ ์ฌ์ฉํ๋ฉด Date.now๋ณด๋ค ๊ฐํธํ๊ฒ ์ด์ฉํ ์ ์๋ค.
console.time('pushTest');
let arr = ['1'];
for (let i = 0; i < 10000000; i++) {
arr.push('' + i);
}
console.timeEnd('pushTest');
pushTest๋ผ๋ label์ ๋ถ์ผ ์ ์๋ค.
ํ๋ ์ผ์ ๋์ผํ๋ค. console ๊ฐ์ฒด์ ์๋ profile๊ณผ ๊ฐ์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ dev tools์์ ๋ ๋ค์ํ ์ ๋ณด๋ฅผ ๋ณผ ์๋ ์๋ค.
nodejs์์๋ ์์ ๋๊ฐ์ง ๋ฐฉ๋ฒ๋ณด๋ค ๋์ฑ ์ ๋ฐํ ๋ฐฉ๋ฒ์ผ๋ก process.hrtime๋ฅผ ์ ๊ณตํ๋ค.
์ด๋ nanoseconds๊น์ง ์ ํํ ์ธก์ ๊ฐ๋ฅํ๋ฉฐ clock drift freeํ๋ค.
const hrstart = process.hrtime();
let arr = ['1'];
for (let i = 0; i < 10000000; i++) {
arr.push('' + i);
}
const hrend = process.hrtime(hrstart);
console.log(hrend);
//[2, 488580674]
hrtime์ ๋ฐํ๊ฐ์ด array์ธ๋ฐ ๊ฐ๊ฐ [seconds, nanoseconds]๋ค.
๋ธ๋ผ์ฐ์ ์์๋ ฮผs๋ผ๊ณ ํ๊ธฐํ๋ ๋ง์ดํฌ๋ก ์ธ์ปจ๋๊น์ง ์ ํํ API๋ฅผ ์ ๊ณตํ๋ค. ์ด performance๋ ์์คํ clock๊ณผ๋ ๋ฌด๊ดํ๋ค.
console.time์ ms๋ ๋ฐ๋ฆฌ์ธ์ปจ๋๋ก 1s / 1000 ์ด๋ค.
performance์์ ์ ๊ณตํ๋ ฮผs๋ 1ms / 1000 ์ด๋ค.
์ฌ์ฉ๋ฐฉ๋ฒ์ Date.now์ ๋์ผํ๋ค.
const start = performance.now();
let arr = ['1'];
for (let i = 0; i < 10000000; i++) {
arr.push('' + i);
}
const end = performance.now();
console.log((end - start) / 1000);
performance.now์ ๋ฐํ๊ฐ์ DOMHighResTimeStamp๋ค. ์ด๋ ms(๋ฐ๋ฆฌ ์ธ์ปจ๋)์ธ๋ฐ ์์์ ์ ํฌํจํ๋ค. ๋ฐ๋ผ์ ๋ง์ดํฌ๋ก ์ธ์ปจ๋๊น์ง ์ ํํ๋ค.
performance.now๊ฐ ์ธก์ ๋๋ ์๊ฐ์ performance.timing.navigationStart๋ถํฐ๋ค.
์ด ์๊ฐ์ ์ด์ document๊ฐ unload๋๋ฉฐ ์๋ก์ด document๋ฅผ fetchํ๋(fetchStart) ์์ ์ด๋ค.
๋ธ๋ผ์ฐ์ ์์ ์ ๊ณตํ๋ ๋ฐฉ๋ฒ๋ค์ web worker๋ฅผ ์ฌ์ฉํ๋ค. ํ์ฌ performance๊ฐ ๊ทธ ์ค์ ๊ฐ์ฅ ์ ํํ ์๊ฐ์ ์ธก์ ํ ์ ์๋ค.
sessionStorage
๋ฅผ ์ด์ฉํ์ฌ ์ธ์ฆ ์ ๋ณด๋ฅผ ์ ์ฅํ๋ ์ค, ์๋ก์ด ํญ์ ์ด์๋๋ฐ ์ธ์ฆ ๋ฐ์ดํฐ๊ฐ ๊ณต์ ๋์ง ์๋ ๋ฌธ์ ๊ฐ ๋ฐ์.
sessionStorage
๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ธ๋ผ์ฐ์ ๋ฅผ ๋ซ์ผ๋ฉด ๋ฐ์ดํฐ๊ฐ ์ญ์ ๋์ง๋ง, ํญ ๊ฐ์๋ ๋ฐ์ดํฐ๊ฐ ๊ณต์ ๋์ง ๋ชปํ๋ค. ํ์ง๋ง, localStorage
๋ฅผ ์ฌ์ฉํ๋ฉด ํญ ๊ฐ์ ๊ณต์ ๋ ๊ฐ๋ฅํ์ง๋ง, ๋ธ๋ผ์ฐ์ ์ฐฝ์ด ์ข
๋ฃ๋์์ ๋๋ localStorage
์๋ ๋ฐ์ดํฐ๊ฐ ๋จ์์๊ธฐ ๋๋ฌธ์ ๋ณด์์ ๋ฌธ์ ๊ฐ ๋ ์ ์๋ค.
sessionStorage
์ localStorage
๊ฐ๊ฐ์ ํน์ฑ์ ์ฌ์ฉํ์ฌ ํญ์ ์๋ก ์ผฐ์ ๋ localStorage
์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ณ ์ด๋ฅผ ์๋ก์ฐ ํญ์ sessionStorage
์ ๋ด๊ณ localStorage
์ ๊ฐ์ ์ง์ด๋ค. ์ฝ๊ฐ์ ์์์๊ธด ํ์ง๋ง, ์ ๋์ํ๋ค.
// transfers sessionStorage from one tab to another
var sessionStorage_transfer = function(event) {
if(!event) { event = window.event; } // ie suq
if(!event.newValue) return; // do nothing if no value to work with
if (event.key == 'getSessionStorage') {
// another tab asked for the sessionStorage -> send it
localStorage.setItem('sessionStorage', JSON.stringify(sessionStorage));
// the other tab should now have it, so we're done with it.
localStorage.removeItem('sessionStorage'); // <- could do short timeout as well.
} else if (event.key == 'sessionStorage' && !sessionStorage.length) {
// another tab sent data <- get it
var data = JSON.parse(event.newValue);
for (var key in data) {
sessionStorage.setItem(key, data[key]);
}
}
};
// listen for changes to localStorage
if(window.addEventListener) {
window.addEventListener("storage", sessionStorage_transfer, false);
} else {
window.attachEvent("onstorage", sessionStorage_transfer);
};
// Ask other tabs for session storage (this is ONLY to trigger event)
if (!sessionStorage.length) {
localStorage.setItem('getSessionStorage', 'foobar');
localStorage.removeItem('getSessionStorage', 'foobar');
};
react-router-dom
๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ด๋ถ์๋ ๊ฐ์ฒด์ ๊ฐ์ ๋น๊ตํ๋ ๊ธฐ๋ฅ์ ๋ด๋ถ์ ์ผ๋ก ์ฌ์ฉํ๊ณ ์๋ค.
value-equal
์ด๋ผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๊ณ ์๊ณ ์ด ์ฝ๋๋ฅผ ์ค์ ๋ก ๊ฐ๋ฐ์์๋ ์ถฉ๋ถํ ์ ์ฉํด๋ณผ ์ ์์ ๊ฒ ๊ฐ๋ค.
WebPack์ ๋ชจ๋ ๋ฒ๋ค๋ฌ.
WebPack์ ํ๋ก์ ํธ ์ ์ฒด๋ฅผ ํ ๋จ์๋ก ๋ถ์ํ๋ค.
์ฆ, ์ง์ ํ ๋ฉ์ธ ํ์ผ์์ ์์ํด ์๋ฐ์คํฌ๋ฆฝํธ์ require(webpack commonJS ๋ชจ๋ ์ง์)๊ณผ import(ES6)๋ฌธ์ ์ฐธ๊ณ ํด ํ๋ก์ ํธ์ ๋ชจ๋ ์์กด์ฑ์ ์กฐ์ฌํ๊ณ ๋ก๋๋ฅผ ์ด์ฉํด ์ฒ๋ฆฌํ ํ ๋ฒ๋ค๋ก ๋ฌถ์ ์๋ฐ์คํฌ๋ฆฝํธ ํ์ผ์ ์์ฑํ๋ค.
Babel์ ํธ๋์คํ์ผ๋ฌ.
Babel์ ES6/ES7 ์ฝ๋๋ฅผ ECMAScript5 ์ฝ๋๋ก transpiling ํ๊ธฐ ์ํ ๋๊ตฌ.(๊ผญ ES5๊ฐ ๋ ํ์๋ ์๋ค.)
Babel์ ๋ค์ํ ์์ ๋ชจ๋๋ค๋ก ๊ตฌ์ฑ๋๋ฉฐ ๋ค์ํ ๋ชจ๋์ ๋ด๋ ์ผ์ข
์ ์์ ์ญํ ์ ํ๋ค.
์ฝ๋๋ฅผ ์ปดํ์ผ ํ๊ธฐ ์ํด ์์ ๋ชจ๋๋ค(ex. presets)์ ์ฌ์ฉํฉ๋๋ค.
function throttled(delay, fn) {
let lastCall = 0;
return function (...args) {
const now = (new Date).getTime();
if (now - lastCall < delay) {
return;
}
lastCall = now;
return fn(...args);
}
}
function debounced(delay, fn) {
let timerId;
return function (...args) {
if (timerId) {
clearTimeout(timerId);
}
timerId = setTimeout(() => {
fn(...args);
timerId = null;
}, delay);
}
}
๊ท์น | ๊ตฌํ | ์ค๋ช |
---|---|---|
๋ชจ๋ ๊ณต๋ฐฑ ์ฐพ๊ธฐ | /\s/g | ๋์ด์ฐ๊ธฐ, ํญ, ์ค๋ฐ๊ฟ ๊ฐ์ง |
ํด๋์ ํ๋ฒํธ | 01[016789]\D?\d{3,4}\D?\d{4} | 010-1234-5678, 01012345678, 010.1234.5678 |
์ด๋ฉ์ผ ์ฒดํฌ | /^0-9a-zA-Z@0-9a-zA-Z.[a-zA-Z]{2,3}$ | [email protected] |
Time Slider๊ฐ ์ด๋ํ ๋ ๋ง๋ค ์ง๋์น๊ฒ ๋ง์ ์์ฒญ์ด ๋ฐ์.
์๊ฐ๋ณ๋ก HeatMap์ ๋ณด์ฌ์ค ํ์๊ฐ ์์ด Time Slider๋ฅผ ์ด์ฉํด ๊ตฌํํ๋ ์ค ๋ฐ์.
Time Slider์ ๋ฏธ์ธํ ์์ง์์๋ ์์ฒญ์ด ๋ฐ์ํ์ฌ ์ง๋์น๊ฒ ๋ง์ ์์ ์๋ชจ.
Debouncing์ ์ ์ฉํด ์ผ์ ์ฃผ๊ธฐ๋ฅผ ๊ธฐ์ค์ผ๋ก ๋ง์ง๋ง ์ด๋ฒคํธ์ ๋ํ ๋น๋๊ธฐ ์์ฒญ๋ง ์ํ.
function onChangeSliderWithPlay() {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function () {
//๋น๋๊ธฐ ์ฒ๋ฆฌ
renderDataWithPlay();
}, 500);
}
๊ฒฐ๊ตญ, Scroll ์ด๋ฒคํธ์ ์ต์ ํ์ ํฌ๊ฒ ๋ค๋ฅด์ง ์๊ธฐ ๋๋ฌธ์ rAF๋ฅผ ์ด์ฉํ ์คํฌ๋กค ์ด๋ฒคํธ ๋ฐฉ์์ผ๋ก ๊ตฌํ์ด ๊ฐ๋ฅํ์ง ์์๊น?
react
์ redux
์ ๊ณต์ ๋ฌธ์๋ฅผ ์ฝ๋ค ๋ณด๋ฉด Immutability
๋ฅผ ๊ฐ์กฐํ๋ ๋๋ชฉ์ ์ข
์ข
๋ณผ ์ ์๋ค.
์ ๋ถ๋ณ์ฑ(Immutability)๋ฅผ ์ ์งํด์ค์ผ ํ ๊น?
๋ถ๋ณ์ฑ์ ์งํค์ง ์์ผ๋ฉด ์ฐ๋ฆฌ๋ ์ปดํฌ๋ํธ๋ฅผ ์ต์ ํ ํ ์ ์๋ค.
๋ฆฌ์กํธ๊ฐ ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋งํ๋ ๊ณผ์ ์ ์ดํด๋ณด์.
- setState๋ฅผ ํธ์ถ (ํน์ ๋ถ๋ชจ๋ก๋ถํฐ props๋ฅผ ์ ๋ฌ ๋ฐ์)
- shouldComponentUpdate๋ฅผ ์คํํ๋๋ฐ false๋ฅผ ๋ฆฌํดํ๋ฉด ์ฌ๊ธฐ์ ๋ฉ์ถ๊ณ , true๋ฅผ ๋ฆฌํดํ๋ฉด ๋ค์ ๋จ๊ณ๋ก ์ด๋
- ๊ฐ์ DOM๊ณผ ์ค์ DOM์ ๋น๊ตํด์ ๋ณ๊ฒฝ์ฌํญ์ด ์์ผ๋ฉด ํ๋ฉด์ ๋ค์ ๊ทธ๋ฆฐ๋ค
ํต์ฌ์ 2๋ฒ์ ์๋ค. ๋ค์๊ณผ ๊ฐ์ 2๊ฐ์ ์ํ๋ฅผ ๋น๊ตํ๋ค๊ณ ๊ฐ์ ํด๋ณด์.
const array = [1,2,3,4];
const sameArray = array;
sameArray.push(5);
console.log(array !== sameArray); // false
const array = [1,2,3,4];
const differentArray = [...array, 5];
console.log(array !== differentArray); // true
์ฒซ ๋ฒ์งธ ์ฝ๋์ array
์ sameArray
๋ณ์๊ฐ ์ฐธ์กฐํ๊ณ ์๋ ๋ฐฐ์ด์ ์ฃผ์ ๊ฐ์ ์๋ก ๊ฐ๋ค. ํ์ง๋ง, ๋ ๋ฒ์งธ ์ฝ๋์ ๊ฐ๊ฐ์ ๋ฐฐ์ด์ ๋ค๋ฅธ ๋ ํผ๋ฐ์ค๋ฅผ ๊ฐ์ง๊ธฐ ๋๋ฌธ์ ๋น๊ตํ์ ๋ ๋ค๋ฅด๋ค๋ ๊ฒฐ๊ณผ ๊ฐ์ด ๋์ค๊ฒ ๋๋ค.
์ด์ ๊ฐ์ด ๋ถ๋ณ์ฑ์ ์ ์งํ์ฌ ์ฝ๋๋ฅผ ์์ฑํ๋ฉด ๊ฐ ๊ฐ์ฒด์ ๊ฐ์ด ์๋ ๋ ํผ๋ฐ์ค ๊ฐ๋ง ๋น๊ต๋ฅผ ํด์ฃผ๋ฉด ๋๋ค.
์ฆ, shouldComponentUpdate
๋ด์ ์ฝ๋๋ ํ ์ค์ด๋ฉด ์ปดํฌ๋ํธ๋ฅผ ์ต์ ํํ ์ ์๊ฒ ๋๋ค.
ํ์ง๋ง, ๋ถ๋ณ์ฑ์ ์ ์งํ๋ฉฐ ์ฝ๋๋ฅผ ์์ฑํ๋ค๋ณด๋ฉด ์ํ ์ ๋ฐ์ดํธ ๋ก์ง์ด ๋ณต์กํด์ง ์ ์๋ค.
state = {
where: {
are: {
you: {
now: 'faded',
away: true // ์ด ๋ถ๋ถ์ ๋ฐ๊พธ๊ณ ์ถ๋ค
},
so: 'lost'
},
under: {
the: true,
sea: false
}
}
}
const { where } = this.state;
this.setState({
where: {
...where,
are: {
...where.are,
you: {
...where.are.you,
away: false
}
}
}
});
์์ ์์๋ง ๋ด๋ ์ถฉ๋ถํ ๋ณต์กํด์ง๋ ๊ฒ์ ๋ณผ ์ ์๋ค. ์ด๋ฐ ์ํ ์
๋ฐ์ดํธ ๋ก์ง์ ํธํ๊ฒ ํด์ฃผ๋ ๋ํ์ ์ธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ฐ๋ฆฌ๊ฐ React
๋ฅผ ์ฌ์ฉํ๋ฉด์ ํจํด ์ฒ๋ผ ๋ง์ด ์ฌ์ฉํด์๋ Immutable.js
์ด๋ค.
์ ๋ฆฌํ์๋ฉด, React์ ์ํ๋ ์ปดํฌ๋ํธ ์ต์ ํ๋ฅผ ์ํด ๋ถ๋ณ์ฑ์ ์ ์งํด์ผํ๋ฉฐ ์ด๋ฅผ ํธํ๊ฒ ํ๊ธฐ ์ํด Immutable.js
๊ฐ์ ๋ถ๋ณ ๊ฐ์ฒด๋ฅผ ๋ฐํํด์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ค.
@SeonHyungJo ๋์ ๊ธ์ ๋ณด๊ณ ๋ฌธ๋ ์๊ฐ์ด ๋ ๊ฑด๋ฐ ๊ธ์ ๋ด์ฉ๋๋ก console.log
์ console
์๋ ํ์ ๋ฉ์๋๊ฐ ์กด์ฌํ ๊ฒ์ด๋ค. ์ด๋ฅผ ์ ์ ๋ฆฌํด๋์
์ ๋งํฌ๋ฅผ ๊ฑธ์ด๋๋ค.
JSON.stringify()
๋ ์ํ์ฐธ์กฐ๋ฅผ ๋ฐ๊ฒฌํ๋ฉด TypeError๋ฅผ ๋ฐ์์ํจ๋ค.
var obj = { a: 1 };
obj.child = obj;
console.log( JSON.stringify( obj ) ); // TypeError: Converting circular structure to JSON
var cache = [];
JSON.stringify(obj, function(key, value) {
if (typeof value === 'object' && value !== null) {
if (cache.indexOf(value) !== -1) {
// Circular reference found, discard key
return;
}
// Store value in our collection
cache.push(value);
}
return value;
});
cache = null; // Enable garbage collection
const getCircularReplacer = () => {
const seen = new WeakSet();
return (key, value) => {
if (typeof value === "object" && value !== null) {
if (seen.has(value)) {
return;
}
seen.add(value);
}
return value;
};
};
JSON.stringify(obj, getCircularReplacer());
ํ๋ฉด ์ด๊ธฐ ๋ ๋๋ง ์ดํ ๋ค์ ๊ฐ์ ํ๋ฉด์ผ๋ก ๋์์ฌ ๊ฒฝ์ฐ, ํด๋น ํ๋ฉด์ ๋ฐ์ดํฐ๊ฐ ์ด๊ธฐ ๋ฐ์ดํฐ๋ก ์ ์ง๋จ.
react-naviagtion
๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ navigate()
ํจ์ ์ฌ์ฉ ์ route๊ฐ ์์ผ๋ฉด ํด๋น route๋ก ์ด๋ํ๊ณ ์์ผ๋ฉด ์คํ์ ๋ฃ๋๋ค. ์ฆ, ์ด๊ธฐ route๊ฐ ์คํ์ ์กด์ฌํ๋ฉด ๊ทธ route๋ก ์ด๋ํ๊ธฐ ๋๋ฌธ์ ์ด๊ธฐ ๋ฐ์ดํฐ๊ฐ ์ ์ง๋๋ค.
์๋ก๊ณ ์นจ์ด ๊ทธ ํ๋ฉด์ผ๋ก ๋์ด๊ฐ ๋๋ง๋ค ํ์ํ๊ธฐ ๋๋ฌธ์ ํด๊ฒฐ์ด ํ์.
์ฒ์์๋ ๋ฌด์กฐ๊ฑด ์คํ์ route๋ฅผ ๋ฃ๋ push()
ํจ์๋ฅผ ์ฌ์ฉํ์์ง๋ง ์ฑ๋ฅ์ ๋ฌธ์ ๊ฐ ์์๊ณ StackNavigator
๋ฅผ ์๋ก ๊ตฌ์ฑํ๋๋ก ๊ตฌํํ์๋ค.
StackNavigator
๋ฅผ ์๋ก ๊ตฌ์ฑํ๊ธฐ ์ํด NavigationActions
๋ฅผ ๊ฐ์ ธ์จ ๋ค, StackNavigator
๋ฅผ reset()
ํจ์๋ฅผ ์ด์ฉํ์ฌ ์๋ก ๊ตฌ์ฑํ๋ค.
import { NavigationActions, StackActions } from 'react-navigation';
changeScreen( screenName ){
const resetAction = StackActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName : screenName })]
});
navigation.dispatch(resetAction);
}
FormData
๋ฅผ Server์ api ๊ท๊ฒฉ์ ๋ง์ถ๊ธฐ ์ํด์ ๋น ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด์ผํ ๊ฒฝ์ฐ๊ฐ ์๋๋ฐ ์ด ๋, ๋งค๋ฒ ๋น ๊ฐ์ฒด์ NPE์ฒดํฌ๋ฅผ ์งํํด์ค์ผํ๋ ๋ถํธํจ์ด ์กด์ฌ.
var MYAPP = MYAPP || {};
MYAPP.namespace = function(ns_string) {
var parts = ns_string.split('.'),
parent = MYAPP,
i;
if (parts[0] === "MYAPP") {
parts = parts.slice(1);
}
for (i = 0; i < parts.length; i += 1) {
if (typeof parent[parts[i]] === "undefined") {
parent[parts[i]] = {};
}
// ์ฐธ์กฐ๋ฅผ ๋ณ๊ฒฝํ๋ค. ( parent -> MYAPP ์์ parent -> MYAPP.parts[i]๋ก ๋ณ๊ฒฝ)
parent = parent[parts[i]];
}
return parent;
}
MYAPP.namespace("MYAPP.modules.module2");
var module2 = MYAPP.namespace("MYAPP.modules.module2");
module2 === MYAPP.modules.module2; // true
์ฌ์ ๋ฒํผ(โถ)์ ํด๋ฆญํ์ ๋, Time Slider ์ ๋๋ฉ์ด์
์ด ์คํ๋๋ฉฐ ํด๋น ์๊ฐ๋์ ๋ง๋ ๋ฐ์ดํฐ๋ฅผ ๋ ๋๋งํ๋ ๊ธฐ๋ฅ์ ๊ตฌํํ๋ ์ค ๋ฐ๊ฒฌ.
๊ธฐ์กด์๋ setInterval
์ ์ด์ฉํด ํด๋น ์๊ฐ๋์ ๋ฐ์ดํฐ ์์ฒญ ํ ๋ ๋๋ง.
๋ธ๋ผ์ฐ์ ์ฐฝ์ ์ต์ํํ์ ๋๋ Time Slider๊ฐ ๋์ํ๋ค์. ๋ธ๋ผ์ฐ์ ์ฐฝ์ด ์ผ์ ธ ์์ ๊ฒฝ์ฐ์๋ง Time Slider๊ฐ ๋์ํ๋๋ก ํด์ฃผ์ธ์...
setInterval
์ ๋ธ๋ผ์ฐ์ ์ ์ต์ ํ ์ํ์ ๊ด๊ณ ์์ด clearInterval
์ ํด์ฃผ๊ธฐ ์ ์๋ ๊ณ์ํด์ ๋์ํจ.
์ ๋๋ฉ์ด์
๊ตฌํ์ผ๋ก ๋ง์ด ์ฐ์ด๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ธ velocity.js
์์ ๋ด๋ถ์ ์ผ๋ก rAF
์ ์ด์ฉํ์ฌ ์ ๋๋ฉ์ด์
์ ๊ตฌํํ๋ค๋ ๊ฒ.
rAF(requestAnimationFrame)
๋ ๋ธ๋ผ์ฐ์ ์ ์ต์ ํ ์ํ๋ฅผ ๊ณ ๋ คํ์ฌ ์ ๋๋ฉ์ด์
์ ์คํ.
๋ธ๋ผ์ฐ์ ํ๋ฉด์ ์ถ์๋ ํ๋ฉด ์ ํ์ ๋ฐ๋ฅธ ์ ๋๋ฉ์ด์
์ ํ๋ ์ ๋๋ฝ์ ๋ง์ ์ ์๋๋ก ๊ตฌํ.
External Component๋ฅผ styled-componets
๋ฅผ ์ด์ฉํด์ styling์ ํ๋ ๊ณผ์ ์์ ๋๊ธด property๋ฅผ Styled Component์์ ๊ทธ๋๋ก ๋ฐ์์ผ๋ค.
๊ทธ ๊ฒฐ๊ณผ, ๋ค์๊ณผ ๊ฐ์ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
Warning: React does not recognize the '[ํ๋กํผํฐ ๋ช
]' prop on a DOM element.
const Input = styled(({ isDirty, ...rest }) => (
<StyledInput {...rest} />
))`
${({ isDirty }) =>
isDirty &&
css`
border: 1px solid rgba(48, 61, 220, 0.25);
background: rgba(142, 172, 255, 0.07);
`};
`;
React Native๋ฅผ ์ด์ฉํ์ฌ ์ฑ ๊ฐ๋ฐ์ ์งํํ๋ฉฐ push ์๋ฆผ ์์ ๊ธฐ๋ฅ์ด ํ์.
react-native-firebase
๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ด์ฉํ์ฌ ๊ฐ๋ฐ์ ์งํํ๋ ์ค background ์ํ์์๋ push ์์ ์ด ๋ฌด๋ฆฌ ์์ด ๋๋ foreground ์ํ์์๋ push ์์ ์ด ์๋๋ ์ํฉ์ด ์ฌํ.
react-native-image-picker
๋ฅผ ์จ์ ์ด๋ฏธ์ง ํ์ผ์ ์
๋ก๋ํ๋ ๊ณผ์ ์์ ์๋ฒ ์ชฝ์์ ์ด๋ฏธ์ง ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ง ๋ชปํ๋ ์ํฉ์ด ์ฌํ๋จ.
Front์์๋ FormData
์ ๋ฐ์ดํฐ๋ฅผ append์์ผ ์๋ฒ๋ก ๋ฐ์ดํฐ๋ฅผ ์ ์กํ๊ณ ์์๋๋ฐ ์๋ฒ ์ชฝ์์๋ json
๋ฐ์ดํฐ๊ฐ ๋์ด๊ฐ ๊ฒ์ผ๋ก ์๊ฐํ๊ณ @RequestBody
๋ฅผ ์ด์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ๋ฐ๊ณ ์์์.
๊ฐ๋จํ ๋ฌธ์ ์๋๋ฐ ์ข ๋ ๊ผผ๊ผผํ์ง ๋ชปํด ๋ฐ์.
์๋ฒ์ชฝ @RequestBody
๋ฅผ @RequestPart
๋ก ์์ .
//Server
@RequestPart(value = "addLogoFileIdList", required = false) MultipartFile addLogoFileIdList)
//Front
const formData = new FormData();
this.state.fileName && formData.append('addLogoFileIdList', {
name: this.state.fileName,
type: this.state.imageType,
uri: Platform.OS === "android" ? this.state.imageUri : this.state.imageUri.uri.replace("file://", ""),
data: this.state.data
});
const data = {
storeName: this.state.storeName,
storePhone: this.state.storePhone,
storeAddress: this.state.storeAddress,
};
Object.keys(data).forEach(key => {
formData.append(key, data[key]);
});
var j = [...new Set([1, 2, 3, 3])]
>> [1, 2, 3]
filter falsy values (0, undefined, null, false, etc.) out of an array.
myArray
.map(item => {
// ...
})
// Get rid of bad values
.filter(Boolean);
Create empty object __proto__
and the usual hasOwnProperty and other object methods are not exist.
let dict = Object.create(null);
// dict.__proto__ === "undefined"
// No object properties exist until you add them
const person = { name: 'David Walsh', gender: 'Male' };
const tools = { computer: 'Mac', editor: 'Atom' };
const attributes = { handsomeness: 'Extreme', hair: 'Brown', eyes: 'Blue' };
const summary = {...person, ...tools, ...attributes};
/*
Object {
"computer": "Mac",
"editor": "Atom",
"eyes": "Blue",
"gender": "Male",
"hair": "Brown",
"handsomeness": "Extreme",
"name": "David Walsh",
}
*/
const isRequired = () => { throw new Error('param is required'); };
const hello = (name = isRequired()) => { console.log(`hello ${name}`) };
// This will throw an error because no name is provided
hello();
// This will also throw an error
hello(undefined);
// These are good!
hello(null);
hello('David');
const obj = { x: 1 };
// Grabs obj.x as { x }
const { x } = obj;
// Grabs obj.x as { otherName }
const { x: otherName } = obj;
// Assuming "?post=1234&action=edit"
var urlParams = new URLSearchParams(window.location.search);
console.log(urlParams.has('post')); // true
console.log(urlParams.get('action')); // "edit"
console.log(urlParams.getAll('action')); // ["edit"]
console.log(urlParams.toString()); // "?post=1234&action=edit"
console.log(urlParams.append('active', '1')); // "?post=1234&action=edit&active=1"
ํ์ ํํด ๊ธฐ๋ฅ ๊ตฌํ ์ค ํ์ธ๊ณผ ์ทจ์ ๋ฒํผ์ ๊ฐ์ง ๋ชจ๋ฌ๊ณผ ๊ฒฐ๊ณผ ๋ฉ์ธ์ง๋ฅผ ํฌํจํ๋ ํ์ธ ๋ฒํผ๋ง ๊ฐ์ง Dialog์ปดํฌ๋ํธ๋ฅผ ๊ฐ๊ฐ ๊ตฌํ.
ํ์ธ ๋ฒํผ์ ๋๋ฌ ํ์ ํํด ์์ฒญ ์ android์์๋ ์ ๋์ํ๋ ios์์๋ ๊ฒฐ๊ณผ ๋ฉ์ธ์ง๋ฅผ ํฌํจํ ๋ชจ๋ฌ ์ฐฝ์ด ๋จ์ง ์๋ ๋ฒ๊ทธ๊ฐ ๋ฐ์.
<RCTModalHostViewController... which is already presenting
iOS์์๋ Dialog์ปดํฌ๋ํธ๋ฅผ 2๊ฐ ์ด์ ๋์์ ๋์ธ ์ ์๋ค.
๊ทธ๋ ๋ค๋ฉด, ๊ธฐ์กด Dialog์ ๋๊ณ ๋์ฐ๋ฉด ๋์ง ์์๊น?
๋ถ๋ช
ํ setState
์ callback์์ ๊ทธ ๋ค์ ๋ฒ Dialog์ ๋์ ๋๋ฐ ๋์ํ์ง ์์.(๊ฐ์ ์๋ฌ ๋ฐ์)
nested component
๋ฐฉ์์ผ๋ก ๊ตฌํ์ด ๊ฐ๋ฅํ๋ค๊ณ ํ๋ค.
๋ค๋ง, ๋ด๊ฐ ์ฌ์ฉํ ๊ฒ์ react-native-dialog
๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ nested component
๋ก ์ปค์คํฐ ๋ง์ด์งํ์ง ์๊ณ ์กฐ๊ฑด์ ๋ฐ๋ผ ๋ค๋ฅธ props๋ฅผ ๋๊ฒจ Dialog๊ฐ ํ๋๋ง ์๋๋ก ์ ์งํ๋ค.
const useDidMountEffect = (func, deps) => {
const didMount = useRef(false);
useEffect(() => {
if (didMount.current) func();
else didMount.current = true;
}, deps);
}
React Native
์ Spring Framework
๋ฅผ ์ด์ฉํ ํ๋ก์ ํธ ์ค Front์์ ์ธํ
ํ ์ธ์ด ์ค์ ์ ์๋ฒ์์ ๋ค์ ์ธํ
ํ์ ๋ ์๋ฒ ์ชฝ์์๋ ์ ๋๋ก ๋จน์ง ์์ ์๋ฒ๋ก ๋ถํฐ ๋ฐ์ ๋ฐ์ดํฐ๊ฐ default Locale๋ก ๋งคํ๋ ๊ฐ์ด ๋์ด์ด.
Front์์ ์ธ์ด ์ค์ ๊ธฐ๋ฅ์ ์คํํ์ ๋ ์๋ฒ์๋ ํด๋น ๊ฐ์ผ๋ก Locale ์ธํ
์ ํด์ฃผ๊ณ ์์.
๋๋ฒ๊น
๊ฒฐ๊ณผ, BaseLocaleAspect
์์ ๋ฐ๋ HttpServletRequest
์ locale ๊ฐ์ด ๊ฐํ์ ์ผ๋ก ์ํ๋ ๊ฐ์ผ๋ก ์ ๋๋ก ์ธํ
๋์ง ์๊ณ ์์ฒญ๋๋ ๊ฒ์ ํ์ธ.
BaseLocaleAspect
์์ execution
์ ์์ ํ์ฌ ๋ชจ๋ rest ์ปจํธ๋กค๋ฌ๋ก ์ด๋ ์ ์ ํ๋๋ก ์์ >
@Before("execution(* com.blockware.keeper.app.server.*.rest.*.*(..))")
๊ฒฐ๊ณผ์ ์ผ๋ก 1๋ฒ ๋ฐฉ๋ฒ์ด ๋ง์๊ณ ๊ฐ๋ฐ ์๋ฒ ์ชฝ ์ธ์ด ์ค์ ๊ด๋ จ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์
๋ฐ์ดํธ ํ๋ ๊ณผ์ ์ ๊ธฐ์กด์ ์ฌ์ฉํ๋ jar
ํ์ผ์ด ๊ฐ๋ฐ ์๋ฒ ์ชฝ์ ๋จ์ ์์ด ๋ฌ๋ ์ค๋ฅ์์.
๊ธฐ์กด jar
ํ์ผ์ ์ญ์ ํ๋ ์ ๋์ํจ.
Spring
์์ ์ ์ ๋ฆฌ์์ค๋ฅผ ๋ชป์ฐพ์์ค๋ ๋ฒ๊ทธ.
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
์์ ๊ฐ์ด url-pattern ์ด /
๋ก ๋์ด ์์ ๊ฒฝ์ฐ ๋ชจ๋ URI ์ ๋ํด Servlet Mapping์ ํ์ธํ๊ธฐ ๋๋ฌธ์ ์ ์ ๋ฆฌ์์ค๋ค์ ๋งคํ์ํฌ์ ์์ผ๋ฏ๋ก 404๊ฐ ๋ฐ์.
tomcat
Module Path๋ฅผ /
๋ก ์ธํ
ํ reboot.
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.css</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.jpg</url-pattern>
</servlet-mapping>
์์ ๊ฐ์ด ์ค์ ํด ๋์ผ๋ฉด dispatcherServlet
์ default Servlet Mapping
์ ์ ์ธํ ๋๋จธ์ง ์์ฒญ์ ๋ํด์๋ง Controller
์ ๋งคํ ์ํจ๋ค.
ํ์ง๋ง ์ด์๊ฐ์ด ์ ์ ๋งค์๋๋ค์ ๋ํด ์ผ์ผ์ด ์ด๊ฑฐ ํ๋ ๊ฒ ๋ณด๋ค๋ Spring 3.x ์ด์๋ถํฐ ์ง์๋๋ <mvc:default-servlet-handler/>
๋ก ๋์ฒดํด์ ์ฌ์ฉํ๋ฉด ๋๋ค.
๋ ๋๋ง ์ต์ ํ๋ฅผ ์งํํ๋ ์ค, React.memo
๋ฅผ ์ฌ์ฉํ์ฌ ๋ถํ์ํ ๋ ๋๋ง์ ์ค์ด๋ ์ค, ๊ธฐ์กด ๊ธฐ๋ฅ์ด ์ ์์ ์ผ๋ก ๋์ํ์ง ์๋ ๋ฒ๊ทธ๊ฐ ๋ฐ์.
React.memo
์ ๋๋ฒ ์งธ ์ธ์๋ก๋ propsAreEqual
ํจ์๋ฅผ ๋๊ธธ ์ ์๋๋ฐ ๊ธฐ์กด์ state
๊ฐ์ ๋ณ๊ฒฝํ๋ ํจ์์์ ํจ์ํ ์
๋ฐ์ดํธ๋ฅผ ์ฌ์ฉํ์ง ์์ ํด๋น ํจ์๊ฐ ์ต์ state
๊ฐ์ ์ฐธ์กฐํ์ง ๋ชปํด์ ๋ฐ์.
export default React.memo(
UserDetail,
(prevProps, nextProps) => prevProps.userData === nextProps.userData
);
์์ ๊ฐ์ด ๋๋ฒ์งธ ์ธ์๋ก props
๋ฅผ ๋น๊ตํ ์ ์๊ณ true๋ฅผ ๋ฐํํ๋ฉด ๋ฆฌ๋ ๋๋ง์ ํ์ง ์๋๋ค.
๋จ, ์์ ๋ก์ง์ด ์ ๋๋ก ๋์ํ๋ ค๋ฉด ํด๋น ์ํ ๊ฐ์ ๋ณ๊ฒฝํ๋ ํจ์์์ ํจ์ํ ์
๋ฐ์ดํธ๋ฅผ ํตํด ๋ณ๊ฒฝํด์ฃผ์ด์ผ ํ๋ค.
// Bad
setUserData({
...userData,
[e.target.name]: [e.target.value]
})
// Good
setUserData(prevState => {
const { name, value } = e.target;
return {
...prevState,
name: value
}
})
TextInput
์ ์ฌ์ฉํ์ฌ form์ ๋ง๋ค๊ณ ํค๋ณด๋๊ฐ ์ฌ๋ผ์์ ๋ ์ตํ๋จ์ ๋ฒํผ์ด ํค๋ณด๋ ์๋ก ํจ๊ป ์ฌ๋ผ์ด.
ํค๋ณด๋ค๊ฐ input ์ฐฝ์ ๋ฎ์ด๋ฒ๋ฆฌ๋ฉด์ ๋ง์ง๋ง ํญ๋ชฉ์ ์
๋ ฅํ๋ ค๋ฉด ํค๋ณด๋๋ฅผ ๋ด๋ฆฌ๊ณ ๋ค์ ํด๋ฆญํด์ผํ๋ ๋ถํธํจ์ด ์์์.
windowsSoftInputMode
๋ฅผ adjustPan
์์ฑ์ผ๋ก ์ค์ ํ๋ฉด ํค๋ณด๋๊ฐ ์ตํ๋จ์ ๋ฒํผ์ ๊ฐ๋ฆฌ๊ณ ์ฌ๋ผ์ด.
KeyboardAwareScrollView
์ปดํฌ๋ํธ๋ก ํค๋ณด๋๊ฐ ์ฌ๋ผ์์ ๋ ์คํฌ๋กค์ ์ฃผ๊ณ ์ถ์ ์์ญ์ ๊ฐ์ธ๋ฉด ํค๋ณด๋๊ฐ ์ฌ๋ผ์๋ ๋ง์ง๋ง ํญ๋ชฉ๋ ์คํฌ๋กค์ ๋ด๋ ค ์
๋ ฅ ๊ฐ๋ฅํจ.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.