워드프레스에서 티스토리로 옮기면서 새로운 주제를 하게 되었습니다.

웹 개발자로서 업무에 대한 지식을 정리하는게 주 목적이긴 하지만, 잘 정리해서 모두에게 도움이 되었으면 하네요.

가급적 쉽게 설명하고 업무에 바로 적용할 정도로 정리하는게 목표이긴 한데... みんなさん、がんばりましょう!


Apache 웹 서버에 대해 스터디 발표를 준비하는데,

아무래도 단계별로 가야할거 같아서 나눠서 포스팅하려고 합니다.


Apache 웹 서버의 목적 - HTTP 프로토콜 처리

Apache 웹 서버는 결국 웹 요청을 처리하기 위해 웹 상에서 작동하는 서버 프로그램입니다.

그렇다면 웹 에서의 요청과 응답이 어떤 것인지 먼저 알아볼 필요가 있을거 같은데요.


HTTP는 World Wide Web 이라는 전세계의 컴퓨터를 인터넷으로 연결해서 정보를 공유하는 일종의 가상 공간 안에서, 

정보를 요청하고 응답을 주기 위한 프로토콜, 즉, 통신 양식에 대한 명세(Spec)입니다. 

이 말인 즉슨, HTTP 명세를 벗어난 요청/응답은 웹 상에서 처리될 수 없다는 것이죠.


그렇다면 HTTP 명세라는 건 어떻게 생겨먹었나 봐야죠.

우선, 가장 큰 구분은 요청이냐 응답이냐 인데요.

HTTP는 웹 상에서 일어나는 행동을 요청과 응답 두 가지로 정의합니다.

정보가 필요한 쪽에서는 정보를 얻기 위해 요청을 보내고,

정보를 가진 쪽에서는 요청을 통해 어떤 정보에 대한 요청인지 확인한 다음 알맞은 정보를 응답으로 보내는거죠.


* 사실 요청과 응답이라는 패러다임은 모든 통신의 행동 방식이죠.

전화도 마찬가지이고, TCP/UDP도 결국은 요청 - 응답의 반복입니다.

다만, 컴퓨터에서는 디지털화된 데이터가 이동하면서 요청 - 응답 메시지에 이전보다 더 많은 정보를 실을수 있게된거고,

정보를 가진 쪽에서 어떤 정보에 대한 요청인지 구분이 용이하도록 header, body 등

명세가 더 세분화되었다고 생각하시면 될거 같습니다.


그럼 요청을 어떻게 하고, 응답은 또 어떻게 받는걸까요?

정답은 메시지 입니다.

메시지에 목적지 주소와 원하는 정보의 위치, 요청하는 정보에 대한 상세 설명, 인증정보 (정보를 가진 쪽에서 요구할 경우), 요청 명세의 버전 등을 적어서 보내면

정보를 가진 쪽에서 메시지를 받고 어떤 정보인지 찾아서

있으면 정보와 상세 설명을, 없으면 왜 없는지(?)를 처리결과와 함께 새로운 메시지로 실어서 요청한 곳으로 다시 보내는거죠.


HTTP 요청/응답 메시지

HTTP 프로토콜은 인터넷 상에서의 통신 규약이므로 구체적으로 알아보려면 RFC문서를 보는게 좋습니다.

