시작하기전에......

 

https://tarelbase.tistory.com/24?category=1003239

 

지난번에 쿠키와 세션에 대해 다룬적이 있다.

 

그러나 개인적으로 이 부분은 몇 번 더 공부해갈 필요가 있다고 판단되어 다시한번 짚어넘어가보겠다

 

1. 쿠키는 무엇이고 왜 필요한가??

초창기 인터넷에는 개인화라는 것이 없었다.

 

개인화란?? 다시말해 사람마다 보는 정보가 다르다는 것을 말한다.

 

내 통장에 100만원이 있다고 해서 다른사람들도 통장에 100만원이 있지는 않을 것이다.

또한 내 통장은 나만 볼 수 있어야 하고 다른사람들은 보게해선 안 된다.

 

초창기 인터넷은 이런 기능이 없었던 것이다.

 

누구나 특정 홈페이지에서 같은 정보를 볼 수 밖에 없었고

이는 마이페이지, 즉 , 나만의 정보에 대한 필요성을 만들었다.

이것이 지금의 쿠키를 낳은 것이다.

 

쿠키를 통해 지금 접속한 사람이 누구인지 판별하고 그 사람만 접근할 수 있는 정보를 제공할 수 있게 되면서

개인화라는 것을 실현할 수 있게 된 것이다.

 

2. 쿠키를 구워보자

일단 기본적인 세팅을 위해 간단한 웹코드를 작성하자

const http = require('http');
http.createServer(function(req,res){
  res.end('cookie')
}).listen(3000)

여기 보면 응답에 cookie라고 적어놨는데, 그냥 웹페이지에서 cookie라는 글자가 출력될 것이다.

한글을 적어도 되긴하는데 글자가 깨진다... 그냥 영어로 적자

 

위 코드를 실행하고 로컬 호스트 3000번으로 들어간 모습, cookie라는 글자가 출력된다.

 

const http = require('http');
http.createServer(function(req,res){

  res.writeHead(200, {
    'Set-Cookie':['smallCookie = choco','cuteCookie = vanilla']		//추가된 부분
  })

  res.end('cookie')
}).listen(3000)

위 코드를 보자, 추가된 부분의 바로 위아래줄이 새로 추가된 부분이다.

res(응답)의 writeHead를 통해 해드를 정의한다. 200은 성공을 나타내는 상태코드(404NotFound같은거)고

바로 아래 Set-Cookie를 통해 쿠키를 발급하고 있다.

여기서 발급된 쿠키가 바로 smallCookie와 cuteCookie다.

즉, 쿠키를 두 개를 구운 것이다.

 

 

구워진 쿠키는 개발자 도구에서 Network -> localhost 에서 확인할 수 있다.

 

 

쿠키를 없애버릴 수도 있다.

Application에서 localhost를 찾고 쿠키를 선택해 없애면 된다.

 

3. 구워진 쿠키를 함 봐보자

기껏 쿠키를 구웠는데 이걸 써먹지 못하면 무슨 소용일까?

자, 쿠키를 읽어보자

console.log(req.headers.cookie)

이걸 쓰면 터미널에서 쿠키를 확인할 수 있다.

아마 웹페이지를 새로고침하면 뜰 것이다.

npm install -s cookie

터미널에 위를 입력해 cookie를 사용하기위한 모듈을 설치해 주고

const cookie = require('cookie');

코드 최상단에 위 코드를 추가해 주자

var cookies ={}

cookies = cookie.parse(req.headers.cookie);

위 코드는 쿠키 데이터를 객체로 가져오는 역할을 한다.

이게 있으면 쿠키를 가져오기 편해진다.

단, cookie값이 undefined라면 에러가 발생할 수 있으니 조건문을 추가해줘야한다.

 

const http = require('http');
const cookie = require('cookie');

http.createServer(function(req,res){
  var cookies = {};
  if(request.headers.cookie !== undefined){
    cookies = cookie.parse(req.headers.cookie);
  }

  console.log(cookies);
  console.log(cookies.smallCookie);
  res.writeHead(200, {
    'Set-Cookie':['smallCookie = choco','cuteCookie = vanilla']
  });

  res.end('cookie')
}).listen(3000)

이쯤되서 재확인하는 완성코드

터미널에는 모든 쿠키의 정보와 smallCookie에 뭘 담고 있는지를 알 수 있을 것이다.

 

웹페이지를 새로고침 할 때마다 터미널에 쿠키의 값이 나타날 것이다.

 

4. 쿠키의 종류? 일회용 쿠키와 쿠키의 유통기한?

쿠키는 Session Cookies와 Permanent Cookies로 나뉜다.

Session Cookies는 위에 다룬 내용이니 넘어가고

Permanent Cookies를 다루어 보자

  res.writeHead(200, {
    'Set-Cookie':['smallCookie = choco','cuteCookie = vanilla', 
    'permanent=cookies; Max-Age=${60*60*24*30}']
  });

res.wirteHead부분을 수정했다.

작은 쿠키와 귀여운 쿠키는 무시하고 아래 permanet라는 것이 또 정의되어 있다.

 

여기에는 세미콜론 뒤에 Max-Age가 온 것을 볼 수 있는데

이는 이 쿠키의 유통기한이라고 할 수 있다.

