Maven 은 Apache 재단에서 제작한 프로젝트로 Java 기반의 프로젝트 세팅에 있어서 매우 손쉽게 프로젝트 관리를 지원해주는 오픈소스 프로젝트이다. 


Maven 을 사용함으로써 얻을 수 있는 장점은 다음과 같다.


1. Library 세팅 및 관리의 용이성


2. 간소화된 빌드와 배포


3. 라이프사이클 각각에 대한 테스트 환경


4. 프로젝트 프로필 관리의 용이함


5. 프로젝트 세팅 통일성 유지



Maven 은 독자적인 LifeCycle 을 갖고 있으며, 프로젝트를 이 LifeCycle 에 따라 빌드하고 배포까지 진행할 수 있다. 

이는 Java 프로젝트에 있어서 동일한 구조의 통일성을 줄 수 있으며, 개발자는 좀 더 개발 자체에만 집중할 수 있는 환경이 된다.


Maven 은 다음 링크에서 설치할 수 있다.

https://maven.apache.org/download.cgi


Maven 은 POM(Project Object Model) 에 대한 정보를 담은 pom.xml 이라는 설정파일을 필요로하며, 이 설정파일을 토대로 프로젝트에 대한 정보를 수집하고 해석한다.


Pom.xml 은 다음과 같은 형태로 구성된다.



<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>Dummy</groupId>
<artifactId>Test</artifactId>
<version>1.0-SNAPSHOT</version>
    <dependencies>
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency>
    </dependencies>
</project>


설정의 항목들은 다음과 같다.


<project> : pom 파일의 root Element 로, pom 파일임을 validation 해준다.

<modelVersion> : POM 파일이 사용되는 버전을 나타낸다.

<groupId> : 프로젝트의 소속을 식별하는 ID 값이다.

<artifactId> : 프로젝트 빌드 결과로 나올 JAR 파일에 대한 대표이름이다.

<version> : 프로젝트 버전에 대한 정보를 기술한다. 개발용은 주로 SNAPSHOT 이라는 명칭을 붙여 버전별로 관리한다.

<packaging> : 프로젝트를 패키징할 방식을 지정한다. 주로 JAR나 톰캣에서 사용하는 WAR 형태로 패키징한다.

<name> : 프로젝트의 이름을 명시한다.

<url> : 프로젝트에 대한 정보를 참고할 수 있는 Reference 사이트가 기재된다.

<description> : 프로젝트에 대한 간략한 설명이 포함된다.


<dependencies> : 하위에 <dependency> 항목들을 포함하며 외부 라이브러리에 대한 의존성을 관리한다. https://mvnrepository.com/ 에서 Maven 에서 관리할 수 있는 Dependency 들을 참조할 수 있다.

<repositories> : 프로젝트에 대한 각종 dependency 들이 참조할 수 있는 원격 Repository 를 설정한다.

<profile> : 개발환경에 대한 프로필들을 관리한다.


프로젝트에 알맞게 Maven 설정을 하고나면, 해당 프로젝트를 Maven 의 라이프사이클 위에서 동작시킬 수 있다. 

다음은 주로 사용하는 Maven 커맨드 들의 목록이다.



- Maven 의 주요 커맨드 목록

 Maven 을 실행시키는 커맨드에는 몇가지 종류가 있다. IDE 등을 통해 코드를 작성하고 커맨드로 배포할 수 있다.


(1) mvn compile – pom.xml 이 있는 경로에서 프로젝트를 빌드하는 커맨드이다. Pom.xml 의 옵션에 따라 컴파일러의 버전도 선택이 가능하다.


(2) mvn test : test 클래스가 위치한junit의 단위 클래스를 통해 테스트하게 된다.


(3) mvn package : pom.xml 파일 내에 packaging 의 설정을 해놓은 경우, mvn package 커맨드로 패키징을 할 수 있다

 이 때, compile > test-compile > test > package 순으로 goal 이 실행된 다음 jar/war 파일이 target 디렉토리 내 에 생성된다 artifactId-version.packaging 의 명명규칙으로 배포되며 war 배포를 위해서는 maven-war-plugin 이 필요하다.


(4) mvn install : 압축한 jar 파일을 local repository에 등록하는goal로 일반적으로 package 이후 수반된다.


(5) mvn deploy : jar/war 파일을 remote repository 에 등록한다.



주로 프로젝트 환경 구축 시, Maven 커맨드를 IDE 의 빌드환경에 연동시켜서 한꺼번에 구동시키는 경우가 많다.

하지만, mvn 커맨드를 쉘에 입력해서 독자적으로 빌드도 가능하니 알아두도록 하자.



 시스템을 구성할 때, 특히 서버시스템이라면 장애(Fault) 에 대한 처리는 상당히 중요한 이슈이다. 

