본문 바로가기
기타 대외활동/투두몰 서포터즈

[구글 앱스 스크립트] 3주차 첫 번째 : 자바스크립트 제어문

by bri9htstar 2023. 7. 13.

이번 자바스크립트에서는 제어문을 배우나, 이전에 배웠던 기록이 있어서 특별히 짚고 넘어가야할 부분을 빼고는 이전에 올렸던 글로 대체하고자 한다. 제어문은 if 문이나 반복문을 사용하는 것이 대표적인데, 어느 프로그래밍 언어를 배우든 제어문은 기본이기도 하고 그래서 겹치거나 비슷하게 관통하는 개념도 많고 … (변명 이하 생략) 그래서 대체하고자 한다.

귀찮아서 아닙니다.

https://bri9htstar.tistory.com/34

 

[SW 코딩 훈련] 핵심 JavaScript 기초 02. 자바스크립트의 객체와 연산

프로퍼티와 메서드 객체는 프로퍼티와 메서드로 나뉘었는데, 프로퍼티는 객체가 가진 성질, 메서드는 객체가 하는 행동이었다. 그런데 비단 객체 자료형에만 의존하는 게 아니라 다른 자료형에

bri9htstar.tistory.com

https://bri9htstar.tistory.com/37

 

[SW 코딩 훈련] 핵심 JavaScript 기초 03. 자바스크립트 제어문

조건문 주어진 조건에 따라 결과값을 출력하는 구문. 조건으로는 비교 연산자 또는 논리 연산자를 사용한다. if 문 if ( 조건 ) { 수행할 명령 } if ~ else 문 else if 문 여러 개의 조건문을 생성할 때

bri9htstar.tistory.com

 


비교 연산자를 쓸 때 주의

자바스크립트에서는 '==' 연산자 말고도 등호를 세 번이나 쓰는 '===' 연산자가 있는데 무슨 의미일까?

 

연산자 설명 예시
== 좌변과 우변이 같으면 true 10 == 10 //true
10 == 11 //false
!= 좌변과 우변이 같지 않으면 true 10 != 10 //false
=== 좌변과 우변이 데이터 타입을 포함해 같으면 true 10 === 10 //true
10 === '10' //false
!== 좌변과 우변이 데이터 타입을 포함해 같지 않으면 true 10 !-- 10 // false
10 !== '10' //true

이 연산자들에 대한 설명은 밑에서 더 보충 설명하기로 한다.

 

기본적인 연산자는 직관적으로 이해하기 쉽지만, 몇 가지 주의할 점이 있다.

아래 예제에서 로그는 각각 어떤 boolean 값을 출력할까?

 

function myFunction() {
  const words1 = ['Google', 'Apps', 'Script'];
  const words2 = ['Google', 'Apps', 'Script'];
  console.log(words1 == words2); // false

  const personA = {name : 'Bob'};
  const personB = {name : 'Bob'};
  console.log(personA == personB); // false
}

 

배열과 객체 두 가지 모두 로그에 false가 출력된다. 배열이나 객체를 변수에 대입한 경우 변수에 저장되는 것은 메모리상의 주소(참조값)이다. 따라서 요소나 구조가 완전히 같아도 물리적으로 다른 주소에 할당되어 있는 배열 또는 객체끼리의 비교는 모두 false가 된다. 2주차 첫 번째 공부글에 썼던 이미지를 다시 가져오면 생각날 수도 있다.

 

배열, 객체는 Non-Primitive

그렇다면 이런 예제는 어떨까?

 

function myFunction() {
  const words1 = ['Google', 'Apps', 'Script'];
  const words2 = words1;
  console.log(words1 == words2); // true

  const personA = {name : 'Bob'};
  const personB = personA;
  console.log(personA == personB); // true
}

 

두 로그는 모두 true가 출력된다. 대입식의 우변을 변수로 하는 것은 해당 참조값을 대입하는 것과는 다르다. 따라서 두 개의 변수가 가리키는 메모리의 주소도 같으므로 비교 결과는 true가 된다.

기억하기 : 배열과 객체는 참조값을 비교한다.

 

관용적인 비교와 엄밀한 비교 : '=='와 '===' 그리고 '!=' 와 '!=='의 차이에 관한 보충

연산자 '=='와 '!='는 좌변과 우변의 데이터 타입이 달라도 데이터 타입을 변환한 상태에서 비교한다.  한편 연산자 '=='와 '!=='는 좌변과 우변의 데이터 타입이 다른 것을 엄밀하게 판단해 비교한다. 그래서 비교 연산자로는 === 와 !==을 우선해서 사용해야 예상치 못한 결과가 나타나는걸 막을 수 있다.

 

