0 quality - utility

source: categories/study/youtube-developers_quality/quality0.md

1. array filter

1.1 filter 메소드 - 실무에서 어떤 경우에 사용할까?



const userList = [
   {userId: "1", userName: "James", phone: "010-1111-1111", address: "Seoul"},
   {userId: "2", userName: "Jeremy", phone: "010-1111-1112", address: "Jeju"},
   {userId: "3", userName: "Justin", phone: "010-1111-1113", address: "Seoul"},
]

let selectedUserInfo = {};
for (let i = 0; i < userList.length; i++) {
   if (userList[i].userId === "2") {
      selectedUserInfo = userList[i]
   }
}
console.log(selectedUserInfo); // {userId: '2', userName: 'Jeremy', phone: '010-1111-1112', address: 'Jeju'}

let selectedUserInfo2 = userList.filter(u => u.userId === "2")[0]
console.log(selectedUserInfo2); // {userId: '2', userName: 'Jeremy', phone: '010-1111-1112', address: 'Jeju'}


2. array map



const userList = [
   {userId: "1", firstName: "Seungwon", lastName: "Go", yyyymmdd: "19770513", phone: "010-1111-1111", address: "Jeju"},
   {userId: "2", firstName: "Jeremy", lastName: "Go", yyyymmdd: "19800103", phone: "010-1111-1112", address: "Seoul"},
   {userId: "3", firstName: "James", lastName: "Go", yyyymmdd: "19821113", phone: "010-1111-1113", address: "Seoul"},
]

const mon = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

const newUserList = userList.map(u => (
   {
      userId: u.userId, 
      firstName: u.firstName, 
      lastName: u.lastName, 
      fullName: u.firstName + ' ' + u.lastName, 
      yyyymmdd: u.yyyymmdd, 
      year: u.yyyymmdd.substring(0, 4),
      month: u.yyyymmdd.substring(4, 6),
      day: u.yyyymmdd.substring(6, 8),
      usDateFormat: mon[parseInt(u.yyyymmdd.substring(4, 6)) - 1] + ' ' + parseInt(u.yyyymmdd.substring(6, 8)) + ', ' + u.yyyymmdd.substring(0, 4), // 미국식 날짜 타입
      phone: u.phone, 
      address: u.address
   }
))
console.log(newUserList);
// [
//     {
//         address: "Jeju"
//         day: "13"
//         firstName: "Seungwon"
//         fullName: "Seungwon Go"
//         lastName: "Go"
//         month: "05"
//         phone: "010-1111-1111"
//         usDateFormat: "May 13, 1977"
//         userId: "1"
//         year: "1977"
//         yyyymmdd: "19770513"
//     },
//     {
//         address: "Seoul"
//         day: "03"
//         firstName: "Jeremy"
//         fullName: "Jeremy Go"
//         lastName: "Go"
//         month: "01"
//         phone: "010-1111-1112"
//         usDateFormat: "Jan 3, 1980"
//         userId: "2"
//         year: "1980"
//         yyyymmdd: "19800103"
//     },
//     {
//         address: "Seoul"
//         day: "13"
//         firstName: "James"
//         fullName: "James Go"
//         lastName: "Go"
//         month: "11"
//         phone: "010-1111-1113"
//         usDateFormat: "Nov 13, 1982"
//         userId: "3"
//         year: "1982"
//         yyyymmdd: "19821113"
//     }
// ]


3. moment.js


npm i moment
# OR
yarn add moment



const moment = require('moment');

const today = moment().format('YYYY-MM-DD');
const usFormatToday = moment().format('MMM D, YYYY');

console.log('오늘 ', today); // 오늘  2021-11-01
console.log('오늘날짜 미국형식 ', usFormatToday); // 오늘날짜 미국형식  Nov 1, 2021

const tomorrow = moment().add(1, 'day').format('YYYY-MM-DD');

console.log('내일 ', tomorrow); // 내일  2021-11-02

const yesterday = moment().subtract(1, 'day').format('YYYY-MM-DD');
const yesterday2 = moment().add(-1, 'day').format('YYYY-MM-DD');
const before3 = moment().subtract(3, 'day').format('YYYY-MM-DD');

console.log('어제 ', yesterday); // 어제  2021-10-31
console.log('어제2 ', yesterday2); // 어제2  2021-10-31
console.log('3일전 ', before3); // 3일전  2021-10-29

const week = moment().add(1, 'week').format('YYYY-MM-DD');
const week2 = moment().subtract(1, 'week').format('YYYY-MM-DD');

console.log('1주일후 ', week); // 1주일후  2021-11-08
console.log('1주일전 ', week2); // 1주일전  2021-10-25

const month = moment().add(1, 'month').format('YYYY-MM-DD');
const month2 = moment().subtract(1, 'month').format('YYYY-MM-DD');

