📌 React란?

화면에 렌더링되는 UI를 컴포넌트 단위로 쪼개 사용할 수 있는 Javascript 라이브러리리 혹은 프레임워크이다.
라이브러리인가? 프레임워크인가?는 크게 중요하지 않다. 우리가 중점적으로 생각해야할 부분은 리액트는 Progressive(점진적)이라는 것이다.

특징

  1. 리액트는 점진적이므로 애플리케이션을 모두 리액트로 구성할 필요가 없다.

  2. 리액트는 Javascript 중점 라이브러리이다. 그러므로 자바스크립트를 잘하면 재밌을 것이다.

  3. 리액트는 별도의 설치가 필요없이 컴포넌트를 즉시 사용해볼 수 있다.

  4. 선언형 프로그래밍으로 명령형 프로그래밍보다 코드를 이해하기가 쉽다.

  5. 리액트를 배우면 웹, 리액트 네이티브를 배우면 모바일 등 한가지를 배워 확장성이 넓다.

시작하기 전

1. React API 라이브러리를 사용하여 UI를 구상하는 VitualDOM을 생성한다.

1
2
// CDN
<script src="//unpkg.com/react@17/umd/react.development.js" crossorigin></script>
  • 만약 버전을 변경하고 싶다면 @version을 써준다.

  • 만약 IE를 고려 해야한다면 17 버전을 사용하고 그렇지 않다면 18 버전 사용하자.

2. ReactDOM API 라이브러리를 사용하여 VirtualDOM을 RealDOM에 렌더링하여 UI를 구현한다.

1
<script src="//unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script>
  • React API와 버젼을 일치시켜 줘야한다.

💡 React v17의 render()와 v18의 createRoot() 차이

1
2
3
4
5
6
7
// version 17
const app = React.createElement('div');

ReactDOM.render(app,
document.getElementById('root'),
() => {console.log('rendered!')}
);
  • 렌더링할 React 요소인 app을 인자로 전달하고 이를 렌더링할 container인 root container를 render()에 전달한다.

리액트 버전 18부터 render() 대신 createRoot()를 사용한다.

1
createRoot(container[, options])
1
2
3
4
5
6
// version 18
const root = createRoot(document.getElementById('root'));

const element = <h1>Hello, world</h1>; // JSX

root.render(element);

문서의 RealDOM 요소 노드인 container를 React Root로 만들어 반환하고, React Root를 render() 메서드를 사용하여 React 요소를 RealDOM에 렌더링한다.

실습

1. React 요소 노드와 Real DOM 요소 노드 차이

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Real
const headlineNode = document.createElement('h1');
headlineNode.classList.add('headline');
headlineNode.setAttribute('land','en');
headlineNode.insertAdjacentHTML('beforeend', `Add <strong>React</strong> to a Website`);

// React
const headlineVnode = React.createElement(
'h1',
{
className : 'headline',
lang: 'en'
},
'Add ',
React.createElement('strong', null, 'React'),
'to a Website'
)

React.createElement

  • RealDOM 요소 노드는 브라우저가 렌더링하면서 생성한 것이고 이와 달리 React 요소 노드는 Object(객체)로 그 형태가 다르게 생겼다.

  • 위 사진을 보면 props라는 객체에 children, className, lang 등이 담겨있는 것을 알 수 있다. props에 대해서는 이후에 알아보자.

❗️ StrictMode 오류 해결

ECMAScript에서 ‘use strict’ 사용하여 문법적 오류를 미리 알려주었듯이 React에서도 이러한 오류를 미리 알려준다.

이에 대한 경고를 해결하기 위해서는 StrictMode 컴포넌트를 사용해야한다.

1
2
3
4
5
React.createElement(
React.StrictMode,
null,
React.createElement(App, { children: [headline, reactLogo] })
)

사용법은 간단하다. 생성하려는 React 요소를 React.StrictMode의 자식 요소로 전달해주면 된다.

2. React Component vs React Element

자바스크립트에서는 재사용을 위해 함수를 사용하고 리액트에서는 재사용을 위해 컴포넌트를 사용한다. 컴포넌트 생성 방법은 함수를 생성하듯이 생성할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
function SvgPath(props) {
return React.createElement('path',{
d: props.d,
fill: 'currentColor'
});
}

