The Pursuit of Happyness

반응형

테스팅


http://www.hanbit.co.kr/book/look.html?isbn=978-89-6848-225-0



- 테스트를 하는 이유

애플리케이션이 우리의 기대에 따라 지속적으로 작동하도록 보장하기 위해서!


- 테스트 시점

-- 개발 전

개발 전에 테스팅 도구를 설치하고 구성해야 한다.

-- 개발 도중

각 부분을 만들 때마다 테스트를 작성하고 실행한다. 새로운 클래스를 추가하면 그 즉시 테스트를 만들어 실행한다.

-- 개발 후

애플리케이션 출시 후 버그를 발견하면 버그 픽스를 만들어야 하며, 올바른 동작을 보장하기 위해 새로운 테스트도 함께 작성한다.

코드 수정시 그에 따른 영향을 받는 테스트 역시 수정해야 한다.


- 테스트 대상

애플리케이션을 구성하는 가장 작은 조각들 - 유닛 테스트 (미시적 관점)

전체 애플리케이션 - 기능 테스트 (거시적 관점)


- 테스트 방법

-- 단위 테스트

큰 애플리케이션에 포함된 클래스, 메써드, 함수를 고립시킨 상태로 검증하는 테스트 방식


-- 테스트 주도 개발 (TDD)

테스트 작성이 애플리케이션 코드 작성보다 선행

의도적인 실패를 통해 애플리케이션의 행동 방식을 묘사하고, 애플리케이션의 기능이 갖춰지고 나서야 성공적으로 수행된다.

목표 지향적이며 무엇을 만들어야 할지, 결과물은 어떻게 작동해야 할지를 사전에 인지할 수 있게 도와준다.


-- 행위 주도 개발 (BDD)

애플리케이션이 행동하는 방식을 이야기처럼 묘사하는 개발 방법론.

--- 스펙 BDD

인간 친화적인 언어로 애플리케이션 구현을 묘사하는 단위 테스트 유형

--- 스토리 BDD

하위 수준 구현 보다는 상위 수준 행동에 관심이 있다.


ex) PDF 보고서를 작성하고 이메일로 발송하는 애플리케이션 제작

---- 스펙 BDD : PDF 생성 클래스 메서드가 매개변수를 입력받아 올바르게 PDF 파일을 생성하는 지 검증

---- 스토리 BDD : 프로젝트 관리자 관점에서 PDF가 생성되어지는지와 email이 보내지는 지 같은 포괄적인 애플리케이션 동작을 검증

=> 둘 간의 차이는 테스트 영역이라고 생각하면 됨


- PHP 유닛

PHP 유닛 테스트 => 테스트 케이스 => 테스트 스위트

테스트 러너를 통해 테스트 스위트를 실행한다.

테스트 케이스는 PHPUnit_Framework_TestCase 클래스를 확장한 단일 PHP 클래스

컴포저를 이용하여 설치할 수 있다.


> composer require --dev phpunit/phpunit 




반응형

반응형

서버 설정 2


http://www.hanbit.co.kr/book/look.html?isbn=978-89-6848-225-0


- PHP-FRM

PHP 프로세스 풀을 관리하는 소프트웨어

PHP 프로세스는 엔진엑스 같은 웹 서버로부터 받은 요청을 처리


repository를 추가하고 패키지 매니저를 이용하여 설치한다.


 # 우분투

> sudo apt-get install python-software-properties

> sudo add-apt-repository ppa:ondrej/php5-5.6

> sudo apt-get update

> sudo apt-get install php5-fpm php5-cli php5-curl php5-gd php5-json php5-mcrypt php5-mysqlnd


# CentOS

> sudo rpm -Uvh http://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-5.noarch.rpm

> sudo rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-7.rpm

> sudo yum -y --enablerepo=epel, remi, remi-php56 install php-fpm php-cli php-gd php-mbstring php-mcrypt php-mysqlnd php-opcache php-pdo php-devel


- 엔진엑스

