상권's

TIL 34 (SQL % noSQL) (2021.11.11) 본문

~2022 작성 글/TIL

TIL 34 (SQL % noSQL) (2021.11.11)

라마치 2021. 11. 11. 20:35
-오늘의 코플릿 2021.11.11-
radixSort
문제
정수를 요소로 갖는 배열을 입력받아 오름차순으로 정렬하여 리턴해야 합니다.
주의사항
기수 정렬을 구현해야 합니다.arr.sort 사용은 금지됩니다.
입력으로 주어진 배열은 중첩되지 않은 1차원 배열입니다.
힌트
기수 정렬(radix sort)은 내부적으로 계수 정렬(counting sort)을 사용합니다.계수 정렬을 먼저 학습하고, 어떤 경우에 기수 정렬을 사용하는지 학습하도록 합니다.
// DP 에서 학습했던 거처럼 진행을 하면 안될까?
// arr에서 maxNum을 구한다.
// maxNum을 구해서 해당 수만큼 0으로 채워진 배열을 만든다. zeroArr
// arr를 돌면서, 해당 숫자의 인덱스 부분을 1로 채워준다.
// zeroArr에서 0이 아닌 요소들을 배열로 만들어서 리턴한다.
계수정렬(counting sort)이란, 숫자를 기준으로 갯수를 세어서 키 값의 위치를 결정 
컴퓨터 과학에서 카운팅 정렬은 작은 양의 정수인 키에 따라 개체 컬렉션을 정렬하는 알고리즘입니다. 
즉, 정수 정렬 알고리즘입니다. 고유 한 키 값을 소유 한 개체 수를 계산하고 해당 개수에 접두사 합계를 적용하여 
출력 시퀀스에서 각 키 값의 위치를 ​​결정하는 방식으로 작동합니다. 출처 위키백과

첫 수도코드 => 먼저, 계수정렬에 대해서 알아보니, 정렬을 해야하는 배열 요소들의 갯수를 세고, 해당 요소들이 위치할 인덱스(해당 요소보다 작은 요소들의 갯수 누적합)를 구해서 그것을 기반으로 정렬을 실행하는 것으로 이해를 했습니다.

 

단순하게 생각해서 굳이 누적합을 안 구하더라도 요소의 갯수를 세고, 해당 갯수만큼 요소를 리턴할 배열에다가 PUSH한 다면, 정렬된 배열이 나오지 않을까 생각을 했습니다.

 

그래서 배열에서 가장 큰 수를 구하고, 그 수만큼의 길이를 가진 배열을 만듭니다. 정렬해야되는 요소의 위치에다가 1씩 올려주게 되면, 인덱스가 정렬을 해야하는 요소가 되며, 그 인덱스의 요소가 갯수가 될 수 있을 거라 판단해서 코드를 아래와 같이 구현했습니다.

function radixSort(arr) {
    let result = []
    let maxNum = Math.max(...arr)
    let zeroArr = new Array(maxNum + 1).fill(0)
  
    while(arr.length) {
      let current = arr.shift()
      zeroArr[current] += 1
    }
    for( let n = 0; n < zeroArr.length; n++) {
        if(zeroArr[n] !== 0) {
            result.push(n)
        }
    }
    return result; 
}

근데 10만 이상의 수를 정렬을 해야한다면, 선제적으로 만들어야 할 배열의 크기가 너무 커지니 이에 따라 시간 복잡도가 증가해 테스트 실행시간이 초과하는 거 같습니다.

계수 정렬은 놀랍게도 작은 숫자에서는 복잡도가 O(n + k) 입니다.
n+k는 처음 보죠? k가 정렬할 수들 중에 가장 큰 값을 의미하는데요. 만약 k가 n보다 작은 수이면 O(n)이 되지만, k가 n보다 매우 큰 수이면 O(무한)이 될 수도 있습니다.
예를 들어 10개의 숫자를 정렬하는 데, 가장 큰 숫자가 100일 경우, O(n^2)이 됩니다. 100(k)은 10(n)의 제곱이니까요. 1000이면 O(n^3)이 되죠. 즉 정렬할 수들의 최대값에 영향을 받는 알고리즘이라고 볼 수 있습니다.