const App = props => React.createElement('div',
{ className: 'App' },
...props.children
);
  • 리액트 컴포넌트는 매개변수로 props를 받는다.

  • props 객체를 통해 다른 컴포넌트에게 본인의 HTML attributes, 자바스크립트 값(객체, 배열, 함수 등)을 전달해줄 수 있다.

  • 함수선언식, 함수표현식 둘 다 가능하다.

하지만 createElement()를 사용하여 컴포넌트를 생성하는 것은 매우 번거로운 일이다. 그 대안으로 생겨난 것이 바로 JSX이다.

JSX란?

JSX는 리액트 컴포넌트 사용을 HTML 처럼 사용하기 위해 생겨났다. JSX는 XML 처럼 생긴 문법 표현식이다.

1
2
3
4
5
6
7
8
9
10
const name = 'loco';

const element = (
<h1>
안녕하세요~! {name}
</h1>
);

// rendered
<h1>안녕하세요~! loco</h1>

자동 세미콜론 삽입이 되는 것을 방지하기 위해 괄호로 감싸는 것을 추천한다.

특징

  1. 선언형, HTML과 비슷한 구조, {}를 사용한 데이터 바인딩이 편리하다.

  2. 브라우저엔진에 의해 해석되지 못하므로 babel이 컴파일을 해줘야만 한다.

  3. HTML 보단 JS에 가까우므로 camelCase 명명규칙을 따른다.

  4. JSX는 렌더링하기 전에 이스케이프하므로 애플리케이션에서 명시적으로 작성되지 않는 내용은 script에 주입되지 않아 XSS 공격으로부터 안전하다.

❗️ React에서 babel 추가 설정

React바벨추가설정

  1. 이전에 React를 사용하기 전에 설정해두었던 babel 사양에서는 React를 컴파일 해줄 수 없다.

  2. 컴포넌트를 모듈 파일로 구분할 때에 babel은 input으로 지정된 파일만 컴파일 해주고 input 파일에서 import한 파일까지 컴파일해주지 않는다.

위 2가지 문제 해결을 위해서는 플러그인을 설치 해줘야한다.

1
npm i -D @babel/preset-react
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// babel.config.js
module.exports = {
comments: false,
presets: [
[
'@babel/preset-env',
{
modules: false,
loose: true,
},
],
'@babel/preset-react',
],
};

  • 설치가 끝났다면 babel.config.js 파일에 해당 플러그인을 사용할 것이라고 등록을 해줘야 정상 동작한다.

  • 옵션값이 있을 경우 []로 감싸서 넣어주고 그렇지 않은 경우 문자열로만 추가한다.

JSX로 컴포넌트 생성하기

이제 JSX를 사용할 준비가 끝났으니 JSX를 사용하여 컴포넌트를 생성해보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const HeadLine = () => (
<h1 className="headline" lang="en">
Add <strong>React</strong> to a Website
</h1>
);

const App = () => {
<div className="App">
<HeadLine></HeadLine>
</div>
}

const container = document.getElementById('react-root');
const ReactDOMRoot = ReactDOM.createRoot(container);

ReactDOMRoot.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
  • React.createElement()를 사용하여 React Element를 생성하여 컴포넌트를 생성하는 방법보다 훨씬 편리하다는 것을 알 수 있다.

🏓 소감

리액트 수업을 듣고 배운것을 차근차근 익혀가면서 정리해보았다. props에 대한 개념도 확실히 잡히고, JSX를 왜 쓰게 되었는지, 안쓰면 무엇인 문제점이고 불편한지를 깨달을 수 있는 시간이여서 좋았다.

공식문서를 읽어볼 때도 영어로 된 것을 자동번역하여 읽지 않고 원문을 보면서 읽으니 시간이 조금 더디지만 그렇기 때문에 기억에 더 오래 남을 것이라고 생각하여 영어로 읽는 연습을 하고있다.

댓글 공유

📌 문제