오랜시간동안 어떤 종류의 문제가 일어나도 문제가 일어나기 전과 후의 성능이 동일해야하며, 아웃풋 또한 동일해야 한다. 

이런 요건들이 마련되었을 때 해당 시스템을 내구력있는(Tolerant) 시스템이라 한다.


시스템을 구성하는 일부에서 fault / failure 발생해도 정상적 / 부분적으로 기능을 수행할 있는 시스템을 만들기 위해서 수행하는 복구의 종류로는 일반적으로 roll forward 복구, roll back 복구가 존재한다


Role forward 복구는 오류 발생 후의 상태에서 시스템을 복구하고 roll back 복구는 오류 발생 이전 check point 시스템을 복구한다.


Role Forward 복구와 Role Back 복구 모두, 복구 point 에러 발생 지점 간의 멱등성{몇번 실행해도 1 실행과 결과가 동일함이 보장되는 성질} 만족해야 한다.


이를 구현하기 위해서는 다음과 같은 테크닉이 주로 이용된다.



(1) 이중화 시스템

-  Replication :  동일 시스템을 복수로 준비하여 병렬로 실행시켜 다수를 만족한 결과를 올바른 결과로 적용한다.

-  Redundancy : 동일한 시스템을 복수로 준비하여 장애 발생시 보조 시스템으로 전환한다.

-  Diversity : 같은 사양에 다른 H/W 시스템을 복수로 준비하여 운용한다.

( 경우 시스템은 같은 장애를 만들어내지 않는다.)


(2) 동작지속 고장나도 지속적으로 동작해야 하며 복구 작업 성능 간섭이 없어야 한다.


(3) 고장분리 시스템이 고장과 분리되어 정상적인 구성요소에 영향을 주지 말아야 한다.


(4) 고장전염 고장이 전염되지 않게 유의해야 한다.



(1)번과 (3)번 원칙은 주로 설계에 대한 내용들이며, (2)번과 (4)번은 구현에도 직접적인 연관이 있는 편이다. 

서비스마다 차이가 있겠지만, 가능한 발생할 수 있는 "장애"의 단위를 격리시키고, 정상적인 결과를 낼 수 있는 Flow 를 고안하면서(설령 Error 가 Report 되더라도) 동시에 처리할 수 있는 "백신"을 모듈화시키는 것이 중요하다.



 첫 회사에 입사해서 배운 개념이다. 글로벌 서비스를 하는 회사였기 때문에 필수적인 용어였고, 마침 OJT 첫 과제가 내부에서 사용할 수 있도록 CDN 을 구축해보는 것이었다.


 CDN컨텐츠를 효율적으로 전달하기 위해 여러 노드를 가진 네트워크에 데이터를 저장하여 제공하는 시스템이다

CDN 의 사용 목적은 거리가 멀어 물리적으로 네트워크 비용이 많이 요구되는 곳에도 데이터를 효과적으로 전달하기 위한 목적으로 구성된다.


주로 ISP에 직접 연결되어 데이터를 전송하여 컨텐츠 병목을 피할 수 있는 장점이 있다. CP들은 CDN 측에는 사용료를, CDN ISP 측에 호스팅 비용을 지불한다. 최신 트랜드는 P2P 기술을 서버와 같이 이용하는 하이브리드 모델을 사용하는 것이다.


 주로 웹요소(텍스트, 그래픽, 스크립트), 다운로드 가능한 파일들, 어플리케이션들이 주로 사용되며, 갱신과 전달이 빈번한 메시지 형태의 데이터나 로그성 데이터가 사용되는 경우도 있다.


사용자와 지리적으로 가까운 노드에 서버를 두는데 있어 주로 Content 캐싱 방식을 이용하며 Static caching 은 사용자의 요청이 없어도 리소스를 저장해 놓는 방식이고 Dynamic caching 은 사용자의 요청을 받으면 Origin server로부터 컨텐츠를 받아서(Cache fill) 캐시에 저장한다. 

주로 저장하는데 있어 TTL(Time To Live) 이 정의되어 있고 이는 상황에 따라 삭제되거나 계속 유지될 수도 있다.


 특히 회사에서 글로벌 서비스를 진행한다면 CDN 의 사용은 필수적이다. 많은 웹 기반 서비스들이 대용량의 리소스를 서버 자체에서 핸들링 하는 경우는 드물며, 지리적 이점 & 네트워크 효율성 및 안정성을 이유로 CDN 을 사용한다.

