[Kakao Cloud School] 2번째 회고록

lango·2022년 11월 14일
0
post-thumbnail

Intro


올바른 주니어가 되기위한 비기너가 되자

이번 주 교육을 진행하면서 이미 알고 있던 내용들도 있었지만 처음 배우는 내용도 있었고, 잘못 알고있던 내용들도 많았다. 이전에는 공부하지 않았음에 채찍질했지만 이제는 공부한 것이 올바른 내용인지를 계속 확인하면서 내것으로 만들어야 한다고 느꼈다.

그리고 동료들에게 자만하고 오만적인 태도로 소통했던 것을 반성한다. 나만의 주관적인 관점에서 생각하고 판단한 내용이나, 확실하게 알고 있지 않던 지식적인 내용들을 전달한 것은 아닌가? 하고 돌아보게 된다. 하지만 이 과정들을 통해서 더 공부할 수 있을테고, 동료들에게 더 많이 배울 수 있다고 생각한다.




Day - 4

2주차 교육이 시작됐다. 자바스크립트를 본격적으로 학습하면서 실무에선 무엇이 더 나은지 왜 나은지를 알고 이해하려고 노력했고, 애매하게 알고 있던 지식들은 다시 복습하는 시간을 통해 올바르게 이해할 수 있었던 것 같다.


if-else문과 삼항연산자

전부터 삼항연산자를 사용해오며, 특히 알고리즘 문제를 풀 때 간결한 코드로 작성할 수 있다는 점을 인지하고 있었다. 삼항연산자는 if-else문과 거의 동일한 기능을 제공한다. 코드는 간결하게 작성할 순 있지만 성능에도 영향을 줄 수 있을까? 라는 의문이 들었다.

int a = 5;
int b = 10;
int bigNum = 0;

// if-else문 사용
if(a>b) {
	bigNum = a;	
}
else {
	bigNum = b;
}

// 삼항연산자 사용
bigNum = a>b ? a:b;

처음 제어문을 배울 때는 if-else문의 경우는 가독성은 좋아보이지만 코드가 길고 그에 반해 삼항연산자를 사용한다면 코드를 간결하게 작성할 수 있지만 썩 좋은 가독성을 보이지는 않는다고 생각했었다.

하지만 이 역시 if-else문이 더 좋아!, 아니 삼항연산자가 더 좋아! 라고 정답을 내릴 수 없어 열심히 다른 분들의 문서를 찾아보다가 삼항연산자의 장점을 잘 정리해두신 분이 계셔 인용해왔다.

Eric Elliott님의 삼항연산자의 멋짐을 모르는 당신이 불쌍해 라는 게시글인데 삼항연산자가 if-else문과 비교하여 몇 가지 더 나은 부분들에 대해서 알 수 있었다. 그 내용은 다음과 같다.

  1. 항상 위에서 아래로 일직선으로 읽을 수 있도록 작성하는 것이 좋습니다. 직선을 따라 읽으면 체인된 삼항식을 무리없이 읽을 수 있습니다.
  2. 삼항 연산자는 구문의 혼란을 줄입니다. 적은 코드 = 버그가 적은 표면 = 버그가 적습니다.
  3. 삼항 연산자는 임시 변수를 필요로하지 않으므로 단기기억력의 부하가 줄어 듭니다.
  4. 삼항식은 신호 대 잡음비가 더 좋습니다.
  5. if문은 부수작용과 변이를 권장하지만, 삼항연산자는 순수한 코드를 권장합니다.
  6. 순수한 코드는 표현과 기능을 서로 분리시킴으로써 우리가 더 나은 개발자가 될 수 있도록 합니다.

요즘 꾸준하게 알고리즘 문제를 풀어가면서 다른 분들의 삼항연산자를 활용한 몇 줄 안되는 간결한 코드 풀이를 보면서도 삼항연산자를 사용하면 무의식적으로 가독성은 떨어지는 것 같아라는 생각으로 잘 활용하지 않았는데 위 글을 보고 생각이 조금 바뀌게 되었다.



void는 실무에서 사용할까?

내가 알고 있는 void는 요청한 결과를 리턴하지 않는다. 많은 CRUD 예제에서는 void를 사용하여 수정이나 삭제등을 수행한다. 그런데 실무에서는 void를 지양해야 한다고 이전부터 종종 들어왔다. 그렇다면 void를 왜 지양해야 하는 것일까?