포비와 크롱이 페이지 번호가 1부터 시작되는 400 페이지의 책을 주웠다. 책을 살펴보니 왼쪽 페이지는 홀수, 오른쪽 페이지는 짝수 번호이고 모든 페이지에는 번호가 적혀있었다. 책이 마음에 든 포비와 크롱은 페이지 번호 게임을 통해 게임에서 이긴 사람이 책을 갖기로 한다. 페이지 번호 게임의 규칙은 아래와 같다.

  1. 책을 임의로 펼친다.
  2. 왼쪽 페이지 번호의 각 자리 숫자를 모두 더하거나, 모두 곱해 가장 큰 수를 구한다.
  3. 오른쪽 페이지 번호의 각 자리 숫자를 모두 더하거나, 모두 곱해 가장 큰 수를 구한다.
  4. 2~3 과정에서 가장 큰 수를 본인의 점수로 한다.
  5. 점수를 비교해 가장 높은 사람이 게임의 승자가 된다.
  6. 시작 면이나 마지막 면이 나오도록 책을 펼치지 않는다.
  7. 포비와 크롱이 펼친 페이지가 들어있는 배열 pobi와 crong이 주어질 때, 포비가 이긴다면 1, 크롱이 이긴다면 2, 무승부는 0, 예외사항은 -1로 return 하도록 solution 메서드를 완성하라.

제한사항

  • pobi와 crong의 길이는 2이다.
  • pobi와 crong에는 [왼쪽 페이지 번호, 오른쪽 페이지 번호]가 순서대로 들어있다.

실행 결과 예시

pobi crong result
[97, 98] [197, 198] 0
[131, 132] [211, 212] 1
[99, 102] [211, 212] -1

나의 해설

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function problem1(pobi, crong) {
const [pobiLeft, pobiRight] = pobi;
const [crongLeft, crongRight] = crong;

if (pobiRight - pobiLeft !== 1 || crongRight - crongLeft !== 1) return -1;

const sum = (str) =>
str
.split("")
.map((v) => +v)
.reduce((acc, cur) => acc + cur, 0);
const multiply = (str) =>
str
.split("")
.map((v) => +v)
.reduce((acc, cur) => acc * cur, 1);

const getMax = (left, right) => {
const leftMax = Math.max(sum(String(left)), multiply(String(left)));
const rightMax = Math.max(sum(String(right)), multiply(String(right)));
return Math.max(leftMax, rightMax);
};

const pobiMax = getMax(pobiLeft, pobiRight);
const crongMax = getMax(crongLeft, crongRight);

return pobiMax > crongMax ? 1 : pobiMax < crongMax ? 2 : 0;
}
  1. 우선 자주 사용될 것 같은 left 페이지 수와 right 페이지 수를 배열 구조분해 할당으로 변수에 할당해주었다.

  2. 이후 예외가 등장했을 때, 빠르게 결과를 내기 위해 바로 예외 처리를 해주었다.

  3. 각 페이지의 자릿수의 합과 곱을 구하는 함수를 생성해주었다.

  4. left와 right의 결과값 중 높은 값을 구하는 함수를 생성하였다.

  5. pobi의 최댓값과 crong의 최댓값을 비교하여 결과를 반환하였다.

🏓 소감

우아한 테크코스 프리코스 첫 문제를 받아보고 내가 이 문제를 푼 방법을 나열하며 정리해보았다. 논리의 흐름을 정리하니 다음번에 비슷한 문제가 나오면 더 빠른 시간 내에 풀고 다양한 시도를 해볼 수 있을 것 같다.

앞으로 4주간 프리코스가 진행될텐데 합격하든 불합격하든 나의 성장을 위해 모든 문제에 대한 풀이를 작성해보도록 할 것이다.

댓글 공유

22년 10월 29일 하루일기

카테고리 Diary

CPU가 너무 바쁘게 돌아가길래 원인을 해결하고자 구글 검색을 해보았다.

icloud가 문제라고 로그아웃하고 다시 로그인 해보라는 해결책을 찾아냈다.

icloud를 로그아웃했다. icloud 문서가 사라졌다.

나의 개발 폴더가 날라갔다.

다시 복구하느라 시간을 많이 소모했다.

🏓 오늘의 교훈

백업을 잘 해두자…

댓글 공유

Babel

카테고리 Settings

📌 babel

Babel은 자바스크립트 컴파일러로 아래의 역할을 하여 브라우저 호환 문제를 해결한다.

  • 구문 변환
  • 대상 환경에 누락된 폴리필 기능
  • 소스 코드 변환
계속 읽기

homebrew

카테고리 Settings

homebrew

mac, Linux에서 제공하지 않는 패키지를 설치할 수 있도록 도와주는 패키지 관리자이다. without requiring sudo.

사용하는 이유

Homebrew 전용 디렉터리에 패키지들을 설치하여 관리한다.