출처

그렇다면 객체로 만들어서 정렬을 해야되는 수만 객체에 키로 넣고, 해당 갯수를 값으로 넣으면 시간을 줄일 수 있지 않을까 했는데, 이 역시 시간초과로 테스트 통과를 못했습니다.

 

2021.11.12 오늘 레프런스 코드를 이해했습니다.

 

=> 블로그를 수정하면서 깨달은 건데, 문제의 이름 자체가 기수정렬이고, 기수정렬은 내부적으로 계수정렬을 이용한다는 주의사항이 있었습니다. 그래서 계수정렬을 먼저 알아봤었는데, 이게 이상한 곳에 꽂혀서 문제의 의도를 파악하지 못하고 계수정렬에만 집중했었던 거 같습니다.. 차분하게 문제를 읽고 의도를 파악했어야 하는데,  빨리 풀고 싶다는 생각과, 실행시간 초과를 것에서 마음이 급해진 것이 코드로 구현하지 못한 폐착인 거 같습니다.

 

레프런스에서는 자릿수 별로 정리(기수정렬) 하면서 계수정렬을 이용해 누적값으로 배열을 정리하고 있습니다.

function getMax(arr) {
  return arr.reduce((max, item) => {
    if (item > max) return item;
    return max;
  }, 0);
}

function countingSort(arr, radix) {
  const N = arr.length;
  const output = Array(N).fill(0); // 정렬한 요소들을 담을 곳!
  const count = Array(10).fill(0); // 자릿수 별로 누적값을 계산하기 위한 곳!

  // 현재 자리수를 기준으로 0~9의 개수를 센다.
  // radix 가 1이면 1의 자릿수만 생각한다. => 아래의 코드를 보게 되면 초기값으로 radix를 1로 진행하고, 돌 때마다 10을 곱해준다!
  // radix가 10이면 10의 자릿수만 생각한다. => 배열 중에 가장 큰 수가 10의 자리이면 두 번 돌기 때문에
  // 처음에는 1의 자릿수로 0 ~ 9까지 만든다. radix가 10이 되면 10의 자릿수로 0 ~ 9까지 만든다.
  // 자릿수 별로 생각을 하기 때문에, count는 0 ~ 9까지만 나온다.
  arr.forEach((item) => {
    const idx = Math.floor(item / radix) % 10;
    count[idx]++;
  });

  // count[i]가 i까지의 누적 개수가 되도록 만든다.
  count.reduce((totalNum, num, idx) => {
    count[idx] = totalNum + num;
    return totalNum + num;
  });

  // 아래 속성이 유지되도록 하기 위해 배열을 거꾸로 순회한다.
  //  1. 가장 큰 값을 먼저 본다. 자릿수별로 누적값(자릿수 별로 정렬된 배열의 위치)부터 1씩 뺴면서 넣어준다!
  //  2. 가장 큰 값을 가장 뒤에 놓는다.(누적값에 해당하는 부분에 놓는다.) <= 레프런스에 적힌 건데 무슨 말인지 이해가 안되서
  // 아래에 내 말로 적어놈

  // 배열을 뒤에서 부터 순회를 해야지, 누적값에 맞춰서 요소들을 놓을 수 있다.
  // 예시) 1의 자리가 8인 수가 3개이고, 누적값이 배열의 마지막일때,
  // 뒤에서부터 3개가 1의 자리가 8인 수가 들어가야 한다.
  // 그렇기 때문에 마지막부터 채우면서 1의 자리가 8인 수들을 넣어간다.
  // 그리고 1회전 때 1의 자릿수로 따지게 되면 7보다 9가 일단의 더 뒤에 가게 된다.
  // 그러면 누적값에서 하나씩 빼면서 들어가기 때문에 10의 자릿수가 0인 애들 중에
  // 가장 먼저 누적값에 들어가야한다.
  let i = N - 1;
  while (i >= 0) {
    const idx = Math.floor(arr[i] / radix) % 10;
    // 70번째 코드로 배열 요소들을 1의 자릿수로 만들었던 걸, 배열의 뒤에서부터 요소들 별로 다시 확인을 한다.
    // count에서 해당 값의 누적값을 찾게 되면, 그게 정렬된 배열에서 위치하는 번호이니깐, output에 넣으려면 1을 빼줘야 한다.
    // 만약에 count[idx]가 3인 수는, 정렬된 배열에서 3번째에 위치를 해야되니깐 인덱스로 보면 2가 된다!

    // 최초의 값은 1의 자릿수가 같은 요소들 중에서 <= 이거도 레프런스에 적혀있던 건데 사실 이해가 잘 안된다.
    // count[idx]: 현재 radix의 idx까지 누적 개수
    // count[idx]개만큼 있으므로, 인덱스는 count[idx] - 1
    output[count[idx] - 1] = arr[i];
    count[idx] -= 1;
    i--; 
    // 해당 자릿수의 누적값에서 하나씩 빼게 되면, 해당 수가 들어갈 수 있는 위치를 찾을 수 있다.
    // count에서 7의 누적값이 2인데, 9의 누적값이 5라고 치면,
    // 1의 자릿수가 9인 요소들이 3개라는 말이다. 그래서 1의 자릿수가 9인 수 하나가 5번째 자리에 위치하게 되면
    // 그 다음 1의 자릿수가 9인 수는 4번째 자리, 그 다음 1의 자릿수가 9인 수는 3번째에 위치를 해야한다.
    // 7 7 9 9 9 식으로 정렬이 될 수 있다. 누적값은 배열의 길이랑 같다. 
  }
  // 처음에 입력받은 배열이 [9, 15, 10, 19, 29, 7]이라고 할 때, radix는 1로 먼저 첫번째 자릿수를 대상으로 배열을 정리한다.
  // parseInt(max / radix)은 29로 '> 0' 에 해당한다
  // 1회전 했을 때 나오는 값은 [10, 15, 7, 9, 19, 29] 가 된다. 제일 큰 수의 자릿수는 10의 자릿수기 때문에 한 번 더 돌아야 한다.
  // 1회전을 돌고 나서 radix는 10을 바뀌게 되었고 parseInt(max / radix)는 2가 된다. '> 0'에 해당한다.
  // 1회전에서 1의 자릿수를 확인했으니, 2번째에는 10의 자릿수를 확인한다.
  // 그래서 [7, 9, 10, 15, 19, 29] 순으로 배열이 정리되게 된다.
  // 2회전을 돌면 radix는 100이 되고 parseInt(max / radix)는 0이 되기 때문에 탈출!
  return output;
}


