반응형

소켓 통신은

Bind 로 ip 와 port 를 설정한 뒤에 

Listen 으로 한번에 몇개의 접속을 받을 수 있을지를 설정하면서 대기하게 하고

Accept 하면서 대기 하고 있다가 클라로부터 접속이 오면 클라와 전용으로 통신할 소켓을 하나 만들어 통신하게 하는 구조다

 

 

반응형

'서버(Server) > Server' 카테고리의 다른 글

비동기 accept 처리 : _listenSocket.AcceptAsync  (0) 2022.12.23
기본적인 소켓 프로그래밍  (0) 2022.12.23
통신 모델 OSI 7 계층  (0) 2022.12.22
javascript : Class  (0) 2018.08.19
Promise [1] catch & all  (0) 2018.08.19
반응형

 

 

어플리케이션 : 유저인터페이스,  HTTP, FPT, DNS 

  • 4단계 : 트랜스포트 TCP/UDP 전송확인/오류해결
  • 3계층 : 네트워크 단계, 라우터 : IP, ipv4, ip6, 라우터 로 경로 설정
  • 2계층 : 데이터 링크 계층 으로  , 스위치, mac address 네트워크 카드에 박혀 있는 주소

 

7 계층 : 도메인 인데 이 도메인을  나중에 IP 로 바꿔야 함

 

 

 

 

 

반응형

'서버(Server) > Server' 카테고리의 다른 글

기본적인 소켓 프로그래밍  (0) 2022.12.23
소켓 통신  (0) 2022.12.22
javascript : Class  (0) 2018.08.19
Promise [1] catch & all  (0) 2018.08.19
Promise [2]  (0) 2018.08.16
반응형

Namespace Photon.Pun missing + RoomSettings not found
Realtime and Chat are available
I'm using PUN 2 - Free
Photon.Pun is only accessable when creating Scripts inside of Photon/PUN/Code

Someone has the same problem or know how to fix this?

 

 

 

 

RoomSettings is not a class or namespace in Photon code.
You probably mean RoomOptions which is under Photon.Realtime.

Photon.Pun is only accessable when creating Scripts inside of Photon/PUN/Code

Maybe you need to reference it using asmdef.

Are you using Unity 2019.3.12? try another version.

 

 

ref : https://forum.photonengine.com/discussion/16435/photon-pun-not-found

반응형
반응형

페이로드(payload)는 전송되는 데이터를 의미합니다. 데이터를 전송할 때, 헤더와 메타데이터, 에러 체크 비트 등과 같은 다양한 요소들을 함께 보내어, 데이터 전송의 효율과 안정성을 높히게 됩니다. 이 때, 보내고자 하는 데이터 자체를 의미하는 것이 바로 페이로드입니다. 우리가 택배 배송을 보내고 받을 때, 택배 물건이 페이로드이고, 송장이나 박스, 뾱뾱이와 같은 완충재 등등은 부가적인 것이기 때문에 페이로드가 아닙니다.

추가적으로 위키피디아에 아주 이해하기 좋은 예시가 아래와 같이 나와있어서 첨부합니다.

페이로드(payload)라는 단어는 운송업에서 비롯하였는데, 지급(pay)해야 하는 적화물(load)을 의미합니다. 예를 들어, 유조선 트럭이 20톤의 기름을 운반한다면 트럭의 총 무게는 차체, 운전자 등의 무게 때문에 그것보다 더 될 것이다. 이 모든 무게를 운송하는데 비용이 들지만, 고객은 오직 기름의 무게만을 지급(pay)하게 된다. 그래서 ‘pay-load’란 말이 나온 것이다

json으로 보는 실제 데이터에서의 payload는 아래의 json에서 “data”입니다. 그 이외의 데이터들은 전부 통신을 하는데 있어 용이하게 해주는 부차적인 정보들입니다.


ref

반응형
반응형
웹 통신에서 HTTPS 트래픽을 처리하기 위해 클라이언트와 서버간 SSL 세션을 맺는 HTTPS 핸드쉐이크 과정 다들 아시지요?

 

! 아래 그림을 참고해 보겠습니다.
 
.. 일반적인 HTTPS 핸드쉐이크 과정입니다. 위 과정이 모두 완료되어야 클라이언트-서버간 암호화된 데이터를 주고 받을 수 있습니다.
 
그런데 여러분..
데이터를 보내려고 할때마다 위와 같이 인증서를 확인하고.. 암호화 방식을 결정하며.. 데이터 암호화에 사용할 키를 생성하고 서로 교환하는 과정을 반복한다면.. 얼마나 비효율적일까요?
 
클라이언트와 서버간 Latency  50ms 라고 한다면.. 실제 데이터를 교환하기 위해 매번 200 ms(클라이언트 기준) 의 핸드쉐이크 과정이 포함되어야 합니다.
가뜩이나 암/복호화로 인해 서버의 응답 지연시간이 HTTP 보다 느려지는데.. 핸드쉐이크 과정까지 매번 수행하게 되면.. 속도측면에서 얼마나 만족스러운 결과를 기대할 수 있을까요?
 
그렇습니다. 우리가 지금 고민하고 있듯이.. SSL 프로토콜을 만드신 분들께서도 이런 사항을 고민하셨겠지요~..
HTTPS 핸드쉐이크의 과정을 대폭 개선한 간소화된 핸드쉐이크 과정이 이미 나와있습니다.
크게 보면.. 2가지네요~.. 
 
 
그 중 먼저 첫 번째 두둥~
 
 
1.     SSL Identifier 메커니즘
 
일단, 최초로 서버와의 통신을 시도하는 클라이언트는 어쩔 수 없습니다.
Full 핸드쉐이크 과정을 처음엔 거쳐야 합니다.
 
먼저 클라이언트는 최초 접속이기 때문에 Client Hello 메시지에 session id 값이 없습니다.
아래 2번과정에서 서버는 Server Hello 메시지에 Session Identifier 를 포함하여 전달합니다
 
 
 
클라이언트는 서버에서 발행해 준 Session Identifier 정보를 로컬에 저장하게 되는데요~..
클라이언트는 다음 HTTPS 세션을 맺을 때 이 값을 사용하게 됩니다
저장해 둔 Session Identifier 값을 Client Hello 메시지에 포함하여 전달합니다
 
서버는 클라이언트가 보내준 Session Identifier 정보를 확인하고, 이를 다시 사용해도 된다고 판단이 되면, Server Hello 메시지에 같은 Session IDs 값을 포함하여 전달합니다. ( 2번 과정)
 
이러면.. 다음 과정들이 생략됩니다.
 
ㄱ.   서버 인증서 확인
ㄴ.   암복호화에 사용할 Cipher Sutes 결정
 
ㄷ.   암복호화에 사용할 암호화 키 교환
 
이전에 사용했던 Cipher Suite 와 암호화 키를 다시 재활용하게 되므로 Full 핸드쉐이크 과정보다 2 Step 정도가 간소화 되네요~.. 
처음과 같이 하나의 과정의 Latency 를 50ms 로 가정한다면.. 100ms 정도를 개선하는 효과가 생깁니다.
 
클라이언트-서버간 주고 받아야 할 트랜잭션이 많을수록 효과는 크게 됩니다
 
 

2.     Session Ticket 메커니즘

 
두번째 방법은 Session Ticket 메커니즘입니다.
 
이 역시도 최초에는 Full 핸드쉐이크 과정을 수행해야 합니다. 가장 마지막 과정에서 서버는 “New Session Ticket”메시지를 클라이언트에게 전달합니다. 여기에는 암복호화에 사용할 마스터 키와 Cipher Sutes 알고리즘이 포함되어 있습니다.
 
세션 티켓의 정보는 서버만 알고 있는 키로 안전하게 암호화 하여 처리합니다.
세션 티켓 메커니즘은 TLS 확장 옵션입니다. 해당 메커니즘은 클라이언트와 서버가 모두 지원해야만 사용할 수 있습니다.
 
클라이언트는 Client Hello 메시지의 확장 옵션부분에 값이 없는 Session Ticket 을 같이 포함하여 전달함으로써 Session Ticket 메커니즘을 지원함을 알립니다. 서버 역시 Server Hello 메시지에 같은 방식으로 지원여부를 알립니다
 
 
 
클라이언트가 다시 서버에 HTTPS 통신을 시도하게 되면 다음과 같이 간소화된 절차를 거치게 됩니다.
 
클라이언트나 서버 둘 중 하나도 이를 지원하지 않는 경우에는 Session Identifier 메커니즘을 사용하게 됩니다.
 
Session Ticket 메커니즘이 상세히 기술된 RFC 5077 에는 Session Identifier 보다 Session Ticket 메카니즘의 사용을 더 권장하고 있습니다.
 
이유는 두가지인데요~
 
첫째, 서버의 메모리 사용이 적습니다. Session Identifier 는 서버가 발행한 Session IDs 값들을 모두 메모리에 기억하고 있어야 합니다
따라서 접속사용자가 많은 경우에는 서버 메모리 자원에 영향을 줄 수 있습니다
반면, Session Ticket 메커니즘은 클라이언트가 해당 내용을 기억하고 서버로 전달하는 방식이라 서버의 메모리 자원에 영향이 적습니다.
 
둘째, L4에 의한 부하분산 환경에서는 Session Identifier 메커니즘을 사용하는데 문제가 될 수 있습니다.
L4 의 부하분산 방식이 사용자의 IP를 참고하지 않는 방식의 경우 사용자를 동일한 서버로 분산하지 않기 때문에 SSL 세션 재활용 효율이 적습니다
이는 서버가 정보를 저장하고 이용하기 때문인데, 반면 클라이언트가 정보를 전달하는 방식인 Session Ticket을 사용할 경우에는 L4의 환경에 영향을 전혀 받지 않습니다
 
 
반응형

'서버(Server)' 카테고리의 다른 글

Photon.Pun not found  (0) 2022.06.14
페이로드(payload)란? 개념 설명  (0) 2022.06.02
반응형

Making games is more accessible than it ever has been. There's now a myriad of game engines to choose from, most of them for free, and an equally important accessibility of storefronts in the PC space. Between Steam, Epic Games Store and Itch.io, among others, the barriers to publishing a game have been hugely reduced.

And not only are games simpler to make and publish, but the range of creations you can easily make is wider than ever. For a long time, aspiring developers would perfect their craft with 2D platformers and Flash games, but now the improved accessibility of tools means they can be much more ambitious, much earlier. PlayerUnknown's Battlegrounds is a good example of that evolution, initially created by Brendan Greene because he found the multiplayer games he was playing too repetitive.

But there's one aspect of making games that has still a long way to go in terms of its accessibility: building backend services, managing multiplayer features, maintaining servers, creating matchmaking services, hosting and analysing players data, and everything that goes into running a live, multiplayer game.

As noted by Linode's Will Blew in a recent GamesIndustry.biz Academy guide to building your back-end in the cloud: "Making the right choices around how you run and support your game over time can be daunting, but there are lots of options available to help."

 

 

 

One of those options is PlayFab, a back-end live ops tech company acquired by Microsoft exactly three years ago. Now operating as Azure PlayFab (Azure being Microsoft's cloud platform), it's keen to conquer more of the industry.

"PlayFab has grown almost ten times since [the acquisition], in terms of both the number of monthly active players, [and] the number of games on the platform," says James Gwertzman, PlayFab co-founder and now Microsoft's general manager for cloud gaming. "All of Microsoft's first party studio games are now on Playfab. We get to host Minecraft, Halo, Gears of War and the new Flight Simulator, and I literally get a visceral thrill every time I see a game using one of our services.

"How do we ultimately deliver that platform that Phil [Spencer, head of Xbox] sold me on, to help the world's game developers be more successful with their games? I'm now responsible not just for PlayFab, but also for Azure, and frankly any [Microsoft] service in the game development space. My job is basically to figure out what game developers need, work across all of Microsoft to deliver it, whether it's working with Playfab, Xbox, Azure, Dynamics, Teams, and stitch together end-to-end solutions from across Microsoft to the games industry."

Gwertzman is keen to advocate for both Microsoft's cloud and its dev tools, reintroducing its basics to the GamesIndustry.biz Academy, and show how it's bloomed since the acquisition.

The challenges facing Azure PlayFab

Despite running some of the biggest titles in the world, Azure is still somehow less known in the gaming space than Amazon Web Services for instance, which is a challenge Gwertzman will have to tackle as he works on growing the cloud.

"I joke all the time that Azure is the world's best cloud for gaming you've never thought of. I think Azure has a reputation as being a cloud that is really focused on the enterprise. If you're an insurance company, or a big bank, or maybe an agriculture company, then I'm sure Azure is a great cloud, but if you're a game developer, you might think Azure's not the cloud for you.

 

 

"And to be honest with you, that's probably going to be the single biggest headwind that we're going to be working on now that I'm in this new role, because I think Microsoft's cloud kind of does have that reputation. And it has a lot to do with where we came from."

He points out that clouds like Amazon's initially targeted startups and entrepreneurs -- early tech adopters that typically include game developers -- and is now trying to figure out how to tackle enterprises and big companies. Microsoft came from the other direction.

 

....

 

 

 

ref : https://www.gamesindustry.biz/articles/2021-02-01-azure-playfab-the-worlds-biggest-dev-tools-youve-never-thought-of

 

반응형

'서버(Server) > Playfab' 카테고리의 다른 글

플레이펩(playfab?) 이란 (Azure)  (0) 2022.05.16
반응형

플레이펩은 마이크로소프트 Azure 의 플레이펩인 빽엔드 플랫폼이고 프로토버전에서 사용이 유용할수있고 상용버전들에서도 사용되는 빽엔드 플랫폼이다

 

빽엔드 플랫폼인데 Javascript 로 어느정도 커스터마이징이 가능한 빽엔드 플랫폼이지만

전체다 컨트롤이 가능하지 않고 커스터마이징의 제한은 플레이어 종속되어 있는 형태이다

(이부분이 살짝 아쉬움, 전체다 컨트롤 하면 좋을것 같음 : 가능하게 해주세요!)

하지만 볼륨이 큰작업이라.. 이게 진행될진 모르겠지만 이것이 된다면 타 빽엔드 플랫폼과는 확실히 다른 차별성을 갖을 수 있을 거라 봅니다

 

그냥사용하기에는 좀 아쉬움이 남지만 그 외의 부분들은 대체적으로 훌룡함

 

 

 

유저가 가입되고 로그인되는 처리를 할수 있고

 

사용자를 눌러 사용자당 다음과 같은 정보들을 볼수 있습니다

 

 

즉 왠만한 빽엔드에서 구현하는건 상당수 되어 있다

 

 

 

좌측에 보면 랭킹, 상점 등에 대한 구현도 고려해볼수 있습니다

 

 

 

자동화 => 수정버전(레거시) 버전이 스크립트로 편집 가능한 부분들..

 

 

 

위는 테스트를 아주 짧게 해본 코드

 

 

 

포톤과도 인테그레이션이 가능하다는 것을 알수 있다

기본적으로 함수가 하나의 API 형식이 되는 꼴이다 사용성은 편하다

 

 

테스트해본결과 기본 유저당 데이터들도 클라와 서버간의 전송도 가능하다

클라와 서버와의 직접적인 데이터 전송은 위험하기에 이런 스크립트를 제공하는 듯 하다

방향성은 괜찮다고 보여짐

 

 

 

 

그외에 제공되는 기능들은 대략적으로 다음들이다

 

 

  1. Multiplayer Services를 통해 더 빠르게 빌드하고 좀 더 비용 효율적인 게임 시작
  2. Analytics로 게임 최적화
  3. LiveOps를 통해 게임을 서비스로 실행하고 플레이어가 더 많은 것을 위해 게임을 지속하도록 유도

Multiplayer Servers

대기 시간이 짧고 신뢰성이 높은 실시간 멀티 플레이어 게임 플레이를 제공하도록 전용 Multiplayer Servers를 동적으로 스케일링합니다.

파티 네트워킹 및 채팅

Azure Cognitive Services에서 지원하는 PlayFab 파티의 원활한 플레이어 간 소통을 통해 플레이어가 관계를 형성하고 커뮤니티를 구축하도록 도와줍니다.

매치 메이킹 및 그룹

플레이어가 새로운 친구와 경쟁자를 찾도록 돕고, 더 흥미롭고 더 나은 보상이 따르는 플레이를 위해 모든 수준의 플레이어에게 더 빠르고 우수한 매치를 제공합니다.

순위표 및 통계

토너먼트, 순위표, 상품을 통해 플레이어의 성공을 추적, 비교, 보상합니다.

네트워크 간 ID 및 데이터

사용 플랫폼에서 플레이어 요구를 충족하고 계정을 연결하여 Xbox, PSN, Nintendo 등에서 로밍하도록 허용합니다.

 

 

 

 

ref : https://azure.microsoft.com/ko-kr/services/playfab/#overview

 

반응형
반응형

Reliable UDP (이하 RUDP)는 신뢰성을 갖는 UDP를 의미합니다.

일반적으로 TCP는 신뢰성을 갖는 대신 느리고, UDP는 신뢰성이 없고 빠르다고 알려져있죠.

여기에 또 하나의 특징은, TCP는 서버 (Listener)와, 클라이언트 (Connector) 관계가 성립한다는 점입니다.

즉, 서버건 클라이언트건 연결 관리가 필요하다는 것이죠.

RUDP의 필요성은, 주로 클라이언트 끼리의 통신에서 대두되었습니다.

우선 일반적인 클라이언트/서버 구조에서의 클라이언트 끼리의 통신은 서버를 경유해서 데이터를 전송함으로써 신뢰성을 갖추는데, UDP보다 느리고 서버에 부하를 주기 때문에 클라이언트 끼리의 통신에서도 TCP의 장점은 신뢰성과, UDP의 장점은 속도를 모두 갖춘 Reliable UDP가 등장하게 된 것이죠.

TCP는 한쪽이 서버가 되어서 대기 하고 있어야하지만, UDP는 그럴 필요가 없이 바로 통신이 가능하다는 점도 또 하나의 장점입니다.

보통 RUDP는 Relay Server와 연동되어서 구현이 되는데요, 모든 상황에서 UDP 통신이 가능한 것이 아니기 때문에, UDP 통신이 실패했을 때 신뢰성 갖춘 통신을 위해 Relay Server를 통한 데이터 전송을 해주기 위해서입니다.

우선 UDP가 Reliable 해지기 위한 방법부터 알아봅시다.

TCP가 UDP와 다른 점은 신뢰성이 있다고 얘기했었죠? TCP가 신뢰성을 갖추고 있는 이유를 먼저 얘기해보겠습니다.

1. 데이터의 순서를 보장해줍니다. 보낸 순서와 받는 순서가 일치하게 해준다는 의미입니다.
2. 데이터의 도착을 보장해줍니다. 보낸 데이터가 반드시 도착한다는 것을 보장해준다는 의미입니다.
3. 데이터의 무결성을 보장해줍니다. 즉, 보낸 데이터와 받는 데이터가 일치 하다는 것을 보장한다는 의미입니다.

위의 세가지 조건을 만족하기에 TCP가 신뢰성을 갖추고 있다고 말하는 것이고, RUDP도 마찬가지로 위 세가지 조건을 UDP를 통해 만족하도록 구현함으로써 신뢰성을 갖게 되는 것이죠.

순서를 보장하는 방법은 패킷에 번호를 붙이고, 번호순서대로 패킷이 도착할때까지 기다렸다가 패킷이 모두 모이면 그때 패킷을 풀면됩니다.

도착을 보장하는 방법은 패킷에 번호를 붙이고, 해당 번호의 패킷이 도착할때까지 재전송하면 됩니다.

보내는 입장에서 재전송을 하는 이유는, UDP이기에 받는 입장에서는 자신에게 보내려는 패킷이 있었는지 알 방법이 없기 때문이죠.

무결성을 보장하는 방법은 체크섬을 통해서, 데이터가 손실되지 않았는지 검증합니다.

RUDP사용시 주의사항은, UDP로 연결을 시도하는 중에도 릴레이 서버를 통한 패킷 전달은 지속적으로 이루어 져야 한다는 점입니다.

그리고 UDP연결이 성공했을 때 UDP를 통한 송신을 시작해야 합니다.

UDP 송수신 중에는 연결 유지를 위해 일정 시간 간격으로 HeartBeat 패킷을 보내고, 일정 시간동안 해당 패킷이 도착하지 않는다면  UDP전송이 다시금 불가능해진 것으로 판단하여, 릴레이 서버를 이용하도록 하면서 지금껏 전송 확인이 안된 패킷들을 릴레이를 통해 전달하면 됩니다. 이렇게 해야 패킷의 지연은 있을 수 있으나 패킷의 소실은 발생하지 않습니다.


ref : 

https://elky.tistory.com/258

반응형
반응형


기존엔 함수 형태를 클래스 처럼 (억지로??) 맞추는 형태였기에
구문 문법 자체가 지저분해졌는데 Class 를 사용함으로서 이보다는 좀더 깔끔하게 정의할 수 있다

JavaScript class는 ECMAScript 6을 통해 소개되었으며, 기존 prototype 기반의 상속 보다 명료하게 사용할 수 있습니다. Class 문법은 새로운 객체지향 상속 모델을 제공하는 것은 아닙니다. JavaScript class는 객체를 생성하고 상속을 다루는데 있어 훨씬 더 단순하고 명확한 문법을 제공합니다.

Link to sectionClass 정의

Class는 사실 함수입니다. 함수를 함수 표현식과 함수 선언으로 정의할 수 있듯이 class 문법도 class 표현식과 class 선언 두 가지 방법을 제공합니다.

Link to sectionClass 선언

class를 정의하는 한 가지 방법은 class 선언을 이용하는 것입니다. class를 선언하기 위해서는 클래스의 이름과 함께 class 키워드를 사용해야 합니다.

class Polygon {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

Hoisting

함수 선언과 클래스 선언의 중요한 차이점은 함수 선언의 경우 호이스팅이 일어나지만, 클래스 선언은 그렇지 않다는 것입니다. 클래스를 사용하기 위해서는 클래스를 먼저 선언 해야 하며, 그렇지 않으면, 다음 아래의 코드는 ReferenceError 에러를 던질 것입니다. :

var p = new Polygon(); // ReferenceError

class Polygon {}

Link to sectionClass 표현식

class 표현식은 class를 정의 하는 또 다른 방법입니다. Class 표현식은 이름을 가질 수도 있고, 갖지 않을 수도 있습니다. 기명 class 표현식에 주어진 이름은 클래스의 body에 대해 local scope에 한해 유효합니다.

// unnamed
var Polygon = class {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};

// named
var Polygon = class Polygon {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};

Link to sectionClass body 와 method 정의

Class body는 중괄호 {} 로 묶여 있는 안쪽 부분입니다.. 이곳은 여러분이 method나 constructor와 같은 class members를 정의할 곳입니다.

Link to sectionStrict mode

클래스 선언과 클래스 표현식의 본문(body)은 strict mode 에서 실행됩니다.

Link to sectionConstructor (생성자)

constructor 메소드는 class 로 생성된 객체를 생성하고 초기화하기 위한 특수한 메소드입니다.  "constructor" 라는 이름을 가진 특수한 메소드는 클래스 안에 한 개만 존재할 수 있습니다. 만약 클래스에 한 개를 초과하는 constructor 메소드를 포함한다면, SyntaxError 가 발생할 것입니다.

constructor는 부모 클래스의 constructor 를 호출하기 위해 super 키워드를 사용할 수 있습니다.

Link to sectionPrototype methods

method definitions도 참조해보세요.

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
  // Getter
  get area() {
    return this.calcArea();
  }
  // Method
  calcArea() {
    return this.height * this.width;
  }
}

const square = new Rectangle(10, 10);

console.log(square.area); // 100

Link to sectionStatic methods

static 키워드는 클래스를 위한 정적(static) 메소드를 정의합니다. 정적 메소드는 클래스의 인스턴스화(instantiating) 없이 호출되며, 클래스의 인스턴스에서는 호출할 수 없습니다. 정적 메소드는 어플리케이션(application)을 위한 유틸리티(utility) 함수를 생성하는데 주로 사용됩니다.

class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }

    static distance(a, b) {
        const dx = a.x - b.x;
        const dy = a.y - b.y;

        return Math.sqrt(dx*dx + dy*dy);
    }
}