function myFunction() {
  console.log(5 == '5'); // true
  console.log(5 === '5'); // false

  console.log(5 != '5'); // false
  console.log(5 !== '5'); // true
}
When you compare two values via ==, one of the values may undergo coercion.
Coercion – Automatically changing a value from one type to another.

 

 

그래서 이런 짤도 있다

Coercion의 끝판왕을 알고 싶으면 이 짤을 보면 된다.

?

 

첫 번째 경우는 기초적인 수준에서 이해할 수 있지만 2, 3, 4번째는 대체 무슨 소리일까?

https://www.freecodecamp.org/news/explaining-the-best-javascript-meme-i-have-ever-seen/

 

The Best JavaScript Meme I've Ever Seen, Explained in detail

TLDR: Coerce yourself to use triple equals. I unintentionally found this JavaScript meme on Reddit, and it's the best one I've ever seen. You can verify this meme's accuracy by running each code snippet in Developer Tools. The result isn't surprising, but

www.freecodecamp.org

궁금하다면 위 페이지에서 그 답을 얻을 수 있다. 영어지만 영어보다도 코딩 내용을 이해하기가 더 어려우니 주의 …

 

논리값으로 타입 변환

자바스크립트에서는 조건식으로 주어진 데이터 타입의 값을 암묵적으로 논리값으로 바꾸는 특성이 있다.

 

function myFunction() {
  if (123 && 'abc' && [1, 2, 3] && {lunch : 'curry'}) {
    console.log('123은 true');
    console.log("'abc'도 true");
    console.log('[1, 2, 3]도 true');
    console.log("{lunch : 'curry'}도 true");
  }

  if (!0 && !'' && !undefined && !null) {
    console.log('0은 false');
    console.log('빈 문자도 false');
    console.log('undefined도 false');
    console.log('null도 false');
  }
}

 

실행 결과

 

두 번째 조건문의 경우 모두 조건식 ! 연산자로 반전시켰기 때문에 반전하기 전 값이 false로 타입 변환된 것을 알 수 있다. 각 데이터 타입이 어떤 논리값으로 변화되는가는 다음과 같다.

 

데이터 타입 변환 후의 값
TRUE FALSE
수치 0과 NaN을 제외한 모든 수치 0, NaN
문자열 길이가 1 이상인 문자열 빈 문자
null, undefined - null, undefined
배열, 객체 모든 배열 및 객체 -

 

요소가 없는 배열과 객체도 논리값으로 타입 변환을 했을 때 true가 나오기 때문에 배열이나 객체의 요소가 한 개 이상 존재하는 가를 판단할 때는 다른 방법을 사용해야 한다.

 

 

Swtich문을 이용한 다중 분기

if문으로는 이분법적으로 조건을 분기할 수 있지만 그 이상의 분기를 주고 싶을 때 if ... else if문 말고도 swtich문이 있다.

 

swtich(식) {
  case 값 1 :
    // 식 === 값 1일 때 실행할 처리
    break;
  case 값 2 :
    // 식 === 값 2일 때 실행할 처리
    break;
  …
  default:
   // 식이 모든 값과 일치하지 않을 때 실행할 처리
}

 

일치하는 값이 존재하면 그 위치의 case절 처리를 진행하고 break문에서 swtich 블록 전체를 벗어나는 구조. 그리고 식의 결과와 일치하는 값이 존재하지 않으면 default절의 처리를 실행한다. 만약 case절 처리 끝에 있는 break문을 생략해버리면, 식에 해당하는 case 처리 이후의 모든 처리를 실행하기게 된다. 일반적으로는 각 case 절 내의 처리만을 실행하고 싶은 경우가 많으므로 그 때는 꼭 swtich 블록을 벗어날 수 있게 각 case 절 끝에 break문을 입력하자.

 

이해를 돕는 도표

아래는 그 시절 성적표기법인 '수우미양가'를 이용한 switch문 예제.

 

function myFunction() {
  const rank = '우';
  switch(rank){
    case '우':
      console.log('대단하네요.');
      break;
    case '양':
      console.log('열심히 했군요');
      break;
    case '가':
      console.log('아슬아슬했네요.');
      break;
    default:
      console.log('다음에 더 열심히 합시다');
  }
}

 

 

 

 

while문을 사용할 때 주의해야 할 무한 루프

https://bri9htstar.tistory.com/37

 

[SW 코딩 훈련] 핵심 JavaScript 기초 03. 자바스크립트 제어문

조건문 주어진 조건에 따라 결과값을 출력하는 구문. 조건으로는 비교 연산자 또는 논리 연산자를 사용한다. if 문 if ( 조건 ) { 수행할 명령 } if ~ else 문 else if 문 여러 개의 조건문을 생성할 때

