본 포스트는 JavaScript 환경에서 Session Key나 사용자 별, 앱 별로 키 값이 필요할 경우 난수로 키를 조합하는 방법에 대한 내용입니다.

 

아래 코드는 Node.js 환경에서 앱이 서버에 접속할 때 신규로 키를 채번하는 부분의 소스코드이며 TypeScript 로 작성했습니다.

const generateAppKey = (keyLength: number): string => {
    const baseChars: string = 'ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvxyz0123456789';
    let keyString: string = '';
        
    for (let inx=0; inx<keyLength; inx++) {
        keyString += baseChars.charAt(Math.floor(Math.random() * baseChars.length))
    }

    return keyString;
};

 

생성하려는 키는 영문 대/소문자 및 숫자만 허용하므로 사용할 기본 문자열을 하나 선언한 후 원하는 키 길이만큼 반복하며 Random으로 채번하면 됩니다.

 

채번한 후에는 해당 값이 유니크해야 하므로

1. DB에 키를 저장하는 경우에는 DB에 동일한 값이 없는지 확인

2. WAS 메모리에 저장할 경우에는 WAS 메모리에 동일한 값이 없는지 확인

 

하시면 됩니다.

300x250

이 포스트는 JavaScript에서 객체나 배열 요소를 연결하기 위해 사용하는 방법에 대해 알아보겠습니다. jQuery에서 사용하는 extend 함수, ES6+ 에서 제공하는 Object.assign 및 Spread expression(전개 구문)을 확인해 보겠습니다.

 

jQuery.extend

Internet Explorer를 지원해야 하는 애플리케이션을 개발할 때는 Object를 결합해야 할 요건이 있을 경우 대부분 jQuery.extend 함수를 사용했습니다. 가장 기본적인 사용법은 아래와 같습니다.

const obj1 = {
    korean: 97,
    english: 100
};

const obj2 = {
    math: 67
}

$.extend(obj1, obj2);
console.log(obj1);

 

위와 같이 작성하면 obj1 Object에 obj2 Object를 병합하여 아래와 같은 결과가 표시됩니다.

{korean: 97, english: 100, math: 67}

 

아래와 같이 Object 아래 Object가 있을 경우에는 결과가 좀 다릅니다.

const obj1 = {
    korean: {
        step1: 97,
        step2: 100,
    },
    english: 100
};

const obj2 = {
    korean: {
        step1: 60
    },
    math: 67
};

$.extend(obj1, obj2);
console.log(obj1);

 

위 소스코드의 실행결과는 아래와 같습니다.

{korean: {step1: 60}, english: 100, math: 67}

 

Object 내에 Object의 경우에는 병합되지 않고, 나중 Object 기준으로 overwrite 됩니다. 하여 Object 내의 Object까지 병합을 하기 위해서는 아래와 같이 작성합니다.

$.extend(true, obj1, obj2);
console.log(obj1);

 

그러면 결과는 아래와 같이 step1은 overwrite 되지만 step2는 obj1 기준으로 병합되어 있습니다.

{korean: {step1: 60, step2: 100}, english: 100, math: 67}

 

보통 jQuery로 extend 함수는 기존 Object로부터 Object를 하나 복사하여 사용할 때 많이 사용합니다. Object 아래 자기 자신을 복제하는 함수를 하나 생성할 때 아래와 같이 작성합니다.

clone: function() {
    const me = this;
    return $.extend(true, {}, me);
}

 

jQuery.extend 함수에 대한 상세 설명은 아래 링크를 참조하시면 됩니다.

https://api.jquery.com/jquery.extend/

 

jQuery.extend() | jQuery API Documentation

Description: Merge the contents of two or more objects together into the first object. When two or more object arguments are supplied to $.extend(), properties from all of the objects are added to the target object. Arguments that are null or undefined are

api.jquery.com

 

 

Object.assign

ES6부터는 Object.assign 이라는 함수를 제공합니다. Internet Explorer에서는 지원하지 않습니다. Object.assign 함수의 사용 형식은 아래와 같습니다.

Object.assign(target, ...source)

 

target Object에 source Object들의 전개 가능한 모든 속성들을 복사해서 붙여 넣습니다. $.extend에서 사용했던 두 번째 예제로 확인해 보겠습니다.

const obj1 = {
    korean: {
        step1: 97,
        step2: 100,
    },
    english: 100
};

const obj2 = {
    korean: {
        step1: 60
    },
    math: 67
};

const merged = Object.assign(obj1, obj2);
console.log(obj1);
console.log(merged);

 

속성에 대한 복사이므로 결과는 아래와 같습니다. 내부 Object 까지는 복사하지 않으므로 이를 "얕은 복사"라고 합니다.

//obj1
{korean: {step1: 60}, english: 100, math: 67}