function radixSort(arr) {
  // 음수는 left에 넣으면서 양수로 바꿔준다
  // 양수는 right에 넣는다.
  // 이후에 left는 reverse하고 -1을 곱해줄 거임. 그러고 right를 concat 하면 정렬이 된다.
  let left = [];
  let right = [];
  arr.forEach((item) => {
    if (item >= 0) right.push(item);
    else left.push(item * -1);
  });

  let max = getMax(left);
  let radix = 1;
  while (parseInt(max / radix) > 0) {
    left = countingSort(left, radix);
    radix *= 10;
  }

  max = getMax(right);
  radix = 1;
  while (parseInt(max / radix) > 0) {
    // 가장 큰 수가 몇 자리 수 인지 여부를 확인한다.
    // 예를 들어서 29라고 치면, radix가 1일때는 parseInt(29 / 1) => 29
    // radix가 10일때는 parseInt(29 / 10) => 2
    // 그리고 한 번 더 돌게 되면은 parseInt(29 / 100) => 0 이기 때문에 radix가 10일 때의 배열로 정해지게 된다.
    right = countingSort(right, radix);
    radix *= 10;
  }

  return left
    .reverse()
    .map((item) => item * -1)
    .concat(right);
    // 음수부분은 128번째 줄에서 -1을 곱했기 때문에 
    // 만약에 -11과 -29가 있으면, 
    // 11, 29가 된다
    // 그러고 나서 정렬을 하고
    // reverse로 반전을 시켜놓고
    // -1을 곱해주면 음수가 되고 정렬이 된다! 
}

