웹 서버
개념 : 웹 서버는 클라이언트가 웹 브라우저에서 어떠한 페이지 요청을 하면 웹 서버에서 그 요청을 받아 정적 컨텐츠를 제공한다. 여기서 정적 컨텐츠란 단순 HTML, CSS, JS, 이미지, 파일 등 즉시 응답할 수 있는 컨텐츠다.
하는 일
예 : Apache, Nginx
WAS
개념 : WAS는 웹 서버와 웹 컨테이너가 합쳐진 형태로서 웹 서버 단독으로는 처리할 수 없는 데이터베이스의 조회나 다양한 로직 처리가 필요한 동적 컨텐츠를 제공한다. 덕분에 사용자의 다양한 요구에 맞춰 웹 서비스를 제공할 수 있다. WAS는 JSP, Servlet 구동 환경을 제공해주기 때문에 웹 컨테이너 혹은 서블릿 컨테이너라 불린다.
하는 일
예 : Tomcat
웹 서버와 WAS를 같이 사용하는 이유
프록시 서버란 클라이언트와 서버 사이의 중계 서버이자 통신을 대리 수행하는 서버로 프록시 서버의 위치에 따라 포워드 프록시, 리버스 프록시 서버로 나뉜다.
포워드 프록시 : 일반적인 프록시라고 하면 포워드 프록시를 말한다. 클라이언트에서 서버로 리소스를 요청할 때 직접 요청하지 않고 프록시 서버를 거쳐서 요청한다.
리버스 프록시 : 포워드 프록시와 반대로 애플리케이션 서버의 앞단에 위치하여 클라이언트가 서버를 요청할 때 리버스 프록시를 호출하고, 리버스 프록시가 서버로부터 응답을 전달받아 다시 클라이언트에게 전송하는 역할을 한다.
우리가 구성하는 일반적인 web(Apache, nginx) - was(Tomcat) 분리 형태에서 web(Apache, nginx)이 리버스 프록시 서버다. 참고로 Apache에는 web, was가 둘다 존재하지만 리버스 프록시 서버라고 볼 수는 없다.
웹 스토리지
오리진이 같은 브라우저 내에서 공유된다.
로컬 스토리지와 세션 스토리지로 구분된다.
구분 | 로컬 스토리지 | 세션 스토리지 |
---|---|---|
데이터 유지 | 브라우저 종료 시 보관 | 브라우저 종료 시 삭제 |
쿠키
origin이란?
로그인은 세션 기반 인증방식과 토큰 기반 인증방식으로 구분된다.
세션 기반 인증방식 : 서버 측에서 관리하는 세션 ID를 통해 인증
장점 : 서버에 저장하기 때문에 관리가 매우 편하고 효율적
단점 : 서버에서 관리하기 때문에 세션 저장소를 따로 마련해야 하는데 트래픽이 몰리면 그에 따른 오버헤드 발생
참고 : 보통 세션을 다루는 라이브러리는 세션 ID를 생성하면 쿠키에 해당 값이 설정된다. 물론 쿠키가 아닌 로컬 스토리지 또는 세션 스토리지에 담을 수 있지만 보통 쿠키에 담는 것이 관례이며 일반적이다.
토큰 기반 인증방식 : 토큰 주로 JWT 토큰을 통해 인증
장점 : 사용자 인증에 필요한 모든 정보는 토큰 자체에 포함되기 때문에 별도의 인증 저장소가 필요 없음
단점 : 토큰이 탈취당할 경우 디코딩했을 때 데이터 열람 가능
참고 : 토큰을 프론트엔드에서 어디에 저장할지가 이슈인데 https 사용 시 보통 refresh 토큰만 쿠키에 저장하고 access token은 프로그램(ex : JS)의 로컬 변수에 담는다고 한다. access token 같은 경우 Header - Authorization으로 전송하는 편이라고 한다.
모니터링을 하면 서버 과부하에 대한 대처를 할 수 있다. "어떤 페이지에 어떤 트래픽이 얼마나 발생했나" 혹은 "어떤 네트워크에서 병목현상이 일어났나" 등 모니터링을 통해 알 수 있다. 또한 Slack 같은 협업 툴과 연동해서 설정한 임계치를 기반으로 알림 서비스를 통해 서버 과부하를 예방할 수 있다. 대표적으로 AWS CloudWatch, Netdata, Prometheus 등이 있다.
로드밸런서란 서버에 가해지는 트래픽을 여러 대의 서버에게 균등하게 분산시켜주는 역할을 하는 장치를 뜻한다. 또한 로드밸러서는 한 서버에 장애가 발생하면 트래픽을 다른 서버로 리다이렉션하여 시스템 중단을 방지할 수도 있다.
외부 서비스에 의한 문제를 방지하기 위해 등장한 것이 서킷 브레이커이다. 서킷 브레이커는 문제가 발생한 지점을 감지하고 실패하는 요청을 계속하지 않도록 방지한다. 이를 통해 시스템의 장애 확산을 막고, 장애 복구를 도와주며 사용자는 불필요하게 대기하지 않게 된다. 즉, 서킷 브레이커 패턴은 클라이언트 측면에서 장애를 방지하기 위한 도구로써, 실패할 수 있는 작업을 계속 시도하지 않도록 방지한다. Msa 환경에서 많이 사용한다고 한다.
쿼리 개선 : 인덱스 추가, select *
지양 등을 통해 서버 부하를 감소시킬 수 있다.
CDN을 통한 컨텐츠 제공 : CDN을 통해 사용자 가까이, 그리고 분산된 대규모 서버 네트워크를 기반으로 컨텐츠를 제공해서 메인 서버에 대한 부하를 줄일 수 있다.
컨텐츠 캐싱 : 네트워크 트래픽을 해결하는 가장 좋은 방법은 해당 트래픽이 발생하지 않도록 하는 것이다. 브라우저 캐시(쿠키, 로컬 저장소, 세션 저장소)를 통해 해당 요청에 관한 항목을 캐시에서 응답을 읽어 네트워크 요청에 관한 비용을 제거할 수 있다.
컨텐츠 압축 : 텍스트 기반 리소스는 gzip 또는 Brotli를 통해 압축해야 한다. 압축하면 70% 정도까지 압축할 수 있다. 다만 압축했기 때문에 압축을 풀기 위해 서버에서 자원(CPU)을 사용하는 양까지 고려해야 하는데 보통은 압축하면 좋다.
WebSocket은 서버와 클라이언트 간의 메시지 교환을 위한 통신 규약(프로토콜)이다. 최초 접속에서만 http 프로토콜 위에서 handshaking하기 때문에 http header를 사용한다. WebSocket을 위한 별도의 포트는 없으며 기존 포트(80, 443)를 사용한다.
WebSocket의 특징
WebSocket 동작 과정
HandShake : 최초 연결 요청 시 클라이언트에서 Http를 통해 웹서버에 handshake 요청을 한다. handshake를 위해 클라이언트는 서버에 아래와 같은 Http header를 보낸다.
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com
그러면 서버는 아래와 같이 응답한다. 101 Status는 성공을 의미한다.
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
양방향 통신 : client와 server는 서로 메세지를 보내며 통신하는데 프레임 단위로 이루어진다. 기존 http보다 가볍다.
연결종료 : client 혹은 server 양측 누구나 연결을 종료할 수 있다. 연결 종료를 원하는 측이 close frame을 상대쪽으로 전송하면 된다.
WebSocket 한계
HTML5 이후에 나온 기술
WebSocket은 HTML5이후에 나온 최신 기술이다. 따라서 Html 5이전의 브라우저에서는 동작이 안될 수 있다.
대안으로 socket.io, SockJS 등
WebSocket은 데이터를 주고 받을 수 있게 해줄 뿐 그 이상의 일을 하지 않음
HTTP는 형식을 정해두어서 모두가 약속을 따르기만 하면 해석 할 수 있지만, WebSocket은 형식이 정해져 있지 않아 애플리케이션에서 쉽게 해석하기 힘들다.
대안으로 Stomp가 있다. Stomp(Simple Text Oriented Message Protocol)는 채팅 통신을 하기 위한 형식을 정의할 수 있다.
유니캐스트 : 유니캐스트란 1:1 통신 형태로 하나의 송신자가 하나의 수신자에게 데이터를 전송하는 방식을 말한다. 가장 일반적인 네트워크 전송 형태로 대표적으로 HTTP, Email 통신이 있다.
멀티캐스트 : 멀티캐스트는 1:N 통신 형태로 하나의 송신자가 특정 그룹의 여러 수신자에게 데이터를 전송하는 방식을 의미한다. 이것은 특정 그룹의 멤버들에게만 데이터를 전달하고 다른 그룹의 멤버들에게는 전달하지 않는다. 대표적인 예로 방송이 있다.
브로드캐스트 : 브로드캐스트는 1:N 통신 형태로, 하나의 송신자가 네트워크에 연결된 모든 수신자에게 데이터를 전송하는 방식이다. 대표적인 예로 ARP가 있다.
API Gateway는 규모에 상관없이 API를 생성, 관리, 모니터링하고 보호하는 서비스다. 클라이언트는 개별 서비스 엔드포인트 대신 API Gateway로 요청을 보내며, API Gateway는 설정에 따라 클라이언트 대신 각 엔드포인트로 요청을 라우팅하고 응답을 클라이언트에게 전달하는 프록시 역할을 수행한다. API Gateway는 다음과 같은 이점을 제공한다.
주로 MSA에서 많이 사용
IPv4 : IPv4는 32비트로 표현되는 주소체계이며 2^32개의 주소를 표현할 수 있다. 8비트 단위로 점을 찍어 4개로 구분해서 표현하며 보통 8비트를 10진수로 표현해서 말한다. 이 주소체계만으로는 부족하기 때문에 NAT, 서브네팅같은 여러개의 부수적인 기술이 생겼다.
IPv6
IPv6는 128비트로 표현되는 주소체계이며 2^128개의 주소를 표현한다. 즉 많은 주소 처리가 가능하며 NAT, 서브네팅이 필요하지 않다. 16비트씩 8개로 구분하고 16비트는 16진수로 변환되어 콜론으로 구분하여 표시한다. 다음은 IPv6의 특징이다.
상위 프로토콜인 TCP, UDP에 체크섬 필드가 있기 때문에 굳이 필요없다. 만약 IPv6 + UDP 조합 사용 시 UDP에 체크섬 필드를 설정해야 한다.
그렇다면 IPv4와 IPv6 중 어떤게 좋을까? 혹은 더 빠를까?
IPv6는 IPv4보다 많은 주소를 표현할 수 있다. 또한 IPv4보다 불필요한 헤더가 삭제되어서 빠르고 기본적으로 IPSec이라는 네트워크 보안 제품군이 포함되었기 때문에 보안 측면에서도 좋다. 보통은 IPv6를 사용하는 것이 IPv4보다 속도가 더 빠르지만, 일부 사용사례에서는 속도가 느린 경우도 있다.
HTTP는 요청을 보낼 때마다 client와 server 간에 새로운 TCP 연결을 설정하고, 응답받은 후에 연결을 닫는 방식으로 동작한다. 이로 인해 요청마다 연결을 설정하고 해제하는 overhead가 발생하므로, 많은 요청을 처리해야 하는 경우 성능 저하를 초래할 수 있다. HTTP 1.0에서는 Keep-Alive 기능을 사용하려면 header에 옵션을 포함시켜야 했지만, HTTP 1.1부터는 Persistent Connection을 기본으로 지원한다. 하지만 무조건 좋은 기능은 아니다. Keep-Alive를 사용할 때에도 서버 및 네트워크 환경에 따라 적절한 설정이 필요하며, 사용자가 많다면 커넥션이 늘어나서 새로운 사용자를 받아들이지 못하는 문제가 발생할 수 있다. 따라서 사용자가 많고 유동이 많은 서비스에서는 Keep-Alive를 적절히 관리하기 위해 타임아웃 값을 조절하거나 기능을 끄는 등의 조치가 필요할 수 있다.