엔진엑스는 아파치와 유사한 웹 서버지만 더 적은 시스템 메모리를 사용한다.


-- 설치

CetnOS 의 경우 바로 앞에서 설치한 EPEL 이 추가되어 있다고 가정


 # 우분투

> sudo add-apt-repository ppa:nginx/stable

> sudo apt-get update

> sudo apt-get install nginx


# CentOS

> sudo yum install nginx

> sudo systemctl enable nginx

> sudo systemctl start nginx


-- 가상 호스트

우분투의 경우 /etc/nginx/sites-available/example.conf 

CentOS 의 경우 /etc/nginx/conf.d/example.conf 

설정을 변경한다.


반응형

반응형

서버 설정 1


http://www.hanbit.co.kr/book/look.html?isbn=978-89-6848-225-0


1. 최초 로그인

ssh 명령어를 실행해 서버에 접속한다. 처음에는 호스트를 확인하는 메시지가 표시되며 yes를 입력하고 암호를 입력하면 로그인이 완료 된다.


2. 소프트웨어 업데이트

운영체제를 최신으로 업데이트 한다.


# 우분투

>  apt-get update

> apt-get upgrade


# CentOS

> yum update


3. 사용자 계정 추가

루트 사용자의 권한을 제한 하기 위해 deploy 라는 이름의 계정을 생성해서 사용해 보자. 

우분투의 경우 adduser 를 실행하면 바로 사용자 암호를 설정하는 화면으로 넘어가지만 CentOS 의 경우 passwd 명령어로 비밀번호 설정을 별도로 해줘야 한다. deploy 유저에게 sudo 권한을 주기위해서 그룹을 설정해 주는데, 우분투에서는 sudo 그룹으로 CentOS에서는 wheel 그룹에 추가해 준다.


# 우분투

> adduser deploy

> usermod -a -G sudo deploy


# CentOS

> adduser deploy

> passwd deploy

> usermod -a -G wheel deploy


4. SSH 키 쌍 인증

ssh 서버 접속시 비밀번호 인증 대신 SSH 키 쌍 인증을 사용해서 서버 보안을 강화한다.


1). 접속할 로컬 컴퓨터 (클라이언트 PC) 에서 ssh-keygen 명령어를 이용해서 개인키 (~/.ssh/id_rsa), 공개키 (~/.ssh/id_rsa.pub)를 생성한다.

개인키는 로컬 컴퓨터에 두고 유출되지 않도록 보호해야 하며, 공개 키는 접속할 ssh 서버로 scp 등을 이용하여 복사한다.


> ssh-keygen


2). 접속할 로컬 컴퓨터에서 scp ~/.ssh/id_rsa.pub deploy@server_address: 명령어를 입력한다.

마지막에 ":" 콜론을 붙이면 deploy의 홈 디렉터리에 업로드 된다.


> scp ~/.ssh/id_rsa.pub deploy@server_address:


3). ssh 서버에서 deploy 홈 디렉터리에서 mkdir ~/.ssh  명령어를 이용하여 ~/.ssh 디렉터리를 생성한다.  


> mkdir ~/.ssh


4). ssh 서버에서 touch ~/.ssh/authorized_keys 명령어를 이용해서 authorized_keys 파일을 생성한다.


> touch ~/.ssh/authorized_keys


5). ssh 서버에서 cat ~/id_rsa.pub >> ~/.ssh/authorized_keys 명령어를 이용해서 공개키를 등록한다.


> cat ~/id_rsa.pub >> ~/.ssh/authorized_keys


6). ssh 서버에서 deploy 사용자만 ~/.ssh 디렉터리에 접근해서 authorized_keys 파일을 읽을 수 있도록 권한을 변경한다.


> chown -R deploy:deploy ~/.ssh

chmod 700 ~/.ssh

chmod 600 ~/.ssh/authorized_keys


여기까지 작업이 되었으면, 개인 키가 있는 로컬 컴퓨터에서는 비밀번호 없이 ssh로 원격 서버에 접속할 수 있다.


