오늘날 많은 웹서비스는 전통적인 Server Side Rendering 방식대신 정적 리소스와 동적 WAS 서버를 분리하는 Client Side Rendering 방식을 사용하고 있으며, 이와 동시에 Single Page Application 형태로 구축하고, 정적 리소스들을 CDN 형태로 서비스하는 경우가 많다.

 

이렇게 구성할 경우 정적 리소스를 엔드유저 근처에서 제공함으로써 Latency 를 낮추고 사용자 경험을 향상시키는 동시에 웹 서버의 부담을 확연히 줄일 수 있다.

또한 Client 와 Server 가 분리되게 된다면 보다 프론트엔드와 백엔드의 역할 구분이 분명해지고 인프라 구축 및 개발 환경 관리가 용이해진다.

 

단, 정적 리소스를 CDN 을 통해 제공하게 될 경우 Caching 옵션에 대해 주의할 필요가 있으며, 그 중에서도 가장 중요한 Cache-Control 헤더에 대해 간략히 기술해보고자 한다.

 

HTTP 헤더에 Cache-Control 헤더를 포함시키면 각 정적 리소스에 대해 헤더를 지정할 수 있고 각자의 캐싱 옵션을 제공할 수 있다. 이 옵션은 다음과 같이 구성되어진다.

위의 예시에서 public 은 모든 캐시가 응답의 사본을 저장한다는 것을 의미한다. 이 말은 CDN, Proxy 서버들이 모두 해당 리소스를 캐싱해도 된다는 것을 의미한다. 이 값을 private 으로 세팅하면 응답의 최종 수신자 (클라이언트 브라우저) 만 파일 사본을 저장할 수 있게 된다.

 

max-age 는 응답이 Fresh 한 것으로 간주되는 시간 단위(초) 를 정의한다. 서버의 컨텐츠가 새로 갱신된다면 200 응답과 함께 새 파일을 다운로드해서 이전 캐시를 Refresh 하고 캐싱 헤더를 유지한다.

서버 컨텐츠가 이미 Fresh 하다면 304 응답을 주며, 새로 파일을 다운로드하지 않는다.

 

그 외에 해당 헤더에 올 수 있는 중요한 다음과 같은 옵션들이 있다.

  • no-store : 이 옵션이 지정될 경우 응답은 절대 캐싱되지 않는다. 모든 요청은 서버를 히트하게 된다.
  • no-cache : 이 설정은 '캐싱하지 않음' 을 의미하지 않으며, 단순히 서버에서 유효성을 재검사하기 이전까지 캐시가 복사본을 제공하지 않음을 의미한다. 이 옵션은 Fresh 한 컨텐츠를 확인하는 가장 합리적인 방법이고, 동적인 HTML 페이지가 사용하기 적합하다.
  • must-revalidate : max-age 와 같이 사용해야하며, 유예기간이 있는 no-cache 옵션처럼 동작한다.
  • immutable : 클라이언트에게 파일은 절대 바뀌지 않음을 알린다

Cache-Control 을 적용한 실제 서비스별 모범 사례를 확인해보자.

 

(1) 온라인 뱅킹 서비스 - 금융 서비스는 트랜잭션과 계좌를 항시 최신으로 보여줘야하고, 어떤 스토리지에도 캐싱해서는 안된다.

(2) 실시간 기차 시간표 서비스 - 실시간성 업데이트가 필요하고, 데이터의 Freshness 가 가장 중요하다.

(3) FAQ 페이지 - 컨텐츠는 자주 업로드되지만 반영이 빠를 필요는 없으므로 페이지는 캐싱되어도 된다.

(4) 정적 CSS 또는 Javascript 번들 - 대부분 정적 파일들은 app.[fingerprint].js 와 같이 관리되며 자주 업데이트되어진다.

 

 

내용은 다음 페이지의 글을 참고하여 작성되었다. 중요한 부분 위주로 번역해서 정리해놓았으나, 웹서비스 바이탈을 설계 시에 고려할만한 명문이라고 생각한다.

 

https://csswizardry.com/2019/03/cache-control-for-civilians/

 

가십 프로토콜 (Gossip Protocol) 이란 분산 환경에서 메시지를 전달하는 커뮤니케이션 방식의 하나이다.

외국에서는 바이러스가 퍼지는 방식으로 동작한다하여 Epidemic Protocol 과 동의어로 사용되기도 한다고 한다.

 