const p1 = new Point(5, 5);
const p2 = new Point(10, 10);

console.log(Point.distance(p1, p2));

 

Link to sectionBoxing with prototype and static methods

정적 메소드나 프로토타입 메소드가 this 값 없이 호출될 때, this 값은 메소드 안에서 undefined가 됩니다. class 문법 안에 있는 코드는 항상 strict mode 로 실행되기 때문에 이동작은 "use strict" 명령어가 없더라도 같습니다.

 

class Animal { 
  speak() {
    return this;
  }
  static eat() {
    return this;
  }
}

let obj = new Animal();
obj.speak(); // Animal {}
let speak = obj.speak;
speak(); // undefined

Animal.eat() // class Animal
let eat = Animal.eat;
eat(); // undefined

If the above is written using traditional function–based syntax, then autoboxing in method calls will happen in non–strict mode based on the initial this value. If the inital value is undefinedthis will be set to the global object.

Autoboxing will not happen in strict mode, the this value remains as passed.

function Animal() { }

Animal.prototype.speak = function() {
  return this;
}

Animal.eat = function() {
  return this;
}

let obj = new Animal();
let speak = obj.speak;
speak(); // global object

let eat = Animal.eat;
eat(); // global object

Link to sectionInstance properties

Instance properties must be defined inside of class methods:

class Rectangle {
  constructor(height, width) {    
    this.height = height;
    this.width = width;
  }
}

Static class-side properties and prototype data properties must be defined outside of the ClassBody declaration:

Rectangle.staticWidth = 20;
Rectangle.prototype.prototypeWidth = 25;

 

 

 

Link to sectionextends를 통한 클래스 상속(sub classing)

extends 키워드는 클래스 선언이나 클래스 표현식에서 다른 클래스의 자식 클래스를 생성하기 위해 사용됩니다.

class Animal { 
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    console.log(this.name + ' makes a noise.');
  }
}

class Dog extends Animal {
  speak() {
    console.log(this.name + ' barks.');
  }
}

subclass에 constructor가 있다면, "this"를 사용하기 전에 가장 먼저 super()를 호출해야 합니다.

또한 es5에서 사용되던 전통적인 함수 기반의 클래스를 통하여 확장할 수도 있습니다.

function Animal (name) {
  this.name = name;  
}
Animal.prototype.speak = function () {
  console.log(this.name + ' makes a noise.');
}

class Dog extends Animal {
  speak() {
    console.log(this.name + ' barks.');
  }
}

var d = new Dog('Mitzie');
d.speak();

클래스는 생성자가 없는 객체(non-constructible)을 확장할 수 없습니다. 만약 기존의 생성자가 없는 객체을 확장하고 싶다면, 이 메소드를 사용하세요. Object.setPrototypeOf():

var Animal = {
  speak() {
    console.log(this.name + ' makes a noise.');
  }
};

class Dog {
  constructor(name) {
    this.name = name;
  }
  speak() {
    console.log(this.name + ' barks.');
  }
}
Object.setPrototypeOf(Dog.prototype, Animal);

var d = new Dog('Mitzie');
d.speak();

Link to sectionSub classing built-in objects

TBD

Link to sectionSpecies

아마 배열을 상속 받은 MyArray 클래스에서 Array를 반환하고 싶을 것입니다. Species 패턴은 기본 생성자를 덮어쓰도록 해줍니다.

예를 들어, map()과 같은 기본 생성자를 반환하는 메서드를 사용할 때 이 메서드가 MyArray 객체 대신 Array 객체가 반환하도록 하고 싶을 것입니다. Symbol.species 심볼은 이러한 것을 가능하게 해줍니다:

class MyArray extends Array {
  // 부모 Array 생성자로 종류 덮어쓰기
  static get [Symbol.species]() { return Array; }
}
var a = new MyArray(1,2,3);
var mapped = a.map(x => x * x);

console.log(mapped instanceof MyArray); // false
console.log(mapped instanceof Array);   // true

Link to sectionsuper 를 통한 상위 클래스 호출

super 키워드는 객체의 부모가 가지고 있는 함수들을 호출하기 위해 사용됩니다..

class Cat { 
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    console.log(this.name + ' makes a noise.');
  }
}

class Lion extends Cat {
  speak() {
    super.speak();
    console.log(this.name + ' roars.');
  }
}

Link to sectionES5 프로토타입 상속 문법과 ES6 클래스 상속 문법의 비교

ES6 (ES2015):

class Cat { 
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    console.log(this.name + ' makes a noise.');
  }
}

class Lion extends Cat {
  speak() {
    super.speak();
    console.log(this.name + ' roars.');
  }
}

ES5:

function Cat(name) {
  this.name = name;
}

Cat.prototype.speak = function () {
  console.log(this.name + ' makes a noise.');
};

function Lion(name) {
  // `super()` 호출
  Cat.call(this, name);
}

// `Cat` 클래스 상속
Lion.prototype = Object.create(Cat.prototype);
Lion.prototype.constructor = Lion;

// `speak()` 메서드 오버라이드
Lion.prototype.speak = function () {
  Cat.prototype.speak.call(this);
  console.log(this.name + ' roars.');
};

 

 

Link to sectionMix-ins

추상 서브 클래스 또는 믹스-인은 클래스를 위한 템플릿입니다. ECMAScript 클래스는 하나의 단일 슈퍼클래스만을 가질 수 있으며, 예를 들어 툴링 클래스로부터의 다중 상속은 불가능합니다. 기능은 반드시 슈퍼클래스에서 제공되어야 합니다.

슈퍼클래스를 인자로 받고 이 슈퍼클래스를 확장하는 서브클래스를 생성하여 반환하는 함수를 사용하여 ECMAScript에서 믹스-인을 구현할 수 있습니다:

아래는 두 함수는 어떤 클래스를 인자로 받고 그 클래스를 상속한 새로운 익명 클래스를 리턴하는 arrow function 입니다.

var calculatorMixin = Base => class extends Base {
  calc() { }
};

var randomizerMixin = Base => class extends Base {
  randomize() { }
};

위 믹스-인을 사용하는 클래스는 다음과 같이 작성할 수 있습니다:

class Foo { }
class Bar extends calculatorMixin(randomizerMixin(Foo)) { }

ref : https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Classes


반응형

'서버(Server) > Server' 카테고리의 다른 글

소켓 통신  (0) 2022.12.22
통신 모델 OSI 7 계층  (0) 2022.12.22
Promise [1] catch & all  (0) 2018.08.19
Promise [2]  (0) 2018.08.16
[javascript] Singleton 싱글톤  (0) 2018.07.02
반응형


Promise

“A promise is an object that may produce a single value some time in the future”

프로미스는 자바스크립트 비동기 처리에 사용되는 객체입니다. 여기서 자바스크립트의 비동기 처리란 ‘특정 코드의 실행이 완료될 때까지 기다리지 않고 다음 코드를 먼저 수행하는 자바스크립트의 특성’을 의미합니다. 비동기 처리에 대한 이해가 없으시다면 이전 글 ‘자바스크립트 비동기 처리와 콜백 함수’를 읽어보시길 추천드립니다 :)

Promise가 왜 필요한가요?

프로미스는 주로 서버에서 받아온 데이터를 화면에 표시할 때 사용합니다. 일반적으로 웹 애플리케이션을 구현할 때 서버에서 데이터를 요청하고 받아오기 위해 아래와 같은 API를 사용합니다.

$.get('url 주소/products/1', function (response) {
  // ...
});

위 API가 실행되면 서버에다가 ‘데이터 하나 보내주세요’ 라는 요청을 보내죠. 그런데 여기서 데이터를 받아오기도 전에 마치 데이터를 다 받아온 것 마냥 화면에 데이터를 표시하려고 하면 오류가 발생하거나 빈 화면이 뜹니다. 이와 같은 문제점을 해결하기 위한 방법 중 하나가 프로미스입니다.

프로미스 코드 - 기초

그럼 프로미스가 어떻게 동작하는지 이해하기 위해 예제 코드를 살펴보겠습니다. 먼저 아래 코드는 간단한 ajax 통신 코드입니다.

function getData(callbackFunc) {
  $.get('url 주소/products/1', function (response) {
    callbackFunc(response); // 서버에서 받은 데이터 response를 callbackFunc() 함수에 넘겨줌
  });
}

getData(function (tableData) {
  console.log(tableData); // $.get()의 response 값이 tableData에 전달됨
});

위 코드는 제이쿼리의 ajax 통신을 이용하여 지정한 url에서 1번 상품 데이터를 받아오는 코드입니다. 비동기 처리를 위해 프로미스 대신에 콜백 함수를 이용했죠.

위 코드에 프로미스를 적용하면 아래와 같은 코드가 됩니다.

function getData(callback) {
  // new Promise() 추가
  return new Promise(function (resolve, reject) {
    $.get('url 주소/products/1', function (response) {
      // 데이터를 받으면 resolve() 호출
      resolve(response);
    });
  });
}

// getData()의 실행이 끝나면 호출되는 then()
getData().then(function (tableData) {
  // resolve()의 결과 값이 여기로 전달됨
  console.log(tableData); // $.get()의 reponse 값이 tableData에 전달됨
});

콜백 함수로 처리하던 구조에서 new Promise()resolve()then()와 같은 프로미스 API를 사용한 구조로 바뀌었습니다. 여기서 new Promise()는 좀 이해가 가겠는데 resolve()then()은 뭐 하는 애들일까요? 아래에서 함께 알아보겠습니다.

프로미스의 3가지 상태(states)

프로미스를 사용할 때 알아야 하는 가장 기본적인 개념이 바로 프로미스의 상태(states)입니다. 여기서 말하는 상태란 프로미스의 처리 과정을 의미합니다. new Promise()로 프로미스를 생성하고 종료될 때까지 3가지 상태를 갖습니다.

  • Pending(대기) : 비동기 처리 로직이 아직 완료되지 않은 상태
  • Fulfilled(이행) : 비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태
  • Rejected(실패) : 비동기 처리가 실패하거나 오류가 발생한 상태

Pending(대기)

먼저 아래와 같이 new Promise() 메서드를 호출하면 Pending(대기) 상태가 됩니다.

new Promise();

이렇게 new Promise() 메서드를 호출할 때 콜백 함수의 인자로 resolve, reject에 접근할 수 있습니다.

new Promise(function (resolve, reject) {
  // ...
});

Fulfilled(이행)

여기서 콜백 함수의 인자 resolve를 아래와 같이 실행하면 Fulfilled(이행) 상태가 됩니다.

new Promise(function (resolve, reject) {
  resolve();
});

그리고 이행 상태가 되면 아래와 같이 then()을 이용하여 처리 결과 값을 받을 수 있습니다.

function getData() {
  return new Promise(function (resolve, reject) {
    var data = 100;
    resolve(data);
  });
}

// resolve()의 결과 값 data를 resolvedData로 받음
getData().then(function (resolvedData) {
  console.log(resolvedData); // 100
});

프로미스의 '이행' 상태를 좀 다르게 표현해보면 '완료' 입니다.

Rejected(실패)

new Promise()로 프로미스 객체를 생성하면 콜백 함수 인자로 resolve와 reject를 사용할 수 있다고 했습니다. 여기서 reject 인자로 reject() 메서드를 실행하면 Rejected(실패) 상태가 됩니다.

new Promise(function (resolve, reject) {
  reject();
});

그리고, 실패 상태가 되면 실패한 이유(실패 처리의 결과 값)를 catch()로 받을 수 있습니다.

function getData() {
  return new Promise(function (resolve, reject) {
    reject(new Error("Request is failed"));
  });
}

// reject()의 결과 값 Error를 err에 받음
getData().then().catch(function (err) {
  console.log(err); // Error: Request is failed
});



프로미스 처리 흐름 - 출처 : MDN

프로미스 코드 - 쉬운 예제

그럼 위에서 배운 내용들을 종합하여 간단한 프로미스 코드를 만들어보겠습니다. 이해하기 쉽게 앞에서 살펴본 ajax 통신 예제 코드에 프로미스를 적용해보겠습니다.

function getData() {
  return new Promise(function (resolve, reject) {
    $.get('url 주소/products/1', function (response) {
      if (response) {
        resolve(response);
      }
      reject(new Error("Request is failed"));
    });
  });
}

// Fulfilled 또는 Rejected의 결과 값 출력
getData().then(function (data) {
  console.log(data); // response 값 출력
}).catch(function (err) {
  console.error(err); // Error 출력
});

위 코드는 서버에서 제대로 응답을 받아오면 resolve() 메서드를 호출하고, 응답이 없으면 reject() 메서드를 호출하는 예제입니다. 호출된 메서드에 따라 then()이나 catch()로 분기하여 데이터 또는 오류를 출력합니다.

여러 개의 프로미스 연결하기 (Promise Chaining)

프로미스의 또 다른 특징은 여러 개의 프로미스를 연결하여 사용할 수 있다는 점입니다. 앞 예제에서 then() 메서드를 호출하고 나면 새로운 프로미스 객체가 반환됩니다. 따라서, 아래와 같이 코딩이 가능합니다.

function getData() {
  return new Promise({
    // ...
  });
}

// then() 으로 여러 개의 프로미스를 연결한 형식
getData()
  .then(function (data) {
    // ...
  })
  .then(function () {
    // ...
  })
  .then(function () {
    // ...
  });

그러면 위의 형식을 참고하여 실제로 돌려볼 수 있는 예제를 살펴보겠습니다. 비동기 처리 예제에서 가장 흔하게 사용되는 setTimeout() API를 사용하였습니다.

new Promise(function(resolve, reject){
  setTimeout(function() {
    resolve(1);
  }, 2000);
})
.then(function(result) {
  console.log(result); // 1
  return result + 10;
})
.then(function(result) {
  console.log(result); // 11
  return result + 20;
})
.then(function(result) {
  console.log(result); // 31
});

위 코드는 프로미스 객체를 하나 생성하고 setTimeout()을 이용해 2초 후에 resolve()를 호출하는 예제입니다.

resolve()가 호출되면 프로미스가 대기 상태에서 이행 상태로 넘어가기 때문에 첫 번째 .then()의 로직으로 넘어갑니다. 첫 번째 .then()에서는 이행된 결과 값 1을 받아서 10을 더한 후 그다음 .then() 으로 넘겨줍니다. 두 번째 .then()에서도 마찬가지로 바로 이전 프로미스의 결과 값 11을 받아서 20을 더하고 다음 .then()으로 넘겨줍니다. 마지막 .then()에서 최종 결과 값 31을 출력합니다.

실무에서 있을 법한 프로미스 연결 사례

실제 웹 서비스에서 있을 법한 사용자 로그인 인증 로직에 프로미스를 여러 개 연결해보겠습니다.

getData(userInfo)
  .then(parseValue)
  .then(auth)
  .then(diaplay);

위 코드는 페이지에 입력된 사용자 정보를 받아와 파싱, 인증 등의 작업을 거치는 코드를 나타내었습니다. 여기서 userInfo는 사용자 정보가 담긴 객체를 의미하고, parseValueauthdisplay는 각각 프로미스를 반환해주는 함수라고 가정했습니다. 아래와 같이 말이죠.

var userInfo = {
  id: 'test@abc.com',
  pw: '****'
};

function parseValue() {
  return new Promise({
    // ...
  });
}
function auth() {
  return new Promise({
    // ...
  });
}
function display() {
  return new Promise({
    // ...
  });
}

이처럼 여러 개의 프로미스를 .then()으로 연결하여 처리할 수 있습니다.

프로미스의 에러 처리 방법

앞에서 살펴본 프로미스 예제는 코드가 항상 정상적으로 동작한다고 가정하고 구현한 예제입니다. 실제 서비스를 구현하다 보면 네트워크 연결, 상태 코드 문제 등으로 인해 오류가 발생할 수 있습니다. 따라서, 프로미스의 에러 처리 방법에 대해서도 알고 있어야 합니다.

에러 처리 방법에는 다음과 같이 2가지 방법이 있습니다. 

1.then()의 두 번째 인자로 에러를 처리하는 방법

getData().then(
  handleSuccess,
  handleError
);

2.catch()를 이용하는 방법

getData().then().catch();

위 2가지 방법 모두 프로미스의 reject() 메서드가 호출되어 실패 상태가 된 경우에 실행됩니다. 간단하게 말해서 프로미스의 로직이 정상적으로 돌아가지 않는 경우 호출되는 거죠. 아래와 같이 말입니다.

function getData() {
  return new Promise(function (resolve, reject) {
    reject('failed');
  });
}

// 1. then()으로 에러를 처리하는 코드
getData().then(function () {
  // ...
}, function (err) {
  console.log(err);
});

// 2. catch()로 에러를 처리하는 코드
getData().then().catch(function (err) {
  console.log(err);
});

프로미스 에러 처리는 가급적 catch()로

앞에서 프로미스 에러 처리 방법 2가지를 살펴봤습니다. 개개인의 코딩 스타일에 따라서 then()의 두 번째 인자로 처리할 수도 있고 catch()로 처리할 수도 있겠지만 가급적 catch()로 에러를 처리하는 게 더 효율적입니다.

그 이유는 아래의 코드를 보시면 알 수 있습니다.

// then()의 두 번째 인자로는 감지하지 못하는 오류
function getData() {
  return new Promise(function (resolve, reject) {
    resolve('hi');
  });
}

getData().then(function (result) {
  console.log(result);
  throw new Error("Error in then()"); // Uncaught (in promise) Error: Error in then()
}, function (err) {
  console.log('then error : ', err);
});

getData() 함수의 프로미스에서 resolve() 메서드를 호출하여 정상적으로 로직을 처리했지만, then()의 첫 번째 콜백 함수 내부에서 오류가 나는 경우 오류를 제대로 잡아내지 못합니다. 따라서 코드를 실행하면 아래와 같은 오류가 납니다.

'에러를 잡지 못했습니다(Uncaught Error)' 로그

하지만 똑같은 오류를 catch()로 처리하면 다른 결과가 나옵니다.

// catch()로 오류를 감지하는 코드
function getData() {
  return new Promise(function (resolve, reject) {
    resolve('hi');
  });
}

getData().then(function (result) {
  console.log(result); // hi
  throw new Error("Error in then()");
}).catch(function (err) {
  console.log('then error : ', err); // then error :  Error: Error in then()
});

위 코드의 처리 결과는 다음과 같습니다.

발생한 에러를 성공적으로 콘솔에 출력한 모습

따라서, 더 많은 예외 처리 상황을 위해 프로미스의 끝에 가급적 catch()를 붙이시기 바랍니다.







Rookie mistake #5: using side effects instead of returning
이 코드에 어떤 문제가 있는 것 같나요?
somePromise().then(function () {
  someOtherPromise();
}).then(function () {
  // Gee, I hope someOtherPromise() has resolved!
  // Spoiler alert: it hasn't.
});

좋습니다. 이 코드를 가지고 당신이 알아야 하는 promise의 모든 것에 대해서 이야기 할 수 있습니다.
진지하게, 이 방법은 꽤나 이상해 보일 수 있지만 한번 이해하고 난 다음에는 우리가 여태 이야기한 모든 에러들을 예방할 수 있는 매우 좋은 방법입니다.

이전에 말했는데 promise가 가진 최대의 이점은 우리에게 소중한 return과 throw를 되돌려준다는 것입니다. 하지만 실제로 이게 어떤 형태일까요?

모든 promise는 우리에게 then() 메서드를 제공합니다. (또는 catch() 메서드나요.) then() 메서드 내부를 보겠습니다.

somePromise().then(function () {
  // I'm inside a then() function!
});

이 함수의 내부에서 우리는 어떤 일을 할 수가 있을까요? 세가지가 가능합니다.
1. return 다른 promise
2. return 발생한 값 이나 undefined
3, throw 에러
이게 다입니다. 이제 이것들을 이해를 하면 여러분은 promise를 이해한 것입니다. 하나씩 살펴보겠습니다.

1. Return another promise
이는 위에서 살펴본 composing promise의 일반적인 패턴입니다.

getUserByName('nolan').then(function (user) {
  return getUserAccountById(user.id);
}).then(function (userAccount) {
  // I got a user account!
});

첫 번째 함수가 getUserAccountById() 함수를 리턴 합니다, 다음 함수로 말이지요. 다음 메서드는 콜백으로 해당 함수의 리턴값을 받습니다. 여기서 return을 하는 것이 매우 중요한데 만약에 return을 하지 않는다면 다음 함수는 아마 undefined를 받게 될 것입니다. 우리는 userAccount가 필요한데 말입니다.

2. Return a synchronous value (or undefined)
undefined를 리턴 하는 일은 종종 발생하는 실수입니다. 하지만 동기적으로 발생한 값을 리턴 하는 것은 동기적인 코드를 promise방식의 코드로 변환하는 굉장한 방법입니다. 예를들어 우리가 사용자 캐시를 in-memory 에 가지고 있다고 하겠습니다. 우리는 아래의 코드처럼 구현할 수 있습니다.

getUserByName('nolan').then(function (user) {
  if (inMemoryCache[user.id]) {
    return inMemoryCache[user.id];    // returning a synchronous value!
  }
  return getUserAccountById(user.id); // returning a promise!
}).then(function (userAccount) {
  // I got a user account!
});