5. 비밀번호 인증 및 루트 로그인 비활성화

1) ssh 서버에서 /etc/ssh/sshd_config 파일을 편집하여 PasswordAuthentication과 PermitRootLogin 설정값을 찾아 모두 no로 수정한다. 주석처리 되어 있으면 주석을 풀어준다.


2) 아래 명령으로 ssh 서비스를 재시작한다.


# 우분투

> sudo service ssh restart


# CentOS

> sudo systemctl restart sshd




반응형

반응형

PHP 모범 사례 4


http://www.hanbit.co.kr/book/look.html?isbn=978-89-6848-225-0


- 스트림

파일, 네트워크, 데이터 압축처럼 공통적인 기능과 사용 방법을 공유하는 작업들을 일반화하는 수단.


-- 스트림 래퍼

고유한 프로토콜로 읽고 쓸수 있는 스트림 데이터의 유형. 다음과 같은 공통 과정을 거친다.

1. 통신 시작

2. 데이터 읽기

3. 데이터 쓰기

4. 통신 종료


형식

<scheme>://<target>

ex) 

https://www.google.com


생략시 file scheme으로 인식

ex) 

fopen('/etc/hosts', 'rb');

fopen('file:///etc/hosts', 'rb'); // "file://" 이후에 절대 경로 ("/etc/hosts" )를 적어 줘야 한다.


-- php:// 스트림 래퍼

php://stdin - 읽기 전용 PHP 스트림, CLI 에서 파이프를 통해 스크립트로 전달된 정보를 수신할 수 있다.

php://stdout - 현재 출력 버퍼에 데이터를 기록할 수 있다. (쓰기 전용으로 읽거나 탐색은 불가)

php://memory - 시스템 메모리 데이터를 읽고 쓸 수 있다. 

php://temp - php://memory 스트림과 동일하게 작동하지만 가용 메모리가 고갈되면 임시 파일에 기록된다.


-- 스트림 콘텍스트

스트림 콘텍스트를 이용하면 file_get_contents() 함수를 이용해서 HTTP POST 요청을 보낼 수 있다.


ex)


<?php

$requestBody = '{"username":"josh"}';

$context = stream_context_create( array(

    'http' => array(

        'method' => 'POST', 'header' => "Content-Type: application/json;charset=utf-8;\r\n" . "Content-Length: " . mb_strlen($requestBody), 'content' => $requestBody

    )

));

$response = file_get_contents('https://your_test_server.com/users', false, $context);


-- 스트림 필터

스트림을 전송하는 동안 데이터를 여과, 변형, 추가, 제거하기 위해 사용


ex) 마크다운 파일을 스트림으로 열어 메모리로 읽어들이면서 HTML 변환을 하는 작업

ex) 파일을 읽어서 모두 대문자로 변경해서 출력하는 작업


<?php

$handle = fopen('data.txt', 'rb');

stream_filter_append($handle, 'string.toupper');

while(feof($handle) !== true) {

    echo fgets($handle);

}

fclose($handle);





반응형

반응형

PHP 모범 사례 3


http://www.hanbit.co.kr/book/look.html?isbn=978-89-6848-225-0


- 데이터베이스

-- PDO 사용 (PHP data objects)

단일한 사용자 인터페이스로 다양한 SQL 데이터베이스와 통신 가능한 PHP 클래스 집합체

데이터베이스 구현을 추상화하고, 이를 이용하여 DBMS 타입에 관계없이 하나의 인터페이스를 통해 쿼리를 작성하고 실행할 수 있다. (DBMS 마다 다른 Query dialect 는 신경써야 한다.)


-- DB 연결 및 DSN

PHP에서 PDO 클래스 인스턴스를 생성하고 PDO 인스턴스로 PHP와 데이터베이스를 연결한다.

PDO 클래스 생성시 DB 연결 정보를 제공하는 문자열 인수를 DSN 이라고 한다.

DSN은 DB 드라이버명과 호스트명 혹은 IP 주소 그리고 포트번호, DB이름, 문자 집합 등이 포함된다.


