Web Application Server (WAS)

 

Web Application Server (WAS)는 웹 애플리케이션을 실행하고 관리하는 서버 소프트웨어이다. WAS는 웹 서버와 유사하지만, 주요 기능과 용도가 다르다. 웹 서버는 정적 콘텐츠(HTML 파일, 이미지 등)를 클라이언트에 제공하는 반면, WAS는 동적 콘텐츠를 생성하고 애플리케이션 로직을 처리하는 데 중점을 둔다.

동적 콘텐츠 생성

WAS는 서버 측에서 코드 실행을 통해 동적 콘텐츠를 생성한다. 사용자의 요청에 따라 웹 페이지를 동적으로 생성하거나 데이터 처리를 통해 결과를 생성하여 클라이언트에게 전달한다. 예를 들어, JavaServer Pages (JSP), ASP, PHP와 같은 서버 측 스크립트를 사용하여 동적 웹 페이지를 생성할 수 있다. 이 기능은 사용자 맞춤형 콘텐츠 제공에 유용하다.

애플리케이션 로직 처리

비즈니스 로직을 처리하여 사용자의 요청에 적절한 응답을 생성한다. 이는 데이터베이스와 상호작용하거나 복잡한 계산을 수행하는 것 등을 포함한다. 예를 들어, 사용자 인증, 결제 처리, 데이터 분석 등의 기능이 여기에 해당한다.

 

세션 관리

사용자 세션을 관리하여 로그인 상태, 사용자 맞춤형 데이터, 장바구니 정보 등 다양한 사용자 상태를 추적하고 유지한다. 세션 관리는 사용자가 로그인 후 여러 페이지를 돌아다녀도 상태를 유지할 수 있게 해준다. 

 

데이터베이스 연동

데이터베이스와 연동하여 사용자 요청에 따라 데이터를 조회하거나 업데이트한다. 이를 통해 동적 콘텐츠를 생성하거나 사용자 요청에 맞는 데이터를 제공한다. 예를 들어, SQL 쿼리를 통해 사용자 데이터를 검색하거나 수정할 수 있다.

 

서비스와 프로토콜 관리

HTTP, HTTPS, SOAP, REST 등 다양한 웹 프로토콜을 지원한다. 이는 웹 서비스와의 통신을 처리하며, 웹 애플리케이션과 다른 시스템 간의 데이터 교환을 가능하게 한다. 이는 웹 서비스의 통합과 API 호출을 지원한다.

 

스케일링과 성능 관리

여러 요청을 동시에 처리할 수 있도록 스케일링을 지원하며, 성능을 최적화하기 위한 다양한 기술을 사용한다. 캐싱, 로드 밸런싱, 세션 클러스터링 등의 기술을 통해 높은 성능과 안정성을 유지한다. 이는 대량의 트래픽을 효율적으로 처리하는 데 도움이 된다.

  • Apache Tomcat: Java Servlet과 JSP를 실행하는 오픈 소스 컨테이너로, Java 기반 웹 애플리케이션을 실행한다.
  • JBoss (WildFly): Red Hat에서 제공하는 Java EE 애플리케이션 서버로, 엔터프라이즈급 웹 애플리케이션을 지원한다.
  • IBM WebSphere: IBM에서 제공하는 상용 Java EE 애플리케이션 서버로, 대규모 기업 환경에서 사용된다.
  • Microsoft IIS (Internet Information Services): Microsoft의 웹 서버로, ASP.NET 애플리케이션을 실행한다.

WAS와 웹 서버의 차이

  • 웹 서버: 정적 콘텐츠(HTML, CSS, 이미지 등)를 클라이언트에 제공하는 역할을 한다. 예를 들어, Apache HTTP Server, Nginx가 있다. 웹 서버는 정적 파일을 제공하는 데 특화되어 있다.
  • WAS: 동적 콘텐츠를 생성하고 비즈니스 로직을 처리한다. 웹 서버와 함께 사용되며, 웹 서버가 정적 콘텐츠를 처리하고 WAS가 동적 콘텐츠를 처리하는 구조로 운영된다. 

Java Archive (JAR)

Java Archive (JAR) 파일은 Java 애플리케이션, 라이브러리 또는 기타 Java 관련 파일을 패키징하는 파일 형식이다.

JAR 파일은 여러 개의 Java 클래스와 관련 자원(이미지, 속성 파일 등)을 하나의 압축된 파일로 묶어 배포할 수 있게 해준다.

 

패키징

여러 Java 클래스 파일과 관련 자원(예: 이미지, 속성 파일 등)을 하나의 압축된 파일로 묶어 배포한다.

패키징을 통해 애플리케이션의 모든 구성 요소를 하나의 파일로 관리할 수 있다.

 

배포

JAR 파일을 통해 애플리케이션이나 라이브러리를 배포하고 관리한다.

여러 파일을 하나의 JAR 파일로 묶어 배포하면 이는 배포 과정을 단순화하고 일관성을 유지한다.

 

 

JAR 파일에는 MANIFEST.MF라는 메타데이터 파일이 포함되어 있으며, 이를 통해 JAR 파일을 실행 가능한 애플리케이션으로 만들 수 있다. MANIFEST.MF 파일에 메인 클래스를 지정하면, java -jar yourfile.jar 명령어로 애플리케이션을 실행한다.

  • myapp.jar: Java 애플리케이션을 패키징한 JAR 파일.
  • library.jar: Java 라이브러리를 패키징한 JAR 파일
  • JAR 파일은 Java 애플리케이션의 코드와 자원을 하나의 파일로 패키징하여 배포하고 실행할 수 있다.
  • JAR 파일은 웹 애플리케이션 서버에서 실행되는 애플리케이션의 구성 요소로 사용될 수 있으며, Java Runtime Environment (JRE)에서 실행된다.

내장 Tomcat은 JAR 파일에 포함되어 있다. 이전의 WAR 는 톰캣을 직접 설치해서 설정해줘야했다면 Spring Boot 같은 프레임워크는 Tomcat을 내장 JAR로 포함시켜서, JAR 파일 하나로 애플리케이션과 서버를 함께 패키징한다. JAR 파일 내의 Tomcat이 애플리케이션을 실행하는 형태가 된다.


  • WAS: 서버 소프트웨어로서, 웹 애플리케이션을 실행하고 관리하는 역할을 한다. 서버 환경을 제공하여 동적 콘텐츠를 생성하고, 비즈니스 로직을 처리한다. WAS는 애플리케이션 서버로서 클라이언트의 요청에 따라 애플리케이션을 실행하고 결과를 생성하여 클라이언트에게 전달한다.
  • JAR: 파일 형식으로, Java 애플리케이션이나 라이브러리를 패키징하는 역할을 한다. JAR 파일 자체는 실행 환경이나 서버 기능을 제공하지 않는다. JAR 파일은 단순히 파일을 압축하고 패키징하는 형식일 뿐, 애플리케이션의 실행이나 서버 관리 기능은 포함되어 있지 않다.

용도와 기능

  • WAS: 애플리케이션 서버로서, 웹 애플리케이션의 실행과 관리를 담당한다. 세션 관리, 데이터베이스 연결, 비즈니스 로직 처리 등의 기능을 제공한다. WAS는 애플리케이션의 실행 환경을 제공하며, 서버에서 애플리케이션을 실행하고 관리한다.
  • JAR: Java 클래스와 자원을 패키징하여 배포할 수 있게 해준다. 애플리케이션 코드와 라이브러리를 묶어 관리할 수 있게 한다. JAR 파일은 웹 애플리케이션 서버에서 실행되거나 Java Runtime Environment (JRE)에서 실행된다

작동 방식

  • WAS: 클라이언트 요청에 따라 서버에서 애플리케이션을 실행하고 결과를 생성하여 클라이언트에게 전달한다. WAS는 동적 콘텐츠 생성, 비즈니스 로직 처리, 데이터베이스 연동 등을 담당한다.
  • JAR: Java 애플리케이션이나 라이브러리를 묶어서 배포하며, JAR 파일이 실제로 실행되기 위해서는 WAS와 같은 서버 환경이나 Java Runtime Environment (JRE)가 필요하다. JAR 파일 자체는 실행 환경을 제공하지 않는다.

'CS > NETWORK' 카테고리의 다른 글

Cross Domain  (0) 2024.08.14
CDN (Content Delivery Network)  (0) 2024.08.14
HTTP 헤더  (0) 2024.08.14
로컬 스토리지 / 세션 스토리지 / 쿠키  (0) 2024.08.14
케이블  (0) 2024.08.13

크로스 도메인(Cross Domain)은 서로 다른 도메인 간의 자원 접근 또는 데이터 교환을 의미한다.

도메인은 웹 주소의 일부로, 특정 웹 사이트나 서버를 식별한다.

예를 들어, example.com과 api.example.com은 서로 다른 도메인이다.

 

출처는 도메인 뿐만 아니라 프로토콜(http, https)와 포트 번호를 포함하여 웹 자원에 접근할 수 있는 특정 주소를 정의한다.

http://example.com:443과 http://example.com:80은 서로 다른 출처로 간주된다.

 

동일 출처 정책(Same-Origin Policy)

 

