http://www.nexpert.net/606


    글 싣는 순서
 1. SSO의 개요
 2. SAML 2.0 Deepdive (상)
 3. SAML 2.0 Deepdive (하) 

시작하며
SSO에 대한 기본적인 개념을 바탕으로 SAML 2.0 프로토콜에 대해 살펴보겠습니다. SSO에 대한 관심이 많다보니 은근히 인기를 끄는 글입니다. 


SAML 2.0 의 개요
SAML 은 Security Assertion Markup Language 의 약어로 같은 네트워크 내의 인증과 권한에 관한 데이타를 서로 교환하기 위한 표준입니다. SAML을 이용하면 사용자를 인증을 위한 IdP 서버와 SP간에 안전하게 인증 정보를 교환할 수 있습니다. 요즘 많은 기업에서 SAML 2.0 기반의 SSO를 구현하는 이유는 다음과 같은 이점이 있기 때문입니다.  

  • 암호 피로 (Password fatigue) 감소 

    사용자가 여러 개의 비밀번호와 유저 네임의 쌍을 외워야 하는 정신적인 피로도를 의미하는 암호피로의 감소  

  • 인증 위임 
    다수의 Service Provider 와 단 하나의 IdP 간의 CoT를 생성하여 IdP로 인증 위임되므로 사용자는 단 한번의 로그인으로 다수의 Service Provider를 사용할 수 있음

  • 안전한 인증 정보의 보호 
    IdP, SP, 사용자 사이에서 교환되는 인증정보를 암호화 

  • 개인 생산성 향상 
    지속적으로 패스워드를 재입력하는 과정이 생략되고, 패스워드 재설정 및 복구에 따른 과정이 필요없음 


시스코의 UC 애플리케이션의 경우 SAML 2.0을 지원하는 대부분의 IdP와 연동이 가능하지만, 제조사에서 직접 테스트 IdP는 다음과 같습니다. 

  • Microsoft Active Directory Federation Services (ADFS) 2.0
  • Open Access Manager (OpenAM) 11.0
  • PingFederate 6.10.0.4 
  • F5 BIP-IP 11.6




SAML 기반의 SSO 주요 요소
SAML 의 동작 방식을 이해하기 위해서 먼저 몇가지 구성 요소를 살펴보겠습니다. . 



  • 클라이언트 
    웹브라우저 또는 단말 

  • Service Provider
    클라이언트가 접근하려는 애플리케이션 또는 서비스 (웹엑스 또는 재버)

  • Identity Provider(IdP)
    사용자 크리덴셜 (사용자명과 패스워드)을 인증하고 SAML Assertion을 발행 

  • SAML Assertion
    사용자 인증을 위해 IdP로 부터 SP로 전달되는 보안 정보 
    사용자명, 권한 등의 내용을 적은 XML 문서로 변조를 막기위해 전자서명됨 

  • SAML Request (인증 요청)
    Service Provider 가 생성하는 인증 요청으로 IdP로 인증 위임

  • Circle of Trust (CoT)
    하나의 IdP를 공유하는 Service Provider 들로 구성

  • Metadata
    SSO를 활성화하는 Service Provider 및 IdP 가 생성하는 XML 파일
    메타데이타의 교환으로 신뢰 관계 설립

  • Assertion Consumer Service (ACS) URL
    ACS URL은 IdP가 특정 URL로 최종 SAML 응답을 포스트하도록 요구한다.  


SAML SSO 은 Assertions, 프로토콜, 바인딩 그리고 프로파일의 특별한 결합입니다. 여러가지 Assertion은 프로토콜과 바인딩을 사용하는 애플리케이션과 사이트 사이에서 교환됩니다. 이 Assertion은 사이트간의 사용자들을 인증합니다.


SAML 2.0 에서 Trust Agreement 
SAML SSO는 IdP와 SP 간이 프로비져닝 과정에서 인증서와 메타데이타(metadata)를 교환하므로서 CoT (신뢰 체인, Circle of Trust)를 설립합니다. 이를 통해 Service Provider는 IdP의 사용자 정보를 신뢰합니다. 

SAML 2.0 에서 IdP와 Service Provider 간에 신뢰관계를 맺기 위해서는 각각의 메타데이타를 교환해야 합니다. 각각의 메타데이타를 생성하면 다음과 같은 XML문서로 나타납니다.  

 


Service Provider의 메타데이타는 <md:EntityDesciptor>로 시작하며, IdP의 메타데이타는 <md:IDPSSODescriptor>로 시작합니다. 실제 교환은 관리자에 의해 수동으로 (Copy & Paste)으로 전달되는 가장 확실한 방식을 주로 사용합니다. 

 

SAML 2.0 기반의 인증 과정 
IdP와 Service Providor 간에 메타데이타를 교환하여 신뢰 관계 설립한 후 SAML 2.0 은 다음과 같이 동작합니다. 