ex)

<?php

try {

    $pdo = new PDO('mysql:host=127.0.0.1;port=3306;dbname=acme;charset=utf8', 'dbaccount', 'dbpassword');

} catch (PDOException $e) {

    echo 'fail to connect';

    exit;

} 


-- 데이터베이스 인증 정보 보관

인증 정보는 PHP 파일에 직접 넣어서 사용하면 안된다. 웹 문서 루트 외부에 있는 설정 파일로 옮겨서 PHP 파일에서 읽어서 처리한다. .gitignore 파일을 이용해서 버전 관리에서도 제외 시킨다.


-- 준비된 구문 (Statement)

Query Injection 등의 위험을 제거하기 위해서 Statement를 이용한다.


ex)

<?php

$sql = 'SELECT id FROM users WHERE email = :email';

$statement = $pdo->prepare($sql);


$email = filter_input(INPUT_GET, 'email');

$statement->bindValue(':email', $email, PDO:PARAM_STR);

$statement->execute();


while( ($result = $statement->fetch(PDO::FETCH_ASSOC)) !== false ) {

    echo $result['email'];

}



-- 쿼리 결과

PDO::FETCH_ASSOC - 배열 키는 데이터베이스 열 이름

PDO::FETCH_NUM - 배열 키는 데이터베이스 열 순서 번호

PDO::FETCH_BOTH - 배열 키는 데이터베이스 열 이름과 열 순서 번호 모두 사용 가능

PDO::FETCH_OBJ - 데이터베이스 열 이름이 객체 속성명으로 사용되는 객체 반환


-- 트랜젝션

트랜잭션은 더 이상 나눌 수 없는 데이터베이스 명령문 집합

DBMS에 따라서 지원 여부가 결정된다.


ex)

<?php

$pdo->beginTransaction();


// insert or update queries


$pdo->commit();







반응형

반응형

PHP 모범 사례 2


http://www.hanbit.co.kr/book/look.html?isbn=978-89-6848-225-0

- 비밀번호 관리

-- DB가 해킹당해도 비밀번호를 알 수 없게 평문이나 복호화가 가능한 비밀번호를 저장하지 말 것

-- 비밀번호에 제한을 두지 말 것

-- 비밀번호를 절대 이메일로 보내지 말 것

-- bcrypt로 해시화 할 것

암호화 NO, 해시화 YES!

해싱 알고리즘 (MD5, SHA1, bcrypt, scrypt ...)

bcrypt - 단점은 느린 속도. 레인보우 테이블 공격을 저지하기 위해 데이터에 솔트를 추가한다.

password_hash() 함수를 사용하면 default로 bcrypt 해싱 알고리즘을 사용한다. (참고 password_verify(), password_needs_rehash())


- 날짜, 시간, 시간대

-- 기본 시간대 지정

php.ini 파일에 date.timezone 항목을 설정한다.

혹은 date_default_timezone_set() 함수를 이용해서 설정한다.


-- DateTime 클래스

-- DateInterval 클래스, DatePeriod 클래스

DateTime 클래스를 조작하기 위해서 사용된다.


ex)


<?php

$datetime = new DateTime();    // create DateTime instance 


$interval = new DateInterval('P2W');    // Period of 2 weeks


$datetime->add($interval);

echo $datetime->format('Y-m-d H:i:s');


$dateInterval = DateInterval::createFromDateString('-1 day');

$datePeriod = new DatePeriod($datetime, $dateInterval, 3);    // 3 periods

foreach ($datePeriod as $date) {

    echo $date->format('Y-m-d') . PHP_EOL;

}



주) 예전에는 strtotime 을 이용해서 date을 변경해 가면서 for 문을 돌렸는데, 조금 더 직관적인 방법이 되었네요..



반응형

반응형

PHP 모범 사례 1


http://www.hanbit.co.kr/book/look.html?isbn=978-89-6848-225-0


입력값의 위험을 제거하고 데이터의 유효성을 검사하고 출력을 예외 처리하라!