macOS에서 앱, 폰트, 플러그인, 오픈소스 등의 설치를 위해 여기로 끌어다 놓으세요.. 라는 행동을 할 필요가 없다.

설치

$brew install wget

이제 Cli 환경에서 brew를 사용하여 프로그램을 설치할 수 있다.

ex) git설치 - $brew install git

댓글 공유

Snippets

카테고리 Settings

Snippets

코드를 작성하다가 재사용되는 코드가 있다면 snippet으로 등록하여 간단한 단축키로 입력하여 코드의 생산성을 높힐 수 있다.

snippets-generator.app

아래 사이트는 snippets을 Editor에 맞게 쉽게 양식을 설정해주는 사이트이다.

https://snippet-generator.app/

설정 방법

vscode 명령 팔레트를 실행하여 configuration snippets를 들어간다.

원하는 언어 환경을 선택하고 파일을 만들어서 위 사이트에서 양식을 붙여넣어주면 된다. 이 때, 글로벌 환경에서 사용하고 싶다면 글로벌 환경으로 설정하고 파일을 만들어서 하면 된다.

관리

snippets 관리는 extension을 사용하여 편리하게 관리할 수 있다.

https://marketplace.visualstudio.com/items?itemName=robole.snippets-ranger

댓글 공유

🥨  리액트

Why?

우리가 CBD 기반의 라이브러리를 직접 사용해보았을 때, 전역상태의 개념 부재, 지역 상태 관리의 어려움 등의 문제를 겪었던 경험이 있다. 리액트는 이러한 어려움을 줄여준다.

👀  props vs state

  • props는 컴포넌트로 전달된 입력 데이터를 객체화한 것
    • 읽기 전용이므로 수정해서는 안된다.
  • state는 컴포넌트의 내부적인 상태 데이터를 객체화한 것 ⇒ 컴포넌트 상태 데이터 변경되면 렌더링 발생
    • 비공개 요소이며, 컴포넌트에 의해 완전히 제어된다.

👹  JSX

⇒ 리액트는 컴포넌트라는 느슨하게 연결된 유닛으로 관심사를 분리한다.

  • React element(객체)를 생성한다. (Babel은 JSX를 React.createElement() 호출로 컴파일한다.)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);

// 위와 동일
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);

// JSX는 아래와 같은 element 객체를 생성하고, 이를 통해 화면에 렌더링을 한다.
const element = {
type: 'h1',
props: {
className: 'greeting',
children: 'Hello, world!'
}
  • 표현식이다.

⇒ if, for 문에 사용될 수 있다.

  • JSX는 HTML보다 JS에 가깝기 때문에 어트리뷰트 이름 대신 camelCase로 명명한다.

ex) class ⇒ className, tabindex ⇒ tabIndex

  • 모든 값을 렌더링하기 전에 이스케이프하므로 악성 사용자가 XSS 공격을 하는 것을 방지할 수 있다.

🖥️  component vs element

element

DOM element와 달리 일반 객체이다. React DOM은 React 엘리먼트와 일치하도록 DOM을 업데이트 한다. 즉, 가상돔을 구성하고 리얼돔을 이와 일치하도록 한다.

React 엘리먼트를 렌더링하기 위해서 1. ReactDOM.createRoot()에 DOM 엘리먼트 전달 2. React 엘리먼트를 root.render() 에게 전달

1
2
3
4
5
const root = ReactDOM.createRoot(
document.getElementById('root')
);
const element = <h1>Hello, world</h1>;
root.render(element);
  • 불변객체로, 생성한 이후 해당 엘리먼트의 자식이나 속성을 변경할 수 없다.
  • 특정 시점의 UI를 보여준다고 생각
  • 이를 업데이트 하기 위해서는 새로운 엘리먼트 생성하고 이를 root.render()에게 전달하는 방법뿐

component

element들이 모여서 컴포넌트를 이룬다. 엘리먼트는 컴포넌트의 구성요소이다.

  • 컴포넌트를 통해 UI를 재사용 가능한 여러 조각으로 나눔 ⇒ CBD
  • 컴포넌트 정의 방법 ⇒ 함수 생성 (props라는 입력 데이터를 전달받는다)
1
2
3
4
5
6
7
8
9
10
11
// 함수형 컴포넌트
function Welcome(props) {
return <h1>Hello~! Welcome {props.name}</h1>;
}