// POST: 게시글
// postUpdate: 게시글 수정
public void postUpdate(Post post) {
	...
}

위 void를 사용한 구문을 보면 post라는 게시글을 수정하는 updatePost를 통해 수정작업을 하는데 수정 처리든 뭐든 처리를 한 후 별도의 리턴값을 넘겨주지 않는다.

이 것은 굉장히 위험하다고 생각이 들었다. 왜냐하면 post라는 원본 게시글에 대해서 수정작업을 하는데, 중간에 어떤 문제가 발생하여 수정이 안되거나 할 수도 있기 때문이다. 만약 수정 중간에 문제가 생겨 수정 작업이 반영이 안되었다면 다시 이전 원본 게시글로 돌아가거나 수정작업이 실패했다는 등의 처리를 해야한다고 생각이 들었다.

특정 작업에 대한 결과를 알려주거나 조작하기 위해 void 형은 가능하다면 사용하지 않도록 해야겠다.



for문을 사용해야 해? 아니면 while문을 사용해야해?

여태까지 for문이든 while문이든 반복문을 수없이 많이 사용해왔는데 특정 상황일 경우에만 두 구조를 구분하여 사용했지, for문과 while문 중 어느 구조를 사용해야 좋을지 딱히 생각해본 적이 없었다. 한번 간단하게 살펴보자.

// for문
for(int i=0; i<N; i++) {
	...
}

//while문
int i = 0;
while(i<N) {
	...
}

for문의 경우 데이터의 범위가 정해져 있을 경우에 주로 사용하고, while문의 경우는 데이터의 범주를 정확하게 모를 때, 즉 몇번 반복될지 모를 때 사용한다.

for문을 살펴보면 조건을 작성할 때 대부분의 경우는 내부에서 변수를 초기화한다. 그리고 while문의 경우는 순회시 사용할 변수를 미리 준비한다면 변수 초기화 코드를 작성할 필요가 없어 만약 반복에 사용할 변수가 미리 준비된 상태라면 while문을 사용하는 것이 더 직관적으로 느껴졌다.

사실 성능상 큰 차이는 없지만 코드를 한 줄에 작성하냐, 여러 줄에 걸쳐 작성하냐를 통해 코드의 가독성을 생각할 수 밖에 없다고 생각한다.

결국 두 반복문 구조를 확실하게 알아두고 특정한 상황에 맞춰 사용하되 가독성 측면에서나 무한루프를 구현하고 싶을 경우엔 while문을 사용하는게 더 좋지는 않을까? 라는 생각이다.



Day - 5

5일차에는 자바스크립트의 함수와 클래스 등의 기초적이지만 필수적인 CS에 대해서 배웠다.


Normal Function 말고 Arrow Function을 쓰라고?

자바스크립트에서 함수를 작성하는 법은 다양하다. 일반함수, 함수표현식, 즉시실행표현식, 생성자함수, 화살표 함수 등의 방식으로 함수를 선언할 수 있다. 이 중에서 일반함수, 함수 표현식, 화살표 함수에 대해서 알아보고 화살표 함수를 왜 사용해야 하는지를 알아보려한다.

  • 일반 함수(Normal Function)
    가장 대중적인 방법이다. 호이스팅이 허용되기에 어느 스코프에서든 호출 할 수 있는 함수가 된다. 일반 함수를 작성하면 아래와 같다.
    function resetPost() {
        ...
    }
  • 함수 표현식(Function Expressions)
    익명함수(이름이 없는 함수)를 변수에 담은 방식이다. 자바스크립트에서 자체적으로 함수의 이름을 변수의 이름으로 측정한다.
    resetPost 함수를 함수표현식으로 작성하면 아래와 같다.
    let resetPost = function() {
        ...
    }
  • 화살표 함수(Arrow Function)
    기존의 함수표현식에서 function 키워드를 쓰지않고 화살표(=>)를 추가하여 화살표 함수로 작성할 수 있다. 화살표 함수는 아래와 같이 작성한다.
    let resetPost = () => {
        ...
    }

Normal Function의 문제점