60은 60초, 60*60은 1시간, 60*60*24는 하루, 60*60*24*30은 한 달로 볼 수 있다

(식만 봐도 얼추 이해가 될 것이다.)

 

이렇게 쿠키의 유통기한을 설정해주면 다른 쿠키들과 다르게 인터넷을 껐다가 다시 켜도 쿠키가 유통기한동안은 남아있다.

물론 유통기한이 경과하면 사라진다.

 

여기서Permanent Cookies의 특징이 드러나는데, 보시다시피 브라우저를 없애든 말든 유통기한 동안은 사라지지 않고

계속 남아있는 쿠키를 말한다.

 

 

5. 쿠키의 보안

내 쿠키는 나만 써야하므로 아무에게나 줄 수 없다.

res.writeHead부분을 수정해보자

  res.writeHead(200, {
    'Set-Cookie':['smallCookie = choco','cuteCookie = vanilla', 
    'permanent=cookies; Max-Age=${60*60*24*30}',
    'lovelySecureCookie= starCookie; Secure',
    'httpOnlyCookie=cookielove; HttpOnly']
  });

4번째 줄을 보면 아까 없던 새로운 쿠키를 정의했다.

쿠키의 이름이나 startCookie는 중요한게 아니고 중요한건 세미콜론 다음의 Secure 문자다.

5번째 줄에도 세미콜론 뒤에 HttpOnly가 있다.

 

이 두가지는 각자의 특징을 통해 쿠키의 보안을 담당하는데 이는 다음과 같다.

 

Secure의 경우

클라이언트에서 자바스크립트를 통한 쿠키 도둑질을 예방할 수 있다.

상술한 것처럼 그저 짧은 문장하나 넣어주는 것 만으로도 예방할 수 있다.

이것은 브라우저와 서버가 https로 통신하는 경우에만 브라우저가 서버에게 쿠키를 전송한다는 의미를 가지고 있다.

 

HTTP Only의 경우

브라우저에서 해당 쿠키로 접근할 수 없게 되지만 쿠키에 포함된 정보의 대부분이 브라우저에서 접근할

필요가 없기 때문에 HTTP only 는 기본적으로 적용하는 것이 좋다.

이것은 JS의 document.cookie를 이용해 쿠키에 접근하는 것을 막는 옵션이다.

 

6. 쿠키의 인증, 경로와 도메인

res.writeHead부분을 수정해보자

  res.writeHead(200, {
    'Set-Cookie':['smallCookie = choco','cuteCookie = vanilla', 
    'permanent=cookies; Max-Age=${60*60*24*30}',
    'lovelySecureCookie= starCookie; Secure',
    'httpOnlyCookie=cookielove; HttpOnly',
    'pathCookie = cocoa; Path=/cookie']
  });

마지막 줄에 페스쿠키가 추가되었다.

 

세미콜론 뒤에 페스라고 되어 있는 것을 볼 수 있는데

이것은 해당 경로(또는 그 경로의 하위)에서만 작동하게 하는 옵션이다.

즉 저 패스쿠키는

http://localhost:3000/cookie 경로나

http://localhost:3000/cookie/sub와 같은 하위 경로에서만 사용할 수 있다.

 

비슷한 기능으로 도메인이 있다.

res.writeHead부분을 수정해 보자

 res.writeHead(200, {
    'Set-Cookie':['smallCookie = choco','cuteCookie = vanilla', 
    'permanent=cookies; Max-Age=${60*60*24*30}',
    'lovelySecureCookie= starCookie; Secure',
    'httpOnlyCookie=cookielove; HttpOnly',
    'pathCookie = cocoa; Path=/cookie',
    'domainCookie = tea; Domain=o2.org']
  });

최하단에 추가된 쿠키를 보자

아마 눈치빠른 사람들은 눈치 챘겠지만

저건 특정 도메인에서만 작동하도록 하는 것이다.

 

우선은 여기까지 하고 나머지 부분은 다음편에 업로드

 

 

클래스의 속성중 하나인 private속성은 class안에서만 사용하는 값이므로 외부에서 수정할 수 없고

확인도 할 수 없다.

 

때문에 상황에 따라서 속성을 읽고 쓸 수 있는 메소드를 만드는데 이것이 게터와 세터라고 할 수 있다.

 

여기서 게터는 getter라는 이름처럼 속성값을 확인할 때 사용하고

세터는 setter라는 이름처럼 속성값을 지정해줄 때 사용한다.

 

