Skip to main content

· 3 min read

리액트와 리랜더링 알아보기

바닐라 JS 변경으로 인해 Element를 다시 그린다

const rootElement = document.getElementById('root')

function random() {
const number = Math.floor(Math.random() * (10 - 1) + 1)

const element = `
<button>${number}</button>
`

rootElement.innerHTML = element
}

random()
  • 바닐라 JS에서 요소에 변경이 발생하면 모든 element들이 다시 그려지게 된다

React는 변경된 부분만 다시 그린다

const rootElement = document.getElementById('root')

function random() {
const number = Math.floor(Math.random() * (10 - 1) + 1)

const element = <button>{number}</button>

ReactDOM.render(element, rootElement)
}

random()
  • React에서는 변경된 요소들의 값만 변경이 이루어 진다

모든 요소가 변경되는 것과 특정 요소만 변경되는 것 둘은 장단점이 존재한다

리액트와 리랜더링 알아보기 2

비교 알고리즘

  • 앨리먼트 타입이 바뀌는 경우
    • 이전 앨리먼트는 버리고 새로 그린다
  • 앨리먼트 타입은 같고 props 만 변경된 경우
    • key를 먼저 비교하고, props를 비교해서 변경사항을 반영한다

정리

  • 리액트의 앨리먼트는 불변 객체이다
  • 리액트의 변경 사항 반영은 리액트에게 일임한다
  • 리액트의 비교는 Reconciliation(재조정)이다 비교 연산(알고리즘)을 통해 진행한다
    • 타입이 바뀌면 이전 내용을 버리고 다시 그림
    • 타입이 같으면 key값 비교후 변경된 props에 대한 변경사항만 반영한다
  • Virtual Dom은 리액트가 가상 돔을 가지고 있다가 비교할떄 사용한다

참조

  • 패스트 캠퍼스 온라인 강좌(한 번에 끝내는 React의 모든 것 초격자 패키지 Online)

· 6 min read

동기

  • state나 props가 갱신되면 render() 함수는 새로운 React Element Tree를 반환할 것이다
  • 이때 React는 방금 만들어진 트리에 맞게 가장 효과적으로 UI를 갱신하는 방법을 알아낼 필요가 있다
  • 하나의 트리를 가지고 다른 트리로 변환하기 위한 최소한의 연순 수를 구하는 알고리즘이 필요하다
  • 최첨단의 알고리즘도 n개의 엘리먼트가 있는 트리에 대해 O(n^3)의 복잡도를 가진다(너무 비싼 연산)
  • React는 두가지 가정을 기반하여 O(n) 복잡도의 휴리스틱 알고리즘을 구현했다
  1. 서로 다른 타입의 두 엘리먼트는 서로 다른 트리를 만들어낸다
  2. 개발자가 key prop을 통해, 여러 렌더링 사이에서 어떤 자식 엘리먼트가 변경되지 않아야 할지 표시해 줄 수 있다

비교 알고리즘(Diffing Algorithm)

  • 두 개의 트리를 비교할 때, React는 두 엘리먼트의 루트(root) 엘리먼트부터 비교한다
  • 이후의 동작은 루트 엘리먼트의 타입에 따라 달라진다

엘리먼트 타입이 다른 경우

  • 두 엘리먼트의 타입이 다르면, 이전 트리를 버리고 완전히 새로운 트리를 구축한다(새로 그린다는 의미)
  • 에를 들어 a태그에서 img로, Article에서 Comment로 Button에서 div로 바뀌는 케이스가 모두 트리 전체를 재구축 하는 경우이다
  • 트리를 버릴 때 이전 DOM 노드들은 모두 파괴된다
    • compoenentWillUnmount()가 실행된다
  • 새로운 트리가 만들어질 떄, 새로운 DOM 노드들이 DOM에 삽입된다
    • UNSAFE_componentWIllMount()가 실행된다(Depreciated)
    • compoenentDidMount()가 이어서 실행된다
    • 이전 트리와 연관된 모든 state는 사라진다
<div>
<Counter />
</div>

<span>
<Counter />
</span>
  • 엘리먼트 타입이 변경되었기 때문에 Counter는 사라지고, 새로 다시 마운트가 된다