일반 함수(normal function)의 치명적인 단점은 바로 호이스팅이 된다는 것이다. 아래 코드를 보면 resetPost 함수를 선언하기 전에 resetPost 함수를 호출하였다. 결과는 어떻게 나올까?

resetPost();

function resetPost() {
	console.log("Post를 초기화 합니다!");
}
// Post를 초기화 합니다! 출력

호이스팅이 되어 함수가 실행되고 있음을 알 수 있다. 그러면 화살표 함수는 어떨까?
resetPost 함수를 화살표 함수로 고쳐보자.

resetPost();

let resetPost = () => {
	console.log("Post를 초기화 합니다!");
}

먼저 함수를 호출하니 선언되지 않은 함수라고 에러로 알려준다. 화살표 함수를 사용하면 호이스팅이 되지 않기에 개발자의 입장에서 바라볼 때 더 편하다고 느껴졌다. 실제로도 많은 최신 문서에서 화살표 함수를 사용하고 있다. 그래서 화살표 함수가 일반 함수와 무엇이 다른지 찾아보았다.

화살표 함수의 특징

MDN 문서를 살펴보니 화살표 함수는 function 표현에 비해 구문이 짧고 자신의 this, arguments, super 또는 new.target을 바인딩 하지 않는다. 화살표 함수는 항상 익명으로써 이 함수 표현은 메소드 함수가 아닌 곳에 가장 적합하고 그렇기에 생성자로서 사용할 수 없다고 한다.

왜 화살표 함수를 메서드에서 사용하면 안될까?

player라는 객체를 작성하였다. 객체 내부의 move라는 메서드를 화살표 함수로 작성하였는데, move 메서드에서 동일 레벨 선상에 있는 name을 this로 가리켜 호출할 경우 undefined가 출력된다.

const player = {
	"name":"adam",
  	move: () => {
    	console.log(this.name);
  	}
}
player.move();
// undefined 출력

화살표 함수에서의 this는 상위 스코프 window 객체를 가리키게 된다. 위 코드에서는 상위 스코프에 name이 없기 때문에 undefined를 출력함을 알 수 있다.

window 객체에 name 값을 할당해주면 player의 move 메서드에서 name값을 가져올 수 있다.

window.name = "adam"
const player = {
	"name":"adam",
  	move: () => {
    	console.log(this.name);
	}
}
player.move();
// adam 출력

그래서 메서드로 사용하는 것은 일반 함수나 함수표현식으로 사용하는게 더 바람직하다. 아래와 같이 일반함수로 작성한 메서드의 경우는 정상적으로 동일 선상의 name을 가져올 수 있다.

const player = {
	"name":"adam",
  	move: function() {
    	console.log(this.name);
  	}	
}
player.move();
// adam 출력

화살표 함수로 선언한 메서드를 prototype에 할당하는 경우도 동일한 문제가 발생하기에 일반 함수로 할당해야하며, 생성자 함수로 사용할 경우도 화살표 함수는 prototype 프로퍼티를 가지고 있지 않기 때문에 바람직하지 않다.

화살표 함수가 간결하여 가독성은 좋을 지 모르지만 위와 같은 상황들에 맞춰 사용할 줄 알도록 복습하고 복습해야겠다.



Primitive Type과 Reference Type?

자바스크립트에서 제공하는 자료형의 종류인 primitive type과 refernect type별로 데이터가 할당되는 기준에 대해서 다시 알아보고자 한다.

  • Primitive Type(원시 타입)
    변수에 데이터의 실제 값이 할당되며 메모리에 고정된 크기로 저장이 된다. 변수가 해당 데이터의 값을 보관하기에 새로운 값을 할당할 경우 변수에 저장된 데이터 값이 변경된다. Boolean, Number, String, Null, Undefined 등이 원시타입에 해당된다.

  • Reference Type(참조 타입)
    변수에 데이터의 위치 값만 할당하며 해당 변수에 데이터 값이 직접 저장되지 않고 데이터에 대한 참조만 저장된다.(데이터 값이 저장된 힙 메모리의 주소값이 저장됨) 다양한 Obejct(객체)들, Array, Function, Object 등이 참조타입에 해당된다.

먼저 원시타입의 예시를 들어보자.