//merged
{korean: {step1: 60}, english: 100, math: 67}

 

$.extend에 recursive 옵션을 true로 준 것처럼 간단하게 "깊은 복사"를 수행하는 방법은 자체적으로는 없습니다. 일반적으로 for ~ in Loop을 사용하여 recursive 하게 프로그램을 짜서 복사하거나 lodash 라이브러리를 사용합니다.

 

Object.assign에 대한 상세 설명은 아래 MDN 링크를 참고할 수 있습니다.

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

 

Object.assign() - JavaScript | MDN

Object.assign() 메서드는 출처 객체들의 모든 열거 가능한 자체 속성을 복사해 대상 객체에 붙여넣습니다. 그 후 대상 객체를 반환합니다.

developer.mozilla.org

 

Spread Expression (전개 구문)

전개 구문 역시 ES6부터 제공하며 Internet Explorer에서는 지원하지 않습니다. Spread 구문은 사용이 편리하여 제법 유용하게 사용할 수 있습니다. 다시 jQuery 두 번째 예제로 확인해 보겠습니다.

const obj1 = {
    korean: {
        step1: 97,
        step2: 100,
    },
    english: 100
};

const obj2 = {
    korean: {
        step1: 60
    },
    math: 67
};

const merged = {...obj1, ...obj2};
console.log(obj1);
console.log(merged);

 

Spread 구문은 Object.assign 과는 달리 obj1으로는 데이터를 병합하지 않습니다. obj1과 obj2는 원래 데이터대로 두고 병합한 데이터를 return 합니다. 결과는 아래와 같습니다.

//obj1
{korean: {step1: 97, step2: 100}, english: 100, math: 67}

//merged
{korean: {step1: 60}, english: 100, math: 67}

 

Object.assign과 마찬가지로 "깊은 복사"는 별도의 라이브러리 사용하거나 프로그램을 작성하여 해결해야 합니다. Spread 구문은 배열을 결합하는 경우에 손쉽게 사용할 수 있습니다.

const a = [1, 3, 4];
const b = [1, 3, 6];

const c = [...a, ...b];
console.log(c);

 

실행 결과는 아래와 같습니다.

[1, 3, 4, 1, 3, 6]

 

Spread Expression은 React나 Vue 등에서 상태를 관리할 때 간편한 구문을 제공합니다. Redux나 Context API 등을 사용하여 전역 상태를 관리할 경우 기존 상태에 변경된 상태 값만 갱신하기 위해서 아래와 같이 작성할 수 있습니다.

switch (action.type) {
    case 'SET_IS_VISIBLE':
        return {
            ...state,
            isVisible: action.isVisible
        }
    default:
        //Do something
}

 

위의 소스코드에서 action에 isVisible을 true로 세팅을 수행하게 되면 변경 전과 변경 후의 state 변화는 아래와 같습니다.

//변경 전
state = {
    isVisible: false,
    showPopup: false,
    showMenu: false
}

//변경 후
state = {
    isVisible: true,
    showPopup: false,
    showMenu: false
}

 

Spread Expression에 대한 상세한 설명은 아래 MDN 링크에서 확인할 수 있습니다.

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Spread_syntax

 

전개 구문 - JavaScript | MDN

전개 구문을 사용하면 배열이나 문자열과 같이 반복 가능한 문자를 0개 이상의 인수 (함수로 호출할 경우) 또는 요소 (배열 리터럴의 경우)로 확장하여, 0개 이상의 키-값의 쌍으로 객체로 확장시

developer.mozilla.org

 

300x250

본 포스트는 HTTP Response Header 의 값 중 서버 시간을 구해서 사용하는 방법에 대한 설명입니다.

 

 

클라이언트에서 서버의 시간을 참조할 일이 있을 경우, 서버 API에서 응답값에 현재 시간을 보통 포함시켜서 전달합니다. 하지만 정말로 딱! 시간만 필요한 경우는 아래와 같이 빈 페이지나 빈 API의 Response Header의 Date를 참조하면 손쉽게 서버 시간을 구할 수 있습니다.

 

인터페이스를 하면 아래와 같이 Response Header가 응답에 포함되어 있습니다.

아래와 같은 식으로 간단하게 Response Header에 있는 Date 값을 구할 수 있습니다.

fetch(url, {
	method: 'GET'
}).then((resp) => {
	console.log(resp.headers.get('Date'));
});

프로젝트에서 jQuery를 사용한다면 아래 코드를 참고하시면 됩니다.

var options = {};
options.url = window.location.href.toString();
options.method = 'HEAD';
var xhr = $.ajax(options);
xhr.always(function() {
	console.log(xhr.getResponseHeader('date'));
});

 

300x250

본 포스트는 JavaScript의 getTimeZoneOffset() 메소드를 사용하여 UTC 시간을 구한 뒤 서버와 클라이언트의 시간을 동일하게 계산할 수 있는 방법에 대한 설명입니다.

 