console.log('1달후 ', month); // 1달후  2021-12-01
console.log('1달전 ', month2); // 1달전  2021-10-01

const year = moment().add(1, 'year').format('YYYY-MM-DD');

console.log('1년후 ', year); // 1년후  2022-11-01

// 사용자 로그인 후 해당 사용자의 타임존을 설정하는 경우가 있음 - 시차
// 데이터베이스는 한군데 있고 사용자는 각 나라 여러곳에 있는 경우
// 데이터베이스는 각 나라별 날짜 포멧을 저장하지 않는다. 당연히 하나의 시간만 저장이 된다. 글로벌 서비스 같은 경우 UTC +0 기준으로 저장이 될 것이다.
// 대한민국은 타임존이 UTC +9 <- 이거 많이 봤지?
// 실제 데이터베이스에 저장된 시간을 그대로 우리나라 사람들에게 보여줬을 때, 우리나라 시간과 9시간의 gap이 생긴다.
// 우리나라 사람에게 보여줄 땐 +9 아홉시간을 더한 후 보여줘야지만 동일한 시간을 받을 수 있다.

// 아래 create_date를 현재 사용자의 타임존에 맞는 시간으로 보여줘야한다.
// 현재 사용자가 UTC +9라면 그 기준에 맞춰줘야한다.
const create_date = new Date(); // UTC +0 기준
const user_timezone = 9;

const system = moment(create_date).format('YYYY-MM-DD HH:mm:ss');
const timezone = moment(create_date).add(user_timezone, 'hour').format('YYYY-MM-DD HH:mm:ss');

console.log('타임존 적용 전 시스템시간 ', system); // 타임존 적용 전 시스템시간  2021-11-01 21:20:24
console.log('해당유저의 타임존에 맞게 변화 ', timezone); // 해당유저의 타임존에 맞게 변화  2021-11-02 06:20:24


Note

날짜말고 또 중요한거. 금액.
우리나라는 3자리마다 콤마, 소수점 이하는 없음
유럽이나 미국은 소수점에도 있음, 그리고 3자리마다 점이고 소수점에 콤마를 씀. 우리나라랑 반대
이런 부분들을 고려를해서 개발해야됨

4. 데이터그리드 구현 (테이블 정렬)



const array1 = [1, 30, 4, 21, 100000];

const res = array1.sort(function (a,b) {
    if (a < b) return -1;
    else if (a > b) return 1;
    return 0;
})

console.log(res);

const res2 = array1.sort((a, b) => a - b);

console.log(res2);

// return하는 -1, 1, 0은 사실 중요하지않고 0보타 큰지 0보다 작은지가 중요한 것이다.
// a가 b보다 크면 a - b 에서 +가 나올 것이고
// a가 b보다 작으면 -
// a랑 b가 같으면 0
// 아래 화살표함수식이 결국 그 위 식이랑 똑같은 것이다.




const numbers = [4, 2, 5, 1, 3];
numbers.sort(function(a, b) {
    console.log(a, b, a - b);
    return a - b;
});
console.log(numbers);
// 2 4 -2
// 5 2 3
// 5 4 1
// 1 4 -3
// 1 2 -1
// 3 4 -1
// 3 2 1
// [1, 2, 3, 4, 5]


Note

안에서 인자 a, b에 넘길 때 따로 내부로직이 있는 듯하다.
우리가 신경쓸 것은 배열 요소 하나하나를 서로 어떻게 비교할지만 신경쓰면된다.



const userList = [
   {userId: "1", firstName: "Seungwon", lastName: "Go", yyyymmdd: "19770513", phone: "010-1111-1111", address: "Jeju"},
   {userId: "2", firstName: "Jeremy", lastName: "Go", yyyymmdd: "19800103", phone: "010-1111-1112", address: "Seoul"},
   {userId: "3", firstName: "James", lastName: "Go", yyyymmdd: "19821113", phone: "010-1111-1113", address: "Seoul"},
]

// firstName순으로 정렬해보자.
const sortUserList = userList.sort((a, b) => a.firstName < b.firstName ? -1 : a.firstName > b.firstName ? 1 : 0);
console.log(sortUserList);
// [
//     {userId: '3', firstName: 'James', lastName: 'Go', yyyymmdd: '19821113', phone: '010-1111-1113', …}
//     {userId: '2', firstName: 'Jeremy', lastName: 'Go', yyyymmdd: '19800103', phone: '010-1111-1112', …}
//     {userId: '1', firstName: 'Seungwon', lastName: 'Go', yyyymmdd: '19770513', phone: '010-1111-1111', …}
// ]





