Today I Learned
- React 입문 강의 수강
- ES6 문법 특강 수강
React 입문 강의
React란?
React는 사용자 인터페이스를 구축하기 위한 선언적이고 효율적이며 유연한 JavaScript 라이브러리.
SPA를 전제로 하고 있으며, Virtual DOM을 활용하여 업데이트해야 하는 DOM요소를 찾아서 해당 부분만 업데이트하기 때문에, 리렌더링이 잦은 동적인 모던 웹에서 향상된 퍼포먼스를 낼 수 있다.
Virtual Dom (가상 돔)이란?
실제 DOM은 조작이 일어나면 바로 브라우저를 통해 여러가지 랜더링 단계를 거치게 된다. 하지만 메모리상에 그냥 값으로 존재하는 가상 돔은 변경이 일어나도 이것이 브라우저 렌더링과는 직접적으로 연결되어 있지 않다. 즉, 리액트가 가상 돔의 변화를 실제 DOM에 적용하지 않는 한 아무리 많은 조작이 가상 돔에 일어나도 브라우저의 렌더링을 일으키지 않는 것이다. 가상 DOM은 이러한 특징을 바탕으로 위에서 말한 for문으로 10개의 DOM 요소를 수정하는 것과 같은 변경 사항들을 한 번에 묶어서 실제 DOM에 반영을 한다. (이것이 👉batching)
첫 리액트 페이지 기념 ㅋㅋ
Component란?
컴포넌트란 화면을 구성하는 하나의 단위. 리액트 세계에서 말하는 컴포넌트는 함수이다. 컴포넌트를 만들 때 html을 return 하는 함수를 만들면 된다.
컴포넌트 기본 구조
const App=()=> {
// 자바스크립트 작성 영역
return (
<div>
{/*JSX 작성 영역*/}
</div>
);
};
주의할 점: 컴포넌트를 만들 때 첫 글자는 반드시 대문자로 만들어야 한다!
React에서 이벤트 처리하기
리액트 예민한 애였구나... 에러가 끝이 없네 ㅎㅎ 갑자기 리액트 네이티브의 악몽이 떠오른다🤮
버튼 클릭하면 alert창 띄우기. 처음에는 addEventListener로 시도했었는데 react에서는 문법이 다른 것인지 실패. 공식 문서를 보고 onClick으로 함수를 연결하는 방법으로 수정했다.
<button onClick={activateLasers}>
Activate Lasers
</button>
React의 이벤트 처리는 조금 다르다. 일단 onClick에 C를 대문자로 작성할 것. 그리고 함수 명을 중괄호 안에 넣어야 한다.
공식 문서 https://ko.reactjs.org/docs/handling-events.html
부모 컴포넌트와 자식 컴포넌트
function Child() {
return <div>나는 자식입니다.</div>;
}
function App() {
return <Child />;
}
export default App;
Child 컴포넌트가 App 컴포넌트 안에 들어가 있는 구조. 여기서 App를 부모 컴포넌트, Child를 자식 컴포넌트라고 부른다. 그리고 이렇게 함수로 만들어진 컴포넌트를 html 태그 사용하듯이 코드를 작성하는 방식을 JSX라고 부른다.
JSX 문법에 따른 주의할 점
1. 태그는 꼭 닫아주기
// input 태그를 닫지 않았을 경우
function App() {
return (
<div className="App">
<input type='text'>
</div>
);
}
SyntaxError: Unterminated JSX contents.
2. 컴포넌트에서는 한 개의 엘리먼트만 반환 가능
// 엘리먼트를 2개 반환하려고 할 경우
return (
<p>안녕하세요! 리액트 반입니다 :)</p>
<div className="App">
<input type='text'/>
</div>
);
SyntaxError: Adjacent JSX elements must be wrapped in an enclosing tag.
3. JSX에서 javascript 값을 가져오려면 중괄호 사용하기
const cat_name = 'perl';
return (
<div>
hello {cat_name}!
</div>
);
값을 가져올 때뿐만 아니라 map, 삼항연산자 등 자바스크립트 문법을 JSX 안에 쓸 때도 {}를 이용할 수 있다.
function App() {
const number = 1;
return (
<div className="App">
<p>{number > 10 ? number+'은 10보다 크다': number+'은 10보다 작다'}</p>
</div>
);
}
4. class 대신 className
<div className="App">
5. 인라인으로 style 주는 방법
// 중괄호를 두 번 쓰는 이유? 딕셔너리도 자바스크립트니까요!
// 이렇게 쓰거나,
<p style={{color: 'orange', fontSize: '20px'}}>orange</p>
//혹은 스타일 딕셔너리를 변수로 만들고 쓸 수 있어요!
function App() {
const styles = {
color: 'orange',
fontSize: '20px'
};
return (
<div className="App">
<p style={styles}>orange</p>
</div>
);
}
Props란?
부모 컴포넌트로부터 받아온 데이터!
import React from "react";
function Child(props) {
return <div>{props.grandFatherName}</div>;
}
function Mother(props) {
return <Child grandFatherName={props.grandFatherName} />;
}
function GrandFather() {
const name = "이범규";
return <Mother grandFatherName={name} />;
}
function App() {
return <GrandFather />;
}
export default App;
grandFatherName이라는 이름으로 name 값을 Mother 컴포넌트에게 전달해주었다. 이 과정을 “Props 로 정보를 전달했다” 라고 표현한다.
Mother가 Child에 props로 전달한 정보를 콘솔로 찍어 보면 객체 형태로 나타나는 것을 볼 수 있다. props란 결국 부모 컴포넌트가 자식에게 넘겨준 데이터들의 묶음이라고 볼 수 있다!
주의할 점: 오직 부모 컴포넌트 -> 자식 컴포넌트로만 props를 전달할 수 있다.
부모 컴포넌트로 정보를 전달하는 children
import React from "react";
function User(props) {
return <div>{props.children}</div>;
}
function App() {
return <User>안녕하세요</User>;
}
export default App;
자식 컴포넌트에서 정보를 받는 방법 -> props.children
children은 Layout 컴포넌트(header)를 만들 때 자주 사용한다.
구조분해 할당과 Props
구조 분해 할당으로 props 내부값 추출하기
function Todo({ title, body, isDone, id }){
return <div>{title}</div>
}
props는 object literal 형태의 데이터이기 때문에 구조 분해 할당을 이용할 수 있다.
defaultProps란?
defaultProps란 부모 컴포넌트에서 props를 보내주지 않아도 설정될 초기 값이다. (임시로 사용)
// 방법1
function Child({ name }){
return <div>내 이름은 {name} 입니다. </div>
}
// 이렇게 설정합니다.
Child.defaultProps={
name: '기본 이름'
}
// 방법2
// 구조 분해 할당 문법 사용
function Child({ name = '기본이름' }){
return <div>내 이름은 {name} 입니다. </div>
}
object literal(객체 리터럴) 이해하기
프로퍼티(property) : 객체의 상태를 나타내는 값
메서드(method) : 프로퍼티를 참조하고 조작할 수 있는 동작
프로퍼티에 접근하는 방법은 2가지
✔ 마침표 표기법(dot notation) : 마침표 프로퍼티 접근 연산자(.) 사용
✔ 대괄호 표기법(bracket notation) : 대괄호 프로퍼티 접근 연산자([ ]) 사용
출처 https://velog.io/@nxnaxx/Javascript-%EA%B0%9D%EC%B2%B4-%EB%A6%AC%ED%84%B0%EB%9F%B4
State란?
State란 컴포넌트 내부에서 보유 및 변경되는 데이터를 의미한다.
UseState로 데이터 생성하기
// const [ state, setState] = useState( 초기값 )
const [name, setName] = useState("김춘식");
setState로 데이터 변경하기
import React, { useState } from "react";
function Child(props) {
return (
<div>
<button
onClick={() => {
props.setName("박할아"); // 드디어 받은 setName을 실행합니다.
}}>
할아버지 이름 바꾸기
</button>
<div>{props.grandFatherName}</div>
</div>
);
}
function Mother(props) {
return (
<Child grandFatherName={props.grandFatherName} setName={props.setName} />
);
}
function GrandFather() {
const [name, setName] = useState("김할아");
return <Mother grandFatherName={name} setName={setName} />;
}
function App() {
return <GrandFather />;
}
export default App;
버튼을 눌렀을 때 이름이 바뀌도록 코드 작성하기
React Hooks란?
React 에서 기존에 사용하던 Class를 이용한 코드를 작성할 필요 없이 state와 여러 React 기능을 사용할 수 있도록 만든 라이브러리.
React에서 기본적으로 지원하는 Hooks 및 출처
useState + onClick Event
import React, { useState } from "react";
function App() {
const [name, setName] = useState('흰둥이')
function btnClick() {
setName('누렁이')
}
return (
<div>{name}
<button onClick={btnClick}>버튼</button>
</div>
);
}
export default App;
버튼을 누르면 이름이 흰둥이에서 누렁이로 변경됨
event.target이란 무엇인가?
예전에도 본 적이 있는데 event.target.value가 정확이 무엇인지 모르겠어서 콘솔에 찍어보기로 했다.
const App = () => {
const [value, setValue] = useState("");
const onChangeHandler = (event) => {
let value = event.target.value;
setValue(value);
}
return (
<div>
<input type="text" onChange={onChangeHandler} value={value}/>
</div>
);
};
input창에 onChange로 입력된 값을 가져오는 함수를 연결해 놨다.
console.log(event)의 결과. 뭔지 모르겠지만 InputEvent라는 단어가 보인다.
console.log(event.target)의 결과. event.target은 input 태그를 가리키고 input 창에 텍스트를 입력하자 value 값이 변하는 것을 볼 수 있다. 사용자가 input에 어떤 값을 입력하면, 그 값을 입력할 때마다(onChange될 때마다) value라는 state에 setValue해서 넣어주는 것이다.
리액트에서 데이터의 불변성이 중요한 이유
데이터 타입
- 기본형(원시) 데이터 : 값이 담긴 주소값을 바로 복제 (불변값)
- 참조형 데이터 : 값이 담긴 주소 값들로 이루어진 묶음을 가리키는 주소 값을 복제 (가변값) -> 객체, 배열, 함수 등
데이터를 수정했을 경우
기본형 데이터 (불변성O)
let number = 1;
let secondNumber = 1;
console.log(number === secondNumber) // true
만약에 기존에 1이던 number를 number = 2 라고 새로운 값을 할당하면 기존 메모리에 저장이 되어 있는 1이라는 값이 변하지 않고, 새로운 메모리 저장 공간에 2가 생기고 number라는 값을 새로운 메모리 공간에 저장된 2를 참조하게 된다. 그래서 secondNumber를 콘솔에 찍으면 여전히 1이라고 찍힌다. 데이터 변경 후에는 전과는 다르게 number와 secondNumber가 각각 다른 메모리 저장공간을 참조하고 있기 때문.
참조형 데이터 (불변성X)
let obj_1 = {name: ‘kim’}
let obj_2 = {name: ‘kim’}
console.log(obj_1 === obj2) //flase
객체는 불변성이 없기 때문에 obj_1.name = ‘park’ 이라고 새로운 값을 할당하면 기존 메모리 저장 공간에 있는 {name: ‘kim’} 이라는 값 자체를 {name : ‘park’} 으로 바꿔 버린다.
리액트에서 참조형 데이터(객체, 배열)의 불변성을 지켜주는 것이 중요한 이유
리액트에서는 화면을 리레더링 할 때 state의 변화를 확인하여 state가 변했으면 리렌더링 하고, state가 변하지 않았으면 리렌더링을 하지 않는다. state가 변했는지 확인하기 위해서 state의 변화 전후의 메모리 주소를 비교하는데 만약 리액트에서 참조형 데이터를 수정할 때 불변성을 지켜주지 않고 직접 수정을 가하면 값은 바뀌지만 메모리 주소는 변함이 없게 되는 것이다. 즉, 값은 바꿨지만 리액트는 state가 변했다고 인지하지 못하게 되기 때문에 리렌더링이 일어나지 않게 된다.
참조형 데이터의 불변성 지키기
function App() {
const [dogs, setDogs] = useState(["말티즈"]);
function onClickHandler() {
// spread operator(전개 연산자)를 이용해서 dogs를 복사합니다.
// 그리고 나서 항목을 추가합니다.
setDogs([...dogs, "시고르자브르종"]);
}
console.log(dogs);
return (
<div>
<button onClick={onClickHandler}>버튼</button>
</div>
);
}
배열을 setState 할 때 불변성을 지켜주기 위해, 직접 수정을 가하지 않고 전개 연산자(spread)를 사용해서 기존의 값을 복사하고 그 이후에 값을 수정하는 식으로 구현한다.
데이터 타입과 불변성-내가 작성했던 TIL 참고하기
https://divheer.tistory.com/55
Programmers 문제 풀기
n의 배수 고르기
function solution(n, numlist) {
let result = [];
for (const num of numlist) {
if (num % n == 0) {
result.push(num)
};
};
return result
};
나의 풀이
function solution(n, numlist) {
return numlist.filter(num => num % n === 0);
}
filter 메서드를 이용한 다른 사람의 풀이보고 수정해 본 코드. 이번에 filter 메서드 처음 이용해봤다..! 코드가 이렇게 간결해지다니 신기하다.
궁금해서 프로그래머스에서 실행 속도를 비교해봤다. (실행 속도 맞겠지..?) 당연히 수정 후 방법이 더욱 효율적인 데이터 처리 방법인 거 같다.
회고
오늘부터 본격적으로 리액트 공부를 시작한다! component, jsx, props, state 등 처음 보는 개념들이 많아서 아직 좀 헤매고 있다. 일단 스파르타에서 제공해 주는 리액트 입문 강의를 완강하는 것을 목표로 하고 이해가 안 되는 부분은 코딩애플 강의로 보충하려 한다. 리액트 입문 강의 오늘 안에 다 들으려 했지만 실패했다 ㅎㅎ
오늘은 내일 새벽에 16강전을 보기 위해 일찍 자야 하기 때문에 좀 일찍 끝냈다. 마지막으로 알고리즘 문제만 하나 풀고 마무리하려 한다.