요즘 개발하는 서비스들은 대부분 국내에서 뿐만 아니라 해외에서도 사용 가능한 형태로 개발합니다. 해외에서 서비스를 사용하는 경우 뿐 아니라 사용자가 각자 사용하는 Device의 국가, 시간 설정을 대한민국이 아닌 다른 곳으로 해두었을 경우 JavaScript의 new Date() 는 해당 국가 설정을 참조하여 시간을 return 합니다.

 

현재 제가 사용하는 Device는 대한민국 표준시를 사용하도록 설정되어 있습니다.

 이 상태에서 아래 코드의 결과는 다음과 같습니다.

new Date();
//Thu Jun 23 2022 07:36:14 GMT+0900 (한국 표준시)

이번에는 Device의 시간을 미국으로 변경해 보겠습니다.

 

 

그러면 동일한 코드는 아래와 같은 결과를 보여줍니다.

new Date();
//Wed Jun 22 2022 17:35:43 GMT-0500 (북미 중부 하계 표준시)

서비스의 성격에 따라 판단할 문제이지만 만약 서버에서 시간을 체크해야 하는데 화면의 Request에서 시간을 받아서 검증이나 사용을 하게될 경우 서버는 대한민국 표준시를 사용하지만, 화면은 북미 중부 표준시를 사용하게 되므로 대한민국 표준시를 사용하지 않는 사용자는 적절하지 않은 오류를 만나게 될 수도 있습니다.

 

경우에 따라 다르지만 클라이언트 JavaScript 코드에서도 대한민국 표준시로 통일하는 방법이 있습니다.

 

const now = new Date();
const utcDate = now.getTime() + (now.getTimezoneOffset() * 60 * 1000);
const korDate = new Date(utcDate + 9 * 60 * 60 * 1000);

우리나라는 그리니치 표준시 + 9H를 사용하므로, UTC 기준시각에 9시간을 더해주는 로직입니다.

 

위와 같이 코드를 작성하면 클라이언트에서 북미 중부 표준시를 사용하더라도 대한민국 표준시를 화면에서 사용할 수 있습니다. 물론 위와 같은 코드보다는 서버의 시간을 Response Header에서 참조하여 변환하는게 좀 더 일반적인 사용 방법입니다. 

 

300x250

몇 년 전에도 모 프로젝트에서 지원 요청이 와서 한 번 Code Review 하여 해결을 했었고, 얼마 전에도 운영 중인 시스템에서도 동일한 Case가 있었던 것을 보면 아주 기초적인 사항임에도 생각보다 잘 지켜지지 않는 부분일 수 있겠다는 생각이 듭니다.

 

jQuery 사용

대기업에서 SI 프로젝트에 몸 담고 있다보면, 혹은 대기업 SI 프로젝트에서 개발한 시스템을 인수하는 운영 담당자의 역할로 있다보면 서비스 기업들에서 많이들 사용하는 Vue, React 등의 Framework, Library는 구경하기 힘들고 2022년인 현재도 많은 프로젝트들이 jQuery 기반으로 진행되는 것을 알 수 있습니다.

 

대규모 SI 프로젝트의 경우 개발자가 수십~수백 명에 이르는데 Vue나 React를 사용하는 개발자들을 충분히 소싱하기 힘들고, 상용 UI 솔루션들과 연계해야 하는 상황에서 아직 jQuery 기반으로 제공되는 솔루션들이 많아 어쩔 수 없는 선택이 되곤 합니다.

(사실 Zepto 등의 경량 Library도 고려 대상일 수 있겠으나, 이미 검증된 수많은 Plug-in이 있다는 점은 SI 프로젝트에 너무 매력적인 요소로 작용하는 듯 합니다.)

 

jQuery 기반의 프로젝트에서 업무 화면이 복잡해져서 하나의 프로그램에서 수행하는 XHR과 calback이 많아지면 아래와 같은 문제들이 종종 발생하게 됩니다.

 

화면 특정 영역에 데이터가 나오다 말다 해요

A와 B 두 개의 function이 있습니다. A function은 DOM을 초기화하는 function이고, B function은 서버에서 데이터를 받아와 A function이 구성한 DOM에 Data를 Bind하는 역할을 하는 function 입니다.

function A() {
	// 특정 조건에 따라 UI에 특정 영역을 표시하거나 제거합니다.
}

function B() {
	// $.ajax 등의 함수로 서버와 통신하여 A 영역에서 생성한 DOM에 Data를 Bind 합니다.
}

생각보다 많은 개발자들이 아래와 같이 작성하고 (물론 위와 같이 너무 단순한 구조는 아닙니다만), 사실 또 대부분의 경우에 연산은 또 순식간에 일어나므로 올바르게 동작합니다.