// 클래스형 컴포넌트
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
  • React 컴포넌트는 자신의 props를 다룰 때, 반드시 순수함수 처럼 동작해야한다.
    • 프론트 개발을 하면서 순수함수로 개발을 한다는 것은 불가능한 일이기에 **state(상태)**가 등장하였다.

🧬  state와 컴포넌트 생명주기

생명주기 메서드를 사용하여 컴포넌트가 마운트되거나 언마운트될 때 일부 코드를 작동할 수 있다.

마운팅

컴포넌트가 처음 DOM에 렌더링 될 때마다 어떤 행동을 하도록 설정해주는 것을 “마운팅”이라고 한다.

언마운팅

컴포넌트가 생성된 DOM에서 삭제될 때마다 어떤 행동을 하도록 설정해주는 것을 “언마운팅”이라고 한다.

🎴 setState 유의사항

  • this.props, this.state가 비동기적으로 업데이트 될 수 있어 state를 계산할 때, 해당 값에 의존하면 안된다.
1
2
3
4
5
6
7
8
9
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});

// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
  • state가 소유하고 설정한 컴포넌트에만 state를 전달할 수 있다. ⇒ props를 통해 하향식(단방향식)으로만
1
2
3
4
5
function FormattedDate(props) {
return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
}

<FormattedDate date={this.state.date} />

FormattedDate 컴포넌트는 date를 자신의 props로 받을 것이고 이것이 Clock의 state로부터 왔는지, Clock의 props에서 왔는지, 수동으로 입력한 것인지 알지 못합니다.

🌃  이벤트 처리

  • camelCase 사용
  • 문자열이 아닌 {함수}로 전달
  • e.preventDefault() 명시적으로 작성
1
2
3
4
5
6
7
8
9
10
11
12
function Form() {
function handleSubmit(e) {
e.preventDefault();
console.log('You clicked submit.');
}

return (
<form onSubmit={handleSubmit}>
<button type="submit">Submit</button>
</form>
);
}

♨️  JSX 콜백안에서 this 바인딩

Javascript 클래스 메서드는 기본적으로 바인딩되어 있지 않는다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};

// 콜백에서 `this`가 작동하려면 아래와 같이 바인딩 해주어야 합니다.
this.handleClick = this.handleClick.bind(this);
}

handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}

render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
  • onClick={this.handleClick}과 같이 뒤에 ()를 사용하지 않고 메서드를 참조할 경우, 해당 메서드를 바인딩 해야 합니다.
  • 만약 constructor에서 bind해주기 싫다면 onClick={this.handleClick.bind(this)} 를 해줘야한다.

이벤트 핸들러에 인자 전달

1
2
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
  • 위줄은 화살표함수, 아랫줄은 bind를 사용하였다.
  • React 이벤트를 나타내는 e 인자가 ID뒤의 두번째 인자로 전달된다.
  • bind 사용시 e 인자가 자동으로 전달된다.

🎁  조건부 렌더링

  • props로 전달되는 조건에 따라 렌더링 해주는 무상태 컴포넌트 렌더링
1
2
3
4
5
6
7
8
9
10
11
12
// 무상태
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}

const root = ReactDOM.createRoot(document.getElementById('root'));
// Try changing to isLoggedIn={true}:
root.render(<Greeting isLoggedIn={false} />);

상태를 가지지 않아 함수형 컴포넌트로 생성하였다. props로 전달되는 값에 따라 조건부 렌더링 한다.

  • state로 관리하는 유상태 컴포넌트 렌더링
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class LoginControl extends React.Component {
constructor(props) {
super(props);
this.handleLoginClick = this.handleLoginClick.bind(this);
this.handleLogoutClick = this.handleLogoutClick.bind(this);
this.state = {isLoggedIn: false};
}

handleLoginClick() {
this.setState({isLoggedIn: true});
}

handleLogoutClick() {
this.setState({isLoggedIn: false});
}

render() {
const isLoggedIn = this.state.isLoggedIn;
let button;
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
}

return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{button}
</div>
);
}
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<LoginControl />);
  • 컴포넌트가 상태를 가지고 있고 상태를 변경하는 메서드도 지니고 있다. (캡슐화)
  • 상태가 바뀌면 바뀐 상태에 따라 button 엘리먼트의 사용자 정의 컴포넌트가 다르게 할당된다.
  • 상태가 바뀌면 바뀐 상태를 무상태 컴포넌트에게 props로 전달해주고 있다.