굉장하지 않나요? 두 번째 함수는 userAccount가 동기적이나 비동기적으로 가져온 값인지 아닌지는 신경 쓰지 않습니다. 첫 번째 함수는 동기적인 값이나 비 동기적인 값 모두를 마음대로 전달할 수가 있습니다.
불행하게도 값을 리턴하지 않는 함수의 경우에는 자바스크립트는 undefined로 값을 처리해버립니다. 이는 예상치 못한 문제를 일으키게 됩니다.
이러한 이유로 저는 개인적으로는 then() 함수 안에서는 반드시 throw나 return을 통해서 무언가를 다음 함수로 전달을 시킵니다. 
여러분도 이렇게 해야 합니다.

3. Throw a synchronous error
throw는 promise를 더욱 굉장하게 만들어 주는 역할을 합니다. 동기적으로 에러가 발생하면 throw를 통해 에러를 전달합니다. 사용자가 로그아웃이 되었다면 에러를 발생시키는 코드입니다. 

getUserByName('nolan').then(function (user) {
  if (user.isLoggedOut()) {
    throw new Error('user logged out!'); // throwing a synchronous error!
  }
  if (inMemoryCache[user.id]) {
    return inMemoryCache[user.id];       // returning a synchronous value!
  }
  return getUserAccountById(user.id);    // returning a promise!
}).then(function (userAccount) {
  // I got a user account!
}).catch(function (err) {
  // Boo, I got an error!
});

우리의 catch()는 사용자가 로그아웃이 되었다면 동기적으로 에러를 받습니다. 그리고 물론 promise가 reject가 발생하는 경우 비동기적으로 에러를 받습니다. 다시 한번 강조하면 함수는 에러가 동기적이든 비동기적이든 전혀 신경 쓰지 않습니다.

이는 매우 유용한데 왜냐하면 개발 시에 코딩의 에러를 발견하는데 도움을 줍니다. 예를 들어 then() 메서드의 내부에 JSON.parse()를 한다고 가정 하겠습니다. 이 경우에 json에 문제가 있다면 동기적으로 에러를 발생시킬 수 있습니다. callback의 경우에는 에러를 무시하는 일이 발생합니다. 하지만 promise와 함께라면 우리는 간단하게 catch() 내부에서 이모든 일들을 처리할 수 있습니다.


Advanced mistakes
자, 이제 조금 더 깊이 있게 들어가보겠습니다.

이번에 다룰 내용들은 advanced 내용입니다. advanced로 분류가 된 이유는 이미 promise를 이해하고 받아들인 개발자들이 일으키는 실수이기 때문입니다. 하지만 우리는 이것들을 다룰 필요가 있습니다. 만약에 우리가 쉽게 해결할 수 있는 문제였다면 앞으로의 내용들을 beginner 섹션에 다뤘을 것입니다.

Advanced mistake #1: not knowing aboutPromise.resolve()
위의 다양한 예들을 통해 promise는 동기적인 코드를 비 동기적인 코드로 감싸주는 매우 유용한 기능입니다. 구현은 아래와 같이 합니다.

new Promise(function (resolve, reject) {
  resolve(someSynchronousValue);
}).then(/* ... */);

위의 표현을 Promise.resolve()를 통해 훨씬 간결하게 표현하는 것이 가능합니다.
Promise.resolve(someSynchronousValue).then(/* ... */);

이 방법은 어떠한 동기적인 에러를 잡는데 매우 유용합니다. 그래서 저는 대부분의 promise-returning API를 구현할 때 제일 첫 시점에 아래와 같이 형태를 잡아두고 시작을 합니다.

function somePromiseAPI() {
  return Promise.resolve().then(function () {
    doSomethingThatMayThrow();
    return 'foo';
  }).then(/* ... */);
}

그냥 기억하세요. 동기적으로 throw가 발생하는 코드는 코드상의 어딘가에서 에러가 묻혀버려 거의 디버깅이 불가능합니다. 하지만 만약 모든 것들을 Promise.resolve()로 감싼다면 우린 항상 이것들일 catch()를 통해 확인할 수가 있습니다.

이와 유사하게 Promise.reject()가 있는데 이는 당장 promise 를 거부시키는데 사용할수있습니다.
Promise.reject(new Error('some awful error'));


Advanced mistake #2: catch() isn't exactly likethen(null, ...)
저는 위에서 catch()는 매우 유용하다고 말했습니다. 그래서 아래의 두 개의 작은 코드는 동일합니다.
somePromise().catch(function (err) {
  // handle error
});

somePromise().then(null, function (err) {
  // handle error
});


하지만 아래의 두 개의 코드는 동일한 의미는 아닙니다.
somePromise().then(function () {
  return someOtherPromise();
}).catch(function (err) {
  // handle error
});

somePromise().then(function () {
  return someOtherPromise();
}, function (err) {
  // handle error
});

만약에 위의 두 코드가 왜 다른지 궁금하다면 첫번째 코드에서 첫 함수가 에러를 반환한다면 무슨 일이 벌어질지를 생각해보면 됩니다.
somePromise().then(function () {
  throw new Error('oh noes');
}).catch(function (err) {
  // I caught your error! :)
});

somePromise().then(function () {
  throw new Error('oh noes');
}, function (err) {
  // I didn't catch your error! :(
});


보시는 봐와 같이 then(resolveHandler, rejectHandler)의 형태로 구현을 한다면 에러가 resolveHandler를 통해 반환이 된경우라면 rejectHandler가 에러를 잡아내지 못하게 됩니다.

이러한 이유로 저는 절대 두번째 방식으로는 구현을 하지 않습니다. 항상 에러에 대비해서는 catch()를 사용합니다. 단, Mocha 를 통한 비동기적인 테스트 코드 구현은 제외합니다.
it('should throw an error', function () {
  return doSomethingThatThrows().then(function () {
    throw new Error('I expected an error!');
  }, function (err) {
    should.exist(err);
  });
});



Promise.all()


Promise.all(iterable) 메서드는 iterable 인자의 모든 프로미스가 이행하거나 iterable 인자가 비어있는 경우 이행하는 프로미스를 반환합니다. iterable 인자 내 임의의 프로미스가 거부하는 경우 첫 번째로 거절한 프로미스의 이유를 사용해 거절합니다.

구문

Promise.all(iterable);

Link to section매개변수

iterable
Array 또는 String과 같이 순회 가능한 객체.

Link to section반환값

  • 인자가 빈 경우, 이미 이행된 Promise.
  • 인자에 프로미스가 없는 경우, 비동기적으로 이행된 Promise. 단, 구글 크롬 58은 이미 이행된 프로미스를 반환합니다.
  • 다른 모든 경우, 대기 중인 Promise. 반환하는 프로미스는 나중에 비동기적으로(스택이 비는 시점에) , 모든 프로미스가 이행하거나 임의의 프로미스가 거부할 때 이행/거부합니다. "Promise.all 실패-우선 연산" 예제를 참고하세요. 반환하는 프로미스의 이행 값은 인자로 넘어온 프로미스의 순서와 일치하며 완료 순서에 영향을 받지 않습니다.

Link to section설명

이 메서드는 여러 프로미스의 결과를 집계할 때 유용하게 사용할 수 있습니다.

이행:
빈 iterable이 전달되면이 메서드는 이미 해결 된 promise를 동기적으로 반환합니다.
전달 된 promise가 모두 충족(fulfilled)되거나 promise가 아닌 경우 Promise.all에서 반환하는 promise는 비동기 적으로 수행됩니다.
모든 경우에 반환된 promise는 인수로 전달 된 반복 가능한 값의 모든 값 (non-promise value)을 포함하는 배열로 충족(fulfilled)됩니다.

거부:
전달 된 promise중 하나라도 거부하면 Promise.all은 다른 promise가 남아있는지에 여부에 관계없이 다른 모든 Promise를 버리고 문제가 되는 값과 함께 거절(reject)한다.

 

Link to section예제

Link to sectionPromise.all사용하기

Promise.all은 배열 내 모든 값이 결정(resolve)할 때까지 기다립니다.

var p1 = Promise.resolve(3);
var p2 = 1337;
var p3 = new Promise(function(resolve, reject) {
  setTimeout(resolve, 100, "foo");
}); 

Promise.all([p1, p2, p3]).then(function(values) { 
  console.log(values); // [3, 1337, "foo"] 
});

Link to sectionPromise.all 실패-우선 연산

Promise.all 은 배열 내 요소 중 어느 하나라도 거절(reject)하면 즉시 거절합니다. 즉 Promise.all 은 빠르게 실패합니다: 만약 timeout 이후 결정(resolve)하는 4개의 Promise를 가지고 있고, 그 중 하나가 거절(reject)하면 Promise.all 은 즉시 거절합니다.


all 구문중 실패시하는  Promise 함수가 있다면 해당 실패 promise 함수의 인자가 넘어오게 된다

성공하면 모든 인자가 출력 된다


var p1 = new Promise(function(resolve, reject) { 
  setTimeout(resolve, 1000, "one"); 
}); 
var p2 = new Promise(function(resolve, reject) { 
  setTimeout(resolve, 2000, "two"); 
});
var p3 = new Promise(function(resolve, reject) {
  setTimeout(resolve, 3000, "three");
});
var p4 = new Promise(function(resolve, reject) {
  setTimeout(resolve, 4000, "four");
});
var p5 = new Promise(function(resolve, reject) {
  reject("reject");
});

Promise.all([p1, p2, p3, p4, p5]).then(function(value) { 
  console.log(value);
}, function(reason) {
  console.log(reason)
});

//From console:
//"reject"


ref : https://joshua1988.github.io/web-development/javascript/promise-for-beginners
ref  :http://yubylab.tistory.com/entry/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Promise-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0
ref : 
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise/all




반응형

'서버(Server) > Server' 카테고리의 다른 글

통신 모델 OSI 7 계층  (0) 2022.12.22
javascript : Class  (0) 2018.08.19
Promise [2]  (0) 2018.08.16
[javascript] Singleton 싱글톤  (0) 2018.07.02
공개/비공개 스태틱 멤버와 클로저를 활용한 객체 생성  (0) 2018.07.01
반응형


ORM이란?

ORM(Object Relational Mapping)은 application과 Database사이를 맵핑시켜주는 도구이다. 한층더 추상화된 layer에서 Database에 대한 작업을 할 수 있게 해준다. ORM을 사용함으로써 얻는 장단점(pros and cons)은 다음과 같다.

1) Pros of ORM

  • 특정 DBMS에 종속되지 않는다.
  • SQL문이 코드에 들어가지 않아 깔끔한 코드를 유지할 수 있다.
  • ORM이 nesting데이터를 바인딩해준다.

2) Conf of ORM

  • RAW query에 비해 performance가 느리다.
  • Query tuning이 힘들다.
  • 서비스가 복잡해 질수록 ORM으로 할 수 있는 작업의 범위에 한계가 있다.

ORM에 대한 정보는 이곳링크를 통해 더 알아볼 수 있다. What is ORM? / Pros and Cons



Sequelize? Promise!

ORM의 종류는 여러가지가 있지만 Nodejs에서 가장 많이 사용되고 있는 ORM은 Sequelize다. Sequelize는 PostgreSQL, MySQL, MariaDB, SQLite, MSSQL을 지원하고 트랜잭션이나 relation, read replication등도 지원한다. 복잡한 쿼리에 사용될 부분들에 대해서는 오픈된 이슈들이 꽤 있고 지금도 활발하게 pull request나 commit이 이뤄지고 있다. 그리고 가장 큰 특징은 Promise를 기본으로 동작한다는 것이다. Promise는 Promise/A+ 로 불리는 spec에 따라 정의된 비동기작업 제어방식이다. ES6에는 native로 Promise가 포함되었다.

Promise의 장점은 다음과 같다.

  • 복잡한 비동기 코드를 깔끔하고 쉽게 만들 수 있는 방법을 제공한다.
  • Chaining 을 통해 값을 전달하거나 연속된 일련의 작업을 처리할 수 있다.
  • Error handling에 대한 처리를 깔끔하게 할 수 있다.

Promise를 구현한 라이브러리에는 대표적으로 QRSVPbluebird가 있다. Sequelize는 이중에서도 bluebird 라이브러리를 살짝 수정한 버전을 사용하고 있다. Promise를 비동기작업을 제어하는 방식으로 사용하는 만큼 Promise에 대해 알고 있는 부분이 많다면 Sequelize의 이용도 한결 수월해진다. Promise에 대해 더 알아보고 싶다면 다음을 참조하자. Javascript Promisehttps://www.promisejs.orgES6 Promise , Awesome-promise



테이블 생성 예


  • publisher 테이블 - 출판사에 관한 정보
  • books테이블 - 책에 대한 정보
  • user테이블 - 유저에 대한 정보
  • rent_history테이블 - 대여내역에 관한 정보




Setting up a connection

Sequelize의 설치는 npm install sequelize로 간단히 할 수 있다. 또한 Sequelize에서 postgres에 대한 작업을 하기 위해서는 추가로 pg, pg-hstore를 설치해야하므로 두개 모듈도 npm으로 설치하도록 하자. 설치 이후에는 서버가 구동될때 DB와 Connection을 맺도록 설정해야 한다. Connection에 대한 정보는 Sequelize객체를 생성할 때 parameter로 들어간다.

new Sequelize(database, [username=null], [password=null], [options={}])

var sequelize = new Sequelize('postgres://sequelize:1234@localhost/sequelize');
var sequelize = new Sequelize('sequelize', 'sequelize', '1234', {
    host: 'localhost',
    dialect: 'postgres'
});

connection연결은 위와 같이 두가지 방법 모두 가능하다.


Model Define

Sequelize에서 Model은 Database공간의 Table의 Schema를 표현하는 수단이다. Table Schema에 대응되는 Model을 정의한 이후에 실제로 메모리 공간에 올려 캐쉬하려면 import를 해야하는데 import는 바로 다음에 알아보도록하자. Model에 대한 정의는 Sequelize의 define 메소드를 이용한다. 예시는 다음과 같다.

sequelize.define('Publisher', {
    pub_id: {type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true},
    name: {type: DataTypes.STRING(32), allowNull: false},
    established_date: {type: DataTypes.DATE, defaultValue: DataTypes.NOW}
}, {
    classMethods: {},
    tableName: 'publisher',
    freezeTableName: true,
    underscored: true,
    timestamps: false
});

define 메소드의 첫번째 파라미터는 model의 name이다. 두번째 파라미터가 실제로 Table Schema와 맵핑되는 정보이다. 즉 table의 각 column들에 대한 schema를 표현한다. 대표적인 설정 값들 몇개를 알아보자.

  • type : Data type
  • primaryKey : 기본키 인지 아닌지 설정 (default: false)
  • autoIncrement : SERIAL(auto increment)인지 아닌지 (default: false)
  • allowNull : NOT NULL 조건인지 아닌지 (default: true)
  • unique : Unique조건인지 아닌지에 대한 옵션. column하나로만 이루어진 unique라면 true/false로 지정한다. 복수개의 column이라면 동일한 문자열을 각 column의 unique속성에 넣어준다.
  • comment : column에 대한 comment
  • validate : 각 column에 대한 validation check옵션을 넣어준다.

세번째 파라미터는 config 옵션이 들어간다. 대표적인 옵션은 이와같다.

  • timestamps : Sequelize는 테이블을 생성한 후 자동적으로 createdAt, updatedAt column을 생성한다. Database에 해당 테이블이 언제 생성되었고 가장 최근에 수정된 시간이 언제인지 추적할 수 있도록 해준다. 기능을 끄려면 false로 설정한다.
  • paranoid : paranoid가 true이면 deletedAt column이 table에 추가된다. 해당 row를 삭제시 실제로 데이터가 삭제되지 않고 deletedAt에 삭제된 날짜가 추가되며 deletedAt에 날짜가 표기된 row는 find작업시 제외된다. 즉 데이터는 삭제되지 않지만 삭제된 효과를 준다. timestamps 옵션이 true여야만 사용할 수 있다.
  • underscored : true이면 column이름을 camalCase가 아닌 underscore방식으로 사용한다.
  • freezeTableName : Sequelize는 define method의 첫번째 파라미터 값으로 tablename을 자동변환하는데 true이면 이작업을 하지 않도록 한다.
  • tableName : 실제 Table name
  • comment : table 에 대한 comment




Data Type

Sequelize에서 지원하는 DataType은 다음과 같다. (sequelize 공식 사이트에서 발췌)

Sequelize.STRING                      // VARCHAR(255)
Sequelize.STRING(1234)                // VARCHAR(1234)
Sequelize.STRING.BINARY               // VARCHAR BINARY
Sequelize.TEXT                        // TEXT

Sequelize.INTEGER                     // INTEGER
Sequelize.BIGINT                      // BIGINT
Sequelize.BIGINT(11)                  // BIGINT(11)

Sequelize.FLOAT                       // FLOAT
Sequelize.FLOAT(11)                   // FLOAT(11)
Sequelize.FLOAT(11, 12)               // FLOAT(11,12)

Sequelize.REAL                        // REAL        PostgreSQL only.
Sequelize.REAL(11)                    // REAL(11)    PostgreSQL only.
Sequelize.REAL(11, 12)                // REAL(11,12) PostgreSQL only.

Sequelize.DOUBLE                      // DOUBLE
Sequelize.DOUBLE(11)                  // DOUBLE(11)
Sequelize.DOUBLE(11, 12)              // DOUBLE(11,12)

Sequelize.DECIMAL                     // DECIMAL
Sequelize.DECIMAL(10, 2)              // DECIMAL(10,2)

Sequelize.DATE                        // DATETIME for mysql / sqlite, TIMESTAMP WITH TIME ZONE for postgres
Sequelize.BOOLEAN                     // TINYINT(1)

Sequelize.ENUM('value 1', 'value 2')  // An ENUM with allowed values 'value 1' and 'value 2'
Sequelize.ARRAY(Sequelize.TEXT)       // Defines an array. PostgreSQL only.

Sequelize.JSON                        // JSON column. PostgreSQL only.
Sequelize.JSONB                       // JSONB column. PostgreSQL only.

Sequelize.BLOB                        // BLOB (bytea for PostgreSQL)
Sequelize.BLOB('tiny')                // TINYBLOB (bytea for PostgreSQL. Other options are medium and long)

Sequelize.UUID                        // UUID datatype for PostgreSQL and SQLite, CHAR(36) BINARY for MySQL (use defaultValue: Sequelize.UUIDV1 or Sequelize.UUIDV4 to make sequelize generate the ids automatically)

Sequelize.RANGE(Sequelize.INTEGER)    // Defines int4range range. PostgreSQL only.
Sequelize.RANGE(Sequelize.BIGINT)     // Defined int8range range. PostgreSQL only.
Sequelize.RANGE(Sequelize.DATE)       // Defines tstzrange range. PostgreSQL only.
Sequelize.RANGE(Sequelize.DATEONLY)   // Defines daterange range. PostgreSQL only.
Sequelize.RANGE(Sequelize.DECIMAL)    // Defines numrange range. PostgreSQL only.

Sequelize.ARRAY(Sequelize.RANGE(Sequelize.DATE)) // Defines array of tstzrange ranges. PostgreSQL only.

INTEGER, BIGINT, FLOAT, DOUBLE type에는 unsigned와 zerofill에 대한 옵션도 지정할 수 있다

Sequelize.INTEGER.UNSIGNED              // INTEGER UNSIGNED
Sequelize.INTEGER(11).UNSIGNED          // INTEGER(11) UNSIGNED
Sequelize.INTEGER(11).ZEROFILL          // INTEGER(11) ZEROFILL
Sequelize.INTEGER(11).ZEROFILL.UNSIGNED // INTEGER(11) UNSIGNED ZEROFILL
Sequelize.INTEGER(11).UNSIGNED.ZEROFILL // INTEGER(11) UNSIGNED ZEROFILL

zerofill은 남는 자릿수를 0으로 채우는 방식이다. 예를 들면 INTEGER(4).ZEROFILL type인 column에 값이 ‘4’가 들어갈경우 0004로 표기되는 방식이다. model정의와 Datatype에 대한 정보는 이곳에서 볼 수 있다. sequelize model definitionsequelize data types

지금까지 Sequelize를 사용하기에 앞서 ORM, Promise에 대한 원론적인 이야기와 함께 Sequelize 설정과 간단한 옵션에 대한 부분을 알아보았다. 다음 챕터에는 실제로 Sequelize가 application에서 어떻게 쿼리를 수행하고 리턴하는지에 대한 부분을 간단한 예제와 함께 살펴보자.



Sync

Sequelize에서는 INSERT, SELECT, DELETE, UPDATE와 같은 DML뿐만 아니라 DDL문도 지원하는데 model에 정의된 스펙을 기준으로 Database의 테이블들을 동기화할 수 있다. 그때 사용하는 메소드가 sync메소드이다. 첫 번째 챕터 model define에 관한 예제였던 publisher model내역이다.

sequelize.define('Publisher', {
    pub_id: {type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true},
    name: {type: DataTypes.STRING(32), allowNull: false},
    established_date: {type: DataTypes.DATE, defaultValue: DataTypes.NOW}
}, {
    classMethods: {},
    tableName: 'publisher',
    freezeTableName: true,
    underscored: true,
    timestamps: false
});

이렇게 정의된 model에 대해서 sync를 하면 어떻게 될까? postgres에서 Database공간의 table의 리스트를 보는 명령어는 \dt 이다. sync를 하기전 database의 상황을 살펴보자

sequlize=> \dt
No relations found.

현재 Database공간에는 아무런 테이블도 생성되지 않았다. 그렇다면 다음과 같이 sync 메소드를 호출해보자.

db.Publisher.sync();

콘솔창에서는 로그를 확인할 수있다.


CREATE TABLE IF NOT EXISTS “publisher” 가 맨 윗줄에 보이는가? Publisher model에 대한 sync메소드를 호출시 하는 작업은 publisher테이블을 생성하는 일이다. NOT EXISTS조건이 있기 때문에 sync를 매번 하더라도 table이 없을때만 테이블 생성이 될 것이다. 다시 한번 테이블 리스트를 확인해보자.

 콘솔창 로그2

publisher 테이블이 생성된 것을 확인할 수 있다. 세부적인 테이블정보를 보면 publisher model을 정의했던 내용대로 pub_id는 primary key로 지정되었고 auto increment sequence를 갖게 되었다. 나머지 column들도 정의된대로 테이블이 생성된 것을 확인 할 수 있다. sync와 반대되는 개념의 메소드는 drop이다. drop은 테이블을 drop한다. drop메소드를 실행하면

db.Publisher.drop();

 콘솔창 로그3

drop명령어가 실행되고 publisher테이블이 삭제된 것을 확인할 수 있다. sync에는 몇가지 옵션을 parameter로 넘길 수 있는데 대표적으로 force옵션이 있다.

