2016년 1월 31일 일요일

Angular 2

0_Angular 2 소개


5 스타트




  1. app/app.component.ts
——————————————————
// 모든 앱은 적어도 하나의 root 컴퍼넌트를 가져야 한다.
// 이름이 보통 AppComponent 불리고, 클라이언트의 사용자 경험을
// 담당한다. 화면 일부를 제어하는 역할을 한다
// Component Angular 가장 기본적인 블럭 요소이다

import { Component } from '@angular/core';

// Component 데코레이션 함수이며 인자로 메타데이터 {..} 가짐
// —> 함수를 컴퍼넌트 클래스에 적용 
// 메타데이터는 angular 컴퍼넌트를 어떻게 만들고 사용 할지를 알려 준다.
@Component({    
  selector: 'my-app',
  template: '<h1>My First Angular 2 App</h1>'
})
// (1) selector: html에서 my-app 만나면 위치에서 AppComponent 
//      객체를 만들고 화면 출력함
// (2) template: html 컴퍼넌트의 데이터를 바인딩하거나 
//      다른 컴퍼넌트를 연결함

export class AppComponent { }
// 다른 곳에서 컴퍼넌트를 import 있도록 속성과 로직을 내보냄 





         2. app/app.module.ts
——————————————————
// 우리가 만든 앱과 기본 앵귤러 앱과의 연관성을 보여 준다
// Angular 앱은 앵귤러 모듈들과 상호 인접하게 연관된 기능들로 분해 가능하다.
// 앱은 적어도 하나의 모듈이 있어야 하며, 이것은 root 모듈이며
// AppModule 불린다
// AppComponent import해서 declarations, bootstrap 적용한다
import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent }  from './app.component';

@NgModule({
  imports:      [ BrowserModule ],  
  declarations: [ AppComponent ],
  bootstrap:    [ AppComponent ]
})
// 브라우저 내에서 앱을 실행시키기 위해 필요하다. 비슷한 것들은
// FormsModule, RouteModule, HttpModule 등이 있다.

export class AppModule { }






         3. app/main.ts
——————————————————
모듈을 로드한다고 앵귤러에게 알려야 .
브라우저에서 앱을 시작할 있거나 모바일에서 시작할 수도 있다.
이런 선택의 설정이 가능하다


import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app.module';

platformBrowserDynamic().bootstrapModule(AppModule);
// 여기에서는 브라우저에서 bootstrap했지만
// 다른 bootstrapper 함께 다중 환경에서 AppModule 시작할수도 있다
// 예를 들면, mobile device에서는 Apache Cordova NativeScript 
// 로드할 수도 있다








         4. index.html
——————————————————
html 내에서 여러가지 다양한 패키지들을 사용해도 runtime에서 손실이 없다
패키지들은 요구될 로딩되기 떄문이다

앵귤러의 실행 순서를 보면 다음과 같다.

main.ts bootstrapModule 함수를 호출 —>
AppModule 메타 데이터를 읽음 —>
AppComponent 시작 컴퍼넌트임을 인지 —>
my-app selector 발견 —>
index.html내에 my-app태그 위치를 찾음


<!DOCTYPE html>
<html>
  <head>
    <title>Angular 2 QuickStart</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="styles.css">

    <!-- 1. Load libraries -->
     <!-- Polyfill(s) for older browsers -->
    <script src="https://npmcdn.com/core-js/client/shim.min.js"></script>
    // core-js: ES2015(ES6)

    <script src="https://npmcdn.com/zone.js@0.6.12?main=browser"></script>
    <script src="https://npmcdn.com/reflect-metadata@0.1.3"></script>
    <script src="https://npmcdn.com/systemjs@0.19.27/dist/system.src.js"></script>

    <!-- 2. Configure SystemJS -->
    <script src="systemjs.config.js"></script>
    <script>
      System.import('app').catch(function(err){ console.error(err); });
    </script>
  </head>


  <!-- 3. Display the application -->
  <body>
    <my-app>Loading...</my-app>
// my-app태그는 AppComponent selector 설정되어 있었다
// 태그 사이에서 어플리케이션 뷰를 rendering한다.

  </body>
</html>




,

앱을 실행시키는 명령은 
npm start 
인데

ts js 번역하는 , server 실행하는 일을 한다.


실행 중에도 내용 변화가 있으면 실시간 반영됨(watch mode)


lib 업데이트는 package.json 주로 바꾸고,
index.html에서 css, lib 추가한다








2016년 1월 29일 금요일

Django 프레임웍

1. Frameworks


