Spring 의 Bean 이 정의될 경우 정의하는 Bean 의 Scope 에 대해 옵션을 부여할 수 있다.


예를 들어 Prototype Scope 는 해당 Bean 을 사용할 때마다 재정의하게끔 성질을 부여한다.


반면 Bean 을 Spring project 전역에서 단 하나의 객체로 취급하고 접근하고자 할 경우 해당 Bean 의 Scope 는 Singleton 으로 정의되는 것이 옳다.


Spring 프레임워크는 현재 5개의 Scope 를 제공하고 있다.


(1) Singleton : Spring 컨테이너 하나당 한개의 Bean 만이 생성되고 해당 Bean 이 요청될 때마다 모든 객체는 한 객체를 가리킨다.


(2) Prototype : 하나의 Bean 은 사용할 때마다 새로운 객체를 할당하여 사용하게 된다.


(3) Request : HTTP request 가 상주하는 Spring Application Context 내에서만 유효한 객체를 생성하고 재사용한다. (web-aware)


(4) Session : Bean 을 해당 세션이 가진 Application Context에 바인딩한다. (web-aware)


(5) Global session : HTTP 세션 전역에서 같은 Bean 을 사용가능하다. (web-aware)


Spring Bean 들의 Default scope 는 Singleton 이기 때문에 대부분 Bean 을 사용할 경우 @Autowired 를 통해 간편하게 사용이 가능하다.


만약 어플리케이션에 따라 다른 종류의 Scope 가 필요하다면 다음 예시와 같이 설정에서 변경이 가능하다.



<bean id = "..." class = "..." scope = "prototype">
//...
</bean>



보통의 어플리케이션에서는 Singleton 으로 설계하고 사용할 수 있으나 간혹 prototype 등으로 특정할 수 있으니 상황에 따라 유용하게 사용할 수도 있다.


<보다 나은 예시 참조 : https://www.tutorialspoint.com/spring/spring_bean_scopes.htm>



Protocol Buffer 는 구글이 만든 언어 및 플랫폼 중립적이고 확장성을 갖춘 새로운 형태의 직렬화 매커니즘이자 데이터 포맷이다. 

 

XML과 유사하지만 더 작고 빠르며, 포맷을 정의하기만 하면 컴파일을 통해서 C++ / C# / Java / Python / Go / Ruby 등의 언어에 대해서 바로 코드형태로 바꿀 수 있다. 


프로토버프의 파일 포맷은 .proto 이며 이 안에 자료 구조를 정의하면 프로토버프 모듈을 통해 각 언어에 맞게 컴파일을 한 뒤 사용하는 방식이다.

언어에 무관하게 포맷만 서로 공유가 되면 각 언어에 맞는 모듈들이 알아서 Serialize / Deserialize 를 해주기 때문에 쉽게 사용할 수 있다.


.proto 의 샘플 데이터 포맷은 다음과 같다.


syntax = "proto2";
option java_package = "com.model.protobuf";

message SimpleProtoBufMessage {
required int32 id = 1;
required string message = 2;
enum MessageType {
PING = 0;
REQUEST = 1;
RESPONSE = 2;
}
optional MessageType messageType = 3 [default = PING];
}


 syntax 는 컴파일할 대상 protobuf 버전을 지칭하며 java_package 와 같은 것들은 option 으로 지정되어 자바로 컴파일 시에만 적용이 된다.

위의 message 는 클래스로 매핑이 되며 그 안의 멤버변수들은 1, 2, 3 과 같은 연속된 숫자가 값으로 할당되는데 이는 직렬화 순서를 의미한다.


protobuf 2 버전과 3 버전에는 몇가지 문법에 있어서 차이가 있다.

예를 들어 위의 예제 양식에서 required 는 proto3에서 없어졌고, optional 은 proto3에서 기본형이라 역시 사라졌다. 

Default 값 지정도 proto3 에서 사라진 내용으로, protobuf 의 현 방향은 기본값을 배제함으로써 null 체크에 대해 좀 더 엄격해진 모습이라고 이해하면 된다.


위의 파일을 proto3 버전으로 바꾼다면 다음과 같이 된다.


syntax = "proto3";
package PB.Simple;
option java_package = "com.model.protobuf";