동일 출처 정책은 웹 브라우저에서 스크립트가 로드된 출처와 다른 출처의 자원에 접근하거나 조작하는 것을 제한하는 보안 정책이다. 악성 웹사이트가 사용자의 데이터를 접근하는 것을 방지하기 위한 보안상의 이유로 설계되었다.

 

이 정책에 의해 자바스크립트는 다른 도메인에서 요청을 보내거나 데이터를 읽어올 수 없다. example.com에서 실행 중인 스크립트는 api.example.com의 데이터를 읽거나 조작할 수 없다.

 

CORS (Cross-Origin Resource Sharing)

 

CORS는 브라우저에서 서로 다른 출처 간의 자원 공유를 허용하도록 설계되었다. 서버가 클라이언트의 출처에 대해 요청을 허용할 수 있도록 HTTP 헤더를 설정한다.

 

작동 방식

클라이언트가 'POST', 'GET' 같은 메서드를 사용할 때 또는 비표준 헤더를 사용하는 경우 브라우저는 먼저 OPTIONS 메서드를 사용하여 사전 요청을 보낸다. 서버는 이 요청에 대한 응답으로 CORS 헤더를 포함하여 클라이언트의 요청을 허용할지를 결정한다.

 

사전 요청이 성공하면, 클라이언트는 실제 요청을 보내고 서버는 CORS 헤더를 포함한 응답을 반환한다.

HTTP/1.1 200 OK
//어떤 출처에서의 요청을 허용할지
Access-Control-Allow-Origin: https://example.com
//허용되는 HTTP 메서드(예: GET, POST, PUT)
Access-Control-Allow-Methods: GET, POST
//허용되는 HTTP 헤더
Access-Control-Allow-Headers: Content-Type

 

JSONP (JSON with Padding)

 JSONP는 크로스 도메인 요청을 처리하기 위해 script 태그를 사용하여 JSON 데이터를 전송하는 기술이다. GET 요청만 지원하며, 응답은 자바스크립트 함수 호출 형태로 패딩된다.

 

JSONP는 보안상의 문제로 인해 현재는 CORS가 선호된다. JSONP는 기본적으로 script 태그를 사용하기 때문에 POST, PUT, DELETE 요청을 지원하지 않으며, 보안 취약점이 존재한다.

<script src="https://api.example.com/data?callback=myCallback"></script>
<script>
  function myCallback(data) {
    console.log(data);
  }
</script>

 

프록시 서버를 통한 크로스 도메인 요청 우회

 프록시 서버는 클라이언트와 실제 서버 사이에서 중계 역할을 하여 크로스 도메인 요청을 우회하는 방법이다.

클라이언트는 프록시 서버에 요청을 보내고, 프록시 서버가 실제 서버로 요청을 전달한다.

 

프록시 서버를 사용하면 클라이언트가 직접 다른 도메인에 요청을 보내지 않아도 되므로

동일 출처 정책을 우회할 수 있다.

 

ex) 클라이언트가 proxy.example.com에 요청을 보내고, 프록시 서버가 api.example.com에 실제 요청을 전달한다.

 

보안 문제 및 대응 방법

  • CSRF (Cross-Site Request Forgery): 악의적인 웹사이트가 사용자의 인증 정보를 이용하여 다른 웹사이트에 요청을 보낼 수 있다. 이를 방지하기 위해 CSRF 토큰을 사용하거나 CORS와 함께 적절한 헤더를 설정한다.
  • XSS (Cross-Site Scripting): 악성 스크립트가 클라이언트 측에서 실행될 수 있다. 이를 방지하기 위해 사용자 입력을 적절히 검증하고, 스크립트 삽입을 방지하는 것이 중요하다.

'CS > NETWORK' 카테고리의 다른 글

WEB SERVER / WAS / JAR  (0) 2024.08.14
CDN (Content Delivery Network)  (0) 2024.08.14
HTTP 헤더  (0) 2024.08.14
로컬 스토리지 / 세션 스토리지 / 쿠키  (0) 2024.08.14
케이블  (0) 2024.08.13

CDN은 "Content Delivery Network"의 약자로, 웹 콘텐츠의 전송을 최적화하기 위해 설계된 분산 네트워크이다.

웹 사이트의 콘텐츠를 여러 서버에 분산하여 저장하고, 사용자가 요청할 때 가장 가까운 서버에서 콘텐츠를 제공하는 방식을 사용다. 이를 통해 웹사이트의 로딩 속도를 빠르게 하고 서버의 부담을 줄이며 안정성을 높인다.

 


1. 캐싱

CDN의 가장 핵심적인 기능 중 하나는 캐싱이다.

웹 콘텐츠(예: HTML, CSS, JavaScript 파일, 이미지 등)를 CDN의 여러 서버(엣지 서버)에 저장해두고,

사용자가 콘텐츠를 요청할 때 가장 가까운 엣지 서버에서 제공함으로써 응답 시간을 단축한다.

 

  • TTL 설정: 콘텐츠의 캐시 만료 시간을 설정한다.
  • TTL이 짧으면 자주 업데이트되는 콘텐츠에 적합하고, 길면 자주 변하지 않는 콘텐츠에 적합하다.
  • 캐시 무효화: 콘텐츠가 변경되었을 때 캐시를 업데이트하도록 설정한다.
  • 이를 통해 사용자에게 최신 콘텐츠를 제공한다.
  • 캐시 규칙 설정: 어떤 콘텐츠를 캐시할지, 어떤 조건에서 캐시를 무시할지 설정다.

 

2. 분산 서버 네트워크

CDN은 전 세계에 분산된 여러 서버를 갖추고 있다. 이 서버들은 주요 데이터 센터에 위치하며,

사용자가 요청한 콘텐츠를 빠르게 제공할 수 있도록 설계되어 있다.

 

  • 오리진 서버(Origin Server): 웹사이트의 원본 콘텐츠가 저장된 서버로, 모든 콘텐츠 요청의 최종 출처이다.
  • 데이터베이스와 웹 애플리케이션이 호스팅된다.
  • 엣지 서버(Edge Server): 전 세계 여러 위치에 분산되어 있으며, 사용자가 가까운 서버에서 콘텐츠를 제공받을 수 있다. 주요 기능은 콘텐츠를 캐시하고, 사용자 요청에 빠르게 응답하는 것다.
  • DNS(도메인 네임 시스템) 라우팅: 사용자가 웹사이트를 요청할 때,
  • DNS는 요청을 가장 가까운 엣지 서버로 라우팅한다. 이를 통해 콘텐츠가 빠르게 전달된다.

 

엣지 서버는 오리진 서버에서 콘텐츠를 다운로드하여 저장한다. 사용자가 콘텐츠를 요청하면, 가장 가까운 엣지 서버에서 캐시된 콘텐츠를 제공한다. 콘텐츠가 업데이트되면, 엣지 서버는 새로운 콘텐츠를 가져와서 오래된 캐시를 무효화다.

 

3. 로드 밸런싱

CDN은 로드 밸런싱을 통해 트래픽을 분산한다. 사용자의 요청이 여러 서버로 분산되어 서버 하나에 과도한 부하가 걸리지 않도록 하며, 이는 성능을 최적화하고 서버 다운타임을 줄이는 데 도움을 준다.

 

  • 트래픽 분산: 사용자 요청을 여러 엣지 서버로 분산시켜 각 서버의 부하를 균등하게 분산한다.
  • 서버 상태 모니터링: CDN은 서버의 상태를 모니터링하고,
  • 장애가 발생하면 다른 서버로 트래픽을 자동으로 전환한다.
  • 지리적 라우팅: 사용자의 위치를 기반으로 가장 가까운 서버를 선택하여 트래픽을 라우팅다.

 

4. 성능 향상

 

  • 전송 최적화: 콘텐츠 압축, 이미지 최적화, HTTP/2 및 HTTP/3 프로토콜을 활용하여 데이터 전송 속도를 개선한다.
  • 지리적 최적화: 엣지 서버의 위치를 전략적으로 배치하여 사용자와의 거리를 최소화한다.
  • 최적화된 캐시 전략: TTL 및 캐시 무효화 규칙을 적절히 설정하여 캐시 효율을 극대화한다.

 

5. 보안

CDN은 DDoS(Distributed Denial of Service) 공격으로부터 웹사이트를 보호하거나,

SSL/TLS 암호화를 통해 데이터를 안전하게 전송하는 기능이 포함되어 있다.

 

  • DDoS 방어: 대량의 트래픽 공격을 필터링하고, 서버에 도달하기 전에 차단한다.
  • SSL/TLS 암호화: 데이터 전송 시 암호화를 통해 보안을 강화한다.
  • WAF (Web Application Firewall): 웹 애플리케이션에 대한 공격을 탐지하고 차단다.

 

6. 신뢰성 및 가용성

CDN은 여러 서버와 데이터 센터를 통해 콘텐츠를 제공하기 때문에, 하나의 서버나 데이터 센터에 문제가 생기더라도

다른 서버가 계속해서 콘텐츠를 제공할 수 있다. 이를 통해 웹사이트의 가용성과 신뢰성을 높일 수 있다.

7. 스케일링

트래픽이 급증하더라도 CDN은 다양한 서버와 네트워크를 활용해 콘텐츠를 분산 제공하므로