db.Publisher.sync({force: true});

force를 true로 놓고 sync를 실행하면 publisher테이블은 먼저 drop된 이후에 새로 생성이 된다. force옵션의 경우에는 민감한 작업을 실행하는 만큼 주의를 요하도록 한다. 또한 소스코드내에서 table에 대한 DDL까지 실행하는 것은 위험부담이 있기 때문에 되도록이면 table에 대한 관리는 flyway와 같은 관리모듈을 사용하거나 수동으로 진행하고 sync, drop 그리고 특히 force true 옵션 사용은 자제하도록 한다.

Querying(SELECT)

이제 Sequelize를 통해 SELECT를 사용해보도록 하자.출판사 리스트를 가져오는 쿼리는 다음과 같다.

models.Publisher.findAll().then(function(results) {
    res.json(results);
}).catch(function(err) {
    //TODO: error handling
});

publisher테이블에 있는 내용을 모두 가져오는 쿼리이다. Sequelize의 모든 쿼리 결과는 promise로 리턴 되기 때문에 findAll메소드 역시 promise를 리턴한다. 실제 쿼리 결과는 then의 callback에서 가져올 수 있다. catch문에서는 적절한 error handling을 해주면 된다. 이렇게 API를 호출해서 받아온 출판사리스트를 확인할 수 있다. Sequelize를 통해 가져온 값이다.

 스크린샷

SELECT와 관련있는 model 메소드는 findAll말고도 몇개가 더 있다. 아래 테이블을 참고하자.


이중에서 가장 많이 사용되는것은 findAll, find 이다. 공통적으로 많이 사용되는 옵션에 대해서 알아보자

  findAll([options]) -> Promise.<Array.<Instance>>

1) where : where옵션은 SQL문에서 where에 해당하는 부분을 기술하는 옵션이다.

  • type : object

  • example : SELECT * FROM publisher WHERE pub_id=1 을 sequelize로 표현하면

models.Publisher.findAll({
        where: {pub_id: 1}
}).then(function(result) {
    //TODO: query결과가 담긴 result에 대한 처리 진행
});
  • example : SELECT * FROM publisher WHERE pub_id > 1 AND pub_id < 4
models.Publisher.findAll({
        where: {
            $and: [{pub_id: {$gt: 1}}, {pub_id: {$lt: 4}}]
        }
}).then(function(results) {
        //TODO: query결과가 담긴 result에 대한 처리 진행
});

더 많은 예제는 이곳에서 찾을 수 있다. Sequelize model usage

2) attributes: table의 특정 column만 select할 때 사용

  • type: array | object

  • example : SELECT pub_id, name FROM publisher 를 sequelize로 표현하면

models.Publisher.findAll({
        attributes: ['pub_id', 'name']
}).then(function(results) {});
  • example : SELECT pub_id, name FROM publisher는 다음과 같은 방식으로도 표현
models.Publisher.findAll({
        attributes: {exclude: ['established_date']}
}).then(function(results) {});
// exclude는 table의 column중에서 제외시킬 리스트를 넣고 include에는 포함시킬 리스트를 넣는다.

3) order: SQL문의 order by에 해당하는 부분을 기술하는 옵션이다.

  • type: string | array

  • example : SELECT * FROM publisher ORDER BY pub_id DESC 는 다음과 같다.

models.Publisher.findAll({
        order: 'pub_id DESC'
}).then(function(results){});
// order: 'pub_id DESC’ 는 order: [['pub_id', 'DESC']] 로도 사용할 수 있다.

4) limit, offset: SQL문의 limit, offset에 해당하는 부분을 기술하는 옵션

  • type : Number

  • example : SELECT * FROM publisher LIMIT 1 OFFSET 1은 다음과 같다.

models.Publisher.findAll({
    offset: 1,
    limit: 1
}).then(function(result) {});

5) include: Eager loading, Relation과 관련된 옵션으로 다음 챕터에서 알아보도록 하자.

6) transaction: 어떤 하나의 트랜잭션 안에서 동작하도록 하는 옵션. 다다음 챕터에서 알아보도록하자.

findAll, find에 대해 더 알아보고 싶다면 이 곳을 참고하자 Sequelize-model-findAll

Querying(INSERT)

예제 Publisher 메뉴에 가면 이런 화면을 볼 수 있을 것이다.

 인서트스샷1

우측에는 미리 넣어놓은 5개의 출판사 정보가 있고 ‘Webframeworks.kr’ 의 이름을 가진 새 출판사를 등록해보자. Register버튼을 누르면 등록 API를 call하고 서버에서는 이와 같은 쿼리를 실행한다.

models.Publisher.create({name: options.name}).then(function(result) {
    res.json(result);
}).catch(function(err) {
    //TODO: error handling
});

options.name에는 화면에서 적었던 Webframeworks.kr 문자열이 들어있다. Sequelize model의 create메소드는 SQL의 INSERT INTO와 같은 역할을 한다. 메소드의 형태는 다음과 같다.

create(values, [options]) -> Promise.<Instance>

values에는 실제로 insert할 값들에 대한 object가 들어간다. options에는 부가적인 정보가 들어가는데 transaction에 대한 정보가 대표적이다. create메소드 역시 promise를 리턴하며 성공적으로 쿼리가 실행되었을 경우에는 insert된 row정보를 얻을 수 있다. API호출이 성공되면 화면에서 새로 추가된 출판사 정보를 확인할 수 있다.

 인서트스샷3

row여러개를 한꺼번에 insert하려면 bulkCreate 메소드를 이용하자 create와 사용방법은 똑같다. 차이점은 첫번째 파라미터에 value object들에 대한 array가 들어가야 한다는 것이다.

bulkCreate(records, [options]) -> Promise.<Array.<Instance>>

create메소드에 대해서 더 알아보고 싶다면 이곳을 참조하자 Sequelize-model-create bulkCreate메소드에 대해서 더 알아보고 싶다면 이곳을 참조하자 Sequelize-model-bulkCreate

Querying(UPDATE)

등록한 출판사의 이름이 잘못등록되어 수정이 필요하다면? Webframeworks.kr의 출판사 이름을 Webframeworks로 바꿔보자. 아래 예제 그림처럼 변경할 출판사 이름을 적고 Update버튼을 누르면 변경 API를 call하고 서버에서는 다음과 같은 쿼리를 실행한다.

 업데이트스샷1

models.Publisher.update({name: newName},
 {where: {pub_id: pub_id}, returning: true}).then(function(result) {
      res.json(result[1][0]);
 }).catch(function(err) {
      //TODO: error handling
 });

SQL문으로는 UPDATE publisher SET name='Webframeworks' WHERE pub_id=6 RETURNING * 과 같다. update메소드는 조건에 맞는 복수개의 row에 대해서 update를 실행한다. 메소드의 형태는 이렇다.

update(value, options) -> Promise.<Array.<affectedCount, affectedRows>>

values에는 update해야할 value들의 object가 들어간다. options에 사용되는 것중 대표적으로는 transaction관련 옵션이 있고 postgres에서만 사용할 수 있는 returning옵션이 있다 returning 옵션이 true이면 update결과 후 row의 정보가 리턴된다. 결과로는 array타입이 리턴되는데 첫번째 인덱스에서는 update된 row의 갯수를 두번째 인덱스에서는 update된 row들의 정보를 얻을 수 있다. API호출이 성공되면 다음과 같이 값이 정상적으로 update된것을 확인할 수 있다.

 업데이트스샷2

update메소드에 대해 더 알아보고 싶다면 이곳을 참조하자 Sequelize-model-update

Querying(DELETE)

위에 예제 스크린샷들을 보면 맨 오른쪽에 있는 휴지통 빨간버튼이 보일 것이다. 이 버튼은 해당 출판사의 정보를 삭제하는 버튼이다. Webframeworks로 등록된 출판사를 삭제해보자. 버튼을 누르면 삭제 API를 call하고 서버에서는 다음과 같은 쿼리가 실행된다.

models.Publisher.destroy({where: {pub_id: pub_id}}).then(function(result) {
    res.json({});
}).catch(function(err) {
    //TODO: error handling
});

SQL문으로는 DELETE FROM publisher where pub_id = 6; 과 같다 destroy메소드는 SQL에서 DELETE와 같은 역할을 하며 조건에 맞는 복수개의 row를 삭제한다. 메소드의 형태는 이렇다.

destory(options) -> promise<integer>

options에 필수적으로 빼놓지 말고 넣어야 하는 사항은 where조건이다. where조건을 넣지 않고 destroy메소드를 실행하면 테이블에 있는 모든 row가 삭제되므로 꼭꼭 조심하도록 하자. 그외에는 transaction 옵션등이 들어간다. delete메소드에 대해 더 알아보고 싶다면 이곳을 참조하자. Sequelize-model-destroy

이번챕터에는 정의된 model에 대한 sync작업과 간단하게 CRUD작업을 할 수 있는 예제를 통해 Sequelize 사용법을 알아보았다. find, findAll의 경우에는 option을 어떻게 주느냐에 따라 복잡한 쿼리에 대응하는 작업도 할 수 있는 만큼 많이 사용해보길 바란다. 다음 챕터에서는 Sequelize로 테이블의 relation을 만들어주고 join을 포함한 작업 및 raw query의 사용법등을 알아보도록 한다.




Associations

예제의 다음 기능은 출판사 별로 등록된 책을 보여주고 새로운 서적을 books테이블에 등록하는 일이다. publisher테이블에 이어 books테이블도 만들어 보자. SQL문으로는 다음과 같다.

CREATE TABLE IF NOT EXISTS books (
    book_id SERIAL PRIMARY KEY,
    pub_id INTEGER REFERENCES publisher NOT NULL,
    title VARCHAR(64) NOT NULL,
    author VARCHAR(16) NOT NULL,
    stock SMALLINT NOT NULL DEFAULT 1,
    register_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);

books테이블의 pubid는 책의 출판사ID이고 publisher테이블의 pubid를 foreign key로 갖는다. Sequelize의 model define으로 표현하면 이렇게 될 것이다.

sequelize.define('Books', {
    book_id: {type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true},
    pub_id: {type: DataTypes.INTEGER, allowNull: false, references: {model: models.Publisher, key: 'pub_id'}},
    title: {type: DataTypes.STRING(64), allowNull: false},
    author: {type: DataTypes.STRING(16), allowNull: false},
    stock: {type: DataTypes.INTEGER, defaultValue: 1},
    register_date: {type: DataTypes.DATE, defaultValue: DataTypes.NOW}
}, {
    classMethods: {},
    tableName: 'books',
    freezeTableName: true,
    underscored: true,
    timestamps: false
});

여기서 publisher 테이블과 다른 점은 books테이블의 column하나가 reference라는 것이다. 출판사는 여러개의 책을 출판하므로 1:N의 관계가 성립된다. Sequelize에서는 model간의 관계를 정의하는 4가지의 association 메소드가 있다. (hasMany, hasOne, belongsTo, belongsToMany) 이 중에서 이번 예제에서 필요한 hasMany 메소드부터 하나씩 살펴보도록하자. association 설정은 다음과 같이 한다.

db.Publisher.hasMany(db.Books, {foreignKey: 'pub_id'});

Publisher model과 Books model이 1:N으로 관계되어있다는 뜻이다. hasMany는 1:N관계를 맺을 때 사용하는 메소드이다. association관련 메소드의 option항목은 대표적으로 foreignKey, as가 있다. foreignKey에는 foreignKey로 사용되는 column의 name이 들어간다





ref : http://webframeworks.kr/tutorials/expressjs/expressjs_orm_one/

ref : http://webframeworks.kr/tutorials/expressjs/expressjs_orm_two/#tocAnchor-1-1

ref : http://webframeworks.kr/tutorials/expressjs/expressjs_orm_three


반응형
반응형

JWT 의 생김새

JWT 는 . 을 구분자로 3가지의 문자열로 되어있습니다. 구조는 다음과 같이 이루어져있습니다:

자, 그럼 이렇게 3가지 부분으로 나뉘어져 있습니다



우선 암호화한다음 복호화된 결과를 보고 넘어가겠습니다


왼쪽은 암호화된 값, 오른쪽은 복호화된 값입니다






Header, payload 부분은 아래 소스코드 처럼 암호화하면 되고 마지막 signature 부분은


(encodedHeader + '.' + encodedPayload) 이 내용을 다시 암호화 하면 되고


최종 jwt 는 이 3개를 '.' 으로 연결하여 최종 jwt 를 만들 수 있습니다

하단 소스코드에서 확인 할 수 있고 복호화 과정은  https://jwt.io/


사이트에서 암호화된 키를 넣으면 복호화 되는 것을 볼 수 있는데 


Invalid Signature 이렇게 드면 제대로 복호화가 안됐다는 것이고

 Signature Verified 이렇게 드면 제대로 복호화가 정상으로 됐다는 뜻입니다


// encode to base64
const encodedHeader = new Buffer(JSON.stringify({암호화할 내용들}))
    .toString('base64')
    .replace('=', '');






헤더 (Header)

Header 는 두가지의 정보를 지니고 있습니다.

typ: 토큰의 타입을 지정합니다. 바로 JWT 이죠.

alg: 해싱 알고리즘을 지정합니다.  해싱 알고리즘으로는 보통 HMAC SHA256 혹은 RSA 가 사용되며, 이 알고리즘은, 토큰을 검증 할 때 사용되는 signature 부분에서 사용됩니다.


(예제에서는 HMAC SHA256 이 해싱 알고리즘으로 사용됩니다)

이 정보를 base64 로 인코딩을 하면 아래 소스처러 됩니다

정보 (payload)

Payload 부분에는 토큰에 담을 정보가 들어있습니다. 여기에 담는 정보의 한 ‘조각’ 을 클레임(claim) 이라고 부르고, 이는 name / value 의 한 쌍으로 이뤄져있습니다. 토큰에는 여러개의 클레임 들을 넣을 수 있습니다.

클레임 의 종류는 다음과 같이 크게 세 분류로 나뉘어져있습니다:

등록된 (registered) 클레임,
공개 (public) 클레임,
비공개 (private) 클레임

그럼, 하나 하나 알아볼까요?

#1 등록된 (registered) 클레임

등록된 클레임들은 서비스에서 필요한 정보들이 아닌, 토큰에 대한 정보들을 담기위하여 이름이 이미 정해진 클레임들입니다. 등록된 클레임의 사용은 모두 선택적 (optional)이며, 이에 포함된 클레임 이름들은 다음과 같습니다:

  • iss: 토큰 발급자 (issuer)
  • sub: 토큰 제목 (subject)
  • aud: 토큰 대상자 (audience)
  • exp: 토큰의 만료시간 (expiraton), 시간은 NumericDate 형식으로 되어있어야 하며 (예: 1480849147370) 언제나 현재 시간보다 이후로 설정되어있어야합니다.
  • nbf: Not Before 를 의미하며, 토큰의 활성 날짜와 비슷한 개념입니다. 여기에도 NumericDate 형식으로 날짜를 지정하며, 이 날짜가 지나기 전까지는 토큰이 처리되지 않습니다.
  • iat: 토큰이 발급된 시간 (issued at), 이 값을 사용하여 토큰의 age 가 얼마나 되었는지 판단 할 수 있습니다.
  • jti: JWT의 고유 식별자로서, 주로 중복적인 처리를 방지하기 위하여 사용됩니다. 일회용 토큰에 사용하면 유용합니다.

#2 공개 (public) 클레임

공개 클레임들은 충돌이 방지된 (collision-resistant) 이름을 가지고 있어야 합니다. 충돌을 방지하기 위해서는, 클레임 이름을 URI 형식으로 짓습니다.

{
    "https://velopert.com/jwt_claims/is_admin": true
}

#3 비공개 (private) 클레임

등록된 클레임도아니고, 공개된 클레임들도 아닙니다. 양 측간에 (보통 클라이언트 <->서버) 협의하에 사용되는 클레임 이름들입니다. 공개 클레임과는 달리 이름이 중복되어 충돌이 될 수 있으니 사용할때에 유의해야합니다.

{
    "username": "velopert"
}







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
 
/*
 주의: base64로 인코딩을 할 때 dA== 처럼 뒤에 = 문자가 한두개 붙을 때가 있습니다. 
 이 문자는 base64 인코딩의 padding 문자라고 부릅니다.
JWT 토큰은 가끔 URL 의 파라미터로 전달 될 때도 있는데요, 이 = 문자는, url-safe 하지 
않으므로, 제거되어야 합니다. 패딩이 한개 생길 때도 있고, 두개 생길 때도 있는데, 
전부 지워(제거해줘도 디코딩 할 때 전혀 문제가 되지 않습니다)
 */
 
const header = { //헤더 정보
    "typ": "JWT",
    "alg": "HS256"
};
 
 
 
// encode to base64
const encodedHeader = new Buffer(JSON.stringify(header))
    .toString('base64')
    .replace('=', '');
 
console.log("");
console.log("encodedHeader:\n" + encodedHeader);
 
 
 
const payload = {
    "iss": "velopert.com",
    "exp": "1485270000000",
    "https://velopert.com/jwt_claims/is_admin": true,
    "userId": "11028373727102",
    "username": "velopert"
};
 
// encode to base64
const encodedPayload = new Buffer(JSON.stringify(payload))
    .toString('base64')
    .replace('=', '');
 
console.log("");
console.log('encodedPayload:\n' + encodedPayload);
 
 
//JSON Web Token 의 마지막 부분은 바로 서명(signature) 입니다. 
//이 서명은 헤더의 인코딩값과, 정보의 인코딩값을 합친후 
//주어진 비밀키로 해쉬를 하여 생성합니다.
const crypto = require('crypto');
const signature = crypto.createHmac('sha256', 'secret')
    .update(encodedHeader + '.' + encodedPayload)       //'.' 으로 합쳐줘야함
    .digest('base64')
    .replace('=', '');
 
console.log("");
console.log('signature:\n' + signature);
 
const token = encodedHeader + '.' + encodedPayload + '.' + signature;
 
 
console.log("");
console.log('jwt token:\n' + token);

cs



ref : https://velopert.com/2389


반응형
반응형


Promise



기본적으로 Promise 객체 생성후 Promise 를 통해서 다음 실행 구문을 연달아 수행하는 형태

그러나 처리 되는 상황에 따라 then(then 안의 함수들) or catch(안의 함수) 를 타고 가는 구문에 차이가 생기게 된다

var p = new Promise(function (resolve, reject) {     //이걸 호출하지 않으면 then 으로 넘어가지 않는다     //resolve(2);       //성공 함수로 넘어간다     //이걸 호출하지 않으면 then 으로 넘어가지 않는다     reject(2);        //실패 함수로 넘어가게 된다 }); //then() 은 Primise 를 리턴한다 p.then(function (value) {       //성공함수     console.log(value);     return value + 1; }, function(reject) {           //실패함수     console.log(reject);     return 10; } ).then(function (value) {        //다음 함수들     console.log(value);     return value + 1; }).then(function (value) {        //다음 함수들

    console.log(value);     throw 100; }).catch(function (ff) {            //예외 함수

    console.log(ff); });


결과
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 
resolve(2); 일때의 결과
 
2
3
4
100
 
 
 
reject(2); 일때의 결과
 
2
10
11
100
 
cs


Promise 생성하기

Promise 오브젝트는 new 키워드와 생성자를 사용하여 만듭니다. 이 생성자는 인수로 "executor 함수"라는 함수를 사용합니다. 이 함수는 매개 변수로 두 가지 함수를 가져야 합니다. 비동기 작업이 성공적으로 완료되고 결과를 값으로 반환하면 첫 번째 함수(resolve)가 호출됩니다. 두 번째 (reject)는 작업이 실패 할 때 호출되며 보통 오류 객체를 반환합니다.

const myFirstPromise = new Promise((resolve, reject) => {
  // do something asynchronous which eventually calls either:
  //
  //   resolve(someValue); // fulfilled
  // or
  //   reject("failure reason"); // rejected
});

함수가 프로미스를 사용하도록 하려면 단순히 프로미스를 반환하면됩니다.

function myAsyncFunction(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open("GET", url);
    xhr.onload = () => resolve(xhr.responseText);
    xhr.onerror = () => reject(xhr.statusText);
    xhr.send();
  });
}


예제

Link to section기본 예제

let myFirstPromise = new Promise((resolve, reject) => 
{
  // We call resolve(...) when what we were doing asynchronously was successful, and reject(...) when it failed.
  // In this example, we use setTimeout(...) to simulate async code. 
  // In reality, you will probably be using something like XHR or an HTML5 API.
  setTimeout(function(){
    resolve("Success!"); // Yay! Everything went well!
  }, 3000);
});

myFirstPromise.then((successMessage) => {
  // successMessage is whatever we passed in the resolve(...) function above.
  // It doesn't have to be a string, but if it is only a succeed message, it probably will be.
  console.log("Yay! " + successMessage);
});



결과 3초 후  

"Success!"





Promise.prototype.then()



then() 메서드는 Promise를 리턴하고 두개의 콜백 함수를 인수로 받습니다. 하나는 Promise가 성공(success)했을 때를 위한 콜백 함수이고, 다른 하나는 실패(failure)했을 때를 위한 콜백 함수입니다.



구문

p.then(onFulfilled, onRejected);

p.then(function(value) {
  // 이행(fulfillment)
  }, function(reason) {
  // 거부
});

Link to section매개변수

onFulfilled
Promise가 수행될 때 호출되는 Function 이고, 수행 값(fulfillment value) 하나를 인수로 받습니다.
onRejected
Promise가 거부될 때 호출되는 Function 이고, 거부 이유(rejection reason) 하나를 인수로 받습니다.

Link to section설명

then 과 Promise.prototype.catch() 메서드는 promise 를 리턴하기 때문에, chaining 이 가능합니다. — composition 이라고도 합니다.

Link to section

Link to sectionthen 메서드 사용

var p1 = new Promise(function(resolve, reject) {
  resolve("Success!");
  // 또는
  // reject ("Error!");
});

p1.then(function(value) {
  console.log(value); // 성공!
}, function(reason) {
  console.log(reason); // 오류!
});

Link to sectionChaining

then 메서드는 Promise를 리턴하기 때문에, 이어지는 then 호출들을 손쉽게 chaining 할 수 있습니다. onFulfilled 또는 onRejected 콜백 함수가 리턴하는 값은 자동으로 resolved promise로 wrapping 되기 때문에 다음에 오는 then 이나 catch 메서드로 전달 됩니다.

var p2 = new Promise(function(resolve, reject) {
  resolve(1);
});

p2.then(function(value) {
  console.log(value); // 1
  return value + 1;
}).then(function(value) {
  console.log(value); // 2
});

p2.then(function(value) {
  console.log(value); // 1
});