SAML 기반의 인증 과정은 다음과 같습니다.


  1. Access Resource (애플리케이션 접속)
    사용자가 웹브라우저에서 최초로 Service Provider 로 접속합니다. 아직 웹브라우저가 세션을 활성화한 상태는 아닙니다. 

  2. Redirect with SAML Authentication Request (인증 요청)
    Service Provider 는 인증요청(Authentication Request)를 생성하여 클라이언트로 전송합니다. 
    IdP URL은 SAML 메타데이타 교환 과정에서 사전 구성됩니다. SP는 iDP와 직접 통신하지는 않고, 사용자의 웹브라우저가 iDP로 인증 요청을 redirect 하도록 합니다. 

    인증 요청은 다음과 같이 보입니다. 

    HTTP/1.1 302 Found

    Location: https://pingsso.home.org:9031/idp/SSO.saml2?SAMLRequest=nZLNbtswEITveQqCd1m0pKoWYRlwYxQ1kDZK5OaQG02tYwISqXLJtH37kkra%2FBjwodflcPab3V2iGPqRr7076lv44QEdIb%2BGXiOfXmrqreZGoEKuxQDIneTt%2BusVz2aMj9Y4I01PL7abmmJWVCxnku07sYCqFAu2KGWVdaycV1AWRbnPPjJZlDkld2BRGV3TYEPJFtHDVqMT2oUSm%2BcJq5Ks2LGK5x84K%2B8p2QQ0pYWbfh2dG5Gn6aj0A6KZHc0AM2MfeACYp6ob07a9nsUEGSWfjZUwJazpQfQIsWEjENUj%2FKs0z1E%2BKd0F0%2FO5908i5F92uyZprtsdJWtEsJHu0mj0A9gW7KOS8P326oVXejkk4F94F0WRpyEBjmmkjdip6JXAEyldXSyjhE%2FDsq%2BWdJ5V%2FOWiq%2FeWy%2FSV4bP9yL8Fi%2B2mMb2Sv%2F%2FnFuK8B%2BHOq2NFdclhknJnhUYF2lHSNrH%2FjQ9DOCiwNT2ZA1n3vfl5aUG4sD5nPdDVU5K37CFQenrdqz8%3D&RelayState=s249030c0bda8e96a8086c92d0619e6446b270c463

    인증 요청을 아래와 같이 디코딩하였습니다.

    <samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"

    ID="s249030c0bda8e96a8086c92d0619e6446b270c463"

    Version="2.0"

    IssueInstant="2013-09-19T09:35:06Z"

    Destination="https://pingsso.home.org:9031/idp/SSO.saml2"

    ForceAuthn="false"

    IsPassive="false"

    ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"

    AssertionConsumerServiceURL="https://cucm-eu.home.org:8443/ssosp/saml/SSO/alias/cucm-eu.home.org">

      <saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">cucm-eu.home.org</saml:Issuer>

      <samlp:NameIDPolicy xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"

    Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"

    SPNameQualifier="cucm-eu.home.org"

    AllowCreate="true"

    />

    </samlp:AuthnRequest>


  3. GET with SAML Authentication Request (인증 요청 획득)
    클라이언트는 인증 요청을 그대로 IdP로 전송합니다. 아직 사용자의 브라우저는 IdP와 세션이 활성화되지 않았습니다. 
     

  4. Challenge for Credential (로그인 요청) 
    IdP는 사용자 웹브라우저에게 로그인을 요청합니다. 인증방식은 사용자명과 패스워드, PKI 등 다양하게 사용할 수 있습니다. 

  5. Credential (로그인 성공)
    클라이언트는 요구되는 인증방식으로 인증을 시도합니다. 이과정에서 Service Provider는 개입하지 않습니다. IdP가 LDAP 서버로 인증을 다시 위힘할 경우에는 LDAP으로 경로가 증가하지만 여기서는 생략합니다. 

  6. Signed response in hidden HTML form 
    IdP는 SP를 위해 SAML response를 생성하여 사용자의 웹브라우저로 전송합니다. SAML Response는 SAML assertion이 포함됩니다. iDP는 웹브라우저에  Session cookie (세션 쿠키)를 설정하고, 이정보는 웹브라우저에 캐싱됩니다.    

  7. POTS signed response
    클라이언트는 Service Provider 의 ACS URL에 Assertion을 포스팅합니다. 

  8. Supply Resoures (자원 접속)
    SP는 POST로 부터 SAML assertion을 추출합니다. 

 

  

마치며
SAML 2.0 Flow에 있는 프로토콜 절차는 IdP-initiated SSO일까요? SP-intiated SSO일까요? 답을 안다면 SAML 에 대한 기본적인 이해는 된 것입니다. 다음 글에서는 세션 쿠키에 대해 좀 더 자세하게 살펴보겠습니다. 나중에 개인적으로 여유가 된다면 SMAL 2.0을 CUCM에서 활성화하는 과정을 데모로 정리해보곘습니다.   



참조자료
Cisco.com : SAML SSO Deployment Guide for Cisco UC Applications 11.0  
위키피디아 : SAML 2.0
OASIS : SAML 2.0 Technical Overview 



'Web' 카테고리의 다른 글

Single Sign On (SSO)의 이해 - 1. SSO의 개요  (0) 2017.03.13
SSO 인증방식.  (0) 2017.03.13
쿠키(Cookie) 와 세션(Session) 개념  (0) 2017.03.13
bluebird 모듈의 promisify 조건은??  (0) 2017.01.11
Promise.promisify  (0) 2017.01.06
블로그 이미지

kuku_dass

,

http://www.nexpert.net/605


   글 싣는 순서
 1. SSO의 개요
 2. SAML 2.0 Deepdive (상)
 3. SAML 2.0 Deepdive (하) 



시작하며
몇 년전부터 SSO를 이용한 인증 프로세스를 도입하는 기업이 늘어나고 있습니다. SSO는 사용자들이 직접 접속하는 애플리케이션을 다루는 엔지니어들에게는 익숙하지만, 네트워크나 협업 솔루션 엔지니어에게는 생소한 단어입니다. 협업 솔루션 구축 시 SSO와 연동을 요구하는 기업이 점차 늘어나고 있으므로 엔지니어들이 SSO 연동 정도는 손쉽게 할 수 있어야 하는 시대가 되었습니다.  

이번 연재는 SSO에 대한 전반적인 기술적인 이해를 시작으로 시스코 협업 솔루션에서 어떻게 구현하였지까지 다룰 수 있도록 하겠습니다.  


SSO의 개요 
사람들이 자주 혼동하는 인증과 권한에 대해 살펴보기 위해 아래 그림을 참조 하겠습니다. 해외여행을 가면  호텔에서 체크인(Check-in)을 합니다. 리센셥의 안내원은 투숙객에게 여권(패스포트)를 요구합니다. 안내원은 여권의 사진과 당신의 얼굴을 비교하여 동일인임을 확인하고 호텔방키를 줍니다. 이제 당신은 호텔방키를 이용하여 자신의 호텔방에도 들어가고, 호텔 수영장이나 헬쓰장도 이용합니다.  즉, 체크인 이후부터는 호텔 직원들이 호텔방키를 확인하지 여권을 확인하지 않습니다.   



기술적으로 정리해 보면, 여권을 이용하여 인증을 진행하고, 호텔방키를 이용하여 권한이 부여된 것입니다.여권을 사용자명과 패스워드 조합으로 보고 호텔방키를 권한 토큰 (Authorization Token)이라 한다면, 우리는 매우 편리하게 여러 자원을 이용할 수 있습니다.  

호텔에서 단 한번의 체크인으로 호텔 내의 모든 서비스를 이용한다는 개념을 생각해 보면 사용자 편의성과 보안 측면에서 효율성을 생각할 수 있습니다. SSO를  이해하기 어려운 순간에는 호텔의 체크인 개념을 떠올리시길 바랍니다. 


SSO의 정의 
SSO는 Single Sign On의 약어로 사용자가 단 한번의 인증 절차만으로 다수의 애플리케이션에 접속할 수 있도록 해주는 인증 프로세스입니다. 정해진 시간 동안 여러 애플리케이션을 이동하더라도 사용자는 추가적인 인증을 요구 받지 않아 사용자 편의성을 증대시키고, 애플리케이션마다 인증절차를 거치지 않으므로 다수의 패스워드를 기억필요가 없으므로 보안을 강화합니다. 

SSO를 기술적으로 자세히 이해하기 위해 프레임워크를 살펴보겠습니다. 

SSO를 구현하기 위해서는 세 가지 컴포넌트가 유기적으로 동작해야 합니다.

  • RP (Relying Party) : 서비스
    사용자가 접근하려는 서비스 또는 애플리케이션으로 SP(service provider) 라고도 한다. 

  • IdP (Identity Provider) : 인증 대행
    사용자 인증을 대행한 후 이름, 역할, 주소 등의 속성과 같은 정보 (Token)를 할당한다. 