가십 커뮤니케이션 방식의 특징은 소문이 전파되어나가듯 Broadcast 해주는 마스터가 없이 각 노드가 주기적으로 TCP/UDP 기반으로 메타데이터를 주고받으면서 데이터를 전송하는 점에 있다.

 

각 노드들은 주기적으로 다른 노드의 Health Check 를 수행하고 통신하므로, 주기적으로 Peer to Peer (P2P) 의 통신으로 전달이 일어나며, 일반적으로 신뢰성은 보장하지 않는다.

다음 그림을 통해 일반적인 분산 환경에서의 싱크업 방식인 Mesh Broadcast 방식과의 차이점을 확인할 수 있다.

 

가십 프로토콜은 아주 가볍기 때문에 특히 클라우드와 같은 분산 환경에서 성능 및 안정성을 위해 많이 사용되는데,

Peer 간 데이터의 비동기 Sync Up 이 가장 큰 예시가 된다.

 

Docker 같은 경우에도 내장된 Control Plane 에서 노드 발견을 위해 Gossip 메커니즘을 사용한다.

이를 통해 기존 분산 환경에서의 동기화 알고리즘인 Paxos 나 Raft 의 어려움과 복잡함 대신 다소 가볍게 멀티 클러스터를 관리할 수 있는 근간 알고리즘으로 동작할 수 있게 된다.

 

 

 

포트 미러링은 소프트웨어 개발자에게 친숙한 단어는 아니다.

 

포트미러링은 흔히 알려진 포트 포워딩과는 다른 개념으로, 네트워크 스위치 상 포트에 전달되는 네트워크 패킷을 다른 포트로 복사하는 개념이다. 

이 때 복제되는 대상 포트를 Mirroring Port 라 하며, 복제된 포트를 Mirrored Port 라고 한다.

 

포트 미러링은 주로 Mirroring Port 의 Ingress 트래픽을 모니터링해서 서버에 연결되는 패킷에 대해 감청하는 목적(Ingress Filtering) 으로 사용되며, 용도에 따라 Egress 트래픽을 모니터링하기도 한다.

(여기서 Ingress 는 들어오는 방향을, Egress 는 나가는 방향을 의미한다고 이해하면 좋다.)

 

또한 네트워크 엔지니어 / 관리자라면 디버그 및 분석 용도로 사용하는 기술이기도 하다.

 

다음은 포트포워딩과의 개념을 비교한 자료이다.

 

<출처 : http://ganeshbhatnetwork.blogspot.com/2013/12/difference-between-port-forwarding-and.html>

 

포트포워딩이 인식된 IP Address 및 Port 를 포워딩된 다른 주소로 매핑시키는 개념이라면, 포트미러링은 패킷을 복제하는 개념이라고 이해하면 쉽다.

 


프로그램 언어를 해석하고 실행시키는 대표적인 방법으로 Compile 과 Interpret 방식이 있다.

Compile 작업은 Compiler 에 의해 실행되고, Interpret 작업은 Interpreter 에 의해 실행되는데, 두 컨셉이 명확하게 다르기 때문에 

많은 프로그래밍 언어들은 둘 중 한가지 방식을 통해 언어를 실행하도록 설계된다. (Java 와 같이 두가지를 모두 채용하는 경우도 있다!)

그렇기 때문에 Compiler 와 Interpreter 를 이해하는 것은 어떤 언어를 배우던지간에 해당 언어의 구동원리를 배울 수 있는 중요한 선행학습이라할 수 있겠다.


컴파일 (Compile)

프로그래밍 언어를 Runtime 이전에 기계어로 해석하는 작업 방식이다.
이때 원래의 소스를 원시 코드, 바뀐 코드를 목적 코드(Object Code) 라 한다.

런타임 이전에 Assembly 언어로 변환하기 때문에 구동 시간이 오래걸리지만, 구동된 이후는 하나의 패키지로 매우 빠르게 작동하게 된다.
구동시에 코드와 함께 시스템으로부터 메모리를 할당받으며 할당받은 메모리를 사용하게 된다.

런타임 이전에 이미 해석을 마치고 대게 컴파일 결과물이 바로 기계어로 전환되기 때문에 OS 및 빌드 환경에 종속적이다.
그러므로 OS 환경에 맞게 호환되는 라이브러리와 빌드환경을 구분해서 구축해줘야 한다.