message SimpleProtoBufMessage {
int32 id = 1;
string message = 2;
enum MessageType {
PING = 0;
REQUEST = 1;
RESPONSE = 2;
}
MessageType messageType = 3;
}



.proto 를 컴파일 하기 위해서는 protobuf 모듈을 OS에 맞게 다운받아서 protoc 모듈을 이용하면 된다.


위와 같이 생성된 .proto 파일을 protoc 모듈을 이용해서 각 언어별로 컴파일을 수행하면, 다소 복잡한 형태의 코드를 얻을 수 있으며 구글에서는 최대한 이 파일을 변형하지 않고 사용하길 권장한다.


<proto 컴파일된 Class 파일 예시>



위처럼 생성된 파일은 해당 언어에서 바로 모듈 혹은 클래스의 형태로 이용이 가능하다. 


대게 프로젝트에서는 직접 이용하기 보다는 별도의 Converter 를 만들어두고, 통신시 혹은 직렬화시에만 Convert 해서 사용한다.


프로젝트 Protobuf 로 통신은 application/x-google-protobuf 와 같은 새로운 형식이며 서버 클라이언트간 통신 시 이 형태로 consume 및 produce 가 일어나야 한다.

이때, 서버와 클라이언트는 동일한 .proto 파일을 공유하기만 하면 각기 환경에 알맞게 컴파일해서 내부 클래스로 사용하면 된다. 

주로 REST 서버로 POST 에서 RequestBody 에 protobuf 모델이 담겨서 통신하게 된다.


통신에 있어서 특징적인 점은, Protobuf 통신시에 값이 default 값과 같다면 통신 시 전송하지 않는다는 점이다. 

이는 프로토버프가 프로토콜 설계시 성능을 크게 고려했다는 점을 알 수 있는 부분이다.


실무에서 사용 결과 JSON 보다 확실히 우월한 퍼포먼스를 내는 것을 실감하고 있다. Json 에 비하자면 아직은 쓰기 불편하지만, 익혀둘만한 기술임은 틀림없다.




예전에 네트워크 수업 시간에 배운 내용 정리 겸, 가끔 인터넷에 잘못 알려진 내용들이 있어서 다시 복습정리해보았다.


TCP 의 통신 방식은 흔히 알고 있는 3-Way Handshake 방식이다.

즉, 요청을 위한 ACK, 응답을 위한 SYN+ACK, 응답에 대한 응답을 위한 ACK 패킷이 그것이다.


원칙적으로는 전송되는 모든 패킷에 대해 TCP는 3-Way Handshake 를 고안하게 되어있다.


여기서 TCP의 특징은 TCP는 신뢰성을 위해 어느정도의 비효율을 감소한다는 점이다.

가령, "Hello World" 라는 메시지를 전송해야한다고 가정하자.


이 때, 만약 어플리케이션이 몇가지 이슈로 인해 H, e, l, l, o, , W, o, r, l, d 와 같이 메시지를 끊어서 보내야한다고 할 때, 이는 TCP 통신에 있어서 커다란 비효율이 된다.

TCP 스러운 통신을 위해 모든 통신은 3-Way handshake 를 거쳐야 하며 그를 통해 신뢰성을 검증하게 된다.


이 상황은 상당히 빈번하게 발생할 수 있는데, 가령 통신 이슈가 생기거나 Host 간의 Window 크기에 있어서도 영향을 받는다.

(자세한 건 다음 내용을 참고하자.)

