스프링 PSA(Portable Service Abstraction)

환경의 변화에 상관없이 일관된 방식의 기술 접근 환경을 제공하려는 추상화 구조

Spring은 Spring Web MVC, Spring Transaction, Spring Cache등의 다양한 기술에 Portable Service Abstraction을 제공하고 있다


Web MVC와 관련된 Service Abstraction

Spring MVC는 서블릿 어플리케이션임에도 불구하고 서블릿이 전혀 겉으로 들어나지 않았다.

단지 @Controller 어노테이션이 붙어잇는 클래스에서 @GetMapping, @PostMapping과 같은 @RequestMapping 애노테이션을 사용해서 요청을 매핑한다

실제롤는 내부적으로 서블릿 기반으로 코드가 동작하고 있지만 서블릿 기술은 추상화 계층에 숨겨져 있는 것이다.

이렇게 추상화 계층을 사용하여 어떤 기술을 내부에 숨기고 개발자에게 편의성을 제공해주는 것을 Service Abstraction이라 한다.

더하여 Service Abstraction으로 제공되는 기술을 다른 기술 스택으로 간편하게 바꿀 수 있는 확장성을 갖고 있는 것이 Portable Service Abstraction이다.


Spring Web MVC

Service Abstraction

일반 클래스에 @Controller 애노테이션을 사용하면 요청을 매핑할 수 있는 컨트롤러 역할을 수행하는 클래스가 된다.

그 클래스에서는 @GetMapping과 @PostMapping 애노테이션을 사용해서 요청을 매핑할 수 있다

요청을 처리하는 메소드들은 뷰를 리턴하며 각 메소드에서 model에 담은 객체가 모델에 해당한다

Spring Web MVC를 사용하면 이렇게 서블릿을 간편하게 개발할 수 있는데, 뒷단에 spring이 제공해주는 여러 기능들이 숨겨져 있기 때문이다.

즉 서블릿의 HttpServlet을 상속받고 doGet(), doPost() 작성하는 등의 작업을 직접하지 않아도 된다

Service Abstarction의 목적 중 하나가 이러한 편의성을 제공하는 것이다.

Portable

Spring Web MVC가 Tomcat기반을 동작하고 있을 때 코드를 거의 그대로 둔 상태에서 톰캣이 아닌 완전히 다른 기술 스택으로 실행하는 것이 가능하다.

spring-boot-starter-web 의존성 대신 프로젝트를 spring-boot-starter-webflux로 변경하기만 하면 톰캣이 아닌 netty기반을 실행되게 할 수 도 있다.

원래 기존에 톰캣으로 실행하던 프로젝트를 netty기반으로 실행하게 하려면 더 복잡한 과정이 필요하지만 spring이 제공해주는 Spring Web MVC 추상화 계층을 사용해서 간단히 netty로 실행할 수 있는 것이다.

이렇게 Spring Web MVC는 @Controller, @RequestMapping과 같은 애노테이션과 뒷단의 여러가지 복잡한 인터페이스들 그리고 기술들을 기반으로 하여 사용자가 웹 기술 스택을 간편하게 바꿀 수 있도록 해준다

중요한 것은 이런 것들이 기존 코드를 거의 변경하지 않고도 가능하다는 것이다.


Spring Transaction

트랜잭션을 처리하려면 setAutoCommit()과 commit(), rollback()을 명시적을 호출해야 한다

그러나 Spring이 제공하는 @Transactional 애노테이션을 사용하면 단순히 메소드에 애노테이션을 붙여줌으로써 트랜잭션 처리가 이루어진다.

이 또한 PSA로써 다양한 기술 스택으로 구현체를 바꿀 수 있다.

예를 들어 JDBC를 사용하면 DatasourceTransactionManager, JPA를 사용하는 JpaTransactionManager, Hibernate를 사용하는 HibernateTransactionManager를 유연하게 바꿔서 사용할 수 있다.

즉 기존 코드는 변경하지 않은 채로 트랜잭션을 실제로 처리하는 구현체를 사용 기술에 따라 바꿔 끼울 수 있다


Spring Cache

Cache도 마찬기지로 JCacheManagerm ConcurrentMapCacheManager, EhCacheManager와 같은 여러가지 구현체를 사용할 수 있다.

사용자는 @Cacheable 애노테이션을 붙여줌으로써 구현체를 크게 신경쓰지 않아도 필요에ㄷ 따라 바꿔 쓸 수 있는 것이다.

이렇게 spring이 제공해주는 다양한 PSA 기술 덕분에 코드는 더 견고해지고 기술이 바뀌어도 유연하게 대처할 수 있게 된다.

Comment and share


스프링 AOP(Aspect Oriented Programming)

AOP

AOP는 Asepect Oriented Programming의 약자로 관점 지향 프로그래밍이라고 불린다.

관점 지향은 어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으로 나누어서 보고

부가적인 관점을 각각 따로 분리해서 모듈화하겠다는 것이다.

예로들어 핵심적인 관점은 우리가 적용하고자 하는 핵심 비지니스 로직이 된다.

또한 부가적인 관점은 핵심 로직외에 행해지는 데이터베이스 연결, 로깅, 파일 입출력등이 있다.

AOP에서 각 관점을 기준으로 로직을 모듈화한다는 것은 코드를 부분적으로 나누어서 모듈화하겠다는 의미다.

이때, 소스 코드상에서 핵심 비지니스 로직(Core Concerns)외에 계속 반복해서 쓰는 코드들을 발견할 수 있는데 이것을 흩어진 관심사(Crosscutting Concerns)라 부른다.

스크린샷 2020-11-27 오후 12 59 42