사용자는 RP에 접근할 때마다 사용자 인증을 수행하는 것이 아니라 IdP와 사용자 인증을 수행한 후 받은 토큰을 이용하여 RP에 접속합니다. 


SP initiated  SSO와 IdP initiated  SSO
SSO 를 구현하는 기본적인 방식은 두 가지입니다. 비슷하지만, 인증 요청의 시작을 어디서 하느냐의 차이입니다. 


  • IdP-Initiated SSO (Unsolicited WebSSO)
    인증 요청이 IdP에서 시작되는 페더레이션 프로세스입니다. 사용자는 IdP로 직접 로그인을 시도 한 후에 SP에 접속하을 시도하면 SAML Response을 IdP로 부터 받아서 SP의 ACS URL에 포스팅하여 접속합니다.    


  • SP-intiated SSO
    인증 요청이 SP에서 시작되는 페더레이션 프로세스입니다. 사용자는 SP에 접속하면 자동으로 인증과정이 IdP 로 리다이렉트됩니다. 사용자는 IdP가 보내는 인증 양식에 따라 로그온을 한 후 SAML Response를 
    SP의 ACS URL에 포스팅하여 접속합니다.   



SSO를 구현을 위한 프로토콜
SSO 구현할 때 일반적으로 사용하는 프로토콜은 두 가지 입니다. 

  • SAML (Security Assertion Markup Language)
    보안 도메인 간에 인증과 권한 데이타를 교환하기 위해 만들어진 프로토콜입니다. SAML 2.0은 iDP와 SP 사이에 사용자에 대한 권한 정보가 들어 있는 보안 토큰을 이용하여 보안정보를 교환하는 XML 기반의 표준 프로토콜입니다. 

  • OATH 
    권한 부여를 위한 표준 프로토콜입니다. 일반적으로 패스워드를 드러내지 않고 마이크로소프트, 구글, 페이스북 또는 트위터 계정을 이용하여 다른 웹사이트에 로그온 하려고 할때 주로 사용합니다. 

기업에서 사용하는 애플리케이션에 대한 SSO를 구현하기 위해서는 SAML을 주로 사용합니다. 시스코의 협업 애플리케이션은 SAML 2.0을 지원합니다. 


마치며
지금까지 SSO의 개요와 주요 컴포넌트에 대해 살펴보았습니다. 다음 글에서는 SAML을 기준으로 어떻게 동작하는 지를 자세히 살펴보겠습니다. 
 