다음과 같이 catch()를 사용할 수도 있습니다.

get('story.json').then(function(response) {
  console
.log("Success!", response);
}).catch(function(error) {
  console
.log("Failed!", error);
})

catch()에 대한 특별한 것은 없습니다. then(undefined, func)의 보완에 불과하지만 가독성은 훨씬 높습니다. 상기 두 코드 예시는 동일하게 동작하지 않습니다. 후자는 다음과 같습니다.

get('story.json').then(function(response) {
  console
.log("Success!", response);
}).then(undefined, function(error) {
  console
.log("Failed!", error);
})

차이는 미묘하지만 매우 유용합니다. 프라미스 거부는 거부 콜백(또는 동일하게 기능하는 catch())을 사용하여 다음 then()으로 건너뜁니다. then(func1, func2)를 사용하는 경우 func1와 func2 중에 하나만 호출되며, 둘이 동시에 호출되지 않습니다. 그러나 then(func1).catch(func2)를 사용하는 경우 둘은 체인에서 개별적인 단계이므로 func1이 거부하면 둘 다 호출됩니다. 다음을 봅시다.

asyncThing1().then(function() {
 
return asyncThing2();
}).then(function() {
 
return asyncThing3();
}).catch(function(err) {
 
return asyncRecovery1();
}).then(function() {
 
return asyncThing4();
}, function(err) {
 
return asyncRecovery2();
}).catch(function(err) {
  console
.log("Don't worry about it");
}).then(function() {
  console
.log("All done!");
})

위의 흐름은 일반 자바스크립트 try/catch와 매우 유사하며, 'try'를 사용하여 발생하는 오류는 즉시 catch() 블록으로 이동합니다. 다음은 이를 흐름도로 만든 것입니다.



프라미스 처리 시에는 청색선을 따르고 프라미스 거부 시에는 적색선을 따르면 됩니다.






new Promise, Promise.resolve()  차이


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//new Promise 패턴을 사용하면 아래와 같이 쓸 수 있습니다.
 
function async1 (param) {
    return new Promise(function(resolve, reject) {
        resolve(param*2);
    });
}
function async2 (param) {
    return new Promise(function(resolve, reject) {
        resolve(param*3);
    });
}
function async3 (param) {
    return new Promise(function(resolve, reject) {
        resolve(param*4);
    });
}
 
var start = 1;
async1(start)
    .then(async2)
    .then(async3)
    .then(result => {
        console.log(result); // 24
    });
 
 
 
 
 
//함수 선언 코드가 좀 길어지긴 했지만... 함수 사용 부분은 좀 더 명확해졌습니다.
//같은 내용을 Promise.resolve() 로 사용하면 아래와 같죠.
 
 
function async1 (param) {
    return Promise.resolve(param*2);
}
function async2 (param) {
    return Promise.resolve(param*3);
}
function async3 (param) {
    return Promise.resolve(param*4);
}
 
var start = 1;
async1(start)
    .then(async2)
    .then(async3)
    .then(result => {
        console.log(result); // 24
    });
 
 
 
//그런데 이런 상황이 존재함
 
//(이 예제는 크롬의 cross origin 제약을 해제하고 실행했습니다.)
 
function request (param) {
    return Promise.resolve()
        .then(function () {
            var xhr = new XMLHttpRequest();
 
            xhr.open('GET''http://google.co.kr/'true);
            xhr.onreadystatechange = function () {
                if (xhr.readyState == 4 && xhr.status == 200) {
                    return Promise.resolve(xhr.response);
                }
            };
            xhr.send(null);
        });
}
 
Promise.resolve()
    .then(request)
    .then(result => {
        console.log('ok');
        console.log(result);
    });
 
/*
위 코드는 http://google.co.kr에 GET 요청을 보낸 뒤 받아서 결과를 텍스트로 출력하는 예제입니다.
 Promise 안에서 readyState와 status를 확인해서 Promise.resolve()를 리턴하므로 얼핏 보면 돌아갈 
 것 같습니다만... 제대로 동작하지 않습니다. 콘솔에 찍히는 result는 undefined 입니다.  */
 
 
결과는 
 
ok
undefined
 
 
 
//request() 함수에서 xhr.send() 를 실행하면 해당 컨텍스트에서의 작업은 끝나고 Promise 컨텍스트도 종료됩니다. 
//xhr이 완료되었을 때 반환하는 객체를 받을 타이밍에는 이미 이전 함수가 끝나있기 때문이죠.
 
//이 코드는 new Promise()를 써서 이렇게 바뀌어야 합니다.
 
 
 
function request (param) {
    return new Promise(function(resolve, reject) {
        var xhr = new XMLHttpRequest();
 
        xhr.open('GET''http://google.co.kr/'true);
        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4 && xhr.status == 200) {
                resolve(xhr.response);
            }
        };
        xhr.send(null);
    })
}
 
Promise.resolve()
    .then(request)
    .then(result => {
        console.log('ok');
        console.log(result);
    });
 
 
 
/*
두번째 라인에서 생성한 Promise 객체의 함수 실행 컨텍스트를 유지하기 위해 Promise.resolve() 대신 new Promise()를 사용하고 
최종 콜백 지점인 여덟번째 라인에서 resolve()를 실행해줍니다. 기존의 callback 사용 방식과 비슷하다고 보면 간단합니다.
이렇게 사용하면 Promise 실행 컨텍스트를 벗어나지 않고 원하는 결과를 얻을 수 있습니다.*/
 
 
 
 
 
 

cs



비슷하게 ajax 콜을 요청하거나 DB에서 데이터를 가져올 때와 같이 async 함수를 감싸는 경우는 new Promise()를
사용하면 됩니다.

처음 Promise를 사용할 때는 왜 같은 흐름의 함수가 두 개로 제공될까 이상했는데, 사용하다보니
return Promise.resolve()와 new Promise()는 
사용 방법이 약간 다릅니다. return Promise.resolve()는
sync 로직 흐름에서, new Promise()는 sync는 물론, async 로직 흐름에서도 사용할 수 있습니다.

요새 모듈은 사용할 때 Promise를 지원하는 경우가 종종 있습니다. 좀 더 많은 모듈이 Promise를 지원하기를 바래봅니다


ref : https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise
ref : https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
ref : https://developers.google.com/web/fundamentals/primers/promises
ref : http://han41858.tistory.com/11


반응형
반응형

MYSQL 외래키(Foreign key) 지정(RESTRICT, CASCADE, NO ACTION, SET NULL)



MYSQL 외래키(Foreign key) 지정 시 

on delete rule(삭제 시), on update rule(변경 시) 옵션 지정


RESTRICT, CASCADE, NO ACTION, SET NULL


1. RESTRICT : 개체를 변경/삭제할 때 다른 개체가 변경/삭제할 개체를 참조하고 있을 경우 변경/삭제가 취소됩니다.(제한)


2. CASCADE : 개체를 변경/삭제할 때 다른 개체가 변경/삭제할 개체를 참조하고 있을 경우 함께 변경/삭제됩니다.


3. NO ACTION : 개체를 변경/삭제할 때 다른 개체가 변경/삭제할 개체를 참조하고 있을 경우 변경/삭제할 개체만 변경/삭제되고 참조하고 있는 개체는 변동이 없습니다.


4. SET NULL : 개체를 변경/삭제할 때 다른 개체가 변경/삭제할 개체를 참조하고 있을 경우 참조하고 있는 값은 NULL로 세팅됩니다.



ref : http://h5bak.tistory.com/125

반응형
반응형


스크린샷 설명: 아무것도 설치되지 않은 깨끗한 Windows Server 2012 R2 액티브 디렉터리 도메인 컨트롤러 설치 ~ 클라이언트 1대(Windows 8.1) 로그인까지. 액티브 디렉터리 구축의 기초 .

관련 포스트: [노트] Windows Server Active Directory 기초(꼬알라 공부방)

1. Active Directory Domain Services 설치

ws2012r2_dns_dhcp_ad_setup_02
먼저, 컴퓨터 이름을 변경한다
AD수업01_0
서버 IP와 DNS를 변경한다.

▲ 최초의 도메인 컨트롤러가 될 서버의 IP와 DNS를 다음과 같이 변경.

  • IP 주소: 100.0.0.1
  • 서브넷 마스크: 255.255.255.0
  • 기본 설정 DNS 서버: 100.0.0.1 (자기 자신)

메모:

  • DNS
    • MS에 뭐가 잘 안 된다는 문의의 70% 이상이 DNS 문의라고 함
    • Computer name -> ip로 바꿔줌(lookup) – 일반적으로 많이 사용
    • Ip를 Computer name으로 바꿈(forward)
      • 클라이언트가 DNS 주소를 168.126.63.1 (KT DNS)를 가리키면? DC를 못 찾을 것임
      • AD가 세팅된 DNS를 바라봐야 정상적으로 작동함



다음으로 로컬 서버에 ‘Active Directory 도메인 서비스’를 추가한다.
AD설치_01
관련된 기능들이 한꺼번에 설치된다.

2. 서버를 Domain Controller로 승격

도메인 컨트롤러 기능이 설치되면 알림 깃발을 눌러 '도메인 컨트롤러로 승격'을 누른다
도메인 컨트롤러 기능이 설치되면 알림 깃발을 눌러 ‘도메인 컨트롤러로 승격’을 누른다

▲ 옛날에는 Domain Controller를 내리려면 OS를 삭제해야 했다. 그래서 옛날 명령어가 dcpromo라고…(지금은 사용 불가능한 명령) 이제는 기능을 OS에 올리고 내리는 컨셉이 적용되었다고 한다.

루트 도메인 이름에는 원하는 도메인 명을 넣어도 된다. 기존에 보유한 도메인을 나중에 연결할 수도 있을 것 같다.
루트 도메인 이름에는 원하는 도메인 명을 넣어도 된다. 기존에 보유한 도메인을 나중에 연결할 수도 있을 것 같다.

▲ 메모:

  • 도메인(Domain)?
    • DC가 영향을 미칠 수 있는 범위
    • 중앙에서 인증하는 방식(AD의 주 역할)
AD설치_04
AD(Active Directory)의 기능 범위를 정하고, DSRM(디렉터리 서비스 복원 모드) 암호를 입력한다.

▲ 포리스트 및 복원 암호를 입력한다. 먼저 AD(Active Directory)의 기능 수준을 정하자. Microsoft는 보통 하위 2개 제품까지 호환성을 보장하기 때문에

  • Windows Server 2008
  • Windows Server 2008 R2
  • Windows Server 2012
  • Windows Server 2012 R2

위 4개 중에서 하나를 선택해서 진행 가능했다. 그리고 DSRM(Directory Services Restore Mode)은 AD가 깨졌을 때 쓸 암호로, 옛날 방식이라고 한다.

AD설치_05
DNS 옵션이 나타난다. 알림 메시지가 뜨지만 무시하면 된다.

▲ ‘권한 있는 부모 영역이 없거나 권한 있는 부모 영역에서 Windows DNS 서버가 실행되고 있지 않고 있으므로 이 DNS 서버에 대한 위임을 만들 수 없습니다. 기존 DNS 인프라와 통합하는 중이면 “archmond.com” 도메인 외부에서 이름 확인을 신뢰할 수 있도록 이 DNS 서버에 대한 위임을 수동으로 만들어야 합니다. 그렇지 않으면 아무 작업도 필요하지 않습니다.’ 라는 메시지가 나타난다.

이야기가 너무 복잡하게 되어 있고 한국어인지 의심이 가는 문장인 것 같다. 추후 DNS 서비스를 설치해서 직접 운영해봐야겠다. 그러면 이해가 가겠지… ;;

AD설치_06
‘NetBIOS 도메인 이름’에 처음에 입력했던 도메인에서 .com이나 .net 등의 최상위 도메인(TLD; Top Level Domain) 명을 제외한 이름이 자동으로 입력되어 있다.

▲ 메모:

  • 컴퓨터 이름 길이를 몇 자 이내로 해야 하는 이유?
    • NETBIOS 때문
      • 옛날에는 NetBbeui를 사용했음. 이젠 NetBIOS 이름이 hostname이 됨
AD설치_08
‘검토 옵션’에서는 지금까지 선택했던 옵션들의 확인을 거친다.
AD설치_07
AD DS(Active Directory Domain Services)의 데이터베이스, 로그 파일 및 SYSVOL 위치를 지정한다. 기본 값으로 진행.

▲ 메모:

  • AD는 데이터베이스
    • 검색 가능
    • LDAP – Database 표준 방식
    • 주소록을 쿼리 하는 것
AD설치_09
‘필수 구성 요소 확인’ 문제 없으면 다음으로~
AD설치_10
내가 알 수 없는(?) AD 설치 과정이 지나간다. Virtual Machine에서 실행해서 그런지 조금 시간이 걸렸다.
AD설치_11
Active Directory Domain Services가 설치되었기 때문에 컴퓨터를 다시 시작한다.

휴, 이제 일단락되었다.

AD설치_12
다시 시작하고 나면 ‘NetBIOS 도메인명Administrator’ 이름으로 로그인하게 된다.

3. 클라이언트를 AD에 가입시키기

윈도우 8.1에서 AD에 가입하기

AD설치_15
IP 주소와 DNS를 적절히 입력한다.

▲ AD에 클라이언트를 연결하기 위해 다음과 같이 설정했다.

AD설치_16
시스템 속성에서 ‘컴퓨터 이름/도메인 변경/을 통해 AD에 가입한다.
AD설치_17
도메인 컨트롤러 서버의 아이디와 비밀 번호로 로그인한다.

 

AD설치_18
클라이언트 컴퓨터의 AD 조인 완료!




ref : http://archmond.net/?p=671












그다음 루트디렉토리를 찾고서 인증서를 세팅하기위해 html 파일을 붙여넣어야 합니다




루트디렉토리는 다음과 같은 경로에서 찾을 수 있습니다



finding the root for a windows iis server



I'm going into a friend's storefront to upload a javascript display on their computers. They said they have installed Windows IIS, which is because you need a webserver for javascript to read "local" files.

The problem is that I have never used IIS and will need to locate the http://localhost or root directory. Is it usually in C drive? Is windows IIS usually installed in a particular directory?



Usually:

c:\Inetpub\wwwroot

Which version of Windows are you using? You can verify this location by looking in the IIS manager, but these look different in Windows 2003 vs 2008.




ref : https://serverfault.com/questions/281159/finding-the-root-for-a-windows-iis-server




반응형
반응형

Example

Create a server that listens on port 8080 of your computer.

When port 8080 get accessed, write "Hello World!" back as a response:

var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type''text/plain'});
  res.write('Hello World!');
  res.end();
}).listen(8080);


Definition and Usage

The server.listen() method creates a listener on the specified port or path.


Syntax

server.listen(porthostnamebacklogcallback);

Parameter Values

ParameterDescription
portOptional. Specifies the port we want to listen to
hostnameOptional. Specifies the IP address we want to listen to
backlogOptional. Specifies the max length of the queue of pending connections. Default 511
callbackOptional. Specifies a function to be executed when the listener has been added

Technical Details

Return Value:None
Node.js Version:0.1.90


ref : https://www.w3schools.com/nodejs/met_server_listen.asp

반응형
반응형

미들웨어 사용

Express는 자체적인 최소한의 기능을 갖춘 라우팅 및 미들웨어 웹 프레임워크이며, Express 애플리케이션은 기본적으로 일련의 미들웨어 함수 호출입니다.

미들웨어 함수는 요청 오브젝트(req), 응답 오브젝트 (res), 그리고 애플리케이션의 요청-응답 주기 중 그 다음의 미들웨어 함수 대한 액세스 권한을 갖는 함수입니다. 그 다음의 미들웨어 함수는 일반적으로 next라는 이름의 변수로 표시됩니다.

미들웨어 함수는 다음과 같은 태스크를 수행할 수 있습니다.

  • 모든 코드를 실행.
  • 요청 및 응답 오브젝트에 대한 변경을 실행.
  • 요청-응답 주기를 종료.
  • 스택 내의 그 다음 미들웨어 함수를 호출.

현재의 미들웨어 함수가 요청-응답 주기를 종료하지 않는 경우에는 next()를 호출하여 그 다음 미들웨어 함수에 제어를 전달해야 합니다. 그렇지 않으면 해당 요청은 정지된 채로 방치됩니다.

Express 애플리케이션은 다음과 같은 유형의 미들웨어를 사용할 수 있습니다.

애플리케이션 레벨 및 라우터 레벨 미들웨어는 선택적인 마운트 경로를 통해 로드할 수 있습니다. 일련의 미들웨어 함수를 함께 로드할 수도 있으며, 이를 통해 하나의 마운트 위치에 미들웨어 시스템의 하위 스택을 작성할 수 있습니다.

애플리케이션 레벨 미들웨어

app.use() 및 app.METHOD() 함수를 이용해 애플리케이션 미들웨어를 앱 오브젝트의 인스턴스에 바인드하십시오. 이때 METHOD는 미들웨어 함수가 처리하는 요청(GET, PUT 또는 POST 등)의 소문자로 된 HTTP 메소드입니다.

다음 예에는 마운트 경로가 없는 미들웨어 함수가 표시되어 있습니다. 이 함수는 앱이 요청을 수신할 때마다 실행됩니다.


var app = express();

app.use(function (req, res, next) {
  console.log('Time:', Date.now());
  next();
});

다음 예에는 /user/:id 경로에 마운트되는 미들웨어 함수가 표시되어 있습니다. 이 함수는 /user/:id 경로에 대한 모든 유형의 HTTP 요청에 대해 실행됩니다.


app.use('/user/:id', function (req, res, next) {
  console.log('Request Type:', req.method);
  next();
});

다음 예에는 라우트 및 해당 라우트의 핸들러 함수(미들웨어 시스템)이 표시되어 있습니다. 이 함수는 /user/:id 경로에 대한 GET 요청을 처리합니다.


app.get('/user/:id', function (req, res, next) {
  res.send('USER');
});

아래에는 하나의 마운트 경로를 통해 일련의 미들웨어 함수를 하나의 마운트 위치에 로드하는 예가 표시되어 있습니다. 이 예는 /user/:id 경로에 대한 모든 유형의 HTTP 요청에 대한 요청 정보를 인쇄하는 미들웨어 하위 스택을 나타냅니다.


app.use('/user/:id', function(req, res, next) {
  console.log('Request URL:', req.originalUrl);
  next();
}, function (req, res, next) {
  console.log('Request Type:', req.method);
  next();
});

라우트 핸들러를 이용하면 하나의 경로에 대해 여러 라우트를 정의할 수 있습니다. 아래의 예에서는 /user/:id 경로에 대한 GET 요청에 대해 2개의 라우트를 정의합니다. 두 번째 라우트는 어떠한 문제도 발생키지 않지만, 첫 번째 라우트가 요청-응답 주기를 종료시키므로 두 번째 라우트는 절대로 호출되지 않습니다.

다음 예에는 /user/:id 경로에 대한 GET 요청을 처리하는 미들웨어 하위 스택이 표시되어 있습니다.


app.get('/user/:id', function (req, res, next) {
  console.log('ID:', req.params.id);
  next();
}, function (req, res, next) {
  res.send('User Info');
});

// handler for the /user/:id path, which prints the user ID
app.get('/user/:id', function (req, res, next) {
  res.end(req.params.id);
});

라우터 미들웨어 스택의 나머지 미들웨어 함수들을 건너뛰려면 next('route')를 호출하여 제어를 그 다음 라우트로 전달하십시오. 참고next('route')는 app.METHOD() 또는 router.METHOD() 함수를 이용해 로드된 미들웨어 함수에서만 작동합니다.

다음 예에는 /user/:id 경로에 대한 GET 요청을 처리하는 미들웨어 하위 스택이 표시되어 있습니다.


app.get('/user/:id', function (req, res, next) {
  // if the user ID is 0, skip to the next route
  if (req.params.id == 0) next('route');
  // otherwise pass the control to the next middleware function in this stack
  else next(); //
}, function (req, res, next) {
  // render a regular page
  res.render('regular');
});

// handler for the /user/:id path, which renders a special page
app.get('/user/:id', function (req, res, next) {
  res.render('special');
});

라우터 레벨 미들웨어

라우터 레벨 미들웨어는 express.Router() 인스턴스에 바인드된다는 점을 제외하면 애플리케이션 레벨 미들웨어와 동일한 방식으로 작동합니다.


var router = express.Router();

router.use() 및 router.METHOD() 함수를 사용하여 라우터 레벨 미들웨어를 로드하십시오.

다음 예의 코드는 위에서 애플리케이션 레벨 미들웨어에 대해 표시된 미들웨어 시스템을 라우터 레벨 미들웨어를 사용하여 복제합니다.


var app = express();
var router = express.Router();

// a middleware function with no mount path. This code is executed for every request to the router
router.use(function (req, res, next) {
  console.log('Time:', Date.now());
  next();
});

// a middleware sub-stack shows request info for any type of HTTP request to the /user/:id path
router.use('/user/:id', function(req, res, next) {
  console.log('Request URL:', req.originalUrl);
  next();
}, function (req, res, next) {
  console.log('Request Type:', req.method);
  next();
});

// a middleware sub-stack that handles GET requests to the /user/:id path
router.get('/user/:id', function (req, res, next) {
  // if the user ID is 0, skip to the next router
  if (req.params.id == 0) next('route');
  // otherwise pass control to the next middleware function in this stack
  else next(); //
}, function (req, res, next) {
  // render a regular page
  res.render('regular');
});

// handler for the /user/:id path, which renders a special page
router.get('/user/:id', function (req, res, next) {
  console.log(req.params.id);
  res.render('special');
});

// mount the router on the app
app.use('/', router);

오류 처리 미들웨어

오류 처리 미들웨어에는 항상 4개의 인수가 필요합니다. 어떠한 함수를 오류 처리 미들웨어 함수로 식별하려면 4개의 인수를 제공해야 합니다. next 오브젝트를 사용할 필요는 없지만, 시그니처를 유지하기 위해 해당 오브젝트를 지정해야 합니다. 그렇지 않으면 next 오브젝트는 일반적인 미들웨어로 해석되어 오류 처리에 실패하게 됩니다.

다른 미들웨어 함수와 동일반 방법으로 다음과 같이 오류 처리 미들웨어 함수를 정의할 수 있지만, 오류 처리 함수는 3개가 아닌 4개의 인수, 구체적으로 말하면 (err, req, res, next) 시그니처를 갖는다는 점이 다릅니다.


app.use(function(err, req, res, next) {
  console.error(err.stack);
  res.status(500).send('Something broke!');
});

오류 처리 미들웨어에 대한 상세 정보는 오류 처리를 참조하십시오.

기본 제공 미들웨어