위와 같이 흩어진 관심사(Crosscutting Concerns)를 Aspect로 모듈화하고 핵심적인 비지니스 로직(Core concerns)에서 분리하여 재사용하겠다는 것이 AOP이다.

AOP 관련 용어

  • 조인 포인트(JoinPoint) : 클라이언트가 호출하는 모든 비지니스 메소드, 조인포인트 중에서 포인트컷이 되기때문에 포인트 컷의 후보로 생각할 수 있다.
  • 포인트 컷(Pointcut) : 특정 조건에 의해 필터링된 조인 포인트, 수 많은 조인 포인트 중에 특정 메소드에서만 Advice를 수행시키기 위해서 사용된다
  • 어드바이스(Advice) : Crosscutting Concern에 해당하는 공통 기능의 코드
  • 애스펙트(Aspect) : 포인트 컷과 어드바이스의 결합이다. 어떤 포인트 컷 메소드에 대해 어떤 어드바이스 메소드가 실행할 지 결정한다.
  • 위빙(Weaving) : 핵심 비지니스 로직과 Aspect를 연결하여 Advised Object를 만드는 과정
    1. complie time
    2. load time
    3. runtime

AOP 구현 방법

  1. 컴파일

    • A.java —— (AOP) —–> A.class
    • 컴파일을 할 때 중간에 공통 로직을 끼워 넣는다
    • 컴파일하기 전 코드에는 해당 로직이 없지만, 컴파일이 완료되면 해당 로직을 가지고
      있는 상태이다
    • AspectJ를 이용해서 만들어진다
  2. 바이트코드 조작

    • A.java —-> A.class —-> (AOP) —-> 메모리
    • load time에서 ClassLoader가 A.class를 읽어온다
    • 읽어와서 메모리에 올릴 때 공통 로직이 들어가도록 조작한다
    • AspectJ를 이용해서 만들어진다
  3. 프록시 패턴

    • Runtime에서 구현하는 방식
    • 프록시 패턴을 적용하여 실제 객체가 실행되는 것이 아닌 실제 객체를 감싸고 있는 프록시 객체를 Runtime에 생성
    • 프록시 객체는 Aspect가 적용되어있고 Core Concern이 들어있는 실제 객체를 reference하고 있다
    • AOP가 적용된 객체를 요청할 때 실제 객체가 아닌 프록시 객체가 실행된다.
    • AspectJ로 만들어져 있으며 SPRING AOP는 이 방식으로 구현되어있다.

Spring AOP

스프링 AOP 특징

  • 프록시 기반의 AOP 구현체
  • 스프링 빈에만 AOP를 적용할 수 있다
  • 모든 AOP 기능을 제공하는 것이 목적이 아니라, Spring IoC와 연동하여 어플리케이션의 문제에 대한 해결책을 제공하는 것이 목적

프록시 패턴

  • 접근 제어 또는 부가기능 추가

    image

    • 인터페이스에 대해서 핵심 비지니스 로직만으로 구성되어진 실제 객체를 만든다
    • 인터페이스에 대해서 또 하나의 Proxy 구현체를 만든다
    • 이 구현체에는 부가기능이 추가있으며 실제 객체를 참조하여 실제 객체의 비지니스 로직을 실행 시킬 수 있다
    • 클라이언트는 객체를 인터페이스를 통해서 접근하게 되며, 실제객체가 직접 실행되는 것이 아닌 Proxy객체를 통해서 부가기능을 수행하고 간접적으로 실행된다.

프록시 패턴을 적용 (Spring AOP를 사용하지 않음)

  • EventService Interface

    1
    2
    3
    4
    5
    6
    7
    public interface EventService {
    void createEvent();

    void publishEvent();

    void deleteEvent();
    }
  • EventService 구현체 (핵심 비지니스 로직 수행)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    @Service
    public class SimpleEventService implements EventService{

    @Override
    public void createEvent() {
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("Created an event");
    }

    @Override
    public void publishEvent() {
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("Publish an event");
    }

    public void deleteEvent() {
    System.out.println("Delete an event");
    }
    }
  • EventService의 Proxy 구현체 (부가기능 + 핵심 로직 객체 참조)

    @Primary로 SimpleEventService가 아닌 ProxySimpleEventService이 실행됨

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    @Primary
    @Service
    public class ProxySimpleEventService implements EventService{

    @Autowired
    SimpleEventService simpleEventService;

    @Override
    public void createEvent() {
    long begin = System.currentTimeMillis();
    simpleEventService.createEvent();
    System.out.println(System.currentTimeMillis() - begin);
    }

    @Override
    public void publishEvent() {
    long begin = System.currentTimeMillis();
    simpleEventService.publishEvent();
    System.out.println(System.currentTimeMillis() - begin);
    }

    @Override
    public void deleteEvent() {
    simpleEventService.deleteEvent();
    }
    }

문제점

  • 매번 프록시 패턴을 적용해서 프록시 클래스를 작성해야한다
  • 여러 클래스 여러 메소드들에 적용할려면 반복적인 작업이 증가한다
  • 객체들관의 관계도 복잡하다

스프링 AOP 등장

  • 스프링 IoC컨테이너가 제공하는 기반 시설과 Dynamic 프록시를 사용하여 여러 복잡한 문제 해결
  • 동적 프록시 : 동적으로 프록시 객체를 생성하는 방법
    • 자바가 제공하는 방법은 인터페이스 기반 프록시 생성
    • CGlib은 클래스 기반 프록시도 지원
  • 스프링 IoC: 기존 빈을 대체하는 동적 프록시 빈을 만들어 등록해준다
    • 클라이언트 코드에 변경이 없다

스프링 AOP 적용해서 개선