bri9htstar.tistory.com

개괄적인 반복문 훑어보기는 여기서 가능.

 

while문을 사용할 때 꼭 while 루프를 벗어나도록 조건을 만들어야 하는데, 만약 while문의 조건식이 false가 되지 않고 무한히 처리가 반복되면 무한 루프에 빠지게 된다. 아래에는 그렇게 잘못 짠 예시.

 

오류인지 표현식 삽입(달러 표시)가 일어나지 않는다

 

 

이 상태에서 무한 루프에 빠졌을 때는 GAS 툴바에서 [□중지]를 클릭해 스크립트 실행을 중지하면 된다. 근데 가장 좋은 것은 while문을 사용할 때는 코드를 실행하기 전 무한 루프가 발생할지를 미리 확인하는 것이다.

 

for … of문 vs for … in문

 

비슷한 듯 다른 for … of문과 for … in문을 비교해보자.

 

for … of문을 이용한 반복

배열에 포함된 모든 요소에 무언가의 처리를 하고 싶을 때는 for … of문을 사용하면 편리하다. 배열이나 문자열 등 반복 가능 객체에 포함된 모든 요소에 대해 반복 처리를 수행할 수 있다.

 

for (변수 of 반복 가능 객체) {
  // 루프 안에서 실행할 처리
}

 

for … of문은 배열 등의 반복 가능 객체에 포함되는 요소를 정해진 순서대로 꺼내고 그것을 변수에 대입하면서 루프를 실행한다. 다음에 꺼낼 요소가 없으면 루프를 종료한다. 요소를 대입하는 변수 대신 const 키워드 선언을 이용해 상수를 이용할 수도 있다.

 

function myFunction() {
  const members = ['Bob', 'Tom', 'Jay'];
  for (const member of members) {
    console.log(member);
  }

  for (const char of 'Hello'){
    console.log(char);
  }
}

실행 결과

 

for문으로도 구현할 수 있다.

 

function myFunction() {
  const members = ['Bob', 'Tom', 'Jay'];
  for (let i = 0; i < 3; i++) {
    console.log('%d: %s', i, members[i]);
  }

  for (let i = 0; i < 5; i++){
    console.log('%d: %s', i, 'Hello'[i]);
  }
}

 

실행 결과

 

for … in문을 이용한 반복

for … in문을 사용하면 모든 속성에 대해 반복을 수행할 수 있다.

 

for (변수 in 객체) {
  // 루프 안에서 실행할 처리
}

 

객체 안의 속성을 임의로 꺼내서 변수에 저장한 뒤 루프 안에서 처리를 실행할 수 있다. 객체 안의 모든 속성을 꺼낸 뒤 루프가 종료된다. 여기에서 변수로 다룬 것은 값이 아니라 속성 자체인 점에 주의하자. 그리고 for … of문과 같이 속성을 대입하는 변수 대신 const 키워드를 이용해 상수를 사용할 수 있다.

 

function myFunction() {
  const points = {Korean : 85, Math: 70, English: 60};
  for (const subject in points) {
    console.log('%s 점수 : %d', subject, points[subject]);
  }
}

 

for … in문을 이용해 객체 points 안의 각 속성을 상수 subject에 저장했다. 상수에 저장된 속성은 문자열 타입이 되며 괄호 표기법을 통해 points[subject]로 각 속성값을 꺼낼 수 있다.

 

for … of문은 요소를 꺼내는 순서를 보증하지만 for … in문은 속성을 꺼내는 순서를 보증하지 않는다.
배열 루프에서는 for … in문을 사용해서는 안 된다. 순서를 보증하지 않음은 물론이고 예상하지 않은 요소까지 반복 대상에 포함될 가능성이 있다. for문, for … of문 또는 Array 객체의 반복 메서드를 사용하는 것이 좋다.

 

루프에 라벨 붙이기

if문 같은 조건 분기, while문이나 for문 같은 반복을 사용할 때 블록 안에 블록을 포함하는 구조를 중첩이라 부른다.

break문에서는 가장 안쪽의 루프를 중단하는 것이 규칙이고, continue문도 마찬가지로 중첩되어 있을 때는 가장 안쪽의 루프만 건너뛴다. 중첩되어 있을 때 바깥쪽 루프를 중단하거나 건너뛰고 싶을 때는 라벨을 부여한다. 라벨은 루프에 부여할 수 있는 식별자로, 루프의 앞에 : (콜론)을 사용해 다음과 같이 입력한다.

 

라벨:

 