솔루션으로써 가장 훌륭한 평가를 받는건 Akamai 의 솔루션으로 생각된다.





 Sharding 이란 RDB에서 대량의 데이터를 처리하기 위해 분산 서버에 데이터를 나누어 파티셔닝하는 기술로, 근래에 탄생하는 웹서비스의 경우 필수적으로 고려해야하는 기술이다. 


 샤딩은 DBMS레벨에서 데이터를 나누는 것이 아니라 데이터베이스 자체의 Access Routing 을 분할하기 때문에 Application 레벨에서 처리되어야 하는 문제이다. 최근에는 이를 공통화시키고 그 자체로 서비스화를 위해 미들웨어로 이식하는 추세에 있다.


 샤딩에는 Partition 기법에 따라 주로 쓰이는 몇가지 기법이 있다.


(1) Horizontal Partitioning


 Schema 가 같은 데이터를 두개 이상의 DB에 나눠 처리하는 방법이다. 가장 간단한 방식의 분할 법이고, Partitioning 정책으로 range based 방법을 사용한다면 단순한 구조로 시스템을 구축할 수 있다. 단, 여러 DB에 대해 파티셔닝을 적용 시, 데이터베이스 JOIN 문제나 Consistency, Replication 등 복잡한 문제가 많이 생기게 된다. 따라서 적용시 가능한 DB를 단순하게 설계한다.



(2) Vertical Partitioning


 테이블 별로 서버를 분할하는 방식이다. 예를들어 프로필 관리용, 사진 관리용 서버 등으로 나누어 데이터를 용도에 맞게 분할하며 구현이 간단하고 전체 시스템에 큰 변화를 주지 않아도 된다. 단 서버의 데이터가 점점 거대해지면 추가 샤딩이 필요하므로 구조를 잘 잡아놓아야 한다.



(3) Range Based Partitioning


 하나의 feature 또는 테이블이 점점 거대해지는 경우 서버를 분리한다. 범위에 따라 데이터를 나누기 때문에 단순하지만 데이터를 분할하는 기준이 예측 가능하고 명확해야 한다.



(4) Key / Hash Based Partitioning


 엔티티를 해쉬 함수에 넣어서 나오는 값을 키로 데이터를 샤딩하는 기법이다. Modular 함수가 주로 사용되며 데이터가 균등하게 분포될 수 있도록 해쉬함수를 정해야 한다. 이 방법의 단점은 Scale out 시 해쉬 함수를 변경하는 작업이 매우 비싸다는 것이다.



(5) Directory Based Partitioning


 DB와 별개로 추상화된 룩업테이블을 만들어 샤드 키에 해당하는 데이터를 Cache해서 찾는 식으로 구현된다.



 샤딩 적용시에는 몇가지 고려할 점이 있다.


- 데이터 재분배(Rebalancing) : 서비스 정지 없이 Scale out 이 가능한지를 고려해야 한다.


- 데이터 조인 : Sharding DB 간의 조인이 불가능하므로 역정규화를 감안하여 설계해야 한다. 데이터 중복은 대용량처리에 대한 tradeoff이다.


- Partitioning 기법 : 어떤 식으로 Data를 나눌 건지를 고민해야 한다. 이는 Scalable 한 아키텍처를 위한 중요한 요소이다.


- Global Unique Key : DB간 사용하는 키의 Uniqueness 를 고려해야 한다.


- Compact Table : 테이블의 단위를 가능한 작게 유지해야 한다.




 HTTP 는 TCP 기술 스택 위에 놓인 Spec 기반의 프로토콜이며, Http Spec documentation 에서 정의한 스팩 대로 프로토콜이 인코딩, 디코딩 된다. 

그 중에서도 면접 단골 질문이자 자주 헷갈리는 부분은 GET 과 POST 간의 차이점이다. 또 많은 엔지니어들이 오해하고 있는 부분이기도 하다.


 HTTP Specification 에 따르면 GET과 POST 에는 몇가지 명확한 차이점이 있다.

먼저 GET 메서드는 idempotent하다. 이 말은, 동일한 작업을 어떠한 부작용 없이 여러 번 계속할 수 있다는 것이며 이 때 동일 요청은 동일한 응답을 가져야 한다는 의미는 아니다.


 HTTP 1.1 스펙에 따르면 GET, HEAD, PUT 메서드가 idempotent 하다.

반면에 POST로 전송되는 BODY는 되돌릴 수 있는 성질의 것이 아니다. 


 HTTP GET 메서드는 key/value 형태의 Query Parameter 가 URL에 담겨 전송되며, 이와 반대로 POST는 request body 에 담겨서 전송된다.

가령 같은 파라미터를 전달하는 요청에 대해 프로토콜은 다음과 같이 메시지를 전송한다.


GET > /url?param1=value1&param2=value2


POST> POST /url HTTP/1.1

      HOST: server

      … (기타 headers)

Content-Type: ~


//주로 JSON 형태로 주고받으므로 JSON 을 예시로 표시

    {

"Param1": value1,

"Param2": value2

    }



 보안에 있어서 GET보다 POST가 뛰어나다고 할 수는 없다. 두 메서드는 보안에 크게 차이가 없으며 보안은 사실상 SSL이 Security Layer에서 전담한다고 봐도 무방하다. 

