programming language/javascript

[javascript] 유용한 js 기능 - 1

공대키메라 2022. 6. 5. 00:47

필자는 회사에서 급하게 투입된 프로젝트에서 배정받은 일의 업무 분배도는

 

DB 쿼리 작성 40%, js 를 이용한 front 화면 생성 40%, 기존 java 로직을 활용한 비즈니스 로직 구현이 20%다. 

 

여기서 javascript를 다루면서 많은 일이 있었는데 인터넷을 뒤져보면 js 사용 팁을 전수해주는데 이 기회에 한 번 정리할 예정이다. 

 

참고 : https://modernweb.com/45-javascript-tips-tricks-practices/

https://sunlightmedia.org/ko/%EC%9E%90%EB%B0%94-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%8C%81/

https://paperblock.tistory.com/62

 

많은 내용이 있지만 필자가 경험하고 내가 실제로 많이 쓰는 것 같은 것만 정리를 했다.

 

또한, 많은 js의 함수도 설명을 해주고 있다. 가령, map, filter, slice, isArray 등등이 있다.

 

그렇기에 위에 사이트에서 더 자세히 보고 참고하기를 바란다. 

 

이 비교적 간단한 코드들을 직접 실험하기 위해 js파일을 따로 만들어서 html에 뿌리지 않고 다음 사이트를 이용했다. 

사용 사이트 : https://jsfiddle.net/fmn6gc15/

 

그러면 한 번 시작해보도록 하겠다.

1. let vs var

function run() {
  var foo = "Foo";
  let bar = "Bar";

  console.log(foo, bar); // Foo Bar

  {
    var moo = "Mooo"
    let baz = "Bazz";
    console.log(moo, baz); // Mooo Bazz
  }

  console.log(moo); // Mooo
  console.log(baz); // ReferenceError
}

run();

 

이 둘을 사실 변수를 선언할 때 사용해도 문제가 없을 수 있다.

 

그러면 차이는 무엇인가?

 

주요 차이점은 scoping rule이다. var keyword로 선언된 변수들은 immediate 함수 body에 범위가 설정되어있다. 
let 변수는 반면에 {}로 명시된 immediate 한 {} 블록세 범위가 설정되어 있다.

출처 : https://stackoverflow.com/questions/762011/whats-the-difference-between-using-let-and-var?page=1&tab=scoredesc#tab-top

 

이에 대한 정보는 stack overflow 에서 가져왔는데 다음 코드를 살펴보면 이해가 더 잘 된다. 

 

function run() {
  var foo = "Foo";
  let bar = "Bar";

  console.log(foo, bar); // Foo Bar

  {
    var moo = "Mooo"
    let baz = "Bazz";
    console.log(moo, baz); // Mooo Bazz
  }

  console.log(moo); // Mooo
  console.log(baz); // ReferenceError
}

run();

 

결과값은 다음과 같다. 클릭!

더보기
"Foo", "Bar"
"Mooo", "Bazz"
"Mooo"
baz is undifined

 

var 와 let의  차이점을 알겟는가?

 

var의 경우 block( {} )에서 벗어나도 변수가 그냥 선언된 채로 삼아있다.

 

let의 경우에는 block( {} )에서 벗어나면 변수가 선언되지 않았다는 에러를 출력한다. 

 

let이 es6에서 도입된 이유는 기존의 function scoping이 혼란스럽고, js에서 종종 버그의 원인이 되기 때문이다. 

 

내가 아는 버그는 hosting으로 일어날 수 있는 것과, 이미 선언한 변수가 겹치는 버그다.

 

한번 보도록 하자.

 

'use strict';
var foo = "foo1";
var foo = "foo2"; // No problem, 'foo1' is replaced with 'foo2'.

 

자... 만약 엄청 큰 프로젝트에서 var를 이용해서 계속 변수를 선언해서 사용할 경우 우리는 foo1도 변수로 사용하고, foo2로 변수로 foo에 선언해서 사용하고 싶은데

 

어딘지 모르는 곳에서 서로 같은 변수 이름으로 사용하게 된다면???

 

var을 사용하는 경우 재선언이 되기 때문에...망하는 것이다. (머쓱타드 ^^;;)

 

사실 현재 근무중인 회사는 var를 아직도 사용하고 있긴 했다.

 

그리고 전에 글에서도 공부 차원에서 정리를 했는데 꽤 오랜 시간이 흐르니 이것의 문제점을 몸소 경험하게 되었다. 

 

아마 오래된 회사이기도 하고 얼마전까지만 해도 Internet Explorer를 사용했으니 옛날 코드를 그대~ 로 사용하는 경우가 많았을 거라 추측한다. 

 