아래 예제를 보자. 바깥쪽 루프에는 outerLoop라는 라벨을 부여하고 break문으로 해당 라벨을 지정한다. 실행 결과를 보면 카운터 i와 j가 동시에 2가 되었을 때 로그 출력을 종료하도록 했다. 만약 라벨 기능을 쓰지 않았다면, 카운터 변수 i와 j가 동시에 2가 됐을 때만 로그가 안 출력되고 나머지는 계속 처리되었을 것이다.

 

function myFunction() {
  outerLoop:                // 바깥쪽 루프에 라벨 outerLoop를 부여한다.
  for (let i = 1; i <= 3; i++) {
    for (let j = 1; j <= 3; j++) {
      if (i === 2 && j === 2) {
        break outerLoop;    // 라벨 outerLoop를 부여한 루프를 중단한다. 
      }
      console.log('i값: %d, j값: %d', i, j);
    }
  }
}

 

실행 결과

 

예외와 예외 처리

GAS로 스크립트를 실행할 때 예상하지 않은 에러가 발생할 수 있다. 이렇게 발생한 에러를 자바스크립트에서는 예외(exception)이라고 부른다.

 

간단한 스탠드얼론 스크립트이지만 에러가 발생하는 예시

 

예외가 애초에 없도록 스크립트를 작성하는 것이 이상적이지만 충분히 에러가 생길 수도 있기 때문에, 예외에 대응하는 예외 처리가 필요하다. 예외 처리를 이용하면 예외가 발생한 것을 감지하고 그에 따른 처리를 계속하거나 예외 내용에 따라 처리를 분기할 수 있다.

 

try … catch … finally문

자바스크립트에서 예외 처리를 할 때 사용한다.

 

try {
  // 예외를 검지할 대상 처리
} catch(변수) {
  // 예외가 발생했을 때 실행할 처리
} finally {
  // 예외 발생에 관계없이 실행할 처리
]

 

아까 전 예외가 발생한 예제를 이 구문으로 다시 작성해보자.

 

 

예외가 발생해도 스크립트가 정지하지 않고 catch 블록이 실행되고 finally 블록은 예외 여부와 관계없이 실행된다.

 

예외를 잘 잡아보자.

 

 

throw문

예외를 수동적으로 검지해 이용할 뿐만 아니라 스크립트 안에서 예외를 능동적으로 발생시키는 빌런짓을 시킬 수 있다.

그리고 예외를 발생시키는 것을 '예외를 던진다'라고 말한다.

throw new Error(에러 메시지)

누구야. 누가 그랬어.

throw문과 try … catch문을 조합할 수 있다. x의 값이 0일 때 예외를 던지고 처리를 분기할 수 있다.

function myFunction() {
  const x = 0;
  try{
    if (x === 0) {
      throw new Error('x에 0 넣은 사람 나와.');
    }
  } catch(e) {
    console.log('너 때문에 예외 발생했잖아: ' + e.message);
  }
}

실행 결과

 

상수 x의 값이 0일 때 예외를 던지고 처리를 분기할 수 있다.

저주 받은 코드

GAS에서는 사용자의 조작, HTTP 통신 결과, 할당 및 제한 등 다양한 외부 요인으로 예외가 발생한다. 이런 가능성이 있는 위치에서는 try … catch문을 사용하는 것이 좋다. 이런 조건 분기와 반복 등 제어 구문은 GAS 프로그래밍에서 매우 자주 사용하게 되므로 확실하게 기억하는 것이 좋다.


이번 장 내용 … 매우 많다. 그래서 줄이고 또 줄였다.

진짜 내가 하고 ㅠ 싶은 말

 

추후 내가 클래스를 만드려면 GAS에 대해서도 배워야 하는데 아무래도 GAS가 자바스크립트를 기반으로 작동하고 책의 절반이 JS인만큼 어느 정도 배우지는 않으면 안 되는 거 같다 ㅠ 진짜 힘들지만 완벽해지려면 더 배우고 싶다. 재밌기는 하다. 근데 머리털이 너무 많이 빠져서 탈모가 올 거 같지만 뭐든지 쉬운 방법을 알려면 어려운 방법부터 가야하는 법. 난 그 과정을 겪고 있다고 생각한다.

그래도 어떻게든 끝내서 행복한 나


위 글은 투두몰 서포터즈 활동의 일환으로 작성된 글입니다.

https://edu.todomall.kr/?utm_source=supporters&utm_medium=contents

 

투두몰 ㅣ 일잘러의 투두리스트를 훔치다

일잘러의 투두리스트를 훔치다! 오피스 툴을 직접 따라하며 배우고, 과제를 통해 결과물을 만들어요.

edu.todomall.kr

댓글