A();
B();

하지만 복잡한 Front-end 프로그램을 개발하다 보면 아주 많은 경우의 수가 발생합니다. 사용자의 휴대폰이 구형이라 브라우저의 동작 자체가 느린 환경도 있고, 데이터 음영 구간도 있고, 접속 상태가 좋지 못한 Wi-Fi를 사용하여 Network가 느린 경우도 있어 반드시 작성한 순서대로 실행을 보장하지는 않습니다.

 

Callback function 사용

순서가 반드시 보장되어야 하는 코드들은 정확히 실행해야 하는 타이밍을 callback function을 사용하여 구현합니다. 

 

위와 같은 경우에도 A function에서 생성해야 할 DOM이 생성되지 않은 상태에서 B function의 Data bind가 수행이 되는 경우가 발생하면 해당 영역에는 올바르게 Data를 표시하지 못하는 경우가 발생합니다.

 

순서를 보장하기 위해서는 아래와 같이 작성합니다.

function A(callback) {
	//특정 조건에 따라 UI에 특정 영역을 표시하거나 제거합니다.
    if (callback && typeof callback === 'function') callback();
}

function B() {
	//$.ajax 등의 함수로 서버와 통신하여 A 영역에서 생성한 DOM에 Binding 합니다.
}

A(B);

위와 같은 형태로 작성하면 DOM을 구성하는 작업이 완료된 후 B function을 실행하므로 화면을 올바르게 표시할 수 있습니다. 

 

사실 수백~수천 라인에 달하는 프로그램에서 모든 순서가 정확하게 보장되기가 쉽지가 않습니다. 이 과정에서 callback 지옥 역시 발생합니다. ES6+에서는 async/await, Promise를 사용하여 이런 callback 지옥 구조를 많이 개선했지만 아직 많은 대고객 서비스에서는 I.E를 지원해야 하기 때문에 여전히 callback 지옥을 헤어 나오기는 힘듭니다.

300x250

백오피스나 내부 사용자를 위한 시스템은 Edge로 전환 후 I.E에 대한 고려를 이제 걷어내어도 되겠지만, 대고객 서비스를 하는 시스템들은 계속 I.E에 대해 고려할 필요가 있습니다.

 

JavaScript에서 일자 formatting이나 연산에 많이 사용하는 momentJs 사용 시 약간의 주의사항이 있습니다.

 

서버 API에서 일자 정보가 아래와 같이 내려온 경우 I.E에서는 정상적으로 처리가 되지 않습니다.

 

const curDate = '2022.06.22';
moment(curDate).format('YYYYMMDD');

위와 같은 코드를 실행할 경우 I.E에서는 "Invalid date"를 return 합니다.

 

I.E를 제외한 Chrome 등의 브라우저에서는 정상적으로 "20220622"를 return 합니다.

 

하여 서버 API에서 전달 받는 일자 데이터에 dot(.)이 포함되어 있을 경우 안전하게 데이터를 가공한 후 formatting 하면 됩니다.

 

const curDate = '2022.06.22';
moment(curDate.replace(/[.]/g, '')).format('YYYYMMDD');
300x250

JavaScript에서 여러 개의 조건을 나열할 때 아래와 같이 or를 길게 연결해서 사용합니다.

 

const code = data.code;

if (code === 'A'
||  code === 'B'
||  code === 'C'
||  code === 'D'
||  code === 'E') {
	//Do anything
}

보통 현업에서 코드를 길게 쓰다보면 해당 라인이 계속 늘어나게 되면 프로그램도 지저분해지고, 가독성도 떨어지게 됩니다. 이럴 경우 Array.prototype.indexOf나 Array.prototype.includes를 활용하여 코드를 개선해 볼 수 있습니다.

 

Array.prototype.indexOf

이 메소드는 배열에서 해당 요소를 찾을 수 있는 첫 번째 인덱스를 return 하거나, 찾지 못할 경우 -1을 return 합니다.

const code = data.code;
const conditions = ['A', 'B', 'C', 'D', 'E'];

if (conditions.indexOf(code) > -1) {
	//Do anything
}

 

Array.prototype.includes

이 메소드는 배열이 해당 요소를 포함하고 있는지 여부를 boolean 으로 return 합니다.

const code = data.code;
const conditions = ['A', 'B', 'C', 'D', 'E'];

if (conditions.includes(code)) {
	//Do anything
}

 

만약 개발하고 있는 애플리케이션의 지원 범위에 I.E가 포함된다면 indexOf를 사용하면 되고, I.E를 지원할 필요가 없다면 includes 메소드를 일반적으로 사용하면 됩니다.

자세한 브라우저 호환성은 JavaScript MDN 문서를 참고하시면 됩니다.

300x250

+ Recent posts