그리고 이 문제를 내가 경험할 뻔했는데, 나도 모르게 같은 변수를 두번 선언하고 있었다. 코드량이 많아지니 scroll을 내리면서 일일이 무슨 변수가 있는지 까먹을 경우도 비일비재하다. let을 사용하게 되면은 동시에 선언된 변수의 경우 already been declared하는 오류를 반환한다. 

 

또 호이스팅으로 생기는 문제는 뭔가?

 

console.log(test) // undefined 출력
var test = 'test'

 

코드를 작성하다 보면 나도모르게 단축키가 눌려서 코드 순서가 바뀌는 경우가 있다.

 

line 을 위 아래도 자주 이동시키는데 그런 경우에 위에서 정말 아무것도 아니지만 분명 선언했는데 왜 어디서 계산이 잘못되서 undefined가 나오는거지? 할 수 있다. 그러면 에러를 보여주지 않으면 직접 찾아야 한다. 

 

찾아보면 사실 더 많은 이유가 있지만 실제적으로 와닿는 것이 나에게는 이 두개였다. 

 

별도로 더 궁금하다면 참고 사이트를 읽기를 추천한다.

 

참고로 필자 회사 (우리라고 하기 싫다.) java 개발자들은 아직도 var를 사용한다. 그냥 생각없이 긁어서 쓰는것이다. 

 

실수는 안하면 되는 것이지만, 실수할 여지를 남기면 안되는 것이라 생각한다. 

2. 배열 메서드

회사에서 내가 이것을 알았어야 하는데... 

 

공통 method를 만들어서 사용을 하고 있었는데 너무 javatic하게 코드를 구성한 것 같다. 

 

필자 키메라는 여러개의 obj를 담은 arra를 직접 만들어 줄 일이 있었다.

 

변수를 배열에 넘길 생각을 못하고 이제 무조건 하나 하나 만들어서 넣어주는 방식만을 떠올렸다. 

 

하지만 위의 방식을 이용하면 훨신 더 간단해질 수 있고 공통 코드로 작성할 여지가 생기는 것이다. 

 

const makeArr = (arr) => {
	let resultArr = [];
  arr.map(function(value){
  	let newObj = {
    	value : value,
    }
    resultArr.push(newObj);
  })
  return resultArr;
}

//let result = makeArr('test1','test2') 로 만들면 매개변수를 일일이 뽑아서 써야함. 
let result = makeArr(['test1','test2'])
console.log(result)

 

이런 방법이 있었다니...

 

사실 최근에 너무 반복되는 코드도 많고 그것을 최대한 내 방식대로 정리해서 사용하고 있었는데 

 

이 방식을 보니 더 좋은 코드가 생각날 것 같기도 하고...

 

참고로 필자는 회사에서 thymeleaf를 view template으로 이용하는데 

 

toast grid 를 이용해서 통계 화면의 테이블들을 구성한다. 

 

여기서 문제는 column에 대한 정보를 어떻게 만들어 주는지 확인하기 위해서 기존 코드들을 확인했는데 정말 엉망이다 ;;;

 

전부 다 그냥 하드코딩으로 박아놔서 너무 짜증이 났다. 그래서 필자는 다음과 같이 코드를 변경해서 사용하면 좋을 것 같다는 생각이 든다. 

 

const makeArr = (arr) => {
  let resultArr = [];
  arr.map(function(value){
      let newObj = {
          name : value.name,
          width: value.width,
          header : value.header
        }
        resultArr.push(newObj);
  })
  return resultArr;
}

const FIRST = {
 name : 'first',
 width : 100,
 header : 'first-header'
}
const SECOND = {
 name : 'second',
 width : 100,
 header : 'second-header'
}
//let result = makeArr('test1','test2') 로 만들면 매개변수를 일일이 뽑아서 써야함.
const targetInfo = [FIRST, SECOND];
let result = makeArr(targetInfo)
console.log(result)

 

배열의 성분으로 객체 자체를 넣게 되면 makeArr 함수에서 value에서 원하는 값을 뽑아서 사용하기 편리할 것이다. 

3. == 대신 ===를 사용하라!

필자는 비교 연산자인 '=='와 '==='의 차이점은 그냥 비교, 빡센(?)비교라고 말하고 있다.

 

console.log([10] === 10)    // is false
console.log([10]  == 10)    // is true
console.log( '10' == 10)     // is true
console.log( '10' === 10)    // is false
console.log( []   == 0)     // is true
console.log( [] ===  0)     // is false
console.log( '' == false   )// is true but true == "a" is false
console.log( '' ===   false) // is false

 

사실 필자는 clas studio라는 회사 자체 node.js 프레임 워크를 이용해 종종 작업을 하는데

 

참 천인노공(?)할 것이 프레임 워크는 잘 만들어놓고 정해놓은 코딩 규약이 없다는 점이었다.

 

무언가 기능은 많은데 왜 이런 식으로 넘겨주는지, 논의하는지에 대한 사항이 없었다.

 