let name = "adam";
let person = name;
name = "lina";
console.log(name, person);
// lina adam 출력

name의 데아터 값을 person에 할당하였는데, 변수 복사가 되어 name은 lina, person은 adam을 출력함을 알 수 있다.

그리고 참조타입의 예시코드도 살펴보자.

let arr1 = [10, 20, 30];
let arr2 = arr1;
arr2[0] = 100;
console.log(arr1, arr2);
// [100, 20, 30] [100, 20, 30] 출력

위와 같이 arr1이라는 배열을 arr2 변수에 할당할 경우 arr1 배열 데이터값의 참조가 복사되어 할당되기에 arr2의 첫번째 원소를 변경했음에도 동일한 배열 객체를 가리키고 있기에 arr1의 첫번째 원소값도 변경됨을 알 수 있다.

그렇다면 arr1과 arr2 모두 배열을 할당하면 어떻게 될까?

let arr1 = [10, 20, 30];
let arr2 = [10, 20, 30];
arr2[0] = 100;
console.log(arr1, arr2);
// [10, 20, 30] [100, 20, 30] 출력

arr1과 arr2 모두 새롭게 배열을 만들어 변수에 할당했기에 동일한 배열 객체를 가지고 있지 않다. 즉 변수마다 각각 새로운 메모리 위치를 만들어 위치값을 저장했기에 arr2의 원소값을 변경해도 arr1과는 다른 참조값을 가지기에 arr2의 원소값만 변경됨을 알 수 있었다.

원시타입와 참조타입의 내용을 꼭 숙지하고 변수를 선언할 때마다 원시타입으로 값을 할당해야할지 참조타입으로 할당해야할지 고민하고 요구사항에 맞출 수 있도록 해야겠다.



Day - 6

6일차에는 상속, 오버로딩, 오버라이딩 등과 같은 기본적인 CS 지식을 배우며 다시 한번 개념을 재정의할 수 있어 좋은 시간이었다.


Java와 JavaScript에서의 오버로딩(Overloading)

자바스크립트는 자바처럼 같은 이름의 함수를 여러 개 생성하면 가장 최신에 작성한 함수만 바라보게 된다. 즉 가장 최신 함수로 덮어쓰기가 되버린다.

동일한 이름의 클래스를 작성해보았다.

var Post = class {
 	constructor(user, title, content) {
    	this.user = user;
	    this.title = title;
    	this.content = content;
  	}
  	getPostInfo() {
    	return this.user + " " + this.title + " " + this.content;
  	}
}

var Post = class {
  	constructor(title, content) {
    	this.title = title;
	    this.content = content;
  	}
  	getPostInfo() {
    	return this.title + " " + this.content;
  	}
}

let post1 = new Post("adam", "제목1", "내용1");
post1.getPostInfo();
// adam 제목1 출력

위 코드를 실행하면 첫번째 Post 클래스는 무시되고 두번째 Post 클래스로 동작함을 알 수 있다. 이러한 점을 사전에 방지하기 위해서는 어떻게 해야 할까? 바로 var 키워드를 지양하는 것이라고 생각이 들었다. let 키워드로 두 클래스를 작성해보면 아래와 같이 이미 선언된 클래스라고 친절하게 에러로 알려주기에 개발 도중에 먼저 확인하고 수정할 가능성이 높아진다.

Uncaught SyntaxError: Identifier 'Post' has already been declared

결국 인스턴스를 만들기 위해 생성자를 호출해야 하는데, 생성자를 직접 만든다고 하면 오버로딩 때문에 여러개 만들어도 소용이 없다.

꼭 생성자는 1개만 만들어서 사용하자.



상속(Inheritance)은 부모-자식 개념과 다르다?

본래 상속이라 함은 부모가 자식에게 무언가를 물려준다 라는 개념으로 알고 있었지만, 개발을 하며 상속이라는 개념에 대해서 다시 생각해보게 되었다.

결론부터 말해서 상속을 적용한다고 하면 단순히 부모-자식의 개념보다는 하위 클래스들을 작성할 때 공통사항이 존재할 경우 상위 클래스를 만들어 하위 클래스에서 상속을 하는 형태로 구조화를 해야한다.

간단히 예를 들어 살펴보자.

class Move {
	playerStanding();
  	...
}