🔑  리스트와 Key

리액트에서 반복문은 주로 map 고차함수를 통해 사용한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class NumberList extends React.Component {
constructor(props) {
super(props);
this.state = {
numbers: [1, 2, 3, 4, 5],
};
}

render() {
const listItems = this.state.numbers.map((number) => (
<li key={number.toString()}>{number}</li>
));
return <ul>{listItems}</ul>;
}
}
  • 만약 li에 key props를 주지 않는다면 오류가 발생할 것이다.

Key란?

React가 어떤 항목을 식별하기 위한 문자열이다. 주로 ID를 Key로 사용하지만 ID가 없다면 최후의 수단으로 index를 사용한다.

  • key를 index로 사용하는 것은 권장하지 않는다.
  • key는 주변 배열의 context에서 의미가 있다. map 함수 내부 엘레먼트에 key 넣자.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function ListItem(props) {
const value = props.value;
return <li>{value}</li>; // 여기에 key가 있는 것이 아니다.
}

class NumberList extends React.Component {
constructor(props) {
super(props);
this.state = {
numbers: [1, 2, 3, 4, 5, 6, 7],
};
}

render() {
const listItems = this.state.numbers.map((number) => (
// numbers 배열이 있는 맥락에 key가 있어야 한다.
<ListItem key={number.toString()} value={number} />
));
return <ul>{listItems}</ul>;
}
}
  • key는 배열의 형제 요소 사이끼리는 고유해야하지만 두 개의 다른 배열을 만들 때는 동일한 key 사용해도된다.
  • 만약 컴포넌트에서 key와 동일한 값이 필요하다면 이는 key가 아닌 다른 이름의 props로 명시적으로 전달해야한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
function Blog(props) {
const sidebar = (
<ul>
{props.posts.map((post) =>
<li key={post.id}>
{post.title}
</li>
)}
</ul>
);
const content = props.posts.map((post) =>
<div key={post.id}>
<h3>{post.title}</h3>
<p>{post.content}</p>
</div>
);
return (
<div>
{sidebar}
<hr />
{content}
</div>
);
}

const posts = [
{id: 1, title: 'Hello World', content: 'Welcome to learning React!'},
{id: 2, title: 'Installation', content: 'You can install React from npm.'}
];

const content = posts.map((post) =>
<Post
key={post.id} // X
id={post.id} // O
title={post.title} />
);

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Blog posts={posts} />);
  • 위 예시에서 Post 컴포넌트는 props.id는 읽을 수 있지만, props.key는 읽을 수 없다.

👔  Form Element

React 에서는 사용자의 입력값을 state로 관리하여 React state가 신뢰 가능한 단일 출처(Single Source of Truth)가 된다. 이렇게 React에 의해 값이 제어되는 입력 폼 엘리먼트를 “제어 컴포넌트”라고 한다.

<input type="text"><textarea> 및 <select> 모두 매우 비슷하게 동작한다.

textarea

HTML에서는 텍스트를 자식노드로 정의하지만, React에서는 textarea 태그에 value 어트리뷰트를 사용한다.

1
<textarea value={this.state.value} onChange={this.handleChange} />

select

React에서는 select 태그에서 value 어트리뷰트를 통해 selected를 구현한다.

1
2
3
4
5
6
<select value={this.state.value} onChange={this.handleChange}>
<option value="grapefruit">Grapefruit</option>
<option value="lime">Lime</option>
<option value="coconut">Coconut</option>
<option value="mango">Mango</option>
</select>
  • multiple옵션 허용 시 value 어트리뷰트에 배열 전달 가능
1
<select multiple={true} value={['B', 'C']}>

📈  State 끌어올리기

보통의 경우 state는 렌더링에 그 값을 필요로 하는 컴포넌트에 먼저 추가한다. 그러고 나서 다른 컴포넌트도 그 값이 필요하게 되면 그 값을 그들의 가장 가까운 공통 조상으로 끌어올리고 공통 조상이 자식 컴포넌트들에게 props를 통해 전달해주면 된다. 이 때, 공통 조상의 state를 변경하는 함수도 props에 담아 같이 전달한다.

  • 하향식 데이터 흐름을 활용
  • 어떤 값이 props 또는 state로부터 계산될 수 있다면, 아마도 그 값을 state에 두어서는 안 됩니다.