Compile 언어의 대표격으로 C / C++ 와 같은 언어들을 들 수 있으며, Java 역시 Byte Code 로 바꾸기 위한 과정에서 컴파일을 수행한다.


인터프릿 (Interpret)

런타임 이전에 기계어로 프로그래밍 언어를 변환하는 컴파일 방식과 다르게, 런타임 이후에 Row 단위로 해석(Interpret) 하며 프로그램을 구동시키는 방식이다.

프로그래밍 언어를 기계어로 바로 바꾸지않고 중간 단계를 거친 뒤, 런타임에 즉시 해석하기 때문에 바로 컴팩트한 패키지 형태로 Binary 파일을 뽑아낼 수 있는 Compile 방식에 비해 낮은 퍼포먼스를 보이게 된다.

런타임에 직접 코드를 구동시키는 특징이 있기 때문에 실제 실행시간은 느리며, 대신 런타임에 실시간 Debugging 및 코드 수정이 가능하다.

또한 메모리를 별도로 할당받아 수행되지 않으며, 필요할 때 할당하여 사용한다. 이와 관련되어 코드의 흐름 자체도 실제 필요할 때, 실제 수행되어야하는 시점에 수행되기 때문에 덕타이핑(Duck Typing) 이 가능한 측면이 있으나, 반대로 정적 분석이 되지않는 Trade off 를 갖고 있다.

 

대표적인 Interpreter 언어로는 Javascript 와 같은 스크립팅 언어들이 있다. 하지만, 스크립트 언어 뿐 아니라 컴파일 이후의 동작에서 Interpret 을 수행하는 언어들도 많이 존재한다.


많은 프로그래밍 언어들의 인터프리터는 해석을 위한 Virtual Machine 을 두고, Machine 위에서 Interpret 을 수행하게 되는데, 이 때 해석의 기반이 되는 머신들이 OS 환경들을 지원해줌으로써, 해당 방식으로 인터프리터는 OS 및 플랫폼 에 종속되지않는 프로그램 구동이 가능하게 된다.
(이런 특징을 지닌 Interpreter 는 Java 의 JVM 과 Python 의 Analyzer 가 있겠다.)


컴파일러와 인터프리터의 차이는 잘 이해하고 언어와 환경을 파악하는데 활용하는 것이 중요하다.

 

OLTP 와 OLAP 는 개발자로서 생각보다 친숙한 개념임에도 불구하고 실제로 많이 사용되는 단어가 아니다보니 생소한 경우가 많다.

 

* OLTP (Online Transaction Processing)

 OLTP 란 온라인 트랜잭션 처리를 말하며, 네트워크 상의 온라인 사용자들의 Database 에 대한 일괄 트랜잭션 처리를 의미한다. 

흔히 말하는 "트랜잭션(Transaction) 처리" 를 OLTP 라 부른다. 트랜잭션이라 부르는 용어의 의미 자체가 OLTP 의 의미를 포함하고 있다고 할 수 있겠다. 

Transaction 에 대한 보다 자세한 설명은 다음 포스팅을 참조해보자.

https://jins-dev.tistory.com/entry/Database-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98%EC%9D%84-%EC%9C%84%ED%95%9C-ACID-%EC%9D%98-%EA%B0%9C%EB%85%90

 

트랜잭션의 주 특징은 그루핑된 연산의 실패시, Rollback 이 지원된다는 점이다.

주로 기술적 특성상, 대규모의 처리보다는 소규모의 정교한 데이터 구성이 필요한 데이터의 처리가 중점이 된다.

 

 

* OLAP (Online Analytical Processing)

 OLAP 란 Database 자체적으로 운용되는 시스템이라기 보다는 데이터 웨어하우스 등의 시스템과 연관되어 Data 를 분석하고 의미있는 정보로 치환하거나, 복잡한 모델링을 가능하게끔 하는 분석 방법을 말한다.

 

기능 자체에 중심을 두는 OLTP 와는 다르게 사용하는 목적과 주제에 보다 중점을 둔다.

그렇기 때문에 주로 대용량의 데이터에 대해 처리하고 보다 복잡한 Data processing 으로 의미를 추출하는데 중점을 둔다.

 

