파이썬/장고 웹서비스 개발 완벽 가이드 with 리액트 강의를 듣고 정리한 글입니다.
리액트를 공부하기위해 필요한 ES6+ 문법을 정리해본다.
var 대신에 const 혹은 let을 사용 → block scope
var div;
var container = document.getElementsByTagName('body')[0];
for(let i=0; i<5; i++) {
div = document.createElement('div’);
div.onclick = function() {
alert("clicked : #" + i); // var/let 사용 여부에 따라 alert 메시지가 달라집니다.
};
div.innerHTML = "#" + i;
container.appendChild(div);
}
⇒ let으로 작성할 경우, i가 새로 선언되지만, var로 작성할 경우에는 하나의 변수를 공유하므로 실제 클릭 이벤트 시 무조건 5로 출력된다.
최근 트렌드는 const위주로, 간간히 let을 사용한다.
// tom1 / tom2는 동일
let tom1 = {
name: "Tom",
age: 10,
region: "Seoul"
};
let tom2 = {
"name": "Tom",
"age": 10,
"region": "Seoul"
};
tom = {
"name": "Tom",
"age": 10,
"region": "Seoul"
}
const tom2 = {
"name": "Tom",
"age": 10,
"region": "Seoul",
["score" + "1"]: 100,
// key는 "scope1" (Array가 아님)
};
const key1 = "location";
const tom = {
name: "Tom",
[key1]: "Seoul"
};
tom = {
"name": "Tom",
"age": 10,
"region": "Seoul",
"score" + "1": 100,
}
key1 = "location"
tom = {
"name": "Tom",
key1: "Seoul"
}
let name = "Tom";
let age = 10;
let tom1 = { // 이와 같이 클래스 없이도 객체를 만들 수 있다. -> tom1.print()
name: name,
age: age,
print: function() {
console.log(`name: ${this.name}, age: ${this.age}`);
}
};
let tom2 = { // 위와 동일한 객체 -> tom2.print()
name,
age,
print() {
console.log(`name: ${this.name}, age: ${this.age}`);
}
};
JS는 Object/Array에 대해서는 대입시에 얕은 복사 (Shallow Copy)
const obj1 = { value1: 10 };
const obj2 = obj1; // 얕은 복사
const obj3 = JSON.parse(JSON.stringify(obj1)) // 깊은 복사
obj1.value1 += 1;
console.log(`obj1:`, obj1);
console.log(`obj2:`, obj2);
console.log(`obj3:`, obj3);
> node t.js
obj1: { value1: 11 }
obj2: { value1: 11 }
obj3: { value1: 10 }
⇒ obj2.value1도 변경되었음.
Multi-line string
String Interpolation
`string text ${expression}
string text`
"""string text
string text"""
f"""string text {expression}
string text"""
리액트에서 자주 쓰는 문법
let [name] = ["Tom", 10, "Seoul"];
let [,age,] = ["Tom", 10, "Seoul"];
let [name, age, region, height] = ["Tom", 10, "Seoul"]
// height = undefined 할당 (파이썬에서는 ValueError 예외)
let [name, age, region, height=150] = ["Tom", 10, "Seoul"]
// height = 디폴트값 할당 (150)
function get_default_height() {
console.log("get_default_height() 호출")
return 150;
}
let [name, age, region, height=get_default_height()] = ["Tom", 10, "Seoul"]
// height에 실제 디폴트값 할당이 필요할때, get_default_height()가 호출됩니다.
리액트에서 자주 쓰는 문법
const tom = {
name: "Tom",
age: 10,
region: "Seoul"
};
// 아래와 같이 할 수 있지만,
const age = tom.age;
const name = tom.name;
// 다음과 같이 객체에서 필요한 값들만 뽑아냅니다. (리액트에서 정말 자주 쓰는 문법)
const {age, name, height}= tom; // height는 undefined
const print_person1 = (person) => {
console.log(person.name);
};
const print_person2 = ({ name }) => {
console.log(name);
};
print_person1(tom);
print_person2(tom);
const people = [
{ name: 'Tom', age: 10, region: 'Seoul' },
{ name: 'Steve', age: 12, region: 'Pusan' }
];
for (const person of people) {
console.log(person.name, person.age);
}
for (const {name, age} of people) {
console.log(name, age);
}
const person = {
name: 'Tom',
age: 10,
region: {
country: '서울',
postcode: '06222',
}
};
const { name, region: { postcode }} = person; // region은 할당X
console.log(name, postcode);
리액트에서는 수많은 값들을 불변객체로서 처리합니다 → 이때 전개연산자를 많이 쓰며, 구조가 복잡할 경우 immer 라이브러리를 쓰는 것이 코드 가독성에 도움이 됩니다.
let [name, ...rest] = ["Tom", 10, "Seoul"];
let names = ["Steve", "John"];
let students = ["Tom", ...names, ...names]; // ["tom", "steve", "john", "steve", "john"]
let printArgs = (...args) => {
console.log(args);
}
let tom = {
name: "Tom",
age: 10,
region: "Seoul"
};
let steve = {
...tom,
name: "Steve" // 속성 명이 중복일 경우 마지막 값이 남습니다.
}; // {name: "Steve", age: 10, region: "Seoul"}
모든 타입의 값들을 디폴트 파라미터로 지정할 수 있습니다.
⇒ 파이썬에서는 Immutable값들만 디폴트 파라미터로 지정 가능
function hello(name="Tom", age=10) {
console.log(`나는 ${name}. ${age}살이야.`)
}
const get_default_age = () => 10
function hello(name="Tom", age=get_default_age()) {
console.log(`나는 ${name}. ${age}살이야.`);
}
console.log(hello("steve"));
function get_default_height() {
console.log("get_default_height() 호출");
return 150;
}
function say_hello(name, height=get_default_height()) {
console.log(`name: ${name}, height: ${height}`);
}
say_hello("Tom", 160);
say_hello("John");
say_hello("Steve", 170);
say_hello("Suji");
> node t.js
name: Tom, height: 160
get_default_height() 호출
name: John, height: 150
name: Steve, height: 170
get_default_height() 호출
name: Suji, height: 150
def get_default_height():
print("get_default_height() 호출")
return 150
def say_hello(name, height=get_default_height()):
print(f"name: {name}, height: {height}")
say_hello("Tom", 160)
say_hello("John")
say_hello("Steve", 170)
say_hello("Suji")
> python t.py
get_default_height() 호출
name: Tom, height: 160
name: John, height: 150
name: Steve, height: 170
name: Suji, height: 150
객체 비구조화를 활용
function print_person1(name, age, region) {
console.log('1>', name, age, region)
}
print_person1('Tom', 10, 'Seoul');
function print_person2({ name, age, region }) {
console.log('2>', name, age, region)
}
print_person2({ name: 'Tom', age: 10, region: 'Seoul' });
def print_person(name, age, region):
print(name, age, region)
print_person('Tom', 10, 'Seoul')
print_person(name='Tom', age=10, region='Seoul')
# out -> ('Tom', 10, 'Seoul')
print_person(**{'name': 'tom', 'age': 10, 'region': 'seoul'})
# out -> ('tom', 10, 'seoul')
return을 사용하지 않아도, 계산된 값을 반환. 인자가 1개일 경우, 소괄호 생략 가능.
var hello1 = function(name, age) {
return `안녕. 나는 ${name}. ${age}이야.`;
};
let hello2 = (name, age) => `안녕. 나는 ${name}. ${age}이야.`; // 함수를 중괄호로 감싸지 않으면, return문을 쓰지 않아도 반환값으로 사용됩니다.
let hello3 = (name, age) => {
return `안녕. 나는 ${name}. ${age}이야.`;
}
this와 arguments를 바인딩하지 않습니다.
var tom = {
name: "Tom",
print1: function() {
console.log(`[print1-1] name : ${this.name}`);
(function() {
console.log(`[print1-2] name : ${this.name}`); // 본래 새로운 function을 생성하면 this도 바뀐다.
})();
},
print2: function() {
console.log(`[print2-1] name : ${this.name}`);
var me = this;
(function() {
console.log(`[print2-2] name : ${me.name}`); // 새로운 function을 생성하면 this도 바뀌기 때문에, me를 통해 this를 바인딩하였다. (꼼수!)
})();
},
print3: function() {
console.log(`[print3-1] name : ${this.name}`);
(() => {
console.log(`[print3-2] name : ${this.name}`); // this가 변경되지 않았다!
})();
}
};
tom.print1();
tom.print2();
tom.print3();
실행 결과
[print1-1] name : Tom
[print1-2] name : undefined
[print2-1] name : Tom
[print2-2] name : Tom
[print3-1] name : Tom
[print3-2] name : Tom
const mysum1 = (x, y) => x + y; // 더한 값 반환
const mysum2 = (x, y) => ({x, y}); // 오브젝트 반환
const mysum3 = (x, y) => ({x: x, y: y}); // mysum2와 같음
const mysum4 = (x, y) => {
return {x: x, y: y};
}
const mysum5 = function(x, y) {
return {x: x, y: y};
};
function mysum6(x, y) {
return {x: x, y: y};
}
비동기 프로그래밍을 위해 콜백(callback)을 많이 사용
fs.readdir(source, function (err, files) {
if (err) {
console.log('Error finding files: ' + err)
} else {
files.forEach(function (filename, fileIndex) {
console.log(filename)
gm(source + filename).size(function (err, values) {
if (err) {
console.log('Error identifying file size: ' + err)
} else {
console.log(filename + ' : ' + values)
aspect = (values.width / values.height)
widths.forEach(function (width, widthIndex) {
height = Math.round(width / aspect)
console.log('resizing ' + filename + 'to ' + height + 'x' + height)
this.resize(width, height).write(dest + 'w' + width + '_' + filename, function(err) {
if (err) console.log('Error writing file: ' + err)
})
}.bind(this))
}
})
})
}
})
const fs = require('fs');
fs.readdir('.', function (err, files) {
if (err) {
console.log('Error finding files: ' + err)
}
else {
console.log(files);
}
});
// 위 fs.readdir이 끝나기 전에 수행
console.log("ENDED");
const fs = require('fs');
const fsPromises = fs.promises;
fsPromises.readdir('.')
.then(files => { // 정상 처리
console.log(files);
})
.catch(err => console.error(err)); // 에러 상황
// 위 fsPromises.readdir이 끝나기 전에 수행
console.log("ENDED");
비동기 문법임에도 동기적으로 코드를 해석할 수 있다.
const fs = require('fs');
const fsPromises = fs.promises;
async function fn() {
try {
let files = await fsPromises.readdir('.');
console.log(files);
}
catch(err) {
console.error(err);
}
}
fn(); // async 함수 이기에, 완료 전에 다음 로직이 동작
console.log("ENDED");
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.print = function() { // 멤버함수 정의
console.log(this.name + ", " + this.age);
}
var tom = new Person("Tom", 10);
tom.print();
class Person {
constructor(name, age) { // 생성자
this.name = name;
this.age = age;
}
print() { // 멤버함수
console.log(this.name + ", " + this.age);
}
}
const tom = new Person("Tom", 10);
tom.print();
class Developer extends Person {
constructor(name, age, field) {
super(name, age);
this.field = field;
}
print() {
super.print();
console.log(`field : ${this.field}`);
}
}
⇒ 문법이 다를 뿐, 여전히 prototype을 사용합니다.
추가:
my_module.js : 모듈 정의
const name = "tom";
const age = 10;
module.exports = {
name,
age,
region: "Seoul"
};
in_nodejs.js : 모듈 불러오기
const my_module = require("./my_module");
const { name } = require("./my_module");
console.log(my_module);
console.log(name);
my_module_es6.js : 모듈 정의
const name = "tom";
const age = 10;
export default {
name,
age,
region: "Seoul"
};
export {
name,
};
in_react.js : 모듈 불러오기
import my_module from "./my_module"; // export default를 참조
import { name } from "./my_module"; // export를 참조
함수를 인자로 받거나 반환이 가능하고, 다른 함수를 조작하는 함수.
함수/클래스 역시 모두 객체
function base_10(fn) {
function wrap(x, y) {
return fn(x, y) + 10;
}
return wrap;
}
function mysum(a, b) {
return a + b;
}
mysum = base_10(mysum);
console.log(mysum(1, 2));
const base_10 = fn => (x, y) => fn(x, y) + 10;
let mysum = (a, b) => a + b;
mysum = base_10(mysum);
console.log(mysum(1, 2));
def base_10(fn):
def wrap(x, y):
return fn(x, y) + 10
return wrap
def mysum(x, y):
return x + y
mysum = base_10(mysum)
print(mysum(1, 2))