4.x 버전 이후의 Express는 더 이상 Connect에 종속되지 않습니다. express.static의 실행을 제외하면, 이전에 Express와 함께 포함되었던 모든 미들웨어 함수는 이제 별도의 모듈에 포함되어 있습니다. 미들웨어 함수 목록을 확인하십시오.

express.static(root, [options])

Express의 유일한 기본 제공 미들웨어 함수는 express.static입니다. 이 함수는 serve-static을 기반으로 하며, Express 애플리케이션의 정적 자산을 제공하는 역할을 합니다.

root 인수는 정적 자산의 제공을 시작하는 위치가 되는 루트 디렉토리를 지정합니다.

선택사항인 options 오브젝트는 다음과 같은 특성을 가질 수 있습니다.

특성설명유형기본값
dotfilesdotfile을 제공하기 위한 옵션입니다. 사용 가능한 값은 “allow”, “deny” 및 “ignore”입니다.문자열“ignore”
etagetag의 생성을 사용 또는 사용 안함으로 설정합니다.부울true
extensions파일 확장자 폴백을 설정합니다.배열[]
index디렉토리 인덱스 파일을 전송합니다. 디렉토리 인덱스 작성을 사용하지 않으려면 false를 설정하십시오.혼합“index.html”
lastModifiedOS에서 해당 파일이 마지막으로 수정된 날짜를 Last-Modified 헤더에 설정합니다. 사용 가능한 값은 true 또는 false입니다.부울true
maxAge밀리초 또는 ms 형식의 문자열로 Cache-Control 헤더의 max-age 특성을 설정합니다.숫자0
redirect경로 이름이 디렉토리인 경우 후미부의 “/”로 경로를 재지정합니다.부울true
setHeaders파일을 제공하도록 HTTP 헤더를 설정하기 위한 함수입니다.함수 

아래에는 상세한 옵션 오브젝트와 함께 express.static 미들웨어 함수를 사용하는 예가 표시되어 있습니다.


var options = {
  dotfiles: 'ignore',
  etag: false,
  extensions: ['htm', 'html'],
  index: false,
  maxAge: '1d',
  redirect: false,
  setHeaders: function (res, path, stat) {
    res.set('x-timestamp', Date.now());
  }
}

app.use(express.static('public', options));

다음과 같이, 하나의 앱은 2개 이상의 정적 디렉토리를 가질 수 있습니다.


app.use(express.static('public'));
app.use(express.static('uploads'));
app.use(express.static('files'));

serve-static 함수 및 그 옵션에 대한 자세한 정보는 serve-static 문서를 참조하십시오.

써드파티 미들웨어

Express 앱에 기능을 추가하려면 써드파티 미들웨어를 사용하십시오.

필요한 기능을 위한 Node.js 모듈을 설치한 후, 애플리케이션 레벨 또는 라우터 레벨에서 해당 모듈을 앱에 로드하십시오.

다음 예는 쿠키 구문 분석 미들웨어 함수인 cookie-parser의 설치 및 로드를 나타냅니다.


$ npm install cookie-parser


var express = require('express');
var app = express();
var cookieParser = require('cookie-parser');

// load the cookie-parsing middleware
app.use(cookieParser());

Express와 함께 일반적으로 사용되고 있는 써드파티 미들웨어 함수의 일부 목록을 확인하려면 써드파티 미들웨어를 참조하십시오.

ref :  http://expressjs.com/ko/guide/using-middleware.html#middleware.application


반응형
반응형


먼저 fs는 file system의 약자입니다.  api 문서 링크 : http://nodejs.org/api/fs.html

상당히 많은 함수를 포함하고 있는 파일시스테 모듈이지만 모듈의 반절은 권한관련입니다

(node.js 의 개발자가 지독한 유닉스 개발자로 알려져 있습니다. 디버깅모듈이 콘솔로 된것만으로도 알수 있겠지요?)

많이 쓰는 모듈 몇가지 예제입니다.


1. 파일 확인

var fs = require('fs');

fs.exists('test1.txt', function (exists) {
  console.log(exists ? "it's there" : "no exists!");
});

fs.exists() 파일이 존재 하는지 확인합니다. 예제의 경우 test1.txt 파일이 존재 하는지 확인합니다. 



2. 파일 생성

var fs = require('fs');

var file = 'test1.txt';
fs.open(file,'w',function(err,fd){
	if (err) throw err;
 	console.log('file open complete');
});

fs.open() 해당 파일을 엽니다. 옵션 'w' 의 경우 해당 파일이 없다면 생성합니다. 

해당 옵션에 따라 파일의 접근이 달라집니다. 


  파일을 읽기로 열며 해당 파일이 없다면 에러발생

 r+

  읽기/쓰기 상태로 파일을 열며 파일이 없다면 에러 발생 

 w

  쓰기로 파일을 열며 존재 하지 않으면 생성. 파일이 존재하면 내용을 지우고 처음부터 씀.

 w+

  읽기/쓰기로 열며  존재 하지 않으면 생성. 파일이 존재하면 내용을 지우고 처음부터 씀.

 a

 추가 쓰기로 열며 파일이 존재하지 않으면 만듬. 

 a+

 추가 읽기/쓰기로 열며 파일이 존재 하지 않으면 만듬. 



3. 파일 이름 변경

var fs = require('fs');

fs.rename('test1.txt', 'text2.txt', function (err) {
  if (err) throw err;
  console.log('renamed complete');
});

fs.rename() 해당파일의 이름을 변경합니다. 예제는 test1.txt -> test2.txt  로 변경합니다. 

해당 파일이 없거나 권한이 없다면 에러가 발생합니다. 또한 변경하려는 이름의 같은 이름의 파일이 존재하면 에러가 발생합니다.


4. 파일 삭제

var fs = require('fs');
fs.unlink('text2.txt', function (err) {
  if (err) throw err;
  console.log('successfully deleted text2.txt');
});

fs.unlink() 파일을 삭제 합니다. 예제의 경우 test2.txt 파일을 삭제 합니다. 

해당 파이 없거나 권한이 없다면 에러가 발생합니다. 



5.파일 읽기

var fs = require('fs');

fs.readFile('test1.txt', 'utf8', function(err, data) {
  console.log(data);
});

fs.readFile() 해당 파일을 읽습니다. 예제의 경우 test1.txt 파일을 읽고 콘솔로 출력합니다.



6. 파일 쓰기

var fs = require('fs');

var data = 'file system example!!';
fs.writeFile('text1.txt', data, 'utf8', function(error){
	console.log('write end')
});

fs.writeFile() 해당파일에 내용을 씁니다. 예제의 경우 test1.txt파일에 data의 내용을 씁니다. 

만일 파일이 존재 하지 않으면 파일을 생성후 내용을 씁니다. 파일의 내용을 이어서 쓰진 않습니다.


7.파일 이어서 쓰기

var fs = require('fs');

fs.appendFile('test1.txt', 'data to append', function (err) {
  if (err) throw err;
  console.log('The "data to append" was appended to file!');
});

fs.appendFile() 해당 파일에 내용을 이어서 씁니다. 예제의 경우 test1.txt 에 'data to append' 를 이어서 씁니다. 파일이 없을경우 새로 생성하여 씁니다. 



대부분의 함수가 파일이 없을 경우 에러를 발생시키므로 먼저 파일을 있는지 확인하거나 파일을 open()를 통하여 쓰기 모드로 연상태에서 해당 함수들을 실행 시킵니다. 다음은 파일 읽기를 할시 파일이 업다면 생성하고 끝내는 예제 입니다. 

var fs = require('fs');
var path = 'test1.txt';
fs.open(path,'a+',function(err,fd){
	if(err) throw err;
	if(fd == '9'){
		console.log('file create.');
	}else{
		fs.readFile(path, 'utf8', function(err, data) {
		  console.log(data);
		});
	}
});




ref : http://uiandwe.tistory.com/933

반응형

'서버(Server) > Server&Nodejs&DB' 카테고리의 다른 글

Node.js server.listen() Method  (0) 2018.07.23
라우터와 미들웨어 use  (0) 2018.07.23
PM2_프로세스 관리 도구  (0) 2018.07.09
파일(모듈) 분리하기  (0) 2018.06.29
쿠키 VS 세션  (0) 2018.06.26
반응형


Gsuite 에 가입한 후 도메인 설정 페이지로 어갑니다

Google 도메인을 통해 도메인 관리 

를 누르시고 그 안에 들어가면 종합 레코드란이 보일겁니다

이 부분에서 세팅 하면 됩니다




종합 레코드

시작하기 전에...

 

종합 레코드

종합 레코드는 Google Domains만의 고유한 개념으로서 G Suite와 통합하는 데 필요한 도메인 및 하위 도메인 매핑, 이메일 전달용 별칭 만들기, 도메인을 타사 웹 호스트와 통합하는 데 필요한 모든 리소스 레코드 추가와 같이 여러 리소스 레코드가 필요한 작업을 수행합니다. 일부 종합 레코드는 Google Domains의 DNS 탭 에서 직접 구성할 수 있습니다. 기타 종합 레코드는 웹 전달, 이메일 전달 또는 타사 웹 호스트 및 웹사이트 개발업체와의 통합을 설정할 때 Google Domains에서 자동으로 설정합니다.

DNS 탭 에서 직접 만들고 수정할 수 있는 종합 레코드는 다음과 같습니다.

  • App Engine - 하위 도메인이 Google App Engine에 호스팅된 앱을 가리키도록 합니다.
    참고: App Engine 종합 레코드는 더 이상 지원되지 않습니다. 기존 App Engine 종합 레코드가 있는 경우에는 아무 변화도 없습니다. App Engine 종합 레코드를 삭제할 수는 있지만 기타 방법으로 변경할 수는 없습니다.
    앞으로도 도메인을 Google App Engine와 통합할 수 있습니다. App Engine과 통합을 참조하세요.
  • 동적 DNS - 도메인 또는 하위 도메인이 동적으로 할당된 IP 주소가 포함된 레코드를 가리키도록 합니다(DNS 탭 에서 수정).
    도움이 필요하면 동적 DNS를 참조하세요.
  • G Suite - G Suite와 통합합니다(DNS 탭 에서 수정).
    도움이 필요하면 G Suite와 통합을 참조하세요.
  • 하위 도메인 전달 - 하위 도메인이 명명된 URL을 가리키도록 합니다(DNS 탭 에서 수정).
    도움이 필요하면 하위 도메인 전달을 참조하세요.

자동으로 설정되는 종합 레코드는 다음과 같습니다.

  • 이메일 전달 - 기존 이메일 주소로 전달되는 이메일 별칭을 만듭니다(이메일 탭 에서 수정). 이메일 전달을 참조하세요.
  • 웹 전달 - www 및 루트 도메인이 명명된 URL을 가리키도록 합니다(웹사이트 탭 에서 수정). 웹 전달을 참조하세요.
  • Shopify - 도메인을 Shopify에서 호스팅하는 사이트와 통합합니다(웹사이트 탭 에서 수정). 웹 인지도를 참조하세요.
  • Squarespace - 도메인을 Squarespace에서 호스팅하는 사이트와 통합합니다(웹사이트 탭 에서 수정). 웹 인지도를 참조하세요.
  • Weebly - 도메인을 Weebly에서 호스팅하는 사이트와 통합합니다(웹사이트 탭 에서 수정). 웹 인지도를 참조하세요.
  • Wix - 도메인을 Wix에서 호스팅하는 사이트와 통합합니다(웹사이트 탭 에서 수정). 웹 인지도를 참조하세요.

종합 레코드를 추가하는 방법은 다음과 같습니다.

  1. DNS 탭 에서 종합 레코드까지 아래로 스크롤합니다.
  2. App EngineG Suite 또는 하위 도메인 전달 레코드 유형을 선택합니다.
  3. 나머지 필드에 값을 입력하거나 원하는 G Suite 기능 및 하위 도메인을 선택하고 추가를 클릭합니다.

Google Domains에서 리소스 레코드 모음을 만들고 종합 레코드를 생성합니다.

각 종합 레코드 유형을 설정하는 방법은 다음을 참조하세요.

언제든지 종합 레코드 목록 옆의 삭제 또는 수정을 클릭하여 레코드를 변경하거나 삭제할 수 있습니다.



 ref : https://support.google.com/domains/answer/6069273?hl=ko&ref_topic=3251230


반응형
반응형

INSERT INTO mysql.user (host,user,authentication_string, password,ssl_cipher, x509_issuer, x509_subject) VALUES ('111.222.333.444','root','',password('비번'),'','','');

GRANT ALL PRIVILEGES ON *.* TO 'root'@'111.222.333.444';


select host, user, password from user;

flush privileges;

반응형
반응형

PM2

  • Node.js 애플리케이션용 프로덕션 프로세스 관리자

  • 로드 밸런서 기본 제공

  • 앱을 항상 작동 상태로 유지

  • 시스템 가동 중단 없이 앱을 다시 로드

    • MAX MEMORY RESTART 기능을 제공하는데 커맨드라인/JSON/코드(JS) 등으로 설정 가능하다.

  • 일반적인 시스템 관리 태스크를 쉽게 처리 가능

  • 애플리케이션 로깅, 모니터링 및 클러스터링을 관리

    • 클러스터 모드의 경우, 여러개의 프로세스를 생성해놓고, 이를 PM2에서 자동으로 로드밸런싱으로 지원해주기 때문에, 앞단 웹 서버(Nginx, Apache Server 등)에서 프록시 설정만 간단히 해줘도 된다.

설치

[sudo] npm install pm2 -g

기본 사용

앱을 시작할때 즉시 백그라운드로 보내진다.

$ pm2 start app.js

메뉴얼을 읽으면서 가장 괜찮다고 생각한 부분이 바로 다른 스크립트 언어도 같이 지원하는 부분이었다.

$ pm2 start echo.pl --interpreter=perl
 
$ pm2 start echo.coffee
$ pm2 start echo.php
$ pm2 start echo.py
$ pm2 start echo.sh
$ pm2 start echo.rb

어플리케이션 설정 파일 사용

JSON, YML 포맷으로 만들어진 파일을 일종의 시작 프로파일로 사용할 수 있다.

$ pm2 start start.yml(start.json)

start.yml

apps:
  script   : app.js
    instances: 4
    exec_mode: cluster
  script : worker.js
    watch  : true
    env    :
      NODE_ENV: development
    env_production:
      NODE_ENV: production

start.json

{
      "apps" : [
            {
            "name"       : "bash-worker",
            "script"     : "./a-bash-script",
            "exec_interpreter": "bash",
            "exec_mode"  : "fork_mode"
            },
            {
            "name"       : "ruby-worker",
            "script"     : "./some-ruby-script",
            "exec_interpreter": "ruby",
            "exec_mode"  : "fork_mode"
            }
      ]
}

CheatSheat

# Fork mode 
$ pm2 start app.js --name my-api # 프로세스 네임 지정 
 
# Cluster mode 
$ pm2 start app.js -i 0     # 가용한 CPU 갯수만큼 클러스터 확장 
$ pm2 start app.js -i max   # 위 설정과 동일하지만 곧 Deprecated 예정? 
 
# Listing 
$ pm2 list               # 모든 프로세스 리스트/상태 확인 
$ pm2 jlist              # 모든 프로세스 리스트/상태 확인(JSON) 
$ pm2 prettylist         # 모든 프로세스 리스트/상태 확인(Beatiful JSON) 
 
$ pm2 describe 0         # 특정 프로세스 정보 확인 
$ pm2 monit              # 모든 프로세스 모니터링 
 
# Logs 
 
$ pm2 logs [--raw]       # Display all processes logs in streaming 
$ pm2 flush              # Empty all log file 
$ pm2 reloadLogs         # Reload all logs 
 
# Actions 
 
$ pm2 stop all           # Stop all processes 
$ pm2 restart all        # Restart all processes 
 
$ pm2 reload all         # Will 0s downtime reload (for NETWORKED apps) 
$ pm2 gracefulReload all # Send exit message then reload (for networked apps) 
 
$ pm2 stop 0             # Stop specific process id 
$ pm2 restart 0          # Restart specific process id 
 
$ pm2 delete 0           # Will remove process from pm2 list 
$ pm2 delete all         # Will remove all processes from pm2 list 
 
# Misc 
 
$ pm2 reset <process>    # Reset meta data (restarted time...) 
$ pm2 updatePM2          # Update in memory pm2 
$ pm2 ping               # Ensure pm2 daemon has been launched 
$ pm2 sendSignal SIGUSR2 my-app # Send system signal to script 
$ pm2 start app.js --no-daemon
$ pm2 start app.js --no-vizion
$ pm2 start app.js --no-autorestart
 

참고





ref : http://stylishc.tistory.com/121

반응형
반응형


[javascript] Singleton 싱글톤




싱글턴의 싱글은 혼자의 싱글이 맞습니다. 

객체를 만들 때, 하나의 생성자로 여러 객체를 만들 수 있었습니다. 

하지만 싱글턴은 필요에 의해 단 하나 객체만을 만들 때 사용합니다. 아래 처럼요.

var obj = {
  a: 'hello',
  b: function() {
    alert(this.a);
  }
};

?? 엄청 간단하네요. 사실 객체 리터럴이 바로 싱글턴 패턴의 대표적인 예입니다. 저 객체는 단 하나밖에 존재하지 않죠. 하지만 모든 속성이 다 공개되어 있다는 단점이 있습니다. 비공개로 만드는 게 바로 제대로 된 싱글턴입니다.

var singleton = (function() {
  var instance;
  var a = 'hello';
  function initiate() {
    return {
      a: a,
      b: function() {
        alert(a);
      }
    };
  }
  return {
    getInstance: function() {
      if (!instance) {
        instance = initiate();
      }
      return instance;
    }
  }
})();
var first = singleton.getInstance();
var second = singleton.getInstance();
console.log(first === second); // true;

코드가 확 길어졌네요. 차근히 살펴보면 쉽습니다. IIFE로 비공개 변수를 가질 수 있게 만들어줍니다. 그리고 그 안에 instance변수와 initiate 함수를 만들어줍니다. initiate 함수 안의 내용이 실제 객체의 내용입니다. 위의 obj 객체와 비교하면 a가 비공개 변수가 되었네요.

IIFE로 즉시 반환되는 부분(return)을 보시죠. getInstance라는 메소드를 가진 객체를 반환하는데, getInstance 함수를 호출하는 순간 내부적으로 initiate 함수가 호출되고, instance에 아까 그 객체의 내용이 저장되고 동시에 반환됩니다. getInstance가 여러 번 호출됐을 경우에는 코드를 보시면 이미 instance 객체가 있는 경우에는 initiate를 거치지 않고 바로 반환하는 것을 알 수 있습니다.

first와 second 변수를 보면 두 번 다 getInstance 함수를 호출했는데요. 결과적으로 두 변수는 같습니다. first 때 initiate된 객체를 second 때도 똑같이 반환받았기 때문이죠. 즉, 아무리 호출해도 기존에 있던 객체는 복사되는 것도 아니고 그냥 그대로 반환됩니다. 싱글턴 패턴은 모듈 패턴을 변형한 디자인 패턴입니다.

싱글턴을 언제 써야할 지 잘 모르겠나요? 처음 네임스페이스를 만들 때 사용하면 됩니다! 예를 들어 게임을 만든다고 치면, 게임 객체를 싱글턴으로 만드는 겁니다. 게임 내의 모든 것을 감싸고 있는 객체를 말이죠. 게임을 실행했을 때 게임은 한 번만 켜져야하기 때문에 싱글턴이 적절합니다.

세 번째는 생성자 패턴입니다. 이것도 이미 배웠죠? 상속을 배울 때 다뤘습니다! 대부분의 객체는 이 패턴으로 만들게 됩니다. 특히 상속이 필요할 때는 제일 많이 쓰이죠. 모듈 패턴과 생성자 패턴을 조합해서 코드를 보기 좋게 만들 수 있습니다.

function Vehicle(name, speed) {
  this.name = name; this.speed = speed;
}
Vehicle.prototype.drive = function () {
  console.log(this.name + ' runs at ' + this.speed)
}; 

function 부분과 prototype 부분으로 따로 떨어져 있는 이 코드를 하나로 묶어줍시다.

var Vehicle = (function() {
  function Vehicle(name, speed) {
    this.name = name; this.speed = speed;
  }
  Vehicle.prototype.drive = function () {
    console.log(this.name + ' runs at ' + this.speed);
  };
  return Vehicle;
})();

생성자와 프로토타입을 모두 Vehicle 변수 안에 넣었습니다. 변수 Vehicle과 생성자 Vehicle 이름이 같아서 걱정하시는 분이 있을 수도 있는데 IIFE라서 바로 변수 Vehicle에 생성자 Vehicle이 덮어씌워집니다.

이외에도 빌더 패턴, 팩토리 패턴, 중재자 패턴, 옵저버 패턴, 메소드 체이닝 패턴 등이 있는데 고급 강좌에서 다뤄보겠습니다. (메소드 체이닝 패턴은 자주 사용됩니다. 28강 턴제 게임 만들기에서 알려드리겠습니다!)

다음 시간에는 자바스크립트 함수형 프로그래밍에 대해 알아보겠습니다.



ref : https://www.zerocho.com/category/JavaScript/post/57541bef7dfff917002c4e86


반응형
반응형

스태틱 멤버

스태틱 프로퍼티와 메서드란 인스턴스에 따라 달라지지 않는 프로퍼티와 메서드 를 말합니다.

클래스 기반 언어에서는 별도의 문법을 통해 스택틱 멤버를 생성하여 클래스 자체의 멤버인 것처럼 사용할 수 있습니다.

예를 들어 MathUtils 클래스에 max() 라는 스태틱 메서드가 있다면MathUtils.max(2,5) 와 같은 식으로 호출할 수 있습니다.

이것은 공개 스태틱 멤버의 예로, 클래스의 인스턴스를 생성하지 않고도 사용할 수 있습니다.

비공개 스태틱 멤버는 클래스 사용자에게는 보이지 않지만 클래스의 인스턴스들은 모두 함께 사용할 수 있습니다.

그럼 자바스크립트에서 공개와 비공개 스태틱 멤버를 구현하는 방법을 살펴보도록 합니다.






공개 스태틱 멤버

자바스크립트에는 스태틱 멤버를 표기하는 별도의 문법이 존재하지 않습니다.

그러나 생성자에 프로퍼티를 추가함으로써 클래스 기반 언어와 동일한 문법을 사용할 수 있습니다.

생성자도 다른 함수와 마찬가지로 객체이고 그 자신의 프로퍼티를 가질 수 있기 때문에 이러한 구현이 가능합니다.

다음 예제는 Gadget 이라는 생성자에 스태틱 메서드인 isShiny() 와 일반적인 인스턴스 메서드인 setPrice() 를 정의한 것입니다.

isShiny() 는 특정 Gadget 객체를 필요로 하지 않기 때문에 스태틱 메서드라 할 수 있습니다.