- 입력값 위험 제거

-- 입력 데이터가 애플리케이션 저장소 계층 (DB, CacheDB 등) 에 도달하기 전에 위험을 제거하는 것이 중요

-- HTML

htmlentities() 함수나 HTML purifier 라이브러리를 이용 

-- SQL

준비된 PDO 문을 사용하여 쿼리 인젝션 공격에 대비한다.

-- 사용자 정보

filter_var(), filter_input() 함수를 이용하여 필터링 한다.


- 유효성 검사

-- filter_var() 함수를 이용하여 유효성 검사를 통해 잠재적인 DB 오류를 방지한다.


- 출력 예외 처리

-- 애플리케이션 사용자에 의해 악의적인 코드가 표시되거나 부주의하게 실행되는 것을 막아준다.

htmlentities() 사용, ENT_QUOTES 를 이용해서 따옴표 처리, 적절한 문자 인코딩을 명시 (보통 UTF-8)


반응형

반응형

모던 PHP 특징


http://www.hanbit.co.kr/book/look.html?isbn=978-89-6848-225-0


- 네임스페이스

-- PHP 코드를 운영체제의 파일시스템 디렉터리 구조처럼 가상 계층 구조로 격리시켜준다.

-- 키워드 namespace, use


- 인터페이스

-- interface 를 정의하고 implements 해서 쓸 수 있다.


- 트레이트

-- 서로 상속관계로 묶기 애매하고 인터페이스로 공통 요소를 추상화 하기도 힘든 경우 사용

-- Trait을 사용할 클래스에 필요한 변수와 메써드를 정의하고, 각 클래스에서 use 키워드를 이용해서 trait 기능을 이용한다.

ex) 영화와 게임 클래스를 만든다고 하면, 둘 간에 상속관계도 없고, 추상화 할 요소도 없다. 각 클래스에 이름이라는 속성과 setName, getName 이라는 메써드만 공통적으로 사용된다면 아래와 같이 trait을 이용할 수 있다.


<?php

trait MediaMetaData 

{

    protected $mediaName;

    public function setMediaName($mediaName)

    {

        $this->mediaName = $mediaName;

    }

    public function getMediaName()

    {

        return $this->mediaName;

    }

} 


<?php

class Movie 

{

    use MediaMetaData;

} 


<?php

$movieTest = new Movie();

$movieTest->setMediaName("Test Movie");

echo $movieTest->getMediaName();


- 제네레이터

-- Iterator 와 달리 요구가 있을 때마다 반복적으로 값을 계산해서 돌려주는 기능

-- 계산 결과를 메모리에 모두 올릴 필요가 없어 매우 효율적인 운영이 가능


아래 예제에서 arrayOld 의 경우 $length 크기의 배열을 생성해서 전달

반면에 arrayGen의 경우 foreach에서 호출시 마다 다음 값만 생성해서 전달


<?php

function arrayOld($length)

{

        $ret = [];

        for ($i = 0;$i < $length; $i++)

        {

                $ret[$i] = $i;

        }

        return $ret;

}


function arrayGen($length)

{

        for ($i = 0;$i < $length; $i++)

                yield $i;

}


$len = 100000


foreach (arrayOld($len) as $i)

    echo $i . PHP_EOL;


foreach (arrayGen($len) as $i)

    echo $i . PHP_EOL; 




- 클로저

-- 생성 당시 자신의 주변 상태를 캡슐화한 함수 (함수 포인터라고 생각하면 된다.)


<?php

$closure = function ($name) {

        return sprintf("Hi %s", $name);

};


echo $closure("Tom") . PHP_EOL;




-  PHP 를 이용하여 http 서비스 테스트 하기

명령 프롬프트에서 다음과 같이 입력하면 8000 포트로 동작하는 http 서버를 띄울 수 있다. 한번에 하나의 요청씩만 처리하기 때문에 개발 테스트 용으로는 쓸수 있으나 Production 서비스 용으로는 적합하지 않다.


php -S localhost:8000


반응형