스프링 AOP는 Spring Container에 등록된 Bean들만 적용된다

  • Pointcut을 Annotation 기반으로 작성

    1
    2
    3
    4
    5
    @Documented
    @Retention(RetentionPolicy.CLASS)
    @Target(ElementType.METHOD)
    public @interface PerfLogging {
    }
  • Aspect 작성 (Around 방식을 적용한 Advice 메소드 작성)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    @Component
    @Aspect
    public class PerfAspect {

    @Around("@annotation(PerfLogging)")
    public Object logPerf(ProceedingJoinPoint pjp) throws Throwable {
    long begin = System.currentTimeMillis();
    Object retVal = pjp.proceed();
    System.out.println(System.currentTimeMillis() - begin);
    return retVal;
    }

    @Before("bean(simpleEventService)")
    public void hello(){
    System.out.println("hello");
    }
    }
    • Around방식은 핵심 비지니스로직 실행 권한을 ProceedingJoinPoint로 받아온다 (Spring Container가 받아옴)
    • 받아온 객체를 실행하기 전 후에 작업을 진행한다
    • 받아온 객체가 return값을 가지고 있으면 Advice메소드에서 그 값을 return해 주어야한다
  • AOP를 적용할 Pointcut메소드를 지정한다(메소드 위에 어노테이션 설정)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    @Service
    public class SimpleEventService implements EventService{
    @PerfLogging
    @Override
    public void createEvent() {
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("Created an event");
    }

    @PerfLogging
    @Override
    public void publishEvent() {
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("Publish an event");
    }

    @Override
    public void deleteEvent() {
    System.out.println("Delete an event");
    }
    }

Pointcut 정의 방식

  1. execution 설정 방식

    • 사용 예

      1
      @Around("execution(* com.example..*.EventService.*(..))")
    • 적용 규칙

      스크린샷 2020-11-27 오후 2 51 06
  2. bean 설정 방식

    • 사용 예

      적용할 빈 이름을 직접 입력

      1
      @Around("bean(simpleEventService)")
  3. annotation 설정 방식 (위에서 설명)

Advice 실행 시점

  1. Before

    • 핵심 비지니스 로직 실행 전에 실행
    • 실행 예
      1
      2
      3
      4
      5
      6
      7
      @Before("execution(* com.rubypaper.biz..*Impl.*(..))")
      public void printLog(JoinPoint jp) {
      String method = jp.getSignature().getName(); // 클라이언트가 호출한 메소드 이름
      Object[] args = jp.getArgs(); // 클라이언트가 전달한 인자 정보

      System.out.println("<사전 처리> " + method + "비지니스 로직 수행 전 동작" + "() 메소드 ARGS 정보 : " + args[0].toString());
      }
  2. After

    • 핵심 비지니스 로직 실행 후에 실행
    • 실행 예
      1
      2
      3
      4
      5
      6
      7
      @After("execution(* com.rubypaper.biz..*Impl.*(..))")
      public void printLog(JoinPoint jp) {
      String method = jp.getSignature().getName(); // 클라이언트가 호출한 메소드 이름
      Object[] args = jp.getArgs(); // 클라이언트가 전달한 인자 정보

      System.out.println("<사후 처리> " + method + "비지니스 로직 수행 후 동작" + "() 메소드 ARGS 정보 : " + args[0].toString());
      }
  3. After Returning

    • 핵심 비지니스 로직 실행한 후 반환된 객체를 Advice에서 받아와서 실행
    • 실행 예
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      @AfterReturning(pointcut = "!void com.rubypaper.biz..*Impl.*(..))", returning = "returnObj")
      public void afterLog(Object returnObj) {
      System.out.println("<사후 처리> 비지니스 로직 리턴 값 : " + returnObj.toString());

      if (returnObj instanceof UserVO) {
      UserVO user = (UserVO) returnObj;
      if(user.getRole().equals("ADMIN")) {
      System.out.println(user.getName() + "님은 관리자 화면으로 바로 이동........");
      }
      }
      }
    • 핵심 비지니스 로직에서 반환된 객체를 returnObj로 받아온다
  4. After Throwing

    • 핵심 비지니스 로직 실행헌 후 예외가 발생하면 예외객체를 받아와서 실행
    • 실행 예
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
       @AfterThrowing(pointcut = "execution(* com.rubypaper.biz..*Impl.*(..))", throwing = "exceptionObj")
      public void exceptionLog(JoinPoint jp, Exception exceptionObj) {
      String method = jp.getSignature().getName(); // 클라이언트가 호출한 메소드 이름

      System.out.println("[ 예외처리 ]"+ method +" 메소드 수행 중 예외 발생");

      if(exceptionObj instanceof IllegalArgumentException){
      System.out.println("0 번 게시 글을 등록할 수 없습니다");
      } else if (exceptionObj instanceof ArithmeticException) {
      System.out.println("0 으로 숫자를 나눌 수 없습니다");
      } else if (exceptionObj instanceof SQLException) {
      System.out.println("SQL 구문에 오류가 있습니다");
      } else {
      System.out.println("문제 발생 !! 시스템을 잠시 종료합니다");
      }
      }
      • 예외객체를 exceptionObj로 받아온다
  5. Around

    • 위에서 설명

Advice에서 Pointcut에 해당되는 메소드에 대한 정보를 얻기 위해 JoinPoint라는 객체를 받아올 수 있다. Around만 예외적으로 ProceedJoinPoint라는 객체를 통해서 받아온다

Comment and share


Goal

  • Cookie와 Session이 왜 필요한 이유
  • Cookie란 무엇인지에 대해 이해한다
  • Session이란 무엇인지에 대해 이해한다
  • Cookie와 Session의 차이에 대해 이해한다

쿠키와 세션의 사용 이유