<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>table sort</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
</head>
<body>
<div class="container">
    <div class="mb-2">
        <input type="search" class="form-control" onkeydown="doSearch(event, this.value)">
    </div>
    <table class="table table-bordered table-sm">
        <thead>
        <tr>
            <th onclick="sort('userId');">userId</th>
            <th onclick="sort('firstName');">firstName</th>
            <th onclick="sort('lastName');">lastName</th>
            <th onclick="sort('yyyymmdd');">yyyymmdd</th>
            <th onclick="sort('phone');">phone</th>
            <th onclick="sort('address');">address</th>
        </tr>
        </thead>
        <tbody>

        </tbody>
    </table>
</div>

<script>
    let userList = [
        {userId: "1", firstName: "Seungwon", lastName: "Go", yyyymmdd: "19770513", phone: "010-1111-1111", address: "Jeju"},
        {userId: "2", firstName: "Jeremy", lastName: "Go", yyyymmdd: "19800103", phone: "010-1111-1112", address: "Seoul"},
        {userId: "3", firstName: "James", lastName: "Go", yyyymmdd: "19821113", phone: "010-1111-1113", address: "Seoul"},
    ]

    let sortOption = {
        userId: true,
        firstName: true,
        lastName: true,
        yyyymmdd: true,
        phone: true,
        address: true,
    }

    function sort(sortField) {
        if (sortOption[sortField]) {
            userList = userList.sort((a, b) => a[sortField] < b[sortField] ? -1 : a[sortField] > b[sortField] ? 1 : 0);
        } else {
            userList = userList.sort((a, b) => a[sortField] < b[sortField] ? 1 : a[sortField] > b[sortField] ? -1 : 0);
        }
        sortOption[sortField] = !sortOption[sortField];
        renderTable(userList);
    }

    function doSearch(e, keyword) {
        if (e.keyCode === 13) { // enter 키 입력했을 때
            const regexp = new RegExp(keyword, 'gi'); // g 모든걸 찾아라 i 대소문자 구분하지말고 찾아라
            const data = userList.filter(item => regexp.test(item.userId) || regexp.test(item.firstName) || regexp.test(item.lastName) || regexp.test(item.yyyymmdd) || regexp.test(item.phone) || regexp.test(item.address));
            renderTable(data);
        }
    }

    function renderTable(data) {
        const oTable = document.querySelector('table tbody');
        const h = [];
        data.forEach(item => {
            h.push("<tr>")
            h.push("<td>" + item.userId + "</td>")
            h.push("<td>" + item.firstName + "</td>")
            h.push(`<td>${item.lastName}</td>`)
            h.push(`<td>${item.yyyymmdd}</td>`)
            h.push(`<td>${item.phone}</td>`)
            h.push(`<td>${item.address}</td>`)
            h.push("</tr>")
        })
        oTable.innerHTML = h.join('');
    }

    renderTable(userList);
</script>
</body>
</html>


5. i18n (다국어 구현) - internalization

구글 검색하면 이미 만들어져있는 모듈이 굉장히 많다.
하지만 이번 시간엔 그런 모듈들을 사용하지 않고 직접 만들어보도록 하겠다.



const text = "My name is {fullName}. I am from {city}.";

const data = {
    id: "1",
    fullName: "HyungJu Lee",
    city: "Seoul",
    company: "ReturnValues",
}

const i18n = {
    en: {
        "My name is {fullName}. I am from {city}.": "My name is {fullName}. I am from {city}.",
    },
    ko: {
        "My name is {fullName}. I am from {city}.": "제 이름은 {fullName} 입니다. 저는 {city} 출신입니다."
    }
}

// 사용자가 로그인한 순간에 사용자가 사용하는 언어에 대한 정보를 가지고온다고 가정
const userLanguage = "ko";

function t(form, params = {}) {
    // g - 모든 문자에서 검색
    // { 이걸로 시작
    // } 이걸로 끝
    // 사이에 } <- 이거 제외한 아무 문자
    return form.replace(/{[^}]*}/g, match => {
        console.log(match);
        console.log(match.slice(1, -1));
        return params[match.slice(1, -1)];
    })
}

console.log(t(text, data));
// {fullName}
// fullName
// {city}
// city
// My name is HyungJu Lee. I am from Seoul.

const translation = t(i18n[userLanguage]["My name is {fullName}. I am from {city}."], data);

console.log(translation);
// {fullName}
// fullName
// {city}
// city
// 제 이름은 HyungJu Lee 입니다. 저는 Seoul 출신입니다.


위와 같이하면 굉장히 간단한 다국어 시스템을 만들 수 있습니다.
오픈소스도 많지만 오픈소스를 보다보니까 굳이 필요없는 코드들이 들어있는 경우도 많더라구요.
오픈소스를 쓸 수도 있지만 이렇게 간단하게 다국어 시스템을 코드로 직접 만들어서 사용할 수도 있습니다.

6. 통화 포멧 구현하기 - Currency

각국마다 금액을 표기하는 방법이 다릅니다.



