TIL
241221 TIL
GoJay
2024. 12. 21. 23:41
- Udemy <The Web Developer 부트캠프 2024>
- 데이터베이스는 데이터 저장, 관리, 보안 유지 등의 작업을 수행해 주는 도구이다.
- SQL은 테이블 간 관계를 형성하는 데이터 베이스 운영 방식이다. 사전에 데이터베이스 설계 규칙에 근거해서 엄격한 테이블 구조 정의를 하는 게 필요하고, 실제 사용할 때에도 해당 설계를 고려해 관계형으로 데이터를 join해 사용한다.
- SQL을 지원하는 데이터베이스로는 MySQL, PostgreSQL 등이 있다.
- SQL이 아닌 모든 데이터베이스는 NoSQL이다. SQL이 No, 즉 '아니다'라는 뜻인 만큼, 이름부터가 상당히 직관적이다.
- NoSQL은 사전에 엄격한 테이블 구조에 대한 설계를 갖추지 않고, 좀 더 유연하게 사용할 수 있다.
- 보통은 하나의 테이블에 모든 데이터를 밀어넣는 방식으로 사용하기 때문에 상대적으로 사용이 수월하다.
- 대표적인 NoSQL 데이터베이스에는 MongoDB와 Redis 등이 있다.
- MongoDB를 설치하고
mongo
를 터미널에 실행하면 mongo shell을 사용할 수 있다. Node를 REPL 환경에서 인라인으로 실행하는 것과 유사하다. - mongo shell에서 db를 입력하면
test
데이터베이스가 확인된다. MongoDB에서 기본으로 제공해 주는 데이터베이스다. show databases
또는show dbs
를 치면 생성된 데이터베이스를 확인할 수 있다. 예시에선test
데이터베이스가 확인된다.- 데이터베이스를 추가하려면
use {데이터베이스 이름}
을 입력해주면 된다.use animalShelter
라고 입력하면 새로운 데이터베이스가 생성되고,db
를 추가로 입력하면 생성된animalShelter
가 참조되는 걸 확인할 수 있다. use
는git
명령어 중branch
와 유사해서 데이터베이스가 없으면 생성해 주고, 데이터베이스가 있으면 해당 데이터베이스로 이동해 준다.- 하지만,
show dbs
를 했을 때 새롭게 생성한animalShelter
데이터베이스가 보이는 게 아니라 기존test
데이터베이스가 그대로 보이게 된다. 이유는, MongoDB는 새롭게 생성된 데이터베이스에 새로운 데이터가 추가되기 전까지는 해당 데이터베이스에 대한 정보를 보여주지 않기 때문이다. - mongo shell에 표시된 명령어와 응답들을 지워주려면
clear
명령어가 아니라cmd + k
단축키를 사용해야 한다.clear
는 터미널의 명령어고, mongo shell에선 지원하지 않는다. - MongoDB는 BSON이라는 데이터 타입을 사용한다. BSON은 Binary JSON의 줄임말로, JSON 데이터 타입을 이진법 형태로 표현한 형식이다.
- JSON은 Date 타입 등 일부 데이터 타입을 처리할 수 없지만, BSON은 좀 더 많은 데이터 타입을 처리해 줄 수 있고, 또한 메모리 공간도 더 적게 차지한다는 장점이 있다.
use animalShelter
로 새로운 데이터베이스를 만들어줬다면, 데이터베이스에 콜렉션을 추가해 줄 수 있다. 콜렉션은 관계형 데이터베이스에서 하나의 테이블로 치환되는 개념이며, 콜렉션은 다수의 도큐먼트(Document)의 집합으로 이루어진다. 도큐먼트는 데이터베이스에서 관리하는 하나의 데이터를 의미한다.- 사용할 데이터베이스로 이동한 상황에서
db.{콜렉션 이름}.insert
를 사용해 주면 도큐먼트들을 콜렉션에 추가해 줄 수 있다. - 예를 들어,
db.dogs.insert
를 해주면dogs
라는 콜렉션이 없을 경우 새로운 콜렉션을 추가한 다음 할당해준 값을 추가해주며, 콜렉션이 있으면 기존에 생성한 콜렉션에 도큐먼트를 추가해 준다. - MongoDB의 커맨드는 자바스크립트 문법과 유사하기 때문에, 객체로 값을 넣을 때 JSON처럼 키에
""
를 씌워줄 필요가 없다. 일반 자바스크립트 객체처럼 넣어주면 알아서 값을 BSON 타입으로 파싱해서 사용한다. db.dogs.insert([{name: 'coco', age: 3}, {name: 'toto', age: 5}])
형태로 데이터를 넣어주면dogs
콜렉션에 두 개의 도큐먼트를 추가된다. 배열이 아니라 단일 객체로 값을 넣으면 하나의 도큐먼트만 추가된다.- 콜렉션의 데이터를 조회할 땐
db.{콜렉션 이름}.find()
를 사용한다. 매개 변수로 아무것도 전달하지 않으면 콜렉션에 포함된 모든 도큐먼트를 가져온다. db.{콜렉션 이름}.findOne()
을 실행하면 전체 도큐먼트 중 하나의 도큐먼트만을 반환한다.find
메서드 안에는 조건을 넣어줄 수도 있다. 예를 들어,db.dogs.find({ catFriendly: true })
라고 조회하면catFriendly
라는 속성이true
인 도큐먼트만 결과로 반환된다.- 즉, 몽고DB에선
find
메서드의 매개 변수로 조건을 전달해서 원하는 값을 쿼리 할 수 있다. 쿼리 조건에서는 논리 연산, 범위 확인 등 다양한 조건문이 사용될 수 있다. - 도큐먼트의 필드가 nesting 된 객체일 경우, 조건을 걸어서 찾기 위해
{'personality.catFriendly': true}
와 같은 식으로 조건을 사용할 수 있다. 이렇게 사용하면personality: {catFriendly: true}
인 도큐먼트를 찾아준다. 이때, 객체의 점 표기법처럼 사용하는 구문은''
또는""
로 감사서 문자열처럼 인식되게 해 줘야 됨을 주의하자. - 비교 연산을 할 땐
db.dogs.find({age: {$gt: 8}})
와 같은 방식으로 사용할 수 있다.$gt
는greater than
의 줄임말이다. '~보다 더 작은' 값을 찾으려면$lt
(less than)을 사용한다. 크거나 같을 경우는$gte
, 작거나 같을 경우는$lte
를 사용한다.$eq
는 같은 경우,$ne
는 같지 않은 경우를 나타낸다. $in
은 특정 필드가 지정한 배열 안의 값에 해당할 경우를 의미한다.db.dogs.find({breed: {$in: ['Mutt', 'Corgi']}})
를 사용하면breed
가Mutt
이거나Corgi
인 경우인 도큐먼트를 찾는다.$nin
은 'not in'의 줄임말로,$in
의 반대로 동작한다.- 조건은
find({})
의 객체 안에,
로 구분해서 나열해 줄 수 있다. 조건을 나열하면 자연스럽게&&
로 해당하는 경우가 찾아진다. db.dogs.find({$or: [{'personality.catFriendly': true}, {age: {$lte: 2}}]})
처럼 사용하면 배열 안의 두 조건이||
으로 연결된 논리 연산이 된다.$or
,$nor
,$and
,$not
등도 사용 가능하다.- 도큐먼트를 수정할 땐
db.{콜렉션 이름}.updateOne
또는db.{콜렉션 이름}.updateMany
가 사용된다.updateOne
은 조건에 맞는 하나의 도큐먼트에 대해 지정된 값으로 수정해 주고,updateMany
는 2개 이상의 도큐먼트에 같은 수정 사항을 적용해 준다. db.dogs.updateOne({name: 'Charlie'}, {$set: {age: 4}})
으로 작성해 주면 이름이Charlie
인 강아지를 찾아 나이를4
로 업데이트해 준다.- 이때, 업데이트하는 구문에
{$set: /* 업데이트 할 내용 */}
형태로 사용돼야 함을 주의하자. - 업데이트 할 내용은 꼭 하나만 들어가야 하진 않고, 두 개 이상이 들어갈 수도 있다.
{ $set: { age: 4, isAdopted: false } }
처럼 작성하면age
와isAdopted
모두 값이 변경된다. - 만약에 현재 도큐먼트에 없는 속성을
{ $set: { breed: 'poodle' } }
형태로 추가해 주면 도큐먼트에 속성과 값이 추가된다. 업데이트의 개념이 '전체 도큐먼트 중 특정 부분이 바뀐다'로 이해했을 땐 자연스러운 동작이다. db.dogs.updateOne({name: 'Charlie'}, {$set: {age: 6}, $currentDate: {lastModified: true}})
처럼 작성하면$currentDate
를 통해 마지막으로 업데이트된 시점의 타임스탬프를lastModified
라는 필드 이름으로 저장해 둘 수 있다.$currentDate
는 객체에 있는 키가true
로 설정되면 해당 이름을 필드명으로 하는 타임스탬프를 찍는다.- 도큐먼트를 삭제할 땐
db.{콜렉션 이름}.deleteOne
또는db.{콜렉션 이름}.deleteMany
를 사용한다.deleteOne
은 조건에 해당하는 하나의 도큐먼트를 삭제하고,deleteMany
는 조건에 해당하는 모든 도큐먼트를 삭제한다. - 만약에 콜렉션의 모든 도큐먼트를 삭제하고 싶다면
db.{콜렉션 이름}.deleteMany({})
로 사용해 주면 된다. 빈 객체를 사용하면 콜렉션에 있는 도큐먼트가 몇 개든 상관없이 전부 삭제한다.
- React
render
함수,useState
구현하기addEventListener
로 이벤트를 부착할 땐 같은 요소에 같은 타입의 핸들러를 중복해서 붙일 수 있다. 그러면 한 번의 이벤트 발생에 대해서도 여러 번 이벤트 처리 함수가 동작할 수 있게 되고, 성능 상 여러 문제가 발생할 수 있다. 이벤트 핸들러를 등록했으면 중복되지 않게 잘 지워주는 것도 신경 써주자.- Fragment를 나타내는 태그를
documnet
의createDocumentFragment
를 사용하면 만들어줄 수 있다. 실제 DOM에 하위 요소들을 연결해줘야 하는데 각각 개별로 연결해 주면 DOM의 업데이트가 좀 더 자주 발생하지만,document.createDocumentFragment
로 만들어준 태그에 연결해 준 다음 실제 DOM에 붙여주면 한 번만 DOM이 업데이트된다. 이런 상황에선 성능을 생각해서 사용을 고려해 볼 만하다. - 요소의 하위 요소가 leaf node인 텍스트로 예상되는 상황에서
$elem.textContent = textContent
이런 식으로 사용을 주로 했었는데, 가급적const textNode = document.createTextNode
로 DOM 요소를 따로 만들어준 다음에$elem.appendChild(textNode)
식으로 연결해 주자. 조금 더 자연스럽고 안전한 방식이다. - 문자열의
prefix
를 추출해 사용하고 싶을 때str.slice(0, 2)
이렇게 원하는 구간을 잘라서 사용할 수도 있지만,str.startsWith
메서드를 사용할 수도 있다.str.startsWith(2)
와 같이 인자로 원하는 길이를 넘겨주면 그 길이만큼 시작하는 구간의 문자열을 확인해 준다. - 문자열을 인덱싱할 때
slice
도 사용 가능하지만,substring
도 사용 가능하다. 사용 방법은 거의 유사하나,slice
는 음수 인덱스가 활용 가능하다는 점,substring
은 첫 번째 인자가 두 번째 인자보다 클 때 값을 switching 해서 인덱싱 해준다는 점 등 일부 차이가 있긴 하다. 차이를 이해하고 상황에 맞게 잘 사용하자. - 리액트의
useState
로 생성한state
는 값을 직접 변경하면 컴포넌트 간 상태의 참조가 끊기고, 리렌더링도 발생하지 않는다는 특징을 가지고 있다. 이러한 특징을 반영하는 식으로useState
를 만들기 위해서 외부로 공개되는state
는 클로저로 관리되도록 하고, 값의 직접 변경이 허용되지 않게 즉시 실행getter
함수로 감싸는 식으로 구현했다. - 컴포넌트 리렌더링 시
useState
도 결국 재호출 된다. 재호출마다 상태 초기값이 계속 재할당되면 상태는 이전 값을 기억하지 못하고 문제가 생기게 될 것이다. 이런 상황을 고려해서,useState
가 리렌더링으로 인해 재호출 될 시에는 상태 초기화를 방지해 주도록 별도 처리가 필요하다. - 리렌더링을 할 때, 결국
render
함수를 재호출 해야 한다. 그런데, 리렌더링 할 부분이 어딘지 알 수 없기 때문에, 일단은 컴포넌트의 최상단이 되는App
을useState
모듈에서 불러와서 인자로 전달해 줬다. 아마도 이후에 Virtual DOM의 Diffing 알고리즘을 구현할 때 해당 부분을 변경이 필요한 부분을 Virtual DOM에서 파악해서 해당 부분만 가지고 변경을 처리해 주는 식으로 바꾸는 게 필요할 것 같다. - 예외 처리가 필요한 부분이 어딘지 조금 더 꼼꼼하게 확인해서 보강해 보자.
- 모던 자바스크립트 딥다이브 스터디 준비
- 자바스크립트는 싱글 스레드이지만, 런타임인 브라우저와 Node가 멀티 스레드를 제공해 주기 때문에 동시성-병렬성 처리가 가능하다(정확히 말하면, Node와 브라우저 모두 다 메인 스레드는 싱글 스레드지만, 백그라운드에서 멀티 스레드로 동작 가능하다).
- 비동기 작업이 있을 때 해당 작업들이 런타임 프로그램-프로세스의 백그라운드로 가서 멀티 스레드로 처리되는 것이 '병렬 처리'의 대표적인 상황이다. 예를 들어서,
setTimeout
이라는 비동기 함수가 실행되면 두 번째 인자로 전달한ms
만큼 시간을 카운트하는 걸 백그라운드에서 병렬로 처리해 주고(시간을 카운트해 주는 동안에도 메인 스레드인 자바스크립트 엔진은 계속 실행 컨텍스트를 돌리고 있다), 병렬 처리가 완료되면 등록돼 있던 콜백 함수를 태스크 큐로 밀어 넣어준다. - 태스크 큐는 이벤트 루프를 통해 관리되는 작업 대기열이다. 별도 스레드에서 관리되는 게 아닌, 싱글 스레드인 자바스크립트 엔진에서 관리되는 가상의 자료 구조이다.
- 이벤트 루프는 싱글 스레드인 콜 스택과, 멀티 스레드로 동작하는 백그라운드를 계속 반복해서 확인한다. 만약 백그라운드에서 병렬 처리로 돌아가던 작업이 완료되면 해당 작업 완료에 대한 콜백 함수를 태스크 큐로 옮겨주고, 콜 스택의 실행 컨텍스트가 비워질 경우 태스크 큐의 작업들을 선입선출 방식으로 빼내서 콜 스택에 밀어 넣어준다.
- 이렇게, 이벤트 루프가 두 개 이상의 스레드(콜 스택과, 브라우저-Node에서 제공하는 백그라운드)를 계속 왔다 갔다 확인하면서 백그라운드 작업 완료에 대한 처리를 해주는 것은 '동시성 처리'에 해당한다고 볼 수 있다.
- 자바스크립트가 싱글 스레드라는 건 개발자가 제어할 수 있는 스레드가 하나라는 의미이다. 위의 작업들을 처리하는 데 있어서 개발자가 직접 조작 가능한 영역은 콜 스택에 해당하는 부분까지이고, 나머지 이벤트 루프와 브라우저-Node의 백그라운드에서 돌아가는 멀티 스레드 작업들에 대해서는 직접 조작이 어렵다(Node.js 환경을 이용하면 일부 가능하다고 듣긴 했다).
- 또한, 자바스크립트의 비동기 처리는 '이벤트 기반'이라는 특징이 있다. 어떠한 이벤트가 발생하면, 그 이벤트에 대한 작업을 백그라운드로 보내고, 또 어떤 이벤트 발생 시 백그라운드 작업을 태스크 큐로 끌어온다.