DOM 엘리먼트 타입이 같은 경우

  • 같은 타입의 두 React DOM 엘리먼트를 비교할 때, React는 두 엘리먼트의 속성을 확인하여, 동일한 내역은 유지하고 변겨된 속성들만 갱신한다
<div className='before' title='stuff' />

<div className='after' title='struff' />
  • React는 새로운 엘리먼트의 내용을 반영하기 위해 현재 컴포넌트 인스턴스의 props를 갱신한다
  1. UNSAFE_componentWillReceiveProps
  2. UNSAFE_componentWillUpdate(Depreciated)
  3. componentDidUpdate

자식에 대한 재귀적 처리

  • DOM 노드의 자식들을 재귀적으로 처리할 때, React는 기본적으로 동시에 두 리스트를 순회하고 차이점이 있으면 변경을 생성한다
<ul>
<li>First</li>
<li>Second</li>
</ul>

<ul>
<li>First</li>
<li>Second</li>
<li>Third</li>
</ul>
  • First, Second의 일치를 확인하고 Third를 생성한다
  • 비효율이 발생하는 경우
<ul>
<li>Duke</li>
<li>Villanova</li>
</ul>

<ul>
<li>Connecticut</li>
<li>Duke</li>
<li>Villanova</li>
</ul>
  • React는 Duke 와 Villanova 종속 트리를 그대로 유지하는 대신 모든 자식을 변경한다(비효율)

Keys

  • 위와 같은 순차적 비교에서 발생하는 비효율을 제거하기 위해 key 속성을 지원한다
  • React는 key를 통해 기존 트리와 이후 트리의 자식들이 일치하는지 확인한다
<ul>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>

<ul>
<li key="2014">Connecticut</li>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>
  • 2014 key를 가진 엘리먼트를 추가하고, 2015, 2016 key를 가진 엘리먼트는 그저 이동만 한다
  • key를 설정할떄는 일반적으로 식별자를 key로 설정한다
<li key={item.id}>{item.name}</li>
  • key는 오로지 형제 사이(같은 속성 태그를 의미하는듯)에서만 유일하면 되고, 전역으로는 유일할 필요가 없다
  • 배열의 인덱스를 key로 사용할 수도 있지만, 재배열 하는 경우 비효율적으로 동작할 것이다
  • 인덱스를 key로 사용할 경우 state 관련 문제가 발생할 수 있다
    • 항목의 순서가 바뀔경우 key 또한 바뀌면, 그 결과로 state가 엉망이 되나가 의도치 않은 결과가 생길 수 있다

· 3 min read

엘리먼트 렌더링 React Document 정리

엘리먼트 렌더링

  • 엘리먼트는 React 앱의 가장 작은 단위이다
const element = <h1>Hello, World</h1>
  • 브라우저 DOM 엘리먼트와 달리 React 엘리먼트는 일반 객체이며(plain object) 쉽게 생성할 수 있다
  • ReactDOM은 React 엘리먼트와 일차하도록 DOM을 업데이트 한다

DOM에 엘리먼트 렌더링하기

  • HTML에 root div가 있다고 가정한다
<div id="root"></div>
  • 이 안에 들어가는 모든 엘리먼트를 React DOM에서 관리하기 때문에 이것을 "루트(root)" DOM노드라고 부른다
  • React로 구현된 애플리케이션은 일반적으로 하나의 루트 DOM 노드가 있다
  • React를 기존 앱에 통합하려는 경우 원하는 만큼 많은 수의 독립된 루트 DOM 노드가 있을 수 있다(이해 못함)
  • React 엘리먼트를 렌더링 하기 위해서는 우선 DOM 엘리먼트를 ReactDOM.createRoot()에 전달한다음 React 엘리먼트를 root.render()에 전달해야 한다
const rootElement = document.getElementById('root')
const element = <h1>Hello, World</h1>
ReactDOM.render(element, rootElement)

렌더링 된 엘리먼트 업데이트하기

  • React 엘리먼트를 불변객체이다
  • 엘리먼트를 생상한 이후에는 해당 엘리먼트의 자식이나 속성을 변경할 수 없다
  • 엘리먼트 영화에서 하나의 프레임과 같이 특정 시점의 UI를 보여준다
  • 위 설명을 바탕으로 하면 UI를 업데이트 하는 유일한 방법은 새로운 엘리먼틑 생성하여 이를 React로 전달하는 것이다
  • Tick Clock Example