class HansUp {
	playerStanding();
  	...
}

위의 Move와 HandUp 클래스를 살펴보면 playerStanding 메서드를 공통적으로 사용하고 있다. 이 것을 상위 클래스를 만들어 별도로 playerStanding 메서드를 제공할 수 있도록 만들 수 있다.

class DefaultPlayer {
	playerStanding() {
    	console.log("사용자 "+ this.name +" 님이 움직일 준비가 되었습니다.");
    }
}

class Move extends DefaultPlayer {
	...
}

class HandsUp extends DefaultPlayer {
	...
}

위와 같은 구조로 상위 클래스르 별도로 만들어 playerStanding 메서드를 선언해둔다면 해당 상위클래스를 상속받는 하위 클래스들은 별도의 선언없이 playerStanding 메서드를 사용할 수 있다.

이런 상속의 개념을 적용하여 각 메서드들을 중복없이 잘 분리할 수 있도록 구조화하는 연습을 자주 해야겠다고 느꼈다.



매개변수 그룹화

보통 일반적으로 함수를 활용하는 예제들을 보면 아래와 같이 매개변수가 많지 않다.

let playerMove = (user, direction) => {
  	...
}

하지만 전달해야할 매개변수의 양이 많아진다면 어떻게 될까?
코드의 가독성도 떨어지고 함수마다 동일한 매개변수를 준비해야 하니 비용도 많이 들어갈 것이라고 예상된다.


let playerMove = (player) => {
  	...
}

하지만 위와 같이 매개변수에 필요한 값들을 객체로 미리 만들어서 넘겨준다면 단지 관련된 객체 하나만 유지보수하면 되기에 더욱 바람직한 방식이 될 수 있다고 생각이 들었다.



Math 클레스를 인스턴스화 하지 않고 바로 사용할 수 있는 이유

여태 Math 클래스를 통해 각종 연산을 진행했었는데, 클래스와 인스턴스에 대해서 배우며 이 Math 클래스의 인스턴스를 별도로 만들지 않고 바로 Math.abs, Math.sqrt .. 등으로 사용할 수 있는지 의문이 생겼다.

먼저 공식문서에서 Math 클래스에 대해서 어떻게 알려주는지 보기 위해 java 1.8 document를 살펴보았다.

확인해보니 모든 메서드가 static임을 알 수 있었다. static 키워드가 있다면 인스턴스가 아니라 클래스 원본에만 접근이 가능하다.

그렇기에 Math.xxx 등으로 해당 클래스를 사용할 수 있었던 것임을 다시 한번 깨닫게 되었다.



Day - 7

7일차에는 클라이언트에게 로그인을 하는 등 DOM에서 제공하는 다양한 것들을 알게 되었다.


서버와 클라이언트

클라이언트가 서버에게 특정한 요청을 할 때마다 요청에 대한 작업을 할 때 비용이 발생한다.

그래서 클라이언트에서 가능한 처리할 수 있는 작업들은 클라이언트 단에서 처리하고 서버는 서버가 본래 할일에 초점을 두어 할 수 있도록 적절하게 분리해야한다.

결국 투입되는 비용을 아끼고 투자 비용대비 고효율의 산출물을 내기 위해 클라이언트와 서버가 분리되어 각자의 역할을 수행할 수도 있다고 느꼈다.



정규표현식으로 비밀번호 안전성 검증하기

정규표현식(RegExp)으로 로그인할 때 비밀번호의 유효성을 검사할 수 있다. 구글에서 많은 리소스를 찾을 수 있는데 그 중 하나를 살펴보자.

// 안전성이 높은 비밀번호(길이가 14 자 이상이고 기호, 대문자, 텍스트의 조합이있는 경우)
let strongRegex = new RegExp("^(?=.{14,})(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*\\W).*$", "g");

// 안전한 비밀번호(길이가 10 자 이상이고 기호, 대문자, 텍스트가 조합 된 경우)
let mediumRegex = new RegExp("^(?=.{10,})(((?=.*[A-Z])(?=.*[a-z]))|((?=.*[A-Z])(?=.*[0-9]))|((?=.*[a-z])(?=.*[0-9]))).*$", "g");