대부분의 경우 OLAP 연산을 수행한다 라고 하면 적재된 데이터에 대한 분석 또는 SELECT Query 를 통한 데이터 스캔 Operation 이 주가 된다. 이는 OLTP 가 Transaction 의 과정에서 INSERT/UPDATE 를 중점적으로 수행하는 것과 대조되게 된다.

(하지만 언급했듯이, 쿼리의 읽기냐 쓰기냐 에 따라 구분되는 개념이라고 보기는 애매하다!)

 

 

면접에서 물어보면 트랜잭션은 알아도 OLTP 는 모르는 경우가 많다..

용어 자체가 중요하다고 보지는 않지만 알아둘 필요가 있겠다.

 

CIDR(사이더) 기법이란, 기존의 IP 주소 할당방식인 Network Class 방식을 대체하기 위해 개발된 도메인간 라우팅 기법으로 최신 IP 할당 방법이다.

 

CIDR 기법은 사이더 블록이라 불리는 그룹으로 IP 주소들을 그루핑하고 그룹들을 계층적으로 관리함으로써 기존의 Network Class 방식에 비해 유연하게 동작할 수 있도록하며, IP 주소 체계를 보다 효율화한다.

(더 많은 IP 주소를 이용할 수 있도록 한다.)

또한, 계층적 구조이기 때문에 Routing 에 있어서 부담이 최소화되는 장점이 있다.

 

기존의 네트워크 클래스 방식의 기법은 IP 주소에 범위를 정하고 해당 범위대로 Class 를 분류한다.

가령, 나뉘는 클래스의 범위는 다음과 같아진다.

 

- A Class : 0 ~ 127

- B Class : 128 ~ 191

- C Class : 192 ~ 223

- D Class : 224 ~ 239

- E Class : 240 ~ 245

 

위와 같은 방식에서, Class 의 범주에 딱맞지않는다면, 호스트 주소공간을 할당하기 위해 클래스를 선택할 때 문제가 발생한다. 즉, 더 높은 클래스를 선택함으로써 남은 주소 블록들을 낭비할것인지, 아니면 낮은 클래스를 사용해서 주소의 부족함을 감수할 것인지에 대한 문제가 생기게 된다.

 

CIDR 은 이에 비해 보다 유연성을 제공하고, 그 덕분에 더 많은 IP 주소 공간의 운용을 가능하도록 한다.

CIDR(사이더)을 이용한 주소 체계는 다음과 같이 지정될 수 있다.

 

 

이는 해당 IP 주소의 첫 24비트를 네트워크 라우팅을 위한 주소로 사용한다는 의미이다.

 

따라서 해당 주소의 표현은 위와 같이 이진수로 나타낼 수 있고, 네트워크 주소로 표현되는 부분과 네트워크 주소 뒤에 호스트 번호가 포함되게 된다. 

다음은 예시로 나타낸 주소에 대해 IP 주소 체계를 분석해본 것이다.

비트연산을 이용해 Subnet Mask 와 Broadcast Address 를 어렵지않게 구해낼 수 있고, 해당 정보를 바탕으로 Host 의 가용 범위 역시 구해낼 수 있다.

즉, 위의 주소 192.168.0.15/24 의 의미는 192.168.0.1 부터 192.168.0.255 까지의 주소 범위를 의미하는 CIDR 표기법이라 할 수 있다. (앞의 24 bit가 Masking 이기 때문이다.)

이를 192.168.0.15/32 로 표기하면 이는 192.168.0.15 주소 그 자체를 의미한다.

 

CIDR 는 네트워크를 이해하는 데 있어 필수적인 기초개념이고, 생각나지않을 때마다 정리해보면 IP 주소 체계를 이해하는데 도움이 될 수 있다.

 

 

SOA(Software Oriented Architecture) 란, 2000년대 초반부터 IT 업계 전반에 걸쳐 녹아든 IT System 의 패러다임이다.

 

고전의 Client-Server Architecture 에서 EJB 로 대표되는 n-Tier Model Architecture 로 진화한 웹 아키텍쳐는 2000년대 이후부터 비즈니스 요구사항에 발빠르게 대처하기 위한 구조로 Service-Oriented Architecture 라는 아키텍쳐로 진화한다.

 

SOA 란, 그전까지의 Application 의 Massive 한 기능들을 비즈니스적인 용도로 분류하고 기능 단위로 묶어서 하나의 표준 Interface 를 통해 각각을 "서비스" 로써 조합하는 Software Architecture 이다.

 