라인하 유씨누스 (CCIEV #18487)  _________________________________________________________
ucwana@gmail.com (라인하트의 구글 이메일) 
http://twitter.com/nexpertnet (넥스퍼트 블로그의 트위터, 최신 업데이트 정보 및 공지 사항) 
http://groups.google.com/group/cciev (시스코 UC를 공부하는 사람들이 모인 구글 구룹스) 
http://groups.google.com/group/ucforum (UC를 공부하는 사람들이 모인 구글 구룹스) 
세상을 이롭게 하는 기술을 지향합니다. ________________________________________________________

'Web' 카테고리의 다른 글

Single Sign On (SSO)의 이해 - 2. SAML 2.0 Deepdive (상)  (0) 2017.03.13
SSO 인증방식.  (0) 2017.03.13
쿠키(Cookie) 와 세션(Session) 개념  (0) 2017.03.13
bluebird 모듈의 promisify 조건은??  (0) 2017.01.11
Promise.promisify  (0) 2017.01.06
블로그 이미지

kuku_dass

,

SSO 인증방식.

Web 2017. 3. 13. 14:54

출처 : http://mercurii.tistory.com/227


SSO의 종류

요약 :

SSO 에이전트가 대행해주는 Delegation(인증 대행) 방식과

SSO 시스템과 신뢰관계를 토대로 사용자를 인증한 사실을 전달받아 SSO를 구현하는 Propagation(인증정보 전달) 방식으로 구분된다.

 propagation 방식중 같은 도메인내에 있다면 one cookie domain sso를 이용하면 손쉽게 sso를 구현할수 있고 이때는 보안을 위해 128bit암호화 알고리즘이나 유효시간 제한등의 방법을 사용한다.


내용 :

일반적으로 사용되는 SSO 시스템은 두 가지 모델로 구분된다. SSO 대상 애플리케이션에서 사용되는 사용자 인증 방법을 별도의 SSO 에이전트가 대행해주는 Delegation(인증 대행) 방식과 SSO 시스템과 신뢰관계를 토대로 사용자를 인증한 사실을 전달받아 SSO를 구현하는 Propagation(인증정보 전달) 방식으로 구분된다.

<그림 1> SSO Delegation Model

△Delegation 방식: 대상 애플리케이션의 인증 방식을 변경하기 어려울 때 많이 사용된다. 대상 애플리케이션의 인증 방식을 전혀 변경하지 않고, 사용자의 대상 애플리케이션 인증 정보를 에이전트가 관리해 사용자 대신 로그온 해주는 방식이다. 즉 Target Server 1을 로그온 할 때 User1이 alice/alice라는 ID/ PWD가 필요하다면, 에이전트가 이 정보를 가지고 있고, User1이 Target Service 1에 접근할 때 에이전트가 대신 alice/alice ID/PWD 정보를 전달해서 로그온 시켜준다. △Propagation 방식: 통합 인증을 수행하는 곳에서 인증을 받아 대상 애플리케이션으로 전달할 토큰(Token)을 발급 받는다. 대상 애플리케이션에 사용자가 접근할 때 토큰을 자동으로 전달해 대상 애플리케이션이 사용자를 확인할 수 있도록 하는 방식이다. 웹 환경에서는 쿠키(Cookie)라는 기술을 이용해 토큰을 자동으로 대상 애플리케이션에 전달할 수 있다. 이러한 웹 환경의 이점으로 웹 환경에서의 SSO는 대부분 이 모델을 채택하고 있다.

<그림 2> SSO Propagation Model

△Delegation & Propagation 방식: 웹 환경이라고 하더라도 Propagation 방식이 모두 적용될 수는 없다. 특히 웹 애플리케이션의 변경이 전혀 불가능하고 사용자 통합이 어려운 경우 Delegation 방식을 사용하게 된다. 또한 대상 애플리케이션들이 많이 있고 애플리케이션의 특성들이 다양한 경우 각 애플리케이션에 Delegation 방식과 Propagation 방식을 혼용해서 전체 시스템의 SSO을 구성한다.

△Web 기반 One Cookie Domain SSO: SSO 대상 서비스와 응용 애플리케이션들이 하나의 Cookie Domain안에 존재할 때 사용된다. 일반적인 기업 내부의 컴퓨팅 환경이다. 통합인증을 받은 사용자는 토큰을 발급받게 되고, 이 토큰은 Cookie Domain에 Cookie로 설정되어 Cookie Domain 내의 다른 서비스로 접근할 때 자동으로 토큰을 서비스에 제공하게 된다. 서비스에서 동작되는 SSO 에이전트는 토큰으로부터 사용자 신원을 확인하고 요청된 자원에 대한 접근을 허가 해준다.

Cookie를 통한 SSO구현시 Cookie 보안 방법

토큰은 쿠키를 통해 전달되므로 외부에 노출되는 정보이다. 완벽한 보안을 위해서는 토큰이 네트워크에서 노출되어서는 안되지만, 비용 및 관리상의 이유로 허용되고 있다. 하지만 토큰을 통해 토큰이 포함하고 있는 정보까지 외부에 노출하는 것은 심각한 결함을 제공한다. 토큰의 네트워크 구간에서의 정보 노출 및 위·변조를 방지하기 위해 다음과 같은 보안기술이 사용된다.

△Data Confidentiality: 토큰은 주요 암호 알고리즘(AES, SEED)과 128bit 이상의 키로 암호화돼 보호되어야 한다. △Data Integrity: 토큰은 MAC

(Message Authentication Code) 등을 포함해 데이터의 무결성을 보장해야 한다. △Replay Attack Protection: 토큰은 사용자와 대상 애플리케이션 사이에 전달되는 인증 정보이다. 일반적으로 토큰은 네트워크에 노출되며, 노출된 토큰을 사용해 다른 사용자가 인증을 받고 들어올 수 있다(Replay Attack). 특히 웹 환경에서 이러한 문제점이 중요한 이슈로 등장하고 있다. 이러한 문제점을 근본적으로 해결하기 위해서는 토큰을 네트워크에 노출시키지 않아야 한다.
토큰을 네트워크에 노출시키지 않기 위해서는 항상 사용자와 대상 애플리케이션 사이에 암호 채널을 형성해야 하며, 이 채널을 통해 토큰을 전달해야 한다. 그러나 SSL과 같은 채널 암호를 사용하는 데에는 매우 많은 비용이 요구되어 실제로 많이 사용되고 있지는 않다.
SSL과 같은 암호채널을 사용하지 않으면서 Replay Attack이 발생할 수 있는 상황을 줄일 수 있도록 다음과 같은 보안 기술들이 사용된다.

△사용자 주소 제한: 토큰이 발행될 때 접속한 사용자 주소 정보를 토큰 내부나 토큰을 발행한 서버에서 기억함으르써 Token이 제출된 사용자 주소와 최초 발행시 기억된 주소를 비교하여 접속한 곳 이외에서의 접속을 제한할 수 있다. 사용자 주소가 업무진행 중에 자주 변경되지 않는 시스템일 경우 유효하다. 예를 들어 회사내의 인터넷 환경일 경우 사용될 수 있다. 인터넷 환경의 경우에는 사용자 주소가 특정 범위에서 자주 바뀔 수 있는 환경도 있기 때문에 불특정 다수를 위한 일반적인 인터넷 서비스에 사용하기에는 부적합하다.

△유효시간 제한: 토큰의 유효시간을 매우 짧게 줌으로써 Replay Attack에 사용될 수 있는 시간을 제한한다. 유효시간내에 임계시간(예: 유효시간의 1/2)을 넘으면 자동으로 토큰을 재 발행하여 사용자는 의식하지 못하고 서비스를 계속 사용하게 한다. 지금까지 사용자가 각 애플리케이션별로 별도의 인증을 받지 않고, 한번의 통합 인증만으로 각 애플리케이션들을 사용할 수 있도록 하는 SSO 기술에 대해 살펴보았다. 대상 애플리케이션의 수정을 최소화할 수 있는 Delegation 모델과 토큰을 생성해 통합 인증 서비스를 제공하는 Propagation 모델 그리고 양자의 장점을 조합한 Delegation & Propagation 방식을 이해한다면 현재 도입 대상 기업의 IT 인프라와 진행중인 서비스 및 애플리케이션의 특성에 적합한 EAM 시스템 도입을 결정하는데 도움이 될 것이다.

 

 URL암호화를 통한 SSO구현

가장 큰 분류로는 아마도 sso 해야할 사이트가 같은 도메인 그룹인지 여부를 파악해야 할것 같네요.
예를 들어서 aaa.xxx.combbb.xxx.com 은 둘다 xxx.com 도메인 그룹안에 포함되어 있기 때문에 이 경우 쿠키를 이용한 sso가 가능합니다.
이 같은 도메인 그룹의 경우 굳이 세션을 이용하지 않고 쿠키를 이용하는 이유는 aaa.xxx.com은 PHP로 제작된 사이트 bbb.xxx.com은 ASP로제작된 사이트..즉 각 서버의 플랫폼이 틀리다면 세션 공유가 안되는것은 잘 아실겁니다. 그래서 플랫폼에 상관없이 클라이언트에 의존되는 쿠키를 이용하게 됩니다.

반면에 서로 도메인이 틀리다면 어쩔수 없이 url로 암호화 시켜서 이리저리 주고 받으면서 sso인증 시켜줘야 합니다.

쿠키를 이용한 방법이나 url로 이용한 방법이나 서로 원리는 똑같습니다. 그래서 그나마 설명하기 쉬운 url을 이용한 방법으로 말씀드리겠습니다. 아무래도 예제를드는것이 간단할듯 -_-...

A사이트 : test.com
B사이트 : abcd.com

aaa란 유저가 A사이트에서 로그인후에 B사이트로 가게 되면 B사이트가 자동으로 로그인 되어있게 하는거잖아요.
(물론 aaa란 계정이A사이트에도 있고 B사이트에도 있어야겠지요)

이럴때 단순히 B사이이트로 자동 로그인 시키고자 한다면 아무래도 아이디/비밀번호가 필요할겁니다. 아래와 같이요

abcd.com/logijn.asp?id=aaa&pwd=1234

헌데 이 id=aaa&pwd=1234 이 부분이 url에 노출되면 상당히 위험하지요 그래서 이 부분을 암호화 시키는 겁니다. 제 홈피에 몇가지 암호화 하는 구문들이 있는데 대부분 URL 정보 노출시키고 싶지 않아서 암호화 시킨다고 적어둔 것들입니다.(제가적은것은 아니고 여기저기 돌아다니다가 -_-.. )

그래서 암호화 시키면 아래처럼 url 변하겠지요

abcd.com?/login.asp?암호된값=dpZe-==234s

요렇게 변할겁니다..(대충 적은것임 -_-) 요 login.asp 페이지에서는 아래와 같이

암호된값 = Request("암호된값")
디코딩된값 = 디코딩(암호된값)    ' 암호된값을 디코딩 함수에 넣어서 암호된값을 id=aaa&pwd=1234 형식으로 보이도록 한다.

id=aaa&pwd=1234 이 값들 보이면 다 끝난거지요..요것 그냥 split로 이리저리 분할하면 id는 aaa , pwd=1234란 값 쉽게 취할수 있습니다.


음..대충 이해가 가셨는지..위에서 설명한 암호화 함수는 그냥 일반 하나의 함수처럼 얘기했는지 제 홈피에 있는 암호화 함수는 암호할때/풀때 어떤 키값으로 풀어야 합니다. 즉 암호할때 a란 키값을 통해서 암호 시켰다면 풀때도 a란 키값으로 암호를 풀어야 합니다. 좀 더 보안상 좋지요..

암호화 방식도 좋지만 ID/pass 가 노출된다는 단점이 있기때문에, 서버가 다를경우 인증키를 생성하는 function 을
만들어서 각 서버에서 생성된 인증키를 비교하는 방식도 괜찮은 방식이겠죠.

쿠키를 이용한 방법은 잠깐 설명드리자만 aaa.com사이트에 로그인 하면 쿠키를 던져둡니다. 던진 값은 2개 입니다 하나는 키값 다른 하다는 암호화된 값 그래서 abcd.com 으로 접속하면 일단  그 abcd.com의 login.asp 페이지에서 던져진 쿠키가 있는지 검사합니다. 그래서 있다면 그 던져진 값들을 이용해서 암호푸는 겁니다. 그리곤 로그인 시키는 거지요. 



참고 URL


정리하면
SSO 는 Delegation 방식(솔루션 존재.)과 Propagation 방식(직접구현)이 있고.
Propagation 방식에는 Cookie 를 이용하는 방법과 URL 인증 방법 이 있겠다.
대부분의 SSO 는 Propagation 방식을 많이 사용하는듯하다!!

블로그 이미지

kuku_dass

,

출처 : http://hoonihoon.tistory.com/entry/%EC%BF%A0%ED%82%A4Cookie-%EC%99%80-%EC%84%B8%EC%85%98Session-%EA%B0%9C%EB%85%90



HTTP 개념

1. HTTP 는 비연결 구조로 사용자의 연결을 계속 유지 하지 않는 방식

2. 사용자의 요청에 대한 응답을 한 후 연결을 해제


아래글은 쿠키와 세션을 이해하기 쉽게 그림으로 설명 해 놓은 글입니다. 


로그인 정보나 장바구니 정보 같은 클라이언트 기반 정보들은 어떻게 저장할까?

다음 그림을 보자.

HTTP는 연결 보장을 하지 않으므로 또 다시 요청이 들어 온다면 서버는 해당 브라우저의 세션정보를 찾아줘야 할 것이다.

그런데 서버입장에서 생각해보면 서버에 요청해놓은 브라우저들이 엄청 많기에 세션정보를 어떻게 줄까?


그래서 자신의 세션 정보를 찾기 위해 자신의 세션을 증명하는 ID 가 필요하다.

보통의 사이트에서는 ID를 쿠키로 가지고 있게 된다. 

쿠키란 브라우저 상에서 저장되는 객체이다.

더쉽게 말하면 쿠키는 사용자 컴퓨터의 하드디스크에 저장되는 작은 텍스트 파일이며 세션은 서버의 메모리상에 존재하는 객체이다.

사용자 컴퓨터에서 세션정보처럼 많은 정보를 저장하기 힘든 관계로 많은 정보는 세션에 저장하고 그 세션 아이디만 web browser(사용자pc) 에 쿠키 형태로 저장을 해두는 것이다.


이런식으로 하면 세션아이디로 로그인 정보를 꺼내 줄 수 있다.

그럼 한번 이 세션아이디로 정말 로그인이 가능한지 테스트 해볼까?



일단 쿠키를 수정하기 위해 크롬 ad-on "Edit This Cookie" 를 설치한다.



원하는 사이트에 로그인 한 후 Edit This Cookie 를 실행해 보면



앵간하면 SESSION ID 라고 써있는 쿠키를 발견할 수 있을 것이다.


SESSION ID 라고 써있는 쿠키를 발견했다면 그 값을 잠시 메모장에 저장해 둔다.


그러고선 SESSION ID 를 지워보자



브라우저를 리플래쉬하면??




당연히 세션ID를 잃어버렸으니까 서버에서 자기 세션을 찾지 못하므로 서버는 이놈이 로그인 안된줄 알고 로그인 하라는 메시지를 보낼 것이다.


그렇다면 다시 아까 메모장에 저장해둔 세션 아이디를 입력하고 리프래쉬 해보자!


어떻게 되는가?




멀쩡하게 로그인 한것과 같은 화면이 되버린다.


아직 서버 안에 세션객체가 죽지 않았기 때문에


알맞는 세션 아이디만 있으면 다시 그 세션(로그인 정보와 장바구니 정보)를 물려줄 것 이다.



이쯤되면 아~ 로그아웃을 꼭 누르고 나와야 겠구나 할것이다.


왠만한 사이트의 로그아웃 기능은 쿠키의 세션아이디를 지워버리는 역할을 한다.


만약 피씨방 같은곳에서 로그아웃을 하지 않고 브라우저 창만 닫고 나온다면?


 


피씨에 저장된 쿠키의 세션 아이디값을 읽어다가 


브라우저에 쿠키를 설정하고 해당 사이트에 들어가면 그대로 로그인이 되버리므로 조심 조심


공공장소에서는 항상 로그아웃버튼을 누르고 나오시길!



[추가정리]
1. 쿠키
- 서버에 접속시 접속한 클라이언트 정보를 클라이언트에 저장한다.
-이후에 서버로 전송되는 요청에는 쿠키가 가지고 있는 정보가 같이 포함되어서 전송
-크기는 4096 Byte 이하
-클라이언트에 총 300개 까지 쿠키를 저장할 수 있다.
-하나의 도메인 당 20개의 값만 가질수 있다.
-쿠키는 이름,값,유효기간,도메인,경로 등으로 구성

사용예)
방문했던 사이트에 다시 방문 하였을 때 아이디와 비밀번호 자동 입력
팝업에서 오늘 이창을 다시 보지 않음체크