const rootElement = document.getElementById('root')

function tick() {
const element = (
<div>
<h1>Hello, World</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
)

ReactDOM.render(element, rootElement)
}

setInterval(tick, 1000)
  • 1초 마다 tick 함수를 호출하여 엘리먼트의 변화를 화면에 보여준다

변경된 부부만 업데이트하기

  • ReactDOM은 해당 엘리먼트와 그 자식 엘리먼트를 이전의 엘리먼트와 비교하고 DOM을 원하는 상태로 만드는데 필요한 경우에만 DOM을 업데이트한다
  • 코드 상에서는 초당 element를 모두 다시 render하지만, 실제 React는 변경된 텍스트 노드만 업데이트 한다

· One min read

SwiftUI Modifiers

Text("This is an apple")
.opacity(0.5)
.border(.black)
  • Text를 그리고 텍스트에 투명도를 설정한 후 그 위에 border를 설정하는 코드이다
  • 위 처럼 Modifier의 순서가 매우 중요하다

Some View Modifiers in SwiftUI

  • Kerning
    • 커닝(kerning)은 글자의 모양 때문에 활자가 고르게 배열되지 않은 것처럼 보이는 것을 글자 모양에 따라 적당한 간격을 두게 조정하여 시각적으로 매끄럽게 보이게 하는 것을 말한다.
  • Bold
  • Font
  • Line Spacing
  • Multiline Text Alignment

· One min read

Instances

  • Dog라는 class를 구현
class Dog {
private var name: String
private var breed: String

init(name: String, breed: String) {
self.name = name
self.breed = breed
}

func toString() {
print("name: \(name), breed: \(breed)")
}
}
  • 스위프트에서 template를 구현하는 방법은 class, struct 2가지가 있다
  • class, struct 은 함수와 data를 가지고 있다

Data

  • 데이터는 position, width, height 와 같은 값들의 집합이다
  • Properties
    • Stored Property
      • var subtotal: Double
    • Stored Propery
      • var tax: Double
    • Computed Property
      • var total: Dobule { return subtotal + tax }

Methods

  • 함수는 User tap, highlight self, trigger action과 같이 특정 이벤트를 정의한다
  • functionality == Methods
Instance
.methodName(parameters)
Text("This is an apple")
.bold()
.kerning(2.0)

정리

  • 앱은 class와 struct의 instance들로 구성된다
  • class 와 struct 은 data와 Methods를 포함하고 있다

· 3 min read
const paint = () => (
<>
<h1>Hi</h1>
<h3>Bye</h3>
</>
)
  • 지난 시간에 만들어 변수로 할당했던 내용을 paint 라는 함수로 변경

함수를 호출하여 element 변수에 할당

const element = (
<>
{paint()}
{paint()}
{paint()}
</>
)
  • element라는 변수에 paint함수를 3번 호출하는 react 요소를 만들어 할당

선언된 element를 rootElement에 render

ReactDOM.render(element, rootElemet)

parameter를 갖는 함수로 변경

const paint = (title, description) => (
<>
<h1>{title}</h1>
<h3>{description}</h3>
</>
)

const element = (
<>
{paint("Name", "Davidyoon")}
{paint("Age", "34")}
{paint("Country", "Korea")}
</>
)
  • Custom Element를 커스텀하게 만들어 함수화 하여 필요한 곳에서 다양하게 활용할 수 있다

Custom Element 만들기

const Paint = ({title, description}) => (
<>
<h1>{title}</h1>
<h3>{description}</h3>
</>
)

const element = (
<>
<Paint title="Hello" description="hello" />
</>
)
  • 함수형 Custom Element를 생성할때는 대문자 이름을 기본으로 한다
    • 소문자로 시작할 시 Warning 과 함께 정상적인 출력이 되지 않음
  • param값을 넣어주는 곳에는 props로 {} 안에 포함되어야 한다
  • HTML 태그 처럼 내부 요소처럼 파라미터를 작성해 주면 된다

Children 넣어보기

const Paint = ({title, description, children}) => (
<>
<h1>{title}</h1>
<h3>{description}</h3>
{children}
</>
)