데이터베이스의 필요성

데이터베이스를 사용하지 않을 때, 데이터를 저장하는 방법

 

1. In-Memory

먼저 JavaScript에서 데이터를 다룰 때에는 프로그램이 실행될 때에만 존재하는 데이터가 있습니다.

JavaScript에서 변수를 만들어 저장한 경우, 프로그램이 종료될 때 해당 프로그램이 사용하던 데이터도 사라집니다. 이 말은 변수 등에 저장한 데이터가 프로그램의 실행에 의존한다는 말입니다. 예기치 못한 상황으로부터 데이터를 보호할 수 없고, 프로그램이 종료된 상태라면 데이터를 원하는 시간에 받아올 수 없으며, 데이터의 수명이 프로그램의 수명에 의존하게 됩니다.

 

2. File I/O

파일을 읽는 방식으로 작동하는 형태를 말합니다. 엑셀 시트나 CSV 같은 파일의 형태는 In-Memory에 비해 데이터를 저장하는 방식으로 적절해 보입니다. 그러나 한계가 분명히 존재합니다.

  • 데이터가 필요할 때마다 전체 파일을 매번 읽어야 합니다. 파일의 크기가 커질수록 이 작업은 버겁고, 비효율적이어서 File I/O 방식의 큰 단점입니다.
  • 파일이 손상되거나 여러 개의 파일들을 동시에 다뤄야 하거나 하는 등 복잡하고 데이터량이 많아질수록 데이터를 불러들이는 작업이 점점 힘들어집니다.

반면에 관계형 데이터베이스에서는 하나의 CSV 파일이나 엑셀 시트를 한 개의 테이블로 저장할 수 있습니다. 한 번에 여러 개의 테이블을 가질 수 있기 때문에 SQL 을 활용해 데이터를 불러오기 수월합니다. 또한, 엑셀 시트와 CSV 파일 등처럼 특정 형태의 파일은 대용량의 데이터를 저장하기 위한 목적이 아닙니다.

 

SQL 소개

하나의 언어인 Structured Query Language (SQL)은 데이터베이스 언어로, 주로 관계형 데이터베이스에서 사용합니다. 

 

SQL이란 데이터베이스 용 프로그래밍 언어입니다. 데이터베이스에 쿼리를 보내 원하는 데이터를 가져오거나 삽입할 수 있습니다. 그리고 이름에서 유추할 수 있듯이, SQL은 (relation 이라고도 불리는) 데이터가 구조화된(structured) 테이블을 사용하는 데이터베이스에서 활용할 수 있습니다.

 

SQL을 사용할 수 있는 데이터베이스와 달리, 데이터의 구조가 고정되어 있지 않은 데이터베이스를 NoSQL이라고 합니다. 관계형 데이터베이스와는 달리, 테이블을 사용하지 않고 데이터를 다른 형태로 저장합니다.

 

이처럼 데이터베이스 세계에서 SQL은 데이터베이스 종류를 SQL이라는 언어 단위로 분류할 정도로 중요한 자리를 차지하고 있습니다. 그리고 SQL을 사용하기 위해서는 데이터가 구조가 고정되어 있어야 합니다.

 

SQL은 구조화된 쿼리 언어입니다.

 

* 쿼리란 ?

쿼리(query)란 무엇일까요? 쿼리는 '질의문' 이라는 뜻을 가지고 있습니다. 예를 들면 검색할 때 입력하는 검색어가 일종의 쿼리입니다. 검색을 할 때, 기존에 존재하는 데이터를 검색어로 필터링합니다. 따라서, 쿼리는 저장되어 있는 데이터를 필터하기 위한 질의문으로도 볼 수 있습니다.

 


트랜잭션

 

트랜잭션이란 여러 개의 작업을 하나로 묶은 실행 유닛입니다. 각 트랜잭션은 하나의 특정 작업으로 시작을 해 묶여 있는 모든 작업을 다 완료해야 정상적으로 종료합니다.

 