HTTP 프로토콜의 특징

  1. 비연결지향(Conectionless)
    • HTTP는 클라이언트가 요청(Request)을 서버에 보내고, 서버는 클라이언트에 요청에 맞는 응답(Response)를 보내면 바로 연결을 끊는다
  2. 상태 정보를 유지 안 함(Stateless)
    • 연결을 끊는 순간 클라이언트와 서버의 통신은 끝나면 상태 정보를 유지하지 않는다

쿠키와 세션의 필요성

  • HTTP 프로토콜은 위와 같은 특징으로 모든 요청간의 의존관계가 없다
  • 즉, 현재 접속한 사용자가 이전에 접속했던 사용자와 같은 사용자인지 아닌지 알 수 있는 방법이 없다
  • 계속해서 연결을 유지하지 않기 때문에 리소스 낭비가 줄어드는 것이 큰 장점이지만, 통신할 때마다 새로 연결하기 때문에 클라이언트는 매 요청마다 인증을 해야한다는 단점이 있다
  • 이전 요청과 현재 요청이 같은 사용자의 요청인지 알기 위해서는 상태를 유지해야한다
  • HTTP프로토콜에서 상태를 유지하기 위한 기술로 쿠키와 세션이 있다

쿠키 (Cookie)

쿠키란?

  • 쿠키는 클라이언트(브라우저) 로컬에 저장되는 키와 값이 들어있는 작은 데이터 파일한다
  • 유효한 시간을 명시할 수 있으며, 유효 시간이 정해지면 브라우저가 종료되어도 쿠키가 유지된다는 특징이 있다
  • 쿠키는 클라이언트의 상태 정보를 로컬에 저장했다가 참조한다

구성요소

  • 쿠키의 이름(name)
  • 쿠키의 값(value)
  • 쿠키의 만료시간(Expire)
  • 쿠키를 전송할 도메인 이름(Domain)
  • 쿠키를 전송할 경로(Path)
  • 보안 연결 여부(Secure)
  • HttpOnly 여부(HttpOnly)
  • 지속 쿠키
    • 만료 날짜/시간을 지정하지 않으면 항상 유지하라는 것으로 판단하고 지속 쿠키에 저장된다
    • 파일로 저장되므로 브라우저가 종료되어도 쿠키는 남아있다
  • 세션 쿠키
    • 만료 날짜/시간을 지정하면 세션 쿠키로 저장된다
    • 브라우저 메모리에 저장되므로 브라우저가 종료되면 쿠키는 사라지게 된다

동작 방식

스크린샷 2020-11-10 오후 9 36 25

  1. 웹 브라우저가 서버에 요청
  2. 상태를 유지하고 싶은 값을 쿠키(Cookie)로 생성
  3. 서버가 응답할 때 Http Response Header의 Set-Cookie에 쿠키를 포함해서 전송
    1
    Set-Cookie: id=chan
  4. 전달받은 쿠키는 웹 브라우저에서 관리하고 있다가, 다음 요청 때 쿠키를 Http 헤더에 넣어서 전송
    1
    cookie: id=chan
  5. 서버에서는 쿠키 정보를 읽어 이전 상태 정보를 확인 후 응답

쿠키 사용 예

  • 아이디, 비밀번호 저장
  • 쇼핑몰 장바구니

쿠키의 제약사항

  • 클라이언트에 최대 300개까지 쿠키를 저장할 수 있다

  • 서버 도메인 하나당 최대 20개의 쿠키를 저장할 수 있다

  • 하나의 쿠키 값은 최대 4KB까지 저장할 수 있다

    쿠키는 사용자가 별도로 요청하지 않아도 브라우저(Client)에서 서버에 요청시 Request Header에 쿠키 값을 자동으로 넣어 보낸다

    그렇다고 모든 쿠키 값을 모든 요청에 넣어서 비효율적으로 동작하지 않고 지정 도메인에 해당하는 쿠키 값만을 제공한다

세션(Session)

세션이란?

  • 세션은 쿠키를 기반으로 동작하지만, 사용자 정보 파일을 브라우저에 저장하는 쿠키와 달리 세션은 서버 측에서 관리한다
  • 서버에서는 클라이언트를 구분하기 위해 세션 ID를 부여하며 웹 브라우저가 서버에 접속해서 브라우저를 종료할 때까지 인증상태를 유지한다
  • 접속 시간에 제한을 두어 일정 시간 응답이 없다면 정보가 유지되지 않게 설정이 가능하다
  • 사용자에 대한 정보를 서버에 두기 때문에 쿠키보다 보안에 좋지만, 사용자가 많아질수록 서버 메모리를 많이 차지하게 된다
  • 동접자 수가 많은 웹 사이트인 경우 서버에 과부하를 주게 되므로 성능 저하의 요인이 된다
  • 클라이언트가 Request를 보내면, 해당 서버 엔진이 클라이언트에게 유일한 ID를 부여하는데 이것이 세션이다

동작 방식

스크린샷 2020-11-10 오후 11 21 26

  1. 웹 브라우저가 서버에 요청
  2. 서버가 해당 웹 브라우저(클라이언트)에 유일한 ID(Session ID)를 부여함
  3. 서버가 응답할 때 HTTP Header(Set-Cookie)에 Session ID를 포함해서 전송
    • 쿠키에 Session ID를 JSESSIONID라는 이름으로 저장
      1
      Set-Cookie : JSESSIONID=g3r2sddh
  4. 웹 브라우저는 이후 웹 브라우저를 닫기까지 다음 요청 때 부여된 Session ID가 담겨있는 쿠키를 HTTP Header에 넣어서 전송
    1
    Cookie : JSESSIONID=g3r2sddh
  5. 서버는 세션 ID를 확인하고, 해당 세션에 관련된 정보를 확인한 후 응답

