React 기본
npm
- node.js의 기본 패키지 관리자이다. 다양한 패키지 사이의 의존성 및 버전 정보를 관리한다.
- 프로젝트 패키지 관련 정보는 "package.json"파일에 저장된다.
- 패키지를 재활용하기 위해 프로젝트별 "./node_moduls/"로컬 폴더에 다운로드 받아 설치된다.
- 주요 npm 명령어
- install: 패키지를 설치한다. 글로벌로 설치된다. packge.json 파일이 있는 경우 파일 정보를 이용하여 설치한다.
- init: 패키지관리에 필요한 package.js를 생성한다.
- uninstall: 패키지를 삭제한다.
- list: 설치된 패키지 목록을 확인한다.
- update: 패키지를 업데이트한다. 패키지명을 생략하면 모두 업데이트 된다.
- cache: 캐쉬를 확인한다. clean --force를 이용하여 삭제한다.
- rebuild: 패키지를 재설치한다.
CRA
- react의 개발 환경을 자동으로 설정하여, 개발에 집중할 수 있도록 한다.
- 폴더 구조
- public: 컴파일이 필요 없는 정적인 파일이 저장되는 폴더
- sr: 컴파일 되어 실행되는 소스와 컴포넌트 및 관련된 파일 저장되는 폴더
- node_modules: Nodejs가 이용되는 패키지가 설치된 폴더
- /package.json: 프로젝트, 패키지 등 프로젝트에 관련된 정보가 있는 설정 파일
폴더 구조
- 일반적으로 리액트로 개발할 경우 다음과 같이 폴더를 생성한 뒤 소스를 분류하여 진행한다.
- 주로 컴포넌트와 관련된 소스가 많이 생성되어, "src"폴더 위주로 분류가 생성된다.
- src 폴더 기준
- components: 재사용 가능한 공통 컴포넌트와 관련 파일이 컴포넌트별 폴더로 관리된다.
- pages: 페이지별로 사용되는 컴포넌트와 관련 파일이 페이지 폴더별로 관리된다.
- store: 상태관리 라이브러리(Redux)와 관련된 코드가 관리된다. context로 하기도 한다.
- utils: 공통 유틸리티가 관리된다.
- assets: 이미지, 스타일시트, 폰트등 폴더별로 자원이 관리된다.
- services: API등 외부 연동 서비스에 관련된 기능이 관리된다.
- hooks: 사용자화 훅을 관리한다.
- npm run eject
- CRA(create-react-app)가 자동으로 설정하고 노출 시키지 않은 환경 설정을 수정할 수 있게 활성화한다.
- config와 scripts 폴더가 생성되고 관련 파일들이 생성된다. 기본적으로 한 번 실행하면 되돌리기가 힘들다.
- 환경 설정을 잘못 할 경우 개발 및 실행에 문제를 일으키기 때문에 될 수 있으면 수정하지 않는다.
실행
- index.js 는 index.html과 App.js를 이용하여 화면을 렌더링(Rendering)하여 출력을 한다.
- index.html
- 리액트가 이용할 DOM 컨테이너(Container)를 제공한다.
- App.js
- 컴포넌트이다. DOM 컨테이너에서 생성될 요소객체(Element Object)등이 선언된다.
- index.js
- 컴포넌트를 이용하여 DOM 컨테이너에 요소객체를 생성하고, DOM에 요소 변경 내용을 추가 또는 수정, 삭제한다.
단계별 명령어
$ npm install -g npm
$ npx create-react-app react-app
$ react-app> npm start
$ react-app> npm run build
모듈(Module)
- 함수나 코드로 구성된 프로그램의 기능적 단위이다. 애플리케이션의 구성요소이며, 자바스크립트의 경우 파일 단위로 생성한다.
- 모듈내 함수나 변수는 모듈 스코프(Module Scope)가 적용되고, export로 지정하지 않는 이상 보호된다.
- 애플리케이션을 기능 단위로 분류하여 복잡성과 코드 간의 정적인 의존성을 최소화하여 재사용성 및 유지보수가 용이해 진다.
- Node.js는 CommonJS, ECMAScript 모듈 사양을 지원한다. CommonJS 모듈 사양이 기본이다.
모듈 탐색
- 파일 경로가 지정된 경우 파일의 위치를 상대 경로를 기본으로 탐색한다. 확장자는 생략 가능하다.
- import App from './App.js';
- Node.js의 코어 모듈인지 확인한다.
- 코어 모듈이 아닌 경우 node_modules 폴더를 기준으로 파일 또는 하위 경로를 탐색한다.
- import React from 'React';
- 모듈내에 함수, 객체, 기본값 등을 외부에서 접근 가능하게 지정하고, 외부에서 참조하여 사용 가능하게 한다.
CommonJS Module(CJS Module)
- 개요
- CommonJS는 웹 브라우저에서 사용에 머물던 JavaScript 생태계를 서버, 데스크탑 등으로 확장
하여 응용 프로그램을 개발하기 위해 진행된 프로젝트이다.
- ECMA 표준 사양과 별개로 Node.js 위주로 발전되어 독자적인 체계를 가지고 있다. 따라서 표준을
준수하는 브라우저 기반 실행 환경과 완벽한 호환되지 않는다.
- Node.js의 기본 사양으로 지속적으로 지원되어 CommonJS로 제작된 많은 패키지들을 이용할 수
있는 장점이 있다.
- 최근에 Node.js는 ECMA 표준 및 모듈 사양 지원을 강화하고 있다.
- CommonJS 모듈은 동기식으로 처리된다.
- 설정
- 'package.json'의 'type' 속성을 'commonjs'로 지정한다. 확장자는 '.js'를 이용한다.
- 파일 확장자를 '.cjs'로 지정한다.
- 문법
- require(string 모듈명) 함수
- id 매개변수로 전달된 모듈명 또는 경로에서 지정된 모듈의 module.exports 객체를 반환한다.-
- 처음 호출시 require.cache에 저장되어, 동일 id를 require하면 cache에 저장된 객체가 반환되어 공유한다. singleton DP와 동일한 기능을 한다.
- 구조 분해 할당(Destructuring assignment)을 하는 경우 export에 사용된 속성명을 사용해야 한다.
- require 문의 위치는 정해진 곳이 없다. if 문내에서도 사용 가능하다.
- module.exports 객체
- 함수, 객체, 값들을 module.exports 에 담아 require 함수에서 반환할 수 있게 한다.
- exports는 module.exports의 단축어이다.
- default를 이용하면 모호성이 발생되어 유지보수 등에서 문제가 될 확률이 높아 피하고, module.exports 를 이용하는 것을 권장한다.
n01.helloworld.js
const hello = require('./modules/helloworld/HelloWorld.js');
console.log('Hello World');
hello.hello('홍길동');
HelloWorld.js
exports.hello = function(name = '회원') {
console.log(`${name}님 안녕하세요`);
};
n02_cjs.js
const a1 = require('#labs/cjs/cjs01.js');
console.log(a1);
const fnA2 = require('#labs/cjs/cjs02.js');
fnA2();
const objA3 = require('#labs/cjs/cjs03.js');
console.log(objA3.a);
objA3.fnA();
console.log('=================================');
const mA = require('#labs/cjs/cjs04.js');
console.log(mA.a);
mA.fnA();
const {a, fnA} = require('#labs/cjs/cjs04.js');
console.log(a);
fnA();
console.log('=================================');
const {m_a, m_fnA} = require('#labs/cjs/cjs05.js');
console.log(m_a);
m_fnA();
console.log('=================================');
if (m_a > 0) {
const a2 = require('#labs/cjs/cjs01.js');
console.log(a2);
}
cjs01.js
module.exports = 10;
cjs02.js
module.exports = function() {
console.log('fnA()');
};
cjs03.js
module.exports = {
a: 10,
fnA: function(){
console.log('fnA');
}
}
cjs04.js
exports.a = 'A';
exports.fnA = function () {
console.log('fnA');
};
cjs05.jsx
const m_a = 'm_A';
function m_fnA() {
console.log('m_fnA');
}
let a = {m_a, m_fnA, m_a2: m_a};
module.exports = a;
n03_mjs.mjs
import a1 from '#labs/esm/esm01.mjs';
console.log(a1);
import fnA1 from '#labs/esm/esm02.mjs';
fnA1();
import objA from '#labs/esm/esm03.mjs';
console.log(objA.a);
objA.fnA();
console.log('================================');
import * as mA from '#labs/esm/esm04.mjs';
console.log(mA.a);
mA.fnA();
import { a, fnA } from '#labs/esm/esm04.mjs';
console.log(a);
fnA();
import { a as a2, fnA as fnA2 } from '#labs/esm/esm04.mjs';
console.log(a2);
fnA2();
console.log('================================');
import { m_a, m_fnA, m_a2} from '#labs/esm/esm05.mjs';
console.log(m_a);
m_fnA();
import * as mB from '#labs/esm/esm05.mjs';
console.log(mB.a);
mB.m_fnA();
esm01.mjs
let a = 10;
export default a = 10;
esm02.mjs
export default function() {
console.log('fnA()');
};
esm03.mjs
export default {
a: 10,
fnA: function () {
console.log('fnA');
},
}
esm04.mjs
export let a = 'A';
export function fnA() {
console.log('fnA');
};
esm05.mjs
const m_a = 'm_A';
function m_fnA() {
console.log('m_fnA');
}
export { m_a, m_fnA, m_a as m_a2 };
ECMAScript Module(ES Module, ESM)
- 개요
- ECMA가 ES6(ES2015)부터 웹 표준으로 지원하는 모듈관련 사양(Spec)이다. Node.js는 표준 지
원 전에 CommonJS 모듈 사양이 이용되었다.
- Node.js v12 부터 사용 가능하다. 하지만 babel등의 번역기를 이용하면 Node.js 버전과 상관없
이 이용 가능하다.
- 최근에는 ECMAScript 모듈 사용이 권장되고 있고, 지원이 강화되고 있다.
- ES 모듈은 비동기식으로 처리된다.
- 설정
- 'package.json'의 'type' 속성을 'module'로 지정한다. 확장자는 '.js'를 이용한다.
- 파일 확장자를 '.mjs'로 지정한다.
- 문법
- 기본적으로 commonjs와 비슷하다.
- 최상위 스코프(Top Level)에 기술해야 한다. 코드의 제일 위에 기술하는 관례가 있다.
- 염격 모드(use strict)가 기본이다.
- import ~ from 모듈명 문
- id 매개변수로 전달된 모듈명 또는 경로에서 지정된 모듈을 참조한다.
- 동일 id를 impor하면 singleton으로 관리된다.
- 구조 분해 할당(Destructuring assignment)을 하는 경우 export에서 사용된 속성명을 사용해야 한다.
- export 문
- 함수, 객체, 값들을 import 에서 접근할 수 있게 한다.
- 내보내는 모듈은 엄격 모드(use strict)이다.
- default는 이름에 대한 모호성이 발생되어 유지보수 등에서 문제가 될 확률이 높아 피한다.
컴포넌트
App.js
import logo from './logo.svg';
import './App.css';
import React, { useState } from 'react';
function Header(props) {
console.log('props', props)
return (
<header>
<h1><a href="/" onClick={(event)=>{
event.preventDedault();
props.onChangeMode();
}}>{props.title}</a></h1>
</header>
)
}
function Nav(props) {
const lis = []
for(let i=0; i<props.topics.length; i++) {
let t = props.topics[i];
lis.push(<li key={t.id}>
<a id={t.id} href={'/read/'+t.id} onClick={event=>{
event.preventDefault();
props.onChangeMode(Number(event.target.id));
}}>{t.title}</a>
</li>);
}
return(
<nav>
<ol>
{}
{lis}
</ol>
</nav>
)
}
function Article(props) {
return(
<article>
<h2>{props.title}</h2>
{props.body}
</article>
)
}
function App() {
const [mode, setMode] = useState('WELCOME');
const [id, setId] = useState(null);
const topics = [
{id:1, title:'html', body:'html is ...'},
{id:2, title:'css', body:'css is ...'},
{id:3, title:'javascript', body:'javascript is ...'}
]
let content = null;
if(mode === 'WELCOME') {
content = <Article title="Welcome" body="Hello, Web"></Article>
} else if(mode === 'READ') {
let title, body = null;
for(let i=0; i<topics.length; i++){
console.log(topics[i].id, id);
if(topics[i].id === id) {
title = topics[i].title;
body = topics[i].body;
}
}
content = <Article title="Welcome" body="Hello, Read"></Article>
}
return (
<div className="App">
<Header title="WEB" onChangeMode={()=>{
setMode('WELCOME');
alert('Header');
}}></Header>
<Nav topics={topics} onChangeMode={(_id)=>{
setMode('READ');
alert(_id);
}}></Nav>
{content}
<Article title="Welcome" body="Hello, Web"></Article>
<Article title="Hi" body="Hello, React"></Article>
{}
</div>
);
}
export default App;
개요
- 리액트 애플리케이션의 UI를 구성하고 있는 재사용 가능한 기본 단위이다. 리액트 애
플리케이션의 UI는 컴포넌트들의 조합으로 구성한다.
- props를 이용하여 데이터를 받고, 일반 객체(plain object)인 리액트 엘리먼트
(React Element)를 반환하는 것을 컴포넌트라고 한다.
- 컴포넌트 명은 HTML Tag와 구분하기 위해 대문자로 시작하는 파스칼(Pascal)표기
법을 따른다.
- 리액트 요소는 주로 JSX(JavaScript XML)을 이용하여 생성한다.
- Tag, CSS, Data, JavaScript 함수 등을 조합하여 재활용 가능한 컴포넌트로 관리할
수 있다.
- 컴포넌트 선언에 CommonJS, ES Module 선언 방법이 이용된다. 최근에 ES
Module 선언 방법을 주로 이용한다.
- UI 구성에 오픈 소스(Open Source)인 Chakra UI, Material UI, Bootstrap등을
이용하기도 한다.
- 기본적으로 container / presentational 패턴과 atomic 패턴을 지원한다.
- container : 데이터와 처리에 관련된 자바스크립트 부분이다.
- presentational : 화면 출력 부분으로 JSX 부분이다.
- atomic : 컴포넌트를 중복을 피하기 위해 작은 단위로 나누는 구조이다.
React DOM
- React DOM은 브라우저(Browser) DOM 구조를 투영한 React 엘리먼트(Element)
로 구성된 Virtual DOM을 생성한다.
- ReactDOM.render()를 이용하여 JSX등을 이용하여 Virtual DOM 생성한다.
- 자식 요소들은 생성하기 위해서는 단일 루트 요소(Root Element)가 필요하다.
- React 엘리먼트는 순수 자바스크립트 객체이고, 상태는 수정되지 않는 불변이다. 수
정을 할 경우 새 요소를 생성해서 갱신한다.
- 이전 상태와 비교해 상태가 변경된 React 엘리먼트을 기준으로 브라우저 DOM을 변
경해 준다.
종류
- 컴포넌트를 선언방법에 따라 클래스와 함수 컴포넌트로 구분할 수 있다.
- 최근에는 함수를 이용한 선언 방법을 권장한다. 그러나 2019년도에 발표된 v16.8 이전까지는 클래스를 이용하여 주로 선언하였다. 따라서 클래스 컴포넌트의 이해도 필요하다.
- 함수 컴포넌트(Function Component)
function WelcomeMsg(props) {
return <h1>안녕, {props.name}</h1>;
}
- 클래스 컴포넌트(Class Component)
class WelcomeMsg extends React.Component {
render() { return <h1>안녕, {this.props.name}</h1> };
}
함수 컴포넌트(Function Component)
개요
- 매개변수를 이용하여 데이터를 받을 수 있고, 리액트 엘리먼트(React Element)를 반환하는 기본
함수가 있으면 함수 컴포넌트가 된다.
- 매개변수명은 관례상 props를 사용한다.
- 기본으로 내보낼 함수명은 소스 코드명와 동일하게 지정한다.
- 리액트 v16.8 버전부터 지원된 리액트 훅(React Hook)전까지는 정적(Static)인 컴포넌트로 많이
이용되었다.
- 컴포넌트 내에 함수는 모듈내 최상위 스코프(Top Level Scope)에 선언한다.
- 중첩함수(Nested Function)는 성능과 디버깅에 문제가 있어 사용하지 않는다.
props
- 값이나 함수 등을 부모 컴포넌트에서 자식 컴포넌트로 단방향(one way)으로 전달하는 경우 사용
된다.
- 함수형 컴포넌트에서는 매개 변수를 이용하고, 다양한 매개 변수명을 사용 가능하나 관례적으로
"props"(property의 줄임말)를 사용한다.
- 불변형(immutable)으로 읽기 전용이다.
- 배열을 이용하여 출력할 경우 key가 필요하다. key를 이용하여 리액트 요소의 고유 식별자로 이용
되고, 리렌더링(re-rendering)에 이용되어 성능을 향상시킨다.
- key 값은 형제 노드에서만 유일한 값이면 되고 글로벌 할 필요는 없다.
- defaultprops
- props가 초기화 되지 않아 undefined되는 것을 피하기 위해 미리 초기화 하는 방법이다.
- 함수형 컴포넌트가 대세가 되면서 사용 중단 예정(deprecated )이다.
state
- 컴포넌트의 상태를 동적 관리하기 위에 사용되는 기본 React Hooks 중 하나이다.
- props와 다르게 컴포넌트 내부에서 관리된다.
- useState() Hook를 이용하여 관리할 상태를 필요한 만큼 설정한다.
- "상태값 변경함수"로 "상태값"을 변경하면, 컴포넌트가 비동기적으로 리렌더링이 된
다.
- 리렌더링을 효율적으로 하기 위해 변경 상태들을 순차적으로 관리하고, 일괄적으로
적용된다.
- 리액트는 부모에서 자식 컴포넌트 방향으로만 데이터가 흐르는 단방향(One way)이
기본이다.
- 필요할 경우 자식의 상태를 부모로 끌어올리는 방법인 state 끌어올리기(lifting
state up)를 적용한다.
- 단방향은 형제 노드들 사이에는 직접 데이터 전달이 불가능한 구조이다. 부모가 같은 형제 요소 노
드에게 데이터를 전달하는 방법이다.
- 자식 컴포넌트에서 이벤트가 발생되면 부모가 전달한 이벤트 핸들러를 이용하여 상태를 변경해야
한다.
- 리액트 요소 트리 깊이가 깊을 경우 발생되는 props drilling이 일어나면 상태 끌어올리기가 복잡
해서 관리가 힘들어 진다. 상태관리 라이브러리인 Context, Redux등 사용을 권장한다.
이벤트
- React 엘리먼트의 이벤트 처리는 DOM 이벤트 처리와 비슷하다. React 엘리먼트 중
DOM 이벤트는 DOM 엘리먼트에 적용 가능하다.
- React 컴포넌트에서 선언된 이벤트 핸들러는 이벤트로 처리되지 않고 props로 전달
된다. 이벤트가 처리되는 DOM 엘리먼트에서 이벤트 핸들러에서 함께 처리된다.
- 이벤트명는 카멜 케이스(camelCase)표기법으로 지정하고, 이벤트 핸들러(Event
Handler)는 함수를 이용한다.
- 이벤트명은 관례적으로 접두어로 'on'을 사용하고, 이벤트 핸들러는 'handle' 접두
어를 사용한다.
- 발생된 이벤트 정보는 합성 이벤트(SyntheticEvent)를 이용하여 확인한다. 합성 이
벤트는 브라우저 이벤트의 래퍼(Wrapper)이다.
- 브라우저 이벤트 정보가 필요할 경우 event.nativeEvent 속성을 이용한다.
JSX
- JSX (JavaScript Syntax Extension, JavaScript XML)의 약자로 XML형식으로
React 요소 트리(React Element Tree)를 생성하는 자바스크립트 확장 표현식이다.
- React 요소를 편리하게 생성하기 위한 문법 설탕(syntactic sugar)이다.
- 웹팩(Webpack)의 번들링(Bundling) 과정에서 바벨(Bable)에 의해 자바스크립트
코드로 변환된다.
- 바벨은 React.createElement()를 호출하여 컴파일 한다.
문법
- 기본적으로 XML의 규칙을 따른다.
- 단일 요소가 아닌 경우 최상위 루트 요소가 있어야 한다.
- 코드는 소문자를 기본으로 하고, 리액트 사용자 컴포넌트는 대문자로 시작하는
Pascal 표기법을 이용한다.
- 단독 태그인 경우 반드시 스스로 닫아야(self-closing ) 한다.
- 주석은 "{ / ~ / }"로 기술한다.
- 시작 태그인 경우 태그 내부에서 "//" 를 이용하여 주석을 기술할 수 있다.
- Boolean, null, undefined는 출력되지 않는다.
- 표현식(Expression)을 "{ ~ }"를 이용하여 변수 값, 객체, 연산자, 함수호출 등의 자
바스크립트 구문을 사용할 수 있다.
안녕하세요 :) 국비지원 부트캠프 엘리스트랙입니다! 오늘도 개발 공부 열심히 하고 계시군요! 멋지십니다 :)
혹시 개발 공부하면서 기술면접에 대한 대비가 막막하시다면, 이번 기술면접 특강도 관심 가져보시면 좋을 것 같아 댓글로 행사 안내드려요~
프론트/ 백엔드 모두 실력있고, 실제 면접관으로 활동하고 계신 개발자 코치님께서 진행하시니 참여해 보세요> https://festa.io/events/4389
그럼 오늘도 화이팅입니다!🙇🏻♀️💪🏻