(HTTP 프로토콜의 RFC 문서 링크 - https://tools.ietf.org/html/rfc2616)

다만 개발자 입장에서 당장 필요한 것은 HTTP를 처리하는 웹 서버에 요청을 어떻게 주는지,

응답은 어떤 모양으로 오는지 이해하는 거겠죠.


요청 메시지는 다음과 같은 모양입니다.

GET /restapi/v1.0 HTTP/1.1
Accept: application/json
Authorization: Bearer UExBMDFUMDRQV1MwMnzpdvtYYNWMSJ7CL8h0zM6q6a9ntw

(출처 : 위키백과, https://ko.wikipedia.org/wiki/HTTP)


첫번째 줄은 start-line으로 정의 되어 있는데, 요청 메시지일 경우는 request-line으로 부르고, METHOD / URI / HTTP버전 으로 구성됩니다.

정보를 요청하고 응답받는 입장에서만 보면 URI가 정보 자원의 위치를 가리키는 것이므로 가장 중요하죠.

HTTP METHOD는 정보 자원이 처리되는 방식을 가리키는 걸로 GET/POST/PUT/PATCH/DELETE/OPTION 등등을 RFC문서에서 정의하고 있지만,

웹 브라우저에서는 GET으로 지정해서 요청 자원을 그대로 받는 형태로 쓰고 있습니다.

이 부분은 다음 포스트에서 다시 설명드리겠습니다.

HTTP버전은 말 그대로 프로토콜의 버전인데, HTTP의 경우 아직은 1.1로 지정해서 사용합니다.


두번째 줄 부터는 헤더라고 해서 요청이나 클라이언트 자신에 대한 부가적인 정보를 나타내는 역할로 사용합니다.

예제 같은 경우는 응답을 JSON형식으로 전달하라는 것이고, 요청하는 정보 자원의 유형을 미리 알고 있으면 저렇게 기재하는 것입니다.

헤더 부분의 Authorization 필드는 인증정보를 요구하는 것이고, 저렇게 자기자신의 개인키를 넣어서 자기자신을 알리는 것이죠.

헤더부분은 저렇게 두번째 줄부터 CRLF(HTTP 메시지 상에서의 개행문자)가 두번 연속으로 들어간 곳 까지입니다.


응답 메시지의 예시는 다음과 같습니다.

HTTP/1.1 200 OK
Date: Mon, 23 May 2005 22:38:34 GMT
Content-Type: text/html; charset=UTF-8
Content-Encoding: UTF-8
Content-Length: 138
Last-Modified: Wed, 08 Jan 2003 23:11:55 GMT
Server: Apache/1.3.3.7 (Unix) (Red-Hat/Linux)
ETag: "3f80f-1b6-3e1cb03b"
Accept-Ranges: bytes
Connection: close

<html>
<head>
  <title>An Example Page</title>
</head>
<body>
  Hello World, this is a very simple HTML document.
</body>
</html>

(출처 : 위키백과, https://ko.wikipedia.org/wiki/HTTP)


첫번째 줄은 요청과 마찬가지로 start-line으로 시작하지만, 응답 메시지일 경우는 status-line으로 다르게 부릅니다.

구성은 요청받은 HTTP버전 / 처리상태 로 이루어집니다.

처리상태는 상태코드와 상태에 대한 설명으로 이루어지는데, 종류가 좀 많습니다.

크게 100 ~ 500 클래스로 구분하죠.

1XX - 요청을 계속 처리중임, 일반적으로는 사용되지 않음

2XX - 요청이 잘 처리되었고, 처리된 정보의 종류를 설명하지만, 보통은 200 코드만 사용됨

3XX -  요청을 처리하기 위한 다음 Action을 나타냄 (즉, Redirection)

4XX - Client Error, 즉 요청 메시지에 오류가 있음을 나타냄

5XX - Server Error, 즉 서버가 어떤 이유로 요청을 처리할 수 없는 상태인지를 나타냄


세부적인 코드는 검색해보시길 바랍니다.

예를 들어, 응답받은 상태코드가 403이면 요청을 서버가 이해하지 못한다는 것인데, 보통은 헤더나 파라미터 같은 필수값이 일부 빠진 상태로 요청을 보내서 그렇죠.

404는 URI가 가리키는 정보가 유효하지 않다는 거니까, 경로가 잘못된게 되는거고요.

반대로 500 클래스가 응답으로 나오면 운영팀에 비상등이 켜졌다고 보면 됩니다. 빼박 서버 문제니까요.


두번째 줄 부터 헤더영역인 것 까지는 동일한데, 이때 헤더의 내용은 응답에 대한 부가정보로 요청때보다 내용이 많아지게 됩니다.

그리고 개행문자를 두번쳐서 헤더를 끝낸다음, 응답하는 정보(데이터)가 이어서 응답 메시지에 들어갑니다.

이때 정보가 들어가는 영역을 message-body라고 부릅니다.

요청때도 이 message-body가 들어갈 수는 있으나, REST 방식 같은 특수 케이스에만 쓰이죠.

일반적인 HTTP 요청 메시지에는 message-body가 들어가지 않는다고 생각하시면 됩니다.


URL과 URI의 차이는?

브라우저에서는 웹 요청을 보낼때는 URL만 이용했습니다.

그럼 저 HTTP 요청/응답 메시지는 어떻게 만들어지고 처리되는 걸까요?

브라우저에서의 처리 절차는 다음과 같습니다.

  1. URL을 입력받으면, 파싱해서 HTTP 요청 메시지를 만든 다음 URL에 명시된 도메인(혹은 IP주소)로 보냄
  2. 응답을 기다림
  3. 응답 상태가 정상이면 메시지에서 message-body 부분을 뽑아서 브라우저 창에 보여줍니다.


그럼 파싱은 어떻게 하는 걸까요?

일반적으로 사용하는 URL의 예를 들어 보면

https://www.sample.com/resource1/sample1.html?param1=xx

이런 식인데,

'://' 을 기준으로 왼쪽은 프로토콜, 오른쪽은 도메인(혹은 IP:PORT)이 됩니다.

도메인 바로 오른쪽의 / 다음 부터는 요청하는 자원의 위치가 되는 거죠.

(물음표 다음부터는 파라미터로 활용하고요)


여기서 URL과 URI을 구별할수 있는데,

URL은 도메인을 포함한 웹 상에서 정보자원의 위치이고, URI는 정보자원의 식별자가 되는겁니다.

즉 URL안에서 프로토콜과 도메인을 제거한 'resource1/sample1.html?param1=xx' 가 자원의 식별자인 URI인거죠.


요청 메시지의 request-line을 구성할때 URI와 프로토콜 버전을 URL로부터 이렇게 뽑아냅니다.

그럼 나머지는?

HTTP METHOD 같은 경우 웹 브라우저에서는 GET으로만 보냅니다.

자원을 받아서 화면에 보여줘야 되니까요.

당연히 헤더 부분도 HTML이나 파일 같은 정적 자원을 요청하는다는 걸로 채워지는 거죠.


Posted by kevin.jeong.
,

Java로 웹 개발을 할때 Spring 프레임워크를 빼놓고 얘기하면 거의 빈 껍데기 혹은 어불성설 같은 느낌일 겁니다. 그만큼 보편화 되어 있는데, 이걸 논할때 트랜잭션 개념을 빼면 겉핧기식으로 공부했다고 보는 것 같아요. 그래서 정리하려고 합니다.

 

1.  트랜잭션 개념

Spring MVC - 트랜잭션이란 도데체 뭐란 말인가!

자세한 설명은 위 블로그 글에 잘 나와 있네요. 구글링을 좀 했는데, 다들 보시면 좋을 거 같습니다.

대신, 요약을 하면

하나의 쿼리에 대한 commit/rollback을 넘어서, 업무처리같이 단위는 하나지만 그 안에 insert/update/delete (+select) 쿼리 여러개가 순차적으로 모두 에러없이 실행되어야 처리되었다고 말할 수 있을때, 이렇게 안에 세부 순서를 가진 업무 단위 혹은 이를 처리하기 위한 기술을 트랜잭션이라고 부릅니다.

 

2. Java에서 트랜잭션을 처리하는 방법 (혹은 이것의 변화과정)

(1) Procedure

Spring이 등장하기 이전에, 혹은 MVC 개념이 발전하기 전엔 DB에서 프로시저를 작성하고 그 안에 쿼리들을 넣어서 트랜잭션을 처리하게 했다고 합니다. 단점은 DB를 바꿀때, 예를 들어 mysql에서 oracle로 바꾼다 그러면 DBA는 죽어나는거죠. 아마 이때쯤에 DB 튜닝 혹은 쿼리 튜닝 일하시는 분들 단가가 엄청 올라갔던거 같습니다. 그리고 오라클 같은 경우는 패키지라고 해서 프로시저를 묶은 개념이 있더라고요. 업무차 두세번 정도 작업 했었는데, 문법 차이만 있지 작성 방법은 비슷했었습니다.

(2) 메소드 내 try ~ catch ~ finally 구문에서 여러 쿼리 호출

개발자 입장에서 트랜잭션을 구현한다고 했을때 쉽게 떠오르는 방식일거 같네요. 우선은 connection 객체에서 setAutoCommit(false) 로 자동 커밋을 막고, 쿼리 여러개를 쭉 수행한 다음 try ~ catch 사이 마지막에 commit() 을 호출하는 겁니다. 문제는 소스코드에서 중복되는 부분이 너무 많다는 거겠죠. 개발자들에게도 스트레스가 될거고요.

(3) Spring 에서 처리하기

스프링에서는 크게 AOP와 어노테이션으로 트랜잭션을 구현할 수 있습니다.

우선 AOP를 이용한 방식입니다.

// mvc-config.xml
<context:component-scan base-package="com.kevins.web" use-default-filters="false">
  <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>

// application-config.xml
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"    // 추가할 부분
       xmlns:tx="http://www.springframework.org/schema/tx"      // 추가할 부분
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/aop                       // 추가할 부분
                           http://www.springframework.org/schema/aop/spring-aop.xsd        // 추가할 부분
                           http://www.springframework.org/schema/tx                        // 추가할 부분
                           http://www.springframework.org/schema/tx/spring-tx.xsd"         // 추가할 부분
>

...

//추가할 부분
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource"/>
</bean>

<aop:config proxy-target-class="true">
  <aop:pointcut id="serviceOperation" expression="execution(public * com.kevins.web..service.*Service.*(..))" />
  <aop:advisor id="transactionAdvisor" pointcut-ref="serviceOperation" advice-ref="txAdvice"/>
</aop:config>

<tx:advice id="txAdvice" transaction-manager="transactionManager">
  <tx:attributes>
    <tx:method name="save*" rollback-for="Exception"/>
    <tx:method name="update*" rollback-for="Exception"/>
    <tx:method name="remove*" rollback-for="Exception"/>
  </tx:attributes>
</tx:advice>

...

</beans>

이렇게 Spring 설정을 해놓으면 Controller/Service/DAO를 구현하고, DAO내 메소드 명을 <tx:method> 태그 부분에 선언한대로만 맞추면 자동으로 실행되는거 같습니다. 대신 DAO 선언시 @Repository 어노태이션을 꼭 붙일것과, AOP 설정을 잘 이해해야겠죠.

어노테이션으로 구현하는 것은 AOP보다 쉽다고 합니다. Service\DAO 구현체나 그 메소드 앞에 @Transactional 이라는 어노테이션을 붙이면 되네요. 대신에 dataSource 설정하는 스프링 설정파일에 transactionManager 까지는 선언되어있어야 합니다.

그리고 프로그램적 선언 방식도 있다고 하는데 설정파일에 선언한 transactionManager를 @Resource 어노테이션으로 직접 호출해서 commit/rollback을 제어하는 방식입니다.

 

3. 마무으리

급하게 정리하다보니 다른 블로그를 많이 참고했습니다. 참고한 블로그 목록 입니다. 정리가 훨씬 잘 되어 있으므로 가급적 참고하시길 바랍니다.

Outsider's Dev Story - [Spring 레퍼런스] 11장 트랜잭션 관리 #1

가리사니 - Spring @Transactional Method 적용범위 : rollback 주의

박철우의 블로그 - 스프링의 트랜잭션 관리

성일만 - [Spring] 스프링 트랜잭션 적용하기 (Spring + MyBatis + MySQL)

Full Stack Web Developer Syaku - #5 스프링 트랜잭션 - 스프링 프레임워크 게시판 : Spring Framework Transaction

 

Posted by kevin.jeong.
,