세션도 쿠키를 사용하여 값을 주고받으며 클라이언트의 상태 정보를 유지한다

즉, 세션이 상태 정보를 유지하는 수단으로 쿠키를 사용한다

쿠키와 세션의 차이

  • 저장 위치
    • 쿠키는 클라이언트(브라우저)의 메모리 또는 파일에 저장
    • 세션은 서버 메모리에 저장
  • 보안
    • 쿠키는 로컬에 저장되고, 특히 파일로 저장되는 경우가 있어 탈취, 변조에 위험하고 Request/Response시에 스니핑당할 위험이 있다
    • 세션은 클라이언트 정보 자체가 서버에 저장되어 있으므로 비교적 안전하다
  • 라이프 사이클
    • 쿠키중 지속 쿠키는 브라우저가 종료하더라도 저장되어있다
    • 세션은 만료시간/날짜를 정해서 기간이 지나면 자동으로 삭제하고 세션쿠키에서 세션 아이디를 정한 경우, 브라우저 종료시 세션아이디가 삭제된다
  • 속도
    • 쿠키는 클라이언트에 저장되어 있기 때문에 서버 요청시 속도가 빠르다
    • 세션은 정보가 서버에 있기 때문에 처리가 요구되어 비교적 느린 속도를 낸다

참고 - 캐시

  • 브라우저 캐시는 이미지(.png, .jpg)나 css, js파일등이 사용자의 브라우저에 저장되는 것이다
  • 이를 이용해서 같은 자원을 로드(load)해야할 경우에, 해당 자원을 다시 서버에 요청하지 않고 브라우저에 캐시되어 있는 자원을 쓰는 방식이다
  • 자원이 브라우저 캐시에 저장되면 브라우저에 있는 결 재사용하기 때문에 경우에 따라서 자원이 변경되어도 변경된 자원을 참조할 수 없는 경우가 있다
  • 따라서 사용자는 브라우저 캐시를 지워주거나 서버에서 클라이언트에 응답을 보낼 때 header에 자원 캐시 만료시간을 명시하는 방식을 사용하여 위와같은 문제를 해결할 수 있다

Comment and share

Web-HTTP

in Web, HTTP

HTTP

정의

  • HTTP(Hyper Text Transfer Protocol)는 웹 서버(Apache, Nginx 등)와 클라이언트(브라우저)가 인터넷 상에서 HTML문서와 같은 리소스들을 주고받을 수 있도록 하기 위해 만든 프로토콜(통신 규약)

    스크린샷 2020-11-10 오후 4 51 43

  • HTTP통신은 클라이언트가 데이터를 요청(request)하면 그 요청을 처리하여 서버가 다시 클라이언트에게 응답(response)하는 큰 흐름을 따른다

  • 어떠한 데이터의 형태도 주고 받을 수 있다

특징

  • 무상태(stateless) & 비연결성(Connectionless)
    • 한 번의 요청에 대해 TCP통신을 설정한 후 한 번의 요청에 대한 응답이 처리되면 TCP연결을 끊어 버리는 형태의 통신
    • TCP연결을 끊어 버리는 형태의 통신으로 연속적인 통신에 부적합
    • 하지만, 인터넷과 같이 다수의 사용자를 대상으로 한 Pull방식의 사용자가 필요한 문서 서비스를 전달 받는 구조에는 적합한 통신 구조
    • 한 번의 요청에 대해 한 번의 응답으로 트랜잭션이 종료되므로 연속적인 작업 처리에 필요한 트랜잭션 상태 정보를 관리하기 위한 Overhead가 없다
    • HTTP를 상업적으로 이용하면서 이러한 구조는 큰 단점이 되었다
      • 대안 기술: Cookies & Session Tracking

HTTP Request Message의 구조

3가지 부분으로 구성

스크린샷 2020-11-10 오후 5 17 15

  • start line(request line)

    • HTTP Method: GET, POST 등 action을 정의
    • Request target: request가 전송되는 uri(end point)
    • HTTP Version
  • headers

    • 클라이언트와 서버가 요청 또는 응답으로 부가적인 정보를 전송할 수 있도록 해주며, key:value 값으로 되어있다
    • Host: (가상 호스팅을 위해) 서버의 도메인명과 서버가 리스닝하는 (부가적인) TCP 포트를 특정한다
    • User-Agent: 요청을 보내는 클라이언트에 대한 정보
    • Accept
      • MIME 타입으로 표현
      • 클라이언트가 이해 가능한 컨텐츠 타입이 무엇인지 알려준다
      • 서버는 제안 중 하나를 선택하고 사용하며 Content-Type 응답 헤더로 클라이언트에게 선택된 타입을 알려준다
    • Cache-Control(HTTP/1.1)
      • Cache를 이용하여 웹 서버와 클라이언트간의 요청과 응답 횟수를 줄여 대역폭을 향상하기 위해 사용된다
      • 만기일(expiration)과 유효일(validation)을 지정
    • Connection
      • 트랜젝션 종료후에 네트워크 연결을 유지할지 말지를 결정한다
      • HTTP/1.0은 트랜젝션 이후의 커넥션을 종료한다
      • colse : 연결을 끊음
      • keep alive : 연결을 지속
    • Content-Type
      • 해당 요청이 보내는 메세지 body의 타입
      • 예를 들어 JSON을 보내면 application/json
    • Content-Length: 메세지 body의 길이
  • blank line: 요청에 대한 meta 정보가 전송되었음을 알린다

  • body:

    • 해당 request의 실제 메세지/내용이 들어있다
    • XML, HTML, JSON 데이터가 들어갈 수 있다

HTTP Response 구조