만약 하나의 트랜잭션에 속해있는 여러 작업 중에서 단 하나의 작업이라도 실패하면, 이 트랜잭션에 속한 모든 작업을 실패한 것으로 판단합니다. 다시 말해 작업이 하나라도 실패를 하게 되면 트랜잭션도 실패이고, 모든 작업이 성공적이면 트랜잭션 또한 성공입니다.

 

성공 또는 실패 라는 두 개의 결과만 존재하는 트랜잭션은, 미완료된 작업 없이 모든 작업을 성공해야 합니다.

 

데이터베이스 트랜잭션은 ACID라는 특성을 가지고 있습니다.

 

ACID

ACID는 데이터베이스 내에서 일어나는 하나의 트랜잭션(transaction)의 안전성을 보장하기 위해 필요한 성질입니다.

  • Atomicity
  • Consistency
  • Isolation
  • Durability

Atomicity(원자성)

원자성은 하나의 트랜잭션에 속해있는 모든 작업이 전부 성공하거나 전부 실패해서 결과를 예측할 수 있어야 합니다.

하나의 트랜잭션 내부에 있는 작업들은 일부는 성공하고 일부는 실패하는 경우가 없이, 모두가 성공하거나, 모두가 실패해야 합니다. 성공을 하게 되면 트랜잭션이 원하는 결과가 도출되어야 하며, 실패했을 경우에는 트랜잭션 이전의 상태로 돌아가야 합니다.

 

Consistency(일관성)

두 번째는 데이터베이스의 상태가 일관되어야 한다는 성질입니다. 하나의 트랜잭션 이전과 이후, 데이터베이스의 상태는 이전과 같이 유효해야 합니다. 다시 말해, 트랜잭션이 일어난 이후의 데이터베이스는 데이터베이스의 제약이나 규칙을 만족해야 한다는 뜻입니다.

 

데이터베이스의 유효한 상태는 다를 수 있지만, 데이터의 상태에 대한 일관성은 변하지 않아야 합니다.

 

Isolation(격리성, 고립성)

Isolation(격리성) 은 모든 트랜잭션은 다른 트랜잭션으로부터 독립되어야 한다 는 뜻입니다.

 

Durability(지속성)

Durability(지속성)는 하나의 트랜잭션이 성공적으로 수행되었다면, 해당 트랜잭션에 대한 로그가 남아야 합니다. 만약 런타임 오류나 시스템 오류가 발생하더라도, 해당 기록은 영구적이어야 한다는 뜻입니다.


SQL(구조화 쿼리 언어) vs. NoSQL(비구조화 쿼리 언어)

 

데이터베이스는 크게 관계형 데이터베이스비관계형 데이터베이스로 구분합니다. 관계형 데이터베이스는 SQL을 기반으로 하고, 비관계형 데이터베이스는 NoSQL로 데이터를 다룹니다. SQL과 NoSQL은 만들어진 방식, 저장하는 정보의 종류, 그리고 저장하는 방법 등에 차이가 있습니다.

 

관계형 데이터베이스에서는 테이블의 구조와 데이터 타입 등을 사전에 정의하고, 테이블에 정의된 내용에 알맞은 형태의 데이터만 삽입할 수 있습니다.

 

관계형 데이터베이스는 행(row)과 열(column)로 구성된 테이블에 데이터를 저장합니다. 각 열은 하나의 속성에 대한 정보를 저장하고, 행에는 각 열의 데이터 형식에 맞는 데이터가 저장됩니다. 특정한 형식을 지키기 때문에, 데이터를 정확히 입력했다면 데이터를 사용할 때에는 매우 수월합니다.

 

관계형 데이터베이스에서는 SQL을 활용해 원하는 정보를 쿼리할 수 있습니다. 이 말은 관계형 데이터베이스에서는 스키마가 뚜렷하게 보인다는 말과 같습니다. 다시 말해, 관계형 데이터베이스에서는 테이블 간의 관계를 직관적으로 파악할 수 있습니다.

대표적인 관계형 데이터베이스는 MySQL, Oracle, SQLite, PostgresSQL, MariaDB 등이 있습니다.

NoSQL은 SQL 앞에 붙은 'No'에서 알 수 있듯이, 주로 데이터가 고정되어 있지 않은 데이터베이스를 가리킵니다.

 