2. 세션
-웹서버 쪽 웹 컨테이너에 상태를 유지하기 위한 정보를 저장
-저장 데이터에 제한이 없음
-웹서버는 각각의 웹브라우저로부터 발생한 요청에 대하여 특정한 식별자를 우여하여 같은 브라우저인지 구별
-세션쿠키라고도 한다.
-브라우저를 닫거나, 서버에서 이 이쿠키를 삭제 했을때만 삭제가 되므로 비교적 지속성이 쿠키보다 안전하다
- 각각의 클라이언트마다 고유의 ID를 부여
-세션 객체마다 저장해 둔 데이터를 이용하여 서로 다른 클라이언트의 요구에 맞게 서비스제공

차이점은 : 
저장되는 곳이 다르다는 것.
= (쿠키는 클라이언트, 세션은 서버)
저장형태 
=(쿠키 텍스트형식, 세션은 Object형으로 메모리에 저장)
만료기간도 다르다는 것
= (쿠키는 저장시 설정하는데 설정하지 않으면 브라우저 종료시 소멸, 세션은 정확한 시점을 알 수 없음)
자원
=(쿠키는 클라이언트자원사용, 세션은 서버의 자원 사용)
용량
=(쿠키는 한 도메인에 20개, 쿠키당 4KB, 총 300개, 세션은 서버가 허용하는 한 용량에 제한 없음)