스크린샷 2020-11-10 오후 6 56 58

  • status line

    • response의 상태를 간략하게 나타내며, 3부분으로 구성되어 있다

      • HTTP Version
      • status code: 응답 상태를 나타내는 코드
      • status text: 으답 상태를 설명(ex: Not Found)
    • headers

      • request의 header와 동일하다
      • response에서만 사용되는 header값이 있다. (예: User-Agent가 없고, Server가 있음)
    • blank line

    • body: 실제 응답하는 데이터를 나타낸다

Comment and share

Web-Servlet

in Web, Servlet

Goal

  • Web Service의 기본적인 동작 과정을 이해한다
  • Servlet을 이해한다

Servlet이란

Servlet의 개념

웹 기반의 요청에 대한 동적인 처리가 가능한 하나의 클래스

  • Server Side에서 돌아가는 Java Program
  • 개발자가 작성하는 부분

Servlet의 기본적인 동작과정

스크린샷 2020-11-10 오전 10 49 45

  1. Web Server는 HTTP Request를 Web Container(Servlet Container)에게 위임한다
    1. web.xml설정에서 어떤 URL과 매핑되어 있는지 확인
    2. 클라이언트(browser)의 요청 URL을 보고 적절한 Servlet을 실행
  2. Web Container는 service() 메소드를 호출하기 전에 Servlet객체를 메모리에 올린다
    1. Web Container는 적절한 Servlet 파일을 컴파일(.class 파일 생성)한다
    2. .class 파일을 메모리에 올려 Servlet 객체를 만든다
    3. 메모리에 로드될 때 Servlet객체를 초기화하는 init()메소드가 실행된다
  3. Web Container는 Request가 올 때 마다 thread를 생성하여 처리한다.
    • 각 thread는 Servlet의 단일 객체에 대한 service() 메소드를 실행한다
  • Servlet Program에서 Thread의 역할
    • Thread의 역할: Servlet의 doGet()또는 doPost()를 호출
    • Web Container(Servlet Container)는 thread의 생성과 제거를 담당한다
      • 하지만 thread의 생성과 제거의 반복은 큰 오버헤드를 만든다
      • 이를 위해서 Tomcat(WAS)는 “Thread Pool”이라는 적절한 매커니즘을 사용해서 오버해드를 줄인다
    • 즉, WAS는 Servlet의 Life cycle을 담당한다
      • 웹 브라우저 클라이언트의 요청이 들어왔을때 Servlet 객체의 생성은 WAS가 알아서 처리한다
      • WAS 위에서 Servlet이 돌아다니고 개발자는 이 Servlet을 만들어야한다

Servlet Life Cycle

스크린샷 2020-11-10 오전 11 18 28

  • 클라이언트에 요청이 들어오면 WAS는 해당 요청에 맞는 Servlet이 메모리에 있는지 확인한다

    • 만약 메모리에 없다면 해당 Servlet Class를 메모리에 올린 후(Servlet 객체 생성)
      • init() 메소드 실행 후 service() 메소드를 실행
    • 메모리에 있다면 바로 service 메소드 실행
      1
      2
      3
      4
      5
      if(메모리에 없음) {
      // 해당 서블릿 클래스를 메모리에 올림
      // init() 메소드를 실행
      }
      // service() 메소드를 실행
  • init()

    • 한 번만 수행된다
    • 클라이언트의 요청에 따라 적절한 Servlet이 생성되고
      이 Servlet이 메모리에 로드될 때 init() 메소드가 실행된다
    • 역할: Servlet 객체를 초기화
  • service()

    • 응답에 대한 모든 내용은 service()메소드에 구현해야한다
    • Servlet이 수신한 모든 request에 대해 service() 메소드가 호출된다
      • HttpServlet을 상속받은 Servlet클래스에서 service()메서드를 오버라이드하지 않았다면, 그 부모인 HttpServlet의 service()가 호출된다
      • HttpServlet의 service()메소드는 템플릿 메소드 패턴으로 구현되었다
      • service()메소드는 request의 type(HTTP Method: GET, POST, PUT, DELETE)에 따라 적절한 메소드(doGet, doPost, doPut, doDelete)를 호출한다
      • 즉, 하위 클래스에서 doGet, doPost등의 메소드를 오버라이드 해두면 HttpServlet의 service()메소드가 요청에 맞는 메소드를 알아서 호출할 수 있게 된다
    • 메소드가 return하면 thread는 종료된다
  • destory()

    • 한 번만 수행된다
    • Web Application이 갱신되거나 WAS를 종료할 때 호출된다
    • 역할: Servlet 객체를 메모리에서 제거

Servlet 메서드 구현 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    // `javax.servlet.http.HttpServlet`를 상속받은 Servlet 클래스
public class LoginServlet extends HttpServlet {
// doPost()를 재정의
protected void doPost(HttpServletRequest request, HttpServletResponse response throws ServletException, IOException {
// read form fields
String username = request.getParameter("username");
String password = request.getParameter("password");

// get response writer
PrintWriter writer = response.getWriter();

/* 여기서 -> DB 접근 등 Business Logic 부분을 처리 */

// build HTML code (view 생성 부분)
String htmlResponse = "<html>";
htmlResponse += "<h2>Your username is: " + username + "<br/>";
htmlResponse += "Your password is: " + password + "</h2>";
htmlResponse += "</html>";

// return response
writer.println(htmlResponse);
}
}
  • WAS는 웹 브라우저로부터 요청을 받으면