웹사이트가 원활하게 작동할 수 있도록 도와다.

CDN 사용의 장점

  • 빠른 로딩 시간: 콘텐츠가 사용자와 가까운 서버에서 제공되므로 웹사이트 로딩 시간이 줄어든다.
  • 서버 부하 감소: 트래픽이 CDN 서버에 분산되므로 원본 서버의 부하가 줄어다.
  • 스케일링 용이: 급증하는 트래픽을 효율적으로 처리할 수 있다.
  • 보안 강화: 다양한 보안 기능으로 웹사이트를 보호할 수 있다.
 

CDN(Content Delivery Network)의 통계 분석

CDN은 제공업체의 관리 대시보드나 API를 통해

사용자가 자신의 웹사이트 성능을 모니터링하고 최적화할 수 있도록 돕는 기능을 제공한다.

  • 트래픽 모니터링: 실시간으로 트래픽 양과 요청 수를 시각화한다.
  • 성능 분석: 응답 시간, 캐시 적중률 등 성능 지표를 제공한다.
  • 오류 모니터링: 오류 발생 빈도와 오류 종류를 기록한다.

트래픽 지표

  • 트래픽 양 (Data Transfer): 전송된 데이터의 총량을 측정한다. 이 지표는 사용자에게 제공된 콘텐츠의 양을 파악한다.
  • 요청 수 (Requests): CDN에 전달된 HTTP 요청의 총 수이다. 

1.2. 성능 지표

  • 응답 시간 (Response Time):
    • 사용자 요청에 대한 CDN의 응답 시간을 측정함으로 콘텐츠 제공 속도를 평가한다.
  • 캐시 적중률 (Cache Hit Rate):
    • CDN이 캐시된 콘텐츠를 제공한 비율이다. 높은 캐시 적중률은 성능 향상과 원본 서버의 부하 감소를 나타다.
  • 캐시 미스율 (Cache Miss Rate):
    • 캐시에서 콘텐츠를 찾을 수 없어 오리진 서버에서 직접 가져와야 하는 요청의 비율이다.

1.3. 오류 지표

  • 오류율 (Error Rate)
    • 사용자 요청 중 오류가 발생한 비율이다. HTTP 상태 코드(예: 404, 500 등)를 기준으로 측정된다.
  • SSL/TLS 오류
    • SSL 인증서 관련 오류나 TLS 핸드셰이크 실패율 등 보안 관련 오류를 모니터링한다.

1.4. 사용자 지표

  • 지리적 분포 (Geographic Distribution)
    • 사용자의 위치에 따른 트래픽 분포이다. 
  • 디바이스 및 브라우저 통계
    • 사용자가 사용하는 디바이스와 브라우저에 대한 데이터이다. 콘텐츠 최적화 및 테스트 전략을 조정할 수 있다.

외부 분석 도구

Google Analytics, New Relic, Datadog 등과 같은 외부 분석 도구를 통해

CDN 성능과 사용자 행동을 추가적으로 분석할 수 있다

  • Google Analytics: 웹사이트 방문자, 페이지뷰, 세션 등을 추적한다.
  • New Relic: 애플리케이션 성능 모니터링 및 문제 진단 도구이다.
  • Datadog: 전체 시스템의 모니터링과 성능 분석을 제공한다.

'CS > NETWORK' 카테고리의 다른 글

WEB SERVER / WAS / JAR  (0) 2024.08.14
Cross Domain  (0) 2024.08.14
HTTP 헤더  (0) 2024.08.14
로컬 스토리지 / 세션 스토리지 / 쿠키  (0) 2024.08.14
케이블  (0) 2024.08.13

HTTP

HTTP 요청은 클라이언트가 서버에게 보내는 메세지로
사용자가 HTTP 요청을 하게 되면 헤더와 바디를 주고 받는다.

HTTP 헤더 (Header)

스크린샷 2024-07-18 193009

사용자의 요청에 대한 정보를 포함하고 있다.
헤더는 바디를 설명하는 정보를 포함하여 여러가지 정보가 묶인 정보묶음이다.

헤더는 콜론 ':' 으로 구분되는 key - value형태로 설정된다.

1. Host : 요청하는 호스트이 도메인 이름 또는 IP 주소

2. User-Agent: 클라이언트의 소프트웨어 식별자 (브라우저나 앱의 정보

3. Accept : 클라이언트가 받을 수 있는 응답 방식 (미디어 타입)

4. Content-Type: 요청 바디의 데이터 타입 (POST 요청에서 사용된다.)

5. Authorization: 인증 정보 (토큰 등)

6. Cookie : 이전에 서버로부터 받은 쿠키 정보

7. Cache-Control : 캐시 제어 지시자

8. Connection : 클라이언트와 서버 간의 연결 설정

헤더의 종류

HTTP요청을 할 때 3가지의 헤더인 일반헤더, 요청헤더, 응답헤더가 자동으로 생긴다.

응답헤더

서버에서 설정하는 헤더

요청헤더

클라이언트에서 설정한 헤더

일반헤더

스크린샷 2024-07-18 193956

요청한 URL, 요청메서드, 자원을 요청할 때 해당자원의 출처를 나타내는 URL을 노출시킬지 말지 정한다.
보안정도가 설정되어있는 Refferrer Policy를 포함한다.

요청헤더

스크린샷 2024-07-18 194027

클라이언트가 서버에 요청할 때 클라이언트가 설정하거나 자동으로 설정되는 헤더.
요청 헤더에는 메서드, 클라이언트의 OS, 브라우저 정보 등이 포함된다.

응답헤더

스크린샷 2024-07-18 194137

서버가 클라이언트에게 응답을 보낼 때 설정하거나 자동으로 설정되는 헤더.
응답 헤더에는 서버의 소프트웨어 정보 등이 담긴다.

예를 들어 nginx를 프록시 서버로 두었을 때 해당 정보가 표기된다.
하지만 대부분의 서버는 어떤 소프트웨어가 사용되고 있는지 알기 어렵게 하기 위해
서버 정보를 숨긴다.

HTTP 바디

HTTP 요청의 바디는
POST 메서드와 같이 데이터를 서버로 전송할 때 사용된다.
서버에서 보내고자 
예를 들어, 웹 폼 데이터나 JSON, html, image 형식의 데이터가 포함된다.
바디의 구조는 헤더의 Content-Type 헤더에 따라 달라진다.

웹 폼 데이터 (application/x-www-form-urlencoded) 형식으로 인코딩된 데이터

key1=value1&key2=value2

JSON 데이터 (application/json) 형식으로 전송되는 데이터

{
  "key1": "value1",
  "key2": "value2"
}

HTTP POST 요청

POST /api/login HTTP/1.1
Host: example.com
Content-Type: application/json
Content-Length: 43

{
  "username": "user",
  "password": "password123"
}
헤더: HOST, Content-Type, Content-Length
바디: JSON 형식의 데이터

응답 헤더 만들어보기

public class httpHeader {

    public static void main(String[] args) throws IOException {
        int port = 3000;

        // HTTP 서버 생성
        HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);

        // 서버 시작 메시지 출력
        System.out.println("Server running at http://localhost:" + port + "/");

        // 컨텍스트 경로 설정 및 핸들러 할당
        server.createContext("/", new MyHandler());

        // 기본 실행자 설정 (null 설정 시 기본 실행자 사용)
        server.setExecutor(null);

        // 서버 시작
        server.start();
    }

    // 요청 핸들러 클래스 정의
    static class MyHandler implements HttpHandler {
        @Override
        public void handle(HttpExchange exchange) throws IOException {
            String response = "큰돌, 그는 신인가?!";

            // 응답 헤더 설정
            exchange.getResponseHeaders().set("Content-Type", "text/plain; charset=UTF-8");
            exchange.getResponseHeaders().set("Kundol", "i love you, but you don't love me");

            // 응답 데이터 전송
            exchange.sendResponseHeaders(200, response.getBytes().length);
            OutputStream os = exchange.getResponseBody();
            os.write(response.getBytes());
            os.close();
        }
    }
}
    public static void main(String[] args) throws IOException {
        int port = 3000;

        // HTTPServer 객체를 생성하여 웹 서버 생성
        HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);

        // 서버 시작 메시지 출력
        System.out.println("Server running at http://localhost:" + port + "/");

        // 컨텍스트 경로를 설정할 핸들러 등록
        server.createContext("/", new MyHandler());

        // 요청을 처리할 스레드 생성 null은 기본 실행자
        server.setExecutor(null);

        // 서버 시작
        server.start();
    }
  // 요청 핸들러 클래스 정의
    static class MyHandler implements HttpHandler {
        @Override
        public void handle(HttpExchange exchange) throws IOException {
            //클라이언트에게 보낼 응답 데이터
            String response = "큰돌, 그는 신인가?!";

            //content-type을 text/plain; charset=UTF-8" 로 설정
            exchange.getResponseHeaders().set("Content-Type", "text/plain; charset=UTF-8");
            //사용자 정의 헤더 추가
            exchange.getResponseHeaders().set("Kundol", "i love you, but you don't love me");

            //HTTP 상태코드 (200) 과 응답 본문의 길이 설정
            exchange.sendResponseHeaders(200, response.getBytes().length);
            //HttpExchange에서 outputstream을 사용하여 바디 가져옴
            OutputStream os = exchange.getResponseBody();
            //출력
            os.write(response.getBytes());
            os.close();
        }
    }
}