// 안전성이 약한 비밀번호(길이가 10 자 미만이고 기호, 대문자, 텍스트의 조합을 포함하지 않는 경우)
let enoughRegex = new RegExp("(?=.{8,}).*", "g");

위와 같이 안전성이 높은지 낮은지를 정규표현식으로 표현할 수 있다.
직접 입력한 비밀번호를 가지고 검증을 진행해보자.

<input type="password" name="userPwd" id="userPwd" />

먼저 비밀번호를 입력할 수 있도록 input 콘텐츠를 하나 만들어주자.

 let loginUserPwd = document.getElementById("userPwd");

그리고 스크립트에서 해당 DOM 객체를 변수에 담아둔다.

loginUserPwd.addEventListener("change", () => {
	if(strongRegex.test(loginUserPwd.value.trim())) pwdStrong.innerHTML = " 안전성이 높은 비밀번호 입니다.";
    else if(mediumRegex.test(loginUserPwd.value.trim())) pwdStrong.innerHTML = " 안전한 비밀번호 입니다.";
    else if(enoughRegex.test(loginUserPwd.value.trim())) pwdStrong.innerHTML = " 안전성이 약한 비밀번호 입니다.";
    else pwdStrong.innerHTML = " 안전성이 매우 약한 비밀번호 입니다.";    
});

그리고 해당 input 콘텐츠에 입력이 있을 때마다 이벤트 처리를 하기 위해 change 이벤트를 하나 만들어 안에서 조건문을 통해 비밀번호 안전성 검증을 할 수 있다.

1234 라는 비밀번호를 입력하면 다음과 같이 모든 정규표현식 조건에 부합되지 않아 else문에 해당되는 문구가 출력된다.

Test12345678!!! 라고 입력하면 강한 비밀번호 정규표현식 조건에 부합되어 최상단 if문에 해당하는 결곽 출력됨을 확인할 수 있다.

꼭 필요한 정규표현식들은 대개 위와 같이 복잡한 형식으로 이루어져있는데 암기할 필요없이 구글에서 찾아보는게 시간을 절약할 수 있을 것 같다.



왜 Oracle은 Cookie를 쓸까?

오라클은 접속하게 되면 다음과 같이 쿠키를 수집한다고 한다.

최근 클라이언트와 서버 애플리케이션을 분리해서 구현하는 경우가 많아지고 있다. 클라이언트와 서버가 분리되면 세션을 사용하지 못한다.

그래서 쿠키를 많이 사용한다. 쿠키의 또 다른 대안으로는 HTML5 API의 Local Storage를 활용할 수 있다.



Day - 8

8일차에는 자바스크립트에서의 Exception Handling과 프로세싱 등에 대해서 배웠다.


Undefined와 Null의 차이점

자바스크립트를 사용하면서 종종 undefined와 null을 보게되는데 둘의 차이점을 딱히 생각해본 적이 없었다. 그래서 이 둘이 무엇이 어떻게 다른지 알아보자.

undefined

먼저 undefined란 직역하면 정의되지 않았다는 뜻인데, MDN 문서를 확인해보면 아래와 같이 나와있다.

변수를 생성하고 해당 변수에 값을 할당하지 않은 상태라고 한다.

let userName;
console.log(userName);
//undefined 출력

위와 같이 변수를 선언하고 값을 할당하지 않은 상태로 해당 변수를 console에 출력해보니 undefined가 출력됨을 알 수 있다.

또한 메서드를 선언할 때 변수를 할당받지 않았다면 undefined가 반환된다. 이 경우도 직접 예를 들어 살펴보자.

let pointAdd = (nowPoint, getPoint) => {
    console.log(nowPoint, getPoint);
}
console.log(pointAdd(5));
// 5 undefined 출력

이 경우도 메서드를 선언한 후 메서드를 호출할 때 매개변수를 덜 넘겼더니 매개변수를 전달받지 못해 undefinded가 출력됨을 알 수 있었다.

마지막으로 함수가 값을 반환하지 않았을 때도 undefined를 반환한다고 한다.

let pointAdd = (nowPoint, getPoint) => {
    ...
}
let result = pointAdd(5,1);
console.log(result);
// undefined 출력

함수 내에서 return을 작성하지 않고 새 변수에 해당 함수를 호출한 값을 저장한다면 undefined를 출력한다.