    1. 요청할 때 가지고 있는 정보를 HttpServletRequest객체를 생성하여 저장한다
    2. 웹 브라우저에게 응답을 보낼 때, 사용하기 위한 HttpServletResponse 객체를 생성한다
    3. 생성된 HttpServletRequest, HttpServletResponse 객체를 Servlet에게 전달한다
  • 일반적으로 javax.servlet.http.HttpServlet를 상속받은 Servlet클래스를 작성한다

    1. HttpServletRequest request 파라미터를 통해서 사용자가 입력한 data를 읽는다
    2. HttpServletResponse 파라지터를 통해서 출력/결과를 생성한다
  • Servlet 클래스에 doGet() 또는 doPost()중 적어도 하나를 재정의하여 작성한다

    • protected doGet()(HttpServletRequest request, HttpServletResponse response){}
    • protected doPost()(HttpServletRequest request, HttpServletResponse response){}
  • HttpServletRequest request 객체

    • 사용자가 입력한 내용을 request 객체에 받아온다

      • Http 프로토콜의 Request정보를 Servlet에 전달
    • 헤더 정보, 파라미터, 쿠키, URI, URL, Body의 Stream등을 읽어 들이는 메소드가 있다.

    • getHeader(“원하는 헤더 이름”)

      • 원하는 헤더 정보를 확인할 수 있다
    • getParameter()

      • 이 메소드를 호출하여 parameter값을 가져온다
        • parameter값은 URL또는 form의 input tag를 통해서 넘어올 수 있다
      • Ex.
        1
        2
        String name = request.getParameter("firstname");
        int year = Integer.parseInt (request.getParameter("year"));
    • getParameterValues()

      • parameter가 여러 개의 값을 반환할 때 이 메소드를 호출한다. (Ex. checkbox)
        1
        String languages[] = request.getParameterValues("language");

Servlet Concurrency

스크린샷 2020-11-10 오후 1 35 15

  • Java 서블릿 컨테니어/ 웹 서버는 일반적으로 멀티 쓰레드 환경이다.

    • 같은 Servlet에 대한 여러 개의 요청이 동시에 실행될 수 있어 runtime에 따라 결과가 달라질 수 있다
    • 즉, Servlet은 메모리에 한 번 올라오고 멀티 쓰레드 환경에서 여러 thread는 하나의 Servlet을 공유하기 대문에 Concurrency Control가 필요하다
  • Servlet의 service() 메소드 안의 변수

    • 정적 변수, 맴버 변수: 공유하는 자원이므로 상호 배제가 필요
    • 지역 변수: thread마다 독립적으로 생성

Servlet Annotation

스크린샷 2020-11-10 오후 1 44 53

  • Servlet API 3.0은 javax.servlet.annotation이라는 새로운 패키지를 도입했다.
    • Tomcat 7이상에서 사용가능
  • Annotation은 Web Deployment Descriptor파일(web.xml)의 XML 설정을 대체할 수 있다
  • Annotation Type
    • @WebServlet: 서블릿 선언
    • @WebInitParam: 초기화 매개 변수 지정
  • Ex)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @WebServlet(value = "/Simple", 
    initParams = {@WebInitParam(name="foo", value="Hello "),
    @WebInitParam(name="bar", value=" World!")})
    public class Simple extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setContentType("text/html");
    PrintWriter out=response.getWriter();

    out.print("<html><body>");
    out.print("<h3>Hello Servlet</h3>");
    out.println(getInitParameter("foo"));
    out.println(getInitParameter("bar"));
    out.print("</body></html>");
    }
    }

Comment and share


Goal

  • Static Pages와 Dynamic Pages 과정을 이해한다
  • Web Server와 WAS의 차이를 이해한다
  • Web 서비스 구조(Web Service Architecture)에 대해 이해한다

Static Pages와 Dynamic Pages

스크린샷 2020-11-10 오전 12 05 07

Static Pages

  • Web Server는 파일 경로 이름을 받아 경로와 일치하는 file contents를 반환한다.
  • 항상 동일한 페이지를 반환
  • Ex) image, html, css, javascript파일

Dynamic Pages

  • 인자의 내용에 맞게 동적인 contents를 반환
  • 웹 어플리케이션 서버에 의해서 실행되는 프로그램을 통해 만들어진 결과물 (Servlet: WAS위에 돌아가는 Java Program)
  • Servlet의 doGet()을 구현

Web Server와 WAS의 차이

스크린샷 2020-11-10 오전 12 15 07

Web Server

  • Web Server의 개념
    • 웹 브라우저 클라이언트로부터 HTTP 요청을 받아 정적인 컨텐츠(.html .jpeg .css등)를 제공하는 컴퓨터 프로그램
  • Web Server의 기능
    • HTTP 프로토콜을 기반으로 하여 클라이언트(웹 브라우저 또는 웹 크롤러)의 요청을 서비스하는 기능을 담당한다
    • 요청에 따라 아래의 두 가지 기능 중 적절하게 선택하여 수행한다
    • 기능 1)
      • 정적인 컨텐츠 제공
      • WAS를 거치지 않고 바로 자원을 제공한다
    • 기능 2)
      • 동적인 컨텐츠 제공을 위한 요청 전달
      • 클라이이언트 요청(Request)를 WAS에 보내고, WAS가 처리한 결과를 클라이언트에게 전달(응답, Response)한다
  • Web Server의 예
    • Ex) Apache Server, Nginx, IIS등