ex) celsiusValue와 fahrenheitValue를 둘 다 저장하는 대신, 단지 최근에 변경된 temperature와 scale만 저장하면 됩니다. 다른 입력 필드의 값은 항상 그 값들에 기반해서 render() 메서드 안에서 계산될 수 있습니다.

댓글 공유

git fork 제대로 파헤치기

카테고리 git

📌 git fork

fork

git fork는 위 사진 처럼 강사님의 원격 저장소의 레포지토리를 나의 원격 저장소로 복제해오는 것이다. 복제해오는 것이지 연결되는 것은 아니다.

사용하는 이유

  1. 프로젝트를 할 때, 팀장 원격 저장소를 두고 팀원들이 fork하여 각자의 원격 저장소에서 작업을 하여 분업화가 가능하다.

  2. 팀장의 원격 저장소는 배포와 밀접한 관련이 있으므로 직접적으로 팀장 원격 저장소로 코드를 push 하지 못하고 Pull Request를 통해 연결할 수 있으므로 안정적인 개발이 가능하다.

사용 방법 및 주의사항

  1. 팀장의 원격 저장소를 나의 원격 저장소로 fork 해온다.

이 때, main 브랜치만 복사해오는 옵션이 있는데, main만 가져오고 싶다면 체크하고 아니면 해제한다.

  1. fork한 나의 원격 저장소를 나의 로컬로 clone 해온다.

  2. 원격 저장소 목록에 팀장의 원격 저장소를 ‘Leader’라는 이름으로 저장하여 추가한다.

git remote add Leader <팀장의 원격 저장소 URL>

URL이지 git clone 링크와는 다르다.

  1. 만약 팀장이 코드를 변경하고 push하여 팀장의 원격 저장소가 바뀌고 이를 나의 로컬로 가져오고 싶다면 ?

git fetch Leader

이 때, 팀장의 모든 브랜치가 아닌 특정 브랜치만 가져오고 싶다면

git fetch Leader <branch 명>

  1. 가져온 코드를 내 로컬과 병합해줘야 한다면 ?

git pull Leader <branch 명>

처음 팀장 원격 저장소 fork 해와서 나의 로컬의 main 브랜치는 팀장의 원격 저장소의 main 브랜치와 연결이 되어있다. 정확히는 track 추적을 할 수 있다.

하지만, 팀장의 원격 저장소에서 새로운 브랜치를 생성하였고 나의 로컬에서도 해당 브랜치를 가져오고 싶다면 추가적으로 track 명령어를 실행해줘야한다.

  1. 팀장이 브랜치 새로 파고 내 로컬에서 가져오고 싶다면?

git fetch Leader <branch 명>

우선 fetch로 해당 branch를 가져온다.

git checkout -t Leader <branch 명>

내 로컬에서 해당 브랜치로 checkout 하여 브랜치를 옮길 때, -t or track 명령어로 추적한다.

git pull Leader <branch 명>

이제 pull을 받아오면 코드를 가져올 수 있다.

만약 추적을 안하고 가져오게 된다면 변경사항이 없는 상태인데도, pull 해올 때 마다 Merge commit을 하라는 문구가 뜨게될 것 이다…

🏓 소감

수업 중에 강사님 원격 저장소와 연결했는데 문제가 생겼다. 처음 fork 할 때는 강사님 원격 저장소에는 main branch 밖에 없어서 내가 fork 해왔을 때, 연결된 branch는 main branch 뿐이었던 것을 몰랐다.

수업 중 강사님께서 브랜치를 새로 생성하셨고 나도 똑같이 fetch, checkout, pull을 하였는데, 다음과 같은 오류가 발생했다.

“관계 없는 커밋 내역의 병합을 거부합니다”

이러한 오류가 발생한 이유는 새로 생성한 branch를 fetch로 가져오기만 하고 강사님의 원격 저장소의 branch를 추적하지 않기 때문에 발생한 문제였다.

git branch -vv

위 명령어를 사용하면 현재 branch가 어떤 branch를 추적하고 있는지 알 수 있다. 그래서 이미 생성된 branch를 git branch -D <branch 명> 명령어로 삭제한 뒤 위의 방법대로 새롭게 생성한 뒤 추적을 해주었더니 문제가 해결되었다.

앞으로 git 사용하다가 오류가 나오면 무시하지 말고 어떤 오류인지 제대로 파악하고 해결하도록 해야겠다.