모든 Gadget 이 빛나는지 알아내는 데는 특정한 하나의 Gadget 이 필요하지 않은 것과 같습니다.

반면 개별 Gadget 들의 가격은 다를 수 있기 때문에 setPrice() 메서드를 쓰려면 객체가 필요합니다.

JAVASCRIPT
// 생성자
var Gadget = function () { };

// 스태틱 메서드
Gadget.isShiny = function () {
	return 'you bet.'
};

// 프로토타입에 일반적인 함수를 추가
Gadget.prototype.setPrice = function (price) {
	this.price = price;
};

이제 이 메서드를 호출해 보도록 합니다.

스태틱 메서드인 isShiny() 는 생성자를 통해 직접 호출되지만, 일반적인 메서드는 인스턴스를 통해 호출됩니다.

JAVASCRIPT
// 스태틱 메서드를 호출하는 방법
console.log(Gadget.isShiny()); // you bet
	
// 인스턴스를 생성한 후 메서드를 호출하기
var iphone = new Gadget();
iphone.setPrice(500);

인스턴스 메서드를 스태틱 메서드와 같은 방법으로 호출하면 동작하지 않습니다.

스태틱 메서드 역시 인스턴스 iphone 객체를 이용해 호출하면 동작하지 않습니다.

JAVASCRIPT
console.log(typeof Gadget.setPrice); // undefined
console.log(typeof iphone.isShiny); // undefined

스태틱 메서드가 인스턴스를 통해 호출했을 때도 동작한다면 편리한 경우가 있을 수 있습니다.

이 경우에는 간단하게 프로토타입에 새로운 메서드를 추가하는 것만으로 쉽게 구현할 수 있습니다.

이 새로운 메서드는 원래의 스태틱 메서드를 가리키는 일종의 퍼사드(facade) 역할을 합니다.

JAVASCRIPT
Gadget.prototype.isShiny = Gadget.isShiny;
console.log(iphone.isShiny());

이런 경우에는 스태틱 메서드 안에서 this 를 사용할 때 주의를 기울여야 합니다.

Gadget.isShiny() 호출했을 때 내부의 this 는 Gadget 생성자를 가리키지만, iphone.isShiny() 를 호출했을 때는 this 는 생성자 함수로 생성된 객체인 iphone 을 가리키게 됩니다.


마지막으로 스태틱한 방법으로도, 스태틱하지 않은 방법으로도 호출될 수 있는 어떤 메서드를 호출 방식에 따라 살짝 다르게 동작하게 하는 예제를 살펴보도록 합니다.

메서드가 어떻게 호출되었는지 판별하기 위해서 instanceof 연산자를 활용해 봅니다.

JAVASCRIPT
// 생성자
var Gadget = function (price) {
	this.price = price;
};

// 스태틱 메서드
Gadget.isShiny = function () {
	
	// 다음은 항상 동작한다.
	var msg = 'you bet';
	
	if (this instanceof Gadget) {
		// 다음은 스태틱하지 않은 방식으로 호출되었을 때만 동작하도록 한다.(인스턴스 멤버를 가리킴)
		msg += ', it costs $' + this.price + '!!';
	}
	return msg;
	
};

// 프로토타입에 일반적인 메서드를 추가한다
Gadget.prototype.isShiny = function () {
	return Gadget.isShiny.call(this); // 이 this 는 생성자 함수로 생성된 인스턴스 객체를 가리키게 된다.
};

스태틱 메서드와 인스턴스를 통해 스태틱하지 않은 방법으로 호출해 보면 다음과 같은 결과가 나타납니다.

JAVASCRIPT
// 스태틱 메서드를 호출
console.log(Gadget.isShiny()); // you bet 이 기록된다.
	
// 인스턴스를 통해 스태틱하지 않은 방법으로 호출
var iphone = new Gadget(345.99);
console.log(iphone.isShiny()); // you bet, it costs $345.99!! 가 기록된다.



비공개 스태틱 멤버

지금까지는 공개 스태틱 멤버를 살펴보았습니다.

이번에는 비공개 스태틱 멤버를 구현하는 방법을 알아봅니다.


비공개 스태틱 멤버란 다음과 같은 의미를 가지고 있습니다.

  • 동일한 생성자 함수로 생성된 객체들이 공유하는 멤버입니다.
  • 생성자 외부에서는 접근할 수 없습니다.


Gadget 생성자 안에 counter 라는 비공개 스태틱 프로퍼티를 구현하는 예제를 살펴보도록 합니다.

비공개 프로퍼티는 먼저 클로저 함수를 만들고, 비공개 멤버를 이 함수로 감싼 후 이 함수를 즉시 실행한 결과로 새로운 함수를 반환하게 됩니다.

반환되는 함수는 Gadget 변수에 할당되어 새로운 생성자가 될 것입니다.

JAVASCRIPT
var Gadget = function () {
	
	// 스태틱 변수/프로퍼티
	var counter = 0;
	
	// 생성자의 새로운 구현 버전을 반환한다.
	return function () {
		console.log(counter += 1);
	};
	
}(); // 즉시 실행한다.

새로운 Gadget 생성자는 단순히 비공개 counter 값을 증가시켜 출력합니다.

몇 개의 인스턴스를 만들어 테스트해보면 실제로 모든 인스턴스가 동일한 counter 값을 공유하고 있음을 확인할 수 있습니다.

JAVASCRIPT
var g1 = new Gadget(); // 1 이 기록된다
var g2 = new Gadget(); // 2 이 기록된다
var g3 = new Gadget(); // 3 이 기록된다

객체당 1씩 counter 를 증가시키고 있기 때문에 이 스태틱 프로퍼티는 Gadget 생성자를 통해 생성된 개별 객체의 유일성을 식별하는 ID 가 될 수 있다.

유일한 식별자는 쓸모가 많으니 특권 메서드로 노출시켜도 좋지 않을까?

앞선 예제에 덧붙여 비공개 스태틱 프로퍼티에 접근할 수 있는 getLastId() 라는 특권 메서드를 추가해보도록 해봅니다.

JAVASCRIPT
// 생성자
var Gadget = function () {
	
	// 스태틱 변수/프로퍼티
	var counter = 0,
	    NewGadget;
	
	// 이 부분이 생성자를 새롭게 구현한 부분이다.
	NewGadget = function () {
		counter += 1;
	};
	
	// 특권 메서드
	NewGadget.prototype.getLastId = function () {
		return counter;
	};
	
	// Gadgt 생성자를 덮어쓴다.
	return NewGadget;
	
}(); // 즉시 실행한다.

새로운 버전을 아래에서 테스트 해봅니다.

JAVASCRIPT
var iphone = new Gadget();
console.log(iphone.getLastId()); // 1 이 기록
	
var ipod = new Gadget();
console.log(ipod.getLastId()); // 2 이 기록

var ipad = new Gadget();
console.log(ipad.getLastId()); // 3 이 기록

공개/비공개 스태틱 프로퍼티는 상당히 편리합니다.

특정 인스턴스에 한정되지 않는 메서드와 데이터를 담을 수 있고 인스턴스별로 매번 재생성되지도 않습니다.




ref : http://webclub.tistory.com/526



반응형

'서버(Server) > Server' 카테고리의 다른 글

Promise [2]  (0) 2018.08.16
[javascript] Singleton 싱글톤  (0) 2018.07.02
JavaScript Closure(자바스크립트 클로저)  (0) 2018.07.01
[Javascript ] 프로토타입 사용 이해하기  (0) 2018.07.01
this 정리  (0) 2018.07.01
반응형

클로저(Closure)

클로저라는 개념은 자바스크립트에 없는 class 의 역할을 대신해 비공개 속성/메서드, 공개 속성/메서드를 구현할 수 있는 근거를 마련할 수 있습니다.

따라서 객체지향적인 특징인 캡슐화(encapsulation)와 정보 은닉(information hiding)을 이해하려면 클로저를 반드시 이해해야 합니다.

클로저는 jQuery 같은 대형 라이브러리에서 채용되는 패턴입니다.




JavaScript Closure

함수를 정의하면 함수 단위의 렉시컬한 변수 스코프가 생성되고 변수 스코프의 체인이 구성됩니다.

즉, 중첩 함수의 경우 코드상에서 함수가 중첩된 그대로의 모습으로 변수 스코프 체인이 구성되어 변수에 대한 접근 권한도 코드의 계층구조 그대로 구성됩니다.


이제 다른 상황을 가정해 봅니다. 

자바스크립트의 함수 역할 중에 함수는 객체로서 다른 함수의 반환값으로 사용될 수 있습니다.

이 경우 반환값으로 사용된 함수의 변수 스코프의 문제를 생각해 보겠습니다.


다음의 코드에서 함수 inner 가 반환값으로 사용되어 다른 영역의 코드에서 실행되는 경우를 가정해 봅니다.

코드는 다음과 같습니다.

JAVASCRIPT
function outer() {
    var x = 0;
    return function () {
        return ++x;
    };
}

// 코드 실행
var x = -1;
var f = outer();
console.log(f()); // 1 반환


위 코드는 클로저가 생성되는 조건에 부합합니다.

  • 내부 함수가 익명함수로 되어 outer의 반환값으로 사용됐다.
  • inner는 outer의 실행 환경에서 실행된다.
  • inner에서 사용되는 변수 x는 outer의 변수 스코프에 있다.


이 프로그램이 실행되면 var f = outer(); 에 의해 파싱 단계에서는 outer의 내부에서 정의됐던 익명 함수가 실행 단계에서는 outer의 외부로 전달되어 실행된다.

앞에서 설명한 상황을 그림으로 나타내면 다음과 같습니다.



            클로저의 렉시컬 환경


실행 환경에 있는 f를 통해 outer가 반환한 익명 함수가 호출되면 return ++x; 에서 사용된 변수 x를 어디에서 검색할까?

원래 정의될 때 생성된 익명 함수의 변수 스코프 객체에서 갬색할까? 아니면 f가 실행되고 있는 영역에서 검색할까?

이는 렉시컬 특성과 유사합니다. 즉, 런터임의 변수는 렉시컬 환경을 기준으로 정의된 변수 스코프 및 체인에서 검색한다는 것입니다.


프로그램 실행 시 변수 검색은 해당 문장(return ++x)이 포함된 함수가 정의된 렉시컬 환경에서의 변수 스코프 체인을 기준으로 한다.


결국 앞의 예제 코드에서 inner를 호출하면 ++x 연산에 의해 1이 반환됩니다.


이제 좀 더 중요한 내용 다뤄보도록 합니다. 

함수 inner 를 계속해서 호출해서 결과를 보면 다음과 같습니다. 

JAVASCRIPT
f(); // 2 반환
f(); // 3 반환
f(); // 4 반환

결과만 봤을 때는 약간 이상하게 보일 수 있습니다.

어떻게 f의 호출이 끝나고 나서도 그 부모의 변수 스코프에 있는 x 값이 유지될 수 있을까?


내부 함수에서 선언된 변수가 아니면서 내부 함수에서 사용하는 outer의 x 같은 변수를 자유 변수(free variable)라고 합니다.

x가 메모리에서 제거되는 시기는 outer가 결정하지 못합니다. 이런 자유 변수는 outer가 실행되고 있는 환경이 "닫는(close)" 역할을 합니다.

즉, x의 경우는 변수 스코프가 outer가 실행되는 환경으로까지 확장됩니다.

외부환경에서 내부 함수에 대한 참조 f를 가지고 이상(즉 f가 메모리에서 사라지지 않는 이상) outer 함수는 "실행 중" 상태에 있습니다.

따라서 자유 변수 x 및 해당 변수 스코프 체인 관계는 메모리에서 계속 유지됩니다.

이처럼 outer 호출이 종료되더라도 outer의 지역 변수 및 변수 스코프 객체의 체인 관계를 유지할 수 있는 구조를 클로저(closure)라고 합니다.


함수 호출이 종료되더라도 그 함수의 지역 변수 및 지역 변수 체인 관계를 유지할 수 있는 구조를 클로저라고 한다.


자유 변수의 경우 그 값은 렉시컬 환경의 영향을 받으면서 그 생명주기는 실행 환경의 영향을 받는다는 것이 결국 클로저를 만들 수 있는 근거가 됩니다.

여기까지 이해하는 것만으로도 많은 것을 배운 것이지만 클로저에 대해 알아야 할 내용은 이뿐만이 아닙니다.


여기서 잠깐!!

다음의 Fucntion 으로 생성한 함수는 클로저를 만들지 못한다는 것을 알아야 합니다. Function 생성자를 이용해 생성한 함수는 렉시컬 영역을 사용하지 않습니다.

그 함수는 항상 전역 영역에서 생성된 것처럼 컴파일 될 것입니다.

JAVASCRIPT
var x = "g";
function f() {
    var x = "1";
    return new Function("return x"); // x를 전역 변수 스코프에서 검색한다.
}
var global = f();
console.log(global()); // g 를 출력한다.

위 코드는 Function 으로 생성된 함수로는 클로저를 구성할 수 없음을 보여주고 있습니다.


그럼 좀더 클로저에 대해 알아보도록 하겠습니다.




클로저 인스턴스

클로저가 생성한 인스턴스라는 의미로서 "클로저 인스턴스"라는 표현을 사용했습니다.

이것은 클로저를 인스턴스를 생성하는 단위로 보겠다는 것입니다.

클로저란 것은 내부 함수를 반환값으로 사용하는 특수한 함수로 볼 수 있습니다.

즉, 클로저를 함수 인스턴스를 만들어내는 특수한 함수로 해석할 수 있다는 것입니다.

일반 객체지향 프로그래밍 언어의 클래스 같은 존재와 비교해 클로저를 "함수를 생성하는 클래스"로 비유적으로 표현할 수 있을 것입니다.


클로저란 호출하면 다른 함수 인스턴스를 생성해내는 특수한 구조의 함수다.


앞에서 본 outer 함수를 클래스로 생각하고 다시 살펴봅니다.

JAVASCRIPT
// outer 정의
function outer() {
    var x = 0;  // 비공개 영역
    return function () { // 외부에서 호출 가능한 영역
        return ++x;  // 공개 영역
    }
}

outer 클로저를 이렇게 비공개 변수를 정의하는 부분과 외부에서 호출이 가능한 공개 영역으로 나눠서 생각해 보면 다른 언어의 클래스와 더욱더 유사해 보일 것입니다.

이제 "outer를 호출" 하는 것을 바로 "함수 객체를 생성"하는 것으로 생각하면 됩니다.

JAVASCRIPT
var f = outer(); // outer 의 인스턴스를 생성


클로저를 호출하는 것은 "클로저 인스턴스를 생성"하는 것이다.


클로저가 반환한 함수 f를 호출하는 것을 outer가 외부에 공개한 메서드를 호출하는 것으로 간주할 수 있습니다.

JAVASCRIPT
f(); // outer 의 공개 함수 사용


클로저 인스턴스를 호출한다는 것은 클로저가 외부에 공개한 멤버를 호출하는 것으로 이해할 수 있다.


"f" 가 사라지지 않는 이상 인스턴스 f가 가지고 있는 변수도 계속 유지됩니다.

이제 다음과 같은 코드를 살펴보겠습니다.

JAVASCRIPT
// outer 정의
function outer() {
    var x = 0;  // 비공개 영역
    return function () {
        return ++x;  // 공개 영역
    }
}

var f = outer();
f(); // 1
f(); // 2
var g = outer();
g(); // 1
g(); // 2

outer()를 호출해서 생성된 함수를 f에 할당했습니다. f를 호출해서 값을 증가시키면 다음에 f를 호출될 때는 이전에 증가된 값이 유지되어 두 번째 호출의 시작값이 됩니다.

함수 f 호출이 종료되더라도 내부 변수 x는 그대로 유지되는 클로저의 속성을 이용하고 있는 것입니다.


중요한 것은 지금부터입니다.

다시 한번 outer()를 호출해서 새로운 인스턴스를 g에 할당한 후 g를 호출해서 결과를 보면 "내부 변수 x가 새롭게 초기화"되었음을 알 수 있습니다.

내부 변수 x가 새롭게 초기화됐다는 것은 이전의 함수 f와 새롭게 생성된 함수 g는 전혀 다른 변수 공간을 사용하는 별도의 존재라는 것입니다.

클래스로 인스턴스를 생성할 때마다 자신만의 닫혀진 공간을 가진 인스턴스가 생성됩니다.

그래서 인스턴스에서는 다른 인스턴스의 닫혀진 공간에 있는 내부 변수에는 직접 접근할 수 없습니다.


클로저를 이렇게 닫혀진 공간을 가진 인스턴스를 생성하는 존재로 생각할 수 있습니다.

클로저를 호출하면 단순히 익명함수가 반환되는 것이 아니라 익명함수와 함께 거기에 연결된 닫혀진 공간이 함께 반환되는 것입니다.

그 닫혀진 공간에 내부 변수가 존재합니다.


클래스와 new를 사용해 여러 개의 닫혀진 공간을 가진 인스턴스를 만들어 내듯이 클로저와 ()를 이용하면 닫혀진 공간을 가지는 인스턴스를 여러 개 반복해서 만들어 낼 수 있습니다.

위의 f와 g는 이제 독립적인 변수 공간을 가지는 인스턴스입니다.

클로저 인스턴스는 단순히 클로저에서 반환된 함수에 대한 참조가 아니라 독립된 변수 공간을 가진 인스턴스를 반환하는 것입니다.

이것이 클로저의 진정한 의미입니다.


클로저란 비공개 내부 변수를 갖는 함수 인스턴스 생성자다. 

그리고 클로저로 생성한 독립된 변수 공간을 가진 인스턴스를 클로저 인스턴스라고 한다.







ref : http://webclub.tistory.com/387

반응형
반응형


[Javascript ] 프로토타입 이해하기

자바스크립트는 프로토타입 기반 언어라고 불립니다. 자바스크립트 개발을 하면 빠질 수 없는 것이 프로토타입인데요. 프로토타입이 거의 자바스크립트 그 자체이기때문에 이해하는 것이 어렵고 개념도 복잡합니다.

하지만 프로토타입이 무엇인지 깨우친 순간 자바스크립트가 재밌어지고, 숙련도가 올라가는 느낌을 팍팍 받을 수 있습니다. 그럼 지금부터 프로토타입을 이해해봅시다.

Prototype vs Class

클래스(Class)라는 것을 한 번쯤은 들어보셨을겁니다. Java, Python, Ruby등 객체지향언어에서 빠질 수 없는 개념이죠. 그런데 중요한 점은 자바스크립트도 객체지향언어라는 것입니다. 이게 왜 중요하냐구요? 자바스크립트에는 클래스라는 개념이 없거든요. 대신 프로토타입(Prototype)이라는 것이 존재합니다. 자바스크립트가 프로토타입 기반 언어라고 불리는 이유이죠.

클래스가 없으니 기본적으로 상속기능도 없습니다. 그래서 보통 프로토타입을 기반으로 상속을 흉내내도록 구현해 사용합니다.

참고로 최근의 ECMA6 표준에서는 Class 문법이 추가되었습니다. 하지만 문법이 추가되었다는 것이지, 자바스크립트가 클래스 기반으로 바뀌었다는 것은 아닙니다.


어디다 쓰나요?

그럼 프로토타입을 언제 쓰는지 알아봅시다.

넌 이미 알고있다

자바스크립트에 클래스는 없지만 함수(function)와 new를 통해 클래스를 비스무리하게 흉내낼 수 있습니다.

function Person() {
this.eyes = 2;
this.nose = 1;
}
var kim  = new Person();
var park = new Person();
console.log(kim.eyes);  // => 2
console.log(kim.nose); // => 1
console.log(park.eyes); // => 2
console.log(park.nose); // => 1

kim과 park은 eyes와 nose를 공통적으로 가지고 있는데, 메모리에는 eyes와 nose가 두 개씩 총 4개 할당됩니다. 객체를100개 만들면 200개의 변수가 메모리에 할당되겠죠?
바로 이런 문제를 프로토타입으로 해결할 수 있습니다.

function Person() {}
Person.prototype.eyes = 2;
Person.prototype.nose = 1;
var kim  = new Person();
var park = new Person():
console.log(kim.eyes); // => 2
...

자바스크립트 개발을 하시는 분이라면 아마 써보진 않았어도 최소한 본 적은 있을겁니다. 간단히 설명하자면 Person.prototype이라는 빈 Object가 어딘가에 존재하고, Person 함수로부터 생성된 객체(kim, park)들은 어딘가에 존재하는 Object에 들어있는 값을 모두 갖다쓸 수 있습니다.
즉, eyes와 nose를 어딘가에 있는 빈 공간에 넣어놓고 kim과 park이 공유해서 사용하는 것이죠. 이해되셨나요?

프로토타입을 깊게 파보면 엄청나게 복잡하지만 개발자가 사용하는 부분만 본다면 이게 거의 전부입니다. 하지만 개발자는 사용법만 알고있는게 아니라 언제나 왜? 를 생각해야합니다.

프로토타입이 왜 이렇게 쓰이는지 조금 더 깊게 알아보도록 하겠습니다.


Prototype Link와 Prototype Object

자바스크립트에는 Prototype Link 와 Prototype Object라는 것이 존재합니다. 그리고 이 둘을 통틀어 Prototype이라고 부릅니다. 프로토타입을 좀 안다는 것은 이 둘을 완벽히 이해하고 갖고 놀 수준이 되었다는 뜻입니다.

제가 프로토타입에 대해 공부하면서 중요하다고 생각되는 포인트가 몇 가지 있었습니다. 그 포인트들을 잘 이해하면서 보시기 바랍니다.

Prototype Object

모든 객체(Object)의 조상은 함수(Function)입니다.

function Person() {} // => 함수
var personObject = new Person(); // => 함수로 객체를 생성

personObject 객체는 Person이라는 함수로부터 파생된 객체입니다. 이렇듯 언제나 객체는 함수로부터 시작됩니다. 여러분이 많이 쓰는 일반적인 객체 생성도 예외는 아닙니다.

var obj = {};

얼핏보면 함수랑 전혀 상관없는 코드로 보이지만 위 코드는 사실 다음 코드와 같습니다.

var obj = new Object();

위 코드에서 Object가 자바스크립트에서 기본적으로 제공하는 함수입니다.

Object도 함수다!

Object와 마찬가지로 Function, Array도 모두 함수로 정의되어 있습니다. 이것이 첫 번째 포인트입니다.

그렇다면 이것이 Prototype Object랑 무슨 상관이있느냐? 함수가 정의될 때는 2가지 일이 동시에 이루어집니다.

1.해당 함수에 Constructor(생성자) 자격 부여

Constructor 자격이 부여되면 new를 통해 객체를 만들어 낼 수 있게 됩니다. 이것이 함수만 new 키워드를 사용할 수 있는 이유입니다.