그 일례로 object형식으로 key 가 data인 것의 value가 배열 형태로 다음과 같이 들어와 있었다.

 

data : [value: '1', value:'2']

 

문제는 정의서에 적힌 data 반환 형식과 실제로 내가 받는 data형식이 다른 경우가 비일비재했다. 

 

data : [value : 1, value : 2]가 될 수 도 있었던 것이다. 

 

맨 처음에 바로 투입이 됏을 때는 이에 대해 잘 몰라서 '=='를 사용했다. 자동적으로 형변환이 되어서 비교를 하기 때문에 우리의 원하는 요구사항에 맞지 않은 경우에 정확한 데이터가 비교되지 않을 위험이 있기에 '===' 사용을 권장한다. 

 

사실 이것도 수정해야 하는 부분들인데 그런 경험이 있었다... 눈물 또르르...

4. 배열의 item들을 looping시에 map사용해라!

이건 사실 취향이긴 한데 map을 이용하면  우리가 for문을 일일이 작성을 하지 않아도 되기에 편리할 것 같다.

 

let squares = [1,2,3,4].map(function (val) {  
    return val * val;  
}); 
// squares will be equal to [1, 4, 9, 16]

 

이것도 사실 몰랐기에 map사용을 적극 활용해야겠다.

5. for문 사용시 initiation 적극 사용하기!

var sum = 0;  
for (var i = 0, len = arrayNumbers.length; i < len; i++) {  
    sum += arrayNumbers[i];  
}

 

for문 안에 len = arrayNumbers.length가 들어가 있는데 이것을 i 비교시에 넣게 되면 loop를 돌 때 마다 length연산이 일어나서 i의 범위에 부합하는지를 확인하는 것이다. 

 

요약하자면... 불필요한 연산이 계속 일어나는 것이다. 

7. spread  연산자 (operator)사용하기!

배열 복사하기

키메라는 기존 배열을 복사해서 사용하고 싶다. 

 

그래서 코드를 다음과 같이 적었다.

 

let arr1 = ['shane', 'doctor kim', 'bangal tiger']
let arr2 = arr1;

console.log(arr1);
console.log(arr2);
console.log(arr1 === arr2);

arr1.push('thelovemsg');
console.log(arr1);
console.log(arr2);
console.log(arr1 === arr2);

 

결과값 클릭!

 

더보기
["shane", "doctor kim", "bangal tiger"]
["shane", "doctor kim", "bangal tiger"]
true
["shane", "doctor kim", "bangal tiger", "thelovemsg"]
["shane", "doctor kim", "bangal tiger", "thelovemsg"]
true

 

무언가 이상하다... 키메라가 생각하기에 다른 배열을 새롭게 만들어서 사용하기에 문제가 없을 줄 알았는데

 

왜 arr1 값을 바꿧는데  arr2값이 변하는거지?

 

그것은 바로 arr2에 arr1의 주소를 넣어줬기 때문이다. 

 

이것을 얕은 복사라고 한다. 

 

그러면 어떻게 해야 하는가? spread 연산자를 사용하면 해결된다. 

 

let arr1 = ['shane', 'doctor kim', 'bangal tiger']
let arr2 = [...arr1];

console.log(arr1);
console.log(arr2);
console.log(arr1 === arr2);

arr1.push('thelovemsg');
console.log(arr1);
console.log(arr2);
console.log(arr1 === arr2);

 

어떤 배열을 생성할 때 기존 데이터를 그대로 복사해서 사용하고 싶으면 spread 연산자( 한국어 번역으로는 전개 구문이라는데 맘에 안든다...)를 사용하면 된다. 

 

즉, spread 연산자는 얕은 복사를 도와주는 것이다. 

 

이것은 함수를 선언할 때도 사용이 가능하다. 

 

function add(...rest) {
  let sum = 0;
  for (let item of rest) {
    sum += item;
  }
  return sum;
}

console.log( add(1) ); // 1
console.log( add(1, 2) ); // 3
console.log( add(1, 2, 3) ); // 6

 

그런데 아까 무언가 비슷한것 본 거 같은데... 아하! 두번째 항목에 배열 메서드로 사용을 했었는데 단순한 경우에는 이렇게 사용도 가능할 것 같지만 ...

 

무언가 더 복잡한 경우에는 2번 항목의 배열 메서드를 사용하면 좋을 것 같다. 


이번에 회사에서 많이 고민하면서 js를 사용하다보니 기본적으로 어디까지 기능이 되는지를 잘 몰랐다.

 

그것을 알고자 하는 생각으로 이번 내용을 다시 정리했다.

 

이어지는 글에는 날짜 변환 / 객체에 대해서 공부할 예정이다. 물론 js 관련 기능도 더 정리해서 말이다.