IT 업계의 요구사항이 많아지고 변화가 빨라짐에 따라 이에 대처하기 위해 나타난 Architecture 이고, 구성요소를 3가지로 분류한다.

 

1. Service Consumer - 서비스의 사용자. 사용자는 실제 클라이언트(End-User) 또는 다른 서비스가 될 수 있다.

2. Service Provider - 서비스 사용자에 요청에 맞는 서비스를 제공. Provider 는 다른 Service 의 Consumer 가 될 수도 있다.

3. Service Registry - 서비스의 정보를 저장한다. Provider 가 Registry 를 통해 Consumer 에게 서비스를 제공한다.

 

여기서 중요한건 Service Provider 와 Service Consumer 로, 이들은 실제 서비스의 유저 뿐 아니라 소프트웨어 아키텍처 내부의 "모듈" 도 대상이 될 수 있다. 

즉, SOA 는 비즈니스적으로 구분된 Service 들을 느슨하게 연결하며, 각 컴포넌트를 독립적으로 운용하여 조립이 가능하게끔 한다.

 

일반적으로 Service 를 위해 서비스 기능별로 모듈을 분리하고, 각 모듈이 다른 모듈과 상호작용할 수 있도록 만들어진 Architecture 를 SOA 라고 이해하면 된다.

 

여기까지 이해했으면 떠오르는 최근의 Architecture 모델이 있을 수 있다.

바로 Micro Service Architecture(MSA) 이다.

 

Micro Service Architecture 역시 각 서비스를 독립적으로 운용하며 서비스들을 조합하여 End User 에게 서비스를 제공하는 형태로 이루어진다. 그렇다면 이는 SOA 와 같은 아키텍처인가?

 

결론부터 말하자면, Micro Service Architecture 는 SOA 의 부분집합이라고 할 수 있다.

정확히는 SOA 는 패러다임이며, 그를 위한 Architecture 의 초안이고, Micro Service Architecture 는 SOA 의 패러다임을 따르되, 그 아키텍처를 따르지 않는다.

 

SOA 와 MSA 의 차이점

 

1. SOA 는 모듈의 의존성은 줄이되 모듈 내에서 공유할 수 있는건 최대한 공유하는 정책을 사용한다.

반면, MSA 는 가능한 공유하지 않고 모듈들이 독립적으로 운용될 수 있도록 아키텍처를 디자인 한다.

 

2. SOA 는 서비스의 Flow 를 유지하려하지만, MSA 는 Flow 의 구별을 요구한다.

가령, 서비스 내에서 결제를 하고자 할때, SOA 는 관련된 루틴을 수행하여 결제를 지원함으로써, 유저에게 제공해주는 "서비스" 를 1차 목적으로 한다.

반면, MSA 는 유저에게 관련된 루틴과 결제 루틴을 별도로 이용하게끔 한다. 즉 서비스 내의 독립이 아닌 독립된 서비스를 지향한다.

그렇다보니 SOA 아키텍처는 대게 어느정도 업격한 Protocol 과 Message 체계를 운용하게 되고, MSA 의 경우 별도의 체계가 없이 경량화된 프로토콜을 통해 운용되게 된다.

 

3. SOA 는 서비스들의 재사용에 중점을 두지만 MSA 는 서비스들의 독립을 추구한다.

이는 블로그 내 다른 포스팅에서 언급한 적 있는 Monolithic Architecture 와 유사한 부분으로, SOA 는 MSA 에 비해 보다 Monolithic Architecture 에 가깝다.