NoSQL이 SQL과 반대되는 개념처럼 사용된다고 해서, NoSQL에 스키마가 반드시 없는 것은 아닙니다. 관계형 데이터베이스에서는 데이터를 입력할 때 스키마에 맞게 입력해야 하는 반면, NoSQL에서는 데이터를 읽어올 때 스키마에 따라 데이터를 읽어 옵니다. 이런 방식을 'schema on read'라고도 합니다. 읽어올 때에만 데이터 스키마가 사용된다고 하여, 데이터를 쓸 때 정해진 방식이 없다는 의미는 아닙니다. 데이터를 입력하는 방식에 따라, 데이터를 읽어올 때 영향을 미칩니다.

대표적인 NoSQL은 몽고DB, Casandra 등이 있습니다.


SQL 기반의 데이터베이스와 NoSQL 데이터베이스의 차이점

 

데이터 저장(Storage)

  • NoSQL은 key-value, document, wide-column, graph 등의 방식으로 데이터를 저장합니다.
  • 관계형 데이터베이스는 SQL을 이용해서 데이터를 테이블에 저장합니다. 미리 작성된 스키마를 기반으로 정해진 형식에 맞게 데이터를 저장해야 합니다.

스키마(Schema)

  • SQL을 사용하려면, 고정된 형식의 스키마가 필요합니다. 다시 말해, 처리하려는 데이터 속성별로 열(column)에 대한 정보를 미리 정해두어야 합니다. 스키마는 나중에 변경할 수 있지만, 이 경우 데이터베이스 전체를 수정하거나 오프라인(down-time)으로 전환할 필요가 있습니다.
  • NoSQL은 관계형 데이터베이스보다 동적으로 스키마의 형태를 관리할 수 있습니다. 행을 추가할 때 즉시 새로운 열을 추가할 수 있고, 개별 속성에 대해서 모든 열에 대한 데이터를 반드시 입력하지 않아도 됩니다.

쿼리(Querying)

  • 쿼리는 데이터베이스에 대해서 정보를 요청하는 질의문입니다. 관계형 데이터베이스는 테이블의 형식과 테이블 간의 관계에 맞춰 데이터를 요청해야 합니다. 그래서 정보를 요청할 때, SQL과 같이 구조화된 쿼리 언어를 사용합니다.
  • 비관계형 데이터베이스의 쿼리는 데이터 그룹 자체를 조회하는 것에 초점을 두고 있습니다. 그래서 구조화되지 않은 쿼리 언어로도 데이터 요청이 가능합니다. UnQL(UnStructured Query Language)이라고 말하기도 합니다.

확장성(Scalability)

  • 일반적으로 SQL 기반의 관계형 데이터베이스는 수직적으로 확장합니다. 높은 메모리, CPU를 사용하는 확장이라고도 합니다. 데이터베이스가 구축된 하드웨어의 성능을 많이 이용하기 때문에 비용이 많이 듭니다. 여러 서버에 걸쳐서 데이터베이스의 관계를 정의할 수 있지만, 매우 복잡하고 시간이 많이 소모됩니다.
  • NoSQL로 구성된 데이터베이스는 수평적으로 확장합니다. 보다 값싼 서버 증설, 또는 클라우드 서비스 이용하는 확장이라고도 합니다. NoSQL 데이터베이스를 위한 서버를 추가적으로 구축하면, 많은 트래픽을 보다 편리하게 처리할 수 있습니다. 그리고 저렴한 범용 하드웨어나 클라우드 기반의 인스턴스에 NoSQL 데이터베이스를 호스팅 할 수 있어서, 수직적 확장보다 상대적으로 비용이 저렴합니다.

SQL 기반의 관계형 데이터베이스를 사용하는 케이스

 

1. 데이터베이스의 ACID 성질을 준수해야 하는 경우

SQL을 사용하면 데이터베이스와 상호 작용하는 방식을 정확하게 규정할 수 있기 때문에, 데이터베이스에서 데이터를 처리할 때 발생할 수 있는 예외적인 상황을 줄이고, 데이터베이스의 무결성을 보호할 수 있습니다.

 