출저: 

 http://jhoonslife.tistory.com/354

http://marcof.tistory.com/16

http://bllizz.tistory.com/15

'Web' 카테고리의 다른 글

Single Sign On (SSO)의 이해 - 1. SSO의 개요  (0) 2017.03.13
SSO 인증방식.  (0) 2017.03.13
bluebird 모듈의 promisify 조건은??  (0) 2017.01.11
Promise.promisify  (0) 2017.01.06
[Redux] 내가 만든 세미나 자료 파일 (리덕스정리)  (0) 2016.11.29
블로그 이미지

kuku_dass

,

Promisify 하고자 하는 함수의 조건?


1. 마지막 전달 인자로 callback 함수를 전달 받는 함수여야 한다.


ex)

 (1) function func(arg1, arg2, callback) - O

 (2) function func(arg1, arg2, arg3)     - X


2. 1번이 성립한다는 가정하에, callback의 첫번째 전달인자가 err, 두번째가 result 여야 한다.


ex)

 (1) function callback(err, result) - O

 (2) function callback(err)         - O

 (3) function callback(result)      - X


-------------------------------------------------------------------------------------------------


const Promise = require('bluebird');


function myCallback(err, result) {

    if (err) {

        console.log('Error msg : ' + err);

    } else {

        console.log('Not Error Result : ' + result);

    }

}


function myFunc(arg1, arg2, myCallback) {

    if (arg1 == arg2) {

        myCallback(null, 'arg1, arg2 are same');

    } else {

        myCallback(new Error('Error!!!!!'));

    }

}


var promisifiedMyFunc = Promise.promisify(myFunc);


//2 ,3 not same, then 'in catch!'

promisifiedMyFunc(2, 3).then((result) => {

    console.log('in then');

}).catch((err) => {

    console.log('in catch');

});

//2 ,2 same, then 'in then!'

promisifiedMyFunc(2, 2).then((result) => {

    console.log('in then');

}).catch((err) => {

    console.log('in catch');

});



//if myCallback has not a result? only has an err ?? , can we use a myFunc by promisify's parameter ??

//let's check this!



function myCallback2(err) {

    if (err) {

        console.log('Error msg : ' + err);

    }

}


function myFunc2(arg1, arg2, myCallback2) {

    if (arg1 == arg2) {

        myCallback2();

    } else {

        myCallback2(new Error('Error!!!!!'));

    }

}


var promisifiedMyFunc2 = Promise.promisify(myFunc2);


// //in case of fail

promisifiedMyFunc2(2, 3).then(() => {

    console.log('in then');

}).catch((err) => {

    console.log('in catch');

});


//in case of success

promisifiedMyFunc2(2, 2).then(() => {

    console.log('in then');

}).catch((err) => {

    console.log('in catch');

});



블로그 이미지

kuku_dass

,

Promise.promisify

Web 2017. 1. 6. 14:22

//the below function means that wrap the given node function


//Promise.promisify({}); 이거에 리턴값은 function 이다.

//아래와 같이 wrapping 된다.

// var test = function(arg1, arg2){

//   return new Promise(executor); //(resolve, reject) => { }

// }

//

// myFunctionPromisified(2,4).then(result => { console.log('success result : ' + result)}).catch(err => {console.log(err)});


//promisify 가 무엇을 할까?

//우리의 node function의 callback 전달인자에다가, 자기가 만든걸 넣어주지않을까?


//node function 실행 -> 성공했으면 then 호출, 실패했으면 catch 호출

//promisify 가 node function의 callback 위치에다가, then 에 있는 함수를 넣거나, catch에 있는 함수를 넣거나..


//근데 아직 내부 로직을 안돌려보고 어떻게 yes or no를 알고서 callback 위치에 넣을까? 합리적 추론이 아니닷... 실패..


//그러면, 중간에 하나의 function을 더 두는거지... 예를들면?

//node function의 마지막 전달인자인 Callback은 내부 로직이 성공했으면 callback(null, result) 일 것이고

//실패했으면 callback(err) 를 호출할텐데,

//이게 약속되어있으니까..


//애초에 node function의 마지막 전달인자 callback을 promisify 측에서

//아래의 wrapCallbackFunction 형태로 바꿔치는거지..


//그래서, node function의 return값이 True 냐 false냐에 따라서, then에 등록한 callback을 실행할지, catch에 등록한 callback을 실행할지

//대신 결정해주는 거지..

//


//내가 생각하는 promisify 함수 모양새...가 그러면 어떻게 생겼을까? 한번해볼까..


//일단 전달인자는 args...., callback 을 전달인자로 받는 nodeFunction!

const Promise = require('bluebird');


function promisify(nodeFunction){

  return function (args){

    return new Promise((resolve, reject) => {

      nodeFunction(args, function(err, result){

        if(err){

          reject(err);

        }else{

          resolve(result);

        }

      });

    });

  };

};


//돌아가나 해볼까?


var myFunction = function(arg, callback){

  var result = arg * arg;


  if(result == 0){

    callback(new Error('result is zero'));

  }else{

    callback(null, result);

  }

};


const myFunctionPromisified = promisify(myFunction);


myFunctionPromisified(0).then((result) => { console.log(result)}).catch(err => {console.log(err)});



//구웃 구웃 구웃 구웃 잘 돌아가넹..


//결국 promisify 는 위와 같이 생겼을것이고,

//사용법은 위와 같을 것이다...

//오홍!

블로그 이미지

kuku_dass

,

세미나자료_Redux_161121.pptx


'Web' 카테고리의 다른 글

bluebird 모듈의 promisify 조건은??  (0) 2017.01.11
Promise.promisify  (0) 2017.01.06
[React_Redux] 내가 만든 Todo App과 Redux Sample App 프로젝트  (0) 2016.11.29
[펌] Webpack 입문 메뉴얼 링크  (0) 2016.11.09
[ 펌] Webpack  (0) 2016.11.09
블로그 이미지

kuku_dass

,

Todo_redux(myApp_reduxSampleApp).zip


'Web' 카테고리의 다른 글

Promise.promisify  (0) 2017.01.06
[Redux] 내가 만든 세미나 자료 파일 (리덕스정리)  (0) 2016.11.29
[펌] Webpack 입문 메뉴얼 링크  (0) 2016.11.09
[ 펌] Webpack  (0) 2016.11.09
Redux 인터넷 강의!  (0) 2016.11.09
블로그 이미지

kuku_dass

,

원문 : https://github.com/AriaFallah/WebpackTutorial/tree/master/part1


한글번역본 :

https://firejune.com/1798/

블로그 이미지

kuku_dass

,

[ 펌] Webpack