댓글 공유

프로젝트를 마치며…

  • 기획을 할 때, 기술적으로 어떻게 풀어나갈지 기술기획을 꼼꼼히 하지 못한 것이 아쉬움

  • 이 때, 기술기획이 제대로 안이루어져서 CBD 라이브러리를 사용하면서 앞으로 겪게될 문제를 개발 과정에 파악하게 되어서 난감하였다. ex) 지역 상태 관리 어려운 점…

  • 노션으로 일정관리와 문서화를 템플릿을 적극 활용하여 일관성있게 해야겠다.

  • 라우터가 App에 종속되어 있는 것이 아니라 render 함수처럼 따로 분리되도록 하고 싶었는데, 그러기 위해서구조를 대폭 수정해야하여 시간적 어려움이 있어 아쉬웠다.

  • 매번 새로운 지식을 얻어감에 즐거움을 느꼈다. 공식문서 읽는 것은 아직 어렵지만, 예제를 따라해보고 이해한 후 공식문서 읽으면 잘 읽히는 것을 알게되었다.

  • 이전 프로젝트 때 했던 실수를 어느정도 개선해나가며 진행할 수 있어서 좋았다. ex) this 바인딩

  • 해결책을 생각해낼 때 과연 이게 진짜 해결책인지, 회피인지 한번 더 생각해보는 안목이 생겼다.

  • 어렵고 막히는 부분이 있을 때 팀원에게 물어보면서 같이 해결해나갈 수 있어 든든하고 즐거웠다.

댓글 공유

1. Software Development Life Cycle (SDLC)

소프트웨어 개발의 전체적인 라이프 사이클을 이해하여 개발 과정에서 발생하는 일을 정략적으로 관리하자.

해야할 일

  1. 요구사항 분석

  2. 설계

  3. 구현

  4. 테스트

  5. 유지보수

위 과정을 진행하기 위한 대표적인 모델은 다음과 같다.

1.1. Waterfall

SDLC 일련의 단계를 모든 팀원들이 참여한다. 심각한 문제라면 다시 이전단계로 돌아가야하겠지만, 작은 문제라면 일단 다음 단계로 넘어간다.(작은 문제를 지금 고친다 하더라도 사이드 이펙트 발생까지 생각하기 어렵다.)

장점

  • 팀을 관리하기가 편하여 대규모 팀에 적합

  • 어떤 단계가 마무리되면 결과가 뚜렷함

1.2. Agile

작은 기능들의 성공을 반복시켜서 프로젝트 완성, 너무 많은 플랜을 지양하고 요구사항에 맞는 코드를 작성한다.

커뮤니케이션 중요(daily scrum, 코드리뷰, 회고)

Daily scrum

매일 15분간 회의를 진행하여 본인이 진행할 기능, 수정사항에 대해 우선순위를 부여하고 순서대로 진행한다.

  • Product Backlog: 제품 전체의 요구사항
  • Sprint Backlog : 프로덕트 기능 하나하나에 대해서 적어 놓음

Sprint

Sprint는 2주 단위로 진행을 한다. 첫날에 planning meeting을 진행하여 2주간 일정을 세세하게 세운다.

ex) 10월 25일 HTML설계, 10월 26일 컴포넌트 구조 설계 …

회고

2주간의 Sprint가 끝나면 회고를 진행한다.

  • 3L 전략(Liked, Learned, Lacked)

Sprint 전 준비사항

  1. UseCase 작성하기

UseCase는 flowchart와 다르니 flowchart화 되고 있다면 재고해봐야한다.

UseCase는 사용자가 어떤 행동을 할 수 있는지를 나열하고 이들간의 관계를 연결한 구조로 작성한다.

flowchart가 아니므로 순서를 따를 필요가 없다.

소감

점점 막히는 부분도 많아지고 이전에 제대로 구조를 설계하지 않고 회피하고 구현에만 집중하다보니 리팩터링을 어떻게 시작해야할지 막막하다…

프로젝트 막바지에 다가오니 의욕도 떨어지는 것 같다. 재충전이 필요하다.

그래도 이틀남았으니 내일 하루는 리팩터링에 시간을 쏟고 목요일은 배포와 회고, 발표준비를 하면 프로젝트를 잘 마무리 할 수 있을 것 같다.

댓글 공유

loco9939

author.bio


author.job