constructor가 아니면 new를 사용할 수 없다!

2.해당 함수의 Prototype Object 생성 및 연결

함수를 정의하면 함수만 생성되는 것이 아니라 Prototype Object도 같이 생성이 됩니다.



함수를 정의하면 이렇게 됩니다

그리고 생성된 함수는 prototype이라는 속성을 통해 Prototype Object에 접근할 수 있습니다. Prototype Object는 일반적인 객체와 같으며 기본적인 속성으로 constructor와 __proto__를 가지고 있습니다.

prototype 속성으로 Prototype Object에 접근

constructor는 Prototype Object와 같이 생성되었던 함수를 가리키고 있습니다.
__proto__는 Prototype Link입니다. 밑에서 자세히 설명합니다.

이제 위에서 kim과 park이 나왔던 예제를 다시 보겠습니다.

function Person() {}
Person.prototype.eyes = 2;
Person.prototype.nose = 1;
var kim  = new Person();
var park = new Person():
console.log(kim.eyes); // => 2
...

이제 왜 Person.prototype을 사용하는지 눈에 보이시나요?

Person.prototype 객체에 eyes와 nose 속성이 추가되었다!

Prototype Object는 일반적인 객체이므로 속성을 마음대로 추가/삭제 할 수 있습니다. kim과 park은 Person 함수를 통해 생성되었으니 Person.prototype을 참조할 수 있게 됩니다.

Prototype Link를 보기 전에 Prototype Object를 어느 정도 이해하시고 보기 바랍니다. 함수가 정의될 때 이루어지는 일들을 이해하는 것이 두 번째 포인트, Prototype Object를 이해하는 것이 세 번째 포인트입니다.


Prototype Link

kim 객체는 eyes가 없는데 ??

kim에는 eyes라는 속성이 없는데도 kim.eyes를 실행하면 2라는 값을 참조하는 것을 볼 수 있습니다. 위에서 설명했듯이 Prototype Object에 존재하는 eyes 속성을 참조한 것인데요, 이게 어떻게 가능한걸까요??

바로 kim이 가지고 있는 딱 하나의 속성 __proto__가 그것을 가능하게 해주는 열쇠입니다.

prototype 속성은 함수만 가지고 있던 것과는 달리(Person.prototype 기억나시죠?) 
__proto__속성은 모든 객체가 빠짐없이 가지고 있는 속성입니다.

__proto__는 객체가 생성될 때 조상이었던 함수의 Prototype Object를 가리킵니다. kim객체는 Person함수로부터 생성되었으니 Person 함수의 Prototype Object를 가리키고 있는 것이죠.

드디어 __proto__를 공개합니다

__proto__를 까보니 역시 Person 함수의 Prototype Object를 가리키고 있었습니다.

객체, 함수, Prototype Object의 관계

kim객체가 eyes를 직접 가지고 있지 않기 때문에 eyes 속성을 찾을 때 까지 상위 프로토타입을 탐색합니다. 최상위인 Object의 Prototype Object까지 도달했는데도 못찾았을 경우 undefined를 리턴합니다. 이렇게 __proto__속성을 통해 상위 프로토타입과 연결되어있는 형태를 프로토타입 체인(Chain)이라고 합니다.




프로토타입 체인, 최상위는 Object

이런 프로토타입 체인 구조 때문에 모든 객체는 Object의 자식이라고 불리고, Object Prototype Object에 있는 모든 속성을 사용할 수 있습니다. 한 가지 예를 들면 toString함수가 있겠습니다.


Object속성인 toString함수를 kim도 사용가능

__proto__와 프로토타입 체인을 이해하는 것이 네 번째 포인트입니다.





ref : https://medium.com/@bluesh55/javascript-prototype-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-f8e67c286b67




반응형
반응형








this 총정리


this 란?


this 란?

this 는 일반적으로 메서드가 호출한 객체의 속성(프로퍼티)입니다.

this 는 객체를 생성하는 동시에 자동 생성됩니다.


JAVASCRIPT
function MyClass(){
    this.property1 = "value1";
}

MyClass.prototype.method1 = function(){
    console.log(this.property1);
}

var my1 = new MyClass();

my1.method1();





this 가 만들어지는 경우는 일반적으로 다음과 같습니다.

1) 일반 함수에서의 this

2) 중첩 함수에서의 this

3) 이벤트에서의 this

4) 메서드에서의 this

5) 메서드 내부의 중첩함수에서의 this




일반 함수에서 this

일반 함수에서서의 this 는 무조건 window 를 가리킵니다.


JAVASCRIPT
var data = 10;
// window.data == this.data  형식과 동일하다.

function outer(){
    this.data = 20;
    data = 30;

    console.log("1. data1 = " + data); // 30
    console.log("2. this.data = " + this.data); // 30
    console.log("3. window.data = " + window.data); // 30
}

outer();




일반 중첩함수에서 this

중첩 함수에서의 this 는 window 를 가리킵니다.


JAVASCRIPT
var data = 10;

function outer(){
    // 중첩함수
    function inner(){
        this.data = 20;
        data = 30;
        console.log("1. data1 = " + data); // 30
        console.log("2. this.data = " + this.data); // 30
        console.log("3. window.data = " + window.data); // 30
    }
    inner();
}
outer();



이벤트 리스너에서 this


이벤트에서의 this 는 이벤트를 발생한 객체를 가리킵니다.


JAVASCRIPT
리스너실행

var data = 10;
$(document).ready(function(){

    // 이벤트 리스너 등록    
    $("#myButton").click(function(){
        this.data = 20;
        data = 30;

        this.style.color = '#000';
        console.log("1. data1 = " + data); // 30
        console.log("2. this.data = " + this.data); // 20 (이벤트 핸들러의 객체)
        console.log("3. window.data = " + window.data); // 30
    });
});



메서드에서 this

메서드 내에서의 this 는 메서드를 호출한 객체를 가리킵니다.


JAVASCRIPT
var data = 10;

function MyClass(){
    this.data = 20;
    data = 30;

    console.log("1. data1 = " + data);
    console.log("2. this.data = " + this.data);
    console.log("3. window.data = " + window.data);
}

// 인스턴스 생성
var my1 = new MyClass();
// this 는 메소드를 호출한 객체를 나타낸다.
// 여기선 my1 이 인스턴스

// 일반 함수 호출할 경우
MyClass(); // 30, 30, 30



메서드 내부의 중첩함수에서 this


메서드 내부의 중첩함수에서의 this 는 window 를 가리킵니다.

그리고 메서드 내부의 중첩함수에서 this 를 보존하는 방법은 아래와 같습니다.


JAVASCRIPT
var data = 10;

function MyClass(){

    this.data = 50;

    // 내부 함수내에서 this 를 보존하는 방법
    var objThis = this;
    objThis.data = 40;

    function inner(){

        this.data = 20; // window.data 와 같음.
        data = 30; // window.data 와 같음.

        console.log("1. data1 = " + data); // 30;
        console.log("2. this.data = " + this.data); // 30 중첩(내부)함수, 즉 컨텍스트에 따라 this 가 달라진다.
        console.log("3. window.data = " + window.data);
        console.log("4. objThis = " + objThis.data);
    }

    inner();
}

// 인스턴스 생성
var my1 = new MyClass();



총정리 : this 가 만들어지는 일반적인 경우

1) 일반함수에서 this

    • window

2) 중첩함수에서 this

    • window

3) 이벤트에서 this

    • 이벤트를 발생한 객체

4) 메서드에서 this

    • 메소드를 호출한 객체

5) 메서드 내부의 중첩 함수에서 this

    •  window




ref : http://webclub.tistory.com/140?category=501533



반응형
반응형
파일(모듈) 분리하기





exports를 이용하여 모듈화 하는 방법

Node 개발시 어플리케이션 소스코드를 분석 및 관리를 쉽게 하기 위해 역할별로 관련된 함수들을 파일 단위로 분리하여 사용하는데 이때 이 파일들을 모듈이라 합니다.
CommonJS 표준에 따라 다음과 같이 모듈을 이용할 수 있습니다.
함수를 모듈로 분리하고 등록해주는 방법으로 Node 어디서나 사용 가능한 전역객체인 exports에 개발한 함수(Function)를 할당합니다.
그리고 분리된 모듈을 불러오는 방법으로는 마찬가지로 전역 함수인 require() 함수를 이용합니다.

두 파일이 상대경로상 같은 디렉터리 안에 위치하는 경우 다음과 같이 모듈로 등록하고 불러올 수 있습니다.

calcModule.js
exports.add = function(arg1 , arg2){
    return arg1 + arg2;
}

main.js

var calc = require('./calcModule');
console.log(calc.add(1,3)); //4


require() 함수에 js 확장자를 뺀 파일의 상대경로를 인자로 넘겨주면 해당 모듈이 경로상에 있는지 확인하여 불러옵니다.
만약 해당 경로에 해당하는 파일이 없다면 폴더명으로 인식하여 찾아 폴더 안에 있는 파일들을 불러옵니다.
또한 해당 모듈 안에서도 다른 모듈을 불러올 수 있으며, 그 모듈 또한 안에서 또 다른 모듈을 불러올 수 있습니다.
이렇게 모듈이 사용하는 대상 모듈을 의존 모듈이라 하는데 Node에서의 모듈들은 서로 사슬처럼 얽혀 있습니다.

다음의 소스들을 main에서부터 시작해서 유추하면서 따라가보시면 이해가 될 것입니다.
주의할 점이 있다면 모듈을 로딩하는 시점과 로딩한 모듈안의 함수를 실행하는 시점이 다르다는 점입니다.

calcModule.js
console.log('calcModule.js 실행');
exports.add = function(arg1 , arg2){
    console.log('calcModule.js add() 실행');
    return arg1 + arg2;
};
console.log('calcModule.js 종료');


calcModule2.js
console.log('calcModule2.js 실행');
exports.add2 = function(arg1, arg2){
    console.log('calcModule2.js add2() 실행');
    
    var calc = require('./calcModule');
    console.log('calcModule 로딩 완료');
    
    return calc.add(arg1, arg2);
};
console.log('calcModule2.js 종료');

main.js 
console.log('main.js 시작')
var calc2 = require('./calcModule2');
console.log('main.js에서 calc2.add() 호출 ' + calc2.add2(1,3));

결과 
main.js 시작
calcModule2.js 실행
calcModule2.js 종료
calcModule2.js add2() 실행
calcModule.js 실행
calcModule.js 종료
calcModule 로딩 완료
calcModule.js add() 실행
main.js에서 calc2.add() 호출 4


module.exports를 이용하여 모듈화 하는 방법
다음과 같이 module.expots 에 할당하고 require를 통해 불러올 수도 있습니다.
같은 변수와 같은 함수명을 사용했는데 require로 불러 올 때 변수명을 통해 불러오는 것 처럼 보이지만 calc 객체의 참조값을 가져온다는 점에 주의합니다.
때문에 모듈1의 calc 객체와 모듈2의 calc 객체는 서로 다른객체 즉 서로 다른 참조값을 가지므로 main.js의 calc.add와 calc2.add는 서로 다른 객체의 add 메서드임을 확인할 수 있습니다.


calcModule.js
var calc = {};
calc.add = function(arg1 , arg2){
    console.log('cal1');
    return arg1 + arg2;
};
module.exports = calc;

calcModule2.js
var calc = {};
calc.add = function(arg1 , arg2){
    console.log('cal2');
    return arg1 + arg2;
};
module.exports = calc;

main.js 
var calc = require('./calcModule');
var calc2 = require('./calcModule2');
console.log('main.js에서 calc.add() 호출 ' + calc.add(1, 3));
console.log('main.js에서 calc2.add() 호출 ' + calc2.add(1, 3));


결과 
cal1
main.js에서 calc.add() 호출 4
cal2
main.js에서 calc2.add() 호출 4




ref : http://dololak.tistory.com/97


반응형
반응형

로컬에 설치된 MySql 접속, 사용자 비밀번호 변경






1. CMD창을 띄우고 위 (설치)경로로 이동한다.


2. mysql -u 사용자아이디 -p 로 실행









3. 사용자 비밀번호 변경문 실행

UPDATE mysql.user SET password=PASSWORD('변경비밀번호') WHERE user='root'; 

FLUSH PRIVILEGES; 

exit



Windows

  1. 시작 -> cmd 에서 services.msc 입력하거나 제어판 -> 관리도구 -> 서비스
  2. MySQL server 중지
  3. 에디터에 Case1항의 3번 암호를 초기화하는 SQL 을 입력하고 c:\init.sql 로 저장

  4. 시작 -> 실행에서 다음 명령어 입력 (MySQL 설치경로 따라 다를수 있음)

    "C:\Program Files\MySQL\MySQL Server 5.5\bin\mysqld.exe"
    --defaults-file="C:\\Program Files\\MySQL\\MySQL Server 5.5\\my.ini"
    --init-file=c:\\init.sql
  5. 잘 반영되었는지 확인하기 위해 mysql cient 로  login 해 보고 제대로 로그인 되면 c:\init.sql는 삭제한다.






ref : https://www.lesstif.com/pages/viewpage.action?pageId=9437268

ref : http://mylife365.tistory.com/353

반응형
반응형



Javascript의 객체

1) prototype 기반의 언어

java와 c++은 클래스(class) 기반의 객체지향 언어인 반면, javacript는 prototype기반의 객체지향 언어입니다.

class 기반의 객체지향 언어는 class라는 껍데기를 정의하고 이를 통해 객체를 생성합니다.

그러나 prototype 기반의 객체지향 언어는 class 정의 없이도 객체를 생성할 수 있습니다.

( 생성자 함수를 통해 class를 따라할 수 있으며, ES6부터는 Class가 추가되었습니다. )



2) 캡슐화와 상속 지원

객체지향 프로그래밍에서 중요한 특징 중 하나는 캡슐화와 상속이라는 개념입니다.

javascript는 class가 없어도 캡슐화와 상속을 지원합니다.

캡슐화는 클로저를 통해, 캡슐화는 Prototype을 통해 가능합니다.

( 클로저에 대한 내용은 여기를, Prototype에 대한 내용은 여기를 참고해주세요 ! )



3) 프로퍼티와 값의 모임

javascript에서 객체는 { }라는 울타리 안에서 프로퍼티와 값을 쌍으로 하는 집합의 모임입니다.

이제부터 나올 예제들을 보면서 살펴보도록 하겠습니다.





객체 생성 방법

1) 리터럴

가장 일반적인 방법은 중괄호{ } 를 사용하여 객체를 생성하는 방법입니다.

예제

1
2
3
4
5
var person = {
  name: "victolee",
  email: "asdf@example.com",
  birth: "0225"
}

위와 같이 정의한 person은 name, email, brith 프로퍼티를 갖고 있고, 각 프로퍼티는 값을 갖고 있습니다.

객체를 정의할 때는 중괄호{ } 안에 프로퍼티 : 값의 쌍을 입력하고 쉼표(,)로 프로퍼티를 구분합니다.

프로퍼티와 값의 구분은 콜론 ( : ), 프로퍼티&값 쌍의 구분은 쉼표 ( , )로 합니다.

( 프로퍼티와 값의 구분은 =이 아닙니다 ! )



2) Object() 생성자 함수

new 키워드를 이용하여 Object 생성자 함수를 호출하면 빈 객체를 얻을 수 있습니다.

예제

1
2
3
4
5
6
7
8
9
10
11
var person = new Object();
console.log(person.name)
console.log(person.email)
console.log(person.birth)
 
person.name = "victolee";
person.email = "asdf@example.com";
person.birth = "0225";
console.log(person.name)
console.log(person.email)
console.log(person.birth)

new Object()를 호출하면 비어있는 객체를 생성합니다.

따라서 name, email, birth 프로퍼티를 갖고 있지 않습니다.

비어있는 객체는 의미가 없으므로, new Object()로 비어있는 객체를 생성했으면 프로퍼티를 추가해줘야 합니다.



3) 생성자 함수

생성자 함수를 사용하면 java나 c++의 class 개념처럼 껍데기를 만들 수 있습니다.

기존 함수에 new 연산자를 붙여서 호출하면 해당 함수는 생성자 함수로 동작합니다.

그런데 일반적인 함수인지, 객체를 만들기 위한 목적의 생성자 함수인지 구분하기 위해 생성자 함수의 첫 문자는 대문자로 표기하는 것이 관례입니다.

예제

1
2
3
4
5
6
7
8
9
10
11
function Person(name,email){
  this.name = name;
  this.email = email;
  this.walk = "직립 보행"
}
 
var person1 = new Person("victolee""foo@example.com");
var person2 = new Person("worrr""goo@example.com");
 
console.log(person1.name + " " + person1.email + " " + person1.walk);
console.log(person2.name + " " + person2.email + " " + person2.walk);

리터럴과 Obejct()로 객체를 생성하는 것과 달리, 생성자 함수를 통해 객체를 생성하면 같은 속성을 가진 객체를 여러 개 생성할 수 있습니다.

즉 person1과 person2 객체는 name, email, walk 프로퍼티를 갖게 됩니다.


또한 생성자 함수에서 정의한 this는 생성자 함수로 생성된 인스턴스가 됩니다.

사실 생성자 함수로 인스턴스를 생성하기 전에 먼저 비어있는 객체를 생성합니다.

this는 이 비어있는 객체를 가리키고, 그 객체에 name, email, walk 프로퍼티를 추가한 것입니다.

생성자 함수에 반환 값이 없으면 비어있는 객체에 새로운 프로퍼티를 추가한 this가 반환됩니다.


( 조금 더 구체적인 내용을 원하시면 여기를 참고하셔서 Prototype에 대해 이해해보세요 ! )



일반 함수와 생성자 함수의 차이점은 new 연산자를 붙이느냐의 차이입니다.

생성자 함수인데 new를 붙이지 않는다면 오류를 발생하겠죠.

이러한 경우를 대비해서 생성자 함수를 호출할 경우 새로운 객체를 만들도록 분기문을 작성하곤 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
function foo(arg){
  if(!(this instanceof foo)){
    return new foo(arg);
  }
  this.value = arg ? arg : -99;
}
 
let a = new foo(10);
let b = foo(100);
 
console.log(a);
console.log(b);

변수 b의 경우 생성자 함수 foo를 호출하려 했지만 new 연산자를 붙이지 않은 경우입니다.

즉 foo()함수를 호출했으므로 this는 전역 객체가 될 것이고, 전역 객체는 foo의 인스턴스가 아니므로 생성자 함수를 호출하도록 new foo(arg)를 호출하도록 작성한 코드입니다.





객체의 프로퍼티 작성, 호출 방법

이전 예제들을 보시면서 객체의 프로퍼티를 작성하는 방법과 호출하는 방법에 대해 자연스럽게 배우셨습니다.

객체의 프로퍼티를 정의할 때는 중괄호{ }내에서 프로퍼티를 작성하면 되고, 호출하는 방법은 객체.프로퍼티를 하시면 됩니다.


그런데 객체의 프로퍼티를 작성 할 때의 유의사항과 호출할 때 다른 방식이 있어서 말씀드리려고 합니다.

1) 프로퍼티 작성시 유의사항

프로퍼티를 정의할 때 작성한 이름은 자동으로 문자열로 취급됩니다.

1
2
3
4
5
6
7
8
9
var person = {
  "name" "victolee",
  "email" "foo@example.com"
}
 
var person = {
  name : "victolee",
  email : "foo@example.com"
}

위의 두 객체 선언 방식은 완전히 동일합니다.


javascript에서 정의한 예약어를 프로퍼티로 사용할 수 있습니다.

예약어를 굳이 바꾸는 것은 별로 좋은 방식은 아니지만 " "을 이용하여 프로퍼티를 선언한다면 바꿀 수 있습니다.

그래서 저는 주로 " "을 사용하지 않는 편입니다.



2) 객체의 프로퍼티 이름으로 변수를 사용

1
2
3
4
5
6
7
let foo = "name"
var person = {
  [foo] : "victolee",
  "email" "foo@example.com"
}
 
console.log(person.name)

프로퍼티 이름에 대괄호[ ]로 덮어주면 변수로 프로퍼티 이름을 작성할 수 있습니다.



3) 또 다른 프로퍼티 호출 방법

1
2
3
4
5
6
7
var person = {
  name: "victolee"
}
 
console.log(person.name)
console.log(person["name"])   // victolee
console.log(person[name])     // undefined

보시는 바와 같이 대괄호 [ ]를 사용하여 프로퍼티를 호출 할 수 있습니다.

이 때 대괄호안에는 무조건 문자열이여야 합니다.



그런데 가끔 대괄호에 문자열이 아닌 변수를 넣을 수도 있습니다.

1
2
3
4
5
6
7
8
9
var person = {
  foo : "name",
  name: "victolee"
}
 
console.log(person["name"])
 
var nameVar = person.foo
console.log(person[nameVar])

person.foo는 문자열인 name을 반환합니다.

name이라는 문자열은 변수 nameVar에 할당되고, person의 속성을 호출하는데 사용됩니다.

어떤 프로퍼티를 호출해야 할지 동적으로 결정해야 할 때 변수에 할당하기도 하므로 기억하두셔야 할 부분입니다.




이상으로 javascript에서의 객체에 대해 알아보았습니다.




ref : http://victorydntmd.tistory.com/51

ref : https://blog.naver.com/matzip84/221304426187

반응형
반응형


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

//쿠키 정보  예제, 쿠키 정보는 브라우저에서 삭제 가능, 즉 서버가 꺼져도 브라우저에 정보가 남아 있게된다
router.route('/count').get(function (req, res) {
    console.log('/count ');
 
    //res 에는 cookie-parser() 를 등록하여 cookie 함수가 생기게됨
    //웹브라우저에 쿠키 정보를 저장하기 위한 값들을 res 에 저장함
    var count = 0;
    if (req.cookies.count) {
        count = parseInt(req.cookies.count) + 1;
    } else {
        count = 0;
    }
 
    res.cookie('count', count);
    ///process/showCookie 로 이동
    res.send('count : ' + count);
});
 
 
 
//session 정보는 브라우저에서 정보가 암호화된 형태로 보여지며 실제 정보는 서버에서 관리한다
//즉 서버를 껐다 키면 해당 정보가 램에서 날라간다
router.route('/countS').get(function (req, res) {
    console.log('/countS ');
    //req.session.count     //session 객체까지가 원래 존재하는 구문이고 뒤에 .count 는 세션에 추가한 변수
    if (req.session.count) {//브라우저에서 cookies 정보 중 connect.sid 가 세션 아이디이다
        req.session.count++;    
    } else {
        req.session.count = 1;
    }
    res.send('count : ' + req.session.count);
});









이미지 출처 : http://croak.tistory.com/96

반응형

+ Recent posts