Web 2016. 11. 9. 19:45

출처 : https://hyunseob.github.io/2016/04/03/webpack-practical-guide/


webpack: module bundler

webpack은 모듈 번들러로, 의존성을 가진 모듈들을 다루고, 그 모듈로부터 정적인 asset을 생성한다.

– webpack 페이지 공식 설명

모듈 로더, 모듈 번들러

Node.js는 CommonJS 표준이 구현되어 있으므로 별도의 라이브러리 없이도 쉽게 코드를 모듈화 할 수 있다. 그러나 브라우저에서는 이러한 CommonJS API가 지원되지 않았으므로 모듈화를 구현하기 어렵다.

브라우저에서도 모듈화를 지원하기 위해 CommonJS 혹은 AMD(Asynchronous Module Definition) 같은 스펙을 구현한 많은 라이브러리들이 있다. 그 중 대표적으로 널리 쓰이는 것으로 RequireJS를 꼽을 수 있을 것이다. RequireJS는 모듈 로더로, <script> 태그 없이도 AMD 스타일의 모듈을 구현한 스크립트를 동적으로 로드할 수 있도록 도우는 라이브러리다.

모듈 번들러도 이와 같이 모듈 형태로 작성된 스크립트를 브라우저에서 사용할 수 있도록 해주지만, 동적으로 불러오는 모듈 로더와는 달리, 사용하고 있는 의존성 모듈들을 빌드 과정을 통해 하나의 스크립트에 포함시키게 된다. 이렇게 의존성 모듈이 포함된 스크립트를 번들이라고 부른다.

혹시나 CommonJS나 AMD에 대해 처음 들어보았다면 이 링크를 참조.

webpack

이러한 기능을 하는 모듈 번들러 중 널리 사용되는 것으로는 Browserify와 webpack이 있다. 두 도구 모두 CommonJS, AMD를 지원하지만 철학이 상이하다. 이 글은 두 도구의 차이점에 대해 다루는 글은 아니므로, 어떤 부분이 다른지 구체적으로 알고 싶다면 Browserify와 WebpackBrowserify VS Webpack - JS Drama같은 글을 참고하면 도움이 될 것 같다.

webpack은 단순한 모듈 번들러가 아니라, npm과 조합하면 사용하기에 따라서 gulp나 grunt 같은 task runner까지 대체가능하다는 점에서 매우 다재다능한 툴이다. 물론 gulp나 grunt도 webpack용 플러그인을 제공하기 때문에 조합해서 사용하는 것 역시 가능하다.

이 글에서는 webpack만을 다루며, webpack의 설치부터 기본적인 CommonJS 모듈과 Stylesheet를 포함한 빌드와 babel을 이용한 ES2015 사용 + ESLint를 이용한 Linting까지 step by step으로 소개할 것이다.

Hello, world!

먼저 webpack을 가지고 놀기 위한 놀이터 디렉토리를 만들자.

$ mkdir webpack-playground && cd webpack-playground

webpack을 사용하기 위해서는 당연히 설치가 필요하다. npm으로 간단히 설치할 수 있다.

$ npm install -g webpack

다음과 같이 빌드된 코드를 로드할 html 코드를 작성한다.

<!-- index.html -->
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<script type="text/javascript" src="bundle.js"></script>
</body>
</html>

그리고 사용할 모듈을 구현한다.

// hello.js
module.exports = 'Hello';
// world.js
module.exports = 'world';

다음으로 해당 모듈을 사용하는 컨트롤러를 작성한다.

// entry.js
var hello = require('./hello');
var world = require('./world');
document.write(hello + ', ' + world + '!');

마지막으로 webpack을 통해 해당 코드를 빌드한다.

$ webpack entry.js bundle.js

그러면 현재 디렉토리 내에 bundle.js 파일이 생성된다. 그 상태로 index.html 파일을 브라우저에서 보면 Hello, world!가 출력된 화면을 만날 수 있다.

Style

webpack은 Stylesheet도 포함해 빌드 할 수 있다. 다만 사용하려면 style-loadercss-loader 같은 별도의 loader 플러그인을 설치해야 한다.

$ npm install --save style-loader css-loader

그리고 간단한 스타일을 작성한다.

/* style.css */
body {
font-size: 24px;
color: blue;
}

그 다음으로는 아까 만들었던 entry.js 파일에 지금 작성한 스타일을 포함시킨다.

// entry.js
require('!style!css!./style.css');
var hello = require('./hello');
var world = require('./world');
document.write(hello + ', ' + world + '!');

require() 안에 들어가는 문자열이 다소 복잡한데, 파일을 로드할 로더를 설정해줘야 하기 때문이다. 아무 설정도 하지 않으면 오류가 발생한다. 덧붙여 자바스크립트 같은 경우는 확장자를 생략할 수 있지만 그 외의 파일은 확장자를 생략하면 찾지 못한다.

마지막으로 다시 빌드하고 나서 브라우저에서 다시 index.html을 열어보면 색깔과 크기가 바뀐 걸 확인할 수 있다.

여기서 끝내면 살짝 아쉬우므로 서드파티 플러그인 sass-loader까지 사용해보겠다.

$ npm install --save node-sass sass-loader

css 파일을 sass로 컨버팅한다.

// style.sass
body
font-size: 24px
color: blue

entry.js 파일을 알맞게 수정한다.

// entry.js
require('!style!css!sass!./style.sass');
var hello = require('./hello');
var world = require('./world');
document.write(hello + ', ' + world + '!');

빌드가 정상적으로 되었다면 다시 index.html을 열어 확인해보자.

Config

그런데 매번 새로운 스타일시트가 추가될 때마다 require() 함수를 저렇게 복잡하게 사용하는 건 솔직히 좀 불편하다. 따라서 webpack.config.js라는 설정파일을 사용할 수 있다.

// webpack.config.js
module.exports = {
entry: './entry.js',
output: {
path: __dirname,
filename: 'bundle.js'
},
module: {
loaders: [
{ test: /\.sass$/, loader: 'style!css!sass' }
]
}
};

설명하지 않아도 직관적으로 이해하기 쉬운 설정파일이지만 간단하게 설명하면, entry 프로퍼티로 빌드되기 전의 파일 경로를 넣어주면 되고, output에는 bundle로 만들어질 파일의 정보를 명시해주면 된다.

별도의 로더를 사용하면 module.loaders 프로퍼티에 나열해주면 된다. 각 로더는 test와 loader 프로퍼티를 가지는데 test는 대상이 되는 파일들이 매치될 수 있는 정규식을 넣어주고, loader에는 사용할 로더들의 이름을 명시해주면 된다.

이렇게 설정파일을 만들어주면 require()를 간단하게 할 수 있다.

// entry.js
require('./style.sass');
var hello = require('./hello');
var world = require('./world');
document.write(hello + ', ' + world + '!');

게다가 이제 커맨드라인 명령어도 간단하게 사용할 수 있다.

$ webpack