(1) html(template) 내에서 form을 통해 입력 event 발생(설정이 필요한 변수도 함께 생성)
(2) javascript로 된 event 처리 함수가 호출(form 변수들은 태그(#~) 대입 형태로 접근)
(3) event 함수 내에서 특정 url 호출
(4) urls.py에 있는 url과 views 함수 사이의 매핑에 따라 연결된 views 함수 호출
(5) views 함수에서 html(template)과 dict를 결합시켜 render할 객체 생성
 -views 함수에서 html에서 필요한 url이나 필요 변수를 dict로 만들고 이를 html로 보내서 사용함
 -만일 page를 전환 한다면(1.html에서 2.html로), view함수에서 dict를 만든 후, 이를 html과 결합할 때 1.html 대신에 2.html과 결합시켜 render 객체 생성.
(6) render 객체(HttpResponse)를 client로 리턴



2. Control flow




(1) html 내의 form은 file을 선택해 주는 file event, 웹 화면상의 객체를 선택해 주는 event 등이 다양하게 발생한다. 또 따로 설정이 필요한 변수들도 발생시킬 수 있다.
(2) event가 발생 시는 처리함수인 javascript함수를 호출하고, 호출된 함수에서는 html의 form이나 특정 id에 #(태그)로 접근하여 선택하고 설정해 준다.
(3) 그리고 폼을 django의 view 함수로 넘긴다. view 함수에서는 넘어온 form으로부터 필요한 image 소스나 data에 접근하여 필요한 가공(가공함수 호출)을 해 준 후에 이 결과를 다시 html로 넘겨준다. 이 때 dict 형태로 데이터를 넘겨준다.
(4) html은 넘어온 dict로 부터 정보를 받아 html을 완성하고 render를 실행하여 HttpResponse객체를 생성한다.


-모든 image(문서)들은 업로드 후에 url이 있는(저장 경로 있음) 위치에 저장. 또는 처음부터 url를 가지고(저장 경로를 가지는) 있다.
-데이터 가공, 처리(함수 실행에 의한)는 이 url를 통해 선택된 이미지를 입력으로 한다.




2016년 1월 23일 토요일

Django 요약

예제로 배우는 python 프로그래밍 (Django 기초: http://pythonstudy.xyz/Python/Django)

Django 소개
-----------
Django는 웹어플프레임웍(web app framework)의 하나이다. 파이썬으로 개발되었고 오픈소스이다.

가상 환경 구축
----------------
Django 설치
-----------
>pip install django
>pip install django=1.8 #특정버전 지정

Django 프로젝트 생성과 실행
---------------
>django-admin startproject myweb #새 프로젝 만듬
>python manage.py runserver #서버 실행
서버실행 시 127.0.0.1:8000 또는 8080에 접속.

Django App
----------
프로젝 하에 위치하며, 하나의 프로젝은 여러개 App을 포함할 수 있다. 잘 모듈화된 App은 여러 프로젝에서 재 사용 가능.
>manage.py startapp home # home이라는 새 App생성

views.py는 html 웹을 호출하는 역할을 한다.
간단하게 화면에 글자를 출력해 보자.

from django.shortcuts import render
from django.http import HttpResponse

# Create your views here.
def index(request):
    return HttpResponse("Hello, World!")

01.settings.py: INSTALLED_APPS 리스트에 App 추가
02.urls.py: urlpatterns 리스트에 사용할 URL 패턴 추가. 또 ^는 시작, $는 끝이므로 빈 문자열, 즉, 루트를 가리킴.


Django 뷰
---------
보통 GUI는 MVC framework인데 여기서는 MTV이다. (Model,View, Controller) == (Model, Templates, View)
장고 뷰는 HTTP Request를 받아, HTTP Response를 리턴.
Request에는 (문서) Model 데이터가 들어 있으며, Response에서는 Template html을 생성하거나 호출.


Django 템플릿
-------------
View에서 이쪽으로 전달된 데이터를 받아, 동적인 웹페이지를 만든다.
App폴더 아래에 templates 폴더 만들고 그아래 html을 위치.
여러개의 App이 한 프로젝 아래에 있는 경우에는

"App폴더/templates/App명/템플릿파일"

로 한다. 복수의 App들이 동일 이름 템플릿을 가진 경우에 대비: 템플릿을 찾을 때, 자신 App아래 템플릿을 먼저 찾는 것이 아니라, 전체 App들 템플릿 폴더들을 처음부터 순서대로 찾는다.

from django.shortcuts import render

def index(request):
    msg = 'My Message'
    return render(request, 'index.html', {'message': msg})

render함수는 index.html을 호출하며, 세번째 파라메터는 dict형태로 넘겨줌.

(템플릿 언어)
.변수: {{ }}로 둘러싸이면 변수 값이 이 위치에 치환.
.템플릿 태그: {%...%}. if, for와 같은 python명령이 내부에 온다.
{% if count > 0 %}
    Data Count = {{ count }}
{% else %}
    No Data
{% endif %}

{% for item in dataList %}
  <li>{{ item.name }}</li>
{% endfor %}

{% csrf_token %} # 해킹 공격에 대응

.템플릿 필터: 변수값을 특정 포맷으로 변형.
날짜 포맷 지정
{{ createDate|date:"Y-m-d" }}

소문자로 변경
{{ lastName|lower }}

.템플릿에서 주석
싱글 라인: {#...#}
여러 라인: {% comment %} ... {% endcomment %}



Django 모델
-----------
필드 타입: CharField, TextField, FileField, ...
ImageField(FileField의 파생클래스로 이미지 파일인지 체크)

  필드 옵션      설명
.null (Field.null): Empty 값을 DB에 NULL로 저장. DB에 Null 허용.
                    예: models.IntegerField(null=True)
.blank (Field.blank): blank=False(Required 필드). blank=True(Optional 필드).
                   예: models.DateTimeField(blank=True)
.primary_key(Field.primary_key): 해당 필드가 Primary Key.
            예: models.CharField(max_length=10, primary_key=True)
.unique (Field.unique): 해당 필드가 테이블에서 Unique함 표시. 해당 컬럼에 대해 Unique Index를 생성.
             예: models.IntegerField(unique=True)
.default (Field.default): 필드 디폴트값을 지정.
            예: models.CharField(max_length=2, default="WA")
.db_column (Field.db_column): 컬럼명은 디폴트로 필드명을 사용하는데, 만약 다르게 쓸 경우 지정한다.



DB 설정과 Migration
-------------------
Model클래스 생성 후, 해당 모델 테이블(틀)을 DB에 생성 가능.
> manage.py makemigrations # migration을 준비하는 과정. 스키마 생성 또는 수정. App내에 migrations서브폴더 생성.
> manage.py migrate # migration을 DB에 적용하는 과정
migrate후에 생기는 파일 명: App명_ModelClass명

DB 관리 쉘
> manage.py dbshell #테이블, 컬럼정보, 테이블 내용 확인 가능
sqlite> .tables #테이블 리스트 출력
sqlite> PRAGMA table_info(리스트중하나) # 테이블 컬럼정보
sqlite> select * from 리스트중하나 # (이미 입력 저장된) 테이블 데이터



Django 모델 API
---------------
일단 모델 클래스가 정의되면 장고는 데이터추가, 갱신에 관련된 다양한 API를 제공

(INSERT)
from feedback.models import *  # feedback(App명)/models.py
from datetime import datetime

# Feedback 객체 생성
fb = Feedback(name = 'Kim', email = 'kim@test.com', comment='Hi', createDate=datetime.now())

# 새 객체 INSERT
fb.save()

(SELECT)
장고는 모델 클래스에 objects라는 관리 객체를 자동 추가해 줌. 이를 통해 여러 제어 가능.
"모델클래스명.objects"로 사용함.
all(): 테이블 데이터 전부 가져옴
for f in Feedback.objects.all():
    s += str(f.id) + ' : ' + f.name + '\n'

get(): 하나의 row만 가져옴. pk(primary key)를 사용.
row = Feedback.objects.get(pk=1)
print(row.name)

filter(): 특정조건에 맞는 row가져옴
rows = Feedback.objects.filter(name='Kim')

exclude(): 특정 조건을 제외한 나머지 row가져옴
rows = Feedback.objects.exclude(name='Kim')

count(): 데이터 갯수
n = Feedback.objects.count()

order_by(): 키를 따라 정렬. -는 내림차순.
rows = Feedback.objects.order_by('id', '-createData') #id를 기준으로 올림차순, createData를 기준으로 내림차순 정렬

distinct(): 중복 시 하나만 표시.
rows = Feedback.objects.distinct('name')

first(): 처음 row만 리턴.
rows = Feedback.objects.order_by('name').first() #정렬 후 첫 요소 리턴

last(): 마지막 row 리턴
rows = Feedback.objects.order_by('name').last()

체인연결 가능
row = Feedback.objects.filter(name='Kim').order_by('-id').first()


(UPDATE)
수정할 객체 얻고 변경 필드 수정. 다시 저장.
fb = Feedback.objects.get(pk=1)
fb.name = 'Park'
fb.save()

(DELETE)
대상 row얻은 후 delete호출
fb = Feedback.objects.get(pk=2)
fb.delete()



URL 매핑
--------
어떤 프로젝 아래에 App이 여러개일 때, proj 아래 urls.py 파일에 각 App의 urls.py를 매핑하면 편리.

myWeb/urls.py
urlpatterns = [
   ...
   url(r'^feedback/', include('feedback.urls')), #'feedback/': App별 기준 url위치.
   url(r'^home/', include('home.urls')),
]

# home/urls.py
from django.conf.urls import url
from home import views

urlpatterns = [
    url(r'^contact', views.contact), #url: /home/contact, 함수:home.views.contact
    url(r'^about', views.about),
]

# feedback/urls.py
from django.conf.urls import url
from feedback import views

urlpatterns = [
    url(r'^list', views.list), #url: /feedback/list, 함수:feedback.views.list
    url(r'^add', views.add),
    url(r'^update', views.update),
]


RegEx사용. r'정규표현식'형식이고, 앞에 r(raw) prepix붙임. ^로 시작.

(..): Capture 표현. 일부 문자열 추출.

url(r'^blogs/([0-9]{4})/([0-9]{2})/$$', views.blogs_month)  #4개/2개 추출
만약 입력 URL이 "/blogs/2015/12" 이면, 이 URL은 views.blogs_month(request, '2015', '12') 과 같이 함수를 호출

Named Group:  (?P<그룹이름>pattern). 캡춰한 것에 이름붙여 전달.
url(r'^blogs/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.blogs_month)
입력 URL이 "/blogs/2015/12" 이면, 이 URL은 views.blogs_month(request, year='2015', month='12') 과 같이 함수를 호출


템플릿 확장
-----------
아래 두 파일 경우, 일단 base.html이라는 파일을 생성하고, 이 내부에서 block위치를 찾는다. 그리고, 그 위치에 index.html에 있는
<h1>{{message}}</h1>
을 치환한다.


(base.html)
<!DOCTYPE html>
<html lang="en">
<head>
     ...
</head>
<body>
     ....
     {% block content %}   # 삭제 후 index.html에 있는
     {% endblock content %} # <h1>{{message}}</h1> 부분 대입

</body>
</html>


(index.html)
{% extends "base.html" %} #첫 라인에 base.html 추가

{% block content %}
    <h1>{{message}}</h1>
{% endblock content %}



Django 폼(form)
---------------
모델 클래스로부터 폼을 자동으로 생성하는 기능.

from django.forms import ModelForm
from .models import Feedback

class FeedbackForm(ModelForm):
    class Meta:
        model = Feedback
        fields = ['id', 'name','email','comment'] #모델 필드중 일부만 폼에서 사용.



이렇게 정의 후, 뷰에서 이 폼을 사용한다.

from django.shortcuts import render, redirect
from .models import *
from .forms import FeedbackForm

def create(request):
    if request.method=='POST': # 뭐가 전달되는지 모르지만 어떤 객체가 전달된 경우를 표현
        form = FeedbackForm(request.POST) #사용자가 입력한 것을 인자로 넘겨줘서 폼 생성.
        if form.is_valid():
            form.save()
        return redirect('/feedback/list') #urls.py의 urlpatterns을 통해 view함수 호출.
    else:
        form = FeedbackForm()

    return render(request, 'feedback.html', {'form': form}) #세번째 인자는 dict()

위 코드는
. 데이터를 입력받을 폼을 보여주는 부분
. 사용자가 데이터 입력하여 저장 버턴 클릭 시, 이를 DB에 저장하는 부분.
. view에서 html로 form을 전달하였다. 그래서, 아래 html에서 이를 랜드링.

{% extends "base.html" %}
{% block content %}
    <p>
        <a href="{% url 'list' %}">Goto Feedback List</a>
    </p>

    <div>
        <form method="POST"> # 뷰에서 form을 넘겨주었었다.
            {% csrf_token %}
            {{ form.as_p }} #p태그(paragraph), 즉 새 문단으로 form 연다.
            <button type="submit">저장</button>
        </form>
   </div>
{% endblock content %}



Static 파일
-----------
장고홈(settings.py 내 BASE_DIR) 아래에 static 폴더를 만들어 js, css, image 등 웹에서 사용하는 파일 저장:
STATIC_URL = '/static/' #웹에서 주소로 살펴볼 때
STATICFILES_DIRS=[os.path.join(BASE_DIR, 'static'),]

.html에서 static 파일 사용 시는 템플릿 상단에
{% load staticfiles%}
를 명시하고, 실제 static file을 가리키기 위해서는 link태그로 지정.
{% static 'bootstrap/css/bootstrap.min.css' %}

.배포(deploy) 시에는 각 App의 static 폴더 아래 파일을 그대로 복사하므로 이름 충돌을 피하기 위해 다음 처럼 한다:
 -App명/static/App명 #각 App하의 static 폴더명 지정 시.
 -settings.py에 다음 지정.
  STATIC_ROOT = '/var/www/myweb_static' #배포용 web 경로하의 static 폴더.

  STATICFILES_FINDERS = (
     'django.contrib.staticfiles.finders.FileSystemFinder',
     'django.contrib.staticfiles.finders.AppDirectoriesFinder',
  )


.collectstatic
배포 시 흩어져 있는 static 파일 들을 모아 특정 폴더로 옮기는데, 이 때,
'manage.py collectstatic' 실행.


샘플 Feedback App
-----------
Site Deployment (Nginx)
-----------
Site Deployment (Apache)
-----------
Django 디버깅


[참고]
1. Django 기초.











2016년 1월 22일 금요일

django

$\underline{DJango\ short\ Tutorial}$

django-admin startproject tutorial #프로젝트를 만듬(이름이 tutorial)
tree
cd tutorial
./manage.py startapp community #프로젝트 앱을 만듬(app명은 comunity)
                                                       
하나의 project내에 여러개의 앱이 있는 구조이다 

./manage.py migrate # db 생성
./manage.py createsupperuser # db 관리할 계정 생성

./manage.py runserver #서버 들어가서 접속해 본다
#접속 주소 밑에 …/admin 치고 들어가서 계정정보를 넣으면 관리자 계정으로 여러 정보를 보는 것이 가능



tutorial(project)아래의 settings.py 가서 방금 생성한
app community 등록해 주어야 한다.

INSTALLED_APPS = [
    'django.contrib.staticfiles',
    'community',
]



(1) 저장할 데이터의 형태(필드) 생성


app community models.py 가서 db 저장될 데이터의 형태에 대한 정의를 주어야 한다
데이터 필드를 가진 클래스를 models에서 상속받아 작성해 준다.

class Article(models.Model):
     name = models.CharField(max_length=50)
     title = models.CharField(max_length=50)
     contents = models.TextField()
     email = models.EmailField()
     cdate = models.DateTimeField(auto_now_add=True)

새로 생성한 app comunity 관련된 db 생성해 주어야 한다
./manage.py make migrations community


community model 생성한 데이터를 반영하기 위해 db 변화가 있는지 체크해서 반영해 준다
./manage.py migrate




(2) 접속 경로 지정과 경로를 통해 호출할 함수 작성


게시판 작성이 목표이므로 사용자가 접근해서 글을 작성할수 있는 페이지가 있어야 한다. 이를 위해 
project urls.py파일에서 write라는 이름으로 접근할 있는 경로를 지정해 준다.
from community.views import *

urlpatterns = [
    …
    url(r'^write/', write, name='write'),
]

url에서 "write"이름의 경로이면 views에서 "write" 함수를 작성한다.  

이제 views.py파일에서 write 함수를 만들어 보자. url명에서 인자가 있다면 request를 통해 넘어간다. 

def write(request):
     return render(request, 'write.html')

views에 있는 write 함수는 화면에 표시할 것을 정의한다. 
여기서는 html template를 이용해서 표시를 수행한다.
따라서 community 아래에 html 파일들을 저장할 templates 폴더를 하나 생성한다(이름을 정확하게 “templates”로 적어야 한다)


views.py의 역할은 urls.py에서 받은 parameters에 맞게 출력할 data를 생성한다. 또, 템플릿을 로드하고 data와 함께 템플릿을 render한다. 




(3) 화면에 표시할 폼과 템플릿을 작성


templates 폴더 아래에 write.html 파일을 하나 만든다.
sublime에서 text 타입을 html로 설정하고 첫줄에 <html.. 라고만 치면 필요한 형식이 자동으로 생성된다.
<body>
hello django!

라고 입력하고 runserver를 다시 실행하고, 
127.0.0.1:8000/write에 접속해 보면 
hello django! 
메시지가 나타난다.




전통적인 방식으로는 write.html에서 입력받는 폼을 복잡하게 구성해야 하나, django에서는 form를 이용하여 간단하게 구성해 줄 수 있다.
community 아래에  forms.py 파일을 만든다. 

from community.models import *
class Form(ModelForm):
     class Meta:
     model = Article
     fields = ['name', 'title', 'contents', 'email', ‘cdate']

db에서 만들었던 필드명을 나열하여 form을 제작한다. 





구성한 폼클래스를 이용하여 폼 객체를 생성해야 하는데, 이것은 views에서 해준다.  또 생성한 form 객체를 html에 전달해 주어야 한다.  

from community.forms import *
def write(request):
     form = Form()
     return render(request, 'write.html', {'form':form})

forms.py의 클래스를 import하고 이를 이용해 객체 form을 생성하고 이를 html에 전달하기 위해 render내에 {…}형식을 사용해 주었다.




(4) 구성된 폼과 템플릿을 화면에 표시


write.html에서 form을 사용하기 위해 p태그를 사용하여 아래와 같이 지정한다.  또 submit 버턴도 추가한다.
<body>
<form action="" method="post">
     {{ form.as_p}}
<button type="submit" class="btn btn-primary">저장</button> 
</form>


이렇게 저장해 놓고 다시 실행해보면 폼 형태를 가진 입력창들이 웹에 표시되게 된다. 
추가로 다양한 외부 공격에 대비하기 위해 {% csrf_token %}와 같은 토컨을 추가한다.
이제 입력 창들에 값들을 입력하면 form이 값들을 받아서 db에 저장하면 되는 것이다.  





form 버턴을 누르면 request를 통해서 POST(또는 GET)가 발생하게 되고 이를 체크해서 필요한 처리를 해준다.  
메세지를 처리해 주기 위해 views.py로 가서 수정해 준다.

def write(request):
     if request.method == 'POST':
          form = Form(request.POST)
          if form.is_valid():
               form.save()
     else:
          form = Form()

     return render(request, 'write.html', {'form':form}) 



여기까지 앱에서 하나의 페이지를 작성하고 데이터를 처리하는 것을 살펴 보았다.  
——————————




이제 작성한 데이터의 리스트를 볼 수 있는 list라는 url을 만들어보자.
위와 똑같이 한다.

project 아래 urls 아래에 다음을 추가한다:
url(r'^list', list, name='list'),



views에 list함수를 만든다.



실행과 테스트

Mac을 기준으로 하면, 먼저 django가 설치된 (아나콘다 등의) 가상 환경에 들어간다.  장고의 프로젝트 폴더로 가서
./manage.py runserver

로 실행할 수 있다. 실행된 곳에 접속하기 위해 브라우저 창에
http://127.0.0.1:8000/admin
http://127.0.0.1:8000/photo/1

등의 프로젝트에 설정된 주소를 입력하여 접속할 수 있다.










2016년 1월 21일 목요일

bootstrap

0_bootstrap case



w3school youtube 한글 강의 자료를 요약하였다
start from scratch, 처음부터 차근차근 시작한다는 의미



Container
bootstrap jquery css 기반하므로 관련 파일들을 html 포함해야 한다.
헤드에 css 파일 추가, 하단에 jquery  bootstrap.js 순서지켜서 추가한다.
<div>..</div> 둘러싸고 속성으로 class=“container” 삽입한다.
<div class=“container”>…</div> 된다.


Jumbotron
Jumbotron 강조를 위한 영역 설정(그레이박스 커진 글씨체로 나타남) 사용.
<div> 속성에 class=“jumbotron” 추가 


Button
버턴을 추가할 (다른 형식도 비슷함) 속성 표시에 같은 단어가 번을 반복해서 나타남.  
class = “btn btn-default”처럼 btn btn-…형태임. 따라서,
<button type=“button” class=“btn btn-default”>  Text </button> 또는
<a href=“#” class=“btn btn-info btn-lg>Search</a> 등으로 링크 연결 버턴에 사용한다.  
btn-default, btn-info, btn-warning,..등의 다양한 버턴이 있고 차이로 용도 표시.
btn-xs, btn-sm, btn-md, btn-lg 등으로 버턴 크기를 설정



Glyphicon, 상형문자
속성 표기에는 위와 비슷하게 같은 단어가 두번 표시됨.
class=“glyphicon glyphicon-print” 프린트모양 상형문자 추가
보통 <span>태그를 사용하여 속성으로 추가하면 된다.
위의 Search 단어 앞에다 추가하면
<a href=“#” class=“btn btn-info btn-lg”>
<span class=“glyphicon glyphicon-search”></span> Search</a>

*span 태그는 자체로는 아무일도 하지 않으나, 문장 단위에서 css 스타일 지정에 사용 



Multicolumn
(row) (column) 있으며, 각각의 설정이 필요하다
설정은 class=“row” 속성으로 <div>태그로 둘러쌈.
내의 칸은 class=“col-xs-3” 등으로 사용. 전체 칸은 항상 12개이므로 개별 칸을 합쳤을 12 되어야
폭은 xs, sm, md, lg 등으로 적용 기기에 따라 나누어
<div class=“row></div> 먼저 줄을 둘러 싼다. 안에 칸에 대한 것을 넣음. 예를 들면, <div class=“col-sm-4>문자 문자 ..</div> 4 짜리 설정이므로 이런 것이 3개가 들어가면 12 된다.



Menu
html 자체가 가지는 기본 메뉴는 다음과 같다
<ul>
   <li> <a href=“#”>Home </a></li>
   <li> <a href=“#”>Menu 1</a></li>
   <li> <a href=“#”>Menu 2</a></li>
</ul>

여기에 클래스를 추가한다. <ul class=“nav nav-tabs“>이다속성에 nav 들어 갔다. nav-tabs외에 nav-pills(알약 디자인처럼 나열 표시하는 형식) 가지 타입이 있다
<li>탭에 class=“active” 주면 화면 표시 때에 메뉴가 선택 .
메뉴 밑에 메뉴도 가능한데, 부메뉴 추가할 메뉴를 고르고 아래에 <li class=“dropdown”> 사용한다.  
<span class=“caret”> 사용하는데 부메뉴 존재를 나타내는 펼침 표기인 삼각형 표시이다.


Navigation bar
페이지의 상단에 고정되어 있는 방향 바이다.
 <nav class=“navbar navbar-default”> </nav> 둘러싼다.
nav html5 태그 중의 하나이다

navbar-default 대신에 navbar-inverse 사용하면 배경 값이 역으로 되어 색다른 디자인이 .
navbar-fixed-top(bottom) 추가하면 화면 부분이 아래위로 스크롤되어도 navbar 스크롤 안되고 상단에(하단에) 고정되는 효과를 가지게 있다.


navbar-right 메뉴를 오른쪽 단에 고정.
<ul class=“nav navbar-nav navbar-right>…</ul>처럼 사용 가능


class=“nav navbar-header” class=“nav navbar-nav”속성을 사용한다.
접을수 있는 방향 바를 만들기 위해서는 navbar-header 부분 수정
<div class=“nav navbar-header>
    <button type=“button” class=“navbar-toggle” data-toggle=“collapse” data-target=“#myNavBar”>
    <span class=“icon-bar”></span>
    <span class=“icon-bar”></span>
    <span class=“icon-bar”></span>
    </button>

toggle 토글된다는 의미, collapse 접을 있음을 의미한다
# 붙은 것은 id표시고, 하단에  id myNavBar 부분과 연동.
icon-bar 표시가 3개가 있고, 화면이 좁을 때는 메뉴대신에 3개가 표시됨.





참고 만한 사이트
Bootstrap 네비게이션바, http://m.blog.naver.com/naripuru/220232331848
밑바닥부터 홈페이지 만들기, http://unikys.tistory.com/333

























2016년 1월 11일 월요일

jQuery의 기본 문법

태그 선택

html 문서 내에서 임의으 태그를 선택할 수 있다. 

$(‘li’).css(‘color’, ‘red’);

항상 $ 시작한다. $(..) 괄호가 있으므로 $ 함수이다. 따라서 $ jquery 함수라 부른다. 인자에는 다양한 값이 들어 갈 수 있으나 일반적 인자는 css 선택자이다.  
위 명령은 li라는 태그 이름을 가진 요소의 선택자이다

$(..) 함수가 실행되면 리턴한 값은 jquery 객체가 된다. 객체의 메쏘드로써 css(..)를 가지며,  요소 li 다음과 같은  작용을 한다.  

<li style=“color: red”>

즉, 실행되면 html 태그 li 스타일 속성에 색 속성을 추가해 준다


이러한 사항을 기존의 DOM 코드로 주려면 요소 선택부터 좀 더 복잡하다.
var lis = document.getElementsByTagName(‘li’); 

선택하면 된다. 수행 후, 여러 요소가 선택되어 배열로 넘어오고,
for(…) {…}

루프를 돌려 각각에 대해 필요한 처리를 해 주어야 한다.



클래스 선택

DOM 코드는
var lis = document.getElementsByClassName(‘active’);
이다. 이처럼 class명이 active 요소를 선택할 때
$(‘.active’).css(…)
해준다.


id 선택
DOM코드
var lis = document.getElementsById(‘active’);
$(‘#active’).css(‘color’, ’red’).css(‘textDecoration’, ‘underline’);
처럼 선택 후, 색은 붉게, 밑줄을 그어 준다

여기서 $(..).css(..) 실행하면 .css(..) 메쏘드이고 리턴 값이 있을 것이다. 리턴 값은 객체이다
따라서 계속해서 .css(..) 객체 메쏘드를 연속 호출해 주었다. 이를 chaining이라 부른다.



참고

[1] 생활코딩, 제어 대상을 찾기(jQuery)

2016년 1월 8일 금요일

javascript 실행 하기

1. inline 방식으로 js 구현

정보(html) 제어(js) 혼재. 유지보수가 어렵다.

<!DOCTYPE html>
<html>
<body>
    <input type="button" onclick="alert('Hello world')" value="Hello world" />
</body>
</html>

button onclick되면 alert(…) 실행함. alert부분이 js 코드임. button, onclick 등은 이미 약속된 html 속성임



2. 스크립트 방식으로 삽입

<script></script> 태그를 만들어 삽입. html 태그와 js코드의 분리가 가능하다. 

<!DOCTYPE html>
<html>
<body>
    <input type="button" id="hw" value="Hello world" />
    <script type="text/javascript">
             var hw = document.getElementById('hw');
             hw.addEventListener('click', function(){
              alert('Hello world');
        })
    </script>
</body>
</html>

html5부터는 <script type="text/javascript">부분을 <script>로만 표시해도 . var hw에서 })까지가 js코드 이고 나머지는 html임.



3. 외부 파일로 js 코드 분리

js 파일로 만들어 분리. 정보와 제어의 분리. 재활용성 강화. 재 사용시 전송량 감소 등의 잇점이 있다. 

html 파일
<!DOCTYPE html>
<html>
<body>
    <input type="button" id="hw" value="Hello world" />
    <script type="text/javascript" src="script2.js"></script>
</body>
</html>

script2.js 파일
var hw = document.getElementById('hw');
hw.addEventListener('click', function(){
    alert('Hello world');
})

html 100% html 코드만으 이루어짐
html js 만나면 화면 렌드링을 멈추고 js 파일을 다운한다. 따라서 body 뒷부분에 위치시키는 것이 유리
<script type="text/javascript" src="script2.js"></script> 부분은 script2.js파일의 다운이 완료되면 js 스크립트를 <script> </script>사이에 삽입하게 된다. 



3. script 파일의 위치

스크립트를 head 위치 넣을 있는데 body 있는 button 생성되기 전이므로 오류가 발생한다. 따라서 브라우저의 모든 구성 요소의 로드가 끝났을 js 호출되어야 한다. 만일 head부에 넣으려면 js부분은 windows.onload=function(){}으로 감싸주면 구성 요소가 로딩된 후에 js 코드가 실행되게 된다

window.onload = function(){
    var hw = document.getElementById('hw');
    hw.addEventListener('click', function(){
        alert('Hello world');
    })
}



4. 실행 방법

파일을 저장하고 chrome 파일->열기에서 오픈하면 자동실행.
js 파일만 실행 시는 Ctrl+Shift+J(windows), Comm+Alt+J(Mac)로 샐행 가능. 




참고




2016년 1월 7일 목요일

php 실행 방법

php 서버쪽에서 실행되는 스크립트다. 따라서 서버가 실행될 있는 환경이 만들어져 있어야 한다. 이를 위해 Bitnami라는 툴을 설치하여 환경을 구성해 준다
Bitnami 툴은 Apache, Mysql, Php 등의 서버에서 필요한 요소를 가지고 있어 이들의 약자로 AMP 부른다.
Windows 설치 파일은 WAMP, MAC용은 MAMP 파일을 다운받아 설치한다.  설치 방법은 여기 참고한다


설치 후에는 localhost 기본 폴더의 위치가 어디인지 확인해야 한다. 위치에 기본 페이지인 index.html가 있다.

Windows에서는 
~설치위치/mampstack-5.6.26-1/apache2/htdocs
이고, mac에서는 
/Applications/mampstack-5.6.26-1/apache2/htdocs
이다


서버 실행은 Mac에서는
cmd+space bar를 눌러 manager-osx을 찾아 런한다.  툴의 Manager-server 탭을 선택하고 MySQL db와 Apache web server를 시작한다.



테스트를 위해 localhost php_test 폴더를 만들고 아래의 demo3.html 파일과  개의 관련 파일을 만들었다. mac에서 접속 시에는 브라우저의 주소창에 
localhost:8080/php_test/demo3.html
접속하면 된다.  

파일이 3개가 필요한데 아래와 같다. 특히 php파일은 demo3.html 같은 폴더 위치에 있어야 한다. php파일의 실행은 주소창에 
localhost:8080/php_test/receivetxt.php
이지만 단독으로 실행하려면 코드 내에서 echo “hello php”로 값을 출력해 보면 된다.  


receivetxt.php (업로드 파일을 받아 처리할 서버쪽 스크립트 파일)
—————————
<?php
echo file_get_contents($_FILES["image"]["name"]);
$output = 'outputtxt.txt';
file_put_contents($\$$output, file_get_contents($\$$_FILES["image"]["name"]));
?>
내용을 화면에 출력하고 파일을 저장한다. txt파일은 저장되었지만 이미지파일은 안됨.   


sendtxt.txt (전송될 파일)
—————————
vwxyz


demo3.html (클라이언트 파일)
—————————
<html>
    <head></head>
    <body>
        <script src="//code.jquery.com/jquery-1.11.0.min.js"></script>

        <input type="file" id="image_to_upload"/>
        <p>filename : <span id="filename"></span></p>
        <p>contents : <span id="contents"></span></p>
        
        <script>
            formdata = new FormData();      
            $("#image_to_upload").on("change", function() {
                var file = this.files[0];

                $('#filename').text(file.name); // 업로드한 파일 이름 화면 표시

                if (formdata) {
                    formdata.append("image", file);
                    $.ajax({
                        url: "./receivetxt.php",  // 같은 폴더 위치. php 파일을 받음.
                        type: "POST", 
                        data: formdata,
                        processData: false,
                        contentType: false,
                        success: function(data) { 
                        // callback함수이고 서버 php 실행 에코를 data 인자로 넘겨줌
                                $('#filename').text(data); // 에코 data 화면 표시
                        }
                    });
                }                       
            }); 
        </script>

    </body>
</html>


수행 작업은 client에서 파일 sendtxt.txt 업로드해서 server 보낸다. 서버의 receivetxt.php 파일을 받아 내용을 화면에 echo한다. 만일 통신이 성공하면 echo 다시 client 전송되어 html 내에 표시된다비동기 통신에 관련된 내용은 여기에서 참고한다





References

2016년 1월 6일 수요일

PHP 기본

client가 browser에서 특정 url 주소를 입력하면 서버컴에 있는 웹서버(예를 들면 apache) 해당 html 파일을 찾아서 client 보내준다.
client컴 사용자가 브라우저에서 입력이나 파일 업로드를 실행하면 html 내의 form action부분에 명시된 php파일이 서버로 호출된다. 웹서버 자신은 php 직접처리 못하므로 하단의 php engine에게 php파일 처리를 위임한다. php 코드는 html 만들어 내고 이에 따라 동적으로 생성된 문장을 html 코드에 추가하여 이를 client 다시 보내고 browser 이를 표시한다.




html 문서 내의 "<?php" "?>" 사이에 코드를 삽입하여 사용한다.
<html>
<body>

<?php
     echo “Hello world”;
?>

</body>
</html>


var_dump(7); # 인자에 변수나 상수, 배열 등을 넣으면 타입 출력

$a=1; #변수의 선언
echo $a+1; #2
echo “<br />” #줄바꿈

$first = “coding”; #문자열 변수 선언
echo $first.” everybody”; # “.”으로  문자열 연결해줌, “coding everybody”출력 


<?php
     echo $_GET[‘id’];
?>

browser에서 http://localhost/IO_form/25.php?id=egoing으로 호출 하면 server php코드는 egoing 출력한다.   http는 hyper text transfer protocal로 전송규약이다. 


클라이언트(browser)에서 실행된 html 코드
<html>
<body>
    <form method="POST" action="4.php">
        id :  <input type="text" name="id" />
        password :  <input type="text" name="password" />
        <input type="submit" />
    </form>
</body>
</html>

서버쪽 php 코드 (위 html과 같은 위치에 있어야 함)
<?php
     echo ${\$}$_POST['id'].','.${\$}$_POST['password'];
?>

client POST방식으로 호출 시는 url 파라메터가 표시되지 않으며, php에서도 $_POST 넘어온 값을 얻는다.





조건문
and, or, true, false 같은 연산자를 사용한다.

<?php
     if(${\$}$_POST[‘id’] ===‘going’ and $_POST[‘password’]===‘1111’) {
         echo ‘right’;
     } else {
          echo “wrong”;
     }
?>


반복문
for, whie 사용한다.

<?php
     for(${\$}$i = 0; ${\$}$i < 10; $i++){
         echo 'coding everybody' . $i . "<br />";
     }
?>


for(${\$}$i = 0; ${\$}$i < 10; $i++){
    if(${\$}$i === 5){
        break;
    }
    echo "coding everybody{$i}<br />";
}





함수의 사용


<?php
function get_arguments(${\$}$arg1, $arg2=50) {
    return ${\$}$arg1 + $arg2;
}
echo get_arguments(10, 20);
echo get_arguments(20, 30);
?>



배열의 사용


<?php
$member = ['egoing', 'k8805', 'sorialgi'];
$member2 = array('egoing', 'k8805', 'sorialgi'); #v5.4이하 하위 호환성을 위해 사용
?>

<?php
function get_members(){
    return ['egoing', 'k8805', 'sorialgi'];
}

$members = get_members();

for(${\$}$i = 0; ${\$}$i < count(${\$}$members); $i++){
    echo ucfirst(${\$}$members[$i]).'<br />';   #ucfirst 첫자를 대문자로 바꾸어 출력하게 해줌
}
?>

배열 조작 명령
array_push, array_merge, array_unshift, array_splice, array_shift, array_pop, sort, rsort


연관배열은 요소접근 index 정수가 아니고 문자열인 배열
<?php
$grades = array('egoing'=>10, 'k8805'=>6, 'sorialgi'=>80);
?>

<?php
${\$}$grades = []; ${\$}$grades['egoing'] = 10; ${\$}$grades['k8805'] = 6; ${\$}$grades['sorialgi'] = 80;
var_dump($grades);
?>

연관배열의 반복작업은 foreach 사용
<?php
$grades = array('egoing'=>10, 'k8805'=>6, 'sorialgi'=>80);

foreach(${\$}$grades as ${\$}$key => ${\$}$value){
    echo "key: {${\$}$key} value:{$value}<br />";
}
?>



파일제어

복사
$file = 'readme.txt';
$newfile = 'example.txt.bak';

if (!copy(${\$}$file, $newfile)) {
    echo "failed to copy $file...\n";
}

삭제 
unlink('deleteme.txt');


읽기
$file = './readme.txt';
echo file_get_contents($file);
쓰기
$file = './writeme.txt';
file_put_contents($file, 'coding everybody');

네트웍 통해서 읽기
echo $homepage;




이미지데이터 업로드

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
</head>   
<body>
<form enctype="multipart/form-data" action="1.php" method="POST">
   <input type="hidden" name="MAX_FILE_SIZE" value="30000" />
   #서버로 전송되는 데이터 중에서 숨겨진 정의. 최대파일 크기를 제한 시킴
   <input name="userfile" type="file" />
   <input type="submit" value="upload" />
</form>
</body>
</html>



<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
</head>   
<body>

<?php

#var_dump($_FILES) #업로드 후에 넘어온 변수 확인할 있음
#exit; #테스트 이후 부분은 무시

ini_set("display_errors", "1"); #에러를 화면에 출력

$uploaddir = 'C:\BitNami\wampstack-5.4.20-0\apache2\htdocs\upload\file\\';
#서버 컴퓨터에 업로드된 전송된 이미지를 저장할 위치

${\$}$uploadfile = ${\$}$uploaddir . basename($_FILES['userfile']['name']);
#저장할 경로+파일명. basename 경로나 기타추가된 사항은 뗴어내고 파일이름 자체만 리턴하는 함수

echo '<pre>';

if (move_uploaded_file(${\$}$_FILES['userfile']['tmp_name'], $uploadfile)) {
#move~함수는 보안이나 형식 체크후 파일을 $uploadfile 위치로 옮김
    echo "파일이 유효하고, 성공적으로 업로드 되었습니다.\n";
} else {
    print "파일 업로드 공격의 가능성이 있습니다!\n";
}
echo '자세한 디버깅 정보입니다:';
print_r($_FILES);
print "</pre>";

?>

<img src=“file/<?=$_FILES[‘user file’][‘name’]?>” />
#서버의 특정경로 위치에 접근하여 파일가져와서 client 화면에 출력하기 위함.
#(?=<pattern>) pattern 존재하는지 찾아붙임

</body>



References

2016년 1월 5일 화요일

정규표현식

표현식


정규표현식은 구분자라고 불리는 특수문자로 둘러싸임:
/, #, +, %, …

/foo bar/im  #im pattern modifier라고 부르며 탐색 패턴을 변경 시켜 줌. i 대소문자 구별을 없애줌. m 여러줄에 적용.

preg_match(“/php/i”, “PHP is the web scrip”, $matches);
preg_match는 PHP에서 제공하는 함수로, 첫인자로 두번째인자를 탐색. 결과를 세번째 인자에 저장.


\b 경계 문자를 검색
/\bweb\b/i  #…the web script… 처럼 독립된 ‘web’문자를 검색. ‘website’라는 단어는 검색 못함



$\$$subject = ’coding everybody http://tutorials.org egoing@egoing.com ...’
에서 url검색하면

preg_match(‘~http://\w+\.\w+~’,…)
~ 내부에 //떄문에 경계표시를 위해 /대신 사용.  \w 문자검색, + 1 이상이니 tutorials 검색.
\. . escape시켜서 .자체를 검색.



preg_match(‘~(http://\w+\.\w+)\s(\w+@\w+\.\w)~’, $\$$subject, $\$$matches);

‘\s’ 공백을 나타내고 () capturing이라고 하며 matches에 

$\$$matches[0] = “http:… com” #전체 검색 결과 저장
$\$$matches[1] = “http://tutorial.com
$\$$matches[2] = “egoing@egoing.com


배열 형태로 부분 부분 추출이 가능.



검색


preg_match('@^(?:http://)?([^/]+)@i',"http://www.php.com/indax.html", $\$$matches);

@...@ 구분자(정규표현식의 시작과 끝). i는 대소문자 동시 매칭, ^는 경계, 즉 행의 시작점을 매칭한다는 의미,

^(..)는 괄호에 있는 항으로 시작하는 행을 검색.
()안에 있는 ?:는 원래 ()가 capturing이므로 $\$$matches[1]에 값이 들어가야 하지만 그러지 말라는 의미. 'http'는 보통 따로 저장하지 않아도 됨.
?는 수량자로 0~1개이므로 "http://"가 하나 있거나 없어도 된다는 의미.
[^/]+는 /을 제외한 어떤 글자가 1개 이상 나와야 된다는 의미.

$\$$host = $\$$matches[1]가 실행되면 $\$$host에 www.php.com이 저장됨.



preg_match('/[^.]+\.[^.]+$\$$/', $\$$host, $\$$matches);

/../는 구분자. [^.]+는 .을 제외한 어떤 문자가 1개이상 나와야 함. \.는 .의 escape니까 문자 .자체를 의미함.
다시 동일한 [^.]가 나온 후, 마지막에 $\$$가 나왔으므로 $\$$는 문자의 끝에서 매칭을 한다는 의미이다.
따라서 .을 중심으로 양쪽의 두 단어를 매칭하는데 www.php가 아니라 php.com이 얻어짐.



또 다른 예를 살펴 보면,

$\$$str = 'foobar: 2008';
preg_match('/(?P<name>\w+): (?P<digit>\d+)/', $\$$str, $\$$matches);

var_dump($\$$matches)해보면

Array
{
[0] => foobar: 2008
[name] => foobar
[1] => foobar
[digit] => 2008
[2] => 2008
}

라는 출력이 나온다.


(?P<name>\w+)에서 \w+는 문자가 1개이상 나오는 것을 매칭하는데 앞부분에 ?P<name>는 연관배열 키값으로 name 키에 매칭된 문자 값 foobar를 저장하라는 의미이다.

(?P<digit>\d+)의 \d+는 숫자가 1개이상 나오는 것을 매칭하며, ?P<digit>는 digit키에 매칭된 패턴 2008을 저장.

이것은 값 표현을 명확히 하기 위해 정수인 index 대신에 연관배열의 키 값을 사용한 것으로 back reference라고 함.





치환



$\$$string = 'April 15, 2003';
$\$$pattern = '/(\w+) (\d+), (\d+)/i';
$\$$replacement = '$\$${1}1,$\$$3';
echo preg_replace($\$$pattern, $\$$replacement, $\$$string);

preg_replace는 첫번째 인자의 패턴으로 3번째 인자에서 찾고, 2번째 인자로 대치하라는 명령이다.
$\$${1}은 April, $\$$2=15, $\$$3=2003이다.

따라서 $\$$replacement=April1,2003이 된다.




$\$$string = 'The quick brown fox jumped over the lazy dog.';
$\$$patterns = array();
$\$$patterns[0] = '/quick/';
$\$$patterns[1] = '/brown/';
$\$$patterns[2] = '/fox/';
$\$$replacements = array();
$\$$replacements[2] = 'bear';
$\$$replacements[1] = 'black';
$\$$replacements[0] = 'slow';
echo preg_replace($\$$patterns, $\$$replacements, $\$$string);


$\$$replacements="The slow black bear jumped over the lazy dog"가 된다.




$\$$patterns = array ('/(19|20)(\d{2})-(\d{1,2})-(\d{1,2})/',
                   '/^\s*{(\w+)}\s*=/');
$\$$replace = array ('\3/\4/\1\2', '$\$$\1 =');

// $\$$startDate = 5/27/1999
echo preg_replace($\$$patterns, $\$$replace, '{startDate} = 1999-5-27');


패턴과 replace가 둘다 배열이다. 먼저 3번째 인자에서 pattern배열의 1번째 인자패턴을 찾아서 replace패턴의 첫번째 배열요소로 바꾼다.

19나 20을 찾음. 다음으로 숫자 2자리. -다음에 숫자 1~2자리, 다시 -다음에 숫자 1~2자리를 찾음. 따라서 "1999-5-27"이 찾아짐.

^는 맨처음, \s는 공백, *는 0~여러개, 다음에 {..}로 중괄호가 나옴. 그 사이에 문자가 1개 이상. 다음에 \s로 공백이 0~여러개 나오고 마지막에 =이다. 따라서 "{startDate} ="가 매칭된다.




References

[1] cheet key
[2] 생활코딩
[3] ZVON
[4] http://www.pyregex.com/