아래 코드를 보자

  // 정사각형 클래스
  class Square {
    #length							//#length는 private

    constructor (length) {					//클래스의 파라미터로 length 추가
      this.length = length
    }

    //아래 코드는 위의 길이값을 바탕으로 둘레, 한 변의 길이, 넓이를 계산하는 코드
    get length () {return this.#length}
    get perimeter () {return this.#length * 4}
    get area () {return this.#length * this.#length}
    
    //아래 코드는 set을 사용
    set length (length) {
      if (length <= 0) {
        throw '길이는 0보다 커야 합니다.'
      }
      this.#length = length
    }
  }

  // 클래스 사용하기
  const squareA = new Square(10)
  console.log(`한 변의 길이: ${squareA.length}`)
  console.log(`둘레: ${squareA.perimeter}`)
  console.log(`넓이: ${squareA.area}`)

  // 예외 발생시키기
  const squareB = new Square(-10)
  
  // 실행결과
  // 한 변의 길이: 10
  // 둘레: 40
  // 넓이: 100
  // 에러 하나

자, 여기서 length는 private다.

 

아래 get을 사용한 코드 3줄이 있는데, 이것은 private 값인 length를 이리저리 조작해서 얻은 값을 불러오게 하는 코드다

실제로 아래 '클래스 사용하기' 줄을 보면 결과적으로, private값을 가져온 것을 알 수 있다.

 

아래 set을 사용한 코드를 보면 조건문의 경우는 그냥 에러걸러내는 용이니 넘어가고 그 다음줄을 보면

this.#length = length가 있다.

즉 private 값인 #length에 외부에서 가져온 파라미터 값인 length를 넣는다는 뜻이다.

 

결과적으로 외부에서 내부 private에 간섭을 한것이라고 볼 수 있는데

 

어쨌든 값 변화는 클래스 내부에서 일어난 것이기 때문에 문제는 없다.

 

 

정리

밖과 절대로 소통하지 않는 방구석 폐인에게 외부인이 편지를 써봤자 그 폐인은 우체통을 결코 확인하지 않을 것이다.

때문에 그와 함께 사는 가족들에게 직접 편지를 건네고

그 가족을 통해 폐인에게 편지가 도착하는 것이다.

 

여기서 방구석 폐인은 private라고 할 수 있고

그와 소통해주는 가족이 게터와 세터라고 할 수 있고

외부인은 그 private에 접근하려고 하는 외부인자들이라고 할 수 있다.

 

다만 주의해야할 것이

코딩의 편의성을 위해 모든 private에 게터와 세터를 사용하려는 경우가 있을 수 있다는 것이다.

하지만 그렇게 했다간 대체 왜 private 속성을 사용하겠는가

만약 값을 함부로 보여줘선 안 된다고 한다면 게터와 세터의 사용을 지양해야 할 것이다.

 

마지막으로 게터와 세터 코드의 간결한 코드를 작성하고 끝내겠다.

 

class 클래스 이름{
	get 이름() {return 값}
    set 이름(value) {}
}

 

 

 

 

처음 설치하고 도구 -> 옵션 -> 인증 -> 추가에 깃허브 계정 추가

 

 

새로운 탭을 만들고 clone을 누르고

깃허브 저장소에서 클론 경로 복사해 붙여놓고

 

목적지경로에는 프로젝트로 쓸 폴더를 지정, 여기서 폴더는 빈폴더여야 함

 

이렇게 생성되면

Pull로 저장소 내의 파일을 가져올 수 있음

만약 남의 것을 가져온 상태라면 우선 Pull을 먼저 해서 그 파일들을 가져오자

 

VS-Code에서 작업하며 파일들을 수정하면 그것이 곧 커밋에 반영된다.

 

커밋을 누르면 수정된 파일 목록이 뜬다.

+버튼을 누르면 해당 항목이 위로 이동한다.

이후 커밋하고자하는 내용(일종의 메모이므로 아무렇게나 작성)을 작성하고

커밋을 누른다.

 

 

커밋했다고 끝이 아니고

Push에서 Push를 해주어야 완료된다.

 

 

 

 

 

 

 

1. 클래스?

C언어를 제외한 대부분의 프로그래밍 언어는 객체지향 패러다임을 기반으로 만들어 졌다.

 

객체지향이란 말 그대로 객체를 우선적으로 생각해서 프로그램을 만든다는 방법론이다.

 

객체지향 말고 다른게 뭐가 있냐? 라는 생각도 들법한데, 바로 절차 지향이다.

 

 

이 두가지는 어떻게 다른가??

 

절차지향은 턴제 시뮬레이션 게임이다.

순서가 정해져 있고, 그 순서가 끝나기 전에 다른 이들이 행동할 수 없다.

객체지향은 

 

 

자, 여기 프로브와 드론이 영혼의 대결을 펼치고 있다.

프로브와 드론의 체력은 20으로 하고 각각 5의 공격력을 가지고 있으며 프로브가 선공을 가진다라고 정의해보자

이걸 절차지향으로 나타내면 다음과 같다.

프로브와 드론의 대결! 프로브 체력 20! 공격력 5!
드론 체력 20! 공격력 5!
프로브의 선공! 드론의 남은체력 15!
드론의 반격! 프로브의 남은체력 15!
프로브의 공격! 드론의 남은체력 10!
드론의 공격! 프로브의 남은체력 10!
프로브의 공격! 드론의 남은체력 5!
드론의 공격! 프로브의 남은체력 5!
프로브의 선공! 드론의 남은체력 0!

프로브가 승리했다!

 

그럼 이걸 객체지향으로 표현한다면??

우리가 스타크래프트로 두 유닛을 붙여놓은거랑 비슷하다.

선공의 개념을 정해줄 필요가 없으며

각 객체가 서로 독립적이고 따로 행동하며

프로브와 드론은 굳이 순서에 얽매이지 않고 유기적으로 행동할 수 있다.

 

2. 추상화?

 

위의 프로브를 보자,

실제로, 프로브는 체력 20, 보호막 20, 공격력 5, 방어력 0, 크기는 소형 이라는 요소를 가진 객체다.

만약 프로브가 실제로 존재한다면 요소는 이게 끝이 아닐 것이다.

아마 질량과 중량, 재질, 각 부분별 길이부터 구성하고 있는 원소의 종류와 수와 상태까지,

열거하자면 그야말로 끝도 없을 것이다.

 

하지만 게임에서 구현할 때는 이런건 전부 필요 없다.

즉, 필요한 요소만 가지고 객체를 표현한 것이 바로 추상화라고 할 수 있다.

 

 

프로토스에겐 프로브만 있는 것이 아니다.

위와 같이 다양한 유닛이 있다.

이것들을 관리하기 위해서 필요한 공통사항을 추출하는데 이를 추상화라고 한다.

 

공통사항으론 다양한 것이 존재한다.

체력, 방어력, 타입, 속성, 공격여부, 지상유닛인지 공중유닛인지에 대한 구분, 건물인지 아닌지에 대한 구분 등이 있을 것이다.

 

이를 코드로 작성해보면 얼추 이렇게 나온다.

const unit []
unit.push({이름: 프로브, 공격력: 5, 방어력: 0, 체력 20, 보호막: 20})
unit.push({이름: 질럿, 공격력: 16, 방어력: 1, 체력 100, 보호막: 60})
unit.push({이름: 드라군, 공격력: 20, 방어력: 1, 체력 100, 보호막: 80})

이하 생략...

 

우리는 이러한 데이터를 가지고 입맛에 맞게 조리할 수 있다.

공격력부터 보호막까지 더한 수치를 전투력이라고 정의해보자

 

for (const p of unit){
	const sum = p.공격력 + p.방어력 + p.체력 + p.보호막
    output = sum
    }
console.log(output)

이걸 실행하면 각 수치를 더한 값을 얻을 수 있다.

 

물론 단순히 더하는 것 뿐만 아니라 지금까지 배운 것을 종합하면

 

 

 

3. 클래스 선언

다시 클래스로 넘어와보자

클래스를 선언해보자

class 클래스의 이름{
}

'클래스의 이름'이라는 클래스를 만들었고 이것을 복사해보자

 

new 클래스의 이름

자, 클래스의 이름의 인스턴스를 만들었다.

이게 뭔 내용인가 이해하기 힘들 수 있는데, 아까 그 프로브를 인용하면 다음 같다.

 

 

자, 맵상에 여러마리의 프로브가 있다.

이 프로브들은 처음 생산될때는 서로 같은 값을 가지지만 이게 항상 동일하지는 않다.

누군가는 공격을 당했을 것이고

누군가는 방어력 업그래이드를 했을 것이다.

 

즉, 클래스란 기초가 되어주는 원본이고

인스턴스란 그 원본을 복사해낸 것이다.

인스턴스가 가지고 있는 값이 변하여도 클래스가 가진 값은 변하지 않는다.

수많은 프로브들 가운데 누가 두들겨 맞았다고 해서

앞으로 생산될 다른 프로브들 까지 두들겨 맞은체로 태어나지는 않을태니 말이다.

 

하지만 클래스가 바뀌어버린다면

이후로 생성될 인스턴스의 값들도 바뀌어버린다.

원본이 수정되었으니 그 이후에 만들어진 사본들도 바뀐체로 태어나는 것이다.

 

 

4. 생성자

클래스 안에 새로운  것을 만들어주는 역할을 한다.

 

class Unit {
	constructor(이름, 공격, 방어, 체력){
    this.이름 = 이름
    this.공격 = 공격
    this.방어 = 방어
    this.체력 = 체력
    }
}

const unit = []
unit.push(new Unit('신캐A', 16, 2, 20))
unit.push(new Unit('신캐B', 216, 12, 4120))
unit.push(new Unit('신캐C', 600, 2, 20000))

 

만약에 스타크래프트에 새로운 유닛이 생성된다고 가정해보자

설령 새로운 유닛이라 할지라도 기존의 유닛들과 공통점이 있을 것이다.

(예를 들면, 위 처럼 이름을 가지고 있다던지 체력을 가지고 있다던지/)

그 공통점을 바탕으로 새로운 유닛을 만들고 정의할 수 있다.

 

그 역할을 해주는 것이 바로 생성자라고 할 수 있다.

 

5. 메소드

클래스 안에 함수를 추가해주는 것이라 할 수 있다.

 

예를들어, 신캐릭터의 가격이 그 캐릭터의 모든 스텟을 합친것과 같다고 가정해보자

 

 

class Unit {
    constructor(이름, 공격, 방어, 체력){
    this.이름 = 이름
    this.공격 = 공격
    this.방어 = 방어
    this.체력 = 체력
    }
    
    price() {
    	return this.공격 + this.방어 + this체력
    }
}

const unit = []
unit.push(new Unit('신캐A', 16, 2, 20))
unit.push(new Unit('신캐B', 216, 12, 4120))
unit.push(new Unit('신캐C', 600, 2, 20000))

 

이렇게 하면 새로 정의한 신캐 A,B,C의 가격은 각각 38, 4348, 20602가 될 것이다.

 

여기서 가격을 계산해준 price()가 메소드가 된다.

1. TCP와 UDP

 

소켓에 대해 알기 전에 먼저 짚고 넘어가자

이 두가지는 데이터 송수신을 위한 프로토콜이다.

 

TCP란 내가 데이터를 전달하고 상대방이 잘 전달 받았는지 확인하는 과정을 거친다.

때문에 신뢰성이 높다.

 

UDP는 내가 데이터를 전달하기만 하면 그대로 끝이다.

때문에 데이터가 옳게 전달 되었는지 알 수 없기에 신뢰성이 낮다.

이러한 특징 덕분에 전송속도가 빠르다는 장점이 있다.

 

2. 소켓

2-1. 소켓이란?

네트워크에서 데이터를 송수신하기 위해 반드시 거쳐야하는 연결부에 해당한다.

소켓은 상술한 TCP와 UDP 소켓이 있다.

주로 TCP를 쓰겠지만 가끔 빠른 전송이 필요할 때는 UDP를 쓰기도 한다.

 

2-2. 패킷이란?

소켓을 통해 송수신하는 데이터 덩어리 하나하나를 말한다.

 

2-3. 웹 소켓이란?

실시간 웹 서비스를 위해 만들어진 소켓이다.

주로 메신저 같은 곳에서 주로 사용한다.

노션과 같은 실시간 공동 편집기능에서도 사용한다.

 

2-4. socket.io?

자바스크립트를 통해 웹소켓을 사용하고자 한다면 가장 많이 쓰이는 라이브러리다.

모든 브라우저가 이걸 지원하지 않지만 그에 대비하기 위해 polling 기능을 지원한다.

 

2-5. socket.io는 웹소켓과 다른가?

그렇다.

socket.io는 웹소켓을 포함하며, 웹소켓을 지원하지 않는 곳에서도 비슷한 기능을 지원하는 라이브러리다.

 

 

3.socket.io를 써보자

 

 

우선 HTML 파일에도 이것과 관련된 코드를 작성해 주어야 한다.

 

head 영역에 다음을 추가하자

<script src="https://cdn.socket.io/socket.io-3.0.1.min.js"></script>

body 영역에는 다음을 추가하자

 

<script>
  const socket = io("ws://localhost:3000");
  socket.on("connect", () => {
    socket.send("Hello!");
  });

  socket.on("message", (data) => {
    console.log(data);
  });
</script>

자바스크립트의 app.js에는 다음을 추가하자

const io = require("socket.io")(3000, {
  cors: {
    origin: "*",
    methods: ["GET", "POST"],
  },
});

io.on("connection", (socket) => {
  console.log("새로운 소켓이 연결됐어요!");

  socket.on("message", (data) => {
    console.log(data);
  });
});

 

가장 기본적인 세팅이다.

app.js 의 가장 위 코드가 socket.io를 쓰겠다는 뜻이며

뒤의 3000은 3000번 포트를 쓰겠다는 선언이다.

 

cors는 프론트엔드 담당자와 협업하면서 에러가 발생할 수 있는 부분이다.

cors의 origin은 어떤 사용자만 접속이 가능하게 할 것인가를 정하고(별표는 모두에게 열어둔다는 의미다.)

methods는 말그대로, 어떤 메소드만 열어둘 것인가를 정한다.

 

io.on은 어떤식으로 연결할 것인가를 말한다.

socket.on은 오른쪽의 "message"라는 소켓에 들어왔을 때의 반응을 정한다.

 

 

정리하자면 맨 위부터 소켓의 포트넘버를 정의하고

어떤 사람들을 받고 어떤 요청을 받을 것인지를 정의하고

어떤식으로 연결할 것인가를 정한다고 볼 수 있다.

 

 

express모듈과 함께 쓰고 싶다면

일단 express 모듈을 설치 하고 app.js를 다음과 같이 편집하자

const express = require("express");
const { createServer } = require("http");

const app = express();
const http = createServer(app);
const io = require('socket.io')(http, {
  cors: {
    origin: "*",
    methods: ["GET", "POST"],
  },
});

http.listen(3000, () => {
  console.log("서버가 요청을 받을 준비가 됐어요");
});

3000번 포트가 들어가던 곳에 http가 들어갔고

http.listen으로 서버를 여는 것을 알 수 있다.

 

 

 

기본적인 부분은 여기까지

 

사실 프리티어는 자바스크립트 뿐만 아니라 프로그래밍 전반에도 비슷한 개념이 있을거라 생각한다.

하지만 일단은 여기에 적도록 한다.

 

 

1. 쉽게 말해 코드를 작성하는 스타일...

코드를 작성하다보면 들여쓰기가 자동으로 되는 것을 알 수 있다.

그런데 이러한 들여쓰기 조차 누구는 Spacebar를 여러번 누르고 누구는 Tap을 한 번 누른다.

 

또한 중괄호를 쓰는 구문을 보면, 누구는 짧은 구문도 시작괄호와 닫는 괄호가 각각 한 줄씩 차지하는 반면

누구는 그딴거 없이 한 줄로 심플하게 끝내기도 한다.

 

그림을 그리는 사람에게 그림체가 있고 글을 쓰는 사람에게 글씨체가 있지만

단순한 모양을 넘어서, 그림 그리는 사람들에겐 주로 그리는 장르나 좋아하는 요소가 있기 마련이고

글을 쓰는 사람들 또한 글씨체를 넘어서, 추구하는 화술이 있다.

 

프로그래머들도 마찬가지고 이 스타일로 인한 가독성 때문에 서로 엄청나게 싸웠다고 한다.

 

 

2. 그래서, 프리티어란?

코드 서식 관리 도구 라고 할 수 있다.

 

https://prettier.io/playground/

 

위 사이트에서 사용해 볼 수 있다.

 

프리티어를 다운 받아서 본인이 사용하고 있는 코딩 프로그램에 적용시킬 수 있다.

또한 프리티어가 기본적으로 제공하는 규칙이 있으나 이 규칙을 본인의 입맛에 맞게 바꿀 수 있다.

 

만약 본인이 VS-Code를 사용한다면 터미널에 다음 명령을 입력하여 설치할 수 있다.

npm i prettier -D

프리티어는 서비스 구동에 필요한 모듈이 아니니 -D로 설치한다.

그리고 나서 app.js 파일이 있는 폴더에 .prettierrc.js 라는 파일을 만들고 (앞에 점 주의) 아래의 내용을 넣는다.

 

{
  "printWidth": 80, //  줄 바꿈 할 폭 길이
  "tabWidth": 2, // 탭 너비 
  "singleQuote": true, // single 쿼테이션 사용 여부
  "trailingComma": "all", // 여러 줄을 사용할 때, 후행 콤마 사용 방식
  "jsxBracketSameLine": false, // JSX의 마지막 `>`를 다음 줄로 내릴지 여부
}

 

그리고 package.json 파일을 보면 scripts 라는 항목이 있는데 그 항목의 하위에다가 다음을 넣어주자

"prettify": "prettier --write *.js **/*.js"

 

참고로 위 코드를 넣으면 대략 다음처럼 나와야 한다

{
    "name": "프로젝트 이름",
    "version": "1.0.0",
    "scripts": {
        "start": "node app.js",
        "prettify": "prettier --write *.js **/*.js"
    },
... 생략
}

 

이후 터미널에 다음을 입력하여 프리티어를 실행한다.

npm run prettify

 

 

3. 프리티어 각 항목의 규칙

아래 코드를 설명하는 형식으로 진행해 보겠다

module.exports = {
  trailingComma: 'es5',
  tabWidth: 2,
  semi: true,
  singleQuote: true,
  arrowParens: 'always',
};

 

1. tarilingComma란 바로 위 코드의 끝부분 처럼 모든 항목에 쉼표를 넣을 것인가에 대한 여부다.

물론 쉼표는 꼭 넣어줘야 하지만 마지막 항목에는 넣어줄 필요가 없는데 이것을 설정하는 것이라고 생각하면 된다.

es5라는 값은 ES5버전에서 허용되는 부분까지만 콤마를 붙인다는 뜻이다.

none이라는 값은 쓸모없다고 판단되는 모든 콤마를 제거합니다.

all은 반대로 바로 위 코드 처럼 끝에도 넣어주는 것이다. 그러나 그런 형태를 지원하지 않는 경우가 있기 때문에 비추천한다.

 

2. tabwidth란 들여쓰기 간격을 뜻한다.

 

3. semi란 코드 뒤에 ;를 붙일 것인가에 대한 규칙이다. 자바스크립트는 세미콜론이 없어도 상관없지만, 몇몇 언어는 이게 없으면 코드 자체가 못 읽히는 현상이 발생하곤 한다.

때문에 가급적이면 true로 해 놓기를 권장한다.

 

4. singleQuote란 문자열을 입력할 때 쌍따옴표를 쓸 것인가, 외 따옴표를 쓸 것인가를 결정한다.

뭘로 해도 상관없지만 외따옴표(true)를 권장한다.

 

5.arrawParens란 함수의 파라미터에서 괄호를 항상 넣을 것인가 말 것인가를 정하는 규칙이다.

이 또한 always로 항상 해두는 것을 권장한다.

 

1. 도메인

1-1. 도메인

개발의 목적은 사업 등을 하면서 필요한 과정을 자동화하거나 개선하기 위한 것이다.

온라인 서점으로 예를 들자면

서점의 일을 온라인으로 해결하고자 하니 온라인 서점은 도메인, 즉, 문제의 영역이 된다.

그리고 그 도메인은 다시 하위 도메인으로 나누어 진다.

 

온라인서점이 기능을 하기 위해선

주문, 회원, 혜택, 결제, 배송, 정산, 리뷰 등의 기능이 필요하다.

이것들이 바로 하위 도메인이 된다.

 

1-2. 도메인 모델

특정 도메인을 개념적으로 정리한 것을 도메인 모델이라고 한다.

이런식으로 모델화를 하면 도메인의 작동을 예상하고 발생하는 문제 등을 빠르게 파악할 수 있다.

 

1-3. 엔티티

실제 DB테이블과 연관되어 있는 핵심 클래스로

엔티티를 기준으로 테이블이 생성되고 DB스키마가 변경된다.

 

엔티티는 요청이나 응답으로 전달하는 클래스로 쓰면 안 된다.

엔티티 내부의 속성이변경되더라도 여전히 동일한 엔티티로 남아있다.

 

 

 

2. 아키텍처 패턴

2-1. 아키텍처 패턴

소프트웨어 구조를 구성하기위한 가장 기본적인 토대라고 할 수 있다.

저장소 패턴, 서비스 계층 패턴, 작업단위 패턴, 애그리게이트 패턴 등이 있으며 이 외에도 다양하게 있다.

 

아키텍처 패턴을 도입하기 전에 고민해야할 것이 있는데 이는 다음과 같다

1. 아키텍처 패턴을 사용하면서 발생하는 이익과 비용에 대해 확실한 이유가 있어야 한다.

2. 해당하는 아키텍처 패턴을 채택했을 때 어떤 장단점이 있는지 명확하게 인지해야 한다.

3. 여러계층을 추가하기 위해 들이는 노력과 시간을 투자할 만한 가치가 있을 정도로 어플리케이션과 도메인이 복잡한 경우에만 도입하는 것이 좋다.

 

2-2. 계층형 아키텍처 패턴

가장 흔하게 사용되고 있는 패턴 중 하나로 단순하고 대중적이면서 비용도 적어서 사실상 표준 아키텍처이다.

어떤 아키텍처 패턴을 도입할까 결정장애가 올 때는 이걸 고르는게 현명할 수 있다.


계층형 아키텍처는 각 계층별로 의존성이 낮아서 모듈을 교체하더라고 코드수정이 용이하다.

 

계층이야 여러개가 있을 수 있지만 이번에 알아 볼 것은 3계층 아키텍처이다

3계층 아키텍쳐는 프레젠테이션, 비지니스, 데이터 엑세스 계층으로 나뉜다

3계층 아키텍처는 다음 3가지의 처리과정을 이용하여 구현한다.

 

컨트롤러: 어플의 가장 바깥으로 요청과 응답을 담당한다. 일종의 고객 대응 직원같은 느낌

서비스: 어플의 중간부분으로 비지니스 로직이 수행되는 부분임과 동시에 실제 중요한 작동이 많이 일어난다.

레퍼스토리: 어플의 가장 안쪽 부분으로 DB와 맞닿아 있어서 실제 DB의 데이터를 사용하는 계층이다.

 

이것은 다음과 같은 순서대로 로직이 수행된다.

1. 사용자가 어떤 것을 요구를 한다. (클라이언트가 요청을 보낸다.)

2. 요구사항에 적합한 담당자를 부른다. (요청URL을 알맞는 컨트롤러가 받는다.)

3. 담당자는 알맞는 서비스 담당자를 부른다. (컨트롤러가 서비스를 호출한다.)

4. 서비스 담당자는 저장소에서 가져온 물건을 가공하여 담당자에게 준다. (서비스는 저장소에서 가져온 데이터를 가공해 컨트롤러에게 넘긴다.)

5. 담당자가 사용자에게 요구한 물건을 넘긴다. (컨트롤러가 서비스의 결과물을 클라이언트에게 전달한다.)

 

위의 특성을 보면 컨트롤러는 말 그대로 고객대응 직원이고 서비스가 중간에서 핵심적인 것을 한다는 것을 알 수 있다.

실제로 서비스코드는 현업에서 엄청나게 비대해진다고 한다.

레퍼스토리는 DB를 지키는 저장소 관리자 같은 느낌이라서 서비스가 DB의 정보가 필요할 때 레퍼스토리에게

요청을 보낸다고 한다.

 

대략적인 관계도

 

 

일단은 여기까지

생각보다 공부해야할 내용이 많다

 

'Framework > Node.js' 카테고리의 다른 글

Node.js 공부정리 - 쿠키 - 복습 - 001  (0) 2022.10.26
Node.js 공부정리 - Socket.io  (0) 2022.10.14
Node.js 공부정리 - 미들웨어  (0) 2022.10.09
Node.js 공부정리 - JWT  (1) 2022.10.08
Node.js 공부정리 - 쿠키와 세션  (1) 2022.10.08

1. 예외란?

초간단하게 그냥 에러다.

그렇다면 예외처리란?

말그대로 에러가 발생했을때 어떻게 할 것인가를 나타낸다.

 

프로그래밍이란 에러와의 싸움이다.

오죽하면 프로그래머의 실력은 경험한 에러의 양과 비례한다고 하겠는가

나도 지금까지 수 많은 에러를 경험했다.

그러니, 이번에 분명히 짚고 넘어가자

 

 

예외처리란 전쟁무기와도 비슷한것 같다.

그 누구도 쓸일이 없기를 바라며 실제로 쓸일이 없었다 한들 방심하고 방치해선 안 되는 점에서 말이다.

 

1-1 구문오류

쉽게말해 나같은 실력이 부족한 병아리 개발자가 오타를 섰거나 괄호 갯수를 잘 못 입력해서 아예 실행조차 안 되는 경우를 말한다.

사소한 오타 하나 때문에 웹 브라우저가 코드를 읽지 못하는 경우는 아주 흔하다.

이런 경우 다음과 같은 에러가 발생한다.

 

예를 들어, console.log()의 괄호를 닫지 않으면 다음과 같이 발생한다.

D:\VSCode-prac\연습용\app.js:1
console.log(


SyntaxError: Unexpected end of input

 

이러한 류의 오류는 보통 어디서 못 읽었는지를 알려주기 때문에 해결하기 쉬운 편이다.

당장 위 코드만해도 D 드라이브의 VSCode-prac 폴더의 연습용 폴더의 app.js 파일의 1번째 줄에서 에러가 났으며

그 에러가 난 코드를 정확하게 보여주고 있다.

 

1-2 예외

실행중 발생하는 에러로 런타임 오류라고 한다.

이번엔 console.log()를 console.rog()로 잘 못 입력해 보겠다.

PS D:\VSCode-prac\연습용> node app
D:\VSCode-prac\연습용\app.js:1
console.rog()
        ^

TypeError: console.rog is not a function

 

이번에도 에러가 난 부분을 정확하게 짚어주고 있다.

console에는 rog라는 메소드가 없다.

때문에 에러가 발생한다.

다만 아까와는 다른 큰 차이점이 있다.

 

그 차이점을 알아보기 위해 다음을 각각출력해 보겠다

console.log("aaaa")
console.rog()
console.log("aaaa")
console.log(

 

이결 출력한 결과는 다음과 같다.

PS D:\VSCode-prac\연습용> node app
aaaa
D:\VSCode-prac\연습용\app.js:2
console.rog()
        ^

TypeError: console.rog is not a function
D:\VSCode-prac\연습용\app.js:2
console.log(


SyntaxError: Unexpected end of input

차이점을 찾았는가?

메소드를 잘 못 입력한 경우에는 aaaa가 출력되는 것을 알 수 있다.

반면 괄호를 입력하지 않은 경우에는 어떤것도 출력되지 않는다.

 

즉 구문오류와는 달리 예외란 문제가 발생한 부분 전까지는 전부 실행된다는 차이점이 있다.

 

2. 기본 예외 처리

조건문을 사용해서 예외를 발생하지 않게 하는 것을 기본 예외처리라고 한다.

예를들어, 특정 값을 추출했는데 if문을 사용해 이 값이 존재하는 값인지 아닌지를 분별해낼 수 있다.

물론 if 문을 쓰지 않고 없는 값을 출력했다고 해서 코드가 멈춰버릴만큼 자바스크립트가 융통성이 떨어지는 언어는 아니다.

다만 그렇다고 해서 예외처리를 해주지 않으면 이것이 나중에 문제를 일으킬 수 있으며

추후에 문제가 발생했을 때 문제를 파악하기 어려울 수도 있다.

때문에 문제가 예상되는 부분에는 조건문으로 예외처리를 해주는 것이 좋다.

 

 

3. 고급 예외 처리

try, catch, finally 문을 사용한 예외처리를 고급 예외처리라고 한다.

이것을 살펴보면 다음과 같다.

 

try{	
			//try 구역
} catch (exception){
			//catch 구역
} finally {
			//finally 구역
}

보시다시피 3개의 구역으로 나뉜다.

 

try: 에러가 발생할 수 있다고 여겨지는 코드들을 여기에 넣는다.

이곳에 들어간 코드는 위에서부터 순차적으로 실행된다.

 

catch: try구역을 실행하던 도중 에러가 발생할 경우, try를 멈춰버리고 이곳으로 넘어간다.

만약 에러가 발생하지 않는다면 이 구역은 실행되지 않는다.

 

finally: try 구역에서 에러가 발생했든 하지 않았든 최종적으로 실행되는 부분이다.

만약 try 구역에서 에러가 발생했다면 catch로 넘어와서 catch를 모두 실행한 다음 finally를 실행한다.

만약 try 구역에서 에러가 발생하지 않았다면 try 구문을 종료하고 바로 finally로 넘어온다.

단, finally는 필수 구역이 아니다. 없어도 상관없는 부분이다.

 

 

3-1. finally는 언제 쓰는가?

 

finally구문은 언제 써야하는가? 라고 생각할 수 있는데

주로 try구문 안에 return이나 continue, break와 같은 함수를 탈출하는 구문을 사용한다면 쓸 수 있다.

이러한 함수를 끝내는 명령어와 만나면 그걸 실행하기 직전에 finally를 실행한다.

try{	
    console.log("aaa")
    return
    console.log("bbb")
} catch (exception){
    console.log("ccc")
} finally {
    console.log("ddd")
}

예를 들어 위의 코드를 보자

aaa를 출력하고 return을 만난다.

그럼 진짜로 return을 하기 전에 finally를 먼저 실행한다.

그리고 진짜로 retrun을 한다.

즉 출력 결과는 아래와 같다.

aaa
ddd

 

 

4. 마지막

에러를 없앨 수는 없을까?

이쯤되면 나같은 병아리 개발자는 에러가 예상된다면 여러가지 시도를 통해 에러가 발생하지 않게 하면 되지 않느냐고 생각할 수 있다.

그러나 에러라는 것은 그렇게 단순한 것만 있는게 아니다.

예를 들면 로그인을 할 때 아이디나 비밀번호를 잘 못 입력한 것도 일종의 에러라고 할 수 있기 때문이다.

즉, 사용자가 로그인 할 때, 아이디나 비밀번호를 잘 못 입력하는 경우가 발생할 경우를 대비해 저런 구문을 사용하는 것이라고도 할 수 있다.

 

또한 예외처리는 이것만 있는게 아니다.

예외처리 고급에 관한 부분은 다음에 이 게시글의 내용을 추가하거나 아니면 새로운 게시글을 작성하도록 하겠다.

+ Recent posts