굳이 따지자면, url 과 parameter 에 전달값이 그대로 노출되는 GET 방식보다, Request Body 에 숨겨져서 노출되지않는 POST 방식이 조금 더 보안에 안전하다고 할 수 있다.


또한 GET 은 담을 수 있는 전송데이터의 길이 상의 제약이 있다. url + parameter 를 전부 포함해서 255자(HTTP/1), 2048자(HTTP/1.1 ~) 로 제한된다. 반면 POST 는 전송 데이터의 길이상 제약이 없다.


 GET 방식은 Browser가 일반적으로 url에 요청할 때 사용하는 방식이다.

가령 Browser Cache 는 기본적으로 GET 요청과 응답에 대해 캐시 내에 저장하는 로직을 지닌다. 반면 POST 와 같은 요청은 브라우저 레벨에서 캐싱하지 않는다. 그 외에 Crawler Bot 등도 웹문서의 수집을 위해 HTTP GET 을 바탕으로 동작한다.

이 때문에, 예기치못한 무분별한 서버로직의 변경을 막을 수 있다는 측면에서 서버 사이드를 수정하는 동작은 GET이 아닌 POST 를 권장한다. 


 RequestBody에 대해 Http Specification 에서는 별 언급이 없지만 GET에도 RequestBody가 수반될 수 있다. 많은 엔지니어들조차 오해하는 부분인데, 원칙적으로 Request Body를 갖지 못할 이유가 없다. 이유는 HTTP 자체도 결국 TCP 위에서 설계된 텍스트 기반 프로토콜이기 때문이다. (HTTP 프로토콜 관련 포스팅을 참조해보자. )


링크 : http://jins-dev.tistory.com/entry/HTTP-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C%EA%B3%BC-%EC%9B%B9%EC%84%9C%EB%B2%84%EC%9D%98-%EA%B5%AC%ED%98%84?category=760047


 하지만 많은 브라우저 및 웹 프레임워크들이 이를 기본적으로 지원하지는 않는 경우가 많고 그말인즉슨, 이는 정식 스펙이 아니기 때문에 권고되는 사항은 아니다. 하위호환성 및 통신에 있어서 많은 불편함이 수반될 것이다.




 실무에서 REST API 설계를 처음 다뤄보았을 때, 부족한 점들이 많이 있었다. 

많이 혼나고 나서 너무나도 좋은 글을 찾아 정리해두었다.


원본 출처는 다음과 같으며, 아래 내용은 한글로 알아보기 쉽게 정리해 놓은 부분이다.