출력!!!

스크린샷 2024-07-18 204554

스크린샷 2024-07-18 203129

HTTP/1.0과 HTTP/1.1의 차이

HTTP/1.0

스크린샷 2024-07-18 204613

HTTP/1.0은 수명이 짧은 연결이다. HTTP요청은 자체 요청에서 완료가 된다.
각 HTTP 요청당 TCP 핸드셰이크가 발생되며 기본적으로 한 연결당 하나의 요청을 처리한다.
한 번 연결할때마다 TCP 연결을 반복하니 RTT가 늘어나는 문제점이 발생했다.

RTT는?

RTT(Round Trip Time: 왕복 지연시간)은 신호를 전송하고 해당 신호의 수신확인에 걸린 시간을 더한 값이자
어떤 메시지가 두 장치 사이를 왕복하는 데 걸리는 시간이다.

HTTP/1.1

HTTP/1.1은 HTTP/1.0의 단점을 보완한 프로토콜이다.

스크린샷 2024-07-18 204825

1. Keep-alive default

데이터를 요청할 때 마다 TCP 연결을 하는게 아닌 
한 번 연결하면 계속해서 데이터를 받을 수 있게 만들어졌다.
Keep-alive 옵션을 기본옵션으로 설정하면서 가능해졌다.

Keep alive 실습

public class keepAlive {
    public static void main(String[] args) throws IOException {
        int port = 12010;

        // HTTP 서버 생성
        HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);

        // 서버 시작 메시지 출력
        System.out.println("Server running at http://localhost:" + port + "/");

        // 컨텍스트 경로 설정 및 핸들러 할당
        server.createContext("/", new MyHandler());

        // 기본 실행자 설정 (null 설정 시 기본 실행자 사용)
        server.setExecutor(null);

        // 서버 시작
        server.start();
    }

    // 요청 핸들러 클래스 정의
    static class MyHandler implements HttpHandler {
        @Override
        public void handle(HttpExchange exchange) throws IOException {
            exchange.getResponseHeaders().set("Content-Type", "application/json");

            String responseBody = "{\"a\": 1}";
            exchange.sendResponseHeaders(200, responseBody.getBytes().length);

            OutputStream os = exchange.getResponseBody();
            os.write(responseBody.getBytes());
            os.close();
        }
    }
}
        // 포트번호 12010
        int port = 12010;

        // HttpServer 객체를 사용한 내장 웹 서버 생성
        HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);

        // 서버 시작 메시지 출력
        System.out.println("Server running at http://localhost:" + port + "/");

        // MyHandler 클래스를 사용한 경로 설정 ("/") 
        server.createContext("/", new MyHandler());

        // 스레드 풀 생성 (null 지정 = 기본 실행자)
        server.setExecutor(null);

        // 서버 시작
        server.start();