Null

어떤 값이 의도적으로 비어있음을 표현하는 것이다. 앞에서 본 undefined는 값이 지정되지 않은 것을 뜻하지만, null은 해당 변수가 어떤 것도 가리키고 있지 않다는 것을 의미한다.

let point;
point = null;
console.log(point);
// null 출력

point변수에 null값을 할당하면 특정 값이나 자료형을 할당하지 않아 null이 출력됨을 볼 수 있는데 개발자가 직접 명시적으로 null값을 할당하여 값이 없는 변수임을 바로 알 수 있다.

결국 null은 값으로써 의미없는 값이 저장된 것이고, undefined는 값조차 저장되어 있지 않은 것이라고 이해하였다.



비동기 방식의 변천사

개발을 하면서 비동기로 작업을 처리해야 하는 경우는 생각보다 많다.

비동기 방식을 간단하게 설명하자면 현재 작업이 완료되지 않더라도 다음 작업을 실행하는 방식이다. 간단한 코드로 비동기 방식을 살펴보자.

let movePlayer = () => {
    console.log("사용자가 움직입니다.");
}
let SayHiPlayer = () => {
    console.log("사용자가 손을 흔듭니다.");
}
setTimeout(movePlayer, 3000);
setTimeout(SayHiPlayer);
// 사용자가 손을 흔듭니다.
// 사용자가 움직입니다.

movePlayer 메서드를 3초뒤에 실행한 후 SayHiPlayer 메서드를 실행할 것 같지만 movePlayer 메서드 종료여부에 상관없이 SayHiPlayer 메서드를 실행한다.

setTimeOut은 자바스크립트 내장 함수가 아닌, 브라우저에서 제공하는 웹 API이자 비동기 함수이기 때문에 비동기로 동작하게 된다.

비동기 방식의 종류

비동기 방식의 종류는 콜백함수, Promise, Async/Await 순으로 등장해왔다.

1. 콜백 함수
콜백 내에서 다시 비동기 작업을 처리할 경우 콜백 내 콜백이 만들어지는 상황이 있어 가독성이 떨어진다. 예외처리의 한계가 있어 콜백 안에서 예외가 발생하면 외부에서는 이를 처리할 수 없다.

콜백함수를 사용하다보면 콜백지옥이라고 불리는 현상을 겪을 수 있다.

콜백지옥이란?
콜백 함수를 익명 함수로 전달하는 과정에서 또 다시 콜백 안에 함수 호출이 반복되어 코드의 들여쓰기 순준이 감당하기 힘들 정도로 깊어지는 현상을 말한다.

직접 콜백지옥을 코드로 작성해보면 다음과 같다.

let oneTwoThree = () => {
    setTimeout(() => {
        console.log("one");
        setTimeout(() => {
            console.log("two");
            setTimeout(() => {
                console.log("three");
            })
        })
    })
}
oneTwoThree();
// one
// two
// three

코드를 보면 알겠지만 콜백함수를 남용하게 되면 코드의 가독성이 떨어지고 유지보수가 어렵게 된다.

2. Promise
위에서 본 콜백함수의 단점을 보완하기 위해 등장한 것이 Promise이다.
Promise는 주로 서버에서 받아온 데이터를 화면에 표시할 때 사용한다.

위에서 작성한 콜백지옥을 Promise를 활용하여 수정해보자.

let oneTwoThreePromise = () => {
    new Promise((resolve, reject) => {
        console.log("one");
        resolve();
    })
    .then(() => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log("two");
                resolve();
            },3000)
        })
    })
    .then(() => {
        console.log("three");
    })
    .catch((e) => {
        console.log(e);
    })
}
oneTwoThreePromise();
// one
// two
// three

Promise를 활용하니 콜백함수를 여러 번 사용했을 때보다 가독성이 좋아졌음을 알 수 있다. 하지만 그럼에도 then() 메서드의 인자로 넘기는 콜백 함수 내에서 조건문이나 반복문을 사용하거나 여러 개의 Promise를 병렬로 또는 중첩해서 호출해야하는 경우들이 발생하게 된다. 그렇다면 콜백함수를 사용했던 때와 비슷하게 다단계 들여쓰기를 해야할 확률이 높아지게 된다.