ES2015 with babel

최신 ECMAScript 스펙은 구현되지 않은 브라우저가 많기 때문에 프론트엔드에서 사용하기는 어려움이 따르는데 babel은 이렇게 최신 스펙은 사용하고 싶으면서도 구형 브라우저 지원도 포기하기 싫은 사람들을 위한 컴파일러이다. ES2015 이상의 문법을 사용해서 구현된 JavaScript 파일을 구형 브라우저도 지원하는 형태로 컴파일해준다. 특히 다른 컴파일러나 언어에 비해서 ES2015의 스펙을 충실히 구현하고 있다.

babel은 webpack용 플러그인 babel-loader를 제공하므로 webpack에서도 쉽게 사용할 수 있다.

먼저 npm을 통해 babel 관련 모듈을 설치한다.

$ npm install --save babel-loader babel-core babel-preset-es2015

설치가 되었으면 이 babel을 빌드과정에 포함시키기 위해 webpack.config.js를 다음과 같이 수정한다.

// webpack.config.js
module.exports = {
entry: './entry.js',
output: {
path: __dirname,
filename: 'bundle.js'
},
module: {
loaders: [
{
test: /\.sass$/,
loader: 'style!css!sass'
}, {
test: /\.js$/,
loader: 'babel',
exclude: /(node_modules|bower_components)/,
query: {
presets: ['es2015']
}
}
]
}
};

이제 빌드과정에 babel을 통한 컴파일 과정이 포함되게 되었다.
다음으로 기존의 코드를 ES2015 스타일로 수정하자.

// hello.js
export default 'Hello';
// world.js
export default 'world';
// entry.js
import './style.sass';
import hello from './hello';
import world from './world';
document.write(`${hello}, ${world}!`);

코드를 수정하고 다시 빌드해보면 빌드시간이 다소 길어졌지만 ES2015로 작성한 코드가 정상적으로 동작하는 걸 확인할 수 있을 것이다.

Lint

기왕 ES2015 사용하는 거, Lint까지 제대로 해보자. Linter로는 ESLint를 사용할 것이다.

먼저 Lint 관련 모듈을 설치한다.

$ npm install --save eslint eslint-loader

설치가 완료되었으면 Lint를 위해서 .eslintrc.js 파일을 수동으로 생성한다. eslint 커맨드라인 도구를 이용해서 쉽게 생성할 수도 있지만 여기서 사용하는 코드와 다소 안 맞는 부분이 있어 그냥 새로 만드는 것을 추천한다.

// .eslintrc.js
module.exports = {
"env": {
"browser": true,
"es6": true
},
"extends": "eslint:recommended",
"rules": {
"indent": [
"error",
2
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"always"
]
},
"parserOptions": {
"sourceType": "module"
}
};

이해하기가 어렵진 않지만 ESLint 설정 파일에 대한 설명을 짤막하게 하자면, 기본적으로 ESLint에서 추천하는 환경설정을 확장한 설정이며, 브라우저에서 동작하므로 browser 프로퍼티를 true로 주었다. 또한 모듈을 사용하고 있기 때문에 parserOptions.sourceType을 'module'로 지정했다. indent 프로퍼티의 경우 사용하는 에디터나 환경에 따라 값을 달리 주어야 할 것이다.

그 다음에는 webpack.config.js를 수정해서 Lint를 빌드하기 전에 수행하도록 한다.

// webpack.config.js
module.exports = {
entry: './entry.js',
output: {
path: __dirname,
filename: 'bundle.js'
},
module: {
preLoaders: [
{
test: /\.js$/,
loader: 'eslint',
exclude: /(node_modules|bower_components)/
}
],
loaders: [
{
test: /\.sass$/,
loader: 'style!css!sass'
}, {
test: /\.js$/,
loader: 'babel',
exclude: /(node_modules|bower_components)/,
query: {
presets: ['es2015']
}
}
]
}
};

전에 못 보던 preLoaders라는 프로퍼티에 eslint 로더가 추가된 것을 볼 수 있는데, preLoaders는 loaders 전에 실행되어야 하는 로더들을 선언하는 부분이다. babel-loader는 ES2015 코드를 컴파일 할 것이기 때문에 컴파일 하기 전에 Lint를 하기 위해서 preLoaders에 eslint를 추가한다.

이제 Lint가 제대로 되고 있는지 확인하기 위해서 규칙에 어긋나는 코드를 추가한다.

// entry.js
import './style.sass';
import hello from './hello';
import world from './world';
var foo = bar; // 에러가 발생할 것이다.
document.write(`${hello}, ${world}!`);

그리고 다시 빌드했을 때 다음과 같이 에러가 뜬다면 Linter가 잘 동작하고 있다는 것이다.

error 'foo' is defined but never used no-unused-vars
error 'bar' is not defined no-undef

외부 모듈 사용하기

webpack을 사용하면서 가지게 되는 장점 중 하나는 더 이상 bower를 통해 패키지 관리를 할 필요가 없다는 것이다. npm에 등록된 Node.js 모듈은 모두 webpack을 통해서 bundle에 포함할 수 있다.

여기서 사용할 외부 모듈은 underscore.js다. npm을 통해 설치하면 된다.

$ npm install --save underscore

모듈 import는 매우 간단하다. 원래 require()를 사용하던 것처럼 import ... from ... 구문을 사용하면 된다. import 한 뒤에는 잘 동작하는지 확인하기 위해서 다음과 같이 random() 메소드를 사용해보자.

// entry.js
import './style.sass';
import hello from './hello';
import world from './world';
import _ from 'underscore';
document.write(`${hello}, ${world}! `);
document.write(`Random: ${_.random(0, 100)}`);

다시 빌드한 뒤, 브라우저에서 실행시켜보면 새로고침 할 때마다 랜덤한 정수가 출력되는 것을 확인할 수 있다.

마치며

사실 webpack은 State of the Art JavaScript in 2016에서도 Build tool 부문에 선정된 바 있는 아주 핫한 도구다. 나의 경우엔 Redux를 공부하려고 했는데 Redux에서는 튜토리얼부터 모듈화를 적극적으로 활용하고 있기 때문에 webpack부터 공부하게 되었다. 직접 공부해보니 의존성 관리를 효과적으로 해결해 줄 수 있는 툴이라는 생각이 들었다. 최근에 회사에서 개발하고 있는 것도 그런 부분에서 어려움을 겪은 적이 있어서 더욱 와닿았다. 거기에 Stylesheet도 import가 가능하다니..! 다만 minimal한 Browserify와는 달리 많은 기능이 있으므로 러닝 커브가 다소 있다는 점이 아쉽지만, 그만큼 강력한 도구인 것은 분명하고 일단 배워두면 써먹을 수 있는 부분이 많을 것이라고 생각한다.

여기에 사용된 코드는 GitHub 레포지토리 webpack-guide에 올라가 있으니 참고하시기 바란다.

블로그 이미지

kuku_dass

,