(https://blog.mwaysolutions.com/2014/06/05/10-best-practices-for-better-restful-api/)



 RESTful Service API 의 설계 업무를 하던 중 처음에 칭찬받아서 대충 설계하다가 다시 공부하게 된 내용.

일반적으로 커뮤니케이션과 개발에 원활한 설계를 하기 위한 10가지의 방법이 존재하며 다음과 같이 서술된다.


(1) Use nouns but no verbs (동사가 아닌 명사를 통한 API 설계를 할것.)


- 가장 초보적인 실수가 CRUD 에 대한 기능을 Path에 명시하는 것이다.

 예를들어 /getAllCars, /createNewCar, /deleteAllCars 와 같은 방식인데, REST 디자인을 위해서는 CRUD 를 Method 를 이용하며 표현하기 때문에 그 외의 것은 전부 entity를 명시하는 명사로 표기한다.


 따라서 예시는 다음과 같이 HttpRequest Method를 통해서 기능이 구분되어야 한다.




 이 원칙은 표준 웹의 REST API 대부분에서 따르는 원칙이며, GET / POST / PUT / DELETE 외의 Method 들, 가령 HEAD 나 TRACE 등, 의 경우 별도의 원칙을 갖고있지 않다.



(2) GET method and query parameters should not alter the state


 역시 위에 명시한 바와 마찬가지로 모든 Functioning 은 Method로 하고 query parameter 역시 state change 등의 기능으로 사용하지 않는다.

(ex) GET /users/811?activate or GET /users/811/activate ---         (X)



(3) Use plural nouns


 path는 entity 항목을 나타내므로 복수형을 쓰고 특정하게 지정하고 싶다면 다음 Path에서 {항목} 으로 지정해준다.

(ex) GET /cars/{carName}            -- cars 와 같이 path 는 복수형. 뒤에 {carName} 으로 단수 객체 모델을 가져온다.



(4) Use sub-resources for relations


 리소스는 관계에 따른 계층형 구조를 유지한다.


(ex) GET /cars/811/drivers/ > 차811 의 드라이버 리스트를 반환한다.

(ex) GET /cars/811/drivers/4 > 차 811의 4번 드라이버 정보를 반환.



(5) Use HTTP headers for serialization formats


 서버와 클라이언트 간 커뮤니케이션에 있어서 Header Serialization format을 이용한다.



(6) Use HATEOAS


 HATEOAS 란 Hypermedia As The Engine Of Application State 의 약자로, 하이퍼텍스트 링크를 데이터 내에 명시할 경우 API를 통해 갈 수 있는 navigator 형태로 표기해준다.


<HATEOAS 의 예시. links 하위의 href 를 주목하자>


 위의 예시에서 drivers.links.href 항목은 주소를 저장함으로써, 외부주소에 저장된 리소스 자체를 가리키고 있다.

REST API 는 이런식으로 실제 리소스를 담는 것이 아닌 경로를 담아서 HyperMedia 컨텐츠를 기록한다.


 이렇게하면 네트워크 비용도 절감되고, 성능면에서도 많은 이점을 갖는다. 물론, 해당 주소로 다시 접근하여 실제 리소스를 받아와야 하는 점이 있지만, 오히려 리소스를 CDN 등을 이용한 static 저장소에 두고 가져온다면 네트워크 비용 측면에서도 훨씬 저렴하고 뛰어난 성능으로 핸들링할 수 있다.



(7) Provide filtering, sorting, field selection and paging for collections


 Filtering 은 특수한 query parameter 를 조건으로 주었을 때 해당 기능을 제공할 수 있게끔 하는 것이다.

(ex) GET /cars?color=red


 Sorting 은 오름차순, 내림차순의 정렬을 multiple field 에 대해 허용하는 것이다. 

(ex) GET /cars?sort=-name, +price > -(ascending) +(descending)


 Field selection 은 select 조건문과 같이 필드만 추출해서 보여줄 수 있도록 하는것으로, 네트워크 트래픽 관리에 효과적이다. 

(ex) GET /cars?fields=name, price


 Paging 은 Offset과 Limit 을 지정할 수 있는 조건으로 DB 조회에 필수적인 기능이다. (ex) GET /cars?offset=10&limit=5



(8) Version your API


 REST API는 Version 을 명기한다. (ex) /blog/api/v1



(9) Handle Errors with HTTP status codes


 에러는 별도의 로직으로 처리한다기 보다 HTTP 상태 코드와 메시지를 통해 처리하는 것이 좋다. 다음은 서버에서 반환하는 표준 HTTP 응답 코드를 설명한다.



에러에 따른 Payload 도 다음과 같이 명시한다.




(10) Allow overriding HTTP method


 몇몇 프록시 서버의 경우 POST와 GET 만을 지원하는 경우가 있다.

이러한 한계점을 지원하기 위해 API 단에서 override HTTP method 를 지원해줄 필요가 있다. X-HTTP-Method-Override 와 같은 Custom HTTP 헤더를 만들어 method 방식을 지원하자.






HTTP는 현재 세계에서 가장 널리 쓰이는 프로토콜 중 하나이다. 우리가 보통 사용하는 인터넷을 위한 기본 프로토콜이기도 하며, 최근래에는 다루는 기술도 비약적으로 발전하여, 예전에 웹의 영역이 아니라고 불렸던 게임 영역, 실시간 대용량 처리, 대용량 메시지 처리 등에서도 HTTP 기반의 웹 스택을 사용하는 경우를 흔히 몰 수 있다.


HTTP TCP 계층의 위에 HTTP 프로토콜 스택을 쌓아올린 Network Layer로 현재 가장 널리 사용되고 있는 Stateless / Connectless 형식의 프로토콜이다.


 여기서 Stateless 란, TCP와 다르게 상호간의 연결된 소켓이 연결을 유지하지 않는다는 의미이다.

 즉, 서로 요청과 응답만 처리하고 "상태" 는 기록하지 않는다. 서로 간에 지속적인 "연결" 이 유지되지 않기때문에 지속적이면서 연속적인 통신에는 적합하지 않다. 일반적으로 채팅 서비스를 구현할때 HTTP가 고려되지 않는 이유이기도 하다.


 대신에 단순한 정보 전달에 있어서는 가장 효율적인 프로토콜이라고 봐도 무방하다. 요청한대로 응답만 보내주면 되기 때문에, 서버 입장에서도 연결 관리에 대한 부담이 덜어지고, 클라이언트 측면에서도 원하는 정보만 얻을 수 있으므로 효율적이다.


 HTTP 역시 네트워크 프로그래밍이기 때문에 당연히 소켓을 이용하여 통신을 하게 되며, HTTP를 서비스하는 웹서버는 특유의 성질을 구현하기 위하여 일반적인 TCP 서버 등과는 다른 HTTP 프로토콜에 특화된 형태를 취하게 된다.


소켓을 이용하여 RAW한 방식으로 HTTP 웹서버를 구축할 때는 연결이 시작된 이후에(Accept) 바로 해당 소켓의 연결이 요청(Request)을 받고, 응답(Response)한 후에 연결이 끊어지게 만드는 것을 잊지 않아야 한다.


 HTTP URI Method 등을 기반으로 작동하게 된다.

 HTTP 프로토콜은 별다른 것이 있는 것이 아니라 말그대로 Socket 의 통신 버퍼에 특유의 프로토콜 스택을 쌓아올리는 것을 말한다. 다음은 요청과 응답에 따른 프로토콜 형태이다.

 

<HTTP Request Header>


 위의 요청 포맷에서 첫번째 라인은 Request Line이라고 해서 요청에 대한 포맷 정보를 명시하는 필수 요소이다. 해당 라인은 3가지의 필드로 이루어져 있으며 각 필드는 다음을 명시한다.


(1)  요청 메서드 : GET, POST, OPTIONS(UPDATE, DELETE), PUSH 등의 요청 방식이 온다.


(2)  요청 URI : 요청하는 자원의 위치를 명시한다.


(3)  HTTP 프로토콜 버전 : 프로토콜의 버전으로 1.0 1.1이 있다.



그 아래로 요청 헤더의 내용이 CRLF Delimeter로 하여 열거된다.

General Header : Cache-Control, Connection, Date, Pragma, Trailer, Transfer-Enco, Upgrade, Via, Warning

Request Header : Accept, Accept-Charset, Accept-Encoding, Accept-Language, Authorization, Expect, From, Host, If-Match, If-Modified-Since, If-None-Match, If-Range, If-Unmodified-Since, Max-Forwards, Proxy-Authorization, Range, Referer, TE, User-Agent

Entity Header : Allow, Content-Encoding, Content-Language, Content-Length, Content-Location, Content-MD5, Content-Range, Content-Type, Expires, Last-Modified, extension-header

요청 헤더의 내용이 전부 명시가 된 이후에는 Message Body두 개의 CRLF 아래에 명시된다. 두개의 CRLF 뒤에는 Request Body 가 포함되게 되며, 이부분은 HTTP 스펙에 따라서 해석의 여부가 나뉜다. (이부분은 REST API 소개 및 HTTP Method 분석 포스팅에서 자세히 다루겠다.)


 다음은 응답 헤더의 모습이다.


<HTTP Response Header>

 

첫번째 라인은 요청헤더의 Request Line 처럼 Response Header에서는 Status Line 이라 불리며 필수 정보를 포함한다.


(1)  응답 프로토콜과 버전 : HTTP/1.0, HTTP/1.1, HTTP/2.0 이 현재 버전으로 존재한다.


(2)  응답 코드 : 1xx, 2xx, 3xx, 4xx, 5xx 등의 번호가 응답 코드로 사용된다.


(3)  응답 메시지 : OK, Not Found, Internal Server Error 등의 메시지를 출력한다.


 역시 해당 라인 아래에 응답 헤더의 내용들이 포함되는데, Accept-Range, Age, Etag, Location, Proxy-Authenticate, Retry-After, Server, Vary, WWW-Authenticate 등의 정보가 포함된다. 이후에 두 개의 CRLF 라인 다음에 Message Body 가 첨부된다.

 


 복잡해보이지만 내부 구성원리는 간단하다. 

일반적인 TCP 서버를 구성하고, 프로토콜을 만드는데, 클라이언트로부터 요청을 받아서 해석하는 부분에 Request Parser를, 서버쪽에서 처리를 마치고 클라이언트에 응답을 내려줄 부분에 Response Builder 를 메시지의 머리에 붙여주면 된다.


 그렇게만 하면 웹서버가 HTTP Speculation 상에 약속한대로 메시지를 해석한다. 그렇다면 웹서버 작동의 기본 로직을 정리해보자.


(1) TCP 소켓을 열어 클라이언트의 접속을 받는다. (Accept)


(2) 커넥션을 관리할 수 있는 객체를 만들어, 쓰레드에 할당한다.


(3) 쓰레드가 해당 커넥션에 대해 HTTP Request 를 분석한다. Method 에 따라 서버 내에서 url Handler 를 라우팅해주고, 해당 라우터 메서드에서 요청에 대한 로직을 구현한다.


(4) 로직에 따라 구현한 HTTP Response 를 클라이언트에 반환하고, 접속을 끊는다. (Stateless)



좀 더 내부 동작 원리가 궁금하다면 자세한 예제는 다음 소스를 확인하면 도움이 될 것이다. 오래전에 작성한 소스라 허접하지만 웹서버 구현에 있어 기본에 충실한 좋은 예제라고 생각한다.

(https://github.com/ParkJinSang/Jinseng-Server)





P2P 는 관심있게 보고있는 기술 중 하나이다. 본 포스팅은 P2P 특징과 연결을 위한 기술들을 기술한다.


P2P 기술 개요


 P2P 기술이란 Peer To Peer 의 약자로, 소수의 서버에 집중되는 형태의 네트워크가 아닌 구성원들의 대역폭에 의존하는 다대다 통신망 기술이다. 여기서 언급하는 Peer 란 하나이자 모든 네트워크 구성요소를 말하며 모든 Peer 가 서버이자 클라이언트가 될 수 있는 구조를 지향한다. 최근에는 그리드 컴퓨팅과 그 이상의 블록체인의 기반 기술로 진화해나가고 있는 기술이다.


P2P 의 특징


 IP 네트워크는 IP주소만 알고 있으면 어떠한 단말기에도 도달할 수 있다. 인터넷에서 P2P 응용기술은 IP 네트워크 오버레이 네트워크(Overlay Network : OLN)라고 볼 수 있다. P2P는 모든 단말이 동일하지만 특별한 기능과 역할을 가진 단말이 존재하지 않으므로 연결하는 사용자수가 방대하게 되어도 특정 단말에 부하가 집중(확장성이 높은 신뢰성)한다. 즉, 더 많은 단말기로 전달이 가능하게 된다. 반면 SC 모델(Server-Client 모델)의 경우 클라이언트의 수가 증가함에 따라 서버의 처리 능력 및 서버에 연결되어있는 네트워크 회선에 부하가 집중되게 된다.(확장성이 낮다) 이에 따라서 서버 회선 비용도 크게 절감할 수 있다. 


 P2P 방식은 통신 상대 특정에 있어서 곤란한 문제를 갖고 있다. 통신상대의 IP 주소를 확인 하는 방법을 마련할 필요가 있는 것이다. 또한 통신 경로에 의한 통신 속도의 제한이 발생하게 된다. 하지만 모든 단말간 회선 품질이 동일함을 보장하는 것은 인터넷에서 불가능한 요구이다. SC 모델이 클라이언트들에 대해서 가능한한 빠른 회선 서버에 연결하도록 설계할 수 있는 반면에 P2P 방식은 그렇지 않기 때문에 회선 망에 있어서 장애를 가져올 수 있다. 


P2P 연결을 위한 기술들

 

 P2P 연결을 위해선 기본적으로 네트워크 망에 대한 이해가 필수적이다. 전통적인 서버-클라이언트 모델이 아니기 때문에, 다음과 같은 테크닉(?) 을 이용해서 연결 구조를 형성한다.


- Relaying : ClientA와 ClientB가 통신을 하는데 있어서 경우에 따라 NAT(Network Address Translation) 하에 놓일 수 있다. 이들의 통신을 중계하기 위해서 중계서버를 두고 서버를 통해 통신하는 방식이다. 접속이 유효한 동안 계속 주고받을 수 있으나 불필요한 대역폭의 낭비와 서버의 리소스를 소모하게 된다. 


- Connection Reversal : 하나의 클라이언트가 NAT 뒤에 위치하는 경우 B가 A로 연결을 시도할 때, A의 사설 IP로는 당연히 접속이 불가능하고, 서버 S가 관찰하는 NAT의 공인 IP로의 접속은 A에서 나가는 것만 허용하기 때문에 역시 접속이 불가능하다. 그래서 중계 서버 S를 이용해서 B의 공인 IP를 서버 S에게 알려준 다음 역으로 A가 접속을 시도하여 커넥션을 맺게 한다. 


- UDP Hole punching : 두 개의 클라이언트가 전부 각각의 NAT 뒤에 위치한 경우이다. 이 경우에는 두 클라이언트가 같은 NAT 뒤에 있는가 혹은 다른 NAT 뒤에 있는가로 경우가 분류된다. 


두 클라이언트가 서로 다른 각각의 NAT 뒤에 있을 때 클라이언트 A가 클라이언트 B와 연결을 맺으려면 중계서버 S를 이용해야 한다. S에 접속할 때 서버는 클라이언트 A의 정보(사설 및 공인 IP)를 취득하여 저장하게 되고 B가 접속할 때도 이 정보를 저장하게 된다. 만약 A가 B에 대해 연결을 요청하게 되면 S는 A와 B에게 동시에 서로의 IP 정보를 보내주게 되고 각 클라이언트는 이를 통해 각각 연결시도를 하게 되어 커넥션이 맺어질 수 있게 된다. 





CORS(Cross Origin Resource Sharing)


최근에는 대부분의 웹 서버가 REST API 서버를 통해 동작하며, 백엔드 개발자라면 익숙하겠지만 프론트엔드만 담당한다면 당황할 수도 있다. 실제로 처음에 본인도 웹페이지부터 서버 개발에 입문하면서 당황했던 부분이다.


웹사이트는 종종 인터넷 상의 다른 서버에 호스팅된 리소스를 요청하는 경우가 있다. 이때, 서버를 거치지 않고 Browser 단에서 직접 외부 호스트로부터 리소스를 받는 일은 서버의 보안 입장에서 유쾌한 일은 아니며, 호스팅하는 서버 입장에서도 무분별한 요청을 받게 되는 일이다. CORS는 이를 위해 보안정책을 통해 이를 제한하고자 하는 개념이다.


 CORS는 웹페이지가 리소스가 존재하는 다른 도메인으로 요청이 발생하는 것을 제한하는 규칙이다. 브라우저와 서버에서 다른 도메인과 안전하게 연결을 하고자 요청을 제약하는 규칙이다.


 이러한 정책이 생겨나게 된 배경에는 웹서버의 API 적 역할이 강화되었기 때문이다. REST API 를 소개하면서 포스팅하겠으나, 많은 종류의 Web API 는 GET을 제외한 요청들이 데이터를 변경할 수 있게끔 되어 있다. 


그렇기 때문에 GET이 아닌 다음과 같은 요청들의 경우 전처리과정(Pre-flight Request) 라 하여, HTTP 요청을 보내기 전에 먼저 OPTIONS 요청을 보내서 호스팅 서버로부터 확인 작업을 거친다.


 만약 호스팅 서버로부터 인증받은 도메인의 허가받은 요청이라면, 그다음 본 요청으로 리소스를 가져온다.


 - Pre-flight 요청이 선행되는 경우 : PUT , DELETE , CONNECT, OPTIONS, TRACE, PATCH

(POST 요청의 경우 조건적으로 특정 MIME Type에 대한 호출에 대해서 Pre-flight가 선행된다.)


 이는 브라우저에서 쉽게 확인할 수 가 있다. 브라우저는 위와 같이 GET / POST 이외의 요청에 대해 OPTIONS Request를 보내는데, 다음처럼 Origin 이라는 헤더를 Request 에 담아 보내게 되고, 서버 측에서 Response로 Access-Control-Allow-Origin 라는 헤더에 허용된 도메인을 담아 보낸다.




그렇다면 호스팅 서버의 응답 값을 같이 살펴보자





위의 예에서 해당 서버는 example.com 이라는 도메인에서 요청한 PUT, DELETE 메서드만 허용한다. 물론 example.com 에서 요청했기 때문에 위의 요청은 PUT 또는 DELETE 라면 에러를 발생시키지 않고, 해당 PUT / DELETE API 를 요청한다.


 CORS 표준은 서버가 지정한 일련의 도메인 들에 대해 정보를 읽을 수 있도록 허가하는 HTTP 헤더를 추가하여 문제를 해결한다.



 * 일반적으로 서버측에서는 요청들에 대해 Credentials 를 담아 처리하게 하며, 별도로 리소스에 대한 접근을 허용하는 Whitelist 방식으로 처리한다.



처음 서버 실무를 시작할 때, 몰랐던 내용인데 의외로 명확히 설명해주는 곳이 없었다. 당연하게들 알고있는것 같다 ㅜ


더군다나 각종 프레임워크나 영미권의 Document 들에서는 Roll the log files 와 같이 쓰면서 Log rolling 을 검색하면 정치 용어 가 등장하여 정리해두었다.


 Log rolling 이란 일정 단위로 로그파일을 재갱신하는 작업으로 Log rotation이라고도 한다. 하지만 영어적 표현으로 Roll(굴리다) 으로 할 뿐 정식 명칭은 Log Rotation 이 맞는 것으로 보인다.


 무식하게 로그를 계속 쌓아나가는 것이 아니라 일정 주기로 백업 또는 별도 처리하고 파일을 덮어쓰면서 순환시킨다. 이 때 순환의 주기는 데이터 별로 다른데, 지워도 문제가 없을 수준으로 보통 정한다.


 이를 정하는 건 역시 노하우... 대부분 크리티컬한 유저 데이터가 아닌 이상 CS 가 발생하지 않을 수준에서 정리하곤 한다.


 apache의 경우 이슈에 대해 파이프 로그를 수행한다. 좀 더 자세히 말하면 아파치 웹서버의 경우 Log가 끊기지 않게 하기 위해서 서버와 함께 파이프 Process를 동작시켜서 Process가 죽더라도 Logging이 죽지 않도록 파이프를 이용해서 순환시킨다. 


 대부분 로그를 작성 시에 Error Log에는 날짜, 시간, 심각성, IP주소, 오류문, 경로, 해결책 등을 포함시키며 포맷을 벗어나지 않게 작성한다. Access Log에는 모든 요청을 기록하며 보통 Access Log는 에러로그 보다도 Massive 한 경우가 많기 때문에 메시지 큐를 담당하는 미들웨어로 보내서 처리하는 경우가 많다. 



+ Recent posts