3. async/await
Promise의 단점들을 해결하기 위해 ES7에서 async/await 키워드가 추가되었습니다. async/await 키워드를 사용하면 비동기 방식의 코드를 마치 동기 방식의 코드처럼 보이게 작성할 수 있다고 한다.

위의 코드를 async/await 키워드를 이용하여 재작성 해보자.

let oneTwoThreeAsyncAwait = async () => {
    new Promise((resolve, reject) => {
        console.log("one");
        resolve();
    })
    .then(() => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log("two");
                resolve();
            },3000)
        })
    })
    .then(() => {
        console.log("three");
    })
    .catch((e) => {
        console.log(e);
    })
}
oneTwoThreeAsyncAwait();
// one
// two
// three

솔직히 말하면 가독성이 좋아졌는지를 잘 모르겠다. 아직 코드 작성 능력이 좋질 않아 수정된 부분이 많지 않지만, 앞으로 공부하면서 고쳐나갈 예정이다.

서버와 통신하기 위해 비동기 처리는 필수적으로 알아야 한다고 생각한다. 개인적으로는 async/await 방식을 많이 연습해야 할 것 같다. 또한 콜백 함수는 되도록이면 사용을 자제해야겠다.



SPA(Single Page Application)의 등장 배경

처음에는 페이지가 변경되어야 할 때마다 서버에서 새로운 페이지를 받아오는 방식인 MPA(Multi Page Application) 형태로만 페이지를 그리는 줄 알았다. 그런데 이 방식은 새로운 페이지를 요청할 때마다 서버에 부하를 준다고 한다.

이러한 단점을 해결하기 위해 SPA(Single Page Application)가 등장하게 되었는데 웹 애플리케이션에 필요한 정적 리소스(HTML, CSS, JS)를 최초 한 번만 다운로드한다. 이후 새로운 페이지가 필요하다면 필요한 데이터(JSON)만 전달받아 페이지를 갱신한다. 결국 서버의 트래픽을 줄일 수 있고 전체 페이지를 다시 렌더링하지 않고 변경되는 부분만을 갱신하므로 새로고침이 발생하지 않아 네이티브 앱과 유사한 사용자 경험을 제공할 수 있다.

그런데, 이렇게 좋아보이는 SPA도 단점이 존재한다.

  • 초기 구동 속도
    SPA는 웹 애플리케이션에 필요한 모든 정적 리소스를 최초 접근시 단 한번 다운로드하기 때문에 초기 구동 속도가 상대적으로 느리다.

  • SEO(검색엔진 최적화) 이슈
    데이터 패치 요청을 통해 서버로부터 데이터를 응답받아 뷰를 동적으로 생성하는데 이때 브라우저 주소창의 URL이 변경되지 않는다. 이는 사용자 방문 history를 관리할 수 없음을 의미하며 SEO 이슈의 발생 원인이 된다.

현재 많이 쓰이고 있는 라이브러리들은 대부분 SPA로 구성되어 있는데, 무턱대고 SPA를 사용하는 것보단 SPA의 장점뿐 아니라 단점들 또한 숙지하고 특정 문제 해결에 적합한지 확인하고 활용해야 한다.




Final..

벌써 카카오 클라우드 스쿨에서의 교육 2주차가 지나갔다. 배웠던 것들을 머릿속에 자리잡아야 하는 것도 있지만, 다음 주엔 무엇을 배우고 어떻게 응용해볼까 하는 생각에 스스로 들떠 있는 것 같다.

3주차인 다음 주에는 서버단 프로그래밍 학습도 진행한다고 하니 ajax나 fetch API로 데이터를 가져오는 연습을 종종 해야겠다.

자바와 스프링 공부, 알고리즘 문제 풀이도 꾸준히 진행해야 하지만 정해진 시간을 알차게 쓰기란 게으른 나에게 상당히 힘든 일이다. 그래도 요즘엔 궁금한 것들이 많아 이것저것 찾아보면서 개인적인 공부도 되고, 모르는 것을 알아가는 과정이 재미있다.



혹여 잘못된 내용이 있다면 지적해주시면 정정하도록 하겠습니다.

참고자료 출처

profile
찍어 먹기보단 부어 먹기를 좋아하는 개발자

0개의 댓글