WAS(Web Application Server)

  • WAS의 개념

    • DB조회나 다양한 로직 처리를 요구하는 동적인 컨텐츠를 제공하기 위해 만들어진 Application Server
    • HTTP를 통해 컴퓨터나 장치에 애플리케이션을 수행해주는 미들웨어(소프트웨어 엔진)이다.
    • 웹 컨테이너(Web Container) 혹은 서블릿 컨테이너(Servlet Container)라고도 불린다
      • Container란 JSP, Servlet을 실행시킬 수 있는 소프트웨어
      • WAS는 JSP, Servlet 구동 환경을 제공한다
  • WAS의 역할

    • WAS = Web Server + Web Container
    • Web Server 기능들을 구조적으로 분리하여 처리하고자하는 목적으로 제시되었다.
      • 분산 트랜젝션, 보안, 메세징, 쓰레드 처리등의 기능을 처리하는 분산 환경에서 사용된다.
      • 주로 DB서버와 같이 수행된다
    • 현재는 WAS가 가지고 있는 Web Server도 정적인 컨텐츠를 처리하는데 있어서 성능상 큰 차이가 없다.
  • WAS의 주요 기능

    1. 프로그램 실행환경과 DB 접속 기능 제공
    2. 여러 개의 트랜잭션(논리적인 작업 단위)관리 기능
    3. 업무를 처리하는 비지니스
  • WAS의 예

    • Ex) Tomcat, JBoss, Jeus, Web Sphere등

Web Serve와 WAS를 구분하는 이유

  • Web Serve가 필요한 이유?

    • 웹 브라우저(클라이언트)에 이미지 파일(정적 컨텐츠)를 보내는 과정을 생각해보자
      • 이미지 파일과 같은 정적인 파일들은 웹 문서(HTML 문서)가 클라이언트로 보내질 때 함께 가는 것이 아니다.
      • 클라이언트가 HTML문서를 먼저 받아오고 그에 맞게 필요한 이미지 파일들을 다시 서버로 요청하면 그 때서야 이미지 파일을 받아온다.
      • Web Server를 통해 정적인 파일들을 Application Server까지 가지 않고 앞단에서 빠르게 보내줄 수 있다.
    • Web Server에서는 정적 컨텐츠만 처리하도록 기능을 분배하여 서버의 부담을 줄일 수 있다
  • WAS가 필요한 이유?

    • 웹 페이지는 정적 컨텐츠와 동적 컨텐츠가 모두 존재한다
      • 사용자의 요청에 맞게 적절한 동적 컨텐츠를 만들어 제공해야 한다
      • 이때, Web Server만을 이용한다면 사용자가 원하는 요청에 대한 결과값을 미리 만들어 놓고 서비스를 해야한다
      • 하지만 이렇게 수행하기에는 자원이 절대적으로 부족하다
    • 따라서 WAS를 통해서 요청에 맞는 데이터를 DB에서 가져와서 비지니스 로직에 맞게 그때 그때 결과를 만들어서 제공함으로써 자원을 효율적으로 사용할 수 있다.
  • 그렇다면 WAS가 Web Server의 기능도 모두 수행하면 되지 않을까?

    1. 기능을 분리하여 서버 부하 방지
      • WAS는 DB조회나 다양한 로직을 처리하느라 바쁘기 때문에 단순한 정적 컨텐츠는 Web Server에서 빠르게 클라이언트에게 제공하는 것이 좋다
      • WAS는 기본적으로 동적 컨텐츠를 제공하기 위해 존재하는 서버
    2. 물리적으로 분리하여 보안 강화
      • SSL에 대한 암복호화 처리에 Web Server를 사용
    3. 여러 대의 WAS를 연결 가능
      • Load Balancing을 위해서 Web Server를 사용
      • fail over(장애 극복), fail back처리에 유리
      • 특히 대용량 웹 어플리케이션의 경우(여러 개의 서버 사용) Web Server와 WAS를 분리하여 무중단 운영을 위한 장애 극복에 쉽게 대응할 수 있다
      • 예를 들어, 앞 단의 Web Server에서 오류가 발생한 WAS를 이용하지 못하도혹 한 후 WAS를 재시작함으로써 사용자는 오류를 느끼지 못하고 이용할 수 있다.
    4. 여러 웹 어플리케이션 서비스 가능
      • 예를 들어, 하나의 서버에서 PHP Application과 Java Application을 함께 사용하는 경우
    • 즉 자원 이용의 효율성 및 장애 극복, 배포 및 유지보수의 편의성을 위해서
      Web Server와 WAS를 분리한다
    • Web Server를 WAS 앞에 두고 WAS를 Web Server에 플러그인 형태로 설정하면 더욱 효율적인 분산처리가 가능하다

Web Service Architecture

  • Architecture 종류
    1. Client -> Web Server -> DB
    2. Client -> WAS -> DB
    3. Client -> Web Server -> WAS -> DB

스크린샷 2020-11-10 오전 10 24 24

  • 3번 구조의 동작 과정
    1. Web Server는 웹 브라우저 클라이언트로부터 HTTP 요청을 받는다
    2. Web Server는 클라이언트의 요청(Request)을 WAS에 보낸다
    3. WAS는 관련된 Servlet을 메모리에 올린다
    4. WAS는 web.xml을 참조하여 해당 Servlet에 대한 Thread를 생성한다 (Thread Pool 이용)
    5. HttpServletRequest와 HttpServletResponse 객체를 생성하여 Servlet에 전달한다
      1. Thread는 Servlet의 service() 메소드를 호출한다
      2. service()메소드는 요청에 맞게 doGet()또는 doPost()메소드를 호출한다
      3. protected doGet(HttpServletRequest request, HttpServletResponse response)
    6. doGet()또는 doPost()메소드는 인자에 맞게 생성된 적절한 동적 페이지를 Response객체에 담아 WAS에 전달한다
    7. WAS는 Response객체를 HttpResponse형태로 바꾸어 Web Server에 전달한다
    8. 생성된 Thread를 종료할고, HttpServletRequest와 HttpServletResponse 객체를 제거한다

Comment and share

  • page 1 of 1

Yechan Kim

author.bio


author.job