// 한국 - 3,000 (소수점이 없다)
// 미국, 유럽 (소수점 있다) 3,000.12
// 유럽 3.000,12 (유럽의 몇몇 국가는 이렇게 소수점과 점을 바꿔서 쓰는 경우도 있다)
// 포멧을 보통 이런식으로 많이 정의합니다. - #,###.## (세자리마다 콤마, 소수점이하는 두자리마다 점으로 표시하라 라는 뜻이다)

function currency(value, format) {
    // 아래 선언한 currencyUnit의 역할
    // $#,###.## <- 간혹가다가 이렇게 화폐 단위가 맨 앞에 오는 포멧이 있다.
    // 어떤 심볼이던지 받기 위한 변수이다.
    let currencyUnit = "";
    // $#,###.## <- 맨 앞에 $처럼 #이 아닌 것이 왔다면 심볼이라고 여긴다.
    if (format.substring(0, 1) !== "#") {
        currencyUnit = format.substring(0, 1);
    }

    // 3자리마다 어떤 구분자로 나눌 것인지. default 값은 , 쉼표
    let groupingSeparator = ",";
    // 소수점 이하 몇자리까지 보여줄 것인지.
    let maxFractionDigits = 0;
    // 소수점 이하 표기는 .으로할건지 ,로 할건지. default는 .
    let decimalSeperator = ".";

    // #,###.## <- 포멧이 이렇다면 ,의 index는 1이 리턴
    // .의 index는 5가 리턴
    // ,의 index가 작으므로 groupingSeparator는 그대로 ,
    // maxFractionDigits은 . 뒤에 #이 몇개가 있는지를 보면된다.
    if (format.indexOf(",") < format.indexOf(".")) {
        maxFractionDigits = format.length - format.indexOf(".") - 1;
    } else {
        // #.###,##
        groupingSeparator = ".";
        decimalSeperator = ",";
        maxFractionDigits = format.length - format.indexOf(",") - 1;
    }

    let prefix = "";
    let d = "";
    let dec = 1;
    for (let i = 0; i < maxFractionDigits; i++) {
        dec = dec * 10;
    }

    // maxFractionDigits이 2라면, 1324.12345 -> 1324.12로 반환되게끔
    let v = String(Math.round(parseFloat(value) * dec) / dec);

    // if (v.startsWith("-"))
    if (v.indexOf("-") > -1) {
        prefix = "-";
        v = v.substring(1); // 앞에 -가 붙어있다면 - 잘라내버리고 숫자만 v에 저장
    }

    // #,###.#0 <- format이 이렇게 들어왔다면. 끝에 0이 붙어있다면. <- 이거는 소수점 2자리까진 무조건 표시해라 라는뜻. 표시할 소수점이없어도.
    // 소수점 이하 몇자리까지 표시하는게 있고 마지막이 0이라면
    if (maxFractionDigits > 0 && format.substring(format.length - 1) === "0") {
        // 소수점 maxFractionDigits자리까지 무조건 만드는 함수.. 소수점이 없다면 0을 붙여서 만들어주는 함수다.
        v = String(parseFloat(v).toFixed(maxFractionDigits)); // 12.333.toFixed(4) -> 12.3330
    }

    // 소수점 이하까지 표현하겠다면,
    if (maxFractionDigits > 0 && v.indexOf(".") > -1) {
        d = v.substring(v.indexOf(".")); // v가 1324.12라면 d에 .12가 들어옴
        d = d.replace(".", decimalSeperator); // decimalSeperator가 ,라면 ,12로 바뀐다
        v = v.substring(0, v.indexOf(".")); // 1324
    }

    // \d는 숫자를 의미
    // 하나 이상의 숫자가있고 연속된 숫자가 3개
    // v에 1324가 담겨있다면
    const r = /(\d+)(\d{3})/; // 앞괄호 $1, 뒷괄호 $2 <- 이렇게 자동으로 매핑
    while (r.test(v)) {
        v = v.replace(r, "$1" + groupingSeparator + "$2"); // 1 + "," + 324 -> 1,324
        // 만약 v가 4234523라면
        // 처음엔 4234 + "," + 523 -> 4234,523
        // 그 다음엔 4 + "," + 234 -> 4,234
    }

    return prefix + currencyUnit + String(v) + String(d);
}

console.log(currency(4234523.12, "#,###.##"));
// 4,234,523.12
console.log(currency(4234523.12, "#.###,##"));
// 4.234.523,12
console.log(currency(4234523.12, "$#.###,##"));
// $4.234.523,12
console.log(currency(-4234523.12, "$#.###,##"));
// -$4.234.523,12
console.log(currency(-4234523.1, "$#.###,##"));
// -$4.234.523,1
console.log(currency(-4234523.1, "$#.###,#0"));
// -$4.234.523,10