const element = (
<>
<Paint title="Hello!" description="it's me">
<span>Davidyoon</span>
</Paint>
</>
)

  • Custom Element에는 children을 넣을 수 있다
  • Paint 컴포넌트에 span 태그 안에 커스텀한 children을 넣어서 사용 가능하다

정리

  • Function 은 재사용이 가능한 Element 즉, Custom Element를 만드는데 사용된다
  • Custom Element를 만들 때에는 기존 html tag와 구분하여 대문자로 만들어야 한다(필수)
  • Children은 당연히 개수에 제한이 없다

참조

  • 패스트 캠퍼스 온라인 강좌(한 번에 끝내는 React의 모든 것 초격자 패키지 Online)

· One min read

SwiftUI 기초 강좌 Kodeco Bullseye 앱 만들기

Programming To-Do List

  • Must Haves
    • 프로그램이 가져야 할 필수적 기능들
  • Nice to Haves
    • 필수는 아니지만 있으면 좋은 기능들

Challenge Time

  • Must Haves
    • 설명 label
    • 과녁 label
    • 과녁이 움직일 수 있는 slider
    • 'Hit me' 버튼
    • style the text like Luke's design
    • 버튼을 탭 했을 때 보여지는 팝업
    • 유저 탭 이후 슬라이더 값 표시
    • 랜덤하게 생성되는 과녁 점숨
    • 점수는 보여주는 디스플레이 기능
  • Nice to Haves
    • 재시작 버튼
    • 리더보드
    • 여러 라운드 기능
    • 추가 적인 UI

Views

  • Some Views in SwiftUI
    • Text
      • Read only text
    • Slider
    • Button
    • Vertical Stack
      • VStack
    • Horizontal Stack
      • HStack

· 2 min read

멀티 Element 생성하기

div className="root"에 어떻게 여러개의 element를 넣으 수 있을까 ?

<div id="root">

const rootElement = document.getElementById('root')
const element = (
<div className='box' children= {[
React.createElement('h1', null, 'Hi'),
React.createElement('h3', null, 'Bye')
React.createElement('h5', null, 'Hello')
]}
)
  • children 속성에 여러개의 element를 넣을 수 있기 때문에 createElement를 사용하여 여러 개의 element를 생성할 수 있다
  • 단 위와 같이 생성할 경우 아래와 같은 형태가 된다
<div className='root'><div className='box'> </div></div>
  • 만약 root 밑에 div 태그 없이 h1, h3, h5를 생성하고 싶다면 React.Fragment를 사용한다
const element = ( <React.Fragment children={
[
React.createElement('h1', null, 'Hi'),
React.createElement('h3', null, 'Bye')
React.createElement('h5', null, 'Hello')
]
}
)
  • 위와같이 생성할 경우 아래와 같은 형태가 된다
<div className='root'><h1>Hi</h1><h3>Bye</h3> </h5>Hello</h5></div>
  • React.Fragment는 부모로써 감싸주는 역할을 한다
const element = ( <React.Fragment> {
[
<h1>Hi</h1>,
<h3>Bye</h3>,
<h5>Hello</h5>
]
}
</React.Frament>
)
  • children 속성을 제거하고 JSX 를 사용하여 위와 같이 표현할 수도 있다
const element =
<>
<h1>Hi</h1>
<h3>Bye</h3>
<h5>Hello</h5>
<>
)
  • 위와 같이 는 React.Fragment를 의미한다

참조

  • 패스트 캠퍼스 온라인 강좌(한 번에 끝내는 React의 모든 것 초격자 패키지 Online)

· 3 min read

DIP Dependency Inversion Principle

  • DIP(Dependency Inversion Principle) - 의존관계 역전 원칙
    • 상위 모듈이 하위 모듈에 의존하면 안되고 두 모듈 모두 추상화에 의존하게 만들어야 한다는 원칙
    • 추상화를 진행해서 각각의 모듈에 더 추상화된 것에 의존하게 만들어야 한다는 것
    • 이렇게 코드를 설계해야 재사용에도 유용하고 하나를 수정했을 때 더욱 수정상황이 많이 없는 훌륭한 프로그램을 설계할 수 있다
    • Refactoring에서도 MVP 패턴을 사용하는 이유를 생각하면 된다

BadCase

class AccountManager {
func getAccount() -> String {
return "XX은행 00-0000-0000-00"
}
}