(참조 : https://jins-dev.tistory.com/entry/%EC%A0%84%ED%86%B5%EC%9D%98-%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EB%AA%A8%EB%8D%B8-%EB%AA%A8%EB%86%80%EB%A6%AC%EC%8B%9DMonolithic-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98)

 

 

아키텍처를 디자인하는 일은 늘 어려운 일이고, 실무에서의 경험을 상당히 요하는 일이다.

아키텍처의 디자인과 이해를 위해서는 최신 트렌드 뿐 아니라 과거의 모델까지도 숙지해두는 것이 필요하겠다.

 

좀 더 자세한 설명은 다음 링크를 참조해보자.

https://dzone.com/articles/microservices-vs-soa-2

https://www.bmc.com/blogs/microservices-vs-soa-whats-difference/

 

 

구글 Protobuf 는 강력한 데이터구조이며 환경간 이식성이 뛰어나고 패킷으로 사용할 때, 자체의 성능(통신 속도 및 작은 패킷 크기)이 뛰어나지만, 정해진 형식이 있다보니 사용에 있어서 알아두어야할 점들이 몇가지 있다.

 

실무에서 사용하면서 알아두어야 했던 점들 및 특징적인 점들 몇가지를 정리해보았다.


1. protobuf 는 패킷간 상속과 추상화를 지원하지않는다.

프로토버프를 도입하기 가장 꺼려지는 이유인데, protobuf 의 Data Structure는 상속과 추상화와 같은 생산성을 위한 개발자들의 구조를 부정한다.

Protobuf 는 애초에 범용(?) 목적으로 설계되었다기 보다는 확실한 단일 목적에 대해 Compact 하게 설계된 Data Structure 이다.

즉, 확실히 의미를 갖는 필요한 데이터만 저장할 수 있게 되어있으며, 그렇기 때문에 Stream 으로 사용할 시 필요한 정보만 주고받는 효율성을 이점으로 취할 수 있는 반면, 데이터를 담는 데 있어서 유연하지 못하다.

 

그렇기 때문에 프로토콜 버퍼를 패킷으로 통신을 해야 한다면 모든 패킷에 필요한 정보들을 분류해서 정의해주는 것이 필요하다.

 


2. protobuf 의 패킷 넘버링은 생각보다 엄격하지않다. 하지만 패킷의 순서는 매우 중요하다.

다음과 같은 에러 처리를 위한 프로토콜 버퍼 Data Structure가 있다고 가정해보자.

위의 데이터 구조에서 time_stamp 패킷의 넘버링을 4로 바꿔도 무방하다. 프로토콜 버퍼에서 구조 내에서 중요한 것은 패킷들의 순서를 지키는 일이다.

다만, Protocol Buffer 를 enum 형태로 정의해서 쓰는 경우라면 조금 얘기가 다른데,

위와 같은 Enum 패킷에서는 numbering 이 의미를 갖는다. enum 의 경우 넘버링이 Enum 클래스의 ordinal 과 동치되기 때문에, Compile 해서 사용할 경우 패킷이 다르다면 예기치 못한 에러가 발생할 수 있다.

 

 

3. Protobuf 구조가 프로토콜로 정해졌다면 데이터 구조는 변경하지 않는 것이 좋다.

통신에 있어서 Protobuf 를 사용하는데, 기존 데이터 구조가 삭제 또는 변경된다면 그 구조를 삭제 & 변경하는 것이 아니라 새로 패킷을 추가하는 편이 좋다.

그 이유는 하위호환성 때문으로, 통신하는 양쪽의 Protobuf 빌드가 항상 동기화가 완벽하다면 문제가 없지만, 개발을 하다보면 버전이 달라질 수밖에 없다.

이 때 문제를 방지하기 위해 데이터 구조의 크기를 늘리는 방법을 선택해야 한다.

하지만 패킷의 크기가 커질지는 염려하지 않아도 된다. Protobuf 의 특징 상 입력되지않는 패킷은 보내지않도록 퍼포먼스 측면에서 최적화가 지원된다.

 


4. 구글이 지원해주는 protobuf 라이브러리가 있으며, 이를 사용하면 프로토버프의 사용이 매우 편리해진다. 

다음 링크를 참조하자.

https://github.com/protocolbuffers/protobuf

 

protocolbuffers/protobuf

Protocol Buffers - Google's data interchange format - protocolbuffers/protobuf

github.com

protobuf 라이브러리는 언어별로 필요한 기능들을 유틸리티 형태로 지원한다.

아주 방대하니 전체를 쓸 생각보다는 환경에 맞게 필요한 만큼만 모듈을 가져다 쓰는 것이 좋다.

또한 protobuf 라이브러리에는 proto 파일 내에서 사용할 수 있는 공통 protobuf 형식들도 정의되어 있으므로 참고하는 것이 좋다. 
가령 proto 파일 내에서 Collection 이나 timestamp 등의 기능을 사용할 수 있도록 정리되어있다. 

 

 

5. 당연한 얘기지만 proto 파일의 주석조차 compile 결과로 빌드된 소스에 포함 된다. 

만약 소스 자체로 어떤 스크립트가 실행되어야 한다면 환경에 주의하자.

주석에 한글 특수문자가 포함된 proto 파일을 자동화 스크립트로 돌리다가 문제가 생기는 일이 더러 있었다.

 

 

프로토콜 버퍼는 구글이 지원하고 있는 직렬화방식이고 강력하며 쓰임새도 다양하다.

하지만 현재로써는 아는만큼 사용할 수 있는 도구임이 분명하다. 사용에 있어 유의하고 항상 공식 지원을 참고하는 것이 좋겠다.

 

면접에서 단골처럼 등장하는 질문이자, 컴퓨터 공학과 시험에서 한번쯤은 보았을 법한 CS 기본 지식을 정리하고자 한다.

 

컴퓨터는 데이터를 저장할 수 있는 몇가지 종류의 공간들을 갖고 있고, 해당 공간들은 쓰임새가 다르고 만들어진 이유가 다르기 때문에 각각 I/O 작업에 있어서 다른 퍼포먼스를 낸다.

 

그 중에서도 Access 에 대한 다음 Computing Operation 의 속도 비교는 알아두어야 한다.

 

 - CPU Register

 - Context Switch

 - Memory Access (RAM)

 - Disk Seek (HDD)

 

위의 Operation 들에 대한 속도 비교 결과는 빠른 순서대로 다음과 같다.

 

1. CPU Register Access

2. Memory Access

3. Context Switching

4. Disk Seek

 

(1) CPU 레지스터에 대한 접근은 단 한번의 CPU 사이클만으로 이루어지기 때문에 즉각적으로 이루어진다.

한 사이클이라는 것은 말그대로 번개와 같은 속도로 이루어진다는 뜻이다.

 

(2) Memory Access 는 일반적으로 RAM 에서 데이터를 읽어내는 것을 말하며, 당연히 RAM 의 목적에 맞게 HDD 로부터 읽어오는 것보다 빠르다.

일반적인 상태에서의 작업은 레지스트리에 접근하는 것에 비견될만큼 빠를 수 있지만 논리 구조 위에서 동작하기 때문에 Virtual Memory Swapping 과 같은 작업에서 자유로울 수 없으며 이런 경우에는 Disk Access 만큼 느려질 수도 있다. 

 

(3) Context Switching 는 대체적으로 빠른 접근이 보장이 된다. 하지만 여러개의 프로세스가 동시에 실행되며 스위칭이 빈번하게 이뤄질 경우 굉장히 느려질 수도 있다.

 

(4) Disk Seek. HDD 에 대한 Disk Seek 은 위에 언급한 Operation 들에 비해 빠를 수가 없는 작업이지만 캐싱을 통해 비약적인 성능 향상이 가능하다.

BUS 에서의 병목을 피할 수 있으며 캐싱을 통해 Main Memory 에 Access 하는 것 만큼의 퍼포먼스를 기대할 수도 있다.

 

 

면접에서 갑작스레 질문받은 내용이라 당황했던 적이 있었다.

알고있었던 내용이라 답변은 잘 했으나... 끝나고나서 다시 점검해볼만큼 기본기가 아직 충분치 못한 것 같아 정리해둔다.

 

 


메서드와 함수의 차이점은 간단하면서도 기초적인 내용이지만 자주 되새겨지는 개념이 아닌데다가, 비슷하게 혼용되어 사용되다보니 많이 잊게 되는 내용이다.


차이점 먼저 서술을 하자면, Method 는 "객체" 에 대한 코드를 말하고 동작(Operation)의 결과로 동작을 수행한 객체가 영향을 주거나 받는다.


즉, Operation 은 해당 메서드를 소유한 객체 중심으로 발생한다.


반면, Function 은 특정 형태의 Data 를 받아서 내부 동작(Operation)을 수행한 후 특정 형태의 Output Data 를 반환한다.


즉, Operation 은 객체와 무관한 독립적인 그 자체의 코드 조각으로의 의미를 지닌다.



- 정리


: Method - Member. 객체에 대한 Operation 을 수행하는 코드 조각.


: Function - Free. 객체와 독립적인 Operation 을 수행하는 코드 조각.



C 에서 사용되는 Logic 들은 모두 함수(Function) 이며 Java 에서 사용되는 Logic 들은 모두 메서드(Method) 이다.




+ Recent posts