(http://jins-dev.tistory.com/entry/%ED%95%9C-%EB%88%88%EC%97%90-%EC%A0%95%EB%A6%AC%ED%95%98%EB%8A%94-TCP%EC%99%80-UDP-%EC%A0%95%EB%A6%AC-%EB%82%B4%EC%9A%A9?category=760148)


이 때 발생하는 주요 문제점은 단순한 메시지의 전달에 대해서도 네트워크 비용이 막대하게 발생한다는 점이다.


메시지 하나하나의 처리량과 반응속도는 높아질 지언정 전체 "Hello World" 메시지를 통신하기 위해서는 글자수만큼의 네트워크 비용을 소모한다.


더군다나 매 통신 시, 컴퓨터는 메시지에 별도의 헤더 등과 같은 추가 정보들을 추가하며 이는 동일한 정보더라도 한 패킷의 사이즈가 커지는 결과를 만들어낸다.

즉, 하나의 메시지 H 를 보내는데 메시지보다 헤더의 데이터가 더 큰 비효율이 발생할 수 있는 것이다.


Nagle's Document 에 따라 이러한 문제는 Small Packet Problem 으로 정의되며 Nagle 알고리즘은 이 문제를 해결하는 방법을 제시한다.


Nagle Algorithm 은 송신에 있어서 버퍼를 둔 뒤 상대방 Host 의 Window 사이즈를 고려한 후, 어느정도 길이만큼의 패킷을 한번에 전송하는 기술이다.

다음은 위키에서 발췌한 Nagle Algorithm 의 수도코드이다.


<출처 : https://en.wikipedia.org/wiki/Nagle%27s_algorithm>



당연히 이렇게 하면 통신 횟수가 적어져서 네트워크 비용은 절감되게 된다. 대신 패킷 하나당 처리량이 늘어나므로 반응속도는 조금 느려질 수 있다.

가령 Health Check ping 을 날리는 경우나, signal 을 전송하는 경우에는 오히려 Nagle Algorithm 을 적용하지 않음으로써 반응속도의 효율을 좋게 만들 수 있다.



 로드밸런싱(Load Balancing) 이란 Software 용어로 부하 분산의 의미를 갖고 있다. 말그대로 동작에 있어서 부하가 심해져 병목현상이 생기는 것을 방지하기 위해 사용한다.

이는 컴퓨팅 리소스, 네트워크 리소스 등 모든 부분에서의 성능향상을 기대할 수 있게 한다.


일반적으로 L4 Load Balancer 와 L7 Load Balancer 가 존재한다.




(1) L4 Load Balancing


 L4는 네트워크 Layer 4번째 계층을 의미한다. 4번째 계층에서 수행되어야할 작업의 중요한 역할 중 하나는 IP, 포트, 세션을 기반으로한 LoadBalancing 작업이 있다.

이런 이유로 L4 Load Balancer 를 Connection Load Balancer 혹은 Session Load Balancer 라 부르기도 한다.


4계층에 포트정보가 들어감으로써 디테일한 로드밸런싱이 가능하기 때문에 일반적으로 Load Balancer 를 말할 때 L4 스위치 자체를 말하기도 한다.

이렇게 L4 스위치만 이용하더라도 부하분산 및 포트에 대한 필터링이 가능하다. 

로드밸런서가 있으면 서버 몇대에 이상이 생기더라도 다른 서버가 대신 작업을 수행하는 Failover가 가능하며,

또한 이 부분에서 TCP/UDP 패킷 분석이 가능하기 때문에 Firewall 처리도 수행한다.



보통 로드밸런서의 설정 시 서버들에 대한 주기적인 health Check를 통해 장애 여부를 판단할 수 있으며 분산알고리즘(metric)에는 흔히 알려진 라운드로빈, Least Connection, Response Time, Min miss, bandwidth based, 해싱 알고리즘이 있다.


- 라운드로빈 : 세션을 순차적으로 맺어주는 방식이다. 일반적으로 5:5의 분산이 가능하나 경로별로 같은 처리량이 보장이 안된다.


- Least Connection : 가장 적은 Open Session을 가진 서버로 연결을 붙여준다. 가장 많이 사용되는 방식이다.


- Response Time : 각 Real Server 들이 다루는 Resource의 양과 Connection 시간, 데이터 양이 다른 경우 사용하기 적합하다. 로드밸런서가 서버와 직접 통신을 하면서 응답시간이 빠른쪽으로 많은 세션을 할당해준다.


- Hash : 특정 클라이언트는 특정 서버로만 할당시키는 방식. 경로가 보장되며 접속자수가 많을수록 분산 및 효율이 뛰어나다. 다만 접속자수가 적을수록 공평하게 분산이 안될수도 있다.


- Minimum Missis : 해시 기법과 유사하지만 특정 서버 다운시 해시값의 재할당이 이루어진다. Source IP 기반으로 해싱을 하기 때문에 프록시를 사용하는 경우 Hashing이나 Min miss를 사용하면 안된다.


- Bandwidth based Loadbalancing : 서버들과의 대역폭을 고려하여 분산한다.



(2) L7 Load Balancing


 L4 Load Balancer 가 일반적으로 TCP / UDP 에 대해서만 동작하는 것과 다르게, L7 Load Balancer 는 OSI 7 층의 HTTP 에 대해서도 작동한다.

Layer 7 의 로드밸런서는 주로 HTTP 라우팅에 대한 처리를 담당한다. 


Layer 7 Load Balancing 의 주요 특징들은 다음과 같다.


- Persistence with Cookies : 주로 쿠키를 활용한 Connection Persistence 를 유지하며, 쿠키 정보를 분석한다. WAS 로 이루어지는 연결에 대해 해당 연결을 동일한 서버로 연결되게끔 해준다.


- Context Switching : 클라이언트가 요청한 리소스에 대해 Context 를 전환할 수 있다. 가령 Static 이미지 리소스 등에 대해 Load Balancer 가 확장명에 따른 분류로 Image Server 로 연결해줄 수 있다.


- Content Rewriting : 전달받은 Request 를 변환해서 재전송이 가능하다.


그 외에도 프록시 처리(X-forwarded-for) 및 보안 로직 처리 등도 이루어진다.

로드밸런싱 알고리즘은 L4 와 유사하며 역시 가장 간단하게 라운드로빈 알고리즘이 주로 사용된다.


일반적으로 L7 로드밸런서는 L4 로드밸런서보다 비싸지만 상위 프로토콜에서 그 이상의 유연성을 보여준다.




 시스템을 구성할 때, 특히 서버시스템이라면 장애(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 되더라도) 동시에 처리할 수 있는 "백신"을 모듈화시키는 것이 중요하다.




Spring framework 는 전세계적으로 손꼽힐정도로 우수한 Java Framework 이다.

개발 생산성을 위해 지원하는 강력한 기능들에 비해 경량의 프레임워크이고 많은 복잡하거나 귀찮은 부분들(특히 Servlet 처리와 같은 부분들)을 굉장히 효과적으로 프레임워크 단에서 처리해주어 개발자들에게 Business Logic 에만 신경쓸 수 있도록 지원한다. 


이러한 장점들 덕분에 스프링을 이용해서 만든 프로젝트들은 특유의 형식에 맞게 읽기 쉬운 코드로 되어 있으며 각 모듈들이 역할별로 잘 나뉘어 서비스 프로젝트에 있어서 최적의 구조를 갖는다.


다음은 Spring Framework 의 특징들에 대한 간략한 정리이다.


 - 경량(Light-weighted) : 특히 크기와 부하 측면에서 독립된 몇 개의 jar 파일로 구성되어있기 때문에 설치가 용이하며 POJO를 사용한다. EJB와 달리 Spring framework 자체의 부하는 무시해도 좋을 수준이다.


- IoC 패턴 : 객체 사이의 의존관계를 코드로 구현하지 않고 Framework에 의해 주입받는다.(Dependency Injection)


- AOP(Aspect Of Control) : 핵심 로직 개발 시 Transaction지원이나 Logging, Security 등 시스템 적 관심사에 신경쓸 필요가 없다.


- Transaction 관리 : Java Object 들에 대한 트랜잭션을 지원하지만 결코 J2EE 와 같은 환경에 얽매이지 않는다.


- Container : Application 객체의 Life cycle과 객체들 간 의존관계를 컨테이너 형태로 관리한다. Servlet Container와 Spring container의 차이점은 특정 WAS. 벤더에 종속적이지 않으면서 자유로운 확장이 가능하다는 것이다.


- Framework : 간단한 컴포넌트 만으로도 Application 구성이 가능하다. xml 설정 파일 상의 선언적인 구성도 가능하다.


- Integration : Container 구조 덕분에 다른 프레임워크들과 결합이 쉽다. 특히 IBATIS, Hibernate 등과의 결합은 공식과도 같다.


위와 같은 철학을 중심으로 유지하면서 최근의 Spring 은 새로운 개발 Trend 를 따라가고 있다.

다음과 같은 새로운 Feature 들이 버전별로 추가되었다. 아직 5는 제대로 써본적이 없지만, 좀 더 안정화되면 추가 정리해야할 것으로 생각된다.



Spring 3.0


Spring3.0 부터 Java5가 지원된다. 기존에 유지하던 하위호환성에 Generic 이나 가변인자(varargs) 등과 같은 개선사항이 추가된다.


(1) 전체 프레임워크를 하나의 spring.jar 파일로 제공하던 부분을 여러개의 jar 파일로 나누어 제공한다. 


(2) SPEL(Spring Expression Language)가 도입되었다.


(3) Rest API 에 대한 지원이 추가되었다.


(4) OXM(Object Xml Mapping) 기능이 추가되어 설정을 Xml 형태로 할 수 있게 지원한다.


(5) Java annotation 을 이용해서 DI 의 지원이 가능하다.



Spring 4.0


Spring 3.0 이 Java5+ 버전들에 대해 지원을 한다면 Spring 4.0 버전은 Java 8 의 특징들을 적용할 수 있게 지원한다.


(1) Starter Pack 이 생겨서 초보 개발자들에게 큰 진입장벽인 POM 설정을 도와준다.


(2) 기존에 사용하지 않지만 호환성을 위해 남겨져있던 Deprecated Package 들이 제거되었으며 Hibernate 3.6 이상, EhCache 2.1 이상, Groovy 1.8 이상, Joda-Time 2.0 이상 등 새로운 Dependency 들에 대해 지원한다.


(3) Java6, Java7, Java8 의 고유 기능들에 대해 지원한다. 람다식, Optional, Callback Interface 등의 기능을 Spring framework 레벨에서 사용할 수 있다.


(4) Java EE 6, 7 에 대해 고려되어 있다. JPA 2.0 과 Servlet 3.0 에 대한 지원이 포함되어 있다는 뜻이다.


(5) Groovy 를 이용한 Bean 설정이 가능하다. 자세한 사용법은 GroovyBeanDefinitionReader 문서를 참조하자.


(6) Core 컨테이너들의 기능 지원이 확대되어있다. 먼저 Repository 들이 좀 더 쉽게 Inject 될 수 있으며, 각종 Metadata Annotation 들을 이용한 Custom Annotation 작성이 가능하다. @Lazy 를 이용한 Lazy Injection 이나 @Order 을 통한 Ordered Interface, @Profile 을 통한 프로필 버전 관리가 쉬워졌다.


(7) Web 을 개발하기 위한 도구들이 생겼다. @RestController 같은 것들이 그것이다.


(8) Web Socket 이나 STOMP 등의 프로토콜을 같이 지원한다.


(9) 테스트 환경이 개선되었다. Framework 레벨에서 Mock 을 위한 ServletContext 를 별도로 지원한다.



Spring 5.0


Spring 5.0 은 JDK 8+, 9 등에 대해서 지원하며 Java8을 표준으로 사용한다. 


(1) 코어로직에 있어서 JDK 8의 특징들이 강화되었다.


(2) HTTP 메시지 코덱의 XML과 JSON 지원에 대한 구현이 Encoder 와 Decoder 의 사용을 통해 추상화 되었다.


(3) 웹에 대한 지원이 향상되었다. 특히 Protobuf 3.0 지원이 적용되었다.



개인적으로 Spring 의 꾸준한 발전은 기존의 탄탄한 철학 위에서 효율적으로 트렌드를 받아들이고 있는 것으로 생각된다. 아마도 앞으로도 꽤 오랜기간 장수하지 않을까..



 MyBatis 란 객체지향 언어인 Java 와 SQL Based 인 관계형 데이터베이스(RDBMS) 사이의 데이터를 다루는 방식의 괴리를 해결하기 위해 만들어진 Persistence Framework 의 일종이다.


 Java 에서 DB와의 Connection 을 위해 제공하는 JDBC 를 Wrapping 한 구조로 되어 있으며, 기존의 JDBC 에 비해 많은 장점들을 갖고 있다.


무엇보다 사용이 간단하고, 60% 정도의 생산성 향상이 있다고 한다.

또한 JDBC 사용시 매번 Query Statement 를 생성하는 구문을 작성해야 했던 것과 다르게 쿼리의 재사용과, 코드와의 분리가 좀 더 수월해졌기 때문에 유지 보수 측면에서도 이점을 갖는다.


 주로 mybatis-config.xml 과 같이 별도의 파일에 환경 설정을 분리시키며, 변수 형태로 DB 연결정보들을 관리한다.

간단한 설정 방법은 다음과 같다.



/* 설정 <pom.xml> */
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>



 Maven 을 사용한다면 위와 같이 먼저 pom.xml 에 MyBatis Dependency 를 기입해주어야 한다. 이후 다음과 같이 프로젝트별로 관리하는 개별 설정 파일에 내용을 기입해주면 된다.



/* 설정 <설정파일.xml> */

//Property 설정 분리
<properties resource="mybatis/config.properties">
<property name="username" value="jinsp"/>
<property name="password" value="epD@kef0"/>
</properties>


//커넥션 풀 사용
<dataSource type="POOLED">
//Database Driver
<property name="driver" value="${driver}"/>
//Database Url
<property name="url" value="${url}"/>
//Database UserName
<property name="username" value="${username}"/>
//Database Password
<property name="password" value="${password}"/>
</dataSource>



 이렇게 설정해준다면 MyBatis 의 SqlSessionFactory 를 통해 Connection 을 연결할 수 있다. MyBatis 의 사용을 위해서는 매퍼 파일(Mapper File) 이라 불리는 Xml 설정이 추가로 필요하다. 이는 다음과 같은 형태로 구성이 되어 있다.




<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="StudentRepository">
<select id="selectStudentList" resultType="student">
SELECT
`id` AS "studentId",
`name` AS "studentName"
FROM
student
</select>
</mapper>



 위의 Mapper 설정에 대한 설명을 간단히 하자면, 설정들이 StudentRepository 라는 @Repository 로 명시된 클래스에 매핑되며, 그 안에 학생들의 리스트를 조회하는 쿼리문인 selectStudentList 를 사용할 수 있게 설정되어있다는 뜻이다. selectStudentList 라는 Id 를 가진 SQL 문은 쿼리문의 결과(ResultType)를 Student 형태의 객체의 형태로 반환하며, 요소가 여러개일 경우 List 형태의 Collection 으로 반환한다.


위와 같이 설정을 하고, 위에 명시된 쿼리의 ID 를 SessionFactory 를 통해 불러와서 실행시켜주면 연동이 간단하게 끝난다.




 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 : 테이블의 단위를 가능한 작게 유지해야 한다.




 클라우드를 처음 접했을 때 가이드 문서에서 가장 헷갈리고 이해 못했던 부분이 바로 High Availability 라는 부분이었다.

물론 주니어여서 와닿지 않은 부분도 있었지만 이 부분은 서버 개발자로써 연마해나갈 수록 이해의 폭이 달라지는 내용이었던 것 같다.


 HA(High Availability) 란 서버와 네트워크, 프로그램 등의 정보 시스템이 상당히 오랜 기간 동안 지속적으로 정상 운영이 가능한 성질을 말한다.


 일반적으로 1년 동안의 기간에 계획된 이벤트를 제외하고 장애시간을 5분 이내로 허용한다는 의미의 5 Nines, 99% 의 매우 높은 수준을 목표로 한다. 이를 위해서는 시스템의 모든 부분이 미리 잘 설계되어야 하며, 실제로 사용되기 전에 완전히 테스트가 이루어져야 한다.


 서버 & 클라우드에서 고가용성을 유지하기 위한 대표적인 전략 중 하나는 Cluster를 구조하는 것으로, 이를 통해 한쪽에 장애가 발생하더라도 다른 서버들이 업무를 대신 수행함으로써 시스템 장애를 손쉽게 복구할 수 있다.


 독립적인 디스크를 RAID 형태로 배치하는 디스크 미러링, 여분의 네트워크나 SAN(Storage Area Network)의 완비 등이 요구된다.

HA Cluster 는 주기적으로 heartbeat를 네트워크에 보냄으로써 health check를 모니터링한다.


 장애 복구를 위해 최대한 빠르게 실패를 전달하고, 다른 한 개의 노드에서, 그리고 그마저도 실패하면 전체 노드가 발생한 장애에 대해 시도하는 방식으로 복구를 시도한다



서버개발자에게 매우 중요한 내용이지만, DevOps 가 아닌 Develop 에 집중하는 엔지니어라면 잘 모를 가능성이 크다. 개발자이면서도 운영 및 인프라 전반에 대해 포괄적인 지식이 필요해보인다.




 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


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



+ Recent posts