static class MyHandler implements HttpHandler {
        @Override
        public void handle(HttpExchange exchange) throws IOException {
           //JSON 형식의 응답 설정
            exchange.getResponseHeaders().set("Content-Type", "application/json");

            //응답으로 보낼 JSON 문자열
            String responseBody = "{\"a\": 1}";
            //HTTP 상태 코드 200과 응답 바디의 길이 설정
            exchange.sendResponseHeaders(200, responseBody.getBytes().length);
//HttpExchange에서 응답을 가져온다
OutputStream os = exchange.getResponseBody();
            //출력
            os.write(responseBody.getBytes());
            os.close();

실행

스크린샷 2024-07-18 205125

스크린샷 2024-07-18 205144

스크린샷 2024-07-18 205206

Keep-alive header

헤더로 TCP 연결을 유지하는 것을 알려주고, 연결 유지시간인 timeout과 최대 요청수 max를 정할 수 있다.

2. 호스트 헤더

HTTP/1.0은 하나의 IP에 하나의 호스트만 가질 수 있었다.
서버는 여러개의 호스트를 가질 수 있으며, 유연성을 위해 HTTP/1.1은
헤더에 특정 호스트를 포함할 수 있게 되면서 항상 호스트를 포함하여 요청하도록 변경되었다.

3. 대역폭 최적화

HTTP/1.0의 경우 파일을 다운로드 받다가 연결이 끊기면 다시 다운로드 받는 것은 불가능했다.
HTTP/1.1 에선 다시 다운로드 받을 수 있게 바뀌었다. 

HTTP/1.0에서 10KB 파일을 다운로드 받을 때 5KB만 받고 다시 다운로드를 못 받는다면
1.1에서는 Range:bytes=5000- 라는 헤더를 추가하여 다운로드 재개 요청을 할 수 있다.

4. 요청을 줄이기 위한 기술

HTTP/1.1로 발전했음에도 불구하고 서버를 요청할 때 마다 RTT는 계속해서 증가하기 때문에
요청을 줄이기 위한 여러가지 기술들이 있었다.

대표적으로 이미지 스프라이트, 코드압축, Base64 인코딩 기술이 있다.

이미지 스프라이트

img

수많은 이미지를 하나의 이미지로 만들어 하나의 이미지를 다운받은 것을
여러 이미지를 다운받은 듯한 효과를 낸다.

코드 압축

https://www.toptal.com/developers/javascript-minifier

코드를 압축해서 서빙한다.

이미지 Base64 인코딩

https://www.base64-image.de/

이미지 파일을 64진법으로 이루어진 문자열로 인코딩하여
이미지 서버에 대한 HTTP요청할 필요 없이 만든다.

하지만 Base64 인코딩을 할 경우 파일크기가 더 커지는 단점이 있다.
기능/특성 HTTP 1.0 HTTP 1.1
지속 연결 기본적으로 지원하지 않음 지속 연결을 지원함
호스트 헤더 지원하지 않음 필수 (하나의 IP 주소에서 여러 도메인 호스팅 가능)
요청 메서드 GET, POST, HEAD GET, POST, HEAD, PUT, DELETE, OPTIONS, TRACE, CONNECT
캐시 제어 제한적이거나 캐시 제어 헤더가 없음 다양한 캐시 제어 헤더 지원 (예: Cache-Control)
파이프라인 지원 지원하지 않음 하나의 연결로 여러 요청을 파이프라인으로 처리 가능
에러 처리 간단하고 사용자 친화적이지 않은 에러 메시지 명확하고 사용자 친화적인 에러 메시지
길이 필드 Content-Length 헤더 필수 Transfer-Encoding 또는 Content-Length 사용 가능
쿠키 지원하지 않음 표준화된 쿠키 사용과 관리
가상 호스팅 지원하지 않음 Host 헤더를 사용하여 가상 호스팅 지원
압축 지원하지 않음 Content-Encoding을 통한 데이터 압축 지원

HTTP/1.1 의 문제점

스크린샷 2024-07-18 213728

HOL과 무더운 헤더를 가진다.

HOL(Head of Line Blocking)

네트워크에서 같은 큐에 있는 패킷 중 첫번째 패킷이 지연되면
전부 지연되는 성능저하현상이다.

HTTP/2

구글에선 HTTP/1.1 의 HOL 문제를 해결하기 위해 SPDY 프로토콜을 개발하고
이후 SPDY를 기반으로 하는 HTTP/2 프로토콜을 만들었다.

바이너리 포맷 계층

스크린샷 2024-07-18 213954

애플리케이션과 전송 계층 사이에 바이너리 포맷 계층을 추가한다.

HTTP 1.0은 일반 텍스트 메시지를 전송하고 줄바꿈으로 나눴다면
HTTP 2.0은 0과 1로 이루어진 바이너리 데이터로 변경하여 캡슐화하여 전송된다.

h2와 h2c

HTTP/2 는 TLS를 선택적으로 쓸 수 있다.
* TLS는 브라우저와 서버간의 데이터 통신을 암호화하는 프로토콜이다.

h2c는 TLS를 사용하지 않고 TCP 연결에서 직접 HTTP/2를 사용하는 것을 의미하며
개발자가 편리하게 테스트를 할 수 있다는 장점, 암호화와 관련된 오버헤드가 없다는 장점이 있다.

h2는 tls가 장착된 http2를 부르며 브라우저에서는 HTTPS가 없는 HTTP2는 지원하지 않기 때문에
브라우저에는 h2c가 아니라 h2만 허용된다.

멀티플렉싱

단일 TCP 연결의 여러 스트림에서 여러 HTTP 요청과 응답을 비동기적으로 보낼 수 있다.
이를 통해 HOL을 해결한다.

HTTP 1.1에서는 병렬요청을 하려면 다중 TCP 연결을 통해서 하고
일반적으로는 TCP 연결 하나당 병렬 요청은 불가능하다.

HTTP/2.0에서는 리소스를 작은 프레임으로 나누고 전달한다.
각각의 프레임은 스트림 ID, 해당 청크의 크기를 나타내는 프레임이 추가되었기 때문에
작게 나눠서 다운로드가 되더라도 결과적으로 응답데이터에서는 올바른 순서로 재조립된다.

청크

스크린샷 2024-07-18 222531

대량의 데이터를 조각으로 나누어 전송하는 방법.

데이터 조각화: 전송하려는 데이터를 일정한 크기의 청크로 나눈다.
각 청크는 크기와 데이터로 구성된다.

청크 크기: 각 청크의 시작 부분에는 해당 청크의 크기 (16진수로 표시된 바이트 수) 가 포함된다.

종료 신호: 마지막 청크는 크기가 0으로 표시되어 전송이 완료되었음을 나타낸다.
7\r\n
Hello, \r\n
6\r\n
world!\r\n
0\r\n
\r\n
Hello, World!
\r\n 은 청크의 크기, 데이터 사이, 청크들 간의 구분, 마지막 청크와 헤더 사이에서 사용된다.
마지막 청크의 크기는 0이다.

청크의 장점

높은 신뢰성

청크 전송은 큰 데이터를 여러 조각으로 나누기 때문에 
네트워크 연결이 불안정한 경우 전체 데이터를 재전송하지 않고 일부 청크만 재전송한다.

서버 자원 활용

클라이언트가 데이터를 받는 속도에 따라 서버는 여러 클라이언트에게 데이터 동시 전송을 할 수 있다.

서버 푸시

스크린샷 2024-07-18 222623

서버가 리소스를 클라이언트에 푸시 할 수 있다.
요청된 html 파일과 함께 다른 개체를 별도로 보낼 수 있다.
만약 요청한 html에 css가 포함되어있다면 별도 요청없이 css를 같이 보낼 수 있다.

헤더 압축

HTTP/1.1 에도 헤더가 무거웠다면
이를 허프만 인코딩 압축 방법 등으로 압축시켰다.

똑같은 서버에서 2개의 이미지를 준다고 할 때, 중복되는 헤더는 제외한 채 보내고
해당 공통 필드로 헤더를 재구성하며 중복되지 않은 헤더값은 허프만 인코딩 압축 방법으로
압축해 전송한다.

허프만 인코딩 압축

데이터 빈도에 따라 가변 길이 코드를 부여하여 데이터를 압축한다.

1. 빈도 계산

입력 데이터에서 각 문자 또는 기호의 빈도를 계산한다.

2. 허프만 트리 구축

빈도가 가장 높은 문자에는 짧은 코드, 낮은 문자에는 긴 코드를 부여하는 이진 트리를 생성한다.

3. 코드 할당

허프만 트리를 통해 각 문자에 압축된 코드(비트 패턴)을 할당한다.
빈도가 높은 문자에는 짧은 코드를 할당하고 낮은 빈도의 문자에는 긴 코드를 할당한다.

4. 데이터 인코딩

할당된 코드를 사용하여 입력 데이터를 압축한 형식으로 인코딩한다.

5. 데이터 디코딩

허프만 트리로 인코딩된 데이터를 디코딩하여 원래 데이터로 복원한다.

예시

입력 데이터가 ABBCCCDDDDEEEE일 때

각 문자의 빈도를 계산한다.

A: 1회
B: 2회
C: 3회
D: 4회
E: 5회

허프만 트리를 구성한다.

가장 빈도가 낮은 문자부터 시작하여 트리를 구축한다.

코드를 할당한다.

A: 1111
B: 111
C: 11
D: 0
E: 10

인코딩

"ABBCCCDDDDEEEEE"는 
"111111111111111111111111111111110000111111111111111011111111111111100000"으로 압축된다.

우선순위

서버에서 원하는 순서대로 우선순위를 정해 리소스를 전달한다.

HTTP/3

스크린샷 2024-07-18 224050

HTTP/2는 TCP를 사용하기 때문에 초기 연결에 대한 RTT로 인한
지연시간이라는 문제점이 있었다.

QUIC이라는 계층 위에서 돌아가며 TCP 기반이 아닌 UDP 기반으로 돌아간다.
HTTP/2의 장점이었던 멀티플렉싱을 가지고 가며
초기 연결 설정시 지연시간 감소라는 특성을 가지고 있다.

HTTP/3의 장점

HTTP/2의 경우 3 - RTT 가 필요하다면 QUIC는 1 - RTT만 필요하다.

HTTP/2의 경우 클라이언트와 서버간의 연결을 맺어 세션에 만드는데 필요한
핸드셰이크, 암호화통신을 구축하기 위한 TLS 핸드셰이크가 각각 필요했다.

HTTP /3은 TLS로 암호화통신을 구축할 때 단 한번의 핸드셰이크를 활용해
클라이언트와 서버간의 연결, 암호화 통신 모두 구축을 한다.
따라서 1 - RTT만에 모든 연결을 성립할 수 있다.

스크린샷 2024-07-18 224420

HTTP/2 : 각각 핸드셰이크 필요

HTTP/3: 단 한번의 핸드셰이크로 가능

순방향 오류 수정 메커니즘 (FEC, Forward Error Correction)

전송된 패킷이 손실 되었다면 수신 측에서 에러를 검출하고 수정하는 방식이며
열약한 네트워크 환경에서도 낮은 패킷 손실률을 자랑한다.

'CS > NETWORK' 카테고리의 다른 글

Cross Domain  (0) 2024.08.14
CDN (Content Delivery Network)  (0) 2024.08.14
로컬 스토리지 / 세션 스토리지 / 쿠키  (0) 2024.08.14
케이블  (0) 2024.08.13
이더넷  (0) 2024.08.13

로컬 스토리지

로컬 스토리지는 웹 스토리지 객체로
브라우저 내에 { key : value } 형태로 오리진에 종속되어 저장되는 데이터이다.

오리진

스크린샷 2024-07-19 022645

해당 데이터가 저장되는 위치.
로컬 스토리지는 특정 웹사이트의 도메인을 기반으로 구분된다.
도메인을 가진 웹사이트에서 저장한 로컬 스토리지 데이터는 해당 도메인을 오리진으로 가진다.
오리진은 url의 프로토콜, 호스트(도메인), 포트로 구성된다.

로컬 스토리지의 특징

스크린샷 2024-07-19 022038

하나의 키에 하나의 값만 저장된다.
데이터는 사용자가 수동으로 삭제하지 않는 한 로컬 저장소에 저장된다.
최대 저장용량은 5MB 이다.
로그인을 유지하기 위한 값 등으로 사용되며 로컬 스토리지 데이터는 자동으로 서버에 전송되지 않는다.

로컬 스토리지 사용법 (자바스크립트)

로컬 스토리지에 데이터 저장하기

localStorage 객체를 사용한다. 데이터는 key-value 쌍으로 저장되며
모든 값은 문자열로 저장된다. 

객체나 배열을 저장하기 위해서는 JSON 문자열로 변환하여 저장해야한다.
// 데이터 저장
localStorage.setItem('key', 'value');

username에 Alice라는 값을 저장할 수도 있다.

localStorage.setItem('username', 'Alice');

로컬 스토리지에서 데이터 가져오기

getItem 메서드를 사용해 저장된 데이터를 가져온다.
// 데이터 가져오기
const username = localStorage.getItem('username');
console.log(username); // 출력: 'Alice'

로컬 스토리지에서 데이터 제거

removeItem 메서드를 사용해 특정 키의 데이터를 제거한다.
// 데이터 제거
localStorage.removeItem('username');

로컬 스토리지 전체 데이터 제거

clear 메서드를 사용해 로컬 스토리지에 저장된 모든 데이터 제거
// 모든 데이터 제거
localStorage.clear();

스크린샷 2024-07-19 022723

도메인

데이터가 저장되는 위치를 식별하는데 사용되는 주소이다.
웹 사이트의 주소를 나타내며 DNS(Domain Name System) 를 통해 IP 주소로 해석된다.

쿼리 스트링

HTTP 요청에서 데이터를 전달하는 방법이다.
URL의 끝에 ? 문자로 시작하며 여러 개의 파라미터를 & 문자로 구분하여 나타낸다.

key value형태로 데이터를 전달하며
"https://www.example.com/search?q=java&page=1"에서 q=java&page=1은 쿼리 스트링이며
여기서 q=java와 page=1은 각각 파라미터이다.

파라미터

key, value로 구성되어 있다.
주로 GET 요청에서 사용되며 서버로 데이터를 전달할 때 사용된다.
예를 들어 "https://www.example.com/search?fruit=apple" 에서 
fruit=apple은 파라미터이며 fruit 와 apple 은 key, value 이다.

key value

로컬 스토리지에서 데이터는 key와 value의 쌍으로 저장된다.
key는 데이터를 식별하며 value는 해당 키에 연결된 데이터이다.

자바스크립트에서는 키와 값을 명시하여 데이터를 저장할 수 있다.
// 로컬 스토리지에 데이터 저장
localStorage.setItem('username', 'Alice');
localStorage.setItem('email', 'alice@example.com');

// 데이터 가져오기
const username = localStorage.getItem('username'); // 'Alice'
const email = localStorage.getItem('email'); // 'alice@example.com'

// 데이터 제거
localStorage.removeItem('email');

로컬스토리지 캐싱해보기

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>로컬 스토리지 예제</title>
</head>
<body>
    <h2>입력값 저장하기</h2>
    <input type="text" id="inputText" placeholder="텍스트를 입력하세요">
    <button onclick="saveToLocalStorage()">저장</button>

    <script>
        function saveToLocalStorage() {
            // 입력된 텍스트 가져오기
            const inputText = document.getElementById('inputText').value;

            // 로컬 스토리지에 저장
            localStorage.setItem('userText', inputText);

            alert('입력된 텍스트가 로컬 스토리지에 저장되었습니다.');
        }
    </script>
</body>
</html>

성공!

스크린샷 2024-07-19 024208

세션 스토리지

웹 스토리지 객체로 로컬 스토리지와 유사하게
{ key : value } 형태로 오리진에 종속되어 저장된다.
하지만 동일한 오리진이라도 각 탭마다 독립적으로 저장된다.
다른 탭에서 세션 스토리지에 저장된 데이터에 접근할 수 없다.

특징

하나의 키에 하나의 값만 저장된다.
최대 저장용량은 5MB이다.
사용자가 브라우저에서 탭을 닫으면 데이터는 만료된다.

메서드

setItem(key, value)

주어진 키와 값을 세션 스토리지에 저장한다.
// 세션 스토리지에 데이터 저장하기
sessionStorage.setItem('username', 'john_doe');

getItem(key)

주어진 키(key)에 해당하는 값을 세션 스토리지에서 가져온다.
// 세션 스토리지에서 데이터 가져오기
const username = sessionStorage.getItem('username');
console.log('Username:', username); // 출력: Username: john_doe

removeItem(key)

주어진 키(key)에 해당하는 값을 세션 스토리지에서 제거
// 세션 스토리지에서 데이터 제거하기
sessionStorage.removeItem('username');

clear()

세션 스토리지에 저장된 모든 데이터 제거
// 세션 스토리지의 모든 데이터 제거하기
sessionStorage.clear();

쿠키

브라우저에 저장된 데이터 조각이다.
클라이언트에서 먼저 설정할 수도 있고 서버에서도 먼저 설정할 수 있으나
서버에서 먼저 설정하여 쿠키를 만든다.

스크린샷 2024-07-19 025100

HTTP 헤더를 통해 클라이언트 혹은 서버가 HTTP 요청 또는 응답 할 때 추가 정보를 전달할 수 있다.
서버에서 응답 헤더로 Set-Cookie로 설정해서 쿠키를 보내면 
클라이언트에서 요청 헤더 Cookie에 설정되어 자동으로 서버에 전달하고, 브라우저에도 저장된다.

특징

서버, 클라이언트 둘 다 조작이 가능하지만 서버에서 설정한다.
최대 4Kb의 저장용량을 가진다.

세션 쿠키

Expires, Max-Age 속성을 지정하지 않았다.
세션 쿠키는 브라우저가 종료되면 쿠키도 사라진다.

영구 쿠키

Expires, Max-Age 속성을 지정했다.
영구 쿠키는 특정날짜 또는 일정기간이 지나면 삭제되기 만든 쿠키이다.
브라우저를 닫을 때 만료된다.

쿠키를 설정할때 사용되는 HTTP 헤더

Set-Cookie 헤더

쿠키를 클라이언트에게 설정할 때 사용하는 헤더.

예제

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Date;

public class CookieExample {

    public static void main(String[] args) throws IOException {
        int port = 8000;
        HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
        System.out.println("Server running at http://localhost:" + port + "/");

        server.createContext("/", new MyHandler());
        server.setExecutor(null); // creates a default executor
        server.start();
    }

    static class MyHandler implements HttpHandler {
        @Override
        public void handle(HttpExchange exchange) throws IOException {
            // Set a cookie
            String cookieName = "username";
            String cookieValue = "john_doe";
            String cookiePath = "/";
            int cookieMaxAge = 3600; // seconds, 1 hour

            // Create cookie header
            String cookie = cookieName + "=" + cookieValue
                            + "; Path=" + cookiePath
                            + "; Max-Age=" + cookieMaxAge
                            + "; HttpOnly";

            // Add cookie to response headers
            exchange.getResponseHeaders().add("Set-Cookie", cookie);

            // Send response
            String response = "Cookie has been set!";
            exchange.sendResponseHeaders(200, response.getBytes().length);
            exchange.getResponseBody().write(response.getBytes());
            exchange.getResponseBody().close();
        }
    }
}

코드 설명

1. HttpServer을 사용하여 서버를 생성하고 / 경로에 대한 요청을 처리할 MyHandler 클래스를 등록한다.

2. Myhandler 클래스에서 handle 메서드를 오버라이드하여 쿠키를 설정한다.

3. cookieName, cookieValue: 쿠키의 이름과 값
    cookiePath: 쿠키의 유효 경로
    cookieMaxAge: 쿠키의 유효 기간을 초 단위로 지정. 1시간으로 설정

4. 쿠키 정보를 기반으로 Set-Cookie 헤더 생성

5. exchange.getResponseHeaders().add("Set-Cookie", cookie)를 통해 응답 헤더에 쿠키 추가

6. 'sendResponseHeader' 메서드를 사용하여 HTTP 응답 코드와 응답 본문의 길이 설정

7. getResponseBody().write 메서드를 사용하여 응답 데이터 전송

쿠키의 보안

Secure 속성

HTTP 연결에서만 쿠키가 전송되도록 하는 속성.
이는 중간자 공격을 방지하고 쿠키의 안정성을 보장한다.

HttpOnly 속성 사용

HttpOnly 속성을 사용하여 JavaScript를 통해 쿠키에 접근할 수 없도록 한다.
이는 XSS 공격을 방지하는 데 도움이 된다.

Domain 속성 사용

Domain 속성을 사용하여 쿠키가 전송될 도메인을 명시적으로 지정한다.
이를 통해 쿠키의 범위를 제한할 수 있다.

Path 속성 사용

쿠키가 전송될 URL 경로를 명시적으로 지정한다.
쿠키의 사용 범위를 제한할 수 있다.

쿠키 값의 검증

서버 측에서 쿠키 값의 유효성을 검증한다.
사용자가 입력한 데이터를 쿠키 값으로 사용할 때는 
유효성 검사를 통해 악의적인 데이터 삽을 방지한다.

예제

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Date;

public class SecureCookieExample {

    public static void main(String[] args) throws IOException {
        int port = 8000;
        HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
        System.out.println("Server running at http://localhost:" + port + "/");

        server.createContext("/", new MyHandler());
        server.setExecutor(null); // creates a default executor
        server.start();
    }

    static class MyHandler implements HttpHandler {
        @Override
        public void handle(HttpExchange exchange) throws IOException {
            // Set a secure and HttpOnly cookie
            String cookieName = "username";
            String cookieValue = "john_doe";
            String cookiePath = "/";
            int cookieMaxAge = 3600; // seconds, 1 hour

            // Create cookie header with Secure and HttpOnly attributes
            String cookie = cookieName + "=" + cookieValue
                            + "; Path=" + cookiePath
                            + "; Max-Age=" + cookieMaxAge
                            + "; Secure"
                            + "; HttpOnly";

            // Add cookie to response headers
            exchange.getResponseHeaders().add("Set-Cookie", cookie);

            // Send response
            String response = "Secure cookie has been set!";
            exchange.sendResponseHeaders(200, response.getBytes().length);
            exchange.getResponseBody().write(response.getBytes());
            exchange.getResponseBody().close();
        }
    }
}

쿠키 허용관련 알림창

서비스 운영시 쿠키를 사용한다면 쿠키허용 관련 알림창을 만들어야 한다.
방문 기록을 추적할 때 쿠키가 사용되기 때문이다.

이는 사용자의 데이터의 간접수집에 해당되며 거기에 해당하는 KISA 지침을 준수해야 한다.

로컬 스토리지, 세션스토리지, 쿠키 비교

공통점

공통점 설명
저장 위치 클라이언트의 브라우저에 저장된다.
클라이언트 사이드 저장소 클라이언트 사이드에서 데이터를 저장한다.
데이터 전송 HTTP 요청 시 서버로 자동으로 전송된다.

차이점

특성/차이점 로컬 스토리지 (Local Storage) 세션 스토리지 (Session Storage) 쿠키 (Cookie)
저장 용량 5MB 5MB 4KB
데이터 지속성 사용자가 명시적으로 삭제할 때까지 유지 브라우저 세션 동안 유지  
(브라우저 종료 시 삭제) 만료 날짜/시간에 따라 지속 기간이 제한    
사용 예시 사용자 환경 설정, 오프라인 데이터 저장 세션 기간 동안의 로그인 정보 저장 로그인 상태 유지, 사용자 설정 유지 등
서버로 전송 여부 X X O (서버와 주고받을 때 자동으로 전송)
보안 클라이언트 사이드에서 액세스 가능 클라이언트 사이드에서 액세스 가능 클라이언트 및 서버 사이에서 액세스 가능

로그인

HTTP 요청을 통해 데이터를 주고 받을 때 요청이 끝나면 요청한 사용자의 정보 등을 유지하지 않는 특성이 있다.
로그인은 이전에 로그인한 상태값이 남아있어야 하기 때문에 세션 기반 인증방식 또는 토큰기반인증방식으로 구현된다.

스크린샷 2024-07-19 033255

세션: 서버와 클라이언트의 연결이 활성화된 상태

세션ID: 웹 서버 또는 DB에 저장되는 클라이언트에 대한 ID

세션 기반 로그인 프로세스

스크린샷 2024-07-19 033614

1. 처음 로그인 > 세션 ID 생성 > 서버에서 세션ID를 쿠키로 설정해 클라이언트에게 전달

2. 클라이언트가 서버에 요청을 보낼 때 해당 세션ID를 쿠키로 담아 전에 로그인했던 아이디지인지 확인

3. 로그인을 유지

단점

1. 사용자의 상태에 관한 데이터를 서버에 저장했을 때 로그인 중인 유저의 수가 증가한다면
서버의 메모리 과부하가 일어날 수 있다.

2. DB 중 RDBMS에 저장한다면 직렬화 및 역직렬화에 관한 오버헤드가 발생한다.

세션 기반 인증방식 실습!

package com.example.demo.controller;

import com.example.demo.mapper.UserMapper;
import com.example.demo.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

@Controller
public class LoginController {

    // 로그인 폼 제출 시 호출
    @PostMapping("/login")
    public String login(@RequestParam String username,
                        @RequestParam String password,
                        HttpServletRequest request) {
        User user = userMapper.findByUsername(username); // 사용자 이름으로 DB에서 사용자 조회

        // 사용자가 존재하고 비밀번호가 일치하면 
        // 세션에 사용자 이름 저장 후 welcome 페이지로 리다이렉트
        if (user != null && user.getPassword().equals(password)) {
            HttpSession session = request.getSession();
            //세션에 사용자 이름 저장
            session.setAttribute("username", username);
             //welcome 페이지로 리다이렉트
            return "redirect:/welcome";
        } else {
            // 인증 실패 시 로그인 페이지로 리다이렉트
            return "redirect:/";
        }
    }

    // 환영 페이지(welcome.html)를 반환
    @GetMapping("/welcome")
    public String welcome(HttpServletRequest request) {
        HttpSession session = request.getSession();

        //세션에서 사용자 이름 가져오기
        String username = (String) session.getAttribute("username");

        // 사용자가 로그인한 경우 welcome 페이지 반환
        // 로그인하지 않은 경우 로그인 페이지로 리다이렉트
        if (username != null) {
            return "welcome";
        } else {
        // 로그인 페이지로 리다이렉트
            return "redirect:/";
        }
    }

    // 로그아웃 처리 메서드
    @GetMapping("/logout")
    public String logout(HttpServletRequest request) {
        HttpSession session = request.getSession();
        session.invalidate(); // 세션 무효화
        return "redirect:/"; // 로그인 페이지로 리다이렉트
    }
}

토큰 기반 로그인 인증방식

상태를 서버에 저장하지 않고 클라이언트 측에 토큰을 저장하여 인증을 수행한다.
이 방식은 RESTful API와 SPA(Single Page Application) 등에서 사용된다.

장점

staless: 서버는 클라이언트의 상태를 유지하지 않아도 된다. 각 요청은 독립적으로 처리된다.

확장성: 토큰은 클라이언트 측에 저장되므로 서버는 추가 저장 공간이나 자원을 할당하지 않는다.

보안 강화: 토큰은 서명되어 있어 변조가 어렵고 HTTPS를 통해 안전하게 전송된다.

주의 사항

토큰 보안: 토큰을 안전하게 보호하기 위해 클라이언트 측에서는 보안을 해야한다.
XSS 공격에 노출될 경우 토큰이 탈취될 수 있다.

토큰 만료: 토큰의 만료 기간을 설정하여 주기적으로 갱신해야 한다.
마료된 토큰을 사용할 경우 보안이 취약해진다.

JWT 토큰

JWT는 정보를 JSON 객체로 안전하게 표현하고 서명하여 검증할 수 있는 방식을 제공한다.

스크린샷 2024-07-19 035901

1. 인증 로직 > JWT 토큰 생성 (access 토큰 , refresh 토큰)
2. 사용자가 이후 access 토큰을 HTTP Header - Authorization 또는 HTTP Header-Cookie에 담아
인증이 필요한 서버에 요청해 원하는 컨텐츠를 가져온다.

JWT 구성 요소

스크린샷 2024-07-19 040028

헤더: JWT의 유형과 사용하는 알고리즘을 정의한다.

{ "alg": "HS256", "typ": "JWT" }

페이로드: 실제 전송할 정보를 포함한다. 클레임이라고도 불리는 키 값 쌍으로 구성된다.

{ "sub": "user123", "name": "John Doe", "exp": 1626587773 }

sub: 주제(subject) - 토큰의 주인 (유저) 지정
name: 사용자 이름 등 추가 정보를 포함
exp: 만료 시간(expiration) - 토큰의 유효 기간 지정

서명: 헤더와 페이로드를 합친 후 보안 키를 통해 서명한다. 토큰이 변조되지 않았음을 검증한다.

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

장점

자가 포함: JWT는 필요한 모든 정보를 자체적으로 포함하고 있기 때문에 서버에서 상태를 유지할 필요가 없다.
이는 staless 서버 설계에 유리하다.

경량화: 다른 유형의 토큰과 비교했을 때, JSON 형식이 때문에 가볍다.

확장성: JWT는 JSON을 사용한 형식이기 때문에 JSON을 기반으로 쉽게 직렬화, 역지렬화가 가능하다.

보안: 서명을 통해 토큰의 무결성을 검증한다. 

단점

크기: 페이로드에 저장되는 정보가 많을 수록 JWT의 크기가 증가한다.

보안: 토큰이 탈취될 경우 토큰에 포함된 정보가 노출될 수 있다.

동작 원리

스크린샷 2024-07-19 040850스크린샷 2024-07-19 040915

1. 사용자 로그인 처리

- 사용자가 아이디와 비밀번호로 로그인을 시도한다.
- 로그인이 성공하면, 서버는 사용자를 식별할 수 있는 정보를 기반으로 Access Token과 Refresh Token을 발급한다.


2. Access Token 발급

- Access Token은 사용자가 리소스에 접근할 때 사용된다.
- 일반적으로 JWT(JSON Web Token) 형식으로 발급된다.
- JWT에는 사용자 정보와 만료 시간 등이 포함된다.
- 서명을 통해 토큰의 무결성을 보장한다.

3. Refresh Token 발급

- Refresh Token은 Access Token의 만료 시간이 지나면 새로운 Access Token을 발급받을 수 있는 권한을 부여한다.
- 데이터베이스에 저장되고, 유효기간이 길고 암호화되어 관리된다.

4. 토큰 저장

- 클라이언트는 발급받은 Access Token을 저장하고, 필요할 때마다 이를 사용하여 서버에 요청한다.
- Refresh Token은 안전한 장소에 저장되어, Access Token이 만료될 때 새로운 Access Token을 요청하는 데 사용된다.

5. 토큰 검증

- 서버는 클라이언트가 제공한 Access Token의 유효성을 검증한다.
- JWT의 서명을 통해 토큰의 변조 여부를 확인하고, 만료 시간을 검사하여 유효성을 판단한다.

6. Access Token 갱신

- Access Token의 유효기간이 다가오면, Refresh Token을 사용하여 새로운 Access Token을 발급받는다.
- 이 과정에서 사용자는 로그인을 다시 하지 않아도 된다.
@Controller
public class AuthController {

    @Autowired
    private UserService userService;

    @PostMapping("/login")
    @ResponseBody

    //ResponseEntity<> 클래스로 HTTP 응답의 상태 코드 명시
    public ResponseEntity<?> login(@RequestBody Map<String, String> credentials) {
        String username = credentials.get("username");
        String password = credentials.get("password");

        //사용자명과 비밀번호 검증
        if (userService.isValidUser(username, password)) {
            // JWT 토큰 생성
            String token = generateToken(username);
            return ResponseEntity.ok(token);
        } else {
            //인증 실패시 UNAUTHORIZED(401) 상태 코드 반환
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
        }
    }

    @PostMapping("/refresh")
    @ResponseBody
    public ResponseEntity<?> refresh(@RequestHeader("Authorization") String refreshToken) {
        // 리프레시 토큰을 검증하고 새로운 액세스 토큰 생성
        String username = validateRefreshToken(refreshToken);
        if (username != null) {
            //사용자 정보 호출
            String email = userService.getEmail(username);

            //새로운 액세스 토큰 성성
            String newToken = generateToken(username);
            return ResponseEntity.ok(newToken);
        } else {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
        }
    }

    private String generateToken(String username) {
        return Jwts.builder() //JWT를 생성하기 위한 빌더 객체 생성
                .setSubject(username) //JWT의 클레임(사용자명 생성
                .setIssuedAt(new Date()) //토큰의 발생 일시 설정. 현재 시간으로 설정
                //토큰의 만료 일시 설정
                .setExpiration(new Date(System.currentTimeMillis() + JwtConfig.ACCESS_TOKEN))
                //토큰을 서명. HS256 알고리즘과 SECRET KEY를 사용하여 서명
                .signWith(SignatureAlgorithm.HS256, JwtConfig.SECRET_KEY)
                //토큰을 문자열로 화직렬화
                .compact();
    }

    private String validateRefreshToken(String refreshToken) {
        try {
            Claims claims = Jwts.parser() //JWT 파서 생성
                    .setSigningKey(JwtConfig.SECRET_KEY) //서명 키 설정
                    .parseClaimsJws(refreshToken) //주어진 토큰 파싱, 서명 검증
                    .getBody(); // 토큰의 클레임 추출

            // 유효한 토큰인 경우 클레임에서 key를 추출하여 반환
            return claims.getSubject();
        } catch (Exception e) {
            //예외 처리
            return null;
        }
    }
}

postman.com 사이트로 발급받은 토큰을 검증한다.

https://web.postman.co/

refresh 토큰 재발급

jwt 부분에 새로 받은 토큰을 집어넣어서 실행.
https://curl.se/windows/ 설치 후 curl 명령어가 동작하게 한다.

주의할 점

- 쿠키에 담은 토큰에 Bearer를 앞에 둬서 토큰 기반 인증방식이라는 것을 알려준다.

- https 방식을 사용한다.

- 쿠키에 저장한다면 sameSite: Strict를 사용한다.

- 수명이 짧은 access token을 발급한다.

- url에 토큰을 전달하지 말아야 한다. 

토큰 탈취 방지 방법

1. Access Token의 수명을 짧게 설정하여 탈취된 토큰의 유효 기간을 최소화한다.

2. Refresh Token을 사용할 때 추가적인 사용자 인증 단계를 요구한다.

사용자가 민감한 작업을 수행할 때 추가 인증을 요구할 수 있으며
Refresh Token을 사용할 때 토큰을 발급받은 IP 주소 및 디바이스 정보를 확인하여
동일한 조건에서만 토큰을 사용할 수 있도록 제한한다.

'CS > NETWORK' 카테고리의 다른 글

CDN (Content Delivery Network)  (0) 2024.08.14
HTTP 헤더  (0) 2024.08.14
케이블  (0) 2024.08.13
이더넷  (0) 2024.08.13
HTTP 상태코드  (0) 2024.08.13

케이블

케이블은 전기적 신호나 데이터를 전달하기 위해 사용되는 물리적인 연결 매체이다.
주로 전기적 신호를 전송하는 전기 케이블과 데이터 통신을 위한 네트워크 케이블로 나뉜다.

네트워크 케이블

데이터 통신을 위해 사용된다.
주로 유선 네트워크에서 컴퓨터, 서버, 네트워크 장비들을 연결하거나 데이터를 전송하는 데 사용된다.
네트워크 케이블의 종류로는 이더넷 케이블(트위스트페어 케이블), 광섬유 케이블 등이 있다.

신뢰성

케이블을 사용하면 무선 네트워크보다 신호 간섭이나 감쇠가 적어져서 더 안정적인 연결을 제공한다. 
이는 데이터 손실을 줄이고 더 빠르고 안정적인 네트워크 연결을 가능하게 한다.

보안

무선 네트워크는 해킹이나 불법 접근 위험이 있을 수 있지만
케이블을 사용하면 물리적 접근이 필요해서 보안성이 더 높아질 수 있다.

속도와 대역폭

케이블은 더 넓은 대역폭과 빠른 전송 속도를 제공할 수 있다. 
특히 대용량 데이터나 영상 전송이 필요한 경우에 효율적이다.

장거리 전송

케이블을 이용하면 무선 네트워크보다 더 긴 거리에서 데이터를 전송할 수 있다. 
특히 건물 간 연결이나 장비 간의 연결에 유리하다.

비용

초기 설치 비용은 무선 네트워크보다 높을 수 있지만
장기적으로 케이블은 유지 관리 및 운영 비용에서 더 저렴하다.

트위스트페어 케이블

스크린샷 2024-07-20 100903

트위스트페어케이블(Twisted Pair Cable)은 주로 데이터 전송을 위해 사용되는 케이블로
전기 신호를 전달하는 데 특화된 구조를 가지고 있다. 
여기서 주로 사용되는 두 가지 유형은 UTP (Unshielded Twisted Pair) 케이블과 STP (Shielded Twisted Pair) 케이블이다.

장점

노이즈 감소: 각각의 쌍이 트위스트되어 있어서 외부에서 발생하는 전자기 간섭(EMI)에 대해 상당히 잘 견딜 수 있다.
경제성: 비교적 저렴하게 제작할 수 있어서 네트워크 구축 비용을 절감할 수 있다.
유연성: 다양한 네트워크 환경에 맞추어 사용할 수 있는 다양한 종류의 케이블이 제공된다.

UTP 케이블

UTP 케이블은 실드(Shield) 처리가 되어 있지 않다. 
대신, 각각의 쌍을 이루는 두 개의 동심 트위스트된 구리 선으로 이루어져 있다. 
각 쌍은 서로 다른 주파수에서 발생할 수 있는 전자기 간섭(EMI)를 줄이기 위해 서로 감겨 있다.
주로 이더넷 네트워크에서 사용되며, 일반적인 가정이나 사무실에서 네트워크 연결을 할 때 많이 사용된다. 
LAN 케이블로 사용되며, RJ45 커넥터를 사용하여 컴퓨터, 스위치, 라우터 등에 연결된다.

STP 케이블

STP 케이블은 각 쌍이 실드로 감싸져 있어서 UTP보다 전자기 간섭을 더욱 효과적으로 차단할 수 있다. 
이러한 실드 처리는 노이즈에 민감한 환경에서 사용될 때 유리할 수 있다.
주로 노이즈가 많은 환경이나 전자기 간섭이 심한 곳에서 사용된다.

LAN 케이블

스크린샷 2024-07-20 100908

LAN 케이블은 Local Area Network(지역 네트워크)을 구축할 때 사용하는 케이블로 
주로 유선 네트워크를 구성하는 데에 쓰인다. 이 케이블은 주로 UTP (Unshielded Twisted Pair) 케이블 타입이다.

UTP 케이블

UTP 케이블은 여러 개의 동심 트위스트된 구리 선이 있고 각 쌍의 선이 서로 감겨져 있다. 
이는 전자기 간섭(EMI)을 최소화하고 신호의 왜곡을 줄이는 데 도움을 준다.
UTP 케이블은 비교적 저렴하고 유연하며, 일반적으로 가정이나 사무실에서 네트워크 장비를 연결하는 데 널리 사용된다.

RJ45 커넥터

스크린샷 2024-07-20 100914

LAN 케이블을 장치에 연결하기 위해 사용되는 커넥터로, 8개의 핀으로 구성돼 있다. 
이 커넥터는 UTP 케이블의 구리 선을 효과적으로 연결하고, 이더넷 네트워크에서 일반적으로 사용된다.
RJ45 커넥터는 표준화된 규격을 따르며, 쉽게 연결 및 분리할 수 있는 장점이 있어 네트워크 장비 간의 연결이 쉽다.

광섬유케이블

스크린샷 2024-07-20 100917

광섬유케이블은 빛을 이용하여 데이터를 전송하는 광통신에 주로 사용되는 케이블이다. 

특징

고속 데이터 전송: 광섬유케이블은 매우 높은 대역폭을 지원하여 100Gbps 이상의 데이터 전송 속도를 가능하게 한다.
저손실: 전기적인 손실이 거의 없어 데이터가 먼 거리에도 신뢰성 있게 전송된다.
보안: 외부에서의 간섭이 적어 데이터의 안전성과 보안성이 높다.
경량 및 공간 효율성: 전통적인 전선보다 훨씬 가벼우며, 공간 효율적인 설치가 가능하다.

이러한 특성들로 인해 광섬유케이블은 긴 거리의 네트워크 연결이나 대용량 데이터 전송에 매우 유용하게 사용된다.

구조

스크린샷 2024-07-20 100921

광섬유케이블은 주로 광섬유라고 불리는 매우 얇고 유연한 섬유로 구성된다. 
이 섬유는 일반적으로 유리나 특수한 종류의 플라스틱으로 제작된다.

코어(Core)

빛이 직접 전달되는 중심 부분이다. 빛의 굴절률이 높아야 하며, 주로 실리콘 또는 유리로 제작된다.

클래딩(Cladding)

코어를 둘러싸고 있는 외부 부분으로, 굴절률이 낮아 빛이 코어 내에서 반사되도록 한다.
이는 빛의 손실을 줄이고 데이터의 전송 효율을 높이는 데 중요한 역할을 수행한다.

동작 원리

레이저나 LED 등의 광원에서 발생한 빛은 광섬유케이블의 한쪽 끝에서 입력된다.
입력된 빛은 코어 내에서 반사하면서 전진하게 되는데, 이 과정에서 반사는 클래딩과 코어의 굴절률 차이로 인해 발생한다.
반사된 빛은 내부에서 계속 반사하면서 전송선으로 이동하고, 반대편 끝까지 도달한다.
이는 반사와 굴절을 이용하여 데이터를 신속하게 전송하는 원리이다.

'CS > NETWORK' 카테고리의 다른 글

HTTP 헤더  (0) 2024.08.14
로컬 스토리지 / 세션 스토리지 / 쿠키  (0) 2024.08.14
이더넷  (0) 2024.08.13
HTTP 상태코드  (0) 2024.08.13
레이어별 네트워크 장치  (0) 2024.08.13

+ Recent posts