리액트 컴포넌트 파일에서 XML형태로 코드를 작성하면 bable이 JSX를 JavaScript로 변환을 해준다.
어떻게 변환되는지 한번 예시를 보자
Babel은 자바스크립트의 문법을 확장해주는 도구이다.
아직 지원되지 않는 최신 문법이나, 편의상 사용하거나 실험적인 자바스크립트 문법들을 정식 자바스크립트 형태로 변환해 줌으로서 구형 브라우저 같은 환경에서도 제대로 실행할수 있게 해주는 역할을 한다.
꼭 닫혀야 하는 태그
태그는 꼭 닫혀있어야 한다.
다음과 같은 코드는 오류가 발생하게 된다.
App.js
import React from 'react';
import Hello from './Hello';
function App() {
return (
<div>
<Hello />
<Hello />
<Hello />
<div>
</div>
);
}
export default App;
태그를 열었으면 꼭, <div></div>이렇게 닫아줘야 된다.
HTML에서는 input또는 br태그를 사용 할 때 닫지 않고 사용하기도 한다. 하지만 리액트에서는 그렇게 하면 안된다.
App.js
import React from 'react';
import Hello from './Hello';
function App() {
return (
<div>
<Hello />
<Hello />
<Hello />
<input>
<br>
</div>
);
}
export default App;
태그와 태그 사이에 내용이 들어가지 않을 때에는, Self Closing태그 라는 것을 사용해야 ㅎ나다. 현재 Hello컴포넌트를 사용 할 떄에도 Self Closing 태그를 사용해 주었는데, 열리고 바로 닫히는 태그를 의미한다.
App.js
import React from 'react';
import Hello from './Hello';
function App() {
return (
<div>
<Hello />
<Hello />
<Hello />
<input />
<br />
</div>
);
}
export default App;
꼭 감싸져야 하는 태그
두개 이상의 태그는 무조건 하나의 태그로 감싸져 있어야 한다. 한번 다음 코드를 작성해보자.
App.js
import React from 'react';
import Hello from './Hello';
function App() {
return (
<Hello />
<div>안녕히계세요.</div>
);
}
export default App;
이런 코드는 오류가 발생하게 된다.
그 대신에 하나의 태그로 감싸주어야 한다.
import React from 'react';
import Hello from './Hello';
function App() {
return (
<div>
<Hello />
<div>안녕히계세요</div>
</div>
);
}
export default App;
하지만 이렇게 단순히 감싸기 위해 불필요한 div로 감싸는게 별로 좋지 않은 상황도 있다. 예를 들어서 스타일 관련 설정을 하다가 복잡해지게 되는 상황도 올수 있고, table관련 태그를 작성 할 때에도 내용을 div같은걸로 감싸기엔 애매하다. 그럴 땐, 리액트의 Fragment라는 것을 사용하면 된다.
App.js
import React from 'react';
import Hello from './Hello';
function App() {
return (
<>
<Hello />
<div>안녕히계세요</div>
</>
);
}
export default App;
태그를 작성 할 때 이름없이 작성을 하게 되면 Fragment가 만들어지는데, Fragment는 브라우저 상에서 따로 별도의 엘리먼트로 나타나지 않는다.
JSX안에 자바스크립트 값 사용하기
JSX내부에 자바스크립트 변수를 보여줘야 할때에는 {}으로 감싸서 보여준다.
import React from 'react';
import Hello from './Hello';
function App() {
const name = 'react';
return (
<>
<Hello />
<div>{name}</div>
</>
);
}
export default App;
style과 className
JSX에서 태그에 style과 CSS class를 설정하는 방법은 HTML에서 설정하는 방법과 다르다.
우선, 인라인 스타일은 객체 형태로 작성을 해야하며, background-color처럼 -로 구분되어 있는 이름들은backgroundColor처럼 camelCase형태로 네이밍 해주어야 한다.
App.js
import React from 'react';
import Hello from './Hello';
function App() {
const name = 'react';
const style = {
backgroundColor: 'black',
color: 'aqua',
fontSize: 24, // 기본 단위 px
padding: '1rem' // 다른 단위 사용 시 문자열로 설정
}
return (
<>
<Hello />
<div style={style}>{name}</div>
</>
);
}
export default App;
그리고 CSS class를 설정 할때에는 class=가 아닌 className=으로 설정을 해주어야 한다. 한번 App.css파일을 열어서 전체 내용을 지운뒤 다음과 같이 수정해보자.
import React from 'react';
import Hello from './Hello';
import './App.css';
function App() {
const name = 'react';
const style = {
backgroundColor: 'black',
color: 'aqua',
fontSize: 24, // 기본 단위 px
padding: '1rem' // 다른 단위 사용 시 문자열로 설정
}
return (
<>
<Hello />
<div style={style}>{name}</div>
<div className="gray-box"></div>
</>
);
}
export default App;
주석
이제, JSX에서 주석은 어떻게 작성하는지 알아보자.
JSX내부의 주석은 {/* 이런형태로 */} 작성한다.
한번 사용해보자.
import React from 'react';
import Hello from './Hello';
import './App.css';
function App() {
const name = 'react';
const style = {
backgroundColor: 'black',
color: 'aqua',
fontSize: 24, // 기본 단위 px
padding: '1rem' // 다른 단위 사용 시 문자열로 설정
}
return (
<>
{/* 주석은 화면에 보이지 않습니다 */}
/* 중괄호로 감싸지 않으면 화면에 보입니다 */
<Hello
/>
<div style={style}>{name}</div>
<div className="gray-box"></div>
</>
);
}
export default App;
추가적으로, 열리는 태그 내무에서는 // 이런 형태로도 주석 작성이 가능하다.
import React from 'react';
import Hello from './Hello';
import './App.css';
function App() {
const name = 'react';
const style = {
backgroundColor: 'black',
color: 'aqua',
fontSize: 24, // 기본 단위 px
padding: '1rem' // 다른 단위 사용 시 문자열로 설정
}
return (
<>
{/* 주석은 화면에 보이지 않습니다 */}
/* 중괄호로 감싸지 않으면 화면에 보입니다 */
<Hello
// 열리는 태그 내부에서는 이렇게 주석을 작성 할 수 있습니다.
/>
<div style={style}>{name}</div>
<div className="gray-box"></div>
</>
);
}
export default App;
import React from 'react';
function Hello() {
return <div>안녕하세요</div>
}
export default Hello;
리액트 컴포넌트를 만들 땐
import React from 'react';
를 통하여 리액트를 불러와 줘야 된다.
리액트 컴포넌트는 함수형태로 작성 할수도 있고 클래스 형태로도 작성할수 있다. 지금은 함수로 작성하는 방법만 해보자.
리액트 컴포넌트에서는 XML 형식의 값을 반환해줄 수 있는데 이를 JSX 라고 부른다.
코드의 최하단
export default Hello;
이 코드는 Hello라는 컴포넌트를 내보내겠다는 의미이다.
이렇게 해주면 다른 컴포넌트에서 불러와서 사용할수 있다.
이 컴포넌트를 한번 App.js에서 불러와서 사용해 보겠다
App.js
import React from 'react';
import Hello from './Hello';
function App() {
return (
<div>
<Hello />
</div>
);
}
export default App;
상단에 있던
import logo from './logo.svg';
import './App.css';
는 SVG파일을 불러오고 CSS를 적용하는 코드인데 이는 현재 불필요 하므로 제거해 주었다.
컴포넌트는 일종의 UI조각이다. 그리고 쉽게 재사용 할수 있다.
import React from 'react';
import Hello from './Hello';
function App() {
return (
<div>
<Hello />
<Hello />
<Hello />
</div>
);
}
export default App;
이제 index.js를 열어보자.
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
여기서 ReactDOM.render 의 역할은 브라우저에 있는 실제 DOM 내부에 리액트 컴포넌트를 렌더링하겠다는 것을 의미한다.
id가root인 DOM 을 선택하고 있는데, 이 DOM 이 어디있는지 찾아보자
public/index.html을 열어보면 내부에
<div id="root"></div>
을 볼수 있다.
결국, 리액트 컴포넌트가 렌더링 될떄에는 렌더링된 결과물이 위 div내부에 렌더링 되는것이다.
각 DOM에 내장 되어 있는 기능들은 정말 다양하지만 그중 중요한것 몇가지만 사용해 보자.
const number = document.getElementById("number");
const increase = document.getElementById("increase");
const decrease = document.getElementById("decrease");
console.log(number.innerText); // 내용
console.log(increase.offsetTop); // top 위치
console.log(decrease.id); // id
이벤트 설정하기
이제 DOM이벤트를 설정해 보자. 버튼들이 클릭 됐을 때 콘솔에 텍스트를 출력하는 이벤트를 설정해보자.
const number = document.getElementById("number");
const increase = document.getElementById("increase");
const decrease = document.getElementById("decrease");
increase.onclick = () => {
console.log("increase 가 클릭됨");
};
decrease.onclick = () => {
console.log("decrease 가 클릭됨");
};
버튼들을 클릭했을 때 콘솔에 우리가 설정한 텍스트들이 출력되는지 확인해 보자.
DOM에 이벤트를 설정 할떄는 on이벤트이름 값에 함수르 설정해주면 됩니다. DOM 이벤트의 종류는 정말 다양하다.
이제 버튼들이 클릭될 때 숫자값을 올리거나 내려보자.
const number = document.getElementById("number");
const increase = document.getElementById("increase");
const decrease = document.getElementById("decrease");
increase.onclick = () => {
const current = parseInt(number.innerText, 10);
number.innerText = current + 1;
};
decrease.onclick = () => {
const current = parseInt(number.innerText, 10);
number.innerText = current - 1;
};
이번에 자바 스크립트의 Scope에 대해서 알아보자. Scope란 우리가 변수 혹은 함수를 선언하게 될 떄 해당 변수 또는 함수가 유효한 범위를 의미한다. Scope는 총 3가지 종류가 있다.
1. Global(전역) Scope: 코드의 모든 범위에서 사용이 가능
2. Function(함수) Scope: 함수 안에서만 사용이 가능하다.
3. Block(블록) Scope : if, for, switch등 특정 블록 내부에서만 사용이 가능하다.
예시를 통한 Scope이해
한번 예시 코드를 보고 Scope를 이해해 보자
const value = 'hello!';
function myFunction() {
console.log('myFunction: ');
console.log(value);
}
function otherFunction() {
console.log('otherFunction: ');
const value = 'bye!';
console.log(value);
}
myFunction();
otherFunction();
console.log('global scope: ');
console.log(value);
위 코드의 결과는 다음과 같다.
코드의 맨 윗줄에서 선언된 value값은 Global Scope로 선언된 값이다. Global Scope로 선언된 값은 어디서든지 사용이 가능하다. myFunction 에서 바로 사용을 할 수 있었다.
otherFunction에서는 함수 내부에서 value값을 'bye!'로 새로 선언을 해주었다. 이렇게 되면, value라는 값은 Function Scope로 지정이 되서 해당 값은 otherFunction 내부에서만 유효한 값이 된다. 이렇게 값을 설정한다고 해서 기존에 Global Scope로 선언된 value값이 바뀌지 않는다.
또 다른 예시를 확인해 보자.
const value = 'hello!';
function myFunction() {
const value = 'bye!';
const anotherValue = 'world';
function functionInside() {
console.log('functionInside: ');
console.log(value);
console.log(anotherValue);
}
functionInside();
}
myFunction()
console.log('global scope: ');
console.log(value);
console.log(anotherValue);
myFunction 내부에 anotherValue라는 새로운 값을 선언했고, functionInside라는 함수도 선언을 했다.
functionInside 함수에서는 myFunction에서 선언한 value값과 anotherValue 값을 조회 할수 있다.
반면, myFunction밖에서는 anotherValue를 조회 할 수 없다.
이제 또다른 예시를 알아보자.
const value = 'hello!';
function myFunction() {
const value = 'bye!';
if (true) {
const value = 'world';
console.log('block scope: ');
console.log(value);
}
console.log('function scope: ');
console.log(value);
}
myFunction();
console.log('global scope: ');
console.log(value);
const로 선언한 값은 Block Scope로 선언이 된다. 따라서 if 문 같은 블록 내에서 새로운 변수/상수를 선언하게 된다면, 해당 블록내부에서만 사용이 가능하고 블록밖의 범위에서 똑같은 이름을 가진 값이 있다고 해도 영향을 주지 않는다.
let또한 마찬가지 이다.
하지만 var는 어떨까?
var value = 'hello!';
function myFunction() {
var value = 'bye!';
if (true) {
var value = 'world';
console.log('block scope: ');
console.log(value);
}
console.log('function scope: ');
console.log(value);
}
myFunction();
console.log('global scope: ');
console.log(value);
var는 Function Scope로 선언이 되므로, if 문 블록 내부에서 선언한 value값이 블록 밖의 value에도 영향을 미치게 된다.
Hoisting 이해하기
Hoisting이란 자바스크립트에서 아직 선언되지 않은 함수/변수를 "끌어올려서" 사용 할수 있는 자바스크립트의 작동 방식을 의미한다.
다음코드를 확인해 보자.
myFunction();
function myFunction() {
console.log('hello world!');
}
위 코드에서는 myFunction함수를 선언하기 전에,
myFunction()을 호출해 주었다. 함수가 아직 선언되지않았음에도 불구하고 코드는 정상적으로 작동하게 된다.
이게 잘 작동하는 이유는, 자바스크립트 엔진이 위 코드를 해석하는 과정에서, 다음과 같이 받아 들이게 되기 때문이다
function myFunction() {
console.log('hello world!');
}
myFunction();
이러한 현상을, Hoisting이라고 부른다.
변수 또한 Hoisting 된다.
예를 들어서, 다음과 같은 코드를 실행한다면,
console.log(number);
이런 오류가 발생한다.
그렇지만, 다음과 같은 코드는
console.log(number);
var number = 2;
undefined가 출력된다.
자바스크립트 엔진이 위 코드를 해석 할 때는 다음과 같이 받아들이게 된다.
var number;
console.log(number);
number = 2;
반면 const와 let은 hoisting이 발생하지 않고, 에러가 발생하게 된다. Codesandbox에서는 자바스크립트가 Bable이라는 도구에 의하여 const와 let이 var로 변환되기 때문에 오류가 발생하지 않는다. Chrome 개발자 도구의 콘솔에서 다음 코드를 실행해 보자.
function fn() {
console.log(a);
let a = 2;
}
fn();
Hoisting은 자바스크립트 엔진이 갖고 있는 성질이며, Hoisting을 일부러 할 필요는 없지만, 방지하는 것이 좋다. 왜냐하면 Hoisting이 발생하는 코드는 이해하기 어렵기 때문에 유지보수도 힘들어지고 의도치 않는 결과물이 나타나기 쉽기 때문이다.
Hoisting을 방지하기 위해서, 함수의 경우 꼭 선언후에 호출을 하도록 주의를 하고, var대신 const,let을 위주로 사용하자. 추가적으로 나중에 자바스크립트 개발을 본격적으로 하게 될 때에는 EsLint 라는것을 사용하여 Hoisting이 발생하는 코드는 에디터상에서 쉽게 발견해 낼수 있다.
rest를 함수 파라미터에서 사용 할 수도 있다. 예를 들어서 우리가 파라미터로 넣어준 모든 값들을 합해주는 함수를 만들어 주고 싶다고 가정해보자.
function sum(a, b, c, d, e, f, g) {
let sum = 0;
if (a) sum += a;
if (b) sum += b;
if (c) sum += c;
if (d) sum += d;
if (e) sum += e;
if (f) sum += f;
if (g) sum += g;
return sum;
}
const result = sum(1, 2, 3, 4, 5, 6);
console.log(result);
위에서의 sum함수는 7개의 파라미터를 받아오는데, 아래에서 사용 할때에는 6개만 넣어줬다.그러면, g 값이 undefined 가 되기 때문에 sum 에 더하는 과정에서 += undefined 를 하게 되면 결과는 NaN 이 되버린다.그렇기 때문에 함수에서 하나하나 유효한 값인지 확인을 해줬다.
함수의 파라미터가 몇개가 될 지 모르는 상황에서 rest파라미터를 사용하면 매우 유용하다. 코드를 다음과 같이 수정해보자.
function sum(...rest) {
return rest;
}
const result = sum(1, 2, 3, 4, 5, 6);
console.log(result);
result가 가르키고 있는 것은 함수에서 받아온 파라미터들로 이루어진 배열이다. 우리가 이제 파라미터들이 들어가 있는 배열을 받았으니 그냥 모두 더해주면 된다.
물론, 코드가 짧다고 해서 무조건 좋은것은 아니다. 단 코드가 짧으면서도 읽었을 때 어떤 역할을 하는지 잘 이해가 될수 없어야 비로소 좋은 코드이다.
값에 따라 다른 결과물을 반환 해야 할 때
이번에는 주어진 값에 따라 다른 결과물을 반환해야 할 때 사용 할 수 있는 유용한 팁을 알아보자.
예를 들어서, 동물 이름을 받아오면, 동물의 소리를 반환하는 함수를 만들고 싶다고 가정해보자.
function getSound(animal) {
if (animal === '개') return '멍멍!';
if (animal === '고양이') return '야옹~';
if (animal === '참새') return '짹짹';
if (animal === '비둘기') return '구구 구 구';
return '...?';
}
console.log(getSound('개')); // 멍멍!
console.log(getSound('비둘기')); // 구구 구 구
if문의 코드 블록이 한줄짜리라면 {}를 생략 할 수도 있다.
만약 여기서 우리가 배운 switch case문을 사용하여 다음과 같이 구현 할 수도 있다.
function getSound(animal) {
switch (animal) {
case '개':
return '멍멍!';
case '고양이':
return '야옹~';
case '참새':
return '짹짹';
case '비둘기':
return '구구 구 구';
default:
return '...?';
}
}
console.log(getSound('개')); // 멍멍!
console.log(getSound('비둘기')); // 구구 구 구
참고로 switch 문에서 return 을 할떄는 break를 생략해도 된다.
우리가 방금 구현한 코드는 큰 문제는 없지만, 이걸 깔끔하게 해결할 방법을 알고 나면 좀 맘에 들지 않는 코드의 형태이다.
이 코드를 더욱 깔끔하게 작성하는 방법을 알아보자
function getSound(animal) {
const sounds = {
개: '멍멍!',
고양이: '야옹~',
참새: '짹짹',
비둘기: '구구 구 구'
};
return sounds[animal] || '...?';
}
console.log(getSound('개')); // 멍멍!
console.log(getSound('비둘기')); // 구구 구 구
훨씬 더 간략하고 가독성도 좋다.
이렇게 특정 값에 따라 반환 해야 하는 값이 다른 조건이 여러가지 있을 때는 객체를 활용하면 좋다.