class BankService {
private let accountManager: AccountManager = AccountManager()

func transfer() {
let accountNumber = accountManager.getAccount()
print("1000원을 \(accountNumber)로 송금합니다.")
}
}

let bankService = BankService()
bankService.transfer()
  • AccountManager 객체가 BankService 내부에서 생성되고 사용되고 있어서 테스트에 용이하지 않다.
  • AccountManager의 변화가 생기면 BankService에서도 수정이 필요하게 된다.

NiceCase

protocol AccountProtocol {
func getAccount() -> String
}

class AccountManagerNice: AccountProtocol {
func getAccount() -> String {
return "XX은행 00-0000-0000-00"
}
}

class BankServiceNice {
let accountManager: AccountProtocol

init(accountManager: AccountProtocol) {
self.accountManager = accountManager
}

func transfer() {
print("1000원을 \(accountManager.getAccount())로 송금합니다.")
}
}

let accountManagerNice = AccountManagerNice()
let bankServiceNice = BankServiceNice(accountManager: accountManagerNice)
bankServiceNice.transfer()
  • protocol을 활용하여 AccountManager를 독립적으로 구현한다
  • BankService 생성자에 AccountProtocol을 사용하여 AccountManager를 주입시켜 처리한다
  • 이렇게 구한하게 되면 테스트 케이스 작성 시 Mock으로 간편하게 AccountManager를 만들어 테스트를 용이하게 처리할 수 있다.

참조

· 4 min read

Git Command

Git Add 취소

  • git reset HEAD [fileName]
    • staged 되어 있는 파일을 unstaged 상태로 변경한다
    • fileName 을 넣지 않으면 staged 되어 있는 모든 파일이 unstaged 상태로 변경된다

Git Alias

  • git config --global alias.co checkout
    • git checkout 명령어를 git co 형태로 사용할 수 있도록 설정
  • git config --global alias.br branch
    • git branch 명령어를 git br 형태로 사용할 수 있도록 설정
  • git config --global alias.ci commit
    • git commit 명령어를 git ci 형태로 사용할 수 있도록 설정
  • git config --global alias.st status
    • git status 명령어를 git st 형태로 사용할 수 있도록 설정
  • git config --global alias.unstage 'reset HEAD --'
    • git reset HEAD로 add 취소 명령어를 git unstage 명령어로 설정
  • git config --global alias.last 'log -1 HEAD'
    • 최근 커밋 하나의 로그만 출력하는 명령어를 git last로 설정

Git commit 메시지 변경

  • git commit --amend
    • 가장 최근 커밋 메시지를 변경

Git commit 취소

  • git reset --soft HEAD^
    • 마지막 commit을 취소하고 마지막 커밋된 파일들을 staged 상태로 변경(Add가 되어 있는 상태)
  • git reset --mixed HEAD^
    • 마지막 commit을 취소하고 마지막 커밋된 파일들을 unstaged 상태로 변경(Add 되어 있지 않은 상태)
    • git reset HEAD^ 커맨드도 동일한 명령
  • git reset HEAD~2
    • 마지막 2개의 commit을 취소
  • git reset --hard HEAD^
    • 마지막 commit을 취소하고 변경된 내역들을 모두 제거한다
    • 매우 위험한 명령(주의), 작업 했던 내용이 모두 사라진다

Git reflog

  • git reflog
    • branch 와 HEAD가 지난 몇 달 동안에 가르켰던 커밋 목록을 확인
    • rebase, merge 작업 시 유용

Git 특정 commit으로 되돌리기

  • git reset HEAD@{number}
  • git reset [commit id]

Git push remote

  • git push origin branchName
    • 해당 브랜치를 origin 저장소에 push
  • git push origin branchName -f
    • 해당 브랜치를 origin 저장소에 강제 push
    • 매우 위험
    • git push origin +branchName 과 동일

Git clean

  • git clean -f
    • untracked 파일들을 제거한다
    • default 값은 강제 삭제가 아니기 때문에 -f 옵션이 필요
    • git clean -f
  • git clean -f -d
    • untracked 디렉토리까지 제거
  • git clean -f -d -x
    • untracked 파일, 디렉토리, 무시된 파일까지 제거
  • git clean -n
    • 가상으로 어떤 파일들이 지워질지 먼저 보여줌

참조