전자 상거래를 비롯한 모든 금융 서비스를 위한 소프트웨어 개발 에서는 반드시 데이터베이스의 ACID 성질을 준수해야 합니다. 그래서 이런 경우에는 일반적으로 SQL을 이용한 관계형 데이터베이스를 사용합니다.

 

2. 소프트웨어에 사용되는 데이터가 구조적이고 일관적인 경우

소프트웨어(프로젝트)의 규모가 많은 서버를 필요로 하지 않고 일관된 데이터를 사용하는 경우, 관계형 데이터베이스를 사용하는 경우가 많습니다. 다양한 데이터 유형과 높은 트래픽을 지원하도록 설계된 NoSQL 데이터베이스를 사용해야만 하는 이유가 없기 때문입니다.

 

NoSQL 기반의 비관계형 데이터베이스를 사용하는 케이스

  • 데이터의 구조가 거의 또는 전혀 없는 대용량의 데이터를 저장하는 경우

대부분의 NoSQL 데이터베이스는 저장할 수 있는 데이터의 유형에 제한이 없습니다. 필요에 따라, 언제든지 데이터의 새 유형을 추가할 수 있습니다. 소프트웨어 개발에 정형화되지 않은 많은 양의 데이터가 필요한 경우, NoSQL을 적용하는 것이 더 효율적일 수 있습니다.

 

  • 클라우드 컴퓨팅 및 저장 공간을 최대한 활용하는 경우

클라우드 기반으로 데이터베이스 저장소를 구축하면, 저렴한 비용의 솔루션을 제공받을 수 있습니다. 소프트웨어에 데이터베이스의 확장성이 중요하다면, 별다른 번거로움 없이 확장할 수 있는 NoSQL 데이터베이스를 사용하는 것이 좋습니다.

  • 빠르게 서비스를 구축하는 과정에서 데이터 구조를 자주 업데이트하는 경우

NoSQL 데이터베이스의 경우 스키마를 미리 준비할 필요가 없기 때문에 빠르게 개발하는 과정에 매우 유리합니다. 시장에 빠르게 프로토타입을 출시해야 하는 경우가 이에 해당합니다. 또한 소프트웨어 버전별로 많은 다운타임(데이터베이스 서버를 오프라인으로 전환하여 데이터 처리를 진행하는 작업 시간) 없이 데이터 구조를 자주 업데이트해야 하는 경우, 스키마를 매번 수정해야 하는 관계형 데이터베이스 보다 NoSQL 기반의 비관계형 데이터베이스를 사용하는 게 더 적합합니다.


HTML Sanitizer API 출처

sanitize 단어는 '불쾌한 부분을 제거하다', '살균하다'와 같은 뜻을 지녔습니다.

단어의 뜻대로 외부에서 프로그램에 오류를 발생시킬 수 있는 것이 들어온다면 청소해주는 기능입니다.

 

The HTML Sanitizer API allow developers to take untrusted strings of HTML and Document or DocumentFragment objects, and sanitize them for safe insertion into a document’s DOM.

 

HTML Sanitizer API를 통해 개발자는 HTML 및 Document 또는 DocumentFragment 개체의 신뢰할 수 없는 문자열을 가져와서 Document DOM에 안전하게 삽입할 수 있도록 제거해줍니다./ 청소해줍니다.

 

=> 외부에서 정보가 입력되는 웹 앱을 개발했을 경우, 오류를 발생시킬 수 있는 문자열을 입력 받을 수도 있습니다. 그래서 항상 외부에서 입력되는 건 안전하지 않다고 간주를 하고 프로그래밍을 해야합니다.

 

The script element is disallowed by the default sanitizer so the alert is removed.

const unsanitized_string = "abc <script>alert(1)</script> def";  // Unsanitized string of HTML

const sanitizer = new Sanitizer();  // Default sanitizer;

// Get the Element with id "target" and set it with the sanitized string.
const target = document.getElementById("target");
target.setHTML(unsanitized_string, sanitizer);

console.log